Merge "AudioFlinger: Allocate client memory based on total device memory"
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
index 28252c0..4db7f85 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
@@ -30,6 +30,7 @@
     const int ERROR_CAMERA_REQUEST = 3;
     const int ERROR_CAMERA_RESULT = 4;
     const int ERROR_CAMERA_BUFFER = 5;
+    const int ERROR_CAMERA_DISABLED = 6;
 
     oneway void onDeviceError(int errorCode, in CaptureResultExtras resultExtras);
     oneway void onDeviceIdle();
diff --git a/camera/camera2/CaptureRequest.cpp b/camera/camera2/CaptureRequest.cpp
index 983d29b..1843ec4 100644
--- a/camera/camera2/CaptureRequest.cpp
+++ b/camera/camera2/CaptureRequest.cpp
@@ -18,6 +18,7 @@
 // #define LOG_NDEBUG 0
 #define LOG_TAG "CameraRequest"
 #include <utils/Log.h>
+#include <utils/String16.h>
 
 #include <camera/camera2/CaptureRequest.h>
 
@@ -42,18 +43,39 @@
         return BAD_VALUE;
     }
 
-    mMetadata.clear();
     mSurfaceList.clear();
     mStreamIdxList.clear();
     mSurfaceIdxList.clear();
+    mPhysicalCameraSettings.clear();
 
     status_t err = OK;
 
-    if ((err = mMetadata.readFromParcel(parcel)) != OK) {
-        ALOGE("%s: Failed to read metadata from parcel", __FUNCTION__);
+    int32_t settingsCount;
+    if ((err = parcel->readInt32(&settingsCount)) != OK) {
+        ALOGE("%s: Failed to read the settings count from parcel: %d", __FUNCTION__, err);
         return err;
     }
-    ALOGV("%s: Read metadata from parcel", __FUNCTION__);
+
+    if (settingsCount <= 0) {
+        ALOGE("%s: Settings count %d should always be positive!", __FUNCTION__, settingsCount);
+        return BAD_VALUE;
+    }
+
+    for (int32_t i = 0; i < settingsCount; i++) {
+        String16 id;
+        if ((err = parcel->readString16(&id)) != OK) {
+            ALOGE("%s: Failed to read camera id!", __FUNCTION__);
+            return BAD_VALUE;
+        }
+
+        CameraMetadata settings;
+        if ((err = settings.readFromParcel(parcel)) != OK) {
+            ALOGE("%s: Failed to read metadata from parcel", __FUNCTION__);
+            return err;
+        }
+        ALOGV("%s: Read metadata from parcel", __FUNCTION__);
+        mPhysicalCameraSettings.push_back({std::string(String8(id).string()), settings});
+    }
 
     int isReprocess = 0;
     if ((err = parcel->readInt32(&isReprocess)) != OK) {
@@ -135,10 +157,25 @@
 
     status_t err = OK;
 
-    if ((err = mMetadata.writeToParcel(parcel)) != OK) {
+    int32_t settingsCount = static_cast<int32_t>(mPhysicalCameraSettings.size());
+
+    if ((err = parcel->writeInt32(settingsCount)) != OK) {
+        ALOGE("%s: Failed to write settings count!", __FUNCTION__);
         return err;
     }
 
+    for (const auto &it : mPhysicalCameraSettings) {
+        if ((err = parcel->writeString16(String16(it.id.c_str()))) != OK) {
+            ALOGE("%s: Failed to camera id!", __FUNCTION__);
+            return err;
+        }
+
+        if ((err = it.settings.writeToParcel(parcel)) != OK) {
+            ALOGE("%s: Failed to write settings!", __FUNCTION__);
+            return err;
+        }
+    }
+
     parcel->writeInt32(mIsReprocess ? 1 : 0);
 
     if (mSurfaceConverted) {
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index 813d6c9..feb04c2 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -1,6 +1,6 @@
 /*
 **
-** Copyright 2015, The Android Open Source Project
+** Copyright 2015-2018, 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.
@@ -64,6 +64,10 @@
     return mIsShared;
 }
 
+String16 OutputConfiguration::getPhysicalCameraId() const {
+    return mPhysicalCameraId;
+}
+
 OutputConfiguration::OutputConfiguration() :
         mRotation(INVALID_ROTATION),
         mSurfaceSetID(INVALID_SET_ID),
@@ -139,6 +143,8 @@
         return err;
     }
 
+    parcel->readString16(&mPhysicalCameraId);
+
     mRotation = rotation;
     mSurfaceSetID = setID;
     mSurfaceType = surfaceType;
@@ -153,8 +159,9 @@
         mGbps.push_back(surface.graphicBufferProducer);
     }
 
-    ALOGV("%s: OutputConfiguration: rotation = %d, setId = %d, surfaceType = %d",
-            __FUNCTION__, mRotation, mSurfaceSetID, mSurfaceType);
+    ALOGV("%s: OutputConfiguration: rotation = %d, setId = %d, surfaceType = %d,"
+          " physicalCameraId = %s", __FUNCTION__, mRotation, mSurfaceSetID,
+          mSurfaceType, String8(mPhysicalCameraId).string());
 
     return err;
 }
@@ -204,6 +211,9 @@
     err = parcel->writeParcelableVector(surfaceShims);
     if (err != OK) return err;
 
+    err = parcel->writeString16(mPhysicalCameraId);
+    if (err != OK) return err;
+
     return OK;
 }
 
diff --git a/camera/include/camera/camera2/CaptureRequest.h b/camera/include/camera/camera2/CaptureRequest.h
index c53799f..506abab 100644
--- a/camera/include/camera/camera2/CaptureRequest.h
+++ b/camera/include/camera/camera2/CaptureRequest.h
@@ -40,7 +40,11 @@
     CaptureRequest(CaptureRequest&& rhs) noexcept;
     virtual ~CaptureRequest();
 
-    CameraMetadata          mMetadata;
+    struct PhysicalCameraSettings {
+        std::string id;
+        CameraMetadata settings;
+    };
+    std::vector<PhysicalCameraSettings> mPhysicalCameraSettings;
 
     // Used by NDK client to pass surfaces by stream/surface index.
     bool                    mSurfaceConverted = false;
diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h
index 3599604..a80f44b 100644
--- a/camera/include/camera/camera2/OutputConfiguration.h
+++ b/camera/include/camera/camera2/OutputConfiguration.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2015-2018 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.
@@ -46,6 +46,7 @@
     int                        getHeight() const;
     bool                       isDeferred() const;
     bool                       isShared() const;
+    String16                   getPhysicalCameraId() const;
     /**
      * Keep impl up-to-date with OutputConfiguration.java in frameworks/base
      */
@@ -74,7 +75,8 @@
                 mHeight == other.mHeight &&
                 mIsDeferred == other.mIsDeferred &&
                 mIsShared == other.mIsShared &&
-                gbpsEqual(other));
+                gbpsEqual(other) &&
+                mPhysicalCameraId == other.mPhysicalCameraId );
     }
     bool operator != (const OutputConfiguration& other) const {
         return !(*this == other);
@@ -102,6 +104,9 @@
         if (mIsShared != other.mIsShared) {
             return mIsShared < other.mIsShared;
         }
+        if (mPhysicalCameraId != other.mPhysicalCameraId) {
+            return mPhysicalCameraId < other.mPhysicalCameraId;
+        }
         return gbpsLessThan(other);
     }
     bool operator > (const OutputConfiguration& other) const {
@@ -120,8 +125,7 @@
     int                        mHeight;
     bool                       mIsDeferred;
     bool                       mIsShared;
-    // helper function
-    static String16 readMaybeEmptyString16(const android::Parcel* parcel);
+    String16                   mPhysicalCameraId;
 };
 } // namespace params
 } // namespace camera2
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index f7cea4f..ef1c61f 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -372,7 +372,8 @@
         const ACaptureRequest* request, /*out*/sp<CaptureRequest>& outReq) {
     camera_status_t ret;
     sp<CaptureRequest> req(new CaptureRequest());
-    req->mMetadata = request->settings->getInternalData();
+    req->mPhysicalCameraSettings.push_back({std::string(mCameraId.string()),
+            request->settings->getInternalData()});
     req->mIsReprocess = false; // NDK does not support reprocessing yet
     req->mContext = request->context;
     req->mSurfaceConverted = true; // set to true, and fill in stream/surface idx to speed up IPC
@@ -418,7 +419,7 @@
 ACaptureRequest*
 CameraDevice::allocateACaptureRequest(sp<CaptureRequest>& req) {
     ACaptureRequest* pRequest = new ACaptureRequest();
-    CameraMetadata clone = req->mMetadata;
+    CameraMetadata clone = req->mPhysicalCameraSettings.begin()->settings;
     pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST);
     pRequest->targets  = new ACameraOutputTargets();
     for (size_t i = 0; i < req->mSurfaceList.size(); i++) {
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index 3f64bcc..a1a8cd6 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -340,6 +340,9 @@
         msg->setString(kCameraIdKey, AString(cameraId));
         msg->post();
     }
+    if (status == hardware::ICameraServiceListener::STATUS_NOT_PRESENT) {
+        mDeviceStatusMap.erase(cameraId);
+    }
 }
 
 } // namespace android
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index 29ad09b..62b0ec9 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -305,6 +305,7 @@
         case ACAMERA_STATISTICS_FACE_DETECT_MODE:
         case ACAMERA_STATISTICS_HOT_PIXEL_MAP_MODE:
         case ACAMERA_STATISTICS_LENS_SHADING_MAP_MODE:
+        case ACAMERA_STATISTICS_OIS_DATA_MODE:
         case ACAMERA_TONEMAP_CURVE_BLUE:
         case ACAMERA_TONEMAP_CURVE_GREEN:
         case ACAMERA_TONEMAP_CURVE_RED:
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 6e861a6..588e96a 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -69,6 +69,7 @@
     ACAMERA_SYNC,
     ACAMERA_REPROCESS,
     ACAMERA_DEPTH,
+    ACAMERA_LOGICAL_MULTI_CAMERA,
     ACAMERA_SECTION_COUNT,
 
     ACAMERA_VENDOR = 0x8000
@@ -104,6 +105,9 @@
     ACAMERA_SYNC_START             = ACAMERA_SYNC              << 16,
     ACAMERA_REPROCESS_START        = ACAMERA_REPROCESS         << 16,
     ACAMERA_DEPTH_START            = ACAMERA_DEPTH             << 16,
+    ACAMERA_LOGICAL_MULTI_CAMERA_START
+                                   = ACAMERA_LOGICAL_MULTI_CAMERA
+                                                                << 16,
     ACAMERA_VENDOR_START           = ACAMERA_VENDOR            << 16
 } acamera_metadata_section_start_t;
 
@@ -1646,7 +1650,7 @@
      * <p>Whether a significant scene change is detected within the currently-set AF
      * region(s).</p>
      *
-     * <p>Type: int32 (acamera_metadata_enum_android_control_af_scene_change_t)</p>
+     * <p>Type: byte (acamera_metadata_enum_android_control_af_scene_change_t)</p>
      *
      * <p>This tag may appear in:
      * <ul>
@@ -1658,11 +1662,9 @@
      * significant illumination change, this value will be set to DETECTED for a single capture
      * result. Otherwise the value will be NOT_DETECTED. The threshold for detection is similar
      * to what would trigger a new passive focus scan to begin in CONTINUOUS autofocus modes.</p>
-     * <p>afSceneChange may be DETECTED only if afMode is AF_MODE_CONTINUOUS_VIDEO or
-     * AF_MODE_CONTINUOUS_PICTURE. In other AF modes, afSceneChange must be NOT_DETECTED.</p>
      * <p>This key will be available if the camera device advertises this key via {@link ACAMERA_REQUEST_AVAILABLE_RESULT_KEYS }.</p>
      */
-    ACAMERA_CONTROL_AF_SCENE_CHANGE =                           // int32 (acamera_metadata_enum_android_control_af_scene_change_t)
+    ACAMERA_CONTROL_AF_SCENE_CHANGE =                           // byte (acamera_metadata_enum_android_control_af_scene_change_t)
             ACAMERA_CONTROL_START + 42,
     ACAMERA_CONTROL_END,
 
@@ -4562,6 +4564,85 @@
      */
     ACAMERA_STATISTICS_LENS_SHADING_MAP_MODE =                  // byte (acamera_metadata_enum_android_statistics_lens_shading_map_mode_t)
             ACAMERA_STATISTICS_START + 16,
+    /**
+     * <p>Whether the camera device outputs the OIS data in output
+     * result metadata.</p>
+     *
+     * <p>Type: byte (acamera_metadata_enum_android_statistics_ois_data_mode_t)</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+     *   <li>ACaptureRequest</li>
+     * </ul></p>
+     *
+     * <p>When set to ON,
+     * ACAMERA_STATISTICS_OIS_TIMESTAMPS, android.statistics.oisShiftPixelX,
+     * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
+     *
+     * @see ACAMERA_STATISTICS_OIS_TIMESTAMPS
+     */
+    ACAMERA_STATISTICS_OIS_DATA_MODE =                          // byte (acamera_metadata_enum_android_statistics_ois_data_mode_t)
+            ACAMERA_STATISTICS_START + 17,
+    /**
+     * <p>An array of timestamps of OIS samples, in nanoseconds.</p>
+     *
+     * <p>Type: int64[n]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+     * </ul></p>
+     *
+     * <p>The array contains the timestamps of OIS samples. The timestamps are in the same
+     * timebase as and comparable to ACAMERA_SENSOR_TIMESTAMP.</p>
+     *
+     * @see ACAMERA_SENSOR_TIMESTAMP
+     */
+    ACAMERA_STATISTICS_OIS_TIMESTAMPS =                         // int64[n]
+            ACAMERA_STATISTICS_START + 18,
+    /**
+     * <p>An array of shifts of OIS samples, in x direction.</p>
+     *
+     * <p>Type: float[n]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+     * </ul></p>
+     *
+     * <p>The array contains the amount of shifts in x direction, in pixels, based on OIS samples.
+     * A positive value is a shift from left to right in active array coordinate system. For
+     * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+     * (3, 0) puts the new optical center at (1003, 500).</p>
+     * <p>The number of shifts must match the number of timestamps in
+     * ACAMERA_STATISTICS_OIS_TIMESTAMPS.</p>
+     *
+     * @see ACAMERA_STATISTICS_OIS_TIMESTAMPS
+     */
+    ACAMERA_STATISTICS_OIS_X_SHIFTS =                           // float[n]
+            ACAMERA_STATISTICS_START + 19,
+    /**
+     * <p>An array of shifts of OIS samples, in y direction.</p>
+     *
+     * <p>Type: float[n]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+     * </ul></p>
+     *
+     * <p>The array contains the amount of shifts in y direction, in pixels, based on OIS samples.
+     * A positive value is a shift from top to bottom in active array coordinate system. For
+     * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+     * (0, 5) puts the new optical center at (1000, 505).</p>
+     * <p>The number of shifts must match the number of timestamps in
+     * ACAMERA_STATISTICS_OIS_TIMESTAMPS.</p>
+     *
+     * @see ACAMERA_STATISTICS_OIS_TIMESTAMPS
+     */
+    ACAMERA_STATISTICS_OIS_Y_SHIFTS =                           // float[n]
+            ACAMERA_STATISTICS_START + 20,
     ACAMERA_STATISTICS_END,
 
     /**
@@ -4634,6 +4715,24 @@
      */
     ACAMERA_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES =  // byte[n]
             ACAMERA_STATISTICS_INFO_START + 7,
+    /**
+     * <p>List of OIS data output modes for ACAMERA_STATISTICS_OIS_DATA_MODE that
+     * are supported by this camera device.</p>
+     *
+     * @see ACAMERA_STATISTICS_OIS_DATA_MODE
+     *
+     * <p>Type: byte[n]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>If no OIS data output is available for this camera device, this key will
+     * contain only OFF.</p>
+     */
+    ACAMERA_STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES =          // byte[n]
+            ACAMERA_STATISTICS_INFO_START + 8,
     ACAMERA_STATISTICS_INFO_END,
 
     /**
@@ -5165,6 +5264,29 @@
             ACAMERA_DEPTH_START + 4,
     ACAMERA_DEPTH_END,
 
+    /**
+     * <p>The accuracy of frame timestamp synchronization between physical cameras</p>
+     *
+     * <p>Type: byte (acamera_metadata_enum_android_logical_multi_camera_sensor_sync_type_t)</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>The accuracy of the frame timestamp synchronization determines the physical cameras'
+     * ability to start exposure at the same time. If the sensorSyncType is CALIBRATED,
+     * the physical camera sensors usually run in master-slave mode so that their shutter
+     * time is synchronized. For APPROXIMATE sensorSyncType, the camera sensors usually run in
+     * master-master mode, and there could be offset between their start of exposure.</p>
+     * <p>In both cases, all images generated for a particular capture request still carry the same
+     * timestamps, so that they can be used to look up the matching frame number and
+     * onCaptureStarted callback.</p>
+     */
+    ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE =             // byte (acamera_metadata_enum_android_logical_multi_camera_sensor_sync_type_t)
+            ACAMERA_LOGICAL_MULTI_CAMERA_START + 1,
+    ACAMERA_LOGICAL_MULTI_CAMERA_END,
+
 } acamera_metadata_tag_t;
 
 /**
@@ -6895,6 +7017,52 @@
      */
     ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING           = 10,
 
+    /**
+     * <p>The camera device is a logical camera backed by two or more physical cameras that are
+     * also exposed to the application.</p>
+     * <p>This capability requires the camera device to support the following:</p>
+     * <ul>
+     * <li>This camera device must list the following static metadata entries in <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html">CameraCharacteristics</a>:<ul>
+     * <li>android.logicalMultiCamera.physicalIds</li>
+     * <li>ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE</li>
+     * </ul>
+     * </li>
+     * <li>The underlying physical cameras' static metadata must list the following entries,
+     *   so that the application can correlate pixels from the physical streams:<ul>
+     * <li>ACAMERA_LENS_POSE_REFERENCE</li>
+     * <li>ACAMERA_LENS_POSE_ROTATION</li>
+     * <li>ACAMERA_LENS_POSE_TRANSLATION</li>
+     * <li>ACAMERA_LENS_INTRINSIC_CALIBRATION</li>
+     * <li>ACAMERA_LENS_RADIAL_DISTORTION</li>
+     * </ul>
+     * </li>
+     * <li>The logical camera device must be LIMITED or higher device.</li>
+     * </ul>
+     * <p>Both the logical camera device and its underlying physical devices support the
+     * mandatory stream combinations required for their device levels.</p>
+     * <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p>
+     * <ul>
+     * <li>Replacing one logical {@link AIMAGE_FORMAT_YUV_420_888 YUV_420_888}
+     *   or raw stream with two physical streams of the same size and format, each from a
+     *   separate physical camera, given that the size and format are supported by both
+     *   physical cameras.</li>
+     * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't
+     *   advertise RAW capability, but the underlying physical cameras do. This is usually
+     *   the case when the physical cameras have different sensor sizes.</li>
+     * </ul>
+     * <p>Using physical streams in place of a logical stream of the same size and format will
+     * not slow down the frame rate of the capture, as long as the minimum frame duration
+     * of the physical and logical streams are the same.</p>
+     *
+     * @see ACAMERA_LENS_INTRINSIC_CALIBRATION
+     * @see ACAMERA_LENS_POSE_REFERENCE
+     * @see ACAMERA_LENS_POSE_ROTATION
+     * @see ACAMERA_LENS_POSE_TRANSLATION
+     * @see ACAMERA_LENS_RADIAL_DISTORTION
+     * @see ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+     */
+    ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA      = 11,
+
 } acamera_metadata_enum_android_request_available_capabilities_t;
 
 
@@ -7226,6 +7394,20 @@
 
 } acamera_metadata_enum_android_statistics_lens_shading_map_mode_t;
 
+// ACAMERA_STATISTICS_OIS_DATA_MODE
+typedef enum acamera_metadata_enum_acamera_statistics_ois_data_mode {
+    /**
+     * <p>Do not include OIS data in the capture result.</p>
+     */
+    ACAMERA_STATISTICS_OIS_DATA_MODE_OFF                             = 0,
+
+    /**
+     * <p>Include OIS data in the capture result.</p>
+     */
+    ACAMERA_STATISTICS_OIS_DATA_MODE_ON                              = 1,
+
+} acamera_metadata_enum_android_statistics_ois_data_mode_t;
+
 
 
 // ACAMERA_TONEMAP_MODE
@@ -7387,6 +7569,37 @@
      */
     ACAMERA_INFO_SUPPORTED_HARDWARE_LEVEL_3                          = 3,
 
+    /**
+     * <p>This camera device is backed by an external camera connected to this Android device.</p>
+     * <p>The device has capability identical to a LIMITED level device, with the following
+     * exceptions:</p>
+     * <ul>
+     * <li>The device may not report lens/sensor related information such as<ul>
+     * <li>ACAMERA_LENS_FOCAL_LENGTH</li>
+     * <li>ACAMERA_LENS_INFO_HYPERFOCAL_DISTANCE</li>
+     * <li>ACAMERA_SENSOR_INFO_PHYSICAL_SIZE</li>
+     * <li>ACAMERA_SENSOR_INFO_WHITE_LEVEL</li>
+     * <li>ACAMERA_SENSOR_BLACK_LEVEL_PATTERN</li>
+     * <li>ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT</li>
+     * <li>ACAMERA_SENSOR_ROLLING_SHUTTER_SKEW</li>
+     * </ul>
+     * </li>
+     * <li>The device will report 0 for ACAMERA_SENSOR_ORIENTATION</li>
+     * <li>The device has less guarantee on stable framerate, as the framerate partly depends
+     *   on the external camera being used.</li>
+     * </ul>
+     *
+     * @see ACAMERA_LENS_FOCAL_LENGTH
+     * @see ACAMERA_LENS_INFO_HYPERFOCAL_DISTANCE
+     * @see ACAMERA_SENSOR_BLACK_LEVEL_PATTERN
+     * @see ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+     * @see ACAMERA_SENSOR_INFO_PHYSICAL_SIZE
+     * @see ACAMERA_SENSOR_INFO_WHITE_LEVEL
+     * @see ACAMERA_SENSOR_ORIENTATION
+     * @see ACAMERA_SENSOR_ROLLING_SHUTTER_SKEW
+     */
+    ACAMERA_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL                   = 4,
+
 } acamera_metadata_enum_android_info_supported_hardware_level_t;
 
 
@@ -7474,6 +7687,25 @@
 } acamera_metadata_enum_android_depth_depth_is_exclusive_t;
 
 
+// ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+typedef enum acamera_metadata_enum_acamera_logical_multi_camera_sensor_sync_type {
+    /**
+     * <p>A software mechanism is used to synchronize between the physical cameras. As a result,
+     * the timestamp of an image from a physical stream is only an approximation of the
+     * image sensor start-of-exposure time.</p>
+     */
+    ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE        = 0,
+
+    /**
+     * <p>The camera device supports frame timestamp synchronization at the hardware level,
+     * and the timestamp of a physical stream image accurately reflects its
+     * start-of-exposure time.</p>
+     */
+    ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED         = 1,
+
+} acamera_metadata_enum_android_logical_multi_camera_sensor_sync_type_t;
+
+
 #endif /* __ANDROID_API__ >= 24 */
 
 __END_DECLS
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 8fe9a86..24c0c51 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -317,6 +317,9 @@
     EXPECT_TRUE(res.isOk()) << res;
 
     EXPECT_EQ(numCameras, static_cast<const int>(statuses.size()));
+    for (const auto &it : statuses) {
+        listener->onStatusChanged(it.status, String16(it.cameraId));
+    }
 
     for (int32_t i = 0; i < numCameras; i++) {
         String16 cameraId = String16(String8::format("%d", i));
@@ -421,6 +424,9 @@
         serviceListener = new TestCameraServiceListener();
         std::vector<hardware::CameraStatus> statuses;
         service->addListener(serviceListener, &statuses);
+        for (const auto &it : statuses) {
+            serviceListener->onStatusChanged(it.status, String16(it.cameraId));
+        }
         service->getNumberOfCameras(hardware::ICameraService::CAMERA_TYPE_BACKWARD_COMPATIBLE,
                 &numCameras);
     }
@@ -439,8 +445,9 @@
     ASSERT_NOT_NULL(service);
     EXPECT_TRUE(serviceListener->waitForNumCameras(numCameras));
     for (int32_t i = 0; i < numCameras; i++) {
+        String8 cameraId8 = String8::format("%d", i);
         // Make sure we're available, or skip device tests otherwise
-        String16 cameraId(String8::format("%d",i));
+        String16 cameraId(cameraId8);
         int32_t s = serviceListener->getStatus(cameraId);
         EXPECT_EQ(hardware::ICameraServiceListener::STATUS_PRESENT, s);
         if (s != hardware::ICameraServiceListener::STATUS_PRESENT) {
@@ -488,7 +495,7 @@
         EXPECT_TRUE(res.isOk()) << res;
 
         hardware::camera2::CaptureRequest request;
-        request.mMetadata = requestTemplate;
+        request.mPhysicalCameraSettings.push_back({cameraId8.string(), requestTemplate});
         request.mSurfaceList.add(surface);
         request.mIsReprocess = false;
         int64_t lastFrameNumber = 0;
@@ -515,7 +522,7 @@
                 /*out*/&requestTemplate);
         EXPECT_TRUE(res.isOk()) << res;
         hardware::camera2::CaptureRequest request2;
-        request2.mMetadata = requestTemplate;
+        request2.mPhysicalCameraSettings.push_back({cameraId8.string(), requestTemplate});
         request2.mSurfaceList.add(surface);
         request2.mIsReprocess = false;
         callbacks->clearStatus();
@@ -548,10 +555,10 @@
         EXPECT_TRUE(res.isOk()) << res;
         android::hardware::camera2::CaptureRequest request3;
         android::hardware::camera2::CaptureRequest request4;
-        request3.mMetadata = requestTemplate;
+        request3.mPhysicalCameraSettings.push_back({cameraId8.string(), requestTemplate});
         request3.mSurfaceList.add(surface);
         request3.mIsReprocess = false;
-        request4.mMetadata = requestTemplate2;
+        request4.mPhysicalCameraSettings.push_back({cameraId8.string(), requestTemplate2});
         request4.mSurfaceList.add(surface);
         request4.mIsReprocess = false;
         std::vector<hardware::camera2::CaptureRequest> requestList;
@@ -585,3 +592,62 @@
     }
 
 };
+
+TEST_F(CameraClientBinderTest, CheckBinderCaptureRequest) {
+    sp<CaptureRequest> requestOriginal, requestParceled;
+    sp<IGraphicBufferProducer> gbProducer;
+    sp<IGraphicBufferConsumer> gbConsumer;
+    BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
+    sp<Surface> surface(new Surface(gbProducer, /*controlledByApp*/false));
+    Vector<sp<Surface>> surfaceList;
+    surfaceList.push_back(surface);
+    std::string physicalDeviceId1 = "0";
+    std::string physicalDeviceId2 = "1";
+    CameraMetadata physicalDeviceSettings1, physicalDeviceSettings2;
+    uint8_t intent1 = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
+    uint8_t intent2 = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
+    EXPECT_EQ(OK, physicalDeviceSettings1.update(ANDROID_CONTROL_CAPTURE_INTENT, &intent1, 1));
+    EXPECT_EQ(OK, physicalDeviceSettings2.update(ANDROID_CONTROL_CAPTURE_INTENT, &intent2, 1));
+
+    requestParceled = new CaptureRequest();
+    Parcel p;
+    EXPECT_TRUE(requestParceled->readFromParcel(&p) != OK);
+    p.writeInt32(0);
+    p.setDataPosition(0);
+    EXPECT_TRUE(requestParceled->readFromParcel(&p) != OK);
+    p.freeData();
+    p.writeInt32(-1);
+    p.setDataPosition(0);
+    EXPECT_TRUE(requestParceled->readFromParcel(&p) != OK);
+    p.freeData();
+    p.writeInt32(1);
+    p.setDataPosition(0);
+    EXPECT_TRUE(requestParceled->readFromParcel(&p) != OK);
+
+    requestOriginal = new CaptureRequest();
+    requestOriginal->mPhysicalCameraSettings.push_back({physicalDeviceId1,
+            physicalDeviceSettings1});
+    requestOriginal->mPhysicalCameraSettings.push_back({physicalDeviceId2,
+            physicalDeviceSettings2});
+    requestOriginal->mSurfaceList.push_back(surface);
+    requestOriginal->mIsReprocess = false;
+    requestOriginal->mSurfaceConverted = false;
+
+    p.freeData();
+    EXPECT_TRUE(requestOriginal->writeToParcel(&p) == OK);
+    p.setDataPosition(0);
+    EXPECT_TRUE(requestParceled->readFromParcel(&p) == OK);
+    EXPECT_EQ(requestParceled->mIsReprocess, false);
+    EXPECT_FALSE(requestParceled->mSurfaceList.empty());
+    EXPECT_EQ(2u, requestParceled->mPhysicalCameraSettings.size());
+    auto it = requestParceled->mPhysicalCameraSettings.begin();
+    EXPECT_EQ(physicalDeviceId1, it->id);
+    EXPECT_TRUE(it->settings.exists(ANDROID_CONTROL_CAPTURE_INTENT));
+    auto entry = it->settings.find(ANDROID_CONTROL_CAPTURE_INTENT);
+    EXPECT_EQ(entry.data.u8[0], intent1);
+    it++;
+    EXPECT_EQ(physicalDeviceId2, it->id);
+    EXPECT_TRUE(it->settings.exists(ANDROID_CONTROL_CAPTURE_INTENT));
+    entry = it->settings.find(ANDROID_CONTROL_CAPTURE_INTENT);
+    EXPECT_EQ(entry.data.u8[0], intent2);
+};
diff --git a/camera/tests/VendorTagDescriptorTests.cpp b/camera/tests/VendorTagDescriptorTests.cpp
index 75cfb73..0ee358d 100644
--- a/camera/tests/VendorTagDescriptorTests.cpp
+++ b/camera/tests/VendorTagDescriptorTests.cpp
@@ -142,6 +142,7 @@
     EXPECT_EQ(OK, vDescOriginal->writeToParcel(&p));
     p.setDataPosition(0);
 
+    vDescParceled = new VendorTagDescriptor();
     ASSERT_EQ(OK, vDescParceled->readFromParcel(&p));
 
     // Ensure consistent tag count
diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp
index 67017eb..fc24646 100644
--- a/cmds/stagefright/audioloop.cpp
+++ b/cmds/stagefright/audioloop.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#define LOG_NDEBUG 0
+#define LOG_TAG "audioloop"
+#include <utils/Log.h>
+
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -29,7 +33,6 @@
 #include <media/stagefright/AudioSource.h>
 #include <media/stagefright/MediaCodecSource.h>
 #include <media/stagefright/MediaDefs.h>
-#include <media/stagefright/MetaData.h>
 #include <media/stagefright/SimpleDecodingSource.h>
 #include "SineSource.h"
 
@@ -37,11 +40,13 @@
 
 static void usage(const char* name)
 {
-    fprintf(stderr, "Usage: %s [-d du.ration] [-m] [-w] [<output-file>]\n", name);
+    fprintf(stderr, "Usage: %s [-d du.ration] [-m] [-w] [-N name] [<output-file>]\n", name);
     fprintf(stderr, "Encodes either a sine wave or microphone input to AMR format\n");
     fprintf(stderr, "    -d    duration in seconds, default 5 seconds\n");
     fprintf(stderr, "    -m    use microphone for input, default sine source\n");
     fprintf(stderr, "    -w    use AMR wideband (default narrowband)\n");
+    fprintf(stderr, "    -N    name of the encoder; must be set with -M\n");
+    fprintf(stderr, "    -M    media type of the encoder; must be set with -N\n");
     fprintf(stderr, "    <output-file> output file for AMR encoding,"
             " if unspecified, decode to speaker.\n");
 }
@@ -54,8 +59,10 @@
     bool outputWBAMR = false;
     bool playToSpeaker = true;
     const char* fileOut = NULL;
+    AString name;
+    AString mediaType;
     int ch;
-    while ((ch = getopt(argc, argv, "d:mw")) != -1) {
+    while ((ch = getopt(argc, argv, "d:mwN:M:")) != -1) {
         switch (ch) {
         case 'd':
             duration = atoi(optarg);
@@ -66,6 +73,12 @@
         case 'w':
             outputWBAMR = true;
             break;
+        case 'N':
+            name.setTo(optarg);
+            break;
+        case 'M':
+            mediaType.setTo(optarg);
+            break;
         default:
             usage(argv[0]);
             return -1;
@@ -76,8 +89,18 @@
     if (argc == 1) {
         fileOut = argv[0];
     }
-    const int32_t kSampleRate = outputWBAMR ? 16000 : 8000;
-    const int32_t kBitRate = outputWBAMR ? 16000 : 8000;
+    if ((name.empty() && !mediaType.empty()) || (!name.empty() && mediaType.empty())) {
+        fprintf(stderr, "-N and -M must be set together\n");
+        usage(argv[0]);
+        return -1;
+    }
+    if (!name.empty() && fileOut != NULL) {
+        fprintf(stderr, "-N and -M cannot be used with <output file>\n");
+        usage(argv[0]);
+        return -1;
+    }
+    int32_t sampleRate = !name.empty() ? 44100 : outputWBAMR ? 16000 : 8000;
+    int32_t bitRate = sampleRate;
 
     android::ProcessState::self()->startThreadPool();
     sp<MediaSource> source;
@@ -87,22 +110,27 @@
         source = new AudioSource(
                 AUDIO_SOURCE_MIC,
                 String16(),
-                kSampleRate,
+                sampleRate,
                 channels);
     } else {
         // use a sine source at 500 hz.
-        source = new SineSource(kSampleRate, channels);
+        source = new SineSource(sampleRate, channels);
     }
 
     sp<AMessage> meta = new AMessage;
-    meta->setString(
-            "mime",
-            outputWBAMR ? MEDIA_MIMETYPE_AUDIO_AMR_WB
-                    : MEDIA_MIMETYPE_AUDIO_AMR_NB);
+    if (name.empty()) {
+        meta->setString(
+                "mime",
+                outputWBAMR ? MEDIA_MIMETYPE_AUDIO_AMR_WB
+                        : MEDIA_MIMETYPE_AUDIO_AMR_NB);
+    } else {
+        meta->setString("mime", mediaType);
+        meta->setString("testing-name", name);
+    }
 
     meta->setInt32("channel-count", channels);
-    meta->setInt32("sample-rate", kSampleRate);
-    meta->setInt32("bitrate", kBitRate);
+    meta->setInt32("sample-rate", sampleRate);
+    meta->setInt32("bitrate", bitRate);
     int32_t maxInputSize;
     if (source->getFormat()->findInt32(kKeyMaxInputSize, &maxInputSize)) {
         meta->setInt32("max-input-size", maxInputSize);
@@ -131,13 +159,14 @@
         sp<MediaSource> decoder = SimpleDecodingSource::Create(encoder);
 
         if (playToSpeaker) {
-            AudioPlayer *player = new AudioPlayer(NULL);
-            player->setSource(decoder);
-            player->start();
+            AudioPlayer player(NULL);
+            player.setSource(decoder);
+            player.start();
             sleep(duration);
 
+ALOGI("Line: %d", __LINE__);
             decoder.clear(); // must clear |decoder| otherwise delete player will hang.
-            delete player; // there is no player->stop()...
+ALOGI("Line: %d", __LINE__);
         } else {
             CHECK_EQ(decoder->start(), (status_t)OK);
             MediaBuffer* buffer;
@@ -151,6 +180,7 @@
             }
             CHECK_EQ(decoder->stop(), (status_t)OK);
         }
+ALOGI("Line: %d", __LINE__);
     }
 
     return 0;
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 0c14201..2e8da4b 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -2,7 +2,9 @@
 // libmediadrm
 //
 
-cc_library_shared {
+// TODO: change it back to cc_library_shared when MediaPlayer2 switches to
+// using NdkMediaDrm, instead of MediaDrm.java.
+cc_library {
     name: "libmediadrm",
 
 
@@ -34,6 +36,7 @@
         "libstagefright_foundation",
         "libutils",
         "android.hardware.drm@1.0",
+        "android.hardware.drm@1.1",
         "libhidlallocatorutils",
         "libhidlbase",
         "libhidltransport",
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index bc37557..e51ec4d 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -37,17 +37,14 @@
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MediaErrors.h>
 
-using ::android::hardware::drm::V1_0::EventType;
-using ::android::hardware::drm::V1_0::IDrmFactory;
-using ::android::hardware::drm::V1_0::IDrmPlugin;
-using ::android::hardware::drm::V1_0::KeyedVector;
-using ::android::hardware::drm::V1_0::KeyRequestType;
-using ::android::hardware::drm::V1_0::KeyStatus;
-using ::android::hardware::drm::V1_0::KeyStatusType;
-using ::android::hardware::drm::V1_0::KeyType;
-using ::android::hardware::drm::V1_0::KeyValue;
-using ::android::hardware::drm::V1_0::SecureStop;
-using ::android::hardware::drm::V1_0::Status;
+using drm::V1_0::KeyedVector;
+using drm::V1_0::KeyStatusType;
+using drm::V1_0::KeyType;
+using drm::V1_0::KeyValue;
+using drm::V1_1::HdcpLevel;;
+using drm::V1_0::SecureStop;
+using drm::V1_1::SecurityLevel;
+using drm::V1_0::Status;
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
@@ -58,6 +55,8 @@
 
 namespace android {
 
+#define INIT_CHECK() {if (mInitCheck != OK) return mInitCheck;}
+
 static inline int getCallingPid() {
     return IPCThreadState::self()->getCallingPid();
 }
@@ -89,6 +88,42 @@
     return hidl_string(string.string());
 }
 
+static DrmPlugin::SecurityLevel toSecurityLevel(SecurityLevel level) {
+    switch(level) {
+    case SecurityLevel::SW_SECURE_CRYPTO:
+        return DrmPlugin::kSecurityLevelSwSecureCrypto;
+    case SecurityLevel::SW_SECURE_DECODE:
+        return DrmPlugin::kSecurityLevelSwSecureDecode;
+    case SecurityLevel::HW_SECURE_CRYPTO:
+        return DrmPlugin::kSecurityLevelHwSecureCrypto;
+    case SecurityLevel::HW_SECURE_DECODE:
+        return DrmPlugin::kSecurityLevelHwSecureDecode;
+    case SecurityLevel::HW_SECURE_ALL:
+        return DrmPlugin::kSecurityLevelHwSecureAll;
+    default:
+        return DrmPlugin::kSecurityLevelUnknown;
+    }
+}
+
+static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel level) {
+    switch(level) {
+    case HdcpLevel::HDCP_NONE:
+        return DrmPlugin::kHdcpNone;
+    case HdcpLevel::HDCP_V1:
+        return DrmPlugin::kHdcpV1;
+    case HdcpLevel::HDCP_V2:
+        return DrmPlugin::kHdcpV2;
+    case HdcpLevel::HDCP_V2_1:
+        return DrmPlugin::kHdcpV2_1;
+    case HdcpLevel::HDCP_V2_2:
+        return DrmPlugin::kHdcpV2_2;
+    case HdcpLevel::HDCP_NO_OUTPUT:
+        return DrmPlugin::kHdcpNoOutput;
+    default:
+        return DrmPlugin::kHdcpLevelUnknown;
+    }
+}
+
 
 static ::KeyedVector toHidlKeyedVector(const KeyedVector<String8, String8>&
         keyedVector) {
@@ -192,6 +227,7 @@
 DrmHal::DrmHal()
    : mDrmSessionClient(new DrmSessionClient(this)),
      mFactories(makeDrmFactories()),
+     mOpenSessionCounter("/drm/mediadrm/open_session", "status"),
      mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT) {
 }
 
@@ -407,6 +443,7 @@
     for (size_t i = 0; i < mFactories.size(); i++) {
         if (mFactories[i]->isCryptoSchemeSupported(uuid)) {
             mPlugin = makeDrmPlugin(mFactories[i], uuid, appPackageName);
+            mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin);
         }
     }
 
@@ -425,9 +462,7 @@
 
 status_t DrmHal::destroyPlugin() {
     Mutex::Autolock autoLock(mLock);
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     closeOpenSessions();
     reportMetrics();
@@ -445,10 +480,7 @@
 
 status_t DrmHal::openSession(Vector<uint8_t> &sessionId) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     status_t  err = UNKNOWN_ERROR;
 
@@ -486,15 +518,14 @@
                 mDrmSessionClient, sessionId);
         mOpenSessions.push(sessionId);
     }
+
+    mOpenSessionCounter.Increment(err);
     return err;
 }
 
 status_t DrmHal::closeSession(Vector<uint8_t> const &sessionId) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     Return<Status> status = mPlugin->closeSession(toHidlVec(sessionId));
     if (status.isOk()) {
@@ -519,10 +550,7 @@
         String8> const &optionalParameters, Vector<uint8_t> &request,
         String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -541,23 +569,63 @@
 
     status_t err = UNKNOWN_ERROR;
 
+    if (mPluginV1_1 != NULL) {
+        Return<void> hResult =
+            mPluginV1_1->getKeyRequest_1_1(
+                toHidlVec(sessionId), toHidlVec(initData),
+                toHidlString(mimeType), hKeyType, hOptionalParameters,
+                [&](Status status, const hidl_vec<uint8_t>& hRequest,
+                    drm::V1_1::KeyRequestType hKeyRequestType,
+                    const hidl_string& hDefaultUrl) {
+
+            if (status == Status::OK) {
+                request = toVector(hRequest);
+                defaultUrl = toString8(hDefaultUrl);
+
+                switch (hKeyRequestType) {
+                    case drm::V1_1::KeyRequestType::INITIAL:
+                        *keyRequestType = DrmPlugin::kKeyRequestType_Initial;
+                        break;
+                    case drm::V1_1::KeyRequestType::RENEWAL:
+                        *keyRequestType = DrmPlugin::kKeyRequestType_Renewal;
+                        break;
+                    case drm::V1_1::KeyRequestType::RELEASE:
+                        *keyRequestType = DrmPlugin::kKeyRequestType_Release;
+                        break;
+                    case drm::V1_1::KeyRequestType::NONE:
+                        *keyRequestType = DrmPlugin::kKeyRequestType_None;
+                        break;
+                    case drm::V1_1::KeyRequestType::UPDATE:
+                        *keyRequestType = DrmPlugin::kKeyRequestType_Update;
+                        break;
+                    default:
+                        *keyRequestType = DrmPlugin::kKeyRequestType_Unknown;
+                        break;
+                }
+                err = toStatusT(status);
+            }
+        });
+        return hResult.isOk() ? err : DEAD_OBJECT;
+    }
+
     Return<void> hResult = mPlugin->getKeyRequest(toHidlVec(sessionId),
             toHidlVec(initData), toHidlString(mimeType), hKeyType, hOptionalParameters,
             [&](Status status, const hidl_vec<uint8_t>& hRequest,
-                    KeyRequestType hKeyRequestType, const hidl_string& hDefaultUrl) {
+                    drm::V1_0::KeyRequestType hKeyRequestType,
+                    const hidl_string& hDefaultUrl) {
 
                 if (status == Status::OK) {
                     request = toVector(hRequest);
                     defaultUrl = toString8(hDefaultUrl);
 
                     switch (hKeyRequestType) {
-                    case KeyRequestType::INITIAL:
+                    case drm::V1_0::KeyRequestType::INITIAL:
                         *keyRequestType = DrmPlugin::kKeyRequestType_Initial;
                         break;
-                    case KeyRequestType::RENEWAL:
+                    case drm::V1_0::KeyRequestType::RENEWAL:
                         *keyRequestType = DrmPlugin::kKeyRequestType_Renewal;
                         break;
-                    case KeyRequestType::RELEASE:
+                    case drm::V1_0::KeyRequestType::RELEASE:
                         *keyRequestType = DrmPlugin::kKeyRequestType_Release;
                         break;
                     default:
@@ -574,10 +642,7 @@
 status_t DrmHal::provideKeyResponse(Vector<uint8_t> const &sessionId,
         Vector<uint8_t> const &response, Vector<uint8_t> &keySetId) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -598,10 +663,7 @@
 
 status_t DrmHal::removeKeys(Vector<uint8_t> const &keySetId) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     return toStatusT(mPlugin->removeKeys(toHidlVec(keySetId)));
 }
@@ -609,10 +671,7 @@
 status_t DrmHal::restoreKeys(Vector<uint8_t> const &sessionId,
         Vector<uint8_t> const &keySetId) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -623,10 +682,7 @@
 status_t DrmHal::queryKeyStatus(Vector<uint8_t> const &sessionId,
         KeyedVector<String8, String8> &infoMap) const {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -650,10 +706,7 @@
         String8 const &certAuthority, Vector<uint8_t> &request,
         String8 &defaultUrl) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     status_t err = UNKNOWN_ERROR;
 
@@ -675,10 +728,7 @@
 status_t DrmHal::provideProvisionResponse(Vector<uint8_t> const &response,
         Vector<uint8_t> &certificate, Vector<uint8_t> &wrappedKey) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     status_t err = UNKNOWN_ERROR;
 
@@ -698,10 +748,7 @@
 
 status_t DrmHal::getSecureStops(List<Vector<uint8_t>> &secureStops) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     status_t err = UNKNOWN_ERROR;
 
@@ -720,10 +767,7 @@
 
 status_t DrmHal::getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     status_t err = UNKNOWN_ERROR;
 
@@ -741,24 +785,141 @@
 
 status_t DrmHal::releaseSecureStops(Vector<uint8_t> const &ssRelease) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     return toStatusT(mPlugin->releaseSecureStop(toHidlVec(ssRelease)));
 }
 
 status_t DrmHal::releaseAllSecureStops() {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     return toStatusT(mPlugin->releaseAllSecureStops());
 }
 
+status_t DrmHal::getHdcpLevels(DrmPlugin::HdcpLevel *connected,
+            DrmPlugin::HdcpLevel *max) const {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    if (connected == NULL || max == NULL) {
+        return BAD_VALUE;
+    }
+    status_t err = UNKNOWN_ERROR;
+
+    if (mPluginV1_1 == NULL) {
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    *connected = DrmPlugin::kHdcpLevelUnknown;
+    *max = DrmPlugin::kHdcpLevelUnknown;
+
+    Return<void> hResult = mPluginV1_1->getHdcpLevels(
+            [&](Status status, const HdcpLevel& hConnected, const HdcpLevel& hMax) {
+                if (status == Status::OK) {
+                    *connected = toHdcpLevel(hConnected);
+                    *max = toHdcpLevel(hMax);
+                }
+                err = toStatusT(status);
+            }
+    );
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHal::getNumberOfSessions(uint32_t *open, uint32_t *max) const {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    if (open == NULL || max == NULL) {
+        return BAD_VALUE;
+    }
+    status_t err = UNKNOWN_ERROR;
+
+    *open = 0;
+    *max = 0;
+
+    if (mPluginV1_1 == NULL) {
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    Return<void> hResult = mPluginV1_1->getNumberOfSessions(
+            [&](Status status, uint32_t hOpen, uint32_t hMax) {
+                if (status == Status::OK) {
+                    *open = hOpen;
+                    *max = hMax;
+                }
+                err = toStatusT(status);
+            }
+    );
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHal::getSecurityLevel(Vector<uint8_t> const &sessionId,
+        DrmPlugin::SecurityLevel *level) const {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    if (level == NULL) {
+        return BAD_VALUE;
+    }
+    status_t err = UNKNOWN_ERROR;
+
+    if (mPluginV1_1 == NULL) {
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    *level = DrmPlugin::kSecurityLevelUnknown;
+
+    Return<void> hResult = mPluginV1_1->getSecurityLevel(toHidlVec(sessionId),
+            [&](Status status, SecurityLevel hLevel) {
+                if (status == Status::OK) {
+                    *level = toSecurityLevel(hLevel);
+                }
+                err = toStatusT(status);
+            }
+    );
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHal::setSecurityLevel(Vector<uint8_t> const &sessionId,
+        const DrmPlugin::SecurityLevel& level) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    if (mPluginV1_1 == NULL) {
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    SecurityLevel hSecurityLevel;
+
+    switch(level) {
+    case DrmPlugin::kSecurityLevelSwSecureCrypto:
+        hSecurityLevel = SecurityLevel::SW_SECURE_CRYPTO;
+        break;
+    case DrmPlugin::kSecurityLevelSwSecureDecode:
+        hSecurityLevel = SecurityLevel::SW_SECURE_DECODE;
+        break;
+    case DrmPlugin::kSecurityLevelHwSecureCrypto:
+        hSecurityLevel = SecurityLevel::HW_SECURE_CRYPTO;
+        break;
+    case DrmPlugin::kSecurityLevelHwSecureDecode:
+        hSecurityLevel = SecurityLevel::HW_SECURE_DECODE;
+        break;
+    case DrmPlugin::kSecurityLevelHwSecureAll:
+        hSecurityLevel = SecurityLevel::HW_SECURE_ALL;
+        break;
+    default:
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    Status status = mPluginV1_1->setSecurityLevel(toHidlVec(sessionId),
+            hSecurityLevel);
+    return toStatusT(status);
+}
+
 status_t DrmHal::getPropertyString(String8 const &name, String8 &value ) const {
     Mutex::Autolock autoLock(mLock);
     return getPropertyStringInternal(name, value);
@@ -767,10 +928,7 @@
 status_t DrmHal::getPropertyStringInternal(String8 const &name, String8 &value) const {
     // This function is internal to the class and should only be called while
     // mLock is already held.
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     status_t err = UNKNOWN_ERROR;
 
@@ -794,10 +952,7 @@
 status_t DrmHal::getPropertyByteArrayInternal(String8 const &name, Vector<uint8_t> &value ) const {
     // This function is internal to the class and should only be called while
     // mLock is already held.
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     status_t err = UNKNOWN_ERROR;
 
@@ -815,12 +970,9 @@
 
 status_t DrmHal::setPropertyString(String8 const &name, String8 const &value ) const {
     Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
 
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
-
-    Status status =  mPlugin->setPropertyString(toHidlString(name),
+    Status status = mPlugin->setPropertyString(toHidlString(name),
             toHidlString(value));
     return toStatusT(status);
 }
@@ -828,24 +980,40 @@
 status_t DrmHal::setPropertyByteArray(String8 const &name,
                                    Vector<uint8_t> const &value ) const {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     Status status = mPlugin->setPropertyByteArray(toHidlString(name),
             toHidlVec(value));
     return toStatusT(status);
 }
 
+status_t DrmHal::getMetrics(MediaAnalyticsItem* metrics) {
+    // TODO: Move mOpenSessionCounter and suffixes to a separate class
+    // that manages the collection of metrics and exporting them.
+    std::string success_count_name =
+        mOpenSessionCounter.metric_name() + "/ok/count";
+    std::string error_count_name =
+        mOpenSessionCounter.metric_name() + "/error/count";
+    mOpenSessionCounter.ExportValues(
+        [&] (status_t status, int64_t value) {
+            if (status == OK) {
+                metrics->setInt64(success_count_name.c_str(), value);
+            } else {
+                int64_t total_errors(0);
+                metrics->getInt64(error_count_name.c_str(), &total_errors);
+                metrics->setInt64(error_count_name.c_str(),
+                                  total_errors + value);
+                // TODO: Add support for exporting the list of error values.
+                // This probably needs to be added to MediaAnalyticsItem.
+            }
+        });
+    return OK;
+}
 
 status_t DrmHal::setCipherAlgorithm(Vector<uint8_t> const &sessionId,
                                  String8 const &algorithm) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -857,10 +1025,7 @@
 status_t DrmHal::setMacAlgorithm(Vector<uint8_t> const &sessionId,
                               String8 const &algorithm) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -873,10 +1038,7 @@
         Vector<uint8_t> const &keyId, Vector<uint8_t> const &input,
         Vector<uint8_t> const &iv, Vector<uint8_t> &output) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -899,10 +1061,7 @@
         Vector<uint8_t> const &keyId, Vector<uint8_t> const &input,
         Vector<uint8_t> const &iv, Vector<uint8_t> &output) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -925,10 +1084,7 @@
         Vector<uint8_t> const &keyId, Vector<uint8_t> const &message,
         Vector<uint8_t> &signature) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -951,10 +1107,7 @@
         Vector<uint8_t> const &keyId, Vector<uint8_t> const &message,
         Vector<uint8_t> const &signature, bool &match) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -979,10 +1132,7 @@
         String8 const &algorithm, Vector<uint8_t> const &message,
         Vector<uint8_t> const &wrappedKey, Vector<uint8_t> &signature) {
     Mutex::Autolock autoLock(mLock);
-
-    if (mInitCheck != OK) {
-        return mInitCheck;
-    }
+    INIT_CHECK();
 
     if (!checkPermission("android.permission.ACCESS_DRM_CERTIFICATES")) {
         return -EPERM;
@@ -1030,6 +1180,7 @@
     }
 }
 
+
 void DrmHal::reportMetrics() const
 {
     Vector<uint8_t> metrics;
diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp
index 8ff6e6a..e7417cc 100644
--- a/drm/libmediadrm/IDrm.cpp
+++ b/drm/libmediadrm/IDrm.cpp
@@ -46,6 +46,7 @@
     GET_PROPERTY_BYTE_ARRAY,
     SET_PROPERTY_STRING,
     SET_PROPERTY_BYTE_ARRAY,
+    GET_METRICS,
     SET_CIPHER_ALGORITHM,
     SET_MAC_ALGORITHM,
     ENCRYPT,
@@ -55,7 +56,11 @@
     VERIFY,
     SET_LISTENER,
     GET_SECURE_STOP,
-    RELEASE_ALL_SECURE_STOPS
+    RELEASE_ALL_SECURE_STOPS,
+    GET_HDCP_LEVELS,
+    GET_NUMBER_OF_SESSIONS,
+    GET_SECURITY_LEVEL,
+    SET_SECURITY_LEVEL,
 };
 
 struct BpDrm : public BpInterface<IDrm> {
@@ -350,6 +355,82 @@
         return reply.readInt32();
     }
 
+    virtual status_t getHdcpLevels(DrmPlugin::HdcpLevel *connected,
+            DrmPlugin::HdcpLevel *max) const {
+        Parcel data, reply;
+
+        if (connected == NULL || max == NULL) {
+            return BAD_VALUE;
+        }
+
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        status_t status = remote()->transact(GET_HDCP_LEVELS, data, &reply);
+        if (status != OK) {
+            return status;
+        }
+
+        *connected = static_cast<DrmPlugin::HdcpLevel>(reply.readInt32());
+        *max = static_cast<DrmPlugin::HdcpLevel>(reply.readInt32());
+        return reply.readInt32();
+    }
+
+    virtual status_t getNumberOfSessions(uint32_t *open, uint32_t *max) const {
+        Parcel data, reply;
+
+        if (open == NULL || max == NULL) {
+            return BAD_VALUE;
+        }
+
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        status_t status = remote()->transact(GET_NUMBER_OF_SESSIONS, data, &reply);
+        if (status != OK) {
+            return status;
+        }
+
+        *open = reply.readInt32();
+        *max = reply.readInt32();
+        return reply.readInt32();
+    }
+
+    virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
+            DrmPlugin::SecurityLevel *level) const {
+        Parcel data, reply;
+
+        if (level == NULL) {
+            return BAD_VALUE;
+        }
+
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, sessionId);
+        status_t status = remote()->transact(GET_SECURITY_LEVEL, data, &reply);
+        if (status != OK) {
+            return status;
+        }
+
+        *level = static_cast<DrmPlugin::SecurityLevel>(reply.readInt32());
+        return reply.readInt32();
+    }
+
+    virtual status_t setSecurityLevel(Vector<uint8_t> const &sessionId,
+            const DrmPlugin::SecurityLevel& level) {
+        Parcel data, reply;
+
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, sessionId);
+        data.writeInt32(static_cast<uint32_t>(level));
+
+        status_t status = remote()->transact(SET_SECURITY_LEVEL, data, &reply);
+        if (status != OK) {
+            return status;
+        }
+
+        return reply.readInt32();
+    }
+
     virtual status_t getPropertyByteArray(String8 const &name, Vector<uint8_t> &value) const {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
@@ -393,6 +474,18 @@
         return reply.readInt32();
     }
 
+    virtual status_t getMetrics(MediaAnalyticsItem *item) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        status_t status = remote()->transact(GET_METRICS, data, &reply);
+        if (status != OK) {
+            return status;
+        }
+
+        item->readFromParcel(reply);
+        return reply.readInt32();
+    }
 
     virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
                                         String8 const &algorithm) {
@@ -788,6 +881,53 @@
             return OK;
         }
 
+        case GET_HDCP_LEVELS:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            DrmPlugin::HdcpLevel connected = DrmPlugin::kHdcpLevelUnknown;
+            DrmPlugin::HdcpLevel max = DrmPlugin::kHdcpLevelUnknown;
+            status_t result = getHdcpLevels(&connected, &max);
+            reply->writeInt32(connected);
+            reply->writeInt32(max);
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case GET_NUMBER_OF_SESSIONS:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            uint32_t open = 0, max = 0;
+            status_t result = getNumberOfSessions(&open, &max);
+            reply->writeInt32(open);
+            reply->writeInt32(max);
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case GET_SECURITY_LEVEL:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId;
+            readVector(data, sessionId);
+            DrmPlugin::SecurityLevel level = DrmPlugin::kSecurityLevelUnknown;
+            status_t result = getSecurityLevel(sessionId, &level);
+            reply->writeInt32(level);
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case SET_SECURITY_LEVEL:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> sessionId;
+            readVector(data, sessionId);
+            DrmPlugin::SecurityLevel level =
+                    static_cast<DrmPlugin::SecurityLevel>(data.readInt32());
+            status_t result = setSecurityLevel(sessionId, level);
+            reply->writeInt32(result);
+            return OK;
+        }
+
         case GET_PROPERTY_STRING:
         {
             CHECK_INTERFACE(IDrm, data, reply);
@@ -829,6 +969,17 @@
             return OK;
         }
 
+        case GET_METRICS:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+
+            MediaAnalyticsItem item;
+            status_t result = getMetrics(&item);
+            item.writeToParcel(reply);
+            reply->writeInt32(result);
+            return OK;
+        }
+
         case SET_CIPHER_ALGORITHM:
         {
             CHECK_INTERFACE(IDrm, data, reply);
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
new file mode 100644
index 0000000..674d3eb
--- /dev/null
+++ b/drm/libmediadrm/tests/Android.bp
@@ -0,0 +1,12 @@
+// Build definitions for unit tests.
+
+cc_test {
+    name: "CounterMetric_test",
+    srcs: ["CounterMetric_test.cpp"],
+    shared_libs: ["libmediadrm"],
+    include_dirs: ["frameworks/av/include/media"],
+    cflags: [
+      "-Werror",
+      "-Wall",
+    ],
+}
diff --git a/drm/libmediadrm/tests/CounterMetric_test.cpp b/drm/libmediadrm/tests/CounterMetric_test.cpp
new file mode 100644
index 0000000..6bca0da
--- /dev/null
+++ b/drm/libmediadrm/tests/CounterMetric_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "CounterMetric.h"
+
+namespace android {
+
+/**
+ * Unit tests for the CounterMetric class.
+ */
+class CounterMetricTest : public ::testing::Test {
+};
+
+TEST_F(CounterMetricTest, IntDataTypeEmpty) {
+  CounterMetric<int> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<int, int64_t> values;
+
+  metric.ExportValues(
+      [&] (int attribute_value, int64_t value) {
+          values[attribute_value] = value;
+      });
+
+  EXPECT_TRUE(values.empty());
+}
+
+TEST_F(CounterMetricTest, IntDataType) {
+  CounterMetric<int> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<int, int64_t> values;
+
+  metric.Increment(7);
+  metric.Increment(8);
+  metric.Increment(8);
+
+  metric.ExportValues(
+      [&] (int attribute_value, int64_t value) {
+          values[attribute_value] = value;
+      });
+
+  ASSERT_EQ(2u, values.size());
+  EXPECT_EQ(1, values[7]);
+  EXPECT_EQ(2, values[8]);
+}
+
+TEST_F(CounterMetricTest, StringDataType) {
+  CounterMetric<std::string> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<std::string, int64_t> values;
+
+  metric.Increment("a");
+  metric.Increment("b");
+  metric.Increment("b");
+
+  metric.ExportValues(
+      [&] (std::string attribute_value, int64_t value) {
+          values[attribute_value] = value;
+      });
+
+  ASSERT_EQ(2u, values.size());
+  EXPECT_EQ(1, values["a"]);
+  EXPECT_EQ(2, values["b"]);
+}
+
+}  // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
index 62bc86f..4fa42e5 100644
--- a/drm/mediadrm/plugins/clearkey/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
@@ -133,6 +133,35 @@
         return android::ERROR_DRM_CANNOT_HANDLE;
     }
 
+    virtual status_t getHdcpLevels(HdcpLevel *connectedLevel,
+            HdcpLevel *maxLevel) const {
+        UNUSED(connectedLevel);
+        UNUSED(maxLevel);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+
+    virtual status_t getNumberOfSessions(uint32_t *currentSessions,
+            uint32_t *maxSessions) const {
+        UNUSED(currentSessions);
+        UNUSED(maxSessions);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
+            SecurityLevel *level) const {
+        UNUSED(sessionId);
+        UNUSED(level);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    virtual status_t setSecurityLevel(Vector<uint8_t> const &sessionId,
+            const SecurityLevel& level) {
+        UNUSED(sessionId);
+        UNUSED(level);
+        return android::ERROR_DRM_CANNOT_HANDLE;
+    }
+
     virtual status_t getPropertyString(
             const String8& name, String8& value) const;
 
diff --git a/include/media/CounterMetric.h b/include/media/CounterMetric.h
new file mode 120000
index 0000000..baba043
--- /dev/null
+++ b/include/media/CounterMetric.h
@@ -0,0 +1 @@
+../../media/libmedia/include/media/CounterMetric.h
\ No newline at end of file
diff --git a/media/extractors/mp4/SampleIterator.cpp b/media/extractors/mp4/SampleIterator.cpp
index 78cc691..c194397 100644
--- a/media/extractors/mp4/SampleIterator.cpp
+++ b/media/extractors/mp4/SampleIterator.cpp
@@ -244,13 +244,14 @@
     switch (mTable->mSampleSizeFieldSize) {
         case 32:
         {
+            uint32_t x;
             if (mTable->mDataSource->readAt(
                         mTable->mSampleSizeOffset + 12 + 4 * sampleIndex,
-                        size, sizeof(*size)) < (ssize_t)sizeof(*size)) {
+                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
                 return ERROR_IO;
             }
 
-            *size = ntohl(*size);
+            *size = ntohl(x);
             break;
         }
 
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 5da8114..e40a6cd 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -510,7 +510,9 @@
  * This could, for example, affect which microphones are used and how the
  * recorded data is processed.
  *
- * The default, if you do not call this function, is AAUDIO_INPUT_PRESET_GENERIC.
+ * The default, if you do not call this function, is AAUDIO_INPUT_PRESET_VOICE_RECOGNITION.
+ * That is because VOICE_RECOGNITION is the preset with the lowest latency
+ * on many platforms.
  *
  * @param builder reference provided by AAudio_createStreamBuilder()
  * @param inputPreset the desired configuration for recording
diff --git a/media/libaaudio/src/binding/AAudioServiceMessage.h b/media/libaaudio/src/binding/AAudioServiceMessage.h
index 9779f24..3981454 100644
--- a/media/libaaudio/src/binding/AAudioServiceMessage.h
+++ b/media/libaaudio/src/binding/AAudioServiceMessage.h
@@ -36,7 +36,6 @@
     AAUDIO_SERVICE_EVENT_PAUSED,
     AAUDIO_SERVICE_EVENT_STOPPED,
     AAUDIO_SERVICE_EVENT_FLUSHED,
-    AAUDIO_SERVICE_EVENT_CLOSED,
     AAUDIO_SERVICE_EVENT_DISCONNECTED,
     AAUDIO_SERVICE_EVENT_VOLUME,
     AAUDIO_SERVICE_EVENT_XRUN
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 4980e97..b611160 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -340,8 +340,13 @@
     }
 }
 
-aaudio_result_t AudioStreamInternal::requestStopInternal()
+aaudio_result_t AudioStreamInternal::requestStop()
 {
+    aaudio_result_t result = stopCallback();
+    if (result != AAUDIO_OK) {
+        return result;
+    }
+
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
         ALOGE("requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
               mServiceStreamHandle);
@@ -355,16 +360,6 @@
     return mServiceInterface.stopStream(mServiceStreamHandle);
 }
 
-aaudio_result_t AudioStreamInternal::requestStop()
-{
-    aaudio_result_t result = stopCallback();
-    if (result != AAUDIO_OK) {
-        return result;
-    }
-    result = requestStopInternal();
-    return result;
-}
-
 aaudio_result_t AudioStreamInternal::registerThread() {
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
         ALOGE("registerThread() mServiceStreamHandle invalid");
@@ -483,10 +478,6 @@
                 onFlushFromServer();
             }
             break;
-        case AAUDIO_SERVICE_EVENT_CLOSED:
-            ALOGD("%s - got AAUDIO_SERVICE_EVENT_CLOSED", __func__);
-            setState(AAUDIO_STREAM_STATE_CLOSED);
-            break;
         case AAUDIO_SERVICE_EVENT_DISCONNECTED:
             // Prevent hardware from looping on old data and making buzzing sounds.
             if (getDirection() == AAUDIO_DIRECTION_OUTPUT) {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 117756d..0f54f8c 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -121,8 +121,6 @@
 
     aaudio_result_t processCommands();
 
-    aaudio_result_t requestStopInternal();
-
     aaudio_result_t stopCallback();
 
     virtual void advanceClientToMatchServerPosition() = 0;
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 5de6a11..5660c1b 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -38,9 +38,12 @@
 
 AudioStreamInternalPlay::~AudioStreamInternalPlay() {}
 
-
-aaudio_result_t AudioStreamInternalPlay::requestPauseInternal()
+aaudio_result_t AudioStreamInternalPlay::requestPause()
 {
+    aaudio_result_t result = stopCallback();
+    if (result != AAUDIO_OK) {
+        return result;
+    }
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
         ALOGE("AudioStreamInternal::requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
               mServiceStreamHandle);
@@ -53,16 +56,6 @@
     return mServiceInterface.pauseStream(mServiceStreamHandle);
 }
 
-aaudio_result_t AudioStreamInternalPlay::requestPause()
-{
-    aaudio_result_t result = stopCallback();
-    if (result != AAUDIO_OK) {
-        return result;
-    }
-    result = requestPauseInternal();
-    return result;
-}
-
 aaudio_result_t AudioStreamInternalPlay::requestFlush() {
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
         ALOGE("AudioStreamInternal::requestFlush() mServiceStreamHandle invalid = 0x%08X",
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index d5c1b1e..04e4a62 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -37,6 +37,16 @@
 
     aaudio_result_t requestFlush() override;
 
+    bool isFlushSupported() const override {
+        // Only implement FLUSH for OUTPUT streams.
+        return true;
+    }
+
+    bool isPauseSupported() const override {
+        // Only implement PAUSE for OUTPUT streams.
+        return true;
+    }
+
     aaudio_result_t write(const void *buffer,
                           int32_t numFrames,
                           int64_t timeoutNanoseconds) override;
@@ -52,8 +62,6 @@
 
 protected:
 
-    aaudio_result_t requestPauseInternal();
-
     void advanceClientToMatchServerPosition() override;
 
     void onFlushFromServer() override;
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 9645ea8..d56701b 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -17,7 +17,7 @@
 
 #define LOG_TAG "AAudioStreamParameters"
 #include <utils/Log.h>
-#include <hardware/audio.h>
+#include <system/audio.h>
 
 #include "AAudioStreamParameters.h"
 
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 82c0667..c4465fd 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -93,7 +93,7 @@
     }
     mInputPreset = builder.getInputPreset();
     if (mInputPreset == AAUDIO_UNSPECIFIED) {
-        mInputPreset = AAUDIO_INPUT_PRESET_GENERIC;
+        mInputPreset = AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
     }
 
     // callbacks
@@ -130,29 +130,106 @@
 }
 
 aaudio_result_t AudioStream::safePause() {
+    if (!isPauseSupported()) {
+        return AAUDIO_ERROR_UNIMPLEMENTED;
+    }
+
     std::lock_guard<std::mutex> lock(mStreamLock);
     if (collidesWithCallback()) {
         ALOGE("%s cannot be called from a callback!", __func__);
         return AAUDIO_ERROR_INVALID_STATE;
     }
+
+    switch (getState()) {
+        // Proceed with pausing.
+        case AAUDIO_STREAM_STATE_STARTING:
+        case AAUDIO_STREAM_STATE_STARTED:
+        case AAUDIO_STREAM_STATE_DISCONNECTED:
+            break;
+
+            // Transition from one inactive state to another.
+        case AAUDIO_STREAM_STATE_OPEN:
+        case AAUDIO_STREAM_STATE_STOPPED:
+        case AAUDIO_STREAM_STATE_FLUSHED:
+            setState(AAUDIO_STREAM_STATE_PAUSED);
+            return AAUDIO_OK;
+
+            // Redundant?
+        case AAUDIO_STREAM_STATE_PAUSING:
+        case AAUDIO_STREAM_STATE_PAUSED:
+            return AAUDIO_OK;
+
+            // Don't interfere with transitional states or when closed.
+        case AAUDIO_STREAM_STATE_STOPPING:
+        case AAUDIO_STREAM_STATE_FLUSHING:
+        case AAUDIO_STREAM_STATE_CLOSING:
+        case AAUDIO_STREAM_STATE_CLOSED:
+        default:
+            ALOGW("safePause() stream not running, state = %s",
+                  AAudio_convertStreamStateToText(getState()));
+            return AAUDIO_ERROR_INVALID_STATE;
+    }
+
     return requestPause();
 }
 
 aaudio_result_t AudioStream::safeFlush() {
+    if (!isFlushSupported()) {
+        ALOGE("flush not supported for this stream");
+        return AAUDIO_ERROR_UNIMPLEMENTED;
+    }
+
     std::lock_guard<std::mutex> lock(mStreamLock);
     if (collidesWithCallback()) {
-        ALOGE("%s cannot be called from a callback!", __func__);
+        ALOGE("stream cannot be flushed from a callback!");
         return AAUDIO_ERROR_INVALID_STATE;
     }
+
+    aaudio_result_t result = AAudio_isFlushAllowed(getState());
+    if (result != AAUDIO_OK) {
+        return result;
+    }
+
     return requestFlush();
 }
 
 aaudio_result_t AudioStream::safeStop() {
     std::lock_guard<std::mutex> lock(mStreamLock);
     if (collidesWithCallback()) {
-        ALOGE("%s cannot be called from a callback!", __func__);
+        ALOGE("stream cannot be stopped from a callback!");
         return AAUDIO_ERROR_INVALID_STATE;
     }
+
+    switch (getState()) {
+        // Proceed with stopping.
+        case AAUDIO_STREAM_STATE_STARTING:
+        case AAUDIO_STREAM_STATE_STARTED:
+        case AAUDIO_STREAM_STATE_DISCONNECTED:
+            break;
+
+        // Transition from one inactive state to another.
+        case AAUDIO_STREAM_STATE_OPEN:
+        case AAUDIO_STREAM_STATE_PAUSED:
+        case AAUDIO_STREAM_STATE_FLUSHED:
+            setState(AAUDIO_STREAM_STATE_STOPPED);
+            return AAUDIO_OK;
+
+        // Redundant?
+        case AAUDIO_STREAM_STATE_STOPPING:
+        case AAUDIO_STREAM_STATE_STOPPED:
+            return AAUDIO_OK;
+
+        // Don't interfere with transitional states or when closed.
+        case AAUDIO_STREAM_STATE_PAUSING:
+        case AAUDIO_STREAM_STATE_FLUSHING:
+        case AAUDIO_STREAM_STATE_CLOSING:
+        case AAUDIO_STREAM_STATE_CLOSED:
+        default:
+            ALOGW("requestStop() stream not running, state = %s",
+                  AAudio_convertStreamStateToText(getState()));
+            return AAUDIO_ERROR_INVALID_STATE;
+    }
+
     return requestStop();
 }
 
@@ -238,6 +315,7 @@
     if (err != 0) {
         return AAudioConvert_androidToAAudioResult(-errno);
     } else {
+        // TODO Use AAudioThread or maybe AndroidThread
         // Name the thread with an increasing index, "AAudio_#", for debugging.
         static std::atomic<uint32_t> nextThreadIndex{1};
         char name[16]; // max length for a pthread_name
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 42b585f..c0db0f9 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -73,6 +73,24 @@
      */
     virtual aaudio_result_t requestStart() = 0;
 
+    /**
+     * Check the state to see if Pause if currently legal.
+     *
+     * @param result pointer to return code
+     * @return true if OK to continue, if false then return result
+     */
+    bool checkPauseStateTransition(aaudio_result_t *result);
+
+    virtual bool isFlushSupported() const {
+        // Only implement FLUSH for OUTPUT streams.
+        return false;
+    }
+
+    virtual bool isPauseSupported() const {
+        // Only implement PAUSE for OUTPUT streams.
+        return false;
+    }
+
     virtual aaudio_result_t requestPause()
     {
         // Only implement this for OUTPUT streams.
@@ -341,11 +359,13 @@
         return mPlayerBase->getResult();
     }
 
+    // Pass pause request through PlayerBase for tracking.
     aaudio_result_t systemPause() {
         mPlayerBase->pause();
         return mPlayerBase->getResult();
     }
 
+    // Pass stop request through PlayerBase for tracking.
     aaudio_result_t systemStop() {
         mPlayerBase->stop();
         return mPlayerBase->getResult();
@@ -452,7 +472,14 @@
     }
 
     void setState(aaudio_stream_state_t state) {
-        mState = state;
+        if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+            ; // CLOSED is a final state
+        } else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
+                && state != AAUDIO_STREAM_STATE_CLOSED) {
+            ; // Once DISCONNECTED, we can only move to CLOSED state.
+        } else {
+            mState = state;
+        }
     }
 
     void setDeviceId(int32_t deviceId) {
@@ -515,9 +542,9 @@
     aaudio_stream_state_t       mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
     aaudio_performance_mode_t   mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
 
-    aaudio_usage_t              mUsage           = AAUDIO_USAGE_MEDIA;
-    aaudio_content_type_t       mContentType     = AAUDIO_CONTENT_TYPE_MUSIC;
-    aaudio_input_preset_t       mInputPreset     = AAUDIO_INPUT_PRESET_GENERIC;
+    aaudio_usage_t              mUsage           = AAUDIO_UNSPECIFIED;
+    aaudio_content_type_t       mContentType     = AAUDIO_UNSPECIFIED;
+    aaudio_input_preset_t       mInputPreset     = AAUDIO_UNSPECIFIED;
 
     int32_t                     mSessionId = AAUDIO_UNSPECIFIED;
 
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index c5dfb7c..3352b33 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -86,10 +86,15 @@
             // AudioRecord::Buffer
             // TODO define our own AudioBuffer and pass it from the subclasses.
             AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
-            if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED || !mCallbackEnabled.load()) {
+            if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
+                ALOGW("processCallbackCommon() data, stream disconnected");
+                audioBuffer->size = SIZE_STOP_CALLBACKS;
+            } else if (!mCallbackEnabled.load()) {
+                ALOGW("processCallbackCommon() stopping because callback disabled");
                 audioBuffer->size = SIZE_STOP_CALLBACKS;
             } else {
                 if (audioBuffer->frameCount == 0) {
+                    ALOGW("processCallbackCommon() data, frameCount is zero");
                     return;
                 }
 
@@ -106,7 +111,7 @@
                 if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
                     audioBuffer->size = audioBuffer->frameCount * getBytesPerFrame();
                 } else { // STOP or invalid result
-                    ALOGW("%s() stop stream by faking an error", __func__);
+                    ALOGW("%s() callback requested stop, fake an error", __func__);
                     audioBuffer->size = SIZE_STOP_CALLBACKS;
                     // Disable the callback just in case AudioFlinger keeps trying to call us.
                     mCallbackEnabled.store(false);
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index 6a506b3..494edbc 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -121,9 +121,6 @@
 
     void forceDisconnect(bool errorCallbackEnabled = true);
 
-    void onStart() { mCallbackEnabled.store(true); }
-    void onStop() { mCallbackEnabled.store(false); }
-
     int64_t incrementFramesWritten(int32_t frames) {
         return mFramesWritten.increment(frames);
     }
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 61a0f8a..f7ae7d7 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -244,11 +244,13 @@
         return AAudioConvert_androidToAAudioResult(err);
     }
 
+    // Enable callback before starting AudioTrack to avoid shutting
+    // down because of a race condition.
+    mCallbackEnabled.store(true);
     err = mAudioRecord->start();
     if (err != OK) {
         return AAudioConvert_androidToAAudioResult(err);
     } else {
-        onStart();
         setState(AAUDIO_STREAM_STATE_STARTING);
     }
     return AAUDIO_OK;
@@ -258,11 +260,11 @@
     if (mAudioRecord.get() == nullptr) {
         return AAUDIO_ERROR_INVALID_STATE;
     }
-    onStop();
     setState(AAUDIO_STREAM_STATE_STOPPING);
     incrementFramesWritten(getFramesRead() - getFramesWritten()); // TODO review
     mTimestampPosition.set(getFramesRead());
     mAudioRecord->stop();
+    mCallbackEnabled.store(false);
     mFramesRead.reset32();
     mTimestampPosition.reset32();
     // Pass false to prevent errorCallback from being called after disconnect
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 52c7822..ee069ee 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -271,11 +271,13 @@
         return AAudioConvert_androidToAAudioResult(err);
     }
 
+    // Enable callback before starting AudioTrack to avoid shutting
+    // down because of a race condition.
+    mCallbackEnabled.store(true);
     err = mAudioTrack->start();
     if (err != OK) {
         return AAudioConvert_androidToAAudioResult(err);
     } else {
-        onStart();
         setState(AAUDIO_STREAM_STATE_STARTING);
     }
     return AAUDIO_OK;
@@ -285,16 +287,11 @@
     if (mAudioTrack.get() == nullptr) {
         ALOGE("requestPause() no AudioTrack");
         return AAUDIO_ERROR_INVALID_STATE;
-    } else if (getState() != AAUDIO_STREAM_STATE_STARTING
-            && getState() != AAUDIO_STREAM_STATE_STARTED) {
-            // TODO What about DISCONNECTED?
-        ALOGE("requestPause(), called when state is %s",
-              AAudio_convertStreamStateToText(getState()));
-        return AAUDIO_ERROR_INVALID_STATE;
     }
-    onStop();
+
     setState(AAUDIO_STREAM_STATE_PAUSING);
     mAudioTrack->pause();
+    mCallbackEnabled.store(false);
     status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
     if (err != OK) {
         return AAudioConvert_androidToAAudioResult(err);
@@ -306,10 +303,8 @@
     if (mAudioTrack.get() == nullptr) {
         ALOGE("requestFlush() no AudioTrack");
         return AAUDIO_ERROR_INVALID_STATE;
-    } else if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
-        ALOGE("requestFlush() not paused");
-        return AAUDIO_ERROR_INVALID_STATE;
     }
+
     setState(AAUDIO_STREAM_STATE_FLUSHING);
     incrementFramesRead(getFramesWritten() - getFramesRead());
     mAudioTrack->flush();
@@ -323,13 +318,14 @@
         ALOGE("requestStop() no AudioTrack");
         return AAUDIO_ERROR_INVALID_STATE;
     }
-    onStop();
+
     setState(AAUDIO_STREAM_STATE_STOPPING);
     incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
     mTimestampPosition.set(getFramesWritten());
     mFramesWritten.reset32();
     mTimestampPosition.reset32();
     mAudioTrack->stop();
+    mCallbackEnabled.store(false);
     return checkForDisconnectRequest(false);;
 }
 
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index a871db4..68608de 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -48,6 +48,16 @@
     aaudio_result_t requestFlush() override;
     aaudio_result_t requestStop() override;
 
+    bool isFlushSupported() const override {
+        // Only implement FLUSH for OUTPUT streams.
+        return true;
+    }
+
+    bool isPauseSupported() const override {
+        // Only implement PAUSE for OUTPUT streams.
+        return true;
+    }
+
     aaudio_result_t getTimestamp(clockid_t clockId,
                                        int64_t *framePosition,
                                        int64_t *timeNanoseconds) override;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 2bee6e3..adc4904 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -341,7 +341,7 @@
     STATIC_ASSERT(AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION == AUDIO_SOURCE_VOICE_COMMUNICATION);
     STATIC_ASSERT(AAUDIO_INPUT_PRESET_UNPROCESSED == AUDIO_SOURCE_UNPROCESSED);
     if (preset == AAUDIO_UNSPECIFIED) {
-        preset = AAUDIO_INPUT_PRESET_GENERIC;
+        preset = AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
     }
     return (audio_source_t) preset; // same value
 }
@@ -441,3 +441,31 @@
     }
     return prop;
 }
+
+aaudio_result_t AAudio_isFlushAllowed(aaudio_stream_state_t state) {
+    aaudio_result_t result = AAUDIO_OK;
+    switch (state) {
+// Proceed with flushing.
+        case AAUDIO_STREAM_STATE_OPEN:
+        case AAUDIO_STREAM_STATE_PAUSED:
+        case AAUDIO_STREAM_STATE_STOPPED:
+        case AAUDIO_STREAM_STATE_FLUSHED:
+            break;
+
+// Transition from one inactive state to another.
+        case AAUDIO_STREAM_STATE_STARTING:
+        case AAUDIO_STREAM_STATE_STARTED:
+        case AAUDIO_STREAM_STATE_STOPPING:
+        case AAUDIO_STREAM_STATE_PAUSING:
+        case AAUDIO_STREAM_STATE_FLUSHING:
+        case AAUDIO_STREAM_STATE_CLOSING:
+        case AAUDIO_STREAM_STATE_CLOSED:
+        case AAUDIO_STREAM_STATE_DISCONNECTED:
+        default:
+            ALOGE("can only flush stream when PAUSED, OPEN or STOPPED, state = %s",
+                  AAudio_convertStreamStateToText(state));
+            result =  AAUDIO_ERROR_INVALID_STATE;
+            break;
+    }
+    return result;
+}
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index 0c59f6d..3673c34 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -23,7 +23,7 @@
 #include <sys/types.h>
 
 #include <utils/Errors.h>
-#include <hardware/audio.h>
+#include <system/audio.h>
 
 #include "aaudio/AAudio.h"
 
@@ -268,6 +268,14 @@
  */
 int32_t AAudioProperty_getHardwareBurstMinMicros();
 
+
+/**
+ * Is flush allowed for the given state?
+ * @param state
+ * @return AAUDIO_OK if allowed or an error
+ */
+aaudio_result_t AAudio_isFlushAllowed(aaudio_stream_state_t state);
+
 /**
  * Try a function f until it returns true.
  *
diff --git a/media/libaaudio/tests/test_attributes.cpp b/media/libaaudio/tests/test_attributes.cpp
index 9cbf113..b01af25 100644
--- a/media/libaaudio/tests/test_attributes.cpp
+++ b/media/libaaudio/tests/test_attributes.cpp
@@ -76,7 +76,7 @@
 
     aaudio_input_preset_t expectedPreset =
             (preset == DONT_SET || preset == AAUDIO_UNSPECIFIED)
-            ? AAUDIO_INPUT_PRESET_GENERIC // default
+            ? AAUDIO_INPUT_PRESET_VOICE_RECOGNITION // default
             : preset;
     EXPECT_EQ(expectedPreset, AAudioStream_getInputPreset(aaudioStream));
 
diff --git a/media/libaaudio/tests/test_various.cpp b/media/libaaudio/tests/test_various.cpp
index dc19985..4b065c9 100644
--- a/media/libaaudio/tests/test_various.cpp
+++ b/media/libaaudio/tests/test_various.cpp
@@ -26,7 +26,6 @@
 #include <gtest/gtest.h>
 #include <unistd.h>
 
-
 // Callback function that does nothing.
 aaudio_data_callback_result_t NoopDataCallbackProc(
         AAudioStream *stream,
@@ -45,73 +44,376 @@
 
 constexpr int64_t NANOS_PER_MILLISECOND = 1000 * 1000;
 
-//int foo() { // To fix Android Studio formatting when editing.
-TEST(test_various, aaudio_stop_when_open) {
+enum FunctionToCall {
+    CALL_START, CALL_STOP, CALL_PAUSE, CALL_FLUSH
+};
+
+void checkStateTransition(aaudio_performance_mode_t perfMode,
+                          aaudio_stream_state_t originalState,
+                          FunctionToCall functionToCall,
+                          aaudio_result_t expectedResult,
+                          aaudio_stream_state_t expectedState) {
     AAudioStreamBuilder *aaudioBuilder = nullptr;
     AAudioStream *aaudioStream = nullptr;
 
-// Use an AAudioStreamBuilder to contain requested parameters.
+    // Use an AAudioStreamBuilder to contain requested parameters.
     ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder));
 
-// Request stream properties.
+    // Request stream properties.
     AAudioStreamBuilder_setDataCallback(aaudioBuilder, NoopDataCallbackProc, nullptr);
-    AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+    AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, perfMode);
 
-// Create an AAudioStream using the Builder.
-    EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
+    // Create an AAudioStream using the Builder.
+    ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
 
-
+    // Verify Open State
     aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
     EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
                                                          AAUDIO_STREAM_STATE_UNKNOWN, &state,
                                                          1000 * NANOS_PER_MILLISECOND));
     EXPECT_EQ(AAUDIO_STREAM_STATE_OPEN, state);
 
-    EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream));
+    // Put stream into desired state.
+    aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_UNINITIALIZED;
+    if (originalState != AAUDIO_STREAM_STATE_OPEN) {
 
-    state = AAUDIO_STREAM_STATE_UNKNOWN;
+        ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream));
+
+        if (originalState != AAUDIO_STREAM_STATE_STARTING) {
+
+            ASSERT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+                                                                 AAUDIO_STREAM_STATE_STARTING,
+                                                                 &state,
+                                                                 1000 * NANOS_PER_MILLISECOND));
+            ASSERT_EQ(AAUDIO_STREAM_STATE_STARTED, state);
+
+            if (originalState == AAUDIO_STREAM_STATE_STOPPING) {
+                ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream));
+            } else if (originalState == AAUDIO_STREAM_STATE_STOPPED) {
+                ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream));
+                inputState = AAUDIO_STREAM_STATE_STOPPING;
+            } else if (originalState == AAUDIO_STREAM_STATE_PAUSING) {
+                ASSERT_EQ(AAUDIO_OK, AAudioStream_requestPause(aaudioStream));
+            } else if (originalState == AAUDIO_STREAM_STATE_PAUSED) {
+                ASSERT_EQ(AAUDIO_OK, AAudioStream_requestPause(aaudioStream));
+                inputState = AAUDIO_STREAM_STATE_PAUSING;
+            }
+        }
+    }
+
+    // Wait until past transitional state.
+    if (inputState != AAUDIO_STREAM_STATE_UNINITIALIZED) {
+        ASSERT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+                                                             inputState,
+                                                             &state,
+                                                             1000 * NANOS_PER_MILLISECOND));
+        ASSERT_EQ(originalState, state);
+    }
+
+    aaudio_stream_state_t transitionalState = originalState;
+    switch(functionToCall) {
+        case FunctionToCall::CALL_START:
+            EXPECT_EQ(expectedResult, AAudioStream_requestStart(aaudioStream));
+            transitionalState = AAUDIO_STREAM_STATE_STARTING;
+            break;
+        case FunctionToCall::CALL_STOP:
+            EXPECT_EQ(expectedResult, AAudioStream_requestStop(aaudioStream));
+            transitionalState = AAUDIO_STREAM_STATE_STOPPING;
+            break;
+        case FunctionToCall::CALL_PAUSE:
+            EXPECT_EQ(expectedResult, AAudioStream_requestPause(aaudioStream));
+            transitionalState = AAUDIO_STREAM_STATE_PAUSING;
+            break;
+        case FunctionToCall::CALL_FLUSH:
+            EXPECT_EQ(expectedResult, AAudioStream_requestFlush(aaudioStream));
+            transitionalState = AAUDIO_STREAM_STATE_FLUSHING;
+            break;
+    }
+
     EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
-                                                         AAUDIO_STREAM_STATE_UNKNOWN, &state, 0));
-    EXPECT_EQ(AAUDIO_STREAM_STATE_OPEN, state);
-
-    AAudioStream_close(aaudioStream);
-    AAudioStreamBuilder_delete(aaudioBuilder);
-}
-
-//int boo() { // To fix Android Studio formatting when editing.
-TEST(test_various, aaudio_flush_when_started) {
-    AAudioStreamBuilder *aaudioBuilder = nullptr;
-    AAudioStream *aaudioStream = nullptr;
-
-// Use an AAudioStreamBuilder to contain requested parameters.
-    ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder));
-
-// Request stream properties.
-    AAudioStreamBuilder_setDataCallback(aaudioBuilder, NoopDataCallbackProc, nullptr);
-    AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
-
-// Create an AAudioStream using the Builder.
-    EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
-    EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream));
-
-    aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
-    EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
-                                                         AAUDIO_STREAM_STATE_STARTING, &state,
+                                                         transitionalState,
+                                                         &state,
                                                          1000 * NANOS_PER_MILLISECOND));
-    EXPECT_EQ(AAUDIO_STREAM_STATE_STARTED, state);
-
-    EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestFlush(aaudioStream));
-
-    state = AAUDIO_STREAM_STATE_UNKNOWN;
-    EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
-                                                         AAUDIO_STREAM_STATE_UNKNOWN, &state, 0));
-    EXPECT_EQ(AAUDIO_STREAM_STATE_STARTED, state);
+    // We should not change state when a function fails.
+    if (expectedResult != AAUDIO_OK) {
+        ASSERT_EQ(originalState, expectedState);
+    }
+    EXPECT_EQ(expectedState, state);
+    if (state != expectedState) {
+        printf("ERROR - expected %s, actual = %s\n",
+                AAudio_convertStreamStateToText(expectedState),
+                AAudio_convertStreamStateToText(state));
+        fflush(stdout);
+    }
 
     AAudioStream_close(aaudioStream);
     AAudioStreamBuilder_delete(aaudioBuilder);
 }
 
-//int main() { // To fix Android Studio formatting when editing.
+// TODO Use parameterized tests instead of these individual specific tests.
+
+// OPEN =================================================================
+TEST(test_various, aaudio_state_lowlat_open_start) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_OPEN,
+            FunctionToCall::CALL_START,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_STARTED);
+}
+
+TEST(test_various, aaudio_state_none_open_start) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_OPEN,
+            FunctionToCall::CALL_START,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_STARTED);
+}
+
+TEST(test_various, aaudio_state_lowlat_open_stop) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_OPEN,
+            FunctionToCall::CALL_STOP,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_STOPPED);
+}
+
+TEST(test_various, aaudio_state_none_open_stop) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_OPEN,
+            FunctionToCall::CALL_STOP,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_STOPPED);
+}
+
+TEST(test_various, aaudio_state_lowlat_open_pause) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_OPEN,
+            FunctionToCall::CALL_PAUSE,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_PAUSED);
+}
+
+TEST(test_various, aaudio_state_none_open_pause) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_OPEN,
+            FunctionToCall::CALL_PAUSE,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_PAUSED);
+}
+
+TEST(test_various, aaudio_state_lowlat_open_flush) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_OPEN,
+            FunctionToCall::CALL_FLUSH,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_FLUSHED);
+}
+
+TEST(test_various, aaudio_state_none_open_flush) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_OPEN,
+            FunctionToCall::CALL_FLUSH,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_FLUSHED);
+}
+
+
+// STARTED =================================================================
+TEST(test_various, aaudio_state_lowlat_started_start) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_STARTED,
+            FunctionToCall::CALL_START,
+            AAUDIO_ERROR_INVALID_STATE,
+            AAUDIO_STREAM_STATE_STARTED);
+}
+
+TEST(test_various, aaudio_state_none_started_start) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_STARTED,
+            FunctionToCall::CALL_START,
+            AAUDIO_ERROR_INVALID_STATE,
+            AAUDIO_STREAM_STATE_STARTED);
+}
+
+TEST(test_various, aaudio_state_lowlat_started_stop) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_STARTED,
+            FunctionToCall::CALL_STOP,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_STOPPED);
+}
+
+TEST(test_various, aaudio_state_none_started_stop) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_STARTED,
+            FunctionToCall::CALL_STOP,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_STOPPED);
+}
+
+TEST(test_various, aaudio_state_lowlat_started_pause) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_STARTED,
+            FunctionToCall::CALL_PAUSE,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_PAUSED);
+}
+
+TEST(test_various, aaudio_state_none_started_pause) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_STARTED,
+            FunctionToCall::CALL_PAUSE,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_PAUSED);
+}
+
+TEST(test_various, aaudio_state_lowlat_started_flush) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_STARTED,
+            FunctionToCall::CALL_FLUSH,
+            AAUDIO_ERROR_INVALID_STATE,
+            AAUDIO_STREAM_STATE_STARTED);
+}
+
+TEST(test_various, aaudio_state_none_started_flush) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_STARTED,
+            FunctionToCall::CALL_FLUSH,
+            AAUDIO_ERROR_INVALID_STATE,
+            AAUDIO_STREAM_STATE_STARTED);
+}
+
+// STOPPED =================================================================
+TEST(test_various, aaudio_state_lowlat_stopped_start) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_STOPPED,
+            FunctionToCall::CALL_START,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_STARTED);
+}
+
+TEST(test_various, aaudio_state_none_stopped_start) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_STOPPED,
+            FunctionToCall::CALL_START,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_STARTED);
+}
+
+TEST(test_various, aaudio_state_lowlat_stopped_stop) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_STOPPED,
+            FunctionToCall::CALL_STOP,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_STOPPED);
+}
+
+TEST(test_various, aaudio_state_none_stopped_stop) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_STOPPED,
+            FunctionToCall::CALL_STOP,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_STOPPED);
+}
+
+TEST(test_various, aaudio_state_lowlat_stopped_pause) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_STOPPED,
+            FunctionToCall::CALL_PAUSE,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_PAUSED);
+}
+
+TEST(test_various, aaudio_state_none_stopped_pause) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_STOPPED,
+            FunctionToCall::CALL_PAUSE,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_PAUSED);
+}
+
+TEST(test_various, aaudio_state_lowlat_stopped_flush) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+            AAUDIO_STREAM_STATE_STOPPED,
+            FunctionToCall::CALL_FLUSH,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_FLUSHED);
+}
+
+TEST(test_various, aaudio_state_none_stopped_flush) {
+    checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+            AAUDIO_STREAM_STATE_STOPPED,
+            FunctionToCall::CALL_FLUSH,
+            AAUDIO_OK,
+            AAUDIO_STREAM_STATE_FLUSHED);
+}
+
+// PAUSED =================================================================
+TEST(test_various, aaudio_state_lowlat_paused_start) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+        AAUDIO_STREAM_STATE_PAUSED,
+        FunctionToCall::CALL_START,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_STARTED);
+}
+
+TEST(test_various, aaudio_state_none_paused_start) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+        AAUDIO_STREAM_STATE_PAUSED,
+        FunctionToCall::CALL_START,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_STARTED);
+}
+
+TEST(test_various, aaudio_state_lowlat_paused_stop) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+        AAUDIO_STREAM_STATE_PAUSED,
+        FunctionToCall::CALL_STOP,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_STOPPED);
+}
+
+TEST(test_various, aaudio_state_none_paused_stop) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+        AAUDIO_STREAM_STATE_PAUSED,
+        FunctionToCall::CALL_STOP,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_STOPPED);
+}
+
+TEST(test_various, aaudio_state_lowlat_paused_pause) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+        AAUDIO_STREAM_STATE_PAUSED,
+        FunctionToCall::CALL_PAUSE,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_PAUSED);
+}
+
+TEST(test_various, aaudio_state_none_paused_pause) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+        AAUDIO_STREAM_STATE_PAUSED,
+        FunctionToCall::CALL_PAUSE,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_PAUSED);
+}
+
+TEST(test_various, aaudio_state_lowlat_paused_flush) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+        AAUDIO_STREAM_STATE_PAUSED,
+        FunctionToCall::CALL_FLUSH,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_FLUSHED);
+}
+
+TEST(test_various, aaudio_state_none_paused_flush) {
+checkStateTransition(AAUDIO_PERFORMANCE_MODE_NONE,
+        AAUDIO_STREAM_STATE_PAUSED,
+        FunctionToCall::CALL_FLUSH,
+        AAUDIO_OK,
+        AAUDIO_STREAM_STATE_FLUSHED);
+}
+
+// ==========================================================================
 TEST(test_various, aaudio_set_buffer_size) {
 
     int32_t bufferCapacity;
@@ -174,7 +476,6 @@
     AAudioStreamBuilder_delete(aaudioBuilder);
 }
 
-
 // ************************************************************
 // Test to make sure that AAUDIO_CALLBACK_RESULT_STOP works.
 
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index ba5643f..3bb09d2 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -39,8 +39,7 @@
 sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient;
 audio_error_callback AudioSystem::gAudioErrorCallback = NULL;
 dynamic_policy_callback AudioSystem::gDynPolicyCallback = NULL;
-record_config_callback  AudioSystem::gRecordConfigCallback = NULL;
-
+record_config_callback AudioSystem::gRecordConfigCallback = NULL;
 
 // establish binder interface to AudioFlinger service
 const sp<IAudioFlinger> AudioSystem::get_audio_flinger()
@@ -859,6 +858,7 @@
                                         audio_io_handle_t *output,
                                         audio_session_t session,
                                         audio_stream_type_t *stream,
+                                        pid_t pid,
                                         uid_t uid,
                                         const audio_config_t *config,
                                         audio_output_flags_t flags,
@@ -867,7 +867,7 @@
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return NO_INIT;
-    return aps->getOutputForAttr(attr, output, session, stream, uid,
+    return aps->getOutputForAttr(attr, output, session, stream, pid, uid,
                                  config,
                                  flags, selectedDeviceId, portId);
 }
@@ -917,11 +917,14 @@
 }
 
 status_t AudioSystem::startInput(audio_io_handle_t input,
-                                 audio_session_t session)
+                                 audio_session_t session,
+                                 audio_devices_t device,
+                                 uid_t uid,
+                                 bool *silenced)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return PERMISSION_DENIED;
-    return aps->startInput(input, session);
+    return aps->startInput(input, session, device, uid, silenced);
 }
 
 status_t AudioSystem::stopInput(audio_io_handle_t input,
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 0302df6..ae9c96f 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -48,6 +48,7 @@
     SET_MODE,
     SET_MIC_MUTE,
     GET_MIC_MUTE,
+    SET_RECORD_SILENCED,
     SET_PARAMETERS,
     GET_PARAMETERS,
     REGISTER_CLIENT,
@@ -306,6 +307,15 @@
         return reply.readInt32();
     }
 
+    virtual void setRecordSilenced(uid_t uid, bool silenced)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.writeInt32(uid);
+        data.writeInt32(silenced ? 1 : 0);
+        remote()->transact(SET_RECORD_SILENCED, data, &reply);
+    }
+
     virtual status_t setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
     {
         Parcel data, reply;
@@ -863,6 +873,7 @@
         case RELEASE_AUDIO_PATCH:
         case LIST_AUDIO_PATCHES:
         case SET_AUDIO_PORT_CONFIG:
+        case SET_RECORD_SILENCED:
             ALOGW("%s: transaction %d received from PID %d",
                   __func__, code, IPCThreadState::self()->getCallingPid());
             return INVALID_OPERATION;
@@ -1028,6 +1039,15 @@
             reply->writeInt32( getMicMute() );
             return NO_ERROR;
         } break;
+        case SET_RECORD_SILENCED: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            uid_t uid = data.readInt32();
+            audio_source_t source;
+            data.read(&source, sizeof(audio_source_t));
+            bool silenced = data.readInt32() == 1;
+            setRecordSilenced(uid, silenced);
+            return NO_ERROR;
+        } break;
         case SET_PARAMETERS: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 53bc1b7..6478975 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -174,6 +174,7 @@
                                         audio_io_handle_t *output,
                                         audio_session_t session,
                                         audio_stream_type_t *stream,
+                                        pid_t pid,
                                         uid_t uid,
                                         const audio_config_t *config,
                                         audio_output_flags_t flags,
@@ -217,6 +218,7 @@
                 data.writeInt32(1);
                 data.writeInt32(*stream);
             }
+            data.writeInt32(pid);
             data.writeInt32(uid);
             data.write(config, sizeof(audio_config_t));
             data.writeInt32(static_cast <uint32_t>(flags));
@@ -330,14 +332,22 @@
     }
 
     virtual status_t startInput(audio_io_handle_t input,
-                                audio_session_t session)
+                                audio_session_t session,
+                                audio_devices_t device,
+                                uid_t uid,
+                                bool *silenced)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
         data.writeInt32(input);
         data.writeInt32(session);
+        data.writeInt32(device);
+        data.writeInt32(uid);
+        data.writeInt32(*silenced ? 1 : 0);
         remote()->transact(START_INPUT, data, &reply);
-        return static_cast <status_t> (reply.readInt32());
+        status_t status = static_cast <status_t> (reply.readInt32());
+        *silenced = reply.readInt32() == 1;
+        return status;
     }
 
     virtual status_t stopInput(audio_io_handle_t input,
@@ -960,6 +970,7 @@
             if (hasStream) {
                 stream = (audio_stream_type_t)data.readInt32();
             }
+            pid_t pid = (pid_t)data.readInt32();
             uid_t uid = (uid_t)data.readInt32();
             audio_config_t config;
             memset(&config, 0, sizeof(audio_config_t));
@@ -970,7 +981,7 @@
             audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
             audio_io_handle_t output = 0;
             status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
-                    &output, session, &stream, uid,
+                    &output, session, &stream, pid, uid,
                     &config,
                     flags, &selectedDeviceId, &portId);
             reply->writeInt32(status);
@@ -1045,7 +1056,12 @@
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
             audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
             audio_session_t session = static_cast <audio_session_t>(data.readInt32());
-            reply->writeInt32(static_cast <uint32_t>(startInput(input, session)));
+            audio_devices_t device = static_cast <audio_devices_t>(data.readInt32());
+            uid_t uid = static_cast <uid_t>(data.readInt32());
+            bool silenced = data.readInt32() == 1;
+            status_t status = startInput(input, session, device, uid, &silenced);
+            reply->writeInt32(static_cast <uint32_t>(status));
+            reply->writeInt32(silenced ? 1 : 0);
             return NO_ERROR;
         } break;
 
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 261a7e9..f7c3d03 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -216,6 +216,7 @@
                                      audio_io_handle_t *output,
                                      audio_session_t session,
                                      audio_stream_type_t *stream,
+                                     pid_t pid,
                                      uid_t uid,
                                      const audio_config_t *config,
                                      audio_output_flags_t flags,
@@ -244,7 +245,10 @@
                                     audio_port_handle_t *portId);
 
     static status_t startInput(audio_io_handle_t input,
-                               audio_session_t session);
+                               audio_session_t session,
+                               audio_devices_t device,
+                               uid_t uid,
+                               bool *silenced);
     static status_t stopInput(audio_io_handle_t input,
                               audio_session_t session);
     static void releaseInput(audio_io_handle_t input,
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index f0417ac..e8d405b 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -368,6 +368,7 @@
     // mic mute/state
     virtual     status_t    setMicMute(bool state) = 0;
     virtual     bool        getMicMute() const = 0;
+    virtual     void        setRecordSilenced(uid_t uid, bool silenced) = 0;
 
     virtual     status_t    setParameters(audio_io_handle_t ioHandle,
                                     const String8& keyValuePairs) = 0;
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index 5558b77..5338927 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -60,6 +60,7 @@
                                       audio_io_handle_t *output,
                                       audio_session_t session,
                                       audio_stream_type_t *stream,
+                                      pid_t pid,
                                       uid_t uid,
                                       const audio_config_t *config,
                                       audio_output_flags_t flags,
@@ -84,7 +85,10 @@
                               audio_port_handle_t *selectedDeviceId,
                               audio_port_handle_t *portId) = 0;
     virtual status_t startInput(audio_io_handle_t input,
-                                audio_session_t session) = 0;
+                                audio_session_t session,
+                                audio_devices_t device,
+                                uid_t uid,
+                                bool *silenced) = 0;
     virtual status_t stopInput(audio_io_handle_t input,
                                audio_session_t session) = 0;
     virtual void releaseInput(audio_io_handle_t input,
diff --git a/media/libaudiohal/2.0/Android.bp b/media/libaudiohal/2.0/Android.bp
new file mode 100644
index 0000000..574b435
--- /dev/null
+++ b/media/libaudiohal/2.0/Android.bp
@@ -0,0 +1,54 @@
+cc_library_shared {
+    name: "libaudiohal@2.0",
+
+    srcs: [
+        "DeviceHalLocal.cpp",
+        "DevicesFactoryHalHybrid.cpp",
+        "DevicesFactoryHalLocal.cpp",
+        "StreamHalLocal.cpp",
+
+        "ConversionHelperHidl.cpp",
+        "DeviceHalHidl.cpp",
+        "DevicesFactoryHalHidl.cpp",
+        "EffectBufferHalHidl.cpp",
+        "EffectHalHidl.cpp",
+        "EffectsFactoryHalHidl.cpp",
+        "StreamHalHidl.cpp",
+    ],
+
+    export_include_dirs: ["."],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libaudiohal_deathhandler",
+        "libaudioutils",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "libhardware",
+        "libbase",
+        "libfmq",
+        "libhwbinder",
+        "libhidlbase",
+        "libhidlmemory",
+        "libhidltransport",
+        "android.hardware.audio@2.0",
+        "android.hardware.audio.common@2.0",
+        "android.hardware.audio.common@2.0-util",
+        "android.hardware.audio.effect@2.0",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libmedia_helper",
+        "libmediautils",
+    ],
+    header_libs: [
+        "libaudiohal_headers"
+    ],
+
+    export_shared_lib_headers: [
+        "libfmq",
+    ],
+}
diff --git a/media/libaudiohal/ConversionHelperHidl.cpp b/media/libaudiohal/2.0/ConversionHelperHidl.cpp
similarity index 100%
rename from media/libaudiohal/ConversionHelperHidl.cpp
rename to media/libaudiohal/2.0/ConversionHelperHidl.cpp
diff --git a/media/libaudiohal/ConversionHelperHidl.h b/media/libaudiohal/2.0/ConversionHelperHidl.h
similarity index 100%
rename from media/libaudiohal/ConversionHelperHidl.h
rename to media/libaudiohal/2.0/ConversionHelperHidl.h
diff --git a/media/libaudiohal/DeviceHalHidl.cpp b/media/libaudiohal/2.0/DeviceHalHidl.cpp
similarity index 99%
rename from media/libaudiohal/DeviceHalHidl.cpp
rename to media/libaudiohal/2.0/DeviceHalHidl.cpp
index 49ef991..0d9c6c4 100644
--- a/media/libaudiohal/DeviceHalHidl.cpp
+++ b/media/libaudiohal/2.0/DeviceHalHidl.cpp
@@ -37,6 +37,7 @@
 using ::android::hardware::audio::common::V2_0::AudioPortConfig;
 using ::android::hardware::audio::common::V2_0::AudioMode;
 using ::android::hardware::audio::common::V2_0::AudioSource;
+using ::android::hardware::audio::common::V2_0::HidlUtils;
 using ::android::hardware::audio::V2_0::DeviceAddress;
 using ::android::hardware::audio::V2_0::IPrimaryDevice;
 using ::android::hardware::audio::V2_0::ParameterValue;
diff --git a/media/libaudiohal/DeviceHalHidl.h b/media/libaudiohal/2.0/DeviceHalHidl.h
similarity index 100%
rename from media/libaudiohal/DeviceHalHidl.h
rename to media/libaudiohal/2.0/DeviceHalHidl.h
diff --git a/media/libaudiohal/DeviceHalLocal.cpp b/media/libaudiohal/2.0/DeviceHalLocal.cpp
similarity index 100%
rename from media/libaudiohal/DeviceHalLocal.cpp
rename to media/libaudiohal/2.0/DeviceHalLocal.cpp
diff --git a/media/libaudiohal/DeviceHalLocal.h b/media/libaudiohal/2.0/DeviceHalLocal.h
similarity index 100%
rename from media/libaudiohal/DeviceHalLocal.h
rename to media/libaudiohal/2.0/DeviceHalLocal.h
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.cpp b/media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp
similarity index 100%
rename from media/libaudiohal/DevicesFactoryHalHidl.cpp
rename to media/libaudiohal/2.0/DevicesFactoryHalHidl.cpp
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.h b/media/libaudiohal/2.0/DevicesFactoryHalHidl.h
similarity index 100%
rename from media/libaudiohal/DevicesFactoryHalHidl.h
rename to media/libaudiohal/2.0/DevicesFactoryHalHidl.h
diff --git a/media/libaudiohal/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/2.0/DevicesFactoryHalHybrid.cpp
similarity index 91%
rename from media/libaudiohal/DevicesFactoryHalHybrid.cpp
rename to media/libaudiohal/2.0/DevicesFactoryHalHybrid.cpp
index 8dc1434..77df6b5 100644
--- a/media/libaudiohal/DevicesFactoryHalHybrid.cpp
+++ b/media/libaudiohal/2.0/DevicesFactoryHalHybrid.cpp
@@ -23,11 +23,6 @@
 
 namespace android {
 
-// static
-sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
-    return new DevicesFactoryHalHybrid();
-}
-
 DevicesFactoryHalHybrid::DevicesFactoryHalHybrid()
         : mLocalFactory(new DevicesFactoryHalLocal()),
           mHidlFactory(new DevicesFactoryHalHidl()) {
diff --git a/media/libaudiohal/DevicesFactoryHalHybrid.h b/media/libaudiohal/2.0/DevicesFactoryHalHybrid.h
similarity index 100%
rename from media/libaudiohal/DevicesFactoryHalHybrid.h
rename to media/libaudiohal/2.0/DevicesFactoryHalHybrid.h
diff --git a/media/libaudiohal/DevicesFactoryHalLocal.cpp b/media/libaudiohal/2.0/DevicesFactoryHalLocal.cpp
similarity index 100%
rename from media/libaudiohal/DevicesFactoryHalLocal.cpp
rename to media/libaudiohal/2.0/DevicesFactoryHalLocal.cpp
diff --git a/media/libaudiohal/DevicesFactoryHalLocal.h b/media/libaudiohal/2.0/DevicesFactoryHalLocal.h
similarity index 100%
rename from media/libaudiohal/DevicesFactoryHalLocal.h
rename to media/libaudiohal/2.0/DevicesFactoryHalLocal.h
diff --git a/media/libaudiohal/EffectBufferHalHidl.cpp b/media/libaudiohal/2.0/EffectBufferHalHidl.cpp
similarity index 97%
rename from media/libaudiohal/EffectBufferHalHidl.cpp
rename to media/libaudiohal/2.0/EffectBufferHalHidl.cpp
index 8b5201b..226a500 100644
--- a/media/libaudiohal/EffectBufferHalHidl.cpp
+++ b/media/libaudiohal/2.0/EffectBufferHalHidl.cpp
@@ -37,14 +37,12 @@
     return counter++;
 }
 
-// static
-status_t EffectBufferHalInterface::allocate(
+status_t EffectBufferHalHidl::allocate(
         size_t size, sp<EffectBufferHalInterface>* buffer) {
     return mirror(nullptr, size, buffer);
 }
 
-// static
-status_t EffectBufferHalInterface::mirror(
+status_t EffectBufferHalHidl::mirror(
         void* external, size_t size, sp<EffectBufferHalInterface>* buffer) {
     sp<EffectBufferHalInterface> tempBuffer = new EffectBufferHalHidl(size);
     status_t result = static_cast<EffectBufferHalHidl*>(tempBuffer.get())->init();
diff --git a/media/libaudiohal/EffectBufferHalHidl.h b/media/libaudiohal/2.0/EffectBufferHalHidl.h
similarity index 92%
rename from media/libaudiohal/EffectBufferHalHidl.h
rename to media/libaudiohal/2.0/EffectBufferHalHidl.h
index d7a43ae..31e0087 100644
--- a/media/libaudiohal/EffectBufferHalHidl.h
+++ b/media/libaudiohal/2.0/EffectBufferHalHidl.h
@@ -32,6 +32,9 @@
 class EffectBufferHalHidl : public EffectBufferHalInterface
 {
   public:
+    static status_t allocate(size_t size, sp<EffectBufferHalInterface>* buffer);
+    static status_t mirror(void* external, size_t size, sp<EffectBufferHalInterface>* buffer);
+
     virtual audio_buffer_t* audioBuffer();
     virtual void* externalData() const;
 
diff --git a/media/libaudiohal/EffectHalHidl.cpp b/media/libaudiohal/2.0/EffectHalHidl.cpp
similarity index 99%
rename from media/libaudiohal/EffectHalHidl.cpp
rename to media/libaudiohal/2.0/EffectHalHidl.cpp
index f4d1958..4fb032c 100644
--- a/media/libaudiohal/EffectHalHidl.cpp
+++ b/media/libaudiohal/2.0/EffectHalHidl.cpp
@@ -31,6 +31,7 @@
 using ::android::hardware::audio::effect::V2_0::EffectConfigParameters;
 using ::android::hardware::audio::effect::V2_0::MessageQueueFlagBits;
 using ::android::hardware::audio::effect::V2_0::Result;
+using ::android::hardware::audio::common::V2_0::HidlUtils;
 using ::android::hardware::audio::common::V2_0::AudioChannelMask;
 using ::android::hardware::audio::common::V2_0::AudioFormat;
 using ::android::hardware::hidl_vec;
diff --git a/media/libaudiohal/EffectHalHidl.h b/media/libaudiohal/2.0/EffectHalHidl.h
similarity index 100%
rename from media/libaudiohal/EffectHalHidl.h
rename to media/libaudiohal/2.0/EffectHalHidl.h
diff --git a/media/libaudiohal/EffectsFactoryHalHidl.cpp b/media/libaudiohal/2.0/EffectsFactoryHalHidl.cpp
similarity index 91%
rename from media/libaudiohal/EffectsFactoryHalHidl.cpp
rename to media/libaudiohal/2.0/EffectsFactoryHalHidl.cpp
index a8081b7..0d40e6d 100644
--- a/media/libaudiohal/EffectsFactoryHalHidl.cpp
+++ b/media/libaudiohal/2.0/EffectsFactoryHalHidl.cpp
@@ -20,10 +20,12 @@
 #include <cutils/native_handle.h>
 
 #include "ConversionHelperHidl.h"
+#include "EffectBufferHalHidl.h"
 #include "EffectHalHidl.h"
 #include "EffectsFactoryHalHidl.h"
 #include "HidlUtils.h"
 
+using ::android::hardware::audio::common::V2_0::HidlUtils;
 using ::android::hardware::audio::common::V2_0::Uuid;
 using ::android::hardware::audio::effect::V2_0::IEffect;
 using ::android::hardware::audio::effect::V2_0::Result;
@@ -31,16 +33,6 @@
 
 namespace android {
 
-// static
-sp<EffectsFactoryHalInterface> EffectsFactoryHalInterface::create() {
-    return new EffectsFactoryHalHidl();
-}
-
-// static
-bool EffectsFactoryHalInterface::isNullUuid(const effect_uuid_t *pEffectUuid) {
-    return memcmp(pEffectUuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t)) == 0;
-}
-
 EffectsFactoryHalHidl::EffectsFactoryHalHidl() : ConversionHelperHidl("EffectsFactory") {
     mEffectsFactory = IEffectsFactory::getService();
     if (mEffectsFactory == 0) {
@@ -145,4 +137,14 @@
     return processReturn(__FUNCTION__, ret);
 }
 
+status_t EffectsFactoryHalHidl::allocateBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) {
+    return EffectBufferHalHidl::allocate(size, buffer);
+}
+
+status_t EffectsFactoryHalHidl::mirrorBuffer(void* external, size_t size,
+                          sp<EffectBufferHalInterface>* buffer) {
+    return EffectBufferHalHidl::mirror(external, size, buffer);
+}
+
+
 } // namespace android
diff --git a/media/libaudiohal/EffectsFactoryHalHidl.h b/media/libaudiohal/2.0/EffectsFactoryHalHidl.h
similarity index 90%
rename from media/libaudiohal/EffectsFactoryHalHidl.h
rename to media/libaudiohal/2.0/EffectsFactoryHalHidl.h
index e89f042..82b5481 100644
--- a/media/libaudiohal/EffectsFactoryHalHidl.h
+++ b/media/libaudiohal/2.0/EffectsFactoryHalHidl.h
@@ -21,6 +21,8 @@
 #include <android/hardware/audio/effect/2.0/types.h>
 #include <media/audiohal/EffectsFactoryHalInterface.h>
 
+#include "ConversionHelperHidl.h"
+
 namespace android {
 
 using ::android::hardware::audio::effect::V2_0::EffectDescriptor;
@@ -49,6 +51,10 @@
 
     virtual status_t dumpEffects(int fd);
 
+    status_t allocateBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) override;
+    status_t mirrorBuffer(void* external, size_t size,
+                          sp<EffectBufferHalInterface>* buffer) override;
+
   private:
     friend class EffectsFactoryHalInterface;
 
diff --git a/media/libaudiohal/StreamHalHidl.cpp b/media/libaudiohal/2.0/StreamHalHidl.cpp
similarity index 100%
rename from media/libaudiohal/StreamHalHidl.cpp
rename to media/libaudiohal/2.0/StreamHalHidl.cpp
diff --git a/media/libaudiohal/StreamHalHidl.h b/media/libaudiohal/2.0/StreamHalHidl.h
similarity index 100%
rename from media/libaudiohal/StreamHalHidl.h
rename to media/libaudiohal/2.0/StreamHalHidl.h
diff --git a/media/libaudiohal/StreamHalLocal.cpp b/media/libaudiohal/2.0/StreamHalLocal.cpp
similarity index 100%
rename from media/libaudiohal/StreamHalLocal.cpp
rename to media/libaudiohal/2.0/StreamHalLocal.cpp
diff --git a/media/libaudiohal/StreamHalLocal.h b/media/libaudiohal/2.0/StreamHalLocal.h
similarity index 100%
rename from media/libaudiohal/StreamHalLocal.h
rename to media/libaudiohal/2.0/StreamHalLocal.h
diff --git a/media/libaudiohal/StreamPowerLog.h b/media/libaudiohal/2.0/StreamPowerLog.h
similarity index 100%
rename from media/libaudiohal/StreamPowerLog.h
rename to media/libaudiohal/2.0/StreamPowerLog.h
diff --git a/media/libaudiohal/Android.bp b/media/libaudiohal/Android.bp
index 700de8e..7ecb9d8 100644
--- a/media/libaudiohal/Android.bp
+++ b/media/libaudiohal/Android.bp
@@ -2,46 +2,52 @@
     name: "libaudiohal",
 
     srcs: [
-        "DeviceHalLocal.cpp",
-        "DevicesFactoryHalHybrid.cpp",
-        "DevicesFactoryHalLocal.cpp",
-        "StreamHalLocal.cpp",
-
-        "ConversionHelperHidl.cpp",
-        "HalDeathHandlerHidl.cpp",
-        "DeviceHalHidl.cpp",
-        "DevicesFactoryHalHidl.cpp",
-        "EffectBufferHalHidl.cpp",
-        "EffectHalHidl.cpp",
-        "EffectsFactoryHalHidl.cpp",
-        "StreamHalHidl.cpp",
+        "DevicesFactoryHalInterface.cpp",
+        "EffectsFactoryHalInterface.cpp",
     ],
 
     cflags: [
         "-Wall",
         "-Werror",
     ],
-    export_include_dirs: ["include"],
 
     shared_libs: [
-        "libaudioutils",
-        "libcutils",
-        "liblog",
-        "libutils",
-        "libhardware",
-        "libbase",
-        "libfmq",
-        "libhwbinder",
-        "libhidlbase",
-        "libhidlmemory",
-        "libhidltransport",
         "android.hardware.audio@2.0",
-        "android.hardware.audio.common@2.0",
-        "android.hardware.audio.common@2.0-util",
         "android.hardware.audio.effect@2.0",
-        "android.hidl.allocator@1.0",
-        "android.hidl.memory@1.0",
-        "libmedia_helper",
-        "libmediautils",
+        "libaudiohal@2.0",
+        "libutils",
     ],
+
+    header_libs: [
+        "libaudiohal_headers"
+    ]
+}
+
+cc_library_shared {
+    name: "libaudiohal_deathhandler",
+
+    srcs: [
+        "HalDeathHandlerHidl.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libhidlbase",
+        "libutils",
+        "liblog",
+    ],
+
+    header_libs: [
+        "libaudiohal_headers"
+    ]
+}
+
+cc_library_headers {
+    name: "libaudiohal_headers",
+
+    export_include_dirs: ["include"],
 }
diff --git a/media/libaudiohal/DevicesFactoryHalInterface.cpp b/media/libaudiohal/DevicesFactoryHalInterface.cpp
new file mode 100644
index 0000000..cfec3d6
--- /dev/null
+++ b/media/libaudiohal/DevicesFactoryHalInterface.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DevicesFactoryHalHybrid.h"
+#include <android/hardware/audio/2.0/IDevicesFactory.h>
+
+using namespace ::android::hardware::audio;
+
+namespace android {
+
+// static
+sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
+    if (V2_0::IDevicesFactory::getService() != nullptr) {
+        return new DevicesFactoryHalHybrid();
+    }
+    return nullptr;
+}
+
+} // namespace android
diff --git a/media/libaudiohal/EffectsFactoryHalInterface.cpp b/media/libaudiohal/EffectsFactoryHalInterface.cpp
new file mode 100644
index 0000000..01a171e
--- /dev/null
+++ b/media/libaudiohal/EffectsFactoryHalInterface.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "EffectsFactoryHalHidl"
+//#define LOG_NDEBUG 0
+
+#include "EffectHalHidl.h"
+#include "EffectsFactoryHalHidl.h"
+
+using ::android::hardware::Return;
+
+using namespace ::android::hardware::audio::effect;
+
+namespace android {
+
+// static
+sp<EffectsFactoryHalInterface> EffectsFactoryHalInterface::create() {
+    if (V2_0::IEffectsFactory::getService() != nullptr) {
+        return new EffectsFactoryHalHidl();
+    }
+    return nullptr;
+}
+
+// static
+bool EffectsFactoryHalInterface::isNullUuid(const effect_uuid_t *pEffectUuid) {
+    return memcmp(pEffectUuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t)) == 0;
+}
+
+} // namespace android
diff --git a/media/libaudiohal/include/media/audiohal/EffectBufferHalInterface.h b/media/libaudiohal/include/media/audiohal/EffectBufferHalInterface.h
index 1cae662..d0603cd 100644
--- a/media/libaudiohal/include/media/audiohal/EffectBufferHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/EffectBufferHalInterface.h
@@ -26,6 +26,7 @@
 // Abstraction for an audio buffer. It may be a "mirror" for
 // a buffer that the effect chain doesn't own, or a buffer owned by
 // the effect chain.
+// Buffers are created from EffectsFactoryHalInterface
 class EffectBufferHalInterface : public RefBase
 {
   public:
@@ -49,9 +50,6 @@
     virtual void update(size_t size) = 0;  // copies partial data from external buffer
     virtual void commit(size_t size) = 0;  // copies partial data to external buffer
 
-    static status_t allocate(size_t size, sp<EffectBufferHalInterface>* buffer);
-    static status_t mirror(void* external, size_t size, sp<EffectBufferHalInterface>* buffer);
-
   protected:
     // Subclasses can not be constructed directly by clients.
     EffectBufferHalInterface() {}
diff --git a/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h b/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
index a616e86..316a46c 100644
--- a/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
@@ -48,6 +48,10 @@
 
     static sp<EffectsFactoryHalInterface> create();
 
+    virtual status_t allocateBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) = 0;
+    virtual status_t mirrorBuffer(void* external, size_t size,
+                                  sp<EffectBufferHalInterface>* buffer) = 0;
+
     // Helper function to compare effect uuid to EFFECT_UUID_NULL.
     static bool isNullUuid(const effect_uuid_t *pEffectUuid);
 
diff --git a/media/libaudioprocessing/BufferProviders.cpp b/media/libaudioprocessing/BufferProviders.cpp
index 862fef6..e19af4a 100644
--- a/media/libaudioprocessing/BufferProviders.cpp
+++ b/media/libaudioprocessing/BufferProviders.cpp
@@ -183,7 +183,7 @@
      mOutFrameSize =
              audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask);
      status_t status;
-     status = EffectBufferHalInterface::mirror(
+     status = mEffectsFactory->mirrorBuffer(
              nullptr, mInFrameSize * bufferFrameCount, &mInBuffer);
      if (status != 0) {
          ALOGE("DownmixerBufferProvider() error %d while creating input buffer", status);
@@ -191,7 +191,7 @@
          mEffectsFactory.clear();
          return;
      }
-     status = EffectBufferHalInterface::mirror(
+     status = mEffectsFactory->mirrorBuffer(
              nullptr, mOutFrameSize * bufferFrameCount, &mOutBuffer);
      if (status != 0) {
          ALOGE("DownmixerBufferProvider() error %d while creating output buffer", status);
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index fd7400a..28684da 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -146,7 +146,7 @@
     ],
 }
 
-cc_library_shared {
+cc_library {
     name: "libmedia",
 
     srcs: [
@@ -249,7 +249,7 @@
     },
 }
 
-cc_library_shared {
+cc_library {
     name: "libmedia_player2_util",
 
     srcs: [
@@ -259,6 +259,7 @@
         "IMediaExtractorService.cpp",
         "IMediaSource.cpp",
         "IStreamSource.cpp",
+        "MediaCodecBuffer.cpp",
         "MediaUtils.cpp",
         "Metadata.cpp",
         "NdkWrapper.cpp",
@@ -267,7 +268,6 @@
     shared_libs: [
         "libbinder",
         "libcutils",
-        "libgui",
         "liblog",
         "libmediaextractor",
         "libmediandk",
@@ -287,9 +287,6 @@
     ],
 
     static_libs: [
-        "libc_malloc_debug_backtrace",  // for memory heap analysis
-
-        "libstagefright_nuplayer2",
         "libstagefright_rtsp",
         "libstagefright_timedtext",
     ],
@@ -316,16 +313,14 @@
     },
 }
 
-cc_library_shared {
+cc_library {
     name: "libmedia_player2",
 
     srcs: [
-        "AudioParameter.cpp",
         "JAudioTrack.cpp",
         "MediaPlayer2Factory.cpp",
         "MediaPlayer2Manager.cpp",
         "TestPlayerStub.cpp",
-        "TypeConverter.cpp",
         "mediaplayer2.cpp",
     ],
 
@@ -366,8 +361,7 @@
     ],
 
     static_libs: [
-        "libc_malloc_debug_backtrace",  // for memory heap analysis
-
+        "libmedia_helper",
         "libstagefright_nuplayer2",
         "libstagefright_rtsp",
         "libstagefright_timedtext",
diff --git a/media/libmedia/JAudioTrack.cpp b/media/libmedia/JAudioTrack.cpp
index b228d8b..99da0f7 100644
--- a/media/libmedia/JAudioTrack.cpp
+++ b/media/libmedia/JAudioTrack.cpp
@@ -112,6 +112,11 @@
     return env->CallIntMethod(mAudioTrackObj, jGetChannelCount);
 }
 
+uint32_t JAudioTrack::latency() {
+    // TODO: Currently hard-coded as returning zero.
+    return 0;
+}
+
 status_t JAudioTrack::getPosition(uint32_t *position) {
     if (position == NULL) {
         return BAD_VALUE;
@@ -125,7 +130,7 @@
     return NO_ERROR;
 }
 
-bool JAudioTrack::getTimeStamp(AudioTimestamp& timestamp) {
+bool JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
 
     jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp");
@@ -392,6 +397,51 @@
     return audioFormatToNative(javaFormat);
 }
 
+status_t JAudioTrack::dump(int fd, const Vector<String16>& args __unused) const
+{
+    String8 result;
+
+    result.append(" JAudioTrack::dump\n");
+
+    // TODO: Remove logs that includes unavailable information from below.
+//    result.appendFormat("  status(%d), state(%d), session Id(%d), flags(%#x)\n",
+//                        mStatus, mState, mSessionId, mFlags);
+//    result.appendFormat("  stream type(%d), left - right volume(%f, %f)\n",
+//                        (mStreamType == AUDIO_STREAM_DEFAULT) ?
+//                                audio_attributes_to_stream_type(&mAttributes) : mStreamType,
+//                        mVolume[AUDIO_INTERLEAVE_LEFT], mVolume[AUDIO_INTERLEAVE_RIGHT]);
+//    result.appendFormat("  format(%#x), channel mask(%#x), channel count(%u)\n",
+//                  format(), mChannelMask, channelCount());
+//    result.appendFormat("  sample rate(%u), original sample rate(%u), speed(%f)\n",
+//            getSampleRate(), mOriginalSampleRate, mPlaybackRate.mSpeed);
+//    result.appendFormat("  frame count(%zu), req. frame count(%zu)\n",
+//                  frameCount(), mReqFrameCount);
+//    result.appendFormat("  notif. frame count(%u), req. notif. frame count(%u),"
+//            " req. notif. per buff(%u)\n",
+//             mNotificationFramesAct, mNotificationFramesReq, mNotificationsPerBufferReq);
+//    result.appendFormat("  latency (%d), selected device Id(%d), routed device Id(%d)\n",
+//                        latency(), mSelectedDeviceId, getRoutedDeviceId());
+//    result.appendFormat("  output(%d) AF latency (%u) AF frame count(%zu) AF SampleRate(%u)\n",
+//                        mOutput, mAfLatency, mAfFrameCount, mAfSampleRate);
+    ::write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+audio_port_handle_t JAudioTrack::getRoutedDeviceId() {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jmethodID jGetRoutedDevice = env->GetMethodID(mAudioTrackCls, "getRoutedDevice",
+            "()Landroid/media/AudioDeviceInfo;");
+    jobject jAudioDeviceInfoObj = env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice);
+    if (env->IsSameObject(jAudioDeviceInfoObj, NULL)) {
+        return AUDIO_PORT_HANDLE_NONE;
+    }
+
+    jclass jAudioDeviceInfoCls = env->FindClass("Landroid/media/AudioDeviceInfo");
+    jmethodID jGetId = env->GetMethodID(jAudioDeviceInfoCls, "getId", "()I");
+    jint routedDeviceId = env->CallIntMethod(jAudioDeviceInfoObj, jGetId);
+    return routedDeviceId;
+}
+
 jobject JAudioTrack::createVolumeShaperConfigurationObj(
         const sp<media::VolumeShaper::Configuration>& config) {
 
diff --git a/media/libmedia/MediaCodecBuffer.cpp b/media/libmedia/MediaCodecBuffer.cpp
index 59d6164..68ae3ea 100644
--- a/media/libmedia/MediaCodecBuffer.cpp
+++ b/media/libmedia/MediaCodecBuffer.cpp
@@ -21,15 +21,13 @@
 #include <media/MediaCodecBuffer.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/MediaBufferBase.h>
 
 namespace android {
 
 MediaCodecBuffer::MediaCodecBuffer(const sp<AMessage> &format, const sp<ABuffer> &buffer)
     : mMeta(new AMessage),
       mFormat(format),
-      mBuffer(buffer),
-      mMediaBufferBase(nullptr) {
+      mBuffer(buffer) {
 }
 
 // ABuffer-like interface
@@ -58,20 +56,6 @@
     return OK;
 }
 
-MediaBufferBase *MediaCodecBuffer::getMediaBufferBase() {
-    if (mMediaBufferBase != NULL) {
-        mMediaBufferBase->add_ref();
-    }
-    return mMediaBufferBase;
-}
-
-void MediaCodecBuffer::setMediaBufferBase(MediaBufferBase *mediaBuffer) {
-    if (mMediaBufferBase != NULL) {
-        mMediaBufferBase->release();
-    }
-    mMediaBufferBase = mediaBuffer;
-}
-
 sp<AMessage> MediaCodecBuffer::meta() {
     return mMeta;
 }
diff --git a/media/libmedia/MediaPlayer2Factory.cpp b/media/libmedia/MediaPlayer2Factory.cpp
index d6aab70..df567ce 100644
--- a/media/libmedia/MediaPlayer2Factory.cpp
+++ b/media/libmedia/MediaPlayer2Factory.cpp
@@ -22,7 +22,6 @@
 #include <cutils/properties.h>
 #include <media/DataSource.h>
 #include <media/MediaPlayer2Engine.h>
-#include <media/stagefright/FileSource.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <utils/Errors.h>
 #include <utils/misc.h>
diff --git a/media/libmedia/MediaPlayer2Manager.cpp b/media/libmedia/MediaPlayer2Manager.cpp
index 720c1e3..c119750 100644
--- a/media/libmedia/MediaPlayer2Manager.cpp
+++ b/media/libmedia/MediaPlayer2Manager.cpp
@@ -64,6 +64,7 @@
 
 #include <memunreachable/memunreachable.h>
 #include <system/audio.h>
+#include <system/window.h>
 
 #include <private/android_filesystem_config.h>
 
@@ -470,8 +471,9 @@
         if (unreachableMemory) {
             result.append("\nDumping unreachable memory:\n");
             // TODO - should limit be an argument parameter?
-            std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */);
-            result.append(s.c_str(), s.size());
+            // TODO: enable GetUnreachableMemoryString if it's part of stable API
+            //std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */);
+            //result.append(s.c_str(), s.size());
         }
     }
     write(fd, result.string(), result.size());
@@ -738,8 +740,8 @@
 
 void MediaPlayer2Manager::Client::disconnectNativeWindow_l() {
     if (mConnectedWindow != NULL && mConnectedWindow->getANativeWindow() != NULL) {
-        status_t err = nativeWindowDisconnect(
-                mConnectedWindow->getANativeWindow(), "disconnectNativeWindow");
+        status_t err = native_window_api_disconnect(
+                mConnectedWindow->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
 
         if (err != OK) {
             ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
@@ -763,7 +765,8 @@
             && mConnectedWindow->getANativeWindow() == nww->getANativeWindow()) {
             return OK;
         }
-        status_t err = nativeWindowConnect(nww->getANativeWindow(), "setVideoSurfaceTexture");
+        status_t err = native_window_api_connect(
+                nww->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
 
         if (err != OK) {
             ALOGE("setVideoSurfaceTexture failed: %d", err);
@@ -792,8 +795,8 @@
         mLock.unlock();
     } else if (nww != NULL) {
         mLock.unlock();
-        status_t err = nativeWindowDisconnect(
-                nww->getANativeWindow(), "disconnectNativeWindow");
+        status_t err = native_window_api_disconnect(
+                nww->getANativeWindow(), NATIVE_WINDOW_API_MEDIA);
 
         if (err != OK) {
             ALOGW("nativeWindowDisconnect returned an error: %s (%d)",
diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp
index 9b06047..4cadeb1 100644
--- a/media/libmedia/TypeConverter.cpp
+++ b/media/libmedia/TypeConverter.cpp
@@ -115,6 +115,9 @@
     MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_DIRECT_PCM),
     MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ),
     MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_VOIP_RX),
+    // FIXME: this cast will be removed when the flag will be
+    // declared in types.hal for audio HAL V4.0 and auto imported to audio-base.h
+    {"AUDIO_OUTPUT_FLAG_INCALL_MUSIC", static_cast<audio_output_flags_t>(AUDIO_OUTPUT_FLAG_INCALL_MUSIC)},
     TERMINATOR
 };
 
diff --git a/media/libmedia/include/media/CounterMetric.h b/media/libmedia/include/media/CounterMetric.h
new file mode 100644
index 0000000..f39ca7c
--- /dev/null
+++ b/media/libmedia/include/media/CounterMetric.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#ifndef ANDROID_COUNTER_METRIC_H_
+#define ANDROID_COUNTER_METRIC_H_
+
+#include <media/MediaAnalyticsItem.h>
+#include <utils/Log.h>
+
+namespace android {
+
+
+// The CounterMetric class is used to hold counts of operations or events.
+// A CounterMetric can break down counts by a dimension specified by the
+// application. E.g. an application may want to track counts broken out by
+// error code or the size of some parameter.
+//
+// Example:
+//
+//   CounterMetric<status_t> workCounter;
+//   workCounter("workCounterName", "result_status");
+//
+//   status_t err = DoWork();
+//
+//   // Increments the number of times called with the given error code.
+//   workCounter.Increment(err);
+//
+//   std::map<int, int64_t> values;
+//    metric.ExportValues(
+//        [&] (int attribute_value, int64_t value) {
+//             values[attribute_value] = value;
+//        });
+//
+//   // Do something with the exported stat.
+//
+template<typename AttributeType>
+class CounterMetric {
+ public:
+  // Instantiate the counter with the given metric name and
+  // attribute names. |attribute_names| must not be null.
+  CounterMetric(
+      const std::string& metric_name,
+      const std::string& attribute_name)
+          : metric_name_(metric_name),
+            attribute_name_(attribute_name) {}
+
+  // Increment the count of times the operation occurred with this
+  // combination of attributes.
+  void Increment(AttributeType attribute) {
+    if (values_.find(attribute) == values_.end()) {
+      values_[attribute] = 1;
+    } else {
+      values_[attribute] = values_[attribute] + 1;
+    }
+  };
+
+  // Export the metrics to the provided |function|. Each value for Attribute
+  // has a separate count. As such, |function| will be called once per value
+  // of Attribute.
+  void ExportValues(
+      std::function<void (const AttributeType&,
+                          const int64_t count)> function) {
+    for (auto it = values_.begin(); it != values_.end(); it++) {
+      function(it->first, it->second);
+    }
+  }
+
+  const std::string& metric_name() { return metric_name_; };
+
+ private:
+  const std::string metric_name_;
+  const std::string attribute_name_;
+  std::map<AttributeType, int64_t> values_;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_COUNTER_METRIC_H_
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index 5d25e4d..f2b25cd 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -19,18 +19,22 @@
 #define DRM_HAL_H_
 
 #include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.1/IDrmPlugin.h>
 #include <android/hardware/drm/1.0/IDrmPluginListener.h>
 #include <android/hardware/drm/1.0/IDrmFactory.h>
 
+#include <media/CounterMetric.h>
 #include <media/IDrm.h>
 #include <media/IDrmClient.h>
+#include <media/MediaAnalyticsItem.h>
 #include <utils/threads.h>
 
-using ::android::hardware::drm::V1_0::EventType;
-using ::android::hardware::drm::V1_0::IDrmFactory;
-using ::android::hardware::drm::V1_0::IDrmPlugin;
-using ::android::hardware::drm::V1_0::IDrmPluginListener;
-using ::android::hardware::drm::V1_0::KeyStatus;
+namespace drm = ::android::hardware::drm;
+using drm::V1_0::EventType;
+using drm::V1_0::IDrmFactory;
+using drm::V1_0::IDrmPlugin;
+using drm::V1_0::IDrmPluginListener;
+using drm::V1_0::KeyStatus;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
@@ -98,12 +102,22 @@
     virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease);
     virtual status_t releaseAllSecureStops();
 
+    virtual status_t getHdcpLevels(DrmPlugin::HdcpLevel *connectedLevel,
+            DrmPlugin::HdcpLevel *maxLevel) const;
+    virtual status_t getNumberOfSessions(uint32_t *currentSessions,
+            uint32_t *maxSessions) const;
+    virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
+            DrmPlugin::SecurityLevel *level) const;
+    virtual status_t setSecurityLevel(Vector<uint8_t> const &sessionId,
+            const DrmPlugin::SecurityLevel& level);
+
     virtual status_t getPropertyString(String8 const &name, String8 &value ) const;
     virtual status_t getPropertyByteArray(String8 const &name,
                                           Vector<uint8_t> &value ) const;
     virtual status_t setPropertyString(String8 const &name, String8 const &value ) const;
     virtual status_t setPropertyByteArray(String8 const &name,
                                           Vector<uint8_t> const &value ) const;
+    virtual status_t getMetrics(MediaAnalyticsItem *item);
 
     virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
                                         String8 const &algorithm);
@@ -165,6 +179,9 @@
 
     const Vector<sp<IDrmFactory>> mFactories;
     sp<IDrmPlugin> mPlugin;
+    sp<drm::V1_1::IDrmPlugin> mPluginV1_1;
+
+    CounterMetric<status_t> mOpenSessionCounter;
 
     Vector<Vector<uint8_t>> mOpenSessions;
     void closeOpenSessions();
diff --git a/media/libmedia/include/media/IDrm.h b/media/libmedia/include/media/IDrm.h
index a57e372..9266f99 100644
--- a/media/libmedia/include/media/IDrm.h
+++ b/media/libmedia/include/media/IDrm.h
@@ -18,6 +18,7 @@
 #include <media/stagefright/foundation/ABase.h>
 #include <media/drm/DrmAPI.h>
 #include <media/IDrmClient.h>
+#include <media/MediaAnalyticsItem.h>
 
 #ifndef ANDROID_IDRM_H_
 
@@ -78,6 +79,16 @@
     virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) = 0;
     virtual status_t releaseAllSecureStops() = 0;
 
+    virtual status_t getHdcpLevels(DrmPlugin::HdcpLevel *connectedLevel,
+            DrmPlugin::HdcpLevel *maxLevel)
+            const = 0;
+    virtual status_t getNumberOfSessions(uint32_t *currentSessions,
+            uint32_t *maxSessions) const = 0;
+    virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
+            DrmPlugin::SecurityLevel *level) const = 0;
+    virtual status_t setSecurityLevel(Vector<uint8_t> const &sessionId,
+            const DrmPlugin::SecurityLevel& level) = 0;
+
     virtual status_t getPropertyString(String8 const &name, String8 &value) const = 0;
     virtual status_t getPropertyByteArray(String8 const &name,
                                           Vector<uint8_t> &value) const = 0;
@@ -86,6 +97,8 @@
     virtual status_t setPropertyByteArray(String8 const &name,
                                           Vector<uint8_t> const &value) const = 0;
 
+    virtual status_t getMetrics(MediaAnalyticsItem *item) = 0;
+
     virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
                                         String8 const &algorithm) = 0;
 
diff --git a/media/libmedia/include/media/IMediaSource.h b/media/libmedia/include/media/IMediaSource.h
index 493742e..dabe231 100644
--- a/media/libmedia/include/media/IMediaSource.h
+++ b/media/libmedia/include/media/IMediaSource.h
@@ -28,7 +28,6 @@
 
 namespace android {
 
-class MetaData;
 class MediaBufferGroup;
 
 class IMediaSource : public IInterface {
diff --git a/media/libmedia/include/media/JAudioTrack.h b/media/libmedia/include/media/JAudioTrack.h
index 8af30b7..10fa5e8 100644
--- a/media/libmedia/include/media/JAudioTrack.h
+++ b/media/libmedia/include/media/JAudioTrack.h
@@ -104,6 +104,12 @@
     size_t frameCount();
     size_t channelCount();
 
+    /* Returns this track's estimated latency in milliseconds.
+     * This includes the latency due to AudioTrack buffer size, AudioMixer (if any)
+     * and audio hardware driver.
+     */
+    uint32_t latency();
+
     /* Return the total number of frames played since playback start.
      * The counter will wrap (overflow) periodically, e.g. every ~27 hours at 44.1 kHz.
      * It is reset to zero by flush(), reload(), and stop().
@@ -130,7 +136,7 @@
      * Returns true if timestamp is valid.
      * The timestamp parameter is undefined on return, if false is returned.
      */
-    bool getTimeStamp(AudioTimestamp& timestamp);
+    bool getTimestamp(AudioTimestamp& timestamp);
 
     /* Set source playback rate for timestretch
      * 1.0 is normal speed: < 1.0 is slower, > 1.0 is faster
@@ -253,6 +259,17 @@
 
     audio_format_t format();
 
+    /*
+     * Dumps the state of an audio track.
+     * Not a general-purpose API; intended only for use by media player service to dump its tracks.
+     */
+    status_t dump(int fd, const Vector<String16>& args) const;
+
+    /* Returns the ID of the audio device actually used by the output to which this AudioTrack is
+     * attached. When the AudioTrack is inactive, it will return AUDIO_PORT_HANDLE_NONE.
+     */
+    audio_port_handle_t getRoutedDeviceId();
+
 private:
     jclass mAudioTrackCls;
     jobject mAudioTrackObj;
diff --git a/media/libmedia/include/media/MediaBufferHolder.h b/media/libmedia/include/media/MediaBufferHolder.h
new file mode 100644
index 0000000..e8e2c4b
--- /dev/null
+++ b/media/libmedia/include/media/MediaBufferHolder.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018, 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.
+ */
+
+#ifndef MEDIA_BUFFER_HOLDER_H_
+
+#define MEDIA_BUFFER_HOLDER_H_
+
+#include <media/stagefright/MediaBuffer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct MediaBufferHolder : public RefBase {
+    MediaBufferHolder(MediaBuffer* buffer)
+        : mMediaBuffer(buffer) {
+        if (mMediaBuffer != nullptr) {
+            mMediaBuffer->add_ref();
+        }
+    }
+
+    virtual ~MediaBufferHolder() {
+        if (mMediaBuffer != nullptr) {
+            mMediaBuffer->release();
+        }
+    }
+
+    MediaBuffer* mediaBuffer() { return mMediaBuffer; }
+
+private:
+    MediaBuffer* const mMediaBuffer;
+};
+
+}  // android
+
+#endif  // MEDIA_BUFFER_HOLDER_H_
diff --git a/media/libmedia/include/media/MediaCodecBuffer.h b/media/libmedia/include/media/MediaCodecBuffer.h
index 501c00b..2c16fba 100644
--- a/media/libmedia/include/media/MediaCodecBuffer.h
+++ b/media/libmedia/include/media/MediaCodecBuffer.h
@@ -50,9 +50,6 @@
     size_t offset() const;
     // Default implementation calls ABuffer::setRange() and returns OK.
     virtual status_t setRange(size_t offset, size_t size);
-    // TODO: These can be removed if we finish replacing all MediaBuffer's.
-    MediaBufferBase *getMediaBufferBase();
-    void setMediaBufferBase(MediaBufferBase *mediaBuffer);
 
     // TODO: Specify each field for meta/format.
     sp<AMessage> meta();
@@ -66,7 +63,6 @@
     const sp<AMessage> mMeta;
     sp<AMessage> mFormat;
     const sp<ABuffer> mBuffer;
-    MediaBufferBase *mMediaBufferBase;
 };
 
 }  // namespace android
diff --git a/media/libmedia/nuplayer2/Android.bp b/media/libmedia/nuplayer2/Android.bp
index d609ba0..700c840 100644
--- a/media/libmedia/nuplayer2/Android.bp
+++ b/media/libmedia/nuplayer2/Android.bp
@@ -22,7 +22,6 @@
     ],
 
     include_dirs: [
-        "frameworks/av/media/libmedia/include",
         "frameworks/av/media/libstagefright",
         "frameworks/av/media/libstagefright/httplive",
         "frameworks/av/media/libstagefright/include",
@@ -54,6 +53,10 @@
         "libpowermanager",
     ],
 
+    static_libs: [
+        "libmedia_helper",
+    ],
+
     name: "libstagefright_nuplayer2",
 
     tags: ["eng"],
diff --git a/media/libmedia/nuplayer2/GenericSource.cpp b/media/libmedia/nuplayer2/GenericSource.cpp
index 011691a..6d5b14d 100644
--- a/media/libmedia/nuplayer2/GenericSource.cpp
+++ b/media/libmedia/nuplayer2/GenericSource.cpp
@@ -24,6 +24,7 @@
 #include <binder/IServiceManager.h>
 #include <cutils/properties.h>
 #include <media/DataSource.h>
+#include <media/MediaBufferHolder.h>
 #include <media/IMediaExtractorService.h>
 #include <media/MediaHTTPService.h>
 #include <media/MediaExtractor.h>
@@ -33,7 +34,6 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSourceFactory.h>
-#include <media/stagefright/FileSource.h>
 #include <media/stagefright/InterfaceUtils.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaClock.h>
@@ -1168,8 +1168,7 @@
 
         // data is already provided in the buffer
         ab = new ABuffer(NULL, mb->range_length());
-        mb->add_ref();
-        ab->setMediaBufferBase(mb);
+        ab->meta()->setObject("mediaBufferHolder", new MediaBufferHolder(mb));
 
         // Modular DRM: Required b/c of the above add_ref.
         // If ref>0, there must be an observer, or it'll crash at release().
diff --git a/media/libmedia/nuplayer2/NuPlayer2Decoder.cpp b/media/libmedia/nuplayer2/NuPlayer2Decoder.cpp
index 25d41f3..a436592 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Decoder.cpp
+++ b/media/libmedia/nuplayer2/NuPlayer2Decoder.cpp
@@ -28,6 +28,7 @@
 #include "NuPlayer2Source.h"
 
 #include <cutils/properties.h>
+#include <media/MediaBufferHolder.h>
 #include <media/MediaCodecBuffer.h>
 #include <media/NdkMediaCodec.h>
 #include <media/NdkWrapper.h>
@@ -40,6 +41,7 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/SurfaceUtils.h>
 
+#include <system/window.h>
 #include "ATSParser.h"
 
 namespace android {
@@ -241,22 +243,25 @@
                 //
                 // at this point MediaPlayer2Manager::client has already connected to the
                 // surface, which MediaCodec does not expect
-                err = nativeWindowDisconnect(nww->getANativeWindow(), "kWhatSetVideoSurface(nww)");
+                err = native_window_api_disconnect(nww->getANativeWindow(),
+                                                   NATIVE_WINDOW_API_MEDIA);
                 if (err == OK) {
                     err = mCodec->setOutputSurface(nww);
                     ALOGI_IF(err, "codec setOutputSurface returned: %d", err);
                     if (err == OK) {
                         // reconnect to the old surface as MPS::Client will expect to
                         // be able to disconnect from it.
-                        (void)nativeWindowConnect(mNativeWindow->getANativeWindow(),
-                                                  "kWhatSetVideoSurface(mNativeWindow)");
+                        (void)native_window_api_connect(mNativeWindow->getANativeWindow(),
+                                                        NATIVE_WINDOW_API_MEDIA);
+
                         mNativeWindow = nww;
                     }
                 }
                 if (err != OK) {
                     // reconnect to the new surface on error as MPS::Client will expect to
                     // be able to disconnect from it.
-                    (void)nativeWindowConnect(nww->getANativeWindow(), "kWhatSetVideoSurface(err)");
+                    (void)native_window_api_connect(nww->getANativeWindow(),
+                                                    NATIVE_WINDOW_API_MEDIA);
                 }
             }
 
@@ -326,7 +331,8 @@
     status_t err;
     if (mNativeWindow != NULL && mNativeWindow->getANativeWindow() != NULL) {
         // disconnect from surface as MediaCodec will reconnect
-        err = nativeWindowDisconnect(mNativeWindow->getANativeWindow(), "onConfigure");
+        err = native_window_api_disconnect(mNativeWindow->getANativeWindow(),
+                                           NATIVE_WINDOW_API_MEDIA);
         // We treat this as a warning, as this is a preparatory step.
         // Codec will try to connect to the surface, which is where
         // any error signaling will occur.
@@ -540,7 +546,8 @@
 
         if (mNativeWindow != NULL && mNativeWindow->getANativeWindow() != NULL) {
             // reconnect to surface as MediaCodec disconnected from it
-            status_t error = nativeWindowConnect(mNativeWindow->getANativeWindow(), "onShutdown");
+            status_t error = native_window_api_connect(mNativeWindow->getANativeWindow(),
+                                                       NATIVE_WINDOW_API_MEDIA);
             ALOGW_IF(error != NO_ERROR,
                     "[%s] failed to connect to native window, error=%d",
                     mComponentName.c_str(), error);
@@ -1075,16 +1082,17 @@
                 memcpy(codecBuffer->data(), buffer->data(), buffer->size());
             } else { // No buffer->data()
                 //Modular DRM
-                mediaBuf = (MediaBuffer*)buffer->getMediaBufferBase();
+                sp<RefBase> holder;
+                if (buffer->meta()->findObject("mediaBufferHolder", &holder)) {
+                    mediaBuf = (holder != nullptr) ?
+                        static_cast<MediaBufferHolder*>(holder.get())->mediaBuffer() : nullptr;
+                }
                 if (mediaBuf != NULL) {
                     codecBuffer->setRange(0, mediaBuf->size());
                     memcpy(codecBuffer->data(), mediaBuf->data(), mediaBuf->size());
 
                     sp<MetaData> meta_data = mediaBuf->meta_data();
                     cryptInfo = AMediaCodecCryptoInfoWrapper::Create(meta_data);
-
-                    // since getMediaBuffer() has incremented the refCount
-                    mediaBuf->release();
                 } else { // No mediaBuf
                     ALOGE("onInputBufferFetched: buffer->data()/mediaBuf are NULL for %p",
                             buffer.get());
diff --git a/media/libmedia/nuplayer2/NuPlayer2Drm.h b/media/libmedia/nuplayer2/NuPlayer2Drm.h
index e762ccc..f9c8711 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Drm.h
+++ b/media/libmedia/nuplayer2/NuPlayer2Drm.h
@@ -18,7 +18,6 @@
 #define NUPLAYER2_DRM_H_
 
 #include <binder/Parcel.h>
-#include <media/stagefright/MetaData.h> // for CryptInfo
 
 
 namespace android {
diff --git a/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp b/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp
index 71f5dce..1a9f246 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp
+++ b/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp
@@ -27,7 +27,6 @@
 #include <media/stagefright/foundation/AUtils.h>
 #include <media/stagefright/MediaClock.h>
 #include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MetaData.h>
 #include <media/stagefright/Utils.h>
 #include <media/stagefright/VideoFrameScheduler.h>
 #include <media/MediaCodecBuffer.h>
diff --git a/media/libmediaextractor/Android.bp b/media/libmediaextractor/Android.bp
index dcdb320..83fea39 100644
--- a/media/libmediaextractor/Android.bp
+++ b/media/libmediaextractor/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
     name: "libmediaextractor",
 
     include_dirs: [
@@ -15,6 +15,7 @@
     ],
 
     shared_libs: [
+        "libbinder",
         "libstagefright_foundation",
         "libutils",
         "libcutils",
@@ -23,6 +24,8 @@
 
     srcs: [
         "DataSource.cpp",
+        "MediaBuffer.cpp",
+        "MediaBufferGroup.cpp",
         "MediaSource.cpp",
         "MediaExtractor.cpp",
     ],
diff --git a/media/libstagefright/foundation/MediaBuffer.cpp b/media/libmediaextractor/MediaBuffer.cpp
similarity index 98%
rename from media/libstagefright/foundation/MediaBuffer.cpp
rename to media/libmediaextractor/MediaBuffer.cpp
index 95951dd..28fc760 100644
--- a/media/libstagefright/foundation/MediaBuffer.cpp
+++ b/media/libmediaextractor/MediaBuffer.cpp
@@ -26,8 +26,6 @@
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MetaData.h>
 
-#include <ui/GraphicBuffer.h>
-
 namespace android {
 
 /* static */
diff --git a/media/libstagefright/foundation/MediaBufferGroup.cpp b/media/libmediaextractor/MediaBufferGroup.cpp
similarity index 100%
rename from media/libstagefright/foundation/MediaBufferGroup.cpp
rename to media/libmediaextractor/MediaBufferGroup.cpp
diff --git a/media/libmediaextractor/include/media/MediaSource.h b/media/libmediaextractor/include/media/MediaSource.h
index 98b136b..25d691d 100644
--- a/media/libmediaextractor/include/media/MediaSource.h
+++ b/media/libmediaextractor/include/media/MediaSource.h
@@ -30,7 +30,6 @@
 namespace android {
 
 class MediaBuffer;
-class MetaData;
 class IMediaSource;
 
 struct MediaSource : public virtual RefBase {
diff --git a/media/libstagefright/include/media/stagefright/MediaBuffer.h b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h
similarity index 97%
rename from media/libstagefright/include/media/stagefright/MediaBuffer.h
rename to media/libmediaextractor/include/media/stagefright/MediaBuffer.h
index 367a467..2b51081 100644
--- a/media/libstagefright/include/media/stagefright/MediaBuffer.h
+++ b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h
@@ -20,7 +20,6 @@
 
 #include <atomic>
 #include <list>
-#include <media/stagefright/foundation/MediaBufferBase.h>
 
 #include <pthread.h>
 
@@ -48,7 +47,7 @@
     MediaBufferObserver &operator=(const MediaBufferObserver &);
 };
 
-class MediaBuffer : public MediaBufferBase {
+class MediaBuffer {
 public:
     // allocations larger than or equal to this will use shared memory.
     static const size_t kSharedMemThreshold = 64 * 1024;
@@ -72,11 +71,11 @@
     //
     // If no MediaBufferGroup is set, the local reference count must be zero
     // when called, whereupon the MediaBuffer is deleted.
-    virtual void release();
+    void release();
 
     // Increments the local reference count.
     // Use only when MediaBufferGroup is set.
-    virtual void add_ref();
+    void add_ref();
 
     void *data() const;
     size_t size() const;
@@ -144,7 +143,7 @@
         return mObserver != nullptr;
     }
 
-    virtual ~MediaBuffer();
+    ~MediaBuffer();
 
     sp<IMemory> mMemory;
 
diff --git a/media/libstagefright/include/media/stagefright/MediaBufferGroup.h b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
similarity index 99%
rename from media/libstagefright/include/media/stagefright/MediaBufferGroup.h
rename to media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
index 3051406..3041181 100644
--- a/media/libstagefright/include/media/stagefright/MediaBufferGroup.h
+++ b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
@@ -25,7 +25,6 @@
 namespace android {
 
 class MediaBuffer;
-class MetaData;
 
 class MediaBufferGroup : public MediaBufferObserver {
 public:
diff --git a/media/libmediametrics/Android.bp b/media/libmediametrics/Android.bp
index 15dac59..07e124b 100644
--- a/media/libmediametrics/Android.bp
+++ b/media/libmediametrics/Android.bp
@@ -1,4 +1,6 @@
-cc_library_shared {
+// TODO: change it back to cc_library_shared when there is a way to
+// expose media metrics as stable API.
+cc_library {
     name: "libmediametrics",
 
     srcs: [
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 423dfb8..2e7efad 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -722,7 +722,7 @@
 
 
 std::string MediaAnalyticsItem::toString() {
-   return toString(-1);
+   return toString(PROTO_LAST);
 }
 
 std::string MediaAnalyticsItem::toString(int version) {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 33c3094..511f46f 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -24,6 +24,7 @@
 #include <binder/IServiceManager.h>
 #include <cutils/properties.h>
 #include <media/DataSource.h>
+#include <media/MediaBufferHolder.h>
 #include <media/MediaExtractor.h>
 #include <media/MediaSource.h>
 #include <media/IMediaHTTPService.h>
@@ -1160,14 +1161,12 @@
 
         // data is already provided in the buffer
         ab = new ABuffer(NULL, mb->range_length());
-        mb->add_ref();
-        ab->setMediaBufferBase(mb);
+        ab->meta()->setObject("mediaBufferHolder", new MediaBufferHolder(mb));
 
         // Modular DRM: Required b/c of the above add_ref.
         // If ref>0, there must be an observer, or it'll crash at release().
         // TODO: MediaBuffer might need to be revised to ease such need.
         mb->setObserver(this);
-        // setMediaBufferBase() interestingly doesn't increment the ref count on its own.
         // Extra increment (since we want to keep mb alive and attached to ab beyond this function
         // call. This is to counter the effect of mb->release() towards the end.
         mb->add_ref();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 1b02adb..1aca96c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -29,6 +29,7 @@
 
 #include <cutils/properties.h>
 #include <media/ICrypto.h>
+#include <media/MediaBufferHolder.h>
 #include <media/MediaCodecBuffer.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -1061,16 +1062,17 @@
                 memcpy(codecBuffer->data(), buffer->data(), buffer->size());
             } else { // No buffer->data()
                 //Modular DRM
-                mediaBuf = (MediaBuffer*)buffer->getMediaBufferBase();
+                sp<RefBase> holder;
+                if (buffer->meta()->findObject("mediaBufferHolder", &holder)) {
+                    mediaBuf = (holder != nullptr) ?
+                        static_cast<MediaBufferHolder*>(holder.get())->mediaBuffer() : nullptr;
+                }
                 if (mediaBuf != NULL) {
                     codecBuffer->setRange(0, mediaBuf->size());
                     memcpy(codecBuffer->data(), mediaBuf->data(), mediaBuf->size());
 
                     sp<MetaData> meta_data = mediaBuf->meta_data();
                     cryptInfo = NuPlayerDrm::getSampleCryptoInfo(meta_data);
-
-                    // since getMediaBuffer() has incremented the refCount
-                    mediaBuf->release();
                 } else { // No mediaBuf
                     ALOGE("onInputBufferFetched: buffer->data()/mediaBuf are NULL for %p",
                             buffer.get());
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 14ea2a8..0ded5be 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -43,6 +43,7 @@
 #include <media/stagefright/PersistentSurface.h>
 #include <media/stagefright/SurfaceUtils.h>
 #include <media/hardware/HardwareAPI.h>
+#include <media/MediaBufferHolder.h>
 #include <media/OMXBuffer.h>
 #include <media/omx/1.0/WOmxNode.h>
 
@@ -5606,7 +5607,7 @@
     // by this "MediaBuffer" object. Now that the OMX component has
     // told us that it's done with the input buffer, we can decrement
     // the mediaBuffer's reference count.
-    info->mData->setMediaBufferBase(NULL);
+    info->mData->meta()->setObject("mediaBufferHolder", sp<MediaBufferHolder>(nullptr));
 
     PortMode mode = getPortMode(kPortIndexInput);
 
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index c3d9c24..01f73a1 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -128,7 +128,6 @@
         "libstagefright_timedtext",
         "libvpx",
         "libwebm",
-        "libstagefright_mpeg2support",
         "libstagefright_esds",
         "libstagefright_id3",
         "libFLAC",
@@ -173,7 +172,7 @@
     },
 }
 
-cc_library_shared {
+cc_library {
     name: "libstagefright_player2",
 
     srcs: [
@@ -190,7 +189,6 @@
         "NuCachedSource2.cpp",
         "RemoteMediaExtractor.cpp",
         "RemoteMediaSource.cpp",
-        "SurfaceUtils.cpp",
         "Utils.cpp",
         "VideoFrameScheduler.cpp",
         "http/MediaHTTP.cpp",
@@ -202,7 +200,6 @@
         "libdrmframework",
         "libgui",
         "liblog",
-        "libmedia_omx",
         "libmedia_player2_util",
         "libaudioclient",
         "libmediaextractor",
@@ -213,15 +210,15 @@
         "libutils",
         "libmedia_helper",
         "libstagefright_foundation",
-        "libdl",
         "libziparchive",
     ],
 
     static_libs: [
         "libstagefright_esds",
-        "libstagefright_id3",
-        "libstagefright_mpeg2support",
-        "libstagefright_timedtext",
+    ],
+
+    header_libs:[
+        "media_plugin_headers",
     ],
 
     export_shared_lib_headers: [
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index eea9c78..ebc9b0a 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -665,16 +665,16 @@
     int32_t flags = 0;
     int32_t tmp = 0;
     if (buffer->meta()->findInt32("eos", &tmp) && tmp) {
-        flags |= C2BufferPack::FLAG_END_OF_STREAM;
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
         ALOGV("input EOS");
     }
     if (buffer->meta()->findInt32("csd", &tmp) && tmp) {
-        flags |= C2BufferPack::FLAG_CODEC_CONFIG;
+        flags |= C2FrameData::FLAG_CODEC_CONFIG;
     }
     std::unique_ptr<C2Work> work(new C2Work);
-    work->input.flags = (C2BufferPack::flags_t)flags;
+    work->input.flags = (C2FrameData::flags_t)flags;
     work->input.ordinal.timestamp = timeUs;
-    work->input.ordinal.frame_index = mFrameIndex++;
+    work->input.ordinal.frameIndex = mFrameIndex++;
     work->input.buffers.clear();
     {
         Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
@@ -724,7 +724,7 @@
         return OK;
     }
 
-    std::list<C2ConstGraphicBlock> blocks = c2Buffer->data().graphicBlocks();
+    std::vector<C2ConstGraphicBlock> blocks = c2Buffer->data().graphicBlocks();
     if (blocks.size() != 1u) {
         ALOGE("# of graphic blocks expected to be 1, but %zu", blocks.size());
         return UNKNOWN_ERROR;
@@ -881,7 +881,7 @@
         }
 
         const std::unique_ptr<C2Worklet> &worklet = work->worklets.front();
-        if (worklet->output.ordinal.frame_index < mFirstValidFrameIndex) {
+        if ((worklet->output.ordinal.frameIndex - mFirstValidFrameIndex.load()).peek() < 0) {
             // Discard frames from previous generation.
             continue;
         }
@@ -897,7 +897,7 @@
         if (buffer) {
             // TODO: transfer infos() into buffer metadata
         }
-        for (const auto &info : worklet->output.infos) {
+        for (const auto &info : worklet->output.configUpdate) {
             if (info->coreIndex() == C2StreamCsdInfo::output::CORE_INDEX) {
                 ALOGV("csd found");
                 csdInfo = static_cast<const C2StreamCsdInfo::output *>(info.get());
@@ -905,7 +905,7 @@
         }
 
         int32_t flags = 0;
-        if (worklet->output.flags & C2BufferPack::FLAG_END_OF_STREAM) {
+        if (worklet->output.flags & C2FrameData::FLAG_END_OF_STREAM) {
             flags |= MediaCodec::BUFFER_FLAG_EOS;
             ALOGV("output EOS");
         }
@@ -914,7 +914,7 @@
         if (csdInfo != nullptr) {
             Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
             if ((*buffers)->registerCsd(csdInfo, &index, &outBuffer)) {
-                outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp);
+                outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek());
                 outBuffer->meta()->setInt32("flags", flags | MediaCodec::BUFFER_FLAG_CODECCONFIG);
                 ALOGV("csd index = %zu", index);
 
@@ -947,7 +947,7 @@
             }
         }
 
-        outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp);
+        outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek());
         outBuffer->meta()->setInt32("flags", flags);
         ALOGV("index = %zu", index);
         mCallback->onOutputBufferAvailable(index, outBuffer);
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 77d9ce4..559b108 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -51,7 +51,6 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaFilter.h>
-#include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/PersistentSurface.h>
 #include <media/stagefright/SurfaceUtils.h>
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index cf800b2..04d83af 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -23,6 +23,7 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/Surface.h>
 #include <media/ICrypto.h>
+#include <media/MediaBufferHolder.h>
 #include <media/MediaCodecBuffer.h>
 #include <media/MediaSource.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -457,13 +458,6 @@
       mGeneration(0) {
     CHECK(mLooper != NULL);
 
-    AString mime;
-    CHECK(mOutputFormat->findString("mime", &mime));
-
-    if (!strncasecmp("video/", mime.c_str(), 6)) {
-        mIsVideo = true;
-    }
-
     if (!(mFlags & FLAG_USE_SURFACE_INPUT)) {
         mPuller = new Puller(source);
     }
@@ -487,6 +481,7 @@
 }
 
 status_t MediaCodecSource::initEncoder() {
+
     mReflector = new AHandlerReflector<MediaCodecSource>(this);
     mLooper->registerHandler(mReflector);
 
@@ -500,23 +495,12 @@
 
     AString outputMIME;
     CHECK(mOutputFormat->findString("mime", &outputMIME));
+    mIsVideo = outputMIME.startsWithIgnoreCase("video/");
 
-    Vector<AString> matchingCodecs;
-    MediaCodecList::findMatchingCodecs(
-            outputMIME.c_str(), true /* encoder */,
-            ((mFlags & FLAG_PREFER_SOFTWARE_CODEC) ? MediaCodecList::kPreferSoftwareCodecs : 0),
-            &matchingCodecs);
-
+    AString name;
     status_t err = NO_INIT;
-    for (size_t ix = 0; ix < matchingCodecs.size(); ++ix) {
-        mEncoder = MediaCodec::CreateByComponentName(
-                mCodecLooper, matchingCodecs[ix]);
-
-        if (mEncoder == NULL) {
-            continue;
-        }
-
-        ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
+    if (mOutputFormat->findString("testing-name", &name)) {
+        mEncoder = MediaCodec::CreateByComponentName(mCodecLooper, name);
 
         mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
         mEncoder->setCallback(mEncoderActivityNotify);
@@ -526,12 +510,38 @@
                     NULL /* nativeWindow */,
                     NULL /* crypto */,
                     MediaCodec::CONFIGURE_FLAG_ENCODE);
+    } else {
+        Vector<AString> matchingCodecs;
+        MediaCodecList::findMatchingCodecs(
+                outputMIME.c_str(), true /* encoder */,
+                ((mFlags & FLAG_PREFER_SOFTWARE_CODEC) ? MediaCodecList::kPreferSoftwareCodecs : 0),
+                &matchingCodecs);
 
-        if (err == OK) {
-            break;
+        for (size_t ix = 0; ix < matchingCodecs.size(); ++ix) {
+            mEncoder = MediaCodec::CreateByComponentName(
+                    mCodecLooper, matchingCodecs[ix]);
+
+            if (mEncoder == NULL) {
+                continue;
+            }
+
+            ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
+
+            mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
+            mEncoder->setCallback(mEncoderActivityNotify);
+
+            err = mEncoder->configure(
+                        mOutputFormat,
+                        NULL /* nativeWindow */,
+                        NULL /* crypto */,
+                        MediaCodec::CONFIGURE_FLAG_ENCODE);
+
+            if (err == OK) {
+                break;
+            }
+            mEncoder->release();
+            mEncoder = NULL;
         }
-        mEncoder->release();
-        mEncoder = NULL;
     }
 
     if (err != OK) {
@@ -730,7 +740,8 @@
             if (mIsVideo) {
                 // video encoder will release MediaBuffer when done
                 // with underlying data.
-                inbuf->setMediaBufferBase(mbuf);
+                inbuf->meta()->setObject("mediaBufferHolder", new MediaBufferHolder(mbuf));
+                mbuf->release();
             } else {
                 mbuf->release();
             }
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index bbf87f0..472c137 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -26,7 +26,6 @@
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/InterfaceUtils.h>
 #include <media/stagefright/MediaExtractorFactory.h>
-#include <media/stagefright/MetaData.h>
 #include <media/IMediaExtractor.h>
 #include <media/IMediaExtractorService.h>
 #include <cutils/properties.h>
@@ -38,8 +37,6 @@
 
 namespace android {
 
-static const char *kSystemApkPath = "/system/app/MediaComponents/MediaComponents.apk";
-
 // static
 sp<IMediaExtractor> MediaExtractorFactory::Create(
         const sp<DataSource> &source, const char *mime) {
@@ -246,7 +243,7 @@
 }
 
 //static
-void MediaExtractorFactory::RegisterExtractors(
+void MediaExtractorFactory::RegisterExtractorsInApk(
         const char *apkPath, List<sp<ExtractorPlugin>> &pluginList) {
     ALOGV("search for plugins at %s", apkPath);
     ZipArchiveHandle zipHandle;
@@ -254,7 +251,8 @@
     if (ret == 0) {
         char abi[PROPERTY_VALUE_MAX];
         property_get("ro.product.cpu.abi", abi, "arm64-v8a");
-        ZipString prefix(String8::format("lib/%s/", abi).c_str());
+        String8 prefix8 = String8::format("lib/%s/", abi);
+        ZipString prefix(prefix8.c_str());
         ZipString suffix("extractor.so");
         void* cookie;
         ret = StartIteration(zipHandle, &cookie, &prefix, &suffix);
@@ -264,6 +262,8 @@
             while (Next(cookie, &entry, &name) == 0) {
                 String8 libPath = String8(apkPath) + "!/" +
                     String8(reinterpret_cast<const char*>(name.name), name.name_length);
+                // TODO: Open with a linker namespace so that it can be linked with sub-libraries
+                // within the apk instead of system libraries already loaded.
                 void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL);
                 if (libHandle) {
                     MediaExtractor::GetExtractorDef getDef =
@@ -290,6 +290,38 @@
     }
 }
 
+//static
+void MediaExtractorFactory::RegisterExtractorsInSystem(
+        const char *libDirPath, List<sp<ExtractorPlugin>> &pluginList) {
+    ALOGV("search for plugins at %s", libDirPath);
+    DIR *libDir = opendir(libDirPath);
+    if (libDir) {
+        struct dirent* libEntry;
+        while ((libEntry = readdir(libDir))) {
+            String8 libPath = String8(libDirPath) + "/" + libEntry->d_name;
+            void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL);
+            if (libHandle) {
+                MediaExtractor::GetExtractorDef getDef =
+                    (MediaExtractor::GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
+                if (getDef) {
+                    ALOGV("registering sniffer for %s", libPath.string());
+                    RegisterExtractor(
+                            new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
+                } else {
+                    ALOGW("%s does not contain sniffer", libPath.string());
+                    dlclose(libHandle);
+                }
+            } else {
+                ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno));
+            }
+        }
+
+        closedir(libDir);
+    } else {
+        ALOGE("couldn't opendir(%s)", libDirPath);
+    }
+}
+
 // static
 void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) {
     Mutex::Autolock autoLock(gPluginMutex);
@@ -302,10 +334,20 @@
 
     std::shared_ptr<List<sp<ExtractorPlugin>>> newList(new List<sp<ExtractorPlugin>>());
 
-    RegisterExtractors(kSystemApkPath, *newList);
+    RegisterExtractorsInSystem("/system/lib"
+#ifdef __LP64__
+            "64"
+#endif
+            "/extractors", *newList);
+
+    RegisterExtractorsInSystem("/vendor/lib"
+#ifdef __LP64__
+            "64"
+#endif
+            "/extractors", *newList);
 
     if (newUpdateApkPath != nullptr) {
-        RegisterExtractors(newUpdateApkPath, *newList);
+        RegisterExtractorsInApk(newUpdateApkPath, *newList);
     }
 
     gPlugins = newList;
diff --git a/media/libstagefright/codec2/SimpleC2Component.cpp b/media/libstagefright/codec2/SimpleC2Component.cpp
index 4d75a31..c3ae00e 100644
--- a/media/libstagefright/codec2/SimpleC2Component.cpp
+++ b/media/libstagefright/codec2/SimpleC2Component.cpp
@@ -342,7 +342,7 @@
             return;
         }
     }
-    if (work->worklets_processed != 0u) {
+    if (work->workletsProcessed != 0u) {
         Mutexed<ExecState>::Locked state(mExecState);
         ALOGV("returning this work");
         state->mListener->onWorkDone_nb(shared_from_this(), vec(work));
@@ -351,7 +351,7 @@
         std::unique_ptr<C2Work> unexpected;
         {
             Mutexed<PendingWork>::Locked pending(mPendingWork);
-            uint64_t frameIndex = work->input.ordinal.frame_index;
+            uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
             if (pending->count(frameIndex) != 0) {
                 unexpected = std::move(pending->at(frameIndex));
                 pending->erase(frameIndex);
diff --git a/media/libstagefright/codec2/SimpleC2Interface.cpp b/media/libstagefright/codec2/SimpleC2Interface.cpp
index f9cab26..f082243 100644
--- a/media/libstagefright/codec2/SimpleC2Interface.cpp
+++ b/media/libstagefright/codec2/SimpleC2Interface.cpp
@@ -23,7 +23,7 @@
 namespace android {
 
 c2_status_t SimpleC2Interface::query_vb(
-        const std::vector<C2Param* const> &stackParams,
+        const std::vector<C2Param*> &stackParams,
         const std::vector<C2Param::Index> &heapParamIndices,
         c2_blocking_t mayBlock,
         std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
diff --git a/media/libstagefright/codec2/include/C2.h b/media/libstagefright/codec2/include/C2.h
index fd99cce..442c89e 100644
--- a/media/libstagefright/codec2/include/C2.h
+++ b/media/libstagefright/codec2/include/C2.h
@@ -210,6 +210,219 @@
     } \
     DEFINE_OTHER_COMPARISON_OPERATORS(type)
 
+template<typename T, typename B>
+class C2_HIDE c2_cntr_t;
+
+/// \cond INTERNAL
+
+/// \defgroup utils_internal
+/// @{
+
+template<typename T>
+struct C2_HIDE _c2_cntr_compat_helper {
+    template<typename U, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
+    __attribute__((no_sanitize("integer")))
+    inline static constexpr T get(const U &value) {
+        return T(value);
+    }
+
+    template<typename U, typename E=typename std::enable_if<(sizeof(U) >= sizeof(T))>::type>
+    __attribute__((no_sanitize("integer")))
+    inline static constexpr T get(const c2_cntr_t<U, void> &value) {
+        return T(value.mValue);
+    }
+};
+
+/// @}
+
+/// \endcond
+
+/**
+ * Integral counter type.
+ *
+ * This is basically an unsigned integral type that is NEVER checked for overflow/underflow - and
+ * comparison operators are redefined.
+ *
+ * \note Comparison of counter types is not fully transitive, e.g.
+ * it could be that a > b > c but a !> c.
+ * std::less<>, greater<>, less_equal<> and greater_equal<> specializations yield total ordering,
+ * but may not match semantic ordering of the values.
+ *
+ * Technically: counter types represent integer values: A * 2^N + value, where A can be arbitrary.
+ * This makes addition, subtraction, multiplication (as well as bitwise operations) well defined.
+ * However, division is in general not well defined, as the result may depend on A. This is also
+ * true for logical operators and boolean conversion.
+ *
+ * Even though well defined, bitwise operators are not implemented for counter types as they are not
+ * meaningful.
+ */
+template<typename T, typename B=typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value>::type>
+class C2_HIDE c2_cntr_t {
+    using compat = _c2_cntr_compat_helper<T>;
+
+    T mValue;
+    constexpr static T HALF_RANGE = T(~0) ^ (T(~0) >> 1);
+
+    template<typename U>
+    friend struct _c2_cntr_compat_helper;
+public:
+
+    /**
+     * Default constructor. Initialized counter to 0.
+     */
+    inline constexpr c2_cntr_t() : mValue(T(0)) {}
+
+    /**
+     * Construct from a compatible type.
+     */
+    template<typename U>
+    inline constexpr c2_cntr_t(const U &value) : mValue(compat::get(value)) {}
+
+    /**
+     * Peek as underlying signed type.
+     */
+    __attribute__((no_sanitize("integer")))
+    inline constexpr typename std::make_signed<T>::type peek() const {
+        return static_cast<typename std::make_signed<T>::type>(mValue);
+    }
+
+    /**
+     * Peek as underlying unsigned type.
+     */
+    inline constexpr T peeku() const {
+        return mValue;
+    }
+
+    /**
+     * Peek as long long - e.g. for printing.
+     */
+    __attribute__((no_sanitize("integer")))
+    inline constexpr long long peekll() const {
+        return (long long)mValue;
+    }
+
+    /**
+     * Peek as unsigned long long - e.g. for printing.
+     */
+    __attribute__((no_sanitize("integer")))
+    inline constexpr unsigned long long peekull() const {
+        return (unsigned long long)mValue;
+    }
+
+    /**
+     * Convert to a smaller counter type. This is always safe.
+     */
+    template<typename U, typename E=typename std::enable_if<sizeof(U) < sizeof(T)>::type>
+    inline operator c2_cntr_t<U>() {
+        return c2_cntr_t<U>(mValue);
+    }
+
+    /**
+     * Arithmetic operators
+     */
+
+#define DEFINE_C2_CNTR_BINARY_OP(attrib, op, op_assign) \
+    template<typename U> \
+    attrib inline c2_cntr_t<T>& operator op_assign(const U &value) { \
+        mValue op_assign compat::get(value); \
+        return *this; \
+    } \
+    \
+    template<typename U, typename E=decltype(compat::get(U(0)))> \
+    attrib inline constexpr c2_cntr_t<T> operator op(const U &value) const { \
+        return c2_cntr_t<T>(mValue op compat::get(value)); \
+    } \
+    \
+    template<typename U, typename E=typename std::enable_if<sizeof(U) < sizeof(T)>::type> \
+    attrib inline constexpr c2_cntr_t<U> operator op(const c2_cntr_t<U> &value) const { \
+        return c2_cntr_t<U>(U(mValue) op value.peeku()); \
+    }
+
+#define DEFINE_C2_CNTR_UNARY_OP(attrib, op) \
+    attrib inline constexpr c2_cntr_t<T> operator op() const { \
+        return c2_cntr_t<T>(op mValue); \
+    }
+
+#define DEFINE_C2_CNTR_CREMENT_OP(attrib, op) \
+    attrib inline c2_cntr_t<T> &operator op() { \
+        op mValue; \
+        return *this; \
+    } \
+    attrib inline c2_cntr_t<T> operator op(int) { \
+        return c2_cntr_t<T, void>(mValue op); \
+    }
+
+    DEFINE_C2_CNTR_BINARY_OP(__attribute__((no_sanitize("integer"))), +, +=)
+    DEFINE_C2_CNTR_BINARY_OP(__attribute__((no_sanitize("integer"))), -, -=)
+    DEFINE_C2_CNTR_BINARY_OP(__attribute__((no_sanitize("integer"))), *, *=)
+
+    DEFINE_C2_CNTR_UNARY_OP(__attribute__((no_sanitize("integer"))), -)
+    DEFINE_C2_CNTR_UNARY_OP(__attribute__((no_sanitize("integer"))), +)
+
+    DEFINE_C2_CNTR_CREMENT_OP(__attribute__((no_sanitize("integer"))), ++)
+    DEFINE_C2_CNTR_CREMENT_OP(__attribute__((no_sanitize("integer"))), --)
+
+    template<typename U, typename E=typename std::enable_if<std::is_unsigned<U>::value>::type>
+    __attribute__((no_sanitize("integer")))
+    inline constexpr c2_cntr_t<T> operator<<(const U &value) const {
+        return c2_cntr_t<T>(mValue << value);
+    }
+
+    template<typename U, typename E=typename std::enable_if<std::is_unsigned<U>::value>::type>
+    __attribute__((no_sanitize("integer")))
+    inline c2_cntr_t<T> &operator<<=(const U &value) {
+        mValue <<= value;
+        return *this;
+    }
+
+    /**
+     * Comparison operators
+     */
+    __attribute__((no_sanitize("integer")))
+    inline constexpr bool operator<=(const c2_cntr_t<T> &other) const {
+        return T(other.mValue - mValue) < HALF_RANGE;
+    }
+
+    __attribute__((no_sanitize("integer")))
+    inline constexpr bool operator>=(const c2_cntr_t<T> &other) const {
+        return T(mValue - other.mValue) < HALF_RANGE;
+    }
+
+    inline constexpr bool operator==(const c2_cntr_t<T> &other) const {
+        return mValue == other.mValue;
+    }
+
+    inline constexpr bool operator!=(const c2_cntr_t<T> &other) const {
+        return !(*this == other);
+    }
+
+    inline constexpr bool operator<(const c2_cntr_t<T> &other) const {
+        return *this <= other && *this != other;
+    }
+
+    inline constexpr bool operator>(const c2_cntr_t<T> &other) const {
+        return *this >= other && *this != other;
+    }
+};
+
+template<typename U, typename T, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
+inline constexpr c2_cntr_t<T> operator+(const U &a, const c2_cntr_t<T> &b) {
+    return b + a;
+}
+
+template<typename U, typename T, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
+inline constexpr c2_cntr_t<T> operator-(const U &a, const c2_cntr_t<T> &b) {
+    return c2_cntr_t<T>(a) - b;
+}
+
+template<typename U, typename T, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
+inline constexpr c2_cntr_t<T> operator*(const U &a, const c2_cntr_t<T> &b) {
+    return b * a;
+}
+
+typedef c2_cntr_t<uint32_t> c2_cntr32_t; /** 32-bit counter type */
+typedef c2_cntr_t<uint64_t> c2_cntr64_t; /** 64-bit counter type */
+
 /// \cond INTERNAL
 
 /// \defgroup utils_internal
@@ -326,4 +539,30 @@
 } // namespace android
 #endif
 
+#include <functional>
+template<typename T>
+struct std::less<::android::c2_cntr_t<T>> {
+    constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+        return lh.peeku() < rh.peeku();
+    }
+};
+template<typename T>
+struct std::less_equal<::android::c2_cntr_t<T>> {
+    constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+        return lh.peeku() <= rh.peeku();
+    }
+};
+template<typename T>
+struct std::greater<::android::c2_cntr_t<T>> {
+    constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+        return lh.peeku() > rh.peeku();
+    }
+};
+template<typename T>
+struct std::greater_equal<::android::c2_cntr_t<T>> {
+    constexpr bool operator()(const ::android::c2_cntr_t<T> &lh, const ::android::c2_cntr_t<T> &rh) const {
+        return lh.peeku() >= rh.peeku();
+    }
+};
+
 #endif  // C2_H_
diff --git a/media/libstagefright/codec2/include/C2Buffer.h b/media/libstagefright/codec2/include/C2Buffer.h
index df9362c..2ca8222 100644
--- a/media/libstagefright/codec2/include/C2Buffer.h
+++ b/media/libstagefright/codec2/include/C2Buffer.h
@@ -284,6 +284,7 @@
 /// @{
 public:
     inline uint32_t offset() const { return mOffset; }
+    inline uint32_t endOffset() const { return mOffset + mSize; }
     inline uint32_t size() const { return mSize; }
 
 protected:
@@ -311,6 +312,32 @@
 /// @}
 };
 
+class C2_HIDE _C2LinearCapacityBase : public _C2LinearCapacityAspect {
+public:
+    inline explicit _C2LinearCapacityBase(size_t capacity)
+        : _C2LinearCapacityAspect(c2_min(capacity, std::numeric_limits<uint32_t>::max())) {}
+};
+
+/**
+ * Utility class for safe range calculations.
+ */
+class C2LinearRange : public _C2LinearRangeAspect {
+public:
+    inline C2LinearRange(const _C2LinearCapacityBase &parent, size_t offset, size_t size)
+        : _C2LinearRangeAspect(&parent, offset, size) {}
+};
+
+/**
+ * Utility class for simple capacity and range construction.
+ */
+class C2LinearCapacity : public _C2LinearCapacityBase {
+public:
+    using _C2LinearCapacityBase::_C2LinearCapacityBase;
+    inline C2LinearRange range(size_t offset, size_t size) {
+        return C2LinearRange(*this, offset, size);
+    }
+};
+
 /**
  * Aspect for objects that have an editable linear range.
  *
@@ -652,14 +679,14 @@
      *
      * \param size    number of bytes to share
      * \param fence   fence to be used for the section
-     * \param blocks  list where the blocks of the section are appended to
+     * \param blocks  vector where the blocks of the section are appended to
      *
      * \retval C2_OK            the portion was successfully shared
      * \retval C2_NO_MEMORY     not enough memory to share the portion
      * \retval C2_TIMED_OUT     the operation timed out (unexpected)
      * \retval C2_CORRUPTED     some unknown error prevented sharing the data (unexpected)
      */
-    c2_status_t share(size_t size, C2Fence fence, std::list<C2ConstLinearBlock> &blocks);
+    c2_status_t share(size_t size, C2Fence fence, std::vector<C2ConstLinearBlock> &blocks);
 
     /**
      * Returns the beginning offset of this segment from the start of this circular block.
@@ -1176,14 +1203,14 @@
      * \return a constant list of const linear blocks of this buffer.
      * \retval empty list if this buffer does not contain linear block(s).
      */
-    const std::list<C2ConstLinearBlock> linearBlocks() const;
+    const std::vector<C2ConstLinearBlock> linearBlocks() const;
 
     /**
      * Gets the graphic blocks of this buffer.
      * \return a constant list of const graphic blocks of this buffer.
      * \retval empty list if this buffer does not contain graphic block(s).
      */
-    const std::list<C2ConstGraphicBlock> graphicBlocks() const;
+    const std::vector<C2ConstGraphicBlock> graphicBlocks() const;
 
 private:
     class Impl;
@@ -1191,8 +1218,8 @@
 
 protected:
     // no public constructor
-    explicit C2BufferData(const std::list<C2ConstLinearBlock> &blocks);
-    explicit C2BufferData(const std::list<C2ConstGraphicBlock> &blocks);
+    explicit C2BufferData(const std::vector<C2ConstLinearBlock> &blocks);
+    explicit C2BufferData(const std::vector<C2ConstGraphicBlock> &blocks);
 };
 
 /**
@@ -1274,7 +1301,7 @@
      *
      * \return a constant list of info objects associated with this buffer.
      */
-    const std::list<std::shared_ptr<const C2Info>> infos() const;
+    const std::vector<std::shared_ptr<const C2Info>> info() const;
 
     /**
      * Attaches (or updates) an (existing) metadata for this buffer.
@@ -1301,8 +1328,8 @@
 
 protected:
     // no public constructor
-    explicit C2Buffer(const std::list<C2ConstLinearBlock> &blocks);
-    explicit C2Buffer(const std::list<C2ConstGraphicBlock> &blocks);
+    explicit C2Buffer(const std::vector<C2ConstLinearBlock> &blocks);
+    explicit C2Buffer(const std::vector<C2ConstGraphicBlock> &blocks);
 
 private:
     class Impl;
diff --git a/media/libstagefright/codec2/include/C2Component.h b/media/libstagefright/codec2/include/C2Component.h
index 38d545e..e023db4 100644
--- a/media/libstagefright/codec2/include/C2Component.h
+++ b/media/libstagefright/codec2/include/C2Component.h
@@ -150,7 +150,7 @@
      *                      (this error code is only allowed for interfaces connected to components)
      */
     virtual c2_status_t query_vb(
-        const std::vector<C2Param* const> &stackParams,
+        const std::vector<C2Param*> &stackParams,
         const std::vector<C2Param::Index> &heapParamIndices,
         c2_blocking_t mayBlock,
         std::vector<std::unique_ptr<C2Param>>* const heapParams) const = 0;
@@ -211,7 +211,7 @@
      *                      (this error code is only allowed for interfaces connected to components)
      */
     virtual c2_status_t config_vb(
-            const std::vector<C2Param* const> &params,
+            const std::vector<C2Param*> &params,
             c2_blocking_t mayBlock,
             std::vector<std::unique_ptr<C2SettingResult>>* const failures) = 0;
 
@@ -682,7 +682,7 @@
      */
     virtual c2_status_t reset() { return C2_OK; }
 
-    virtual c2_status_t parseFrame(C2BufferPack &frame);
+    virtual c2_status_t parseFrame(C2FrameData &frame);
 
     virtual ~C2FrameInfoParser() = default;
 };
@@ -850,7 +850,7 @@
      *                      (unexpected)
      */
     virtual c2_status_t query_sm(
-        const std::vector<C2Param* const> &stackParams,
+        const std::vector<C2Param*> &stackParams,
         const std::vector<C2Param::Index> &heapParamIndices,
         std::vector<std::unique_ptr<C2Param>>* const heapParams) const = 0;
 
@@ -889,7 +889,7 @@
      *                      (unexpected)
      */
     virtual c2_status_t config_sm(
-            const std::vector<C2Param* const> &params,
+            const std::vector<C2Param*> &params,
             std::vector<std::unique_ptr<C2SettingResult>>* const failures) = 0;
 
     // REFLECTION MECHANISM (USED FOR EXTENSION)
diff --git a/media/libstagefright/codec2/include/C2Param.h b/media/libstagefright/codec2/include/C2Param.h
index 2a8c1b2..e2df62d 100644
--- a/media/libstagefright/codec2/include/C2Param.h
+++ b/media/libstagefright/codec2/include/C2Param.h
@@ -397,8 +397,8 @@
 
     /// safe(r) type cast from pointer and size
     inline static C2Param* From(void *addr, size_t len) {
-        // _mSize must fit into size
-        if (len < sizeof(_mSize) + offsetof(C2Param, _mSize)) {
+        // _mSize must fit into size, but really C2Param must also to be a valid param
+        if (len < sizeof(C2Param)) {
             return nullptr;
         }
         // _mSize must match length
@@ -446,7 +446,7 @@
     // if other is the same kind of (valid) param as this, copy it into this and return true.
     // otherwise, do not copy anything, and return false.
     inline bool updateFrom(const C2Param &other) {
-        if (other._mSize == _mSize && other._mIndex == _mIndex && _mSize > 0) {
+        if (other._mSize <= _mSize && other._mIndex == _mIndex && _mSize > 0) {
             memcpy(this, &other, _mSize);
             return true;
         }
@@ -620,6 +620,8 @@
 #endif
 
 private:
+    friend struct _C2ParamInspector;
+
     uint32_t _mOffset; // offset of field
     uint32_t _mSize;   // size of field
 };
@@ -719,30 +721,51 @@
 
     DEFINE_OTHER_COMPARISON_OPERATORS(C2ParamField)
 
+protected:
+    inline C2ParamField(C2Param::Index index, uint32_t offset, uint32_t size)
+        : _mIndex(index), _mFieldId(offset, size) {}
+
 private:
+    friend struct _C2ParamInspector;
+
     C2Param::Index _mIndex; ///< parameter index
     _C2FieldId _mFieldId;   ///< field identifier
 };
 
 /**
+ * Structure uniquely specifying a field, an array element of a field, or a
+ * parameter in a configuration
+ */
+struct C2ParamOrField : public C2ParamField {
+//public:
+    template<typename S>
+    inline C2ParamOrField(S* param)
+        : C2ParamField(param->index(), 0u, param->size()) {}
+};
+
+/**
  * A shared (union) representation of numeric values
  */
 class C2Value {
 public:
     /// A union of supported primitive types.
     union Primitive {
-        int32_t  i32;   ///< int32_t value
-        uint32_t u32;   ///< uint32_t value
-        int64_t  i64;   ///< int64_t value
-        uint64_t u64;   ///< uint64_t value
-        float    fp;    ///< float value
+        int32_t     i32;   ///< int32_t value
+        uint32_t    u32;   ///< uint32_t value
+        c2_cntr32_t c32;   ///< c2_cntr32_t value
+        int64_t     i64;   ///< int64_t value
+        uint64_t    u64;   ///< uint64_t value
+        c2_cntr64_t c64;   ///< c2_cntr64_t value
+        float       fp;    ///< float value
 
         // constructors - implicit
-        Primitive(int32_t value)  : i32(value) { }
-        Primitive(uint32_t value) : u32(value) { }
-        Primitive(int64_t value)  : i64(value) { }
-        Primitive(uint64_t value) : u64(value) { }
-        Primitive(float value)    : fp(value)  { }
+        Primitive(int32_t value)     : i32(value) { }
+        Primitive(uint32_t value)    : u32(value) { }
+        Primitive(c2_cntr32_t value) : c32(value) { }
+        Primitive(int64_t value)     : i64(value) { }
+        Primitive(uint64_t value)    : u64(value) { }
+        Primitive(c2_cntr64_t value) : c64(value) { }
+        Primitive(float value)       : fp(value)  { }
 
         Primitive() : u64(0) { }
 
@@ -755,8 +778,10 @@
         NO_INIT,
         INT32,
         UINT32,
+        CNTR32,
         INT64,
         UINT64,
+        CNTR64,
         FLOAT,
     };
 
@@ -788,12 +813,16 @@
 template<> inline const int64_t &C2Value::Primitive::ref<int64_t>() const { return i64; }
 template<> inline const uint32_t &C2Value::Primitive::ref<uint32_t>() const { return u32; }
 template<> inline const uint64_t &C2Value::Primitive::ref<uint64_t>() const { return u64; }
+template<> inline const c2_cntr32_t &C2Value::Primitive::ref<c2_cntr32_t>() const { return c32; }
+template<> inline const c2_cntr64_t &C2Value::Primitive::ref<c2_cntr64_t>() const { return c64; }
 template<> inline const float &C2Value::Primitive::ref<float>() const { return fp; }
 
 template<> constexpr C2Value::type_t C2Value::typeFor<int32_t>() { return INT32; }
 template<> constexpr C2Value::type_t C2Value::typeFor<int64_t>() { return INT64; }
 template<> constexpr C2Value::type_t C2Value::typeFor<uint32_t>() { return UINT32; }
 template<> constexpr C2Value::type_t C2Value::typeFor<uint64_t>() { return UINT64; }
+template<> constexpr C2Value::type_t C2Value::typeFor<c2_cntr32_t>() { return CNTR32; }
+template<> constexpr C2Value::type_t C2Value::typeFor<c2_cntr64_t>() { return CNTR64; }
 template<> constexpr C2Value::type_t C2Value::typeFor<float>() { return FLOAT; }
 
 /**
@@ -813,8 +842,10 @@
         // primitive types
         INT32   = C2Value::INT32,  ///< 32-bit signed integer
         UINT32  = C2Value::UINT32, ///< 32-bit unsigned integer
+        CNTR32  = C2Value::CNTR32, ///< 32-bit counter
         INT64   = C2Value::INT64,  ///< 64-bit signed integer
         UINT64  = C2Value::UINT64, ///< 64-bit signed integer
+        CNTR64  = C2Value::CNTR64, ///< 64-bit counter
         FLOAT   = C2Value::FLOAT,  ///< 32-bit floating point
 
         // array types
@@ -899,13 +930,15 @@
     // 2) this is at parameter granularity.
 
     // type resolution
-    inline static type_t getType(int32_t*)  { return INT32; }
-    inline static type_t getType(uint32_t*) { return UINT32; }
-    inline static type_t getType(int64_t*)  { return INT64; }
-    inline static type_t getType(uint64_t*) { return UINT64; }
-    inline static type_t getType(float*)    { return FLOAT; }
-    inline static type_t getType(char*)     { return STRING; }
-    inline static type_t getType(uint8_t*)  { return BLOB; }
+    inline static type_t getType(int32_t*)     { return INT32; }
+    inline static type_t getType(uint32_t*)    { return UINT32; }
+    inline static type_t getType(c2_cntr32_t*) { return CNTR32; }
+    inline static type_t getType(int64_t*)     { return INT64; }
+    inline static type_t getType(uint64_t*)    { return UINT64; }
+    inline static type_t getType(c2_cntr64_t*) { return CNTR64; }
+    inline static type_t getType(float*)       { return FLOAT; }
+    inline static type_t getType(char*)        { return STRING; }
+    inline static type_t getType(uint8_t*)     { return BLOB; }
 
     template<typename T,
              class=typename std::enable_if<std::is_enum<T>::value>::type>
@@ -932,8 +965,10 @@
 // non-enumerated integral types.
 DEFINE_NO_NAMED_VALUES_FOR(int32_t)
 DEFINE_NO_NAMED_VALUES_FOR(uint32_t)
+DEFINE_NO_NAMED_VALUES_FOR(c2_cntr32_t)
 DEFINE_NO_NAMED_VALUES_FOR(int64_t)
 DEFINE_NO_NAMED_VALUES_FOR(uint64_t)
+DEFINE_NO_NAMED_VALUES_FOR(c2_cntr64_t)
 DEFINE_NO_NAMED_VALUES_FOR(uint8_t)
 DEFINE_NO_NAMED_VALUES_FOR(char)
 DEFINE_NO_NAMED_VALUES_FOR(float)
diff --git a/media/libstagefright/codec2/include/C2Work.h b/media/libstagefright/codec2/include/C2Work.h
index 105cf81..58a9174 100644
--- a/media/libstagefright/codec2/include/C2Work.h
+++ b/media/libstagefright/codec2/include/C2Work.h
@@ -83,32 +83,64 @@
     kParamIndexWorkOrdinal,
 };
 
+/**
+ * Information for ordering work items on a component port.
+ */
 struct C2WorkOrdinalStruct {
-    uint64_t timestamp;
-    uint64_t frame_index;    // submission ordinal on the initial component
-    uint64_t custom_ordinal; // can be given by the component, e.g. decode order
+//public:
+    c2_cntr64_t timestamp;     /** frame timestamp in microseconds */
+    c2_cntr64_t frameIndex;    /** submission ordinal on the initial component */
+    c2_cntr64_t customOrdinal; /** can be given by the component, e.g. decode order */
 
     DEFINE_AND_DESCRIBE_C2STRUCT(WorkOrdinal)
     C2FIELD(timestamp, "timestamp")
-    C2FIELD(frame_index, "frame-index")
-    C2FIELD(custom_ordinal, "custom-ordinal")
+    C2FIELD(frameIndex, "frame-index")
+    C2FIELD(customOrdinal, "custom-ordinal")
 };
 
-struct C2BufferPack {
+/**
+ * This structure represents a Codec 2.0 frame with its metadata.
+ *
+ * A frame basically consists of an ordered sets of buffers, configuration changes and info buffers
+ * along with some non-configuration metadata.
+ */
+struct C2FrameData {
 //public:
     enum flags_t : uint32_t {
-        FLAG_CODEC_CONFIG  = (1 << 0),
-        FLAG_DROP_FRAME    = (1 << 1),
-        FLAG_END_OF_STREAM = (1 << 2),
+        /**
+         * For input frames: no output frame shall be generated when processing this frame, but
+         * metadata shall still be processed.
+         * For output frames: this frame shall be discarded and but metadata is still valid.
+         */
+        FLAG_DROP_FRAME    = (1 << 0),
+        /**
+         * This frame is the last frame of the current stream. Further frames are part of a new
+         * stream.
+         */
+        FLAG_END_OF_STREAM = (1 << 1),
+        /**
+         * This frame shall be discarded with its metadata.
+         * This flag is only set by components - e.g. as a response to the flush command.
+         */
+        FLAG_DISCARD_FRAME = (1 << 2),
+        /**
+         * This frame contains only codec-specific configuration data, and no actual access unit.
+         *
+         * \deprecated pass codec configuration with using the \todo codec-specific configuration
+         * info together with the access unit.
+         */
+        FLAG_CODEC_CONFIG  = (1u << 31),
     };
 
+    /**
+     * Frame flags */
     flags_t  flags;
     C2WorkOrdinalStruct ordinal;
     std::vector<std::shared_ptr<C2Buffer>> buffers;
     //< for initial work item, these may also come from the parser - if provided
     //< for output buffers, these are the responses to requestedInfos
-    std::list<std::unique_ptr<C2Info>>       infos;
-    std::list<std::shared_ptr<C2InfoBuffer>> infoBuffers;
+    std::vector<std::unique_ptr<C2Param>>      configUpdate;
+    std::vector<std::shared_ptr<C2InfoBuffer>> infoBuffers;
 };
 
 struct C2Worklet {
@@ -116,59 +148,61 @@
     // IN
     c2_node_id_t component;
 
-    std::list<std::unique_ptr<C2Param>> tunings; //< tunings to be applied before processing this
-                                                 // worklet
-    std::list<C2Param::Type> requestedInfos;
-    std::vector<std::shared_ptr<C2BlockPool>> allocators; //< This vector shall be the same size as
-                                                          //< output.buffers. \deprecated
+    /** Configuration changes to be applied before processing this worklet. */
+    std::vector<std::unique_ptr<C2Tuning>> tunings;
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
 
     // OUT
-    C2BufferPack output;
-    std::list<std::unique_ptr<C2SettingResult>> failures;
+    C2FrameData output;
 };
 
 /**
+ * Information about partial work-chains not part of the current work items.
+ *
+ * To be defined later.
+ */
+struct C2WorkChainInfo;
+
+/**
  * This structure holds information about all a single work item.
  *
  * This structure shall be passed by the client to the component for the first worklet. As such,
  * worklets must not be empty. The ownership of this object is passed.
- *
- * input:
- *      The input data to be processed. This is provided by the client with ownership. When the work
- *      is returned, the input buffer-pack's buffer vector shall contain nullptrs.
- *
- * worklets:
- *      The chain of components and associated allocators, tunings and info requests that the data
- *      must pass through. If this has more than a single element, the tunnels between successive
- *      components of the worklet chain must have been (successfully) pre-registered at the time
- *      the work is submitted. Allocating the output buffers in the worklets is the responsibility
- *      of each component. Upon work submission, each output buffer-pack shall be an appropriately
- *      sized vector containing nullptrs. When the work is completed/returned to the client,
- *
- * worklets_processed:
- *      It shall be initialized to 0 by the client when the work is submitted.
- *      It shall contain the number of worklets that were successfully processed when the work is
- *      returned. If this is less then the number of worklets, result must not be success.
- *      It must be in the range of [0, worklets.size()].
- *
- * result:
- *      The final outcome of the work. If 0 when work is returned, it is assumed that all worklets
- *      have been processed.
  */
 struct C2Work {
 //public:
-    // pre-chain infos (for portions of a tunneling chain that happend before this work-chain for
-    // this work item - due to framework facilitated (non-tunneled) work-chaining)
-    std::list<std::pair<std::unique_ptr<C2PortMimeConfig>, std::unique_ptr<C2Info>>> preChainInfos;
-    std::list<std::pair<std::unique_ptr<C2PortMimeConfig>, std::unique_ptr<C2Buffer>>> preChainInfoBlobs;
+    /// additional work chain info not part of this work
+    std::shared_ptr<C2WorkChainInfo> chainInfo;
 
-    C2BufferPack input;
+    /// The input data to be processed as part of this work/work-chain. This is provided by the
+    /// client with ownership. When the work is returned (via onWorkDone), the input buffer-pack's
+    /// buffer vector shall contain nullptrs.
+    C2FrameData input;
+
+    /// The chain of components, tunings (including output buffer pool IDs) and info requests that the
+    /// data must pass through. If this has more than a single element, the tunnels between successive
+    /// components of the worklet chain must have been (successfully) pre-registered at the time that
+    /// the work is submitted. Allocating the output buffers in the worklets is the responsibility of
+    /// each component. Upon work submission, each output buffer-pack shall be an appropriately sized
+    /// vector containing nullptrs. When the work is completed/returned to the client, output buffers
+    /// pointers from all but the final worklet shall be nullptrs.
     std::list<std::unique_ptr<C2Worklet>> worklets;
 
-    uint32_t worklets_processed;
+    /// Number of worklets successfully processed in this chain. This shall be initialized to 0 by the
+    /// client when the work is submitted. It shall contain the number of worklets that were
+    /// successfully processed when the work is returned to the client. If this is less then the number
+    /// of worklets, result must not be success. It must be in the range of [0, worklets.size()].
+    uint32_t workletsProcessed;
+
+    /// The final outcome of the work (corresponding to the current workletsProcessed). If 0 when
+    /// work is returned, it is assumed that all worklets have been processed.
     c2_status_t result;
 };
 
+/**
+ * Information about a future work to be submitted to the component. The information is used to
+ * reserve the work for work ordering purposes.
+ */
 struct C2WorkOutline {
 //public:
     C2WorkOrdinalStruct ordinal;
diff --git a/media/libstagefright/codec2/include/SimpleC2Interface.h b/media/libstagefright/codec2/include/SimpleC2Interface.h
index 3796b0b..b934f12 100644
--- a/media/libstagefright/codec2/include/SimpleC2Interface.h
+++ b/media/libstagefright/codec2/include/SimpleC2Interface.h
@@ -59,12 +59,12 @@
     inline C2String getName() const override { return mName; }
     inline c2_node_id_t getId() const override { return mId; }
     c2_status_t query_vb(
-            const std::vector<C2Param* const> &stackParams,
+            const std::vector<C2Param*> &stackParams,
             const std::vector<C2Param::Index> &heapParamIndices,
             c2_blocking_t mayBlock,
             std::vector<std::unique_ptr<C2Param>>* const heapParams) const override;
     inline c2_status_t config_vb(
-            const std::vector<C2Param* const> &,
+            const std::vector<C2Param*> &,
             c2_blocking_t,
             std::vector<std::unique_ptr<C2SettingResult>>* const) override {
         return C2_OMITTED;
diff --git a/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp b/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp
index 339f927..f50af81 100644
--- a/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp
+++ b/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp
@@ -137,7 +137,7 @@
     template <typename T> void queryUnsupportedParam();
 
     // Execute an interface's config_vb(). |T| is a single parameter type, not std::vector.
-    // config() creates std::vector<C2Param *const> {p} and passes it to config_vb().
+    // config() creates std::vector<C2Param *> {p} and passes it to config_vb().
     template <typename T>
     c2_status_t
     config(T *const p,
@@ -195,7 +195,7 @@
     } while (false)
 
 template <typename T> c2_status_t C2CompIntfTest::queryOnStack(T *const p) {
-    std::vector<C2Param *const> stackParams{p};
+    std::vector<C2Param*> stackParams{p};
     return mIntf->query_vb(stackParams, {}, C2_DONT_BLOCK, nullptr);
 }
 
@@ -260,7 +260,7 @@
 template <typename T>
 c2_status_t C2CompIntfTest::config(
         T *const p, std::vector<std::unique_ptr<C2SettingResult>> *const failures) {
-    std::vector<C2Param *const> params{p};
+    std::vector<C2Param*> params{p};
     return mIntf->config_vb(params, C2_DONT_BLOCK, failures);
 }
 
@@ -276,7 +276,7 @@
 void C2CompIntfTest::configReadOnlyParam(const T &newParam) {
     std::unique_ptr<T> p = makeParamFrom(newParam);
 
-    std::vector<C2Param *const> params{p.get()};
+    std::vector<C2Param*> params{p.get()};
     std::vector<std::unique_ptr<C2SettingResult>> failures;
 
     // config_vb should be failed because a parameter is read-only.
@@ -289,7 +289,7 @@
 void C2CompIntfTest::configWritableParamValidValue(const T &newParam, c2_status_t *configResult) {
     std::unique_ptr<T> p = makeParamFrom(newParam);
 
-    std::vector<C2Param *const> params{p.get()};
+    std::vector<C2Param*> params{p.get()};
     std::vector<std::unique_ptr<C2SettingResult>> failures;
     // In most cases, config_vb return C2_OK and the parameter's value should be changed
     // to |newParam|, which is confirmed in a caller of configWritableParamValueValue().
@@ -312,7 +312,7 @@
 void C2CompIntfTest::configWritableParamInvalidValue(const T &newParam) {
     std::unique_ptr<T> p = makeParamFrom(newParam);
 
-    std::vector<C2Param *const> params{p.get()};
+    std::vector<C2Param*> params{p.get()};
     std::vector<std::unique_ptr<C2SettingResult>> failures;
     // Although a parameter is writable, config_vb should be failed,
     // because a new value is invalid.
diff --git a/media/libstagefright/codec2/tests/C2Param_test.cpp b/media/libstagefright/codec2/tests/C2Param_test.cpp
index 8ebc584..d186292 100644
--- a/media/libstagefright/codec2/tests/C2Param_test.cpp
+++ b/media/libstagefright/codec2/tests/C2Param_test.cpp
@@ -2450,7 +2450,7 @@
     }
 
     virtual c2_status_t config_vb(
-            const std::vector<C2Param* const> &params,
+            const std::vector<C2Param*> &params,
             c2_blocking_t mayBlock,
             std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
         (void)params;
@@ -2465,7 +2465,7 @@
     }
 
     virtual c2_status_t query_vb(
-            const std::vector<C2Param* const> &stackParams,
+            const std::vector<C2Param*> &stackParams,
             const std::vector<C2Param::Index> &heapParamIndices,
             c2_blocking_t mayBlock,
             std::vector<std::unique_ptr<C2Param>>* const heapParams) const override {
diff --git a/media/libstagefright/codec2/tests/C2_test.cpp b/media/libstagefright/codec2/tests/C2_test.cpp
index 92a3d91..46f545f 100644
--- a/media/libstagefright/codec2/tests/C2_test.cpp
+++ b/media/libstagefright/codec2/tests/C2_test.cpp
@@ -75,4 +75,88 @@
 static_assert(c2_const_checker<max_u32_u64>::num() == 3, "should be 3");
 static_assert(c2_const_checker<max_u32_u8>::num() == 0x7fffffff, "should be 0x7fffffff");
 
+/* ======================================= COUNTER TESTS ======================================= */
+
+void c2_cntr_static_test() {
+    // sanity checks for construction/assignment
+    constexpr c2_cntr32_t c32_a(123);
+    constexpr c2_cntr64_t c64_a(-456);
+    c2_cntr32_t c32_b __unused = c64_a;
+    // c32_b = 64.; // DISALLOWED
+    // c2_cntr64_t c64_b = c32_a; // DISALLOWED
+
+    // sanity checks for some constexpr operators
+    static_assert(std::is_same<decltype(c32_a + c64_a), decltype(c64_a + c32_a)>::value, "+ should result same type");
+    static_assert(c32_a + c64_a == c2_cntr32_t(-333), "123 + -456 = -333");
+    static_assert(c32_a + c32_a == c2_cntr32_t(246), "123 + 123 = 246");
+    static_assert(c64_a + c32_a == c2_cntr32_t(-333), "-456 + 123 = 579");
+    static_assert(std::is_same<decltype(c32_a + 1), decltype(1 + c32_a)>::value, "+ should result same type");
+    static_assert(c32_a + 456 == c2_cntr32_t(579), "123 + 456 = 579");
+    static_assert(456 + c64_a == c2_cntr64_t(0), "456 + -456 = 0");
+    static_assert(std::is_same<decltype(c32_a - c64_a), decltype(c64_a - c32_a)>::value, "- should result same type");
+    static_assert(c32_a - c64_a == c2_cntr32_t(579), "123 - -456 = 579");
+    static_assert(c32_a - c32_a == c2_cntr32_t(0), "123 - 123 = 0");
+    static_assert(c64_a - c32_a == c2_cntr32_t(-579), "-456 - 123 = -579");
+    static_assert(std::is_same<decltype(c32_a - 1), decltype(1 - c32_a)>::value, "- should result same type");
+    static_assert(c32_a - 456 == c2_cntr32_t(-333), "123 - 456 = -333");
+    static_assert(456 - c64_a == c2_cntr64_t(912), "456 - -456 = 912");
+    static_assert(std::is_same<decltype(c32_a * c64_a), decltype(c64_a * c32_a)>::value, "* should result same type");
+    static_assert(c32_a * c64_a == c2_cntr32_t(-56088), "123 * -456 = -56088");
+    static_assert(c32_a * c32_a == c2_cntr32_t(15129), "123 * 123 = 15129");
+    static_assert(c64_a * c32_a == c2_cntr32_t(-56088), "-456 * 123 = -56088");
+    static_assert(std::is_same<decltype(c32_a * 1), decltype(1 * c32_a)>::value, "* should result same type");
+    static_assert(c32_a * 456 == c2_cntr32_t(56088), "123 * 456 = 56088");
+    static_assert(456 * c64_a == c2_cntr64_t(-207936), "456 * -456 = -207936");
+
+    static_assert((c32_a << 26u) == c2_cntr32_t(0xEC000000), "123 << 26 = 0xEC000000");
+
+    // sanity checks for unary operators
+    static_assert(c2_cntr32_t(1) == +c2_cntr32_t(1), "1 == +1");
+    static_assert(c2_cntr32_t(1) == -c2_cntr32_t(-1), "1 == --1");
+
+    // sanity checks for comparison
+    using c8_t = c2_cntr_t<uint8_t>;
+    static_assert(c8_t(-0x80) > c8_t(0x7f), "80 > 7F");
+    static_assert(c8_t(-0x80) >= c8_t(0x7f), "80 >= 7F");
+    static_assert(c8_t(0x7f) > c8_t(0x7e), "7F > 7E");
+    static_assert(c8_t(0x7f) >= c8_t(0x7e), "7F >= 7E");
+    static_assert(!(c8_t(-0x80) > c8_t(0)), "80 !> 00");
+    static_assert(!(c8_t(-0x80) >= c8_t(0)), "80 !>= 00");
+    static_assert(!(c8_t(-0x80) > c8_t(-0x80)), "80 !> 80");
+    static_assert(c8_t(-0x80) >= c8_t(-0x80), "80 >= 80");
+
+    static_assert(c8_t(-0x80) == c8_t(0x80), "80 == 80");
+    static_assert(!(c8_t(-0x80) == c8_t(0)), "80 != 0");
+    static_assert(c8_t(-0x80) != c8_t(0x7f), "80 != 7F");
+    static_assert(!(c8_t(0x7f) != c8_t(0x7f)), "80 != 7F");
+
+    static_assert(c8_t(0x7f) < c8_t(-0x80), "7F < 80");
+    static_assert(c8_t(0x7f) <= c8_t(-0x80), "7F < 80");
+    static_assert(c8_t(0x7e) < c8_t(0x7f), "7E < 7F");
+    static_assert(c8_t(0x7e) <= c8_t(0x7f), "7E < 7F");
+    static_assert(!(c8_t(-0x40) < c8_t(0x40)), "-40 !< 40");
+    static_assert(!(c8_t(-0x40) <= c8_t(0x40)), "-40 !<= 40");
+    static_assert(!(c8_t(-0x40) < c8_t(-0x40)), "-40 !< -40");
+    static_assert(c8_t(-0x40) <= c8_t(-0x40), "-40 <= -40");
+
+    static_assert(c2_cntr32_t(-0x7fffffff - 1) > c2_cntr32_t(0x7fffffff), "80 > 7F");
+    static_assert(!(c2_cntr32_t(-0x7fffffff - 1) > c2_cntr32_t(0)), "80 !> 00");
+    static_assert(c2_cntr32_t(1) == c2_cntr32_t(c2_cntr64_t(0x100000001ul)), "1 == 1");
+}
+
+class C2Test : public ::testing::Test {
+};
+
+TEST_F(C2Test, CounterTest) {
+    c2_cntr32_t c32_a(123);
+    c2_cntr64_t c64_a(-456);
+    EXPECT_EQ(c32_a += 3, c2_cntr32_t(126));
+    EXPECT_EQ(c32_a += c64_a, c2_cntr32_t(-330));
+    EXPECT_EQ(c32_a <<= 2u, c2_cntr32_t(-1320));
+    EXPECT_EQ(c64_a *= 3, c2_cntr64_t(-1368));
+    EXPECT_EQ(c32_a -= c64_a, c2_cntr32_t(48));
+    EXPECT_EQ(c32_a -= 40, c2_cntr32_t(8));
+    EXPECT_EQ(c32_a *= c32_a, c2_cntr32_t(64));
+}
+
 } // namespace android
diff --git a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
index f6e6478..a310717 100644
--- a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
+++ b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
@@ -359,14 +359,14 @@
 
 class BufferData : public C2BufferData {
 public:
-    explicit BufferData(const std::list<C2ConstLinearBlock> &blocks) : C2BufferData(blocks) {}
-    explicit BufferData(const std::list<C2ConstGraphicBlock> &blocks) : C2BufferData(blocks) {}
+    explicit BufferData(const std::vector<C2ConstLinearBlock> &blocks) : C2BufferData(blocks) {}
+    explicit BufferData(const std::vector<C2ConstGraphicBlock> &blocks) : C2BufferData(blocks) {}
 };
 
 class Buffer : public C2Buffer {
 public:
-    explicit Buffer(const std::list<C2ConstLinearBlock> &blocks) : C2Buffer(blocks) {}
-    explicit Buffer(const std::list<C2ConstGraphicBlock> &blocks) : C2Buffer(blocks) {}
+    explicit Buffer(const std::vector<C2ConstLinearBlock> &blocks) : C2Buffer(blocks) {}
+    explicit Buffer(const std::vector<C2ConstGraphicBlock> &blocks) : C2Buffer(blocks) {}
 };
 
 TEST_F(C2BufferTest, BufferDataTest) {
@@ -487,45 +487,45 @@
     std::shared_ptr<C2Info> info1(new C2Number1Info(1));
     std::shared_ptr<C2Info> info2(new C2Number2Info(2));
     buffer.reset(new Buffer( { block->share(0, kCapacity, C2Fence()) }));
-    EXPECT_TRUE(buffer->infos().empty());
+    EXPECT_TRUE(buffer->info().empty());
     EXPECT_FALSE(buffer->hasInfo(info1->type()));
     EXPECT_FALSE(buffer->hasInfo(info2->type()));
 
     ASSERT_EQ(C2_OK, buffer->setInfo(info1));
-    EXPECT_EQ(1u, buffer->infos().size());
-    EXPECT_EQ(*info1, *buffer->infos().front());
+    EXPECT_EQ(1u, buffer->info().size());
+    EXPECT_EQ(*info1, *buffer->info().front());
     EXPECT_TRUE(buffer->hasInfo(info1->type()));
     EXPECT_FALSE(buffer->hasInfo(info2->type()));
 
     ASSERT_EQ(C2_OK, buffer->setInfo(info2));
-    EXPECT_EQ(2u, buffer->infos().size());
+    EXPECT_EQ(2u, buffer->info().size());
     EXPECT_TRUE(buffer->hasInfo(info1->type()));
     EXPECT_TRUE(buffer->hasInfo(info2->type()));
 
     std::shared_ptr<C2Info> removed = buffer->removeInfo(info1->type());
     ASSERT_TRUE(removed);
     EXPECT_EQ(*removed, *info1);
-    EXPECT_EQ(1u, buffer->infos().size());
-    EXPECT_EQ(*info2, *buffer->infos().front());
+    EXPECT_EQ(1u, buffer->info().size());
+    EXPECT_EQ(*info2, *buffer->info().front());
     EXPECT_FALSE(buffer->hasInfo(info1->type()));
     EXPECT_TRUE(buffer->hasInfo(info2->type()));
 
     removed = buffer->removeInfo(info1->type());
     ASSERT_FALSE(removed);
-    EXPECT_EQ(1u, buffer->infos().size());
+    EXPECT_EQ(1u, buffer->info().size());
     EXPECT_FALSE(buffer->hasInfo(info1->type()));
     EXPECT_TRUE(buffer->hasInfo(info2->type()));
 
     std::shared_ptr<C2Info> info3(new C2Number2Info(3));
     ASSERT_EQ(C2_OK, buffer->setInfo(info3));
-    EXPECT_EQ(1u, buffer->infos().size());
+    EXPECT_EQ(1u, buffer->info().size());
     EXPECT_FALSE(buffer->hasInfo(info1->type()));
     EXPECT_TRUE(buffer->hasInfo(info2->type()));
 
     removed = buffer->removeInfo(info2->type());
     ASSERT_TRUE(removed);
     EXPECT_EQ(*info3, *removed);
-    EXPECT_TRUE(buffer->infos().empty());
+    EXPECT_TRUE(buffer->info().empty());
     EXPECT_FALSE(buffer->hasInfo(info1->type()));
     EXPECT_FALSE(buffer->hasInfo(info2->type()));
 }
diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp
index 65a271e..4ab3e05 100644
--- a/media/libstagefright/codec2/vndk/C2Buffer.cpp
+++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp
@@ -602,44 +602,44 @@
 
 class C2BufferData::Impl {
 public:
-    explicit Impl(const std::list<C2ConstLinearBlock> &blocks)
+    explicit Impl(const std::vector<C2ConstLinearBlock> &blocks)
         : mType(blocks.size() == 1 ? LINEAR : LINEAR_CHUNKS),
           mLinearBlocks(blocks) {
     }
 
-    explicit Impl(const std::list<C2ConstGraphicBlock> &blocks)
+    explicit Impl(const std::vector<C2ConstGraphicBlock> &blocks)
         : mType(blocks.size() == 1 ? GRAPHIC : GRAPHIC_CHUNKS),
           mGraphicBlocks(blocks) {
     }
 
     Type type() const { return mType; }
-    const std::list<C2ConstLinearBlock> &linearBlocks() const { return mLinearBlocks; }
-    const std::list<C2ConstGraphicBlock> &graphicBlocks() const { return mGraphicBlocks; }
+    const std::vector<C2ConstLinearBlock> &linearBlocks() const { return mLinearBlocks; }
+    const std::vector<C2ConstGraphicBlock> &graphicBlocks() const { return mGraphicBlocks; }
 
 private:
     Type mType;
-    std::list<C2ConstLinearBlock> mLinearBlocks;
-    std::list<C2ConstGraphicBlock> mGraphicBlocks;
+    std::vector<C2ConstLinearBlock> mLinearBlocks;
+    std::vector<C2ConstGraphicBlock> mGraphicBlocks;
 };
 
-C2BufferData::C2BufferData(const std::list<C2ConstLinearBlock> &blocks) : mImpl(new Impl(blocks)) {}
-C2BufferData::C2BufferData(const std::list<C2ConstGraphicBlock> &blocks) : mImpl(new Impl(blocks)) {}
+C2BufferData::C2BufferData(const std::vector<C2ConstLinearBlock> &blocks) : mImpl(new Impl(blocks)) {}
+C2BufferData::C2BufferData(const std::vector<C2ConstGraphicBlock> &blocks) : mImpl(new Impl(blocks)) {}
 
 C2BufferData::Type C2BufferData::type() const { return mImpl->type(); }
 
-const std::list<C2ConstLinearBlock> C2BufferData::linearBlocks() const {
+const std::vector<C2ConstLinearBlock> C2BufferData::linearBlocks() const {
     return mImpl->linearBlocks();
 }
 
-const std::list<C2ConstGraphicBlock> C2BufferData::graphicBlocks() const {
+const std::vector<C2ConstGraphicBlock> C2BufferData::graphicBlocks() const {
     return mImpl->graphicBlocks();
 }
 
 class C2Buffer::Impl {
 public:
-    Impl(C2Buffer *thiz, const std::list<C2ConstLinearBlock> &blocks)
+    Impl(C2Buffer *thiz, const std::vector<C2ConstLinearBlock> &blocks)
         : mThis(thiz), mData(blocks) {}
-    Impl(C2Buffer *thiz, const std::list<C2ConstGraphicBlock> &blocks)
+    Impl(C2Buffer *thiz, const std::vector<C2ConstGraphicBlock> &blocks)
         : mThis(thiz), mData(blocks) {}
 
     ~Impl() {
@@ -676,8 +676,8 @@
         return C2_OK;
     }
 
-    std::list<std::shared_ptr<const C2Info>> infos() const {
-        std::list<std::shared_ptr<const C2Info>> result(mInfos.size());
+    std::vector<std::shared_ptr<const C2Info>> info() const {
+        std::vector<std::shared_ptr<const C2Info>> result(mInfos.size());
         std::transform(
                 mInfos.begin(), mInfos.end(), result.begin(),
                 [] (const auto &elem) { return elem.second; });
@@ -712,10 +712,10 @@
     std::list<std::pair<OnDestroyNotify, void *>> mNotify;
 };
 
-C2Buffer::C2Buffer(const std::list<C2ConstLinearBlock> &blocks)
+C2Buffer::C2Buffer(const std::vector<C2ConstLinearBlock> &blocks)
     : mImpl(new Impl(this, blocks)) {}
 
-C2Buffer::C2Buffer(const std::list<C2ConstGraphicBlock> &blocks)
+C2Buffer::C2Buffer(const std::vector<C2ConstGraphicBlock> &blocks)
     : mImpl(new Impl(this, blocks)) {}
 
 const C2BufferData C2Buffer::data() const { return mImpl->data(); }
@@ -728,8 +728,8 @@
     return mImpl->unregisterOnDestroyNotify(onDestroyNotify, arg);
 }
 
-const std::list<std::shared_ptr<const C2Info>> C2Buffer::infos() const {
-    return mImpl->infos();
+const std::vector<std::shared_ptr<const C2Info>> C2Buffer::info() const {
+    return mImpl->info();
 }
 
 c2_status_t C2Buffer::setInfo(const std::shared_ptr<C2Info> &info) {
diff --git a/media/libstagefright/codec2/vndk/C2ParamInternal.h b/media/libstagefright/codec2/vndk/C2ParamInternal.h
new file mode 100644
index 0000000..0f3812a
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/C2ParamInternal.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ANDROID_STAGEFRIGHT_C2PARAM_INTERNAL_H_
+#define ANDROID_STAGEFRIGHT_C2PARAM_INTERNAL_H_
+
+#include <C2Param.h>
+
+namespace android {
+
+struct C2_HIDE _C2ParamInspector {
+   inline static uint32_t getIndex(const C2ParamField &pf) {
+        return pf._mIndex;
+    }
+
+    inline static uint32_t getOffset(const C2ParamField &pf) {
+        return pf._mFieldId._mOffset;
+    }
+
+    inline static uint32_t getSize(const C2ParamField &pf) {
+        return pf._mFieldId._mSize;
+    }
+
+    inline static
+    C2ParamField CreateParamField(C2Param::Index index, uint32_t offset, uint32_t size) {
+        return C2ParamField(index, offset, size);
+    }
+};
+
+}
+
+#endif // ANDROID_STAGEFRIGHT_C2PARAM_INTERNAL_H_
+
diff --git a/media/libstagefright/codec2/vndk/C2Store.cpp b/media/libstagefright/codec2/vndk/C2Store.cpp
index eb72d17..555e77b 100644
--- a/media/libstagefright/codec2/vndk/C2Store.cpp
+++ b/media/libstagefright/codec2/vndk/C2Store.cpp
@@ -167,7 +167,7 @@
     virtual c2_status_t querySupportedParams_nb(
             std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const override;
     virtual c2_status_t query_sm(
-            const std::vector<C2Param *const> &stackParams,
+            const std::vector<C2Param*> &stackParams,
             const std::vector<C2Param::Index> &heapParamIndices,
             std::vector<std::unique_ptr<C2Param>> *const heapParams) const override;
     virtual c2_status_t createInterface(
@@ -177,7 +177,7 @@
     virtual c2_status_t copyBuffer(
             std::shared_ptr<C2GraphicBuffer> src, std::shared_ptr<C2GraphicBuffer> dst) override;
     virtual c2_status_t config_sm(
-            const std::vector<C2Param *const> &params,
+            const std::vector<C2Param*> &params,
             std::vector<std::unique_ptr<C2SettingResult>> *const failures) override;
     C2PlatformComponentStore();
 
@@ -405,6 +405,7 @@
     // TODO: move this also into a .so so it can be updated
     mComponents.emplace("c2.google.avc.decoder", "libstagefright_soft_c2avcdec.so");
     mComponents.emplace("c2.google.aac.decoder", "libstagefright_soft_c2aacdec.so");
+    mComponents.emplace("c2.google.aac.encoder", "libstagefright_soft_c2aacenc.so");
 }
 
 c2_status_t C2PlatformComponentStore::copyBuffer(
@@ -415,7 +416,7 @@
 }
 
 c2_status_t C2PlatformComponentStore::query_sm(
-        const std::vector<C2Param *const> &stackParams,
+        const std::vector<C2Param*> &stackParams,
         const std::vector<C2Param::Index> &heapParamIndices,
         std::vector<std::unique_ptr<C2Param>> *const heapParams) const {
     // there are no supported configs
@@ -424,7 +425,7 @@
 }
 
 c2_status_t C2PlatformComponentStore::config_sm(
-        const std::vector<C2Param *const> &params,
+        const std::vector<C2Param*> &params,
         std::vector<std::unique_ptr<C2SettingResult>> *const failures) {
     // there are no supported configs
     (void)failures;
diff --git a/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
index 81c5495..3168248 100644
--- a/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
+++ b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
@@ -59,6 +59,7 @@
 
 /// \endcond
 
+#undef DEFINE_C2_ENUM_VALUE_AUTO_HELPER
 #define DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, ...) \
 template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
     return C2ParamUtils::sanitizeEnumValues( \
@@ -67,6 +68,7 @@
             prefix); \
 }
 
+#undef DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER
 #define DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, type, names, ...) \
 template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
     return C2ParamUtils::customEnumValues( \
@@ -260,6 +262,21 @@
         }
         return namedValues;
     }
+
+    /// safe(r) parsing from parameter blob
+    static
+    C2Param *ParseFirst(const uint8_t *blob, size_t size) {
+        // _mSize must fit into size, but really C2Param must also to be a valid param
+        if (size < sizeof(C2Param)) {
+            return nullptr;
+        }
+        // _mSize must match length
+        C2Param *param = (C2Param*)blob;
+        if (param->size() > size) {
+            return nullptr;
+        }
+        return param;
+    }
 };
 
 /* ---------------------------- UTILITIES FOR PARAMETER REFLECTION ---------------------------- */
diff --git a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
index 390f36c..5ddfc14 100644
--- a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
+++ b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
@@ -316,9 +316,9 @@
             work->worklets.front()->output.buffers.clear();
             work->worklets.front()->output.buffers.push_back(buffer);
             work->worklets.front()->output.ordinal = work->input.ordinal;
-            work->worklets_processed = 1u;
+            work->workletsProcessed = 1u;
         };
-        if (work && work->input.ordinal.frame_index == outInfo.frameIndex) {
+        if (work && work->input.ordinal.frameIndex == c2_cntr64_t(outInfo.frameIndex)) {
             fillWork(work);
         } else {
             finish(outInfo.frameIndex, fillWork);
@@ -332,7 +332,7 @@
 void C2SoftAac::process(
         const std::unique_ptr<C2Work> &work,
         const std::shared_ptr<C2BlockPool> &pool) {
-    work->worklets_processed = 0u;
+    work->workletsProcessed = 0u;
     if (mSignalledError) {
         return;
     }
@@ -346,8 +346,8 @@
     size_t offset = 0u;
     size_t size = view.capacity();
 
-    bool eos = (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) != 0;
-    bool codecConfig = (work->input.flags & C2BufferPack::FLAG_CODEC_CONFIG) != 0;
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+    bool codecConfig = (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0;
 
     //TODO
 #if 0
@@ -381,8 +381,8 @@
     }
 
     Info inInfo;
-    inInfo.frameIndex = work->input.ordinal.frame_index;
-    inInfo.timestamp = work->input.ordinal.timestamp;
+    inInfo.frameIndex = work->input.ordinal.frameIndex.peeku();
+    inInfo.timestamp = work->input.ordinal.timestamp.peeku();
     inInfo.bufferSize = size;
     inInfo.decodedSizes.clear();
     while (size > 0u) {
@@ -604,13 +604,13 @@
             work->worklets.front()->output.buffers.clear();
             work->worklets.front()->output.buffers.emplace_back(nullptr);
             work->worklets.front()->output.ordinal = work->input.ordinal;
-            work->worklets_processed = 1u;
+            work->workletsProcessed = 1u;
         };
         while (mBuffersInfo.size() > 1u) {
             finish(mBuffersInfo.front().frameIndex, fillEmptyWork);
             mBuffersInfo.pop_front();
         }
-        if (work->worklets_processed == 0u) {
+        if (work->workletsProcessed == 0u) {
             fillEmptyWork(work);
         }
         mBuffersInfo.clear();
diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp
index 9342351..4caef92 100644
--- a/media/libstagefright/codecs/aacenc/Android.bp
+++ b/media/libstagefright/codecs/aacenc/Android.bp
@@ -1,4 +1,42 @@
 cc_library_shared {
+    name: "libstagefright_soft_c2aacenc",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    srcs: ["C2SoftAacEnc.cpp"],
+
+    cflags: ["-Werror"],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    static_libs: [
+        "libFraunhoferAAC",
+        "libstagefright_codec2_vndk"
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "libion",
+        "liblog",
+        "libstagefright_codec2",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+        "libutils",
+    ],
+}
+
+cc_library_shared {
     name: "libstagefright_soft_aacenc",
     vendor_available: true,
     vndk: {
diff --git a/media/libstagefright/codecs/aacenc/C2SoftAacEnc.cpp b/media/libstagefright/codecs/aacenc/C2SoftAacEnc.cpp
new file mode 100644
index 0000000..7bce21d
--- /dev/null
+++ b/media/libstagefright/codecs/aacenc/C2SoftAacEnc.cpp
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftAacEnc"
+#include <utils/Log.h>
+
+#include <inttypes.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+#include "C2SoftAacEnc.h"
+
+namespace android {
+
+C2SoftAacEnc::C2SoftAacEnc(
+        const char *name,
+        c2_node_id_t id)
+    : SimpleC2Component(
+            SimpleC2Interface::Builder(name, id)
+            .inputFormat(C2FormatAudio)
+            .outputFormat(C2FormatCompressed)
+            .build()),
+      mAACEncoder(NULL),
+      mNumChannels(1),
+      mSampleRate(44100),
+      mBitRate(64000),
+      mSBRMode(-1),
+      mSBRRatio(0),
+      mAACProfile(AOT_AAC_LC),
+      mNumBytesPerInputFrame(0u),
+      mOutBufferSize(0u),
+      mSentCodecSpecificData(false),
+      mInputSize(0),
+      mInputTimeUs(-1ll),
+      mSignalledError(false) {
+}
+
+C2SoftAacEnc::~C2SoftAacEnc() {
+    onReset();
+}
+
+c2_status_t C2SoftAacEnc::onInit() {
+    status_t err = initEncoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+status_t C2SoftAacEnc::initEncoder() {
+    if (AACENC_OK != aacEncOpen(&mAACEncoder, 0, 0)) {
+        ALOGE("Failed to init AAC encoder");
+        return UNKNOWN_ERROR;
+    }
+    return setAudioParams();
+}
+
+c2_status_t C2SoftAacEnc::onStop() {
+    mSentCodecSpecificData = false;
+    mInputSize = 0u;
+    mInputTimeUs = -1ll;
+    mSignalledError = false;
+    return C2_OK;
+}
+
+void C2SoftAacEnc::onReset() {
+    (void)onStop();
+    aacEncClose(&mAACEncoder);
+}
+
+void C2SoftAacEnc::onRelease() {
+    // no-op
+}
+
+c2_status_t C2SoftAacEnc::onFlush_sm() {
+    mSentCodecSpecificData = false;
+    mInputSize = 0u;
+    return C2_OK;
+}
+
+static CHANNEL_MODE getChannelMode(uint32_t nChannels) {
+    CHANNEL_MODE chMode = MODE_INVALID;
+    switch (nChannels) {
+        case 1: chMode = MODE_1; break;
+        case 2: chMode = MODE_2; break;
+        case 3: chMode = MODE_1_2; break;
+        case 4: chMode = MODE_1_2_1; break;
+        case 5: chMode = MODE_1_2_2; break;
+        case 6: chMode = MODE_1_2_2_1; break;
+        default: chMode = MODE_INVALID;
+    }
+    return chMode;
+}
+
+//static AUDIO_OBJECT_TYPE getAOTFromProfile(OMX_U32 profile) {
+//    if (profile == OMX_AUDIO_AACObjectLC) {
+//        return AOT_AAC_LC;
+//    } else if (profile == OMX_AUDIO_AACObjectHE) {
+//        return AOT_SBR;
+//    } else if (profile == OMX_AUDIO_AACObjectHE_PS) {
+//        return AOT_PS;
+//    } else if (profile == OMX_AUDIO_AACObjectLD) {
+//        return AOT_ER_AAC_LD;
+//    } else if (profile == OMX_AUDIO_AACObjectELD) {
+//        return AOT_ER_AAC_ELD;
+//    } else {
+//        ALOGW("Unsupported AAC profile - defaulting to AAC-LC");
+//        return AOT_AAC_LC;
+//    }
+//}
+
+status_t C2SoftAacEnc::setAudioParams() {
+    // We call this whenever sample rate, number of channels, bitrate or SBR mode change
+    // in reponse to setParameter calls.
+
+    ALOGV("setAudioParams: %u Hz, %u channels, %u bps, %i sbr mode, %i sbr ratio",
+         mSampleRate, mNumChannels, mBitRate, mSBRMode, mSBRRatio);
+
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_AOT, mAACProfile)) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SAMPLERATE, mSampleRate)) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_BITRATE, mBitRate)) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_CHANNELMODE,
+            getChannelMode(mNumChannels))) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_TRANSMUX, TT_MP4_RAW)) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+
+    if (mSBRMode != -1 && mAACProfile == AOT_ER_AAC_ELD) {
+        if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_MODE, mSBRMode)) {
+            ALOGE("Failed to set AAC encoder parameters");
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    /* SBR ratio parameter configurations:
+       0: Default configuration wherein SBR ratio is configured depending on audio object type by
+          the FDK.
+       1: Downsampled SBR (default for ELD)
+       2: Dualrate SBR (default for HE-AAC)
+     */
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_RATIO, mSBRRatio)) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+void C2SoftAacEnc::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    work->workletsProcessed = 0u;
+
+    if (mSignalledError) {
+        return;
+    }
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+
+    if (!mSentCodecSpecificData) {
+        // The very first thing we want to output is the codec specific
+        // data.
+
+        if (AACENC_OK != aacEncEncode(mAACEncoder, NULL, NULL, NULL, NULL)) {
+            ALOGE("Unable to initialize encoder for profile / sample-rate / bit-rate / channels");
+            // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+            mSignalledError = true;
+            return;
+        }
+
+        uint32_t actualBitRate = aacEncoder_GetParam(mAACEncoder, AACENC_BITRATE);
+        if (mBitRate != actualBitRate) {
+            ALOGW("Requested bitrate %u unsupported, using %u", mBitRate, actualBitRate);
+        }
+
+        AACENC_InfoStruct encInfo;
+        if (AACENC_OK != aacEncInfo(mAACEncoder, &encInfo)) {
+            ALOGE("Failed to get AAC encoder info");
+            // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+            mSignalledError = true;
+            return;
+        }
+
+        std::unique_ptr<C2StreamCsdInfo::output> csd =
+            C2StreamCsdInfo::output::alloc_unique(encInfo.confSize, 0u);
+        // TODO: check NO_MEMORY
+        memcpy(csd->m.value, encInfo.confBuf, encInfo.confSize);
+        ALOGV("put csd");
+#if defined(LOG_NDEBUG) && !LOG_NDEBUG
+        hexdump(csd->m.value, csd->flexCount());
+#endif
+        work->worklets.front()->output.configUpdate.push_back(std::move(csd));
+
+        mOutBufferSize = encInfo.maxOutBufBytes;
+        mNumBytesPerInputFrame = encInfo.frameLength * mNumChannels * sizeof(int16_t);
+        mInputTimeUs = work->input.ordinal.timestamp;
+
+        mSentCodecSpecificData = true;
+    }
+
+    C2ReadView view = work->input.buffers[0]->data().linearBlocks().front().map().get();
+    uint64_t timestamp = mInputTimeUs.peeku();
+
+    size_t numFrames = (view.capacity() + mInputSize + (eos ? mNumBytesPerInputFrame - 1 : 0))
+            / mNumBytesPerInputFrame;
+    ALOGV("capacity = %u; mInputSize = %zu; numFrames = %zu", view.capacity(), mInputSize, numFrames);
+
+    std::shared_ptr<C2LinearBlock> block;
+    std::unique_ptr<C2WriteView> wView;
+    uint8_t *outPtr = nullptr;
+    size_t outAvailable = 0u;
+
+    if (numFrames) {
+        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        // TODO: error handling, proper usage, etc.
+        c2_status_t err = pool->fetchLinearBlock(mOutBufferSize * numFrames, usage, &block);
+        if (err != C2_OK) {
+            ALOGE("err = %d", err);
+        }
+
+        wView.reset(new C2WriteView(block->map().get()));
+        outPtr = wView->data();
+        outAvailable = wView->size();
+    }
+
+    AACENC_InArgs inargs;
+    AACENC_OutArgs outargs;
+    memset(&inargs, 0, sizeof(inargs));
+    memset(&outargs, 0, sizeof(outargs));
+    inargs.numInSamples = view.capacity() / sizeof(int16_t);
+
+    void* inBuffer[]        = { (unsigned char *)view.data() };
+    INT   inBufferIds[]     = { IN_AUDIO_DATA };
+    INT   inBufferSize[]    = { (INT)view.capacity() };
+    INT   inBufferElSize[]  = { sizeof(int16_t) };
+
+    AACENC_BufDesc inBufDesc;
+    inBufDesc.numBufs           = sizeof(inBuffer) / sizeof(void*);
+    inBufDesc.bufs              = (void**)&inBuffer;
+    inBufDesc.bufferIdentifiers = inBufferIds;
+    inBufDesc.bufSizes          = inBufferSize;
+    inBufDesc.bufElSizes        = inBufferElSize;
+
+    void* outBuffer[]       = { outPtr };
+    INT   outBufferIds[]    = { OUT_BITSTREAM_DATA };
+    INT   outBufferSize[]   = { 0 };
+    INT   outBufferElSize[] = { sizeof(UCHAR) };
+
+    AACENC_BufDesc outBufDesc;
+    outBufDesc.numBufs           = sizeof(outBuffer) / sizeof(void*);
+    outBufDesc.bufs              = (void**)&outBuffer;
+    outBufDesc.bufferIdentifiers = outBufferIds;
+    outBufDesc.bufSizes          = outBufferSize;
+    outBufDesc.bufElSizes        = outBufferElSize;
+
+    // Encode the mInputFrame, which is treated as a modulo buffer
+    AACENC_ERROR encoderErr = AACENC_OK;
+    size_t nOutputBytes = 0;
+
+    while (encoderErr == AACENC_OK && inargs.numInSamples > 0) {
+        memset(&outargs, 0, sizeof(outargs));
+
+        outBuffer[0] = outPtr;
+        outBufferSize[0] = outAvailable - nOutputBytes;
+
+        encoderErr = aacEncEncode(mAACEncoder,
+                                  &inBufDesc,
+                                  &outBufDesc,
+                                  &inargs,
+                                  &outargs);
+
+        if (encoderErr == AACENC_OK) {
+            if (outargs.numOutBytes > 0) {
+                mInputSize = 0;
+                int consumed = ((view.capacity() / sizeof(int16_t)) - inargs.numInSamples);
+                mInputTimeUs = work->input.ordinal.timestamp
+                        + (consumed * 1000000ll / mNumChannels / mSampleRate);
+            } else {
+                mInputSize += outargs.numInSamples * sizeof(int16_t);
+                mInputTimeUs += outargs.numInSamples * 1000000ll / mNumChannels / mSampleRate;
+            }
+            outPtr += outargs.numOutBytes;
+            nOutputBytes += outargs.numOutBytes;
+
+            if (outargs.numInSamples > 0) {
+                inBuffer[0] = (int16_t *)inBuffer[0] + outargs.numInSamples;
+                inBufferSize[0] -= outargs.numInSamples * sizeof(int16_t);
+                inargs.numInSamples -= outargs.numInSamples;
+            }
+        }
+        ALOGV("nOutputBytes = %zu; inargs.numInSamples = %d", nOutputBytes, inargs.numInSamples);
+    }
+
+    if (eos && inBufferSize[0] > 0) {
+        memset(&outargs, 0, sizeof(outargs));
+
+        outBuffer[0] = outPtr;
+        outBufferSize[0] = outAvailable - nOutputBytes;
+
+        // Flush
+        inargs.numInSamples = -1;
+
+        (void)aacEncEncode(mAACEncoder,
+                           &inBufDesc,
+                           &outBufDesc,
+                           &inargs,
+                           &outargs);
+
+        nOutputBytes += outargs.numOutBytes;
+    }
+
+    work->worklets.front()->output.flags =
+        (C2FrameData::flags_t)(eos ? C2FrameData::FLAG_END_OF_STREAM : 0);
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->worklets.front()->output.ordinal.timestamp = timestamp;
+    work->workletsProcessed = 1u;
+    if (nOutputBytes) {
+        work->worklets.front()->output.buffers.push_back(
+                createLinearBuffer(block, 0, nOutputBytes));
+    } else {
+        work->worklets.front()->output.buffers.emplace_back(nullptr);
+    }
+
+#if 0
+    ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)",
+          nOutputBytes, mInputTimeUs.peekll(), outHeader->nFlags);
+
+    hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen);
+#endif
+}
+
+c2_status_t C2SoftAacEnc::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    switch (drainMode) {
+        case DRAIN_COMPONENT_NO_EOS:  // fall-through
+        case NO_DRAIN:
+            // no-op
+            return C2_OK;
+        case DRAIN_CHAIN:
+            return C2_OMITTED;
+        case DRAIN_COMPONENT_WITH_EOS:
+            break;
+        default:
+            return C2_BAD_VALUE;
+    }
+
+    (void)pool;
+    mSentCodecSpecificData = false;
+    mInputSize = 0u;
+
+    // TODO: we don't have any pending work at this time to drain.
+    return C2_OK;
+}
+
+class C2SoftAacEncFactory : public C2ComponentFactory {
+public:
+    virtual c2_status_t createComponent(
+            c2_node_id_t id, std::shared_ptr<C2Component>* const component,
+            std::function<void(::android::C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(new C2SoftAacEnc("aacenc", id), deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(::android::C2ComponentInterface*)> deleter) override {
+        *interface =
+                SimpleC2Interface::Builder("aacenc", id, deleter)
+                .inputFormat(C2FormatAudio)
+                .outputFormat(C2FormatCompressed)
+                .build();
+        return C2_OK;
+    }
+
+    virtual ~C2SoftAacEncFactory() override = default;
+};
+
+}  // namespace android
+
+extern "C" ::android::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftAacEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/libstagefright/codecs/aacenc/C2SoftAacEnc.h b/media/libstagefright/codecs/aacenc/C2SoftAacEnc.h
new file mode 100644
index 0000000..c9f440f
--- /dev/null
+++ b/media/libstagefright/codecs/aacenc/C2SoftAacEnc.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef C2_SOFT_AAC_ENC_H_
+
+#define C2_SOFT_AAC_ENC_H_
+
+#include <SimpleC2Component.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#include "aacenc_lib.h"
+
+namespace android {
+
+class C2SoftAacEnc : public SimpleC2Component {
+public:
+    C2SoftAacEnc(const char *name, c2_node_id_t id);
+    virtual ~C2SoftAacEnc();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    HANDLE_AACENCODER mAACEncoder;
+
+    uint32_t mNumChannels;
+    uint32_t mSampleRate;
+    uint32_t mBitRate;
+    int32_t mSBRMode;
+    int32_t mSBRRatio;
+    AUDIO_OBJECT_TYPE mAACProfile;
+    UINT mNumBytesPerInputFrame;
+    UINT mOutBufferSize;
+
+    bool mSentCodecSpecificData;
+    size_t mInputSize;
+    c2_cntr64_t mInputTimeUs;
+
+    bool mSignalledError;
+
+    status_t initEncoder();
+
+    status_t setAudioParams();
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2SoftAacEnc);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_AAC_ENC_H_
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
index ffe6332..cc12d3c 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
@@ -206,14 +206,14 @@
 
 void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
     uint32_t flags = 0;
-    if ((work->input.flags & C2BufferPack::FLAG_END_OF_STREAM)) {
-        flags |= C2BufferPack::FLAG_END_OF_STREAM;
+    if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM)) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
     }
-    work->worklets.front()->output.flags = (C2BufferPack::flags_t)flags;
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
     work->worklets.front()->output.buffers.clear();
     work->worklets.front()->output.buffers.emplace_back(nullptr);
     work->worklets.front()->output.ordinal = work->input.ordinal;
-    work->worklets_processed = 1u;
+    work->workletsProcessed = 1u;
 }
 
 }  // namespace
@@ -448,7 +448,7 @@
 }
 
 c2_status_t C2SoftAvcDecIntf::query_vb(
-        const std::vector<C2Param* const> & stackParams,
+        const std::vector<C2Param*> & stackParams,
         const std::vector<C2Param::Index> & heapParamIndices,
         c2_blocking_t mayBlock,
         std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
@@ -485,7 +485,7 @@
 }
 
 c2_status_t C2SoftAvcDecIntf::config_vb(
-        const std::vector<C2Param* const> &params,
+        const std::vector<C2Param*> &params,
         c2_blocking_t mayBlock,
         std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
     (void)mayBlock;
@@ -1061,17 +1061,17 @@
     std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mAllocatedBlock));
     auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
         uint32_t flags = 0;
-        if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
-            flags |= C2BufferPack::FLAG_END_OF_STREAM;
+        if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+            flags |= C2FrameData::FLAG_END_OF_STREAM;
             ALOGV("EOS");
         }
-        work->worklets.front()->output.flags = (C2BufferPack::flags_t)flags;
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
         work->worklets.front()->output.buffers.clear();
         work->worklets.front()->output.buffers.push_back(buffer);
         work->worklets.front()->output.ordinal = work->input.ordinal;
-        work->worklets_processed = 1u;
+        work->workletsProcessed = 1u;
     };
-    if (work && index == work->input.ordinal.frame_index) {
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
         fillWork(work);
     } else {
         finish(index, fillWork);
@@ -1084,25 +1084,25 @@
     bool eos = false;
 
     work->result = C2_OK;
-    work->worklets_processed = 0u;
+    work->workletsProcessed = 0u;
 
     const C2ConstLinearBlock &buffer =
         work->input.buffers[0]->data().linearBlocks().front();
     if (buffer.capacity() == 0) {
-        ALOGV("empty input: %llu", (long long)work->input.ordinal.frame_index);
+        ALOGV("empty input: %llu", work->input.ordinal.frameIndex.peekull());
         // TODO: result?
         fillEmptyWork(work);
-        if ((work->input.flags & C2BufferPack::FLAG_END_OF_STREAM)) {
+        if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM)) {
             eos = true;
         }
         return;
-    } else if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
-        ALOGV("input EOS: %llu", (long long)work->input.ordinal.frame_index);
+    } else if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        ALOGV("input EOS: %llu", work->input.ordinal.frameIndex.peekull());
         eos = true;
     }
 
     C2ReadView input = work->input.buffers[0]->data().linearBlocks().front().map().get();
-    uint32_t workIndex = work->input.ordinal.frame_index & 0xFFFFFFFF;
+    uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
     size_t inOffset = 0u;
 
     while (inOffset < input.capacity()) {
@@ -1266,7 +1266,7 @@
     }
 
     if (drainMode == DRAIN_COMPONENT_WITH_EOS
-            && work && work->worklets_processed == 0u) {
+            && work && work->workletsProcessed == 0u) {
         fillEmptyWork(work);
     }
 
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
index 0e8cf77..6632bf3 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
@@ -84,12 +84,12 @@
     virtual C2String getName() const override;
     virtual c2_node_id_t getId() const override;
     virtual c2_status_t query_vb(
-            const std::vector<C2Param* const> &stackParams,
+            const std::vector<C2Param*> &stackParams,
             const std::vector<C2Param::Index> &heapParamIndices,
             c2_blocking_t mayBlock,
             std::vector<std::unique_ptr<C2Param>>* const heapParams) const override;
     virtual c2_status_t config_vb(
-            const std::vector<C2Param* const> &params,
+            const std::vector<C2Param*> &params,
             c2_blocking_t mayBlock,
             std::vector<std::unique_ptr<C2SettingResult>>* const failures) override;
     virtual c2_status_t createTunnel_sm(c2_node_id_t targetComponent) override;
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
index 32fdbd3..379d41e 100644
--- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
@@ -26,7 +26,6 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MetaData.h>
 #include <OMX_IndexExt.h>
 #include <OMX_VideoExt.h>
 
diff --git a/media/libstagefright/codecs/cmds/Android.bp b/media/libstagefright/codecs/cmds/Android.bp
index 40f1a3d..6dba0a3 100644
--- a/media/libstagefright/codecs/cmds/Android.bp
+++ b/media/libstagefright/codecs/cmds/Android.bp
@@ -13,6 +13,7 @@
         "libcutils",
         "libgui",
         "liblog",
+        "libmediaextractor",
         "libstagefright",
         "libstagefright_codec2",
         "libstagefright_codec2_vndk",
diff --git a/media/libstagefright/codecs/cmds/codec2.cpp b/media/libstagefright/codecs/cmds/codec2.cpp
index 78fb527..ec05d7a 100644
--- a/media/libstagefright/codecs/cmds/codec2.cpp
+++ b/media/libstagefright/codecs/cmds/codec2.cpp
@@ -247,7 +247,7 @@
             }
             int slot;
             sp<Fence> fence;
-            ALOGV("Render: Frame #%" PRId64, work->worklets.front()->output.ordinal.frame_index);
+            ALOGV("Render: Frame #%lld", work->worklets.front()->output.ordinal.frameIndex.peekll());
             const std::shared_ptr<C2Buffer> &output = work->worklets.front()->output.buffers[0];
             if (output) {
                 const C2ConstGraphicBlock &block = output->data().graphicBlocks().front();
@@ -266,7 +266,7 @@
                 status_t err = igbp->attachBuffer(&slot, buffer);
 
                 IGraphicBufferProducer::QueueBufferInput qbi(
-                        work->worklets.front()->output.ordinal.timestamp * 1000ll,
+                        (work->worklets.front()->output.ordinal.timestamp * 1000ll).peekll(),
                         false,
                         HAL_DATASPACE_UNKNOWN,
                         Rect(block.width(), block.height()),
@@ -338,9 +338,9 @@
                 mQueueCondition.wait_for(l, 100ms);
             }
         }
-        work->input.flags = (C2BufferPack::flags_t)0;
+        work->input.flags = (C2FrameData::flags_t)0;
         work->input.ordinal.timestamp = timestamp;
-        work->input.ordinal.frame_index = numFrames;
+        work->input.ordinal.frameIndex = numFrames;
 
         std::shared_ptr<C2LinearBlock> block;
         mLinearPool->fetchLinearBlock(
diff --git a/media/libstagefright/foundation/ABuffer.cpp b/media/libstagefright/foundation/ABuffer.cpp
index 804046a..c8965d9 100644
--- a/media/libstagefright/foundation/ABuffer.cpp
+++ b/media/libstagefright/foundation/ABuffer.cpp
@@ -19,13 +19,11 @@
 #include "ADebug.h"
 #include "ALooper.h"
 #include "AMessage.h"
-#include "MediaBufferBase.h"
 
 namespace android {
 
 ABuffer::ABuffer(size_t capacity)
-    : mMediaBufferBase(NULL),
-      mRangeOffset(0),
+    : mRangeOffset(0),
       mInt32Data(0),
       mOwnsData(true) {
     mData = malloc(capacity);
@@ -39,8 +37,7 @@
 }
 
 ABuffer::ABuffer(void *data, size_t capacity)
-    : mMediaBufferBase(NULL),
-      mData(data),
+    : mData(data),
       mCapacity(capacity),
       mRangeOffset(0),
       mRangeLength(capacity),
@@ -66,8 +63,6 @@
             mData = NULL;
         }
     }
-
-    setMediaBufferBase(NULL);
 }
 
 void ABuffer::setRange(size_t offset, size_t size) {
@@ -85,19 +80,5 @@
     return mMeta;
 }
 
-MediaBufferBase *ABuffer::getMediaBufferBase() {
-    if (mMediaBufferBase != NULL) {
-        mMediaBufferBase->add_ref();
-    }
-    return mMediaBufferBase;
-}
-
-void ABuffer::setMediaBufferBase(MediaBufferBase *mediaBuffer) {
-    if (mMediaBufferBase != NULL) {
-        mMediaBufferBase->release();
-    }
-    mMediaBufferBase = mediaBuffer;
-}
-
 }  // namespace android
 
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index df3e280..2258e2c 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -4,7 +4,7 @@
     vendor_available: true,
 }
 
-cc_library_shared {
+cc_library {
     name: "libstagefright_foundation",
     vendor_available: true,
     vndk: {
@@ -62,8 +62,6 @@
         "AStringUtils.cpp",
         "ByteUtils.cpp",
         "ColorUtils.cpp",
-        "MediaBuffer.cpp",
-        "MediaBufferGroup.cpp",
         "MediaDefs.cpp",
         "MediaKeys.cpp",
         "MetaData.cpp",
diff --git a/media/libstagefright/foundation/MetaData.cpp b/media/libstagefright/foundation/MetaData.cpp
index a8965f0..2415c61 100644
--- a/media/libstagefright/foundation/MetaData.cpp
+++ b/media/libstagefright/foundation/MetaData.cpp
@@ -17,6 +17,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MetaData"
 #include <inttypes.h>
+#include <utils/KeyedVector.h>
 #include <utils/Log.h>
 
 #include <stdlib.h>
@@ -29,30 +30,81 @@
 
 namespace android {
 
-MetaData::MetaData() {
+struct MetaData::typed_data {
+    typed_data();
+    ~typed_data();
+
+    typed_data(const MetaData::typed_data &);
+    typed_data &operator=(const MetaData::typed_data &);
+
+    void clear();
+    void setData(uint32_t type, const void *data, size_t size);
+    void getData(uint32_t *type, const void **data, size_t *size) const;
+    // may include hexdump of binary data if verbose=true
+    String8 asString(bool verbose) const;
+
+private:
+    uint32_t mType;
+    size_t mSize;
+
+    union {
+        void *ext_data;
+        float reservoir;
+    } u;
+
+    bool usesReservoir() const {
+        return mSize <= sizeof(u.reservoir);
+    }
+
+    void *allocateStorage(size_t size);
+    void freeStorage();
+
+    void *storage() {
+        return usesReservoir() ? &u.reservoir : u.ext_data;
+    }
+
+    const void *storage() const {
+        return usesReservoir() ? &u.reservoir : u.ext_data;
+    }
+};
+
+struct MetaData::Rect {
+    int32_t mLeft, mTop, mRight, mBottom;
+};
+
+
+struct MetaData::MetaDataInternal {
+    KeyedVector<uint32_t, MetaData::typed_data> mItems;
+};
+
+
+MetaData::MetaData()
+    : mInternalData(new MetaDataInternal()) {
 }
 
 MetaData::MetaData(const MetaData &from)
     : RefBase(),
-      mItems(from.mItems) {
+      mInternalData(new MetaDataInternal()) {
+    mInternalData->mItems = from.mInternalData->mItems;
 }
 
 MetaData::~MetaData() {
     clear();
+    delete mInternalData;
 }
 
 void MetaData::clear() {
-    mItems.clear();
+    mInternalData->mItems.clear();
 }
 
 bool MetaData::remove(uint32_t key) {
-    ssize_t i = mItems.indexOfKey(key);
+    ssize_t i = mInternalData->mItems.indexOfKey(key);
 
     if (i < 0) {
         return false;
     }
 
-    mItems.removeItemsAt(i);
+    mInternalData->mItems.removeItemsAt(i);
 
     return true;
 }
@@ -192,15 +244,15 @@
         uint32_t key, uint32_t type, const void *data, size_t size) {
     bool overwrote_existing = true;
 
-    ssize_t i = mItems.indexOfKey(key);
+    ssize_t i = mInternalData->mItems.indexOfKey(key);
     if (i < 0) {
         typed_data item;
-        i = mItems.add(key, item);
+        i = mInternalData->mItems.add(key, item);
 
         overwrote_existing = false;
     }
 
-    typed_data &item = mItems.editValueAt(i);
+    typed_data &item = mInternalData->mItems.editValueAt(i);
 
     item.setData(type, data, size);
 
@@ -209,13 +261,13 @@
 
 bool MetaData::findData(uint32_t key, uint32_t *type,
                         const void **data, size_t *size) const {
-    ssize_t i = mItems.indexOfKey(key);
+    ssize_t i = mInternalData->mItems.indexOfKey(key);
 
     if (i < 0) {
         return false;
     }
 
-    const typed_data &item = mItems.valueAt(i);
+    const typed_data &item = mInternalData->mItems.valueAt(i);
 
     item.getData(type, data, size);
 
@@ -223,7 +275,7 @@
 }
 
 bool MetaData::hasData(uint32_t key) const {
-    ssize_t i = mItems.indexOfKey(key);
+    ssize_t i = mInternalData->mItems.indexOfKey(key);
 
     if (i < 0) {
         return false;
@@ -369,11 +421,11 @@
 
 String8 MetaData::toString() const {
     String8 s;
-    for (int i = mItems.size(); --i >= 0;) {
-        int32_t key = mItems.keyAt(i);
+    for (int i = mInternalData->mItems.size(); --i >= 0;) {
+        int32_t key = mInternalData->mItems.keyAt(i);
         char cc[5];
         MakeFourCCString(key, cc);
-        const typed_data &item = mItems.valueAt(i);
+        const typed_data &item = mInternalData->mItems.valueAt(i);
         s.appendFormat("%s: %s", cc, item.asString(false).string());
         if (i != 0) {
             s.append(", ");
@@ -382,25 +434,25 @@
     return s;
 }
 void MetaData::dumpToLog() const {
-    for (int i = mItems.size(); --i >= 0;) {
-        int32_t key = mItems.keyAt(i);
+    for (int i = mInternalData->mItems.size(); --i >= 0;) {
+        int32_t key = mInternalData->mItems.keyAt(i);
         char cc[5];
         MakeFourCCString(key, cc);
-        const typed_data &item = mItems.valueAt(i);
+        const typed_data &item = mInternalData->mItems.valueAt(i);
         ALOGI("%s: %s", cc, item.asString(true /* verbose */).string());
     }
 }
 
 status_t MetaData::writeToParcel(Parcel &parcel) {
     status_t ret;
-    size_t numItems = mItems.size();
+    size_t numItems = mInternalData->mItems.size();
     ret = parcel.writeUint32(uint32_t(numItems));
     if (ret) {
         return ret;
     }
     for (size_t i = 0; i < numItems; i++) {
-        int32_t key = mItems.keyAt(i);
-        const typed_data &item = mItems.valueAt(i);
+        int32_t key = mInternalData->mItems.keyAt(i);
+        const typed_data &item = mInternalData->mItems.valueAt(i);
         uint32_t type;
         const void *data;
         size_t size;
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ABuffer.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ABuffer.h
index ef11434..8fe9f8d 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/ABuffer.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/ABuffer.h
@@ -27,7 +27,6 @@
 namespace android {
 
 struct AMessage;
-class MediaBufferBase;
 
 struct ABuffer : public RefBase {
     explicit ABuffer(size_t capacity);
@@ -49,17 +48,12 @@
 
     sp<AMessage> meta();
 
-    MediaBufferBase *getMediaBufferBase();
-    void setMediaBufferBase(MediaBufferBase *mediaBuffer);
-
 protected:
     virtual ~ABuffer();
 
 private:
     sp<AMessage> mMeta;
 
-    MediaBufferBase *mMediaBufferBase;
-
     void *mData;
     size_t mCapacity;
     size_t mRangeOffset;
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaBufferBase.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaBufferBase.h
deleted file mode 100644
index 99418fb..0000000
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaBufferBase.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MEDIA_BUFFER_BASE_H_
-
-#define MEDIA_BUFFER_BASE_H_
-
-namespace android {
-
-class MediaBufferBase {
-public:
-    MediaBufferBase() {}
-
-    virtual void release() = 0;
-    virtual void add_ref() = 0;
-
-protected:
-    virtual ~MediaBufferBase() {}
-
-private:
-    MediaBufferBase(const MediaBufferBase &);
-    MediaBufferBase &operator=(const MediaBufferBase &);
-};
-
-}  // namespace android
-
-#endif  // MEDIA_BUFFER_BASE_H_
diff --git a/media/libstagefright/gbs/Android.bp b/media/libstagefright/gbs/Android.bp
new file mode 100644
index 0000000..a53b7b7
--- /dev/null
+++ b/media/libstagefright/gbs/Android.bp
@@ -0,0 +1,58 @@
+cc_library_shared {
+    name: "libstagefright_gbs",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+
+    srcs: [
+        "FrameDropper.cpp",
+        "GraphicBufferSource.cpp",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    header_libs: [
+        "media_plugin_headers",
+    ],
+
+    export_header_lib_headers: [
+        "media_plugin_headers",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libutils",
+        "liblog",
+        "libui",
+        "libgui",
+        "libcutils",
+        "libstagefright_foundation",
+        "libnativewindow", // TODO(b/62923479): use header library
+    ],
+
+    export_shared_lib_headers: [
+        "libstagefright_foundation",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wno-unused-parameter",
+        "-Wno-documentation",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+}
diff --git a/media/libstagefright/omx/FrameDropper.cpp b/media/libstagefright/gbs/FrameDropper.cpp
similarity index 97%
rename from media/libstagefright/omx/FrameDropper.cpp
rename to media/libstagefright/gbs/FrameDropper.cpp
index 0c50c58..9f0b8cc 100644
--- a/media/libstagefright/omx/FrameDropper.cpp
+++ b/media/libstagefright/gbs/FrameDropper.cpp
@@ -18,7 +18,7 @@
 #define LOG_TAG "FrameDropper"
 #include <utils/Log.h>
 
-#include <media/stagefright/omx/FrameDropper.h>
+#include <media/stagefright/gbs/FrameDropper.h>
 #include <media/stagefright/foundation/ADebug.h>
 
 namespace android {
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/gbs/GraphicBufferSource.cpp
similarity index 96%
rename from media/libstagefright/omx/GraphicBufferSource.cpp
rename to media/libstagefright/gbs/GraphicBufferSource.cpp
index f331dbb..139c916 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/gbs/GraphicBufferSource.cpp
@@ -22,9 +22,8 @@
 
 #define STRINGIFY_ENUMS // for asString in HardwareAPI.h/VideoAPI.h
 
-#include <media/stagefright/omx/GraphicBufferSource.h>
-#include <media/stagefright/omx/FrameDropper.h>
-#include <media/stagefright/omx/OMXUtils.h>
+#include <media/stagefright/gbs/GraphicBufferSource.h>
+#include <media/stagefright/gbs/FrameDropper.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/ColorUtils.h>
@@ -34,9 +33,6 @@
 #include <ui/GraphicBuffer.h>
 #include <gui/BufferItem.h>
 #include <media/hardware/HardwareAPI.h>
-#include <media/openmax/OMX_Component.h>
-#include <media/openmax/OMX_IndexExt.h>
-#include <media/OMXBuffer.h>
 
 #include <inttypes.h>
 
@@ -361,9 +357,9 @@
     }
 }
 
-Status GraphicBufferSource::onOmxExecuting() {
+Status GraphicBufferSource::start() {
     Mutex::Autolock autoLock(mMutex);
-    ALOGV("--> executing; available=%zu, submittable=%zd",
+    ALOGV("--> start; available=%zu, submittable=%zd",
             mAvailableBuffers.size(), mFreeCodecBuffers.size());
     CHECK(!mExecuting);
     mExecuting = true;
@@ -411,8 +407,8 @@
     return Status::ok();
 }
 
-Status GraphicBufferSource::onOmxIdle() {
-    ALOGV("omxIdle");
+Status GraphicBufferSource::stop() {
+    ALOGV("stop");
 
     Mutex::Autolock autoLock(mMutex);
 
@@ -424,7 +420,7 @@
     return Status::ok();
 }
 
-Status GraphicBufferSource::onOmxLoaded(){
+Status GraphicBufferSource::release(){
     Mutex::Autolock autoLock(mMutex);
     if (mLooper != NULL) {
         mLooper->unregisterHandler(mReflector->id());
@@ -434,7 +430,7 @@
         mLooper.clear();
     }
 
-    ALOGV("--> loaded; available=%zu+%d eos=%d eosSent=%d acquired=%d",
+    ALOGV("--> release; available=%zu+%d eos=%d eosSent=%d acquired=%d",
             mAvailableBuffers.size(), mNumAvailableUnacquiredBuffers,
             mEndOfStream, mEndOfStreamSent, mNumOutstandingAcquires);
 
@@ -442,7 +438,7 @@
     mFreeCodecBuffers.clear();
     mSubmittedCodecBuffers.clear();
     mLatestBuffer.mBuffer.reset();
-    mOMXNode.clear();
+    mComponent.clear();
     mExecuting = false;
 
     return Status::ok();
@@ -537,7 +533,8 @@
     mLastDataspace = dataspace;
 
     if (ColorUtils::convertDataSpaceToV0(dataspace)) {
-        mOMXNode->dispatchDataSpaceChanged(mLastDataspace, mDefaultColorAspectsPacked, pixelFormat);
+        mComponent->dispatchDataSpaceChanged(
+                mLastDataspace, mDefaultColorAspectsPacked, pixelFormat);
     }
 }
 
@@ -631,7 +628,7 @@
                 default:
                     TRESPASS_DBG("Unknown action type");
                     // return true here because we did consume an available buffer, so the
-                    // loop in onOmxExecuting will eventually terminate even if we hit this.
+                    // loop in start will eventually terminate even if we hit this.
                     return false;
             }
         }
@@ -799,7 +796,7 @@
 
 status_t GraphicBufferSource::submitBuffer_l(const VideoBuffer &item) {
     CHECK(!mFreeCodecBuffers.empty());
-    IOMX::buffer_id codecBufferId = *mFreeCodecBuffers.begin();
+    uint32_t codecBufferId = *mFreeCodecBuffers.begin();
 
     ALOGV("submitBuffer_l [slot=%d, bufferId=%d]", item.mBuffer->getSlot(), codecBufferId);
 
@@ -815,15 +812,14 @@
     }
 
     std::shared_ptr<AcquiredBuffer> buffer = item.mBuffer;
-    // use a GraphicBuffer for now as OMXNodeInstance is using GraphicBuffers to hold references
+    // use a GraphicBuffer for now as component is using GraphicBuffers to hold references
     // and it requires this graphic buffer to be able to hold its reference
     // and thus we would need to create a new GraphicBuffer from an ANWBuffer separate from the
     // acquired GraphicBuffer.
     // TODO: this can be reworked globally to use ANWBuffer references
     sp<GraphicBuffer> graphicBuffer = buffer->getGraphicBuffer();
-    status_t err = mOMXNode->emptyBuffer(
-            codecBufferId, OMX_BUFFERFLAG_ENDOFFRAME, graphicBuffer, codecTimeUs,
-            buffer->getAcquireFenceFd());
+    status_t err = mComponent->submitBuffer(
+            codecBufferId, graphicBuffer, codecTimeUs, buffer->getAcquireFenceFd());
 
     if (err != OK) {
         ALOGW("WARNING: emptyGraphicBuffer failed: 0x%x", err);
@@ -849,11 +845,10 @@
         ALOGV("submitEndOfInputStream_l: no codec buffers available");
         return;
     }
-    IOMX::buffer_id codecBufferId = *mFreeCodecBuffers.begin();
+    uint32_t codecBufferId = *mFreeCodecBuffers.begin();
 
     // We reject any additional incoming graphic buffers. There is no acquired buffer used for EOS
-    status_t err = mOMXNode->emptyBuffer(
-            codecBufferId, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS);
+    status_t err = mComponent->submitEos(codecBufferId);
     if (err != OK) {
         ALOGW("emptyDirectBuffer EOS failed: 0x%x", err);
     } else {
@@ -959,7 +954,7 @@
 
 bool GraphicBufferSource::areWeDiscardingAvailableBuffers_l() {
     return mEndOfStreamSent // already sent EOS to codec
-            || mOMXNode == nullptr // there is no codec connected
+            || mComponent == nullptr // there is no codec connected
             || (mSuspended && mActionQueue.empty()) // we are suspended and not waiting for
                                                     // any further action
             || !mExecuting;
@@ -970,7 +965,7 @@
         // This should only be possible if a new buffer was queued after
         // EOS was signaled, i.e. the app is misbehaving.
         ALOGW("onFrameAvailable: EOS is sent, ignoring frame");
-    } else if (mOMXNode == NULL || (mSuspended && mActionQueue.empty())) {
+    } else if (mComponent == NULL || (mSuspended && mActionQueue.empty())) {
         // FIXME: if we are suspended but have a resume queued we will stop repeating the last
         // frame. Is that the desired behavior?
         ALOGV("onFrameAvailable: suspended, ignoring frame");
@@ -1064,13 +1059,13 @@
 }
 
 status_t GraphicBufferSource::configure(
-        const sp<IOmxNodeWrapper>& omxNode,
+        const sp<ComponentWrapper>& component,
         int32_t dataSpace,
         int32_t bufferCount,
         uint32_t frameWidth,
         uint32_t frameHeight,
         uint32_t consumerUsage) {
-    if (omxNode == NULL) {
+    if (component == NULL) {
         return BAD_VALUE;
     }
 
@@ -1088,7 +1083,7 @@
 
     {
         Mutex::Autolock autoLock(mMutex);
-        mOMXNode = omxNode;
+        mComponent = component;
 
         err = mConsumer->setDefaultBufferSize(frameWidth, frameHeight);
         if (err != NO_ERROR) {
@@ -1320,7 +1315,7 @@
     // Set the end-of-stream flag.  If no frames are pending from the
     // BufferQueue, and a codec buffer is available, and we're executing,
     // and there is no stop timestamp, we initiate the EOS from here.
-    // Otherwise, we'll let codecBufferEmptied() (or omxExecuting) do it.
+    // Otherwise, we'll let codecBufferEmptied() (or start) do it.
     //
     // Note: if there are no pending frames and all codec buffers are
     // available, we *must* submit the EOS from here or we'll just
diff --git a/media/libstagefright/gbs/include/media/stagefright/gbs/ComponentWrapper.h b/media/libstagefright/gbs/include/media/stagefright/gbs/ComponentWrapper.h
new file mode 100644
index 0000000..e27829b
--- /dev/null
+++ b/media/libstagefright/gbs/include/media/stagefright/gbs/ComponentWrapper.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018, 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.
+ */
+
+#ifndef COMPONENT_WRAPPER_H_
+#define COMPONENT_WRAPPER_H_
+
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+#include <ui/GraphicBuffer.h>
+
+#include <stdint.h>
+
+namespace android {
+
+struct ComponentWrapper : public RefBase {
+    virtual status_t submitBuffer(
+            int32_t bufferId, const sp<GraphicBuffer> &buffer = nullptr,
+            int64_t timestamp = 0, int fenceFd = -1) = 0;
+    virtual status_t submitEos(int32_t bufferId) = 0;
+    virtual void dispatchDataSpaceChanged(
+            int32_t dataSpace, int32_t aspects, int32_t pixelFormat) = 0;
+};
+
+}  // namespace android
+
+#endif  // COMPONENT_WRAPPER_H_
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/FrameDropper.h b/media/libstagefright/gbs/include/media/stagefright/gbs/FrameDropper.h
similarity index 100%
rename from media/libstagefright/omx/include/media/stagefright/omx/FrameDropper.h
rename to media/libstagefright/gbs/include/media/stagefright/gbs/FrameDropper.h
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h b/media/libstagefright/gbs/include/media/stagefright/gbs/GraphicBufferSource.h
similarity index 93%
rename from media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h
rename to media/libstagefright/gbs/include/media/stagefright/gbs/GraphicBufferSource.h
index 84fee6f..89f6cf8 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/gbs/include/media/stagefright/gbs/GraphicBufferSource.h
@@ -18,21 +18,16 @@
 
 #define GRAPHIC_BUFFER_SOURCE_H_
 
-#include <gui/IGraphicBufferProducer.h>
+#include <binder/Status.h>
 #include <gui/BufferQueue.h>
+#include <gui/IGraphicBufferProducer.h>
 #include <utils/RefBase.h>
 
 #include <media/hardware/VideoAPI.h>
-#include <media/IOMX.h>
-#include <media/OMXFenceParcelable.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/foundation/AHandlerReflector.h>
 #include <media/stagefright/foundation/ALooper.h>
-
-#include <android/BnGraphicBufferSource.h>
-#include <android/BnOMXBufferSource.h>
-
-#include "IOmxNodeWrapper.h"
+#include <media/stagefright/gbs/ComponentWrapper.h>
 
 namespace android {
 
@@ -41,7 +36,7 @@
 struct FrameDropper;
 
 /*
- * This class is used to feed OMX codecs from a Surface via BufferQueue or
+ * This class is used to feed codecs from a Surface via BufferQueue or
  * HW producer.
  *
  * Instances of the class don't run on a dedicated thread.  Instead,
@@ -49,7 +44,7 @@
  *
  *  - Availability of a new frame of data from the BufferQueue (notified
  *    via the onFrameAvailable callback).
- *  - The return of a codec buffer (via OnEmptyBufferDone).
+ *  - The return of a codec buffer.
  *  - Application signaling end-of-stream.
  *  - Transition to or from "executing" state.
  *
@@ -91,39 +86,37 @@
         return mProducer;
     }
 
-    // OmxBufferSource interface
-    // ------------------------------
-
-    // This is called when OMX transitions to OMX_StateExecuting, which means
+    // This is called when component transitions to running state, which means
     // we can start handing it buffers.  If we already have buffers of data
     // sitting in the BufferQueue, this will send them to the codec.
-    Status onOmxExecuting();
+    Status start();
 
-    // This is called when OMX transitions to OMX_StateIdle, indicating that
+    // This is called when component transitions to stopped, indicating that
     // the codec is meant to return all buffers back to the client for them
     // to be freed. Do NOT submit any more buffers to the component.
-    Status onOmxIdle();
+    Status stop();
 
-    // This is called when OMX transitions to OMX_StateLoaded, indicating that
+    // This is called when component transitions to released, indicating that
     // we are shutting down.
-    Status onOmxLoaded();
+    Status release();
 
     // A "codec buffer", i.e. a buffer that can be used to pass data into
     // the encoder, has been allocated.  (This call does not call back into
-    // OMXNodeInstance.)
+    // component.)
     Status onInputBufferAdded(int32_t bufferId);
 
-    // Called from OnEmptyBufferDone.  If we have a BQ buffer available,
-    // fill it with a new frame of data; otherwise, just mark it as available.
+    // Called when encoder is no longer using the buffer.  If we have a BQ
+    // buffer available, fill it with a new frame of data; otherwise, just mark
+    // it as available.
     Status onInputBufferEmptied(int32_t bufferId, int fenceFd);
 
     // IGraphicBufferSource interface
     // ------------------------------
 
-    // Configure the buffer source to be used with an OMX node with the default
+    // Configure the buffer source to be used with a component with the default
     // data space.
     status_t configure(
-        const sp<IOmxNodeWrapper> &omxNode,
+        const sp<ComponentWrapper> &component,
         int32_t dataSpace,
         int32_t bufferCount,
         uint32_t frameWidth,
@@ -335,10 +328,10 @@
     // called when the data space of the input buffer changes
     void onDataspaceChanged_l(android_dataspace dataspace, android_pixel_format pixelFormat);
 
-    // Pointer back to the Omx node that created us.  We send buffers here.
-    sp<IOmxNodeWrapper> mOMXNode;
+    // Pointer back to the component that created us.  We send buffers here.
+    sp<ComponentWrapper> mComponent;
 
-    // Set by omxExecuting() / omxIdling().
+    // Set by start() / stop().
     bool mExecuting;
 
     bool mSuspended;
diff --git a/media/libstagefright/gbs/tests/Android.bp b/media/libstagefright/gbs/tests/Android.bp
new file mode 100644
index 0000000..1463e73
--- /dev/null
+++ b/media/libstagefright/gbs/tests/Android.bp
@@ -0,0 +1,15 @@
+cc_test {
+    name: "FrameDropper_test",
+
+    srcs: ["FrameDropper_test.cpp"],
+
+    shared_libs: [
+        "libstagefright_gbs",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+}
diff --git a/media/libstagefright/omx/tests/FrameDropper_test.cpp b/media/libstagefright/gbs/tests/FrameDropper_test.cpp
similarity index 98%
rename from media/libstagefright/omx/tests/FrameDropper_test.cpp
rename to media/libstagefright/gbs/tests/FrameDropper_test.cpp
index a925da6..54a84d3 100644
--- a/media/libstagefright/omx/tests/FrameDropper_test.cpp
+++ b/media/libstagefright/gbs/tests/FrameDropper_test.cpp
@@ -20,7 +20,7 @@
 
 #include <gtest/gtest.h>
 
-#include <media/stagefright/omx/FrameDropper.h>
+#include <media/stagefright/gbs/FrameDropper.h>
 #include <media/stagefright/foundation/ADebug.h>
 
 namespace android {
diff --git a/media/libstagefright/httplive/Android.bp b/media/libstagefright/httplive/Android.bp
index ac113b8..de560b6 100644
--- a/media/libstagefright/httplive/Android.bp
+++ b/media/libstagefright/httplive/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
     name: "libstagefright_httplive",
 
     srcs: [
diff --git a/media/libstagefright/include/media/stagefright/AACWriter.h b/media/libstagefright/include/media/stagefright/AACWriter.h
index aa60a19..7c63ddd 100644
--- a/media/libstagefright/include/media/stagefright/AACWriter.h
+++ b/media/libstagefright/include/media/stagefright/AACWriter.h
@@ -24,7 +24,6 @@
 namespace android {
 
 struct MediaSource;
-class MetaData;
 
 struct AACWriter : public MediaWriter {
     AACWriter(int fd);
diff --git a/media/libstagefright/include/media/stagefright/AMRWriter.h b/media/libstagefright/include/media/stagefright/AMRWriter.h
index 7d2c879..2ea2f78 100644
--- a/media/libstagefright/include/media/stagefright/AMRWriter.h
+++ b/media/libstagefright/include/media/stagefright/AMRWriter.h
@@ -25,8 +25,6 @@
 
 namespace android {
 
-class MetaData;
-
 struct AMRWriter : public MediaWriter {
     AMRWriter(int fd);
 
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 5d2c120..2a062cc 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -30,7 +30,6 @@
 
 struct AMessage;
 class MediaBuffer;
-class MetaData;
 struct ABuffer;
 
 class MPEG4Writer : public MediaWriter {
diff --git a/media/libstagefright/include/media/stagefright/MediaBuffer.h b/media/libstagefright/include/media/stagefright/MediaBuffer.h
new file mode 120000
index 0000000..1d49c1a
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MediaBuffer.h
@@ -0,0 +1 @@
+../../../../libmediaextractor/include/media/stagefright/MediaBuffer.h
\ No newline at end of file
diff --git a/media/libstagefright/include/media/stagefright/MediaBufferGroup.h b/media/libstagefright/include/media/stagefright/MediaBufferGroup.h
new file mode 120000
index 0000000..009b3d9
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MediaBufferGroup.h
@@ -0,0 +1 @@
+../../../../libmediaextractor/include/media/stagefright/MediaBufferGroup.h
\ No newline at end of file
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecSource.h b/media/libstagefright/include/media/stagefright/MediaCodecSource.h
index bc0653d..eec115e 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecSource.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecSource.h
@@ -30,7 +30,6 @@
 struct AReplyToken;
 class IGraphicBufferProducer;
 struct MediaCodec;
-class MetaData;
 
 struct MediaCodecSource : public MediaSource,
                           public MediaBufferObserver {
diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
index 55654f1..e5c67e1 100644
--- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
+++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
@@ -48,8 +48,10 @@
     static std::shared_ptr<List<sp<ExtractorPlugin>>> gPlugins;
     static bool gPluginsRegistered;
 
-    static void RegisterExtractors(
+    static void RegisterExtractorsInApk(
             const char *apkPath, List<sp<ExtractorPlugin>> &pluginList);
+    static void RegisterExtractorsInSystem(
+            const char *libDirPath, List<sp<ExtractorPlugin>> &pluginList);
     static void RegisterExtractor(
             const sp<ExtractorPlugin> &plugin, List<sp<ExtractorPlugin>> &pluginList);
 
diff --git a/media/libstagefright/include/media/stagefright/MediaWriter.h b/media/libstagefright/include/media/stagefright/MediaWriter.h
index c4bba0e..2c12a87 100644
--- a/media/libstagefright/include/media/stagefright/MediaWriter.h
+++ b/media/libstagefright/include/media/stagefright/MediaWriter.h
@@ -24,8 +24,6 @@
 
 namespace android {
 
-class MetaData;
-
 struct MediaWriter : public RefBase {
     MediaWriter()
         : mMaxFileSizeLimitBytes(0),
diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libstagefright/include/media/stagefright/MetaData.h
index 3438c56..5959e86 100644
--- a/media/libstagefright/include/media/stagefright/MetaData.h
+++ b/media/libstagefright/include/media/stagefright/MetaData.h
@@ -24,7 +24,6 @@
 
 #include <binder/Parcel.h>
 #include <utils/RefBase.h>
-#include <utils/KeyedVector.h>
 #include <utils/String8.h>
 
 namespace android {
@@ -229,7 +228,7 @@
     kTypeD263        = 'd263',
 };
 
-class MetaData : public RefBase {
+class MetaData final : public RefBase {
 public:
     MetaData();
     MetaData(const MetaData &from);
@@ -279,59 +278,22 @@
     String8 toString() const;
     void dumpToLog() const;
 
-    status_t writeToParcel(Parcel &parcel);
-    status_t updateFromParcel(const Parcel &parcel);
-    static sp<MetaData> createFromParcel(const Parcel &parcel);
-
 protected:
     virtual ~MetaData();
 
 private:
-    struct typed_data {
-        typed_data();
-        ~typed_data();
+    friend class BpMediaSource;
+    friend class BnMediaSource;
+    friend class BpMediaExtractor;
+    friend class BnMediaExtractor;
 
-        typed_data(const MetaData::typed_data &);
-        typed_data &operator=(const MetaData::typed_data &);
-
-        void clear();
-        void setData(uint32_t type, const void *data, size_t size);
-        void getData(uint32_t *type, const void **data, size_t *size) const;
-        // may include hexdump of binary data if verbose=true
-        String8 asString(bool verbose) const;
-
-    private:
-        uint32_t mType;
-        size_t mSize;
-
-        union {
-            void *ext_data;
-            float reservoir;
-        } u;
-
-        bool usesReservoir() const {
-            return mSize <= sizeof(u.reservoir);
-        }
-
-        void *allocateStorage(size_t size);
-        void freeStorage();
-
-        void *storage() {
-            return usesReservoir() ? &u.reservoir : u.ext_data;
-        }
-
-        const void *storage() const {
-            return usesReservoir() ? &u.reservoir : u.ext_data;
-        }
-    };
-
-    struct Rect {
-        int32_t mLeft, mTop, mRight, mBottom;
-    };
-
-    KeyedVector<uint32_t, typed_data> mItems;
-
-    // MetaData &operator=(const MetaData &);
+    status_t writeToParcel(Parcel &parcel);
+    status_t updateFromParcel(const Parcel &parcel);
+    static sp<MetaData> createFromParcel(const Parcel &parcel);
+    struct typed_data;
+    struct Rect;
+    struct MetaDataInternal;
+    MetaDataInternal *mInternalData;
 };
 
 }  // namespace android
diff --git a/media/libstagefright/include/media/stagefright/Utils.h b/media/libstagefright/include/media/stagefright/Utils.h
index 7d4a611..6a28e0b 100644
--- a/media/libstagefright/include/media/stagefright/Utils.h
+++ b/media/libstagefright/include/media/stagefright/Utils.h
@@ -28,7 +28,6 @@
 
 namespace android {
 
-class MetaData;
 struct AMessage;
 status_t convertMetaDataToMessage(
         const sp<MetaData> &meta, sp<AMessage> *format);
diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp
index fe50656..4e2d398 100644
--- a/media/libstagefright/omx/1.0/Omx.cpp
+++ b/media/libstagefright/omx/1.0/Omx.cpp
@@ -24,7 +24,7 @@
 
 #include <media/stagefright/omx/OMXUtils.h>
 #include <media/stagefright/omx/OMXMaster.h>
-#include <media/stagefright/omx/GraphicBufferSource.h>
+#include <media/stagefright/omx/OmxGraphicBufferSource.h>
 
 #include <media/stagefright/omx/1.0/WOmxNode.h>
 #include <media/stagefright/omx/1.0/WOmxObserver.h>
@@ -148,7 +148,7 @@
 Return<void> Omx::createInputSurface(createInputSurface_cb _hidl_cb) {
     sp<::android::IGraphicBufferProducer> bufferProducer;
 
-    sp<GraphicBufferSource> graphicBufferSource = new GraphicBufferSource();
+    sp<OmxGraphicBufferSource> graphicBufferSource = new OmxGraphicBufferSource();
     status_t err = graphicBufferSource->initCheck();
     if (err != OK) {
         LOG(ERROR) << "Failed to create persistent input surface: "
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
index 3201c32..ed272bb 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
@@ -79,9 +79,9 @@
 };
 
 struct TWGraphicBufferSource::TWOmxBufferSource : public IOmxBufferSource {
-    sp<GraphicBufferSource> mSource;
+    sp<OmxGraphicBufferSource> mSource;
 
-    TWOmxBufferSource(const sp<GraphicBufferSource> &source): mSource(source) {
+    TWOmxBufferSource(const sp<OmxGraphicBufferSource> &source): mSource(source) {
     }
 
     Return<void> onOmxExecuting() override {
@@ -115,7 +115,7 @@
 
 // TWGraphicBufferSource
 TWGraphicBufferSource::TWGraphicBufferSource(
-        sp<GraphicBufferSource> const& base) :
+        sp<OmxGraphicBufferSource> const& base) :
     mBase(base),
     mOmxBufferSource(new TWOmxBufferSource(base)) {
 }
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 8539864..306a8eb 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -6,12 +6,11 @@
     },
 
     srcs: [
-        "FrameDropper.cpp",
-        "GraphicBufferSource.cpp",
         "BWGraphicBufferSource.cpp",
         "OMXMaster.cpp",
         "OMXNodeInstance.cpp",
         "OMXUtils.cpp",
+        "OmxGraphicBufferSource.cpp",
         "SimpleSoftOMXComponent.cpp",
         "SoftOMXComponent.cpp",
         "SoftOMXPlugin.cpp",
@@ -49,6 +48,7 @@
         "libgui",
         "libcutils",
         "libstagefright_foundation",
+        "libstagefright_gbs",
         "libstagefright_xmlparser",
         "libdl",
         "libhidlbase",
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.cpp b/media/libstagefright/omx/BWGraphicBufferSource.cpp
index 94ef598..fa30a46 100644
--- a/media/libstagefright/omx/BWGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/BWGraphicBufferSource.cpp
@@ -55,9 +55,9 @@
 };
 
 struct BWGraphicBufferSource::BWOMXBufferSource : public BnOMXBufferSource {
-    sp<GraphicBufferSource> mSource;
+    sp<OmxGraphicBufferSource> mSource;
 
-    BWOMXBufferSource(const sp<GraphicBufferSource> &source): mSource(source) {
+    BWOMXBufferSource(const sp<OmxGraphicBufferSource> &source): mSource(source) {
     }
 
     Status onOmxExecuting() override {
@@ -83,7 +83,7 @@
 };
 
 BWGraphicBufferSource::BWGraphicBufferSource(
-        sp<GraphicBufferSource> const& base) :
+        sp<OmxGraphicBufferSource> const& base) :
     mBase(base),
     mOMXBufferSource(new BWOMXBufferSource(base)) {
 }
diff --git a/media/libstagefright/omx/OmxGraphicBufferSource.cpp b/media/libstagefright/omx/OmxGraphicBufferSource.cpp
new file mode 100644
index 0000000..83feac8
--- /dev/null
+++ b/media/libstagefright/omx/OmxGraphicBufferSource.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <inttypes.h>
+
+#define LOG_TAG "OmxGraphicBufferSource"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <media/stagefright/gbs/ComponentWrapper.h>
+#include <media/stagefright/gbs/GraphicBufferSource.h>
+#include <media/stagefright/omx/OmxGraphicBufferSource.h>
+
+namespace android {
+
+namespace {
+
+class OmxComponentWrapper : public ComponentWrapper {
+public:
+    explicit OmxComponentWrapper(const sp<IOmxNodeWrapper> &node)
+        : mOmxNode(node) {}
+    virtual ~OmxComponentWrapper() = default;
+
+    status_t submitBuffer(
+            int32_t bufferId, const sp<GraphicBuffer> &buffer,
+            int64_t timestamp, int fenceFd) override {
+        return mOmxNode->emptyBuffer(
+                bufferId, OMX_BUFFERFLAG_ENDOFFRAME, buffer, timestamp, fenceFd);
+    }
+
+    status_t submitEos(int32_t bufferId) override {
+        return mOmxNode->emptyBuffer(bufferId, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS);
+    }
+
+    void dispatchDataSpaceChanged(
+            int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
+        mOmxNode->dispatchDataSpaceChanged(dataSpace, aspects, pixelFormat);
+    }
+
+private:
+    sp<IOmxNodeWrapper> mOmxNode;
+
+    DISALLOW_EVIL_CONSTRUCTORS(OmxComponentWrapper);
+};
+
+}  // namespace
+
+Status OmxGraphicBufferSource::onOmxExecuting() {
+    return start();
+}
+
+Status OmxGraphicBufferSource::onOmxIdle() {
+    return stop();
+}
+
+Status OmxGraphicBufferSource::onOmxLoaded(){
+    return release();
+}
+
+status_t OmxGraphicBufferSource::configure(
+        const sp<IOmxNodeWrapper>& omxNode,
+        int32_t dataSpace,
+        int32_t bufferCount,
+        uint32_t frameWidth,
+        uint32_t frameHeight,
+        uint32_t consumerUsage) {
+    if (omxNode == NULL) {
+        return BAD_VALUE;
+    }
+
+    return GraphicBufferSource::configure(
+            new OmxComponentWrapper(omxNode), dataSpace, bufferCount,
+            frameWidth, frameHeight, consumerUsage);
+}
+
+}  // namespace android
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferSource.h
index b9f22ab..4e56c98 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferSource.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferSource.h
@@ -28,7 +28,7 @@
 
 #include <android/BnGraphicBufferSource.h>
 
-#include <media/stagefright/omx/GraphicBufferSource.h>
+#include <media/stagefright/omx/OmxGraphicBufferSource.h>
 
 namespace android {
 namespace hardware {
@@ -37,7 +37,7 @@
 namespace V1_0 {
 namespace implementation {
 
-using ::android::GraphicBufferSource;
+using ::android::OmxGraphicBufferSource;
 using ::android::hardware::graphics::common::V1_0::Dataspace;
 using ::android::hardware::media::omx::V1_0::ColorAspects;
 using ::android::hardware::media::omx::V1_0::IGraphicBufferSource;
@@ -69,10 +69,10 @@
 struct TWGraphicBufferSource : public TGraphicBufferSource {
     struct TWOmxNodeWrapper;
     struct TWOmxBufferSource;
-    sp<GraphicBufferSource> mBase;
+    sp<OmxGraphicBufferSource> mBase;
     sp<IOmxBufferSource> mOmxBufferSource;
 
-    TWGraphicBufferSource(sp<GraphicBufferSource> const& base);
+    TWGraphicBufferSource(sp<OmxGraphicBufferSource> const& base);
     Return<Status> configure(
             const sp<IOmxNode>& omxNode, Dataspace dataspace) override;
     Return<Status> setSuspend(bool suspend, int64_t timeUs) override;
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h
index 0f78eb6..0efff22 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h
@@ -23,14 +23,14 @@
 #include <android/BnOMXBufferSource.h>
 #include <media/IOMX.h>
 
-#include "GraphicBufferSource.h"
+#include "OmxGraphicBufferSource.h"
 #include "IOmxNodeWrapper.h"
 
 namespace android {
 
 using ::android::binder::Status;
 using ::android::BnGraphicBufferSource;
-using ::android::GraphicBufferSource;
+using ::android::OmxGraphicBufferSource;
 using ::android::IOMXNode;
 using ::android::sp;
 
@@ -38,10 +38,10 @@
     struct BWOMXBufferSource;
     struct BWOmxNodeWrapper;
 
-    sp<GraphicBufferSource> mBase;
+    sp<OmxGraphicBufferSource> mBase;
     sp<IOMXBufferSource> mOMXBufferSource;
 
-    BWGraphicBufferSource(sp<GraphicBufferSource> const &base);
+    BWGraphicBufferSource(sp<OmxGraphicBufferSource> const &base);
 
     Status configure(
             const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OmxGraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/OmxGraphicBufferSource.h
new file mode 100644
index 0000000..4b0f3d2
--- /dev/null
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OmxGraphicBufferSource.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef OMX_GRAPHIC_BUFFER_SOURCE_H_
+
+#define OMX_GRAPHIC_BUFFER_SOURCE_H_
+
+#include <media/stagefright/gbs/GraphicBufferSource.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#include <android/BnGraphicBufferSource.h>
+#include <android/BnOMXBufferSource.h>
+
+#include "IOmxNodeWrapper.h"
+
+namespace android {
+
+using ::android::binder::Status;
+
+/*
+ * This class is used to feed OMX codecs from a Surface via BufferQueue or
+ * HW producer.
+ *
+ * See media/stagefright/gbs/GraphicBufferSource.h for documentation.
+ */
+class OmxGraphicBufferSource : public GraphicBufferSource {
+public:
+    OmxGraphicBufferSource() = default;
+    virtual ~OmxGraphicBufferSource() = default;
+
+    // OmxBufferSource interface
+    // ------------------------------
+
+    // This is called when OMX transitions to OMX_StateExecuting, which means
+    // we can start handing it buffers.  If we already have buffers of data
+    // sitting in the BufferQueue, this will send them to the codec.
+    Status onOmxExecuting();
+
+    // This is called when OMX transitions to OMX_StateIdle, indicating that
+    // the codec is meant to return all buffers back to the client for them
+    // to be freed. Do NOT submit any more buffers to the component.
+    Status onOmxIdle();
+
+    // This is called when OMX transitions to OMX_StateLoaded, indicating that
+    // we are shutting down.
+    Status onOmxLoaded();
+
+    // Rest of the interface in GraphicBufferSource.
+
+    // IGraphicBufferSource interface
+    // ------------------------------
+
+    // Configure the buffer source to be used with an OMX node with the default
+    // data space.
+    status_t configure(
+        const sp<IOmxNodeWrapper> &omxNode,
+        int32_t dataSpace,
+        int32_t bufferCount,
+        uint32_t frameWidth,
+        uint32_t frameHeight,
+        uint32_t consumerUsage);
+
+    // Rest of the interface in GraphicBufferSource.
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(OmxGraphicBufferSource);
+};
+
+}  // namespace android
+
+#endif  // OMX_GRAPHIC_BUFFER_SOURCE_H_
diff --git a/media/libstagefright/omx/tests/Android.bp b/media/libstagefright/omx/tests/Android.bp
index 999d9d4..3b521ab 100644
--- a/media/libstagefright/omx/tests/Android.bp
+++ b/media/libstagefright/omx/tests/Android.bp
@@ -34,21 +34,3 @@
 
     compile_multilib: "32",
 }
-
-cc_test {
-    name: "FrameDropper_test",
-
-    srcs: ["FrameDropper_test.cpp"],
-
-    shared_libs: [
-        "libstagefright_omx",
-        "libutils",
-    ],
-
-    include_dirs: ["frameworks/av/media/libstagefright/omx"],
-
-    cflags: [
-        "-Werror",
-        "-Wall",
-    ],
-}
diff --git a/media/libstagefright/rtsp/AMPEG2TSAssembler.h b/media/libstagefright/rtsp/AMPEG2TSAssembler.h
index f39c2b5..c987b5b 100644
--- a/media/libstagefright/rtsp/AMPEG2TSAssembler.h
+++ b/media/libstagefright/rtsp/AMPEG2TSAssembler.h
@@ -24,7 +24,6 @@
 
 struct AMessage;
 struct AString;
-class MetaData;
 
 struct AMPEG2TSAssembler : public ARTPAssembler {
     AMPEG2TSAssembler(
diff --git a/media/libstagefright/rtsp/rtp_test.cpp b/media/libstagefright/rtsp/rtp_test.cpp
index 98a8fb4..4590699 100644
--- a/media/libstagefright/rtsp/rtp_test.cpp
+++ b/media/libstagefright/rtsp/rtp_test.cpp
@@ -25,7 +25,6 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MetaData.h>
 #include <media/stagefright/SimpleDecodingSource.h>
 
 #include "ARTPSession.h"
diff --git a/media/libstagefright/tests/Android.bp b/media/libstagefright/tests/Android.bp
index 35119c2..e67a949 100644
--- a/media/libstagefright/tests/Android.bp
+++ b/media/libstagefright/tests/Android.bp
@@ -15,6 +15,7 @@
         "libcutils",
         "libgui",
         "libmedia",
+        "libmediaextractor",
         "libstagefright",
         "libstagefright_foundation",
         "libstagefright_omx",
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index a9025c0..b86876b 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -25,7 +25,6 @@
 #include <utils/StrongPointer.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MetaData.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_util_Binder.h>
 
diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp
index 80a4391..dffc4d7 100644
--- a/media/ndk/NdkMediaMuxer.cpp
+++ b/media/ndk/NdkMediaMuxer.cpp
@@ -27,7 +27,6 @@
 #include <utils/StrongPointer.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MetaData.h>
 #include <media/stagefright/MediaMuxer.h>
 #include <media/IMediaHTTPService.h>
 #include <android_runtime/AndroidRuntime.h>
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index f2bc6d0..d6dae5b 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_shared {
+cc_library {
     name: "libmediautils",
 
     srcs: [
diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk
index bbd2afa..61d73a0 100644
--- a/packages/MediaComponents/Android.mk
+++ b/packages/MediaComponents/Android.mk
@@ -34,22 +34,19 @@
 
 LOCAL_JAVA_LIBRARIES += android-support-annotations
 
-# Embed native libraries in package, rather than installing to /system/lib*.
-# TODO: Find a right way to include libs in the apk. b/72066556
-LOCAL_MODULE_TAGS := samples
-
 # To embed native libraries in package, uncomment the lines below.
-LOCAL_JNI_SHARED_LIBRARIES := \
-    libaacextractor \
-    libamrextractor \
-    libflacextractor \
-    libmidiextractor \
-    libmkvextractor \
-    libmp3extractor \
-    libmp4extractor \
-    libmpeg2extractor \
-    liboggextractor \
-    libwavextractor \
+#LOCAL_MODULE_TAGS := samples
+#LOCAL_JNI_SHARED_LIBRARIES := \
+#    libaacextractor \
+#    libamrextractor \
+#    libflacextractor \
+#    libmidiextractor \
+#    libmkvextractor \
+#    libmp3extractor \
+#    libmp4extractor \
+#    libmpeg2extractor \
+#    liboggextractor \
+#    libwavextractor \
 
 # TODO: Remove dependency with other support libraries.
 LOCAL_STATIC_ANDROID_LIBRARIES += \
@@ -59,3 +56,5 @@
 LOCAL_USE_AAPT2 := true
 
 include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_media_cc_disabled.png b/packages/MediaComponents/res/drawable-hdpi/ic_media_subtitle_disabled.png
similarity index 100%
rename from packages/MediaComponents/res/drawable-hdpi/ic_media_cc_disabled.png
rename to packages/MediaComponents/res/drawable-hdpi/ic_media_subtitle_disabled.png
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_media_cc_enabled.png b/packages/MediaComponents/res/drawable-hdpi/ic_media_subtitle_enabled.png
similarity index 100%
rename from packages/MediaComponents/res/drawable-hdpi/ic_media_cc_enabled.png
rename to packages/MediaComponents/res/drawable-hdpi/ic_media_subtitle_enabled.png
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_media_cc_disabled.png b/packages/MediaComponents/res/drawable-mdpi/ic_media_subtitle_disabled.png
similarity index 100%
rename from packages/MediaComponents/res/drawable-mdpi/ic_media_cc_disabled.png
rename to packages/MediaComponents/res/drawable-mdpi/ic_media_subtitle_disabled.png
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_media_cc_enabled.png b/packages/MediaComponents/res/drawable-mdpi/ic_media_subtitle_enabled.png
similarity index 100%
rename from packages/MediaComponents/res/drawable-mdpi/ic_media_cc_enabled.png
rename to packages/MediaComponents/res/drawable-mdpi/ic_media_subtitle_enabled.png
Binary files differ
diff --git a/packages/MediaComponents/res/drawable/ic_aspect_ratio.xml b/packages/MediaComponents/res/drawable/ic_aspect_ratio.xml
new file mode 100644
index 0000000..c6228e6
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_aspect_ratio.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M19,12h-2v3h-3v2h5v-5zM7,9h3L10,7L5,7v5h2L7,9zM21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.99h18v14.02z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_cast.xml b/packages/MediaComponents/res/drawable/ic_cast.xml
deleted file mode 100644
index ac22a4b..0000000
--- a/packages/MediaComponents/res/drawable/ic_cast.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v3h2L3,5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM1,18v3h3c0,-1.66 -1.34,-3 -3,-3zM1,14v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zM1,10v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.93,-11 -11,-11z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_mute.xml b/packages/MediaComponents/res/drawable/ic_mute.xml
new file mode 100644
index 0000000..560aaec
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_mute.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_replay.xml b/packages/MediaComponents/res/drawable/ic_replay.xml
new file mode 100644
index 0000000..2bde120
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_replay.xml
@@ -0,0 +1,4 @@
+<vector android:height="40dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="40dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M24,10V2L14,12l10,10v-8c6.63,0 12,5.37 12,12s-5.37,12 -12,12 -12,-5.37 -12,-12H8c0,8.84 7.16,16 16,16s16,-7.16 16,-16 -7.16,-16 -16,-16z"/>
+</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_settings.xml b/packages/MediaComponents/res/drawable/ic_settings.xml
new file mode 100644
index 0000000..a59ecc1
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_settings.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_unmute.xml b/packages/MediaComponents/res/drawable/ic_unmute.xml
new file mode 100644
index 0000000..9dfb2b9
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_unmute.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index ff4f12a..4d05546 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -46,14 +46,12 @@
             android:paddingStart="4dp"
             android:paddingEnd="4dp"
             android:textSize="20sp"
-            android:text="North by Northwest"
             android:textColor="#FFFFFFFF" />
 
-        <ImageButton
-            android:id="@+id/cast"
+        <view class="com.android.support.mediarouter.app.MediaRouteButton" android:id="@+id/cast"
             android:layout_alignParentEnd="true"
             android:layout_centerVertical="true"
-            style="@style/TitleBarButton.MediaRouteButton"/>
+            style="@style/TitleBarButton" />
 
     </RelativeLayout>
 
@@ -85,7 +83,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingLeft="15dp"
-        android:paddingRight="15dp"
         android:orientation="horizontal">
 
         <TextView
@@ -112,25 +109,50 @@
             android:textStyle="bold"
             android:textColor="#BBBBBB" />
 
-        <ImageButton
-            android:id="@+id/overflow"
-            android:layout_alignParentEnd="true"
+        <LinearLayout
+            android:id="@+id/basic_controls"
+            android:layout_alignParentRight="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
             android:layout_centerVertical="true"
-            style="@style/BottomBarButton.Overflow"/>
+            android:orientation="horizontal" >
 
-        <ImageButton
-            android:id="@+id/fullscreen"
-            android:layout_toLeftOf="@id/overflow"
-            android:layout_centerVertical="true"
-            style="@style/BottomBarButton.FullScreen"/>
+            <ImageButton
+                android:id="@+id/subtitle"
+                android:scaleType="fitCenter"
+                style="@style/BottomBarButton.CC" />
+            <ImageButton
+                android:id="@+id/fullscreen"
+                style="@style/BottomBarButton.FullScreen"/>
+            <ImageButton
+                android:id="@+id/overflow_right"
+                style="@style/BottomBarButton.OverflowRight"/>
+        </LinearLayout>
 
-        <ImageButton
-            android:id="@+id/cc"
-            android:scaleType="fitCenter"
-            android:layout_toLeftOf="@id/fullscreen"
+        <LinearLayout
+            android:id="@+id/extra_controls"
+            android:layout_alignParentRight="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
             android:layout_centerVertical="true"
-            style="@style/BottomBarButton.CC" />
+            android:visibility="gone"
+            android:orientation="horizontal" >
+
+            <ImageButton
+                android:id="@+id/mute"
+                style="@style/BottomBarButton.Mute" />
+            <ImageButton
+                android:id="@+id/aspect_ratio"
+                style="@style/BottomBarButton.AspectRatio" />
+            <ImageButton
+                android:id="@+id/settings"
+                style="@style/BottomBarButton.Settings" />
+            <ImageButton
+                android:id="@+id/overflow_left"
+                style="@style/BottomBarButton.OverflowLeft"/>
+        </LinearLayout>
 
     </RelativeLayout>
 
 </LinearLayout>
+
diff --git a/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml b/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml
index b409e6b..3751002 100644
--- a/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml
+++ b/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml
@@ -15,87 +15,192 @@
 -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-          android:id="@+id/mr_expandable_area"
-          android:layout_width="fill_parent"
-          android:layout_height="fill_parent">
+    android:id="@+id/mr_expandable_area"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
     <LinearLayout android:id="@+id/mr_dialog_area"
-                  android:layout_width="fill_parent"
-                  android:layout_height="wrap_content"
-                  android:layout_gravity="center"
-                  android:orientation="vertical"
-                  android:background="?attr/colorBackgroundFloating">
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:orientation="vertical"
+        android:background="?attr/colorBackgroundFloating">
         <LinearLayout android:id="@+id/mr_title_bar"
-                      android:layout_width="fill_parent"
-                      android:layout_height="wrap_content"
-                      android:paddingLeft="24dp"
-                      android:paddingRight="12dp"
-                      android:orientation="horizontal" >
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:paddingLeft="24dp"
+            android:paddingRight="12dp"
+            android:orientation="horizontal" >
             <TextView android:id="@+id/mr_name"
-                      android:layout_width="0dp"
-                      android:layout_height="72dp"
-                      android:layout_weight="1"
-                      android:gravity="center_vertical"
-                      android:singleLine="true"
-                      android:ellipsize="end"
-                      android:textAppearance="@style/TextAppearance.MediaRouter.Title" />
+                android:layout_width="0dp"
+                android:layout_height="72dp"
+                android:layout_weight="1"
+                android:gravity="center_vertical"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:textAppearance="@style/TextAppearance.MediaRouter.Title" />
             <ImageButton android:id="@+id/mr_close"
-                         android:layout_width="48dp"
-                         android:layout_height="48dp"
-                         android:layout_gravity="center_vertical"
-                         android:contentDescription="@string/mr_controller_close_description"
-                         android:src="?attr/mediaRouteCloseDrawable"
-                         android:background="?attr/selectableItemBackgroundBorderless" />
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:layout_gravity="center_vertical"
+                android:contentDescription="@string/mr_controller_close_description"
+                android:src="?attr/mediaRouteCloseDrawable"
+                android:background="?attr/selectableItemBackgroundBorderless" />
         </LinearLayout>
         <FrameLayout android:id="@+id/mr_custom_control"
-                     android:layout_width="fill_parent"
-                     android:layout_height="wrap_content"
-                     android:visibility="gone" />
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone" />
         <FrameLayout android:id="@+id/mr_default_control"
-                     android:layout_width="fill_parent"
-                     android:layout_height="wrap_content">
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content">
             <ImageView android:id="@+id/mr_art"
-                       android:layout_width="fill_parent"
-                       android:layout_height="wrap_content"
-                       android:adjustViewBounds="true"
-                       android:scaleType="fitXY"
-                       android:background="?attr/colorPrimary"
-                       android:layout_gravity="top"
-                       android:contentDescription="@string/mr_controller_album_art"
-                       android:visibility="gone" />
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:adjustViewBounds="true"
+                android:scaleType="fitXY"
+                android:background="?attr/colorPrimary"
+                android:layout_gravity="top"
+                android:contentDescription="@string/mr_controller_album_art"
+                android:visibility="gone" />
             <LinearLayout android:layout_width="fill_parent"
-                          android:layout_height="wrap_content"
-                          android:orientation="vertical"
-                          android:layout_gravity="bottom"
-                          android:splitMotionEvents="false">
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:layout_gravity="bottom"
+                android:splitMotionEvents="false">
                 <LinearLayout android:id="@+id/mr_media_main_control"
-                              android:layout_width="fill_parent"
-                              android:layout_height="wrap_content"
-                              android:orientation="vertical"
-                              android:paddingTop="16dp"
-                              android:paddingBottom="16dp"
-                              android:layout_gravity="bottom"
-                              android:theme="?attr/mediaRouteControlPanelThemeOverlay">
-                    <include android:id="@+id/mr_playback_control"
-                             layout="@layout/mr_playback_control" />
-                    <View android:id="@+id/mr_control_divider"
-                          android:layout_width="fill_parent"
-                          android:layout_height="8dp"
-                          android:visibility="gone" />
-                    <include android:id="@+id/mr_volume_control"
-                             layout="@layout/mr_volume_control" />
-                </LinearLayout>
-                <android.support.v7.app.OverlayListView
-                        android:id="@+id/mr_volume_group_list"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="vertical"
+                    android:paddingTop="16dp"
+                    android:paddingBottom="16dp"
+                    android:layout_gravity="bottom"
+                    android:theme="?attr/mediaRouteControlPanelThemeOverlay">
+                    <RelativeLayout
+                        android:id="@+id/mr_playback_control"
                         android:layout_width="fill_parent"
                         android:layout_height="wrap_content"
-                        android:paddingTop="@dimen/mr_controller_volume_group_list_padding_top"
-                        android:scrollbarStyle="outsideOverlay"
-                        android:clipToPadding="false"
-                        android:visibility="gone"
-                        android:splitMotionEvents="false"
-                        android:theme="?attr/mediaRouteControlPanelThemeOverlay" />
+                        android:orientation="horizontal"
+                        android:paddingLeft="24dp"
+                        android:paddingRight="12dp" >
+                        <ImageButton android:id="@+id/mr_control_playback_ctrl"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_marginLeft="12dp"
+                            android:layout_alignParentRight="true"
+                            android:layout_centerVertical="true"
+                            android:contentDescription="@string/mr_controller_play"
+                            android:background="?attr/selectableItemBackgroundBorderless"
+                            android:visibility="gone" />
+                        <LinearLayout android:id="@+id/mr_control_title_container"
+                            android:orientation="vertical"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_toLeftOf="@id/mr_control_playback_ctrl"
+                            android:layout_alignParentLeft="true"
+                            android:layout_centerVertical="true">
+                            <TextView android:id="@+id/mr_control_title"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:textAppearance="@style/TextAppearance.MediaRouter.PrimaryText"
+                                android:singleLine="true" />
+                            <TextView android:id="@+id/mr_control_subtitle"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText"
+                                android:singleLine="true" />
+                        </LinearLayout>
+                    </RelativeLayout>
+                    <View android:id="@+id/mr_control_divider"
+                        android:layout_width="fill_parent"
+                        android:layout_height="8dp"
+                        android:visibility="gone" />
+                    <LinearLayout
+                        android:id="@+id/mr_volume_control"
+                        android:layout_width="fill_parent"
+                        android:layout_height="wrap_content"
+                        android:minHeight="48dp"
+                        android:gravity="center_vertical"
+                        android:paddingLeft="24dp"
+                        android:paddingRight="12dp"
+                        android:splitMotionEvents="false">
+                        <ImageView
+                            android:layout_width="24dp"
+                            android:layout_height="24dp"
+                            android:src="?attr/mediaRouteAudioTrackDrawable"
+                            android:gravity="center"
+                            android:scaleType="center"/>
+                        <!-- Since dialog's top layout mr_expandable_area is clickable, it propagates pressed state
+                             to its non-clickable children. Specify android:clickable="true" to prevent volume slider
+                             from having false pressed state. -->
+                        <com.android.support.mediarouter.app.MediaRouteVolumeSlider
+                            android:id="@+id/mr_volume_slider"
+                            android:layout_width="0dp"
+                            android:layout_height="wrap_content"
+                            android:minHeight="48dp"
+                            android:maxHeight="48dp"
+                            android:layout_weight="1"
+                            android:clickable="true"
+                            android:contentDescription="@string/mr_controller_volume_slider" />
+                        <com.android.support.mediarouter.app.MediaRouteExpandCollapseButton
+                            android:id="@+id/mr_group_expand_collapse"
+                            android:layout_width="48dp"
+                            android:layout_height="48dp"
+                            android:padding="12dp"
+                            android:background="?attr/selectableItemBackgroundBorderless"
+                            android:visibility="gone"/>
+                    </LinearLayout>
+                </LinearLayout>
+                <com.android.support.mediarouter.app.OverlayListView
+                    android:id="@+id/mr_volume_group_list"
+                    android:layout_width="fill_parent"
+                    android:layout_height="wrap_content"
+                    android:paddingTop="@dimen/mr_controller_volume_group_list_padding_top"
+                    android:scrollbarStyle="outsideOverlay"
+                    android:clipToPadding="false"
+                    android:visibility="gone"
+                    android:splitMotionEvents="false"
+                    android:theme="?attr/mediaRouteControlPanelThemeOverlay" />
             </LinearLayout>
         </FrameLayout>
-        <include layout="@layout/abc_alert_dialog_button_bar_material" />
+        <ScrollView
+            android:id="@+id/buttonPanel"
+            style="?attr/buttonBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:fillViewport="true"
+            android:scrollIndicators="top|bottom">
+            <android.support.v7.widget.ButtonBarLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="bottom"
+                android:layoutDirection="locale"
+                android:orientation="horizontal"
+                android:paddingBottom="4dp"
+                android:paddingLeft="12dp"
+                android:paddingRight="12dp"
+                android:paddingTop="4dp">
+                <Button
+                    android:id="@android:id/button3"
+                    style="?attr/buttonBarNeutralButtonStyle"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"/>
+                <android.support.v4.widget.Space
+                    android:id="@+id/spacer"
+                    android:layout_width="0dp"
+                    android:layout_height="0dp"
+                    android:layout_weight="1"
+                    android:visibility="invisible"/>
+                <Button
+                    android:id="@android:id/button2"
+                    style="?android:attr/buttonBarNegativeButtonStyle"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"/>
+                <Button
+                    android:id="@android:id/button1"
+                    style="?attr/buttonBarPositiveButtonStyle"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"/>
+            </android.support.v7.widget.ButtonBarLayout>
+        </ScrollView>
     </LinearLayout>
 </FrameLayout>
diff --git a/packages/MediaComponents/res/layout/mr_playback_control.xml b/packages/MediaComponents/res/layout/mr_playback_control.xml
deleted file mode 100644
index 870dd50..0000000
--- a/packages/MediaComponents/res/layout/mr_playback_control.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2018 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.
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                android:layout_width="fill_parent"
-                android:layout_height="wrap_content"
-                android:orientation="horizontal"
-                android:paddingLeft="24dp"
-                android:paddingRight="12dp" >
-    <ImageButton android:id="@+id/mr_control_playback_ctrl"
-                 android:layout_width="wrap_content"
-                 android:layout_height="wrap_content"
-                 android:layout_marginLeft="12dp"
-                 android:layout_alignParentRight="true"
-                 android:layout_centerVertical="true"
-                 android:contentDescription="@string/mr_controller_play"
-                 android:background="?attr/selectableItemBackgroundBorderless"
-                 android:visibility="gone" />
-    <LinearLayout android:id="@+id/mr_control_title_container"
-                  android:orientation="vertical"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:layout_toLeftOf="@id/mr_control_playback_ctrl"
-                  android:layout_alignParentLeft="true"
-                  android:layout_centerVertical="true">
-        <TextView android:id="@+id/mr_control_title"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:textAppearance="@style/TextAppearance.MediaRouter.PrimaryText"
-                  android:singleLine="true" />
-        <TextView android:id="@+id/mr_control_subtitle"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText"
-                  android:singleLine="true" />
-    </LinearLayout>
-</RelativeLayout>
diff --git a/packages/MediaComponents/res/layout/mr_volume_control.xml b/packages/MediaComponents/res/layout/mr_volume_control.xml
deleted file mode 100644
index 5212532..0000000
--- a/packages/MediaComponents/res/layout/mr_volume_control.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2018 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:minHeight="48dp"
-        android:gravity="center_vertical"
-        android:paddingLeft="24dp"
-        android:paddingRight="12dp"
-        android:splitMotionEvents="false">
-    <ImageView
-            android:layout_width="24dp"
-            android:layout_height="24dp"
-            android:src="?attr/mediaRouteAudioTrackDrawable"
-            android:gravity="center"
-            android:scaleType="center"/>
-    <!-- Since dialog's top layout mr_expandable_area is clickable, it propagates pressed state
-         to its non-clickable children. Specify android:clickable="true" to prevent volume slider
-         from having false pressed state. -->
-    <android.support.v7.app.MediaRouteVolumeSlider
-            android:id="@+id/mr_volume_slider"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:minHeight="48dp"
-            android:maxHeight="48dp"
-            android:layout_weight="1"
-            android:clickable="true"
-            android:contentDescription="@string/mr_controller_volume_slider" />
-    <android.support.v7.app.MediaRouteExpandCollapseButton
-            android:id="@+id/mr_group_expand_collapse"
-            android:layout_width="48dp"
-            android:layout_height="48dp"
-            android:padding="12dp"
-            android:background="?attr/selectableItemBackgroundBorderless"
-            android:visibility="gone"/>
-</LinearLayout>
diff --git a/packages/MediaComponents/res/values/colors.xml b/packages/MediaComponents/res/values/colors.xml
new file mode 100644
index 0000000..9e071d7
--- /dev/null
+++ b/packages/MediaComponents/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 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.
+-->
+
+<resources>
+    <integer name="gray">0xff444444</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/values/strings.xml b/packages/MediaComponents/res/values/strings.xml
index e93269c..35db0e5 100644
--- a/packages/MediaComponents/res/values/strings.xml
+++ b/packages/MediaComponents/res/values/strings.xml
@@ -84,6 +84,7 @@
 
     <string name="lockscreen_pause_button_content_description">Pause</string>
     <string name="lockscreen_play_button_content_description">Play</string>
+    <string name="lockscreen_replay_button_content_description">Replay</string>
 
     <!-- Text for error alert when a video container is not valid for progressive download/playback. -->
     <string name="VideoView2_error_text_invalid_progressive_playback">This video isn\'t valid for streaming to this device.</string>
diff --git a/packages/MediaComponents/res/values/style.xml b/packages/MediaComponents/res/values/style.xml
index d31b41d..c59380c 100644
--- a/packages/MediaComponents/res/values/style.xml
+++ b/packages/MediaComponents/res/values/style.xml
@@ -34,29 +34,42 @@
         <item name="android:layout_margin">10dp</item>
     </style>
 
-    <style name="TitleBarButton.MediaRouteButton">
-        <item name="android:src">@drawable/ic_cast</item>
-    </style>
-
-
     <style name="BottomBarButton">
         <item name="android:background">@null</item>
-        <item name="android:layout_width">24dp</item>
-        <item name="android:layout_height">24dp</item>
-        <item name="android:layout_margin">10dp</item>
+        <item name="android:layout_width">44dp</item>
+        <item name="android:layout_height">34dp</item>
+        <item name="android:layout_marginTop">5dp</item>
+        <item name="android:layout_marginBottom">5dp</item>
+        <item name="android:paddingLeft">5dp</item>
+        <item name="android:paddingRight">5dp</item>
     </style>
 
     <style name="BottomBarButton.CC">
-        <item name="android:src">@drawable/ic_media_cc_disabled</item>
+        <item name="android:src">@drawable/ic_media_subtitle_disabled</item>
     </style>
 
     <style name="BottomBarButton.FullScreen">
         <item name="android:src">@drawable/ic_fullscreen</item>
     </style>
 
-    <style name="BottomBarButton.Overflow">
+    <style name="BottomBarButton.OverflowRight">
         <item name="android:src">@drawable/ic_chevron_right</item>
     </style>
 
-</resources>
+    <style name="BottomBarButton.OverflowLeft">
+        <item name="android:src">@drawable/ic_chevron_left</item>
+    </style>
 
+    <style name="BottomBarButton.Settings">
+        <item name="android:src">@drawable/ic_settings</item>
+    </style>
+
+    <style name="BottomBarButton.AspectRatio">
+        <item name="android:src">@drawable/ic_aspect_ratio</item>
+    </style>
+
+    <style name="BottomBarButton.Mute">
+        <item name="android:src">@drawable/ic_mute</item>
+    </style>
+
+</resources>
diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
new file mode 100644
index 0000000..59d8366
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.content.Context;
+import android.media.IMediaSession2;
+import android.media.MediaBrowser2;
+import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaSession2.CommandButton;
+import android.media.SessionToken;
+import android.media.update.MediaBrowser2Provider;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class MediaBrowser2Impl extends MediaController2Impl implements MediaBrowser2Provider {
+    private final String TAG = "MediaBrowser2";
+    private final boolean DEBUG = true; // TODO(jaewan): change.
+
+    private final MediaBrowser2 mInstance;
+    private final MediaBrowser2.BrowserCallback mCallback;
+
+    public MediaBrowser2Impl(MediaBrowser2 instance, Context context, SessionToken token,
+            BrowserCallback callback, Executor executor) {
+        super(instance, context, token, callback, executor);
+        mInstance = instance;
+        mCallback = callback;
+    }
+
+    @Override
+    public void getBrowserRoot_impl(Bundle rootHints) {
+        final IMediaSession2 binder = getSessionBinder();
+        if (binder != null) {
+            try {
+                binder.getBrowserRoot(getControllerStub(), rootHints);
+            } catch (RemoteException e) {
+                // TODO(jaewan): Handle disconnect.
+                if (DEBUG) {
+                    Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+                }
+            }
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
+        }
+    }
+
+    @Override
+    public void subscribe_impl(String parentId, Bundle options) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void unsubscribe_impl(String parentId, Bundle options) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void getItem_impl(String mediaId) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void getChildren_impl(String parentId, int page, int pageSize, Bundle options) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void search_impl(String query, int page, int pageSize, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    public void onGetRootResult(
+            final Bundle rootHints, final String rootMediaId, final Bundle rootExtra) {
+        getCallbackExecutor().execute(() -> {
+            mCallback.onGetRootResult(rootHints, rootMediaId, rootExtra);
+        });
+    }
+
+    public void onCustomLayoutChanged(final List<CommandButton> layout) {
+        getCallbackExecutor().execute(() -> {
+            mCallback.onCustomLayoutChanged(layout);
+        });
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
new file mode 100644
index 0000000..ac3f311
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -0,0 +1,603 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.media.IMediaSession2;
+import android.media.IMediaSession2Callback;
+import android.media.MediaController2.PlaybackInfo;
+import android.media.MediaItem2;
+import android.media.MediaSession2;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaController2;
+import android.media.MediaController2.ControllerCallback;
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2.PlaylistParam;
+import android.media.MediaSessionService2;
+import android.media.PlaybackState2;
+import android.media.Rating2;
+import android.media.SessionToken;
+import android.media.session.PlaybackState;
+import android.media.update.MediaController2Provider;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.support.annotation.GuardedBy;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class MediaController2Impl implements MediaController2Provider {
+    private static final String TAG = "MediaController2";
+    private static final boolean DEBUG = true; // TODO(jaewan): Change
+
+    private final MediaController2 mInstance;
+
+    /**
+     * Flag used by MediaController2Record to filter playback callback.
+     */
+    static final int CALLBACK_FLAG_PLAYBACK = 0x1;
+
+    static final int REQUEST_CODE_ALL = 0;
+
+    private final Object mLock = new Object();
+
+    private final Context mContext;
+    private final MediaSession2CallbackStub mSessionCallbackStub;
+    private final SessionToken mToken;
+    private final ControllerCallback mCallback;
+    private final Executor mCallbackExecutor;
+    private final IBinder.DeathRecipient mDeathRecipient;
+
+    @GuardedBy("mLock")
+    private final List<PlaybackListenerHolder> mPlaybackListeners = new ArrayList<>();
+    @GuardedBy("mLock")
+    private SessionServiceConnection mServiceConnection;
+    @GuardedBy("mLock")
+    private boolean mIsReleased;
+
+    // Assignment should be used with the lock hold, but should be used without a lock to prevent
+    // potential deadlock.
+    // Postfix -Binder is added to explicitly show that it's potentially remote process call.
+    // Technically -Interface is more correct, but it may misread that it's interface (vs class)
+    // so let's keep this postfix until we find better postfix.
+    @GuardedBy("mLock")
+    private volatile IMediaSession2 mSessionBinder;
+
+    // TODO(jaewan): Require session activeness changed listener, because controller can be
+    //               available when the session's player is null.
+    public MediaController2Impl(MediaController2 instance, Context context, SessionToken token,
+            ControllerCallback callback, Executor executor) {
+        mInstance = instance;
+
+        if (context == null) {
+            throw new IllegalArgumentException("context shouldn't be null");
+        }
+        if (token == null) {
+            throw new IllegalArgumentException("token shouldn't be null");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("callback shouldn't be null");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("executor shouldn't be null");
+        }
+        mContext = context;
+        mSessionCallbackStub = new MediaSession2CallbackStub(this);
+        mToken = token;
+        mCallback = callback;
+        mCallbackExecutor = executor;
+        mDeathRecipient = () -> {
+            mInstance.close();
+        };
+
+        mSessionBinder = null;
+
+        if (token.getSessionBinder() == null) {
+            mServiceConnection = new SessionServiceConnection();
+            connectToService();
+        } else {
+            mServiceConnection = null;
+            connectToSession(token.getSessionBinder());
+        }
+    }
+
+    // Should be only called by constructor.
+    private void connectToService() {
+        // Service. Needs to get fresh binder whenever connection is needed.
+        final Intent intent = new Intent(MediaSessionService2.SERVICE_INTERFACE);
+        intent.setClassName(mToken.getPackageName(), mToken.getServiceName());
+
+        // Use bindService() instead of startForegroundService() to start session service for three
+        // reasons.
+        // 1. Prevent session service owner's stopSelf() from destroying service.
+        //    With the startForegroundService(), service's call of stopSelf() will trigger immediate
+        //    onDestroy() calls on the main thread even when onConnect() is running in another
+        //    thread.
+        // 2. Minimize APIs for developers to take care about.
+        //    With bindService(), developers only need to take care about Service.onBind()
+        //    but Service.onStartCommand() should be also taken care about with the
+        //    startForegroundService().
+        // 3. Future support for UI-less playback
+        //    If a service wants to keep running, it should be either foreground service or
+        //    bounded service. But there had been request for the feature for system apps
+        //    and using bindService() will be better fit with it.
+        // TODO(jaewan): Use bindServiceAsUser()??
+        boolean result = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+        if (!result) {
+            Log.w(TAG, "bind to " + mToken + " failed");
+        } else if (DEBUG) {
+            Log.d(TAG, "bind to " + mToken + " success");
+        }
+    }
+
+    private void connectToSession(IMediaSession2 sessionBinder) {
+        try {
+            sessionBinder.connect(mContext.getPackageName(), mSessionCallbackStub);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to call connection request. Framework will retry"
+                    + " automatically");
+        }
+    }
+
+    @Override
+    public void close_impl() {
+        if (DEBUG) {
+            Log.d(TAG, "relese from " + mToken);
+        }
+        final IMediaSession2 binder;
+        synchronized (mLock) {
+            if (mIsReleased) {
+                // Prevent re-enterance from the ControllerCallback.onDisconnected()
+                return;
+            }
+            mIsReleased = true;
+            if (mServiceConnection != null) {
+                mContext.unbindService(mServiceConnection);
+                mServiceConnection = null;
+            }
+            mPlaybackListeners.clear();
+            binder = mSessionBinder;
+            mSessionBinder = null;
+            mSessionCallbackStub.destroy();
+        }
+        if (binder != null) {
+            try {
+                binder.asBinder().unlinkToDeath(mDeathRecipient, 0);
+                binder.release(mSessionCallbackStub);
+            } catch (RemoteException e) {
+                // No-op.
+            }
+        }
+        mCallbackExecutor.execute(() -> {
+            mCallback.onDisconnected();
+        });
+    }
+
+    IMediaSession2 getSessionBinder() {
+        return mSessionBinder;
+    }
+
+    MediaSession2CallbackStub getControllerStub() {
+        return mSessionCallbackStub;
+    }
+
+    Executor getCallbackExecutor() {
+        return mCallbackExecutor;
+    }
+
+    @Override
+    public SessionToken getSessionToken_impl() {
+        return mToken;
+    }
+
+    @Override
+    public boolean isConnected_impl() {
+        final IMediaSession2 binder = mSessionBinder;
+        return binder != null;
+    }
+
+    @Override
+    public void play_impl() {
+        sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_START);
+    }
+
+    @Override
+    public void pause_impl() {
+        sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE);
+    }
+
+    @Override
+    public void stop_impl() {
+        sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_STOP);
+    }
+
+    @Override
+    public void skipToPrevious_impl() {
+        sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM);
+    }
+
+    @Override
+    public void skipToNext_impl() {
+        sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM);
+    }
+
+    private void sendCommand(int code) {
+        // TODO(jaewan): optimization) Cache Command objects?
+        Command command = new Command(code);
+        // TODO(jaewan): Check if the command is in the allowed group.
+
+        final IMediaSession2 binder = mSessionBinder;
+        if (binder != null) {
+            try {
+                binder.sendCommand(mSessionCallbackStub, command.toBundle(), null);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+            }
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
+        }
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////
+    // TODO(jaewan): Implement follows
+    //////////////////////////////////////////////////////////////////////////////////////
+    @Override
+    public PendingIntent getSessionActivity_impl() {
+        // TODO(jaewan): Implement
+        return null;
+    }
+
+    @Override
+    public int getRatingType_impl() {
+        // TODO(jaewan): Implement
+        return 0;
+    }
+
+    @Override
+    public void setVolumeTo_impl(int value, int flags) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void adjustVolume_impl(int direction, int flags) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public PlaybackInfo getPlaybackInfo_impl() {
+        // TODO(jaewan): Implement
+        return null;
+    }
+
+    @Override
+    public void prepareFromUri_impl(Uri uri, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void prepareFromSearch_impl(String query, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void prepareMediaId_impl(String mediaId, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void playFromSearch_impl(String query, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void playFromUri_impl(String uri, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void playFromMediaId_impl(String mediaId, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void setRating_impl(Rating2 rating) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void sendCustomCommand_impl(Command command, Bundle args, ResultReceiver cb) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public List<MediaItem2> getPlaylist_impl() {
+        // TODO(jaewan): Implement
+        return null;
+    }
+
+    @Override
+    public void prepare_impl() {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void fastForward_impl() {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void rewind_impl() {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void seekTo_impl(long pos) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void setCurrentPlaylistItem_impl(int index) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public PlaybackState2 getPlaybackState_impl() {
+        // TODO(jaewan): Implement
+        return null;
+    }
+
+    @Override
+    public void removePlaylistItem_impl(MediaItem2 index) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void addPlaylistItem_impl(int index, MediaItem2 item) {
+    // TODO(jaewan): Implement
+    }
+
+    @Override
+    public PlaylistParam getPlaylistParam_impl() {
+        // TODO(jaewan): Implement
+        return null;
+    }
+
+    ///////////////////////////////////////////////////
+    // Protected or private methods
+    ///////////////////////////////////////////////////
+    // Should be used without a lock to prevent potential deadlock.
+    private void registerCallbackForPlaybackNotLocked() {
+        final IMediaSession2 binder = mSessionBinder;
+        if (binder != null) {
+            try {
+                binder.registerCallback(mSessionCallbackStub,
+                        CALLBACK_FLAG_PLAYBACK, REQUEST_CODE_ALL);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Cannot connect to the service or the session is gone", e);
+            }
+        }
+    }
+
+    private void pushPlaybackStateChanges(final PlaybackState state) {
+        synchronized (mLock) {
+            for (int i = 0; i < mPlaybackListeners.size(); i++) {
+                mPlaybackListeners.get(i).postPlaybackChange(state);
+            }
+        }
+    }
+
+    // Called when the result for connecting to the session was delivered.
+    // Should be used without a lock to prevent potential deadlock.
+    private void onConnectionChangedNotLocked(IMediaSession2 sessionBinder,
+            CommandGroup commandGroup) {
+        if (DEBUG) {
+            Log.d(TAG, "onConnectionChangedNotLocked sessionBinder=" + sessionBinder
+                    + ", commands=" + commandGroup);
+        }
+        boolean release = false;
+        try {
+            if (sessionBinder == null || commandGroup == null) {
+                // Connection rejected.
+                release = true;
+                return;
+            }
+            boolean registerCallbackForPlaybackNeeded;
+            synchronized (mLock) {
+                if (mIsReleased) {
+                    return;
+                }
+                if (mSessionBinder != null) {
+                    Log.e(TAG, "Cannot be notified about the connection result many times."
+                            + " Probably a bug or malicious app.");
+                    release = true;
+                    return;
+                }
+                mSessionBinder = sessionBinder;
+                try {
+                    // Implementation for the local binder is no-op,
+                    // so can be used without worrying about deadlock.
+                    mSessionBinder.asBinder().linkToDeath(mDeathRecipient, 0);
+                } catch (RemoteException e) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Session died too early.", e);
+                    }
+                    release = true;
+                    return;
+                }
+                registerCallbackForPlaybackNeeded = !mPlaybackListeners.isEmpty();
+            }
+            // TODO(jaewan): Keep commands to prevents illegal API calls.
+            mCallbackExecutor.execute(() -> {
+                mCallback.onConnected(commandGroup);
+            });
+            if (registerCallbackForPlaybackNeeded) {
+                registerCallbackForPlaybackNotLocked();
+            }
+        } finally {
+            if (release) {
+                // Trick to call release() without holding the lock, to prevent potential deadlock
+                // with the developer's custom lock within the ControllerCallback.onDisconnected().
+                mInstance.close();
+            }
+        }
+    }
+
+    // TODO(jaewan): Pull out this from the controller2, and rename it to the MediaController2Stub
+    //               or MediaBrowser2Stub.
+    static class MediaSession2CallbackStub extends IMediaSession2Callback.Stub {
+        private final WeakReference<MediaController2Impl> mController;
+
+        private MediaSession2CallbackStub(MediaController2Impl controller) {
+            mController = new WeakReference<>(controller);
+        }
+
+        private MediaController2Impl getController() throws IllegalStateException {
+            final MediaController2Impl controller = mController.get();
+            if (controller == null) {
+                throw new IllegalStateException("Controller is released");
+            }
+            return controller;
+        }
+
+        // TODO(jaewan): Refactor code to get rid of these pattern.
+        private MediaBrowser2Impl getBrowser() throws IllegalStateException {
+            final MediaController2Impl controller = getController();
+            if (controller instanceof MediaBrowser2Impl) {
+                return (MediaBrowser2Impl) controller;
+            }
+            return null;
+        }
+
+        public void destroy() {
+            mController.clear();
+        }
+
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) throws RuntimeException {
+            final MediaController2Impl controller = getController();
+            controller.pushPlaybackStateChanges(state);
+        }
+
+        @Override
+        public void onConnectionChanged(IMediaSession2 sessionBinder, Bundle commandGroup)
+                throws RuntimeException {
+            final MediaController2Impl controller;
+            try {
+                controller = getController();
+            } catch (IllegalStateException e) {
+                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+                return;
+            }
+            controller.onConnectionChangedNotLocked(
+                    sessionBinder, CommandGroup.fromBundle(commandGroup));
+        }
+
+        @Override
+        public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra)
+                throws RuntimeException {
+            final MediaBrowser2Impl browser;
+            try {
+                browser = getBrowser();
+            } catch (IllegalStateException e) {
+                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+                return;
+            }
+            if (browser == null) {
+                // TODO(jaewan): Revisit here. Could be a bug
+                return;
+            }
+            browser.onGetRootResult(rootHints, rootMediaId, rootExtra);
+        }
+
+        @Override
+        public void onCustomLayoutChanged(List<Bundle> commandButtonlist) {
+            if (commandButtonlist == null) {
+                // Illegal call. Ignore
+                return;
+            }
+            final MediaBrowser2Impl browser;
+            try {
+                browser = getBrowser();
+            } catch (IllegalStateException e) {
+                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+                return;
+            }
+            if (browser == null) {
+                // TODO(jaewan): Revisit here. Could be a bug
+                return;
+            }
+            List<CommandButton> layout = new ArrayList<>();
+            for (int i = 0; i < commandButtonlist.size(); i++) {
+                CommandButton button = CommandButton.fromBundle(commandButtonlist.get(i));
+                if (button != null) {
+                    layout.add(button);
+                }
+            }
+            browser.onCustomLayoutChanged(layout);
+        }
+    }
+
+    // This will be called on the main thread.
+    private class SessionServiceConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            // Note that it's always main-thread.
+            if (DEBUG) {
+                Log.d(TAG, "onServiceConnected " + name + " " + this);
+            }
+            // Sanity check
+            if (!mToken.getPackageName().equals(name.getPackageName())) {
+                Log.wtf(TAG, name + " was connected, but expected pkg="
+                        + mToken.getPackageName() + " with id=" + mToken.getId());
+                return;
+            }
+            final IMediaSession2 sessionBinder = IMediaSession2.Stub.asInterface(service);
+            connectToSession(sessionBinder);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // Temporal lose of the binding because of the service crash. System will automatically
+            // rebind, so just no-op.
+            // TODO(jaewan): Really? Either disconnect cleanly or
+            if (DEBUG) {
+                Log.w(TAG, "Session service " + name + " is disconnected.");
+            }
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            // Permanent lose of the binding because of the service package update or removed.
+            // This SessionServiceRecord will be removed accordingly, but forget session binder here
+            // for sure.
+            mInstance.close();
+        }
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
new file mode 100644
index 0000000..b177dda
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.media.MediaLibraryService2;
+import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.MediaSessionService2;
+import android.media.VolumeProvider;
+import android.media.update.MediaLibraryService2Provider;
+import android.os.Bundle;
+
+public class MediaLibraryService2Impl extends MediaSessionService2Impl implements
+        MediaLibraryService2Provider {
+    private final MediaSessionService2 mInstance;
+    private MediaLibrarySession mLibrarySession;
+
+    public MediaLibraryService2Impl(MediaLibraryService2 instance) {
+        super(instance);
+        mInstance = instance;
+    }
+
+    @Override
+    public void onCreate_impl() {
+        super.onCreate_impl();
+
+        // Effectively final
+        MediaSession2 session = getSession();
+        if (!(session instanceof MediaLibrarySession)) {
+            throw new RuntimeException("Expected MediaLibrarySession, but returned MediaSession2");
+        }
+        mLibrarySession = (MediaLibrarySession) getSession();
+    }
+
+    @Override
+    Intent createServiceIntent() {
+        Intent serviceIntent = new Intent(mInstance, mInstance.getClass());
+        serviceIntent.setAction(MediaLibraryService2.SERVICE_INTERFACE);
+        return serviceIntent;
+    }
+
+    public static class MediaLibrarySessionImpl extends MediaSession2Impl
+            implements MediaLibrarySessionProvider {
+        private final MediaLibrarySession mInstance;
+        private final MediaLibrarySessionCallback mCallback;
+
+        public MediaLibrarySessionImpl(MediaLibrarySession instance, Context context,
+                MediaPlayerBase player, String id,
+                MediaLibrarySessionCallback callback, VolumeProvider volumeProvider, int ratingType,
+                PendingIntent sessionActivity) {
+            super(instance, context, player, id, callback, volumeProvider, ratingType,
+                    sessionActivity);
+            mInstance = instance;
+            mCallback = callback;
+        }
+
+        @Override
+        public void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
+                Bundle options) {
+            // TODO(jaewan): Implements
+        }
+
+        @Override
+        public void notifyChildrenChanged_impl(String parentId, Bundle options) {
+            // TODO(jaewan): Implements
+        }
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
new file mode 100644
index 0000000..2e71641
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.Manifest.permission;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.media.AudioAttributes;
+import android.media.IMediaSession2Callback;
+import android.media.MediaItem2;
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2;
+import android.media.MediaSession2.Builder;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.PlaylistParam;
+import android.media.MediaSession2.SessionCallback;
+import android.media.SessionToken;
+import android.media.VolumeProvider;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.media.update.MediaSession2Provider;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.util.Log;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MediaSession2Impl implements MediaSession2Provider {
+    private static final String TAG = "MediaSession2";
+    private static final boolean DEBUG = true;//Log.isLoggable(TAG, Log.DEBUG);
+
+    private final MediaSession2 mInstance;
+
+    private final Context mContext;
+    private final String mId;
+    private final Handler mHandler;
+    private final MediaSession2Stub mSessionStub;
+    private final SessionToken mSessionToken;
+
+    private MediaPlayerBase mPlayer;
+
+    private final List<PlaybackListenerHolder> mListeners = new ArrayList<>();
+    private MyPlaybackListener mListener;
+    private MediaSession2 instance;
+
+    /**
+     * Can be only called by the {@link Builder#build()}.
+     * 
+     * @param instance
+     * @param context
+     * @param player
+     * @param id
+     * @param callback
+     * @param volumeProvider
+     * @param ratingType
+     * @param sessionActivity
+     */
+    public MediaSession2Impl(MediaSession2 instance, Context context, MediaPlayerBase player,
+            String id, SessionCallback callback, VolumeProvider volumeProvider, int ratingType,
+            PendingIntent sessionActivity) {
+        mInstance = instance;
+        // TODO(jaewan): Keep other params.
+
+        // Argument checks are done by builder already.
+        // Initialize finals first.
+        mContext = context;
+        mId = id;
+        mHandler = new Handler(Looper.myLooper());
+        mSessionStub = new MediaSession2Stub(this, callback);
+        // Ask server to create session token for following reasons.
+        //   1. Make session ID unique per package.
+        //      Server can only know if the package has another process and has another session
+        //      with the same id. Let server check this.
+        //      Note that 'ID is unique per package' is important for controller to distinguish
+        //      a session in another package.
+        //   2. Easier to know the type of session.
+        //      Session created here can be the session service token. In order distinguish,
+        //      we need to iterate AndroidManifest.xml but it's already done by the server.
+        //      Let server to create token with the type.
+        MediaSessionManager manager =
+                (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        mSessionToken = manager.createSessionToken(mContext.getPackageName(), mId, mSessionStub);
+        if (mSessionToken == null) {
+            throw new IllegalStateException("Session with the same id is already used by"
+                    + " another process. Use MediaController2 instead.");
+        }
+
+        setPlayerInternal(player);
+    }
+
+    // TODO(jaewan): Add explicit release() and do not remove session object with the
+    //               setPlayer(null). Token can be available when player is null, and
+    //               controller can also attach to session.
+    @Override
+    public void setPlayer_impl(MediaPlayerBase player, VolumeProvider volumeProvider) throws IllegalArgumentException {
+        ensureCallingThread();
+        if (player == null) {
+            throw new IllegalArgumentException("player shouldn't be null");
+        }
+        setPlayerInternal(player);
+    }
+
+    private void setPlayerInternal(MediaPlayerBase player) {
+        if (mPlayer == player) {
+            // Player didn't changed. No-op.
+            return;
+        }
+        mHandler.removeCallbacksAndMessages(null);
+        if (mPlayer != null && mListener != null) {
+            // This might not work for a poorly implemented player.
+            mPlayer.removePlaybackListener(mListener);
+        }
+        mListener = new MyPlaybackListener(this, player);
+        player.addPlaybackListener(mListener, mHandler);
+        notifyPlaybackStateChanged(player.getPlaybackState());
+        mPlayer = player;
+    }
+
+    @Override
+    public void close_impl() {
+        // Flush any pending messages.
+        mHandler.removeCallbacksAndMessages(null);
+        if (mSessionStub != null) {
+            if (DEBUG) {
+                Log.d(TAG, "session is now unavailable, id=" + mId);
+            }
+            // Invalidate previously published session stub.
+            mSessionStub.destroyNotLocked();
+        }
+    }
+
+    @Override
+    public MediaPlayerBase getPlayer_impl() {
+        return getPlayer();
+    }
+
+    // TODO(jaewan): Change this to @NonNull
+    @Override
+    public SessionToken getToken_impl() {
+        return mSessionToken;
+    }
+
+    @Override
+    public List<ControllerInfo> getConnectedControllers_impl() {
+        return mSessionStub.getControllers();
+    }
+
+    @Override
+    public void setAudioAttributes_impl(AudioAttributes attributes) {
+        // implement
+    }
+
+    @Override
+    public void setAudioFocusRequest_impl(int focusGain) {
+        // implement
+    }
+
+    @Override
+    public void play_impl() {
+        ensureCallingThread();
+        ensurePlayer();
+        mPlayer.play();
+    }
+
+    @Override
+    public void pause_impl() {
+        ensureCallingThread();
+        ensurePlayer();
+        mPlayer.pause();
+    }
+
+    @Override
+    public void stop_impl() {
+        ensureCallingThread();
+        ensurePlayer();
+        mPlayer.stop();
+    }
+
+    @Override
+    public void skipToPrevious_impl() {
+        ensureCallingThread();
+        ensurePlayer();
+        mPlayer.skipToPrevious();
+    }
+
+    @Override
+    public void skipToNext_impl() {
+        ensureCallingThread();
+        ensurePlayer();
+        mPlayer.skipToNext();
+    }
+
+    @Override
+    public void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout) {
+        ensureCallingThread();
+        if (controller == null) {
+            throw new IllegalArgumentException("controller shouldn't be null");
+        }
+        if (layout == null) {
+            throw new IllegalArgumentException("layout shouldn't be null");
+        }
+        mSessionStub.notifyCustomLayoutNotLocked(controller, layout);
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////
+    // TODO(jaewan): Implement follows
+    //////////////////////////////////////////////////////////////////////////////////////
+    @Override
+    public void setPlayer_impl(MediaPlayerBase player) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void notifyMetadataChanged_impl() {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void sendCustomCommand_impl(ControllerInfo controller, Command command, Bundle args,
+            ResultReceiver receiver) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void sendCustomCommand_impl(Command command, Bundle args) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void setPlaylist_impl(List<MediaItem2> playlist, PlaylistParam param) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void prepare_impl() {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void fastForward_impl() {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void rewind_impl() {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void seekTo_impl(long pos) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void setCurrentPlaylistItem_impl(int index) {
+        // TODO(jaewan): Implement
+    }
+
+    ///////////////////////////////////////////////////
+    // Protected or private methods
+    ///////////////////////////////////////////////////
+
+    // Enforces developers to call all the methods on the initially given thread
+    // because calls from the MediaController2 will be run on the thread.
+    // TODO(jaewan): Should we allow calls from the multiple thread?
+    //               I prefer this way because allowing multiple thread may case tricky issue like
+    //               b/63446360. If the {@link #setPlayer()} with {@code null} can be called from
+    //               another thread, transport controls can be called after that.
+    //               That's basically the developer's mistake, but they cannot understand what's
+    //               happening behind until we tell them so.
+    //               If enforcing callling thread doesn't look good, we can alternatively pick
+    //               1. Allow calls from random threads for all methods.
+    //               2. Allow calls from random threads for all methods, except for the
+    //                  {@link #setPlayer()}.
+    // TODO(jaewan): Should we pend command instead of exception?
+    private void ensureCallingThread() {
+        if (mHandler.getLooper() != Looper.myLooper()) {
+            throw new IllegalStateException("Run this on the given thread");
+        }
+    }
+
+    private void ensurePlayer() {
+        // TODO(jaewan): Should we pend command instead? Follow the decision from MP2.
+        //               Alternatively we can add a API like setAcceptsPendingCommands(boolean).
+        if (mPlayer == null) {
+            throw new IllegalStateException("Player isn't set");
+        }
+    }
+
+    Handler getHandler() {
+        return mHandler;
+    }
+
+    private void notifyPlaybackStateChanged(PlaybackState state) {
+        // Notify to listeners added directly to this session
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).postPlaybackChange(state);
+        }
+        // Notify to controllers as well.
+        mSessionStub.notifyPlaybackStateChangedNotLocked(state);
+    }
+
+    Context getContext() {
+        return mContext;
+    }
+
+    MediaSession2 getInstance() {
+        return mInstance;
+    }
+
+    MediaPlayerBase getPlayer() {
+        return mPlayer;
+    }
+
+    private static class MyPlaybackListener implements MediaPlayerBase.PlaybackListener {
+        private final WeakReference<MediaSession2Impl> mSession;
+        private final MediaPlayerBase mPlayer;
+
+        private MyPlaybackListener(MediaSession2Impl session, MediaPlayerBase player) {
+            mSession = new WeakReference<>(session);
+            mPlayer = player;
+        }
+
+        @Override
+        public void onPlaybackChanged(PlaybackState state) {
+            MediaSession2Impl session = mSession.get();
+            if (session == null || session.getHandler().getLooper() != Looper.myLooper()
+                    || mPlayer != session.mInstance.getPlayer()) {
+                Log.w(TAG, "Unexpected playback state change notifications. Ignoring.",
+                        new IllegalStateException());
+                return;
+            }
+            session.notifyPlaybackStateChanged(state);
+        }
+    }
+
+    public static class ControllerInfoImpl implements ControllerInfoProvider {
+        private final ControllerInfo mInstance;
+        private final int mUid;
+        private final String mPackageName;
+        private final boolean mIsTrusted;
+        private final IMediaSession2Callback mControllerBinder;
+
+        // Flag to indicate which callbacks should be returned for the controller binder.
+        // Either 0 or combination of {@link #CALLBACK_FLAG_PLAYBACK},
+        // {@link #CALLBACK_FLAG_SESSION_ACTIVENESS}
+        private int mFlag;
+
+        public ControllerInfoImpl(ControllerInfo instance, Context context, int uid,
+                int pid, String packageName, IMediaSession2Callback callback) {
+            mInstance = instance;
+            mUid = uid;
+            mPackageName = packageName;
+
+            // TODO(jaewan): Remove this workaround
+            if ("com.android.server.media".equals(packageName)) {
+                mIsTrusted = true;
+            } else if (context.checkPermission(permission.MEDIA_CONTENT_CONTROL, pid, uid) ==
+                    PackageManager.PERMISSION_GRANTED) {
+                mIsTrusted = true;
+            } else {
+                // TODO(jaewan): Also consider enabled notification listener.
+                mIsTrusted = false;
+                // System apps may bind across the user so uid can be differ.
+                // Skip sanity check for the system app.
+                try {
+                    int uidForPackage = context.getPackageManager().getPackageUid(packageName, 0);
+                    if (uid != uidForPackage) {
+                        throw new IllegalArgumentException("Illegal call from uid=" + uid +
+                                ", pkg=" + packageName + ". Expected uid" + uidForPackage);
+                    }
+                } catch (NameNotFoundException e) {
+                    // Rethrow exception with different name because binder methods only accept
+                    // RemoteException.
+                    throw new IllegalArgumentException(e);
+                }
+            }
+            mControllerBinder = callback;
+        }
+
+        @Override
+        public String getPackageName_impl() {
+            return mPackageName;
+        }
+
+        @Override
+        public int getUid_impl() {
+            return mUid;
+        }
+
+        @Override
+        public boolean isTrusted_impl() {
+            return mIsTrusted;
+        }
+
+        @Override
+        public int hashCode_impl() {
+            return mControllerBinder.hashCode();
+        }
+
+        @Override
+        public boolean equals_impl(ControllerInfoProvider obj) {
+            return equals(obj);
+        }
+
+        @Override
+        public int hashCode() {
+            return mControllerBinder.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof ControllerInfoImpl)) {
+                return false;
+            }
+            ControllerInfoImpl other = (ControllerInfoImpl) obj;
+            return mControllerBinder.asBinder().equals(other.mControllerBinder.asBinder());
+        }
+
+        public ControllerInfo getInstance() {
+            return mInstance;
+        }
+
+        public IBinder getId() {
+            return mControllerBinder.asBinder();
+        }
+
+        public IMediaSession2Callback getControllerBinder() {
+            return mControllerBinder;
+        }
+
+        public boolean containsFlag(int flag) {
+            return (mFlag & flag) != 0;
+        }
+
+        public void addFlag(int flag) {
+            mFlag |= flag;
+        }
+
+        public void removeFlag(int flag) {
+            mFlag &= ~flag;
+        }
+
+        public static ControllerInfoImpl from(ControllerInfo controller) {
+            return (ControllerInfoImpl) controller.getProvider();
+        }
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
new file mode 100644
index 0000000..3f7a1b1
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import static com.android.media.MediaController2Impl.CALLBACK_FLAG_PLAYBACK;
+
+import android.content.Context;
+import android.media.IMediaSession2;
+import android.media.IMediaSession2Callback;
+import android.media.MediaLibraryService2.BrowserRoot;
+import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
+import android.media.MediaSession2;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.session.PlaybackState;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.support.annotation.GuardedBy;
+import android.util.ArrayMap;
+import android.util.Log;
+import com.android.media.MediaSession2Impl.ControllerInfoImpl;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MediaSession2Stub extends IMediaSession2.Stub {
+    private static final String TAG = "MediaSession2Stub";
+    private static final boolean DEBUG = true; // TODO(jaewan): Rename.
+
+    private final Object mLock = new Object();
+    private final CommandHandler mCommandHandler;
+    private final WeakReference<MediaSession2Impl> mSession;
+    private final Context mContext;
+    private final SessionCallback mSessionCallback;
+    private final MediaLibrarySessionCallback mLibraryCallback;
+
+    @GuardedBy("mLock")
+    private final ArrayMap<IBinder, ControllerInfo> mControllers = new ArrayMap<>();
+
+    public MediaSession2Stub(MediaSession2Impl session, SessionCallback callback) {
+        mSession = new WeakReference<>(session);
+        mContext = session.getContext();
+        // TODO(jaewan): Should be executor from the session builder
+        mCommandHandler = new CommandHandler(session.getHandler().getLooper());
+        mSessionCallback = callback;
+        mLibraryCallback = (callback instanceof MediaLibrarySessionCallback)
+                ? (MediaLibrarySessionCallback) callback : null;
+    }
+
+    public void destroyNotLocked() {
+        final List<ControllerInfo> list;
+        synchronized (mLock) {
+            mSession.clear();
+            mCommandHandler.removeCallbacksAndMessages(null);
+            list = getControllers();
+            mControllers.clear();
+        }
+        for (int i = 0; i < list.size(); i++) {
+            IMediaSession2Callback callbackBinder =
+                    ((ControllerInfoImpl) list.get(i).getProvider()).getControllerBinder();
+            try {
+                // Should be used without a lock hold to prevent potential deadlock.
+                callbackBinder.onConnectionChanged(null, null);
+            } catch (RemoteException e) {
+                // Controller is gone. Should be fine because we're destroying.
+            }
+        }
+    }
+
+    private MediaSession2Impl getSession() throws IllegalStateException {
+        final MediaSession2Impl session = mSession.get();
+        if (session == null) {
+            throw new IllegalStateException("Session is died");
+        }
+        return session;
+    }
+
+    @Override
+    public void connect(String callingPackage, IMediaSession2Callback callback) {
+        if (callback == null) {
+            // Requesting connect without callback to receive result.
+            return;
+        }
+        ControllerInfo request = new ControllerInfo(mContext,
+                Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, callback);
+        mCommandHandler.postConnect(request);
+    }
+
+    @Override
+    public void release(IMediaSession2Callback caller) throws RemoteException {
+        synchronized (mLock) {
+            ControllerInfo controllerInfo = mControllers.remove(caller.asBinder());
+            if (DEBUG) {
+                Log.d(TAG, "releasing " + controllerInfo);
+            }
+        }
+    }
+
+    @Override
+    public void sendCommand(IMediaSession2Callback caller, Bundle command, Bundle args)
+            throws RuntimeException {
+        ControllerInfo controller = getController(caller);
+        if (controller == null) {
+            if (DEBUG) {
+                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
+            }
+            return;
+        }
+        mCommandHandler.postCommand(controller, Command.fromBundle(command), args);
+    }
+
+    @Override
+    public void getBrowserRoot(IMediaSession2Callback caller, Bundle rootHints)
+            throws RuntimeException {
+        if (mLibraryCallback == null) {
+            if (DEBUG) {
+                Log.d(TAG, "Session cannot hand getBrowserRoot()");
+            }
+            return;
+        }
+        final ControllerInfo controller = getController(caller);
+        if (controller == null) {
+            if (DEBUG) {
+                Log.d(TAG, "getBrowerRoot from a controller that hasn't connected. Ignore");
+            }
+            return;
+        }
+        mCommandHandler.postOnGetRoot(controller, rootHints);
+    }
+
+    @Deprecated
+    @Override
+    public PlaybackState getPlaybackState() throws RemoteException {
+        MediaSession2Impl session = getSession();
+        // TODO(jaewan): Check if mPlayer.getPlaybackState() is safe here.
+        return session.getInstance().getPlayer().getPlaybackState();
+    }
+
+    @Deprecated
+    @Override
+    public void registerCallback(final IMediaSession2Callback callbackBinder,
+            final int callbackFlag, final int requestCode) throws RemoteException {
+        // TODO(jaewan): Call onCommand() here. To do so, you should pend message.
+        synchronized (mLock) {
+            ControllerInfo controllerInfo = getController(callbackBinder);
+            if (controllerInfo == null) {
+                return;
+            }
+            ControllerInfoImpl.from(controllerInfo).addFlag(callbackFlag);
+        }
+    }
+
+    @Deprecated
+    @Override
+    public void unregisterCallback(IMediaSession2Callback callbackBinder, int callbackFlag)
+            throws RemoteException {
+        // TODO(jaewan): Call onCommand() here. To do so, you should pend message.
+        synchronized (mLock) {
+            ControllerInfo controllerInfo = getController(callbackBinder);
+            if (controllerInfo == null) {
+                return;
+            }
+            ControllerInfoImpl.from(controllerInfo).removeFlag(callbackFlag);
+        }
+    }
+
+    private ControllerInfo getController(IMediaSession2Callback caller) {
+        synchronized (mLock) {
+            return mControllers.get(caller.asBinder());
+        }
+    }
+
+    public List<ControllerInfo> getControllers() {
+        ArrayList<ControllerInfo> controllers = new ArrayList<>();
+        synchronized (mLock) {
+            for (int i = 0; i < mControllers.size(); i++) {
+                controllers.add(mControllers.valueAt(i));
+            }
+        }
+        return controllers;
+    }
+
+    public List<ControllerInfo> getControllersWithFlag(int flag) {
+        ArrayList<ControllerInfo> controllers = new ArrayList<>();
+        synchronized (mLock) {
+            for (int i = 0; i < mControllers.size(); i++) {
+                ControllerInfo controllerInfo = mControllers.valueAt(i);
+                if (ControllerInfoImpl.from(controllerInfo).containsFlag(flag)) {
+                    controllers.add(controllerInfo);
+                }
+            }
+        }
+        return controllers;
+    }
+
+    // Should be used without a lock to prevent potential deadlock.
+    public void notifyPlaybackStateChangedNotLocked(PlaybackState state) {
+        final List<ControllerInfo> list = getControllersWithFlag(CALLBACK_FLAG_PLAYBACK);
+        for (int i = 0; i < list.size(); i++) {
+            IMediaSession2Callback callbackBinder =
+                    ControllerInfoImpl.from(list.get(i)).getControllerBinder();
+            try {
+                callbackBinder.onPlaybackStateChanged(state);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Controller is gone", e);
+                // TODO(jaewan): What to do when the controller is gone?
+            }
+        }
+    }
+
+    public void notifyCustomLayoutNotLocked(ControllerInfo controller, List<CommandButton> layout) {
+        // TODO(jaewan): It's OK to be called while it's connecting, but not OK if the connection
+        //               is rejected. Handle the case.
+        IMediaSession2Callback callbackBinder =
+                ControllerInfoImpl.from(controller).getControllerBinder();
+        try {
+            List<Bundle> layoutBundles = new ArrayList<>();
+            for (int i = 0; i < layout.size(); i++) {
+                Bundle bundle = layout.get(i).toBundle();
+                if (bundle != null) {
+                    layoutBundles.add(bundle);
+                }
+            }
+            callbackBinder.onCustomLayoutChanged(layoutBundles);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Controller is gone", e);
+            // TODO(jaewan): What to do when the controller is gone?
+        }
+    }
+
+    // TODO(jaewan): Remove this. We should use Executor given by the session builder.
+    private class CommandHandler extends Handler {
+        public static final int MSG_CONNECT = 1000;
+        public static final int MSG_COMMAND = 1001;
+        public static final int MSG_ON_GET_ROOT = 2000;
+
+        public CommandHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final MediaSession2Impl session = MediaSession2Stub.this.mSession.get();
+            if (session == null || session.getPlayer() == null) {
+                return;
+            }
+
+            switch (msg.what) {
+                case MSG_CONNECT: {
+                    ControllerInfo request = (ControllerInfo) msg.obj;
+                    CommandGroup allowedCommands = mSessionCallback.onConnect(request);
+                    // Don't reject connection for the request from trusted app.
+                    // Otherwise server will fail to retrieve session's information to dispatch
+                    // media keys to.
+                    boolean accept = allowedCommands != null || request.isTrusted();
+                    ControllerInfoImpl impl = ControllerInfoImpl.from(request);
+                    if (accept) {
+                        synchronized (mLock) {
+                            mControllers.put(impl.getId(), request);
+                        }
+                        if (allowedCommands == null) {
+                            // For trusted apps, send non-null allowed commands to keep connection.
+                            allowedCommands = new CommandGroup();
+                        }
+                    }
+                    if (DEBUG) {
+                        Log.d(TAG, "onConnectResult, request=" + request
+                                + " accept=" + accept);
+                    }
+                    try {
+                        impl.getControllerBinder().onConnectionChanged(
+                                accept ? MediaSession2Stub.this : null,
+                                allowedCommands == null ? null : allowedCommands.toBundle());
+                    } catch (RemoteException e) {
+                        // Controller may be died prematurely.
+                    }
+                    break;
+                }
+                case MSG_COMMAND: {
+                    CommandParam param = (CommandParam) msg.obj;
+                    Command command = param.command;
+                    boolean accepted = mSessionCallback.onCommandRequest(
+                            param.controller, command);
+                    if (!accepted) {
+                        // Don't run rejected command.
+                        if (DEBUG) {
+                            Log.d(TAG, "Command " + command + " from "
+                                    + param.controller + " was rejected by " + session);
+                        }
+                        return;
+                    }
+
+                    switch (param.command.getCommandCode()) {
+                        case MediaSession2.COMMAND_CODE_PLAYBACK_START:
+                            session.getInstance().play();
+                            break;
+                        case MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE:
+                            session.getInstance().pause();
+                            break;
+                        case MediaSession2.COMMAND_CODE_PLAYBACK_STOP:
+                            session.getInstance().stop();
+                            break;
+                        case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM:
+                            session.getInstance().skipToPrevious();
+                            break;
+                        case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM:
+                            session.getInstance().skipToNext();
+                            break;
+                        default:
+                            // TODO(jaewan): Handle custom command.
+                    }
+                    break;
+                }
+                case MSG_ON_GET_ROOT: {
+                    final CommandParam param = (CommandParam) msg.obj;
+                    final ControllerInfoImpl controller = ControllerInfoImpl.from(param.controller);
+                    BrowserRoot root = mLibraryCallback.onGetRoot(param.controller, param.args);
+                    try {
+                        controller.getControllerBinder().onGetRootResult(param.args,
+                                root == null ? null : root.getRootId(),
+                                root == null ? null : root.getExtras());
+                    } catch (RemoteException e) {
+                        // Controller may be died prematurely.
+                        // TODO(jaewan): Handle this.
+                    }
+                    break;
+                }
+            }
+        }
+
+        public void postConnect(ControllerInfo request) {
+            obtainMessage(MSG_CONNECT, request).sendToTarget();
+        }
+
+        public void postCommand(ControllerInfo controller, Command command, Bundle args) {
+            CommandParam param = new CommandParam(controller, command, args);
+            obtainMessage(MSG_COMMAND, param).sendToTarget();
+        }
+
+        public void postOnGetRoot(ControllerInfo controller, Bundle rootHints) {
+            CommandParam param = new CommandParam(controller, null, rootHints);
+            obtainMessage(MSG_ON_GET_ROOT, param).sendToTarget();
+        }
+    }
+
+    private static class CommandParam {
+        public final ControllerInfo controller;
+        public final Command command;
+        public final Bundle args;
+
+        private CommandParam(ControllerInfo controller, Command command, Bundle args) {
+            this.controller = controller;
+            this.command = command;
+            this.args = args;
+        }
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
new file mode 100644
index 0000000..773a06f
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import static android.content.Context.NOTIFICATION_SERVICE;
+
+import android.app.NotificationManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.MediaPlayerBase.PlaybackListener;
+import android.media.MediaSession2;
+import android.media.MediaSessionService2;
+import android.media.MediaSessionService2.MediaNotification;
+import android.media.session.PlaybackState;
+import android.media.update.MediaSessionService2Provider;
+import android.os.IBinder;
+import android.os.Looper;
+import android.support.annotation.GuardedBy;
+import android.util.Log;
+
+// TODO(jaewan): Need a test for session service itself.
+public class MediaSessionService2Impl implements MediaSessionService2Provider {
+
+    private static final String TAG = "MPSessionService"; // to meet 23 char limit in Log tag
+    private static final boolean DEBUG = true; // TODO(jaewan): Change this.
+
+    private final MediaSessionService2 mInstance;
+    private final PlaybackListener mListener = new SessionServicePlaybackListener();
+
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private NotificationManager mNotificationManager;
+    @GuardedBy("mLock")
+    private Intent mStartSelfIntent;
+
+    private boolean mIsRunningForeground;
+    private MediaSession2 mSession;
+
+    public MediaSessionService2Impl(MediaSessionService2 instance) {
+        if (DEBUG) {
+            Log.d(TAG, "MediaSessionService2Impl(" + instance + ")");
+        }
+        mInstance = instance;
+    }
+
+    @Override
+    public MediaSession2 getSession_impl() {
+        return getSession();
+    }
+
+    MediaSession2 getSession() {
+        synchronized (mLock) {
+            return mSession;
+        }
+    }
+
+    @Override
+    public MediaNotification onUpdateNotification_impl(PlaybackState state) {
+        // Provide default notification UI later.
+        return null;
+    }
+
+    @Override
+    public void onCreate_impl() {
+        mNotificationManager = (NotificationManager) mInstance.getSystemService(
+                NOTIFICATION_SERVICE);
+        mStartSelfIntent = new Intent(mInstance, mInstance.getClass());
+
+        Intent serviceIntent = createServiceIntent();
+        ResolveInfo resolveInfo = mInstance.getPackageManager()
+                .resolveService(serviceIntent, PackageManager.GET_META_DATA);
+        String id;
+        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+            throw new IllegalArgumentException("service " + mInstance + " doesn't implement"
+                    + serviceIntent.getAction());
+        } else if (resolveInfo.serviceInfo.metaData == null) {
+            if (DEBUG) {
+                Log.d(TAG, "Failed to resolve ID for " + mInstance + ". Using empty id");
+            }
+            id = "";
+        } else {
+            id = resolveInfo.serviceInfo.metaData.getString(
+                    MediaSessionService2.SERVICE_META_DATA, "");
+        }
+        mSession = mInstance.onCreateSession(id);
+        if (mSession == null || !id.equals(mSession.getToken().getId())) {
+            throw new RuntimeException("Expected session with id " + id + ", but got " + mSession);
+        }
+        // TODO(jaewan): Uncomment here.
+        // mSession.addPlaybackListener(mListener, mSession.getExecutor());
+    }
+
+    Intent createServiceIntent() {
+        Intent serviceIntent = new Intent(mInstance, mInstance.getClass());
+        serviceIntent.setAction(MediaSessionService2.SERVICE_INTERFACE);
+        return serviceIntent;
+    }
+
+    public IBinder onBind_impl(Intent intent) {
+        if (MediaSessionService2.SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mSession.getToken().getSessionBinder().asBinder();
+        }
+        return null;
+    }
+
+    private void updateNotification(PlaybackState state) {
+        MediaNotification mediaNotification = mInstance.onUpdateNotification(state);
+        if (mediaNotification == null) {
+            return;
+        }
+        switch((int) state.getState()) {
+            case PlaybackState.STATE_PLAYING:
+                if (!mIsRunningForeground) {
+                    mIsRunningForeground = true;
+                    mInstance.startForegroundService(mStartSelfIntent);
+                    mInstance.startForeground(mediaNotification.id, mediaNotification.notification);
+                    return;
+                }
+                break;
+            case PlaybackState.STATE_STOPPED:
+                if (mIsRunningForeground) {
+                    mIsRunningForeground = false;
+                    mInstance.stopForeground(true);
+                    return;
+                }
+                break;
+        }
+        mNotificationManager.notify(mediaNotification.id, mediaNotification.notification);
+    }
+
+    private class SessionServicePlaybackListener implements PlaybackListener {
+        @Override
+        public void onPlaybackChanged(PlaybackState state) {
+            if (state == null) {
+                Log.w(TAG, "Ignoring null playback state");
+                return;
+            }
+            MediaSession2Impl impl = (MediaSession2Impl) mSession.getProvider();
+            if (impl.getHandler().getLooper() != Looper.myLooper()) {
+                Log.w(TAG, "Ignoring " + state + ". Expected " + impl.getHandler().getLooper()
+                        + " but " + Looper.myLooper());
+                return;
+            }
+            updateNotification(state);
+        }
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
new file mode 100644
index 0000000..4d06463
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.media.MediaPlayerBase;
+import android.media.MediaPlayerBase.PlaybackListener;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.os.Message;
+import android.support.annotation.NonNull;
+import java.util.List;
+
+/**
+ * Holds {@link android.media.MediaPlayerBase.PlaybackListener} with the {@link Handler}.
+ */
+public class PlaybackListenerHolder extends Handler {
+    private static final int ON_PLAYBACK_CHANGED = 1;
+
+    public final MediaPlayerBase.PlaybackListener listener;
+
+    public PlaybackListenerHolder(
+            @NonNull MediaPlayerBase.PlaybackListener listener, @NonNull Handler handler) {
+        super(handler.getLooper());
+        this.listener = listener;
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case ON_PLAYBACK_CHANGED:
+                listener.onPlaybackChanged((PlaybackState) msg.obj);
+                break;
+        }
+    }
+
+    public void postPlaybackChange(PlaybackState state) {
+        obtainMessage(ON_PLAYBACK_CHANGED, state).sendToTarget();
+    }
+
+    /**
+     * Returns {@code true} if the given list contains a {@link PlaybackListenerHolder} that holds
+     * the given listener.
+     *
+     * @param list list to check
+     * @param listener listener to check
+     * @return {@code true} if the given list contains listener. {@code false} otherwise.
+     */
+    public static <Holder extends PlaybackListenerHolder> boolean contains(
+            @NonNull List<Holder> list, PlaybackListener listener) {
+        return indexOf(list, listener) >= 0;
+    }
+
+    /**
+     * Returns the index of the {@link PlaybackListenerHolder} that contains the given listener.
+     *
+     * @param list list to check
+     * @param listener listener to check
+     * @return {@code index} of item if the given list contains listener. {@code -1} otherwise.
+     */
+    public static <Holder extends PlaybackListenerHolder> int indexOf(
+            @NonNull List<Holder> list, PlaybackListener listener) {
+        for (int i = 0; i < list.size(); i++) {
+            if (list.get(i).listener == listener) {
+                return i;
+            }
+        }
+        return -1;
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 633a342..43acaf9 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -16,9 +16,30 @@
 
 package com.android.media.update;
 
+import android.app.PendingIntent;
+import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
+import android.media.MediaBrowser2;
+import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaController2;
+import android.media.MediaLibraryService2;
+import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.MediaSessionService2;
+import android.media.IMediaSession2Callback;
+import android.media.SessionToken;
+import android.media.VolumeProvider;
+import android.media.update.MediaBrowser2Provider;
 import android.media.update.MediaControlView2Provider;
+import android.media.update.MediaController2Provider;
+import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
+import android.media.update.MediaSession2Provider;
+import android.media.update.MediaSessionService2Provider;
 import android.media.update.VideoView2Provider;
 import android.media.update.StaticProvider;
 import android.media.update.ViewProvider;
@@ -27,9 +48,17 @@
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
 
+import com.android.media.MediaBrowser2Impl;
+import com.android.media.MediaController2Impl;
+import com.android.media.MediaLibraryService2Impl;
+import com.android.media.MediaLibraryService2Impl.MediaLibrarySessionImpl;
+import com.android.media.MediaSession2Impl;
+import com.android.media.MediaSessionService2Impl;
 import com.android.widget.MediaControlView2Impl;
 import com.android.widget.VideoView2Impl;
 
+import java.util.concurrent.Executor;
+
 public class ApiFactory implements StaticProvider {
     public static Object initialize(Resources libResources, Theme libTheme)
             throws ReflectiveOperationException {
@@ -38,6 +67,57 @@
     }
 
     @Override
+    public MediaController2Provider createMediaController2(
+            MediaController2 instance, Context context, SessionToken token,
+            MediaController2.ControllerCallback callback, Executor executor) {
+        return new MediaController2Impl(instance, context, token, callback, executor);
+    }
+
+    @Override
+    public MediaBrowser2Provider createMediaBrowser2(MediaBrowser2 instance, Context context,
+            SessionToken token, BrowserCallback callback, Executor executor) {
+        return new MediaBrowser2Impl(instance, context, token, callback, executor);
+    }
+
+    @Override
+    public MediaSession2Provider createMediaSession2(MediaSession2 instance, Context context,
+            MediaPlayerBase player, String id, SessionCallback callback,
+            VolumeProvider volumeProvider, int ratingType,
+            PendingIntent sessionActivity) {
+        return new MediaSession2Impl(instance, context, player, id, callback,
+                volumeProvider, ratingType, sessionActivity);
+    }
+
+    @Override
+    public MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfoProvider(
+            ControllerInfo instance, Context context, int uid, int pid, String packageName,
+            IMediaSession2Callback callback) {
+        return new MediaSession2Impl.ControllerInfoImpl(
+                instance, context, uid, pid, packageName, callback);
+    }
+
+    @Override
+    public MediaSessionService2Provider createMediaSessionService2(
+            MediaSessionService2 instance) {
+        return new MediaSessionService2Impl(instance);
+    }
+
+    @Override
+    public MediaSessionService2Provider createMediaLibraryService2(
+            MediaLibraryService2 instance) {
+        return new MediaLibraryService2Impl(instance);
+    }
+
+    @Override
+    public MediaLibrarySessionProvider createMediaLibraryService2MediaLibrarySession(
+            MediaLibrarySession instance, Context context, MediaPlayerBase player, String id,
+            MediaLibrarySessionCallback callback, VolumeProvider volumeProvider, int ratingType,
+            PendingIntent sessionActivity) {
+        return new MediaLibrarySessionImpl(instance, context, player, id, callback, volumeProvider,
+                ratingType, sessionActivity);
+    }
+
+    @Override
     public MediaControlView2Provider createMediaControlView2(
             MediaControlView2 instance, ViewProvider superProvider) {
         return new MediaControlView2Impl(instance, superProvider);
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
index 26f858c..b0ca1bd 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
@@ -16,8 +16,16 @@
 
 package com.android.media.update;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.support.mediarouter.app.MediaRouteButton;
 
 public class ApiHelper {
     private static ApiHelper sInstance;
@@ -46,4 +54,31 @@
     public static Resources.Theme getLibTheme() {
         return sInstance.mLibTheme;
     }
+
+    public static LayoutInflater getLayoutInflater(Context context) {
+        LayoutInflater layoutInflater = LayoutInflater.from(context).cloneInContext(
+                new ContextThemeWrapper(context, getLibTheme()));
+        layoutInflater.setFactory2(new LayoutInflater.Factory2() {
+            @Override
+            public View onCreateView(
+                    View parent, String name, Context context, AttributeSet attrs) {
+                if (MediaRouteButton.class.getCanonicalName().equals(name)) {
+                    return new MediaRouteButton(context, attrs);
+                }
+                return null;
+            }
+
+            @Override
+            public View onCreateView(String name, Context context, AttributeSet attrs) {
+                return onCreateView(null, name, context, attrs);
+            }
+        });
+        return layoutInflater;
+    }
+
+    public static View inflateLibLayout(Context context, int libResId) {
+        try (XmlResourceParser parser = getLibResources().getLayout(libResId)) {
+            return getLayoutInflater(context).inflate(parser, null);
+        }
+    }
 }
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
index 68e8d3a..65fc88c 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.drawable.AnimationDrawable;
@@ -36,6 +37,7 @@
 import android.view.SoundEffectConstants;
 import android.view.View;
 
+import com.android.media.update.ApiHelper;
 import com.android.media.update.R;
 import com.android.support.mediarouter.media.MediaRouteSelector;
 import com.android.support.mediarouter.media.MediaRouter;
@@ -128,8 +130,11 @@
         mRouter = MediaRouter.getInstance(context);
         mCallback = new MediaRouterCallback();
 
-        TypedArray a = context.obtainStyledAttributes(attrs,
+        Resources.Theme theme = ApiHelper.getLibResources().newTheme();
+        theme.applyStyle(MediaRouterThemeHelper.getRouterThemeId(context), true);
+        TypedArray a = theme.obtainStyledAttributes(attrs,
                 R.styleable.MediaRouteButton, defStyleAttr, 0);
+
         mButtonTint = a.getColorStateList(R.styleable.MediaRouteButton_mediaRouteButtonTint);
         mMinWidth = a.getDimensionPixelSize(
                 R.styleable.MediaRouteButton_android_minWidth, 0);
@@ -290,8 +295,9 @@
      * button when the button is long pressed.
      */
     void setCheatSheetEnabled(boolean enable) {
-        TooltipCompat.setTooltipText(this,
-                enable ? getContext().getString(R.string.mr_button_content_description) : null);
+        TooltipCompat.setTooltipText(this, enable
+                ? ApiHelper.getLibResources().getString(R.string.mr_button_content_description)
+                : null);
     }
 
     @Override
@@ -533,7 +539,7 @@
         } else {
             resId = R.string.mr_cast_button_disconnected;
         }
-        setContentDescription(getContext().getString(resId));
+        setContentDescription(ApiHelper.getLibResources().getString(resId));
     }
 
     private final class MediaRouterCallback extends MediaRouter.Callback {
@@ -590,7 +596,7 @@
 
         @Override
         protected Drawable doInBackground(Void... params) {
-            return getContext().getResources().getDrawable(mResId);
+            return ApiHelper.getLibResources().getDrawable(mResId);
         }
 
         @Override
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java
index b4b49df..7440130 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java
@@ -196,7 +196,7 @@
         return value.data;
     }
 
-    private static int getRouterThemeId(Context context) {
+    static int getRouterThemeId(Context context) {
         int themeId;
         if (isLightTheme(context)) {
             if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java
index f5e1e61..33d92b4 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java
@@ -27,6 +27,7 @@
 import android.support.annotation.RequiresApi;
 import android.view.Display;
 
+import com.android.media.update.ApiHelper;
 import com.android.media.update.R;
 
 import java.util.ArrayList;
@@ -266,7 +267,7 @@
             mCallbackObj = createCallbackObj();
             mVolumeCallbackObj = createVolumeCallbackObj();
 
-            Resources r = context.getResources();
+            Resources r = ApiHelper.getLibResources();
             mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory(
                     mRouterObj, r.getString(R.string.mr_user_route_category_name), false);
 
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index 8053245e..bc370d8 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,144 +16,889 @@
 
 package com.android.widget;
 
+import android.content.res.Resources;
+import android.media.MediaMetadata;
 import android.media.session.MediaController;
+import android.media.session.PlaybackState;
 import android.media.update.MediaControlView2Provider;
 import android.media.update.ViewProvider;
+import android.os.Bundle;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.ImageButton;
 import android.widget.MediaControlView2;
+import android.widget.ProgressBar;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+import com.android.media.update.ApiHelper;
+import com.android.media.update.R;
+
+import java.util.Formatter;
+import java.util.Locale;
 
 public class MediaControlView2Impl implements MediaControlView2Provider {
+    private static final String TAG = "MediaControlView2";
+
     private final MediaControlView2 mInstance;
     private final ViewProvider mSuperProvider;
 
-    static final String ACTION_SHOW_SUBTITLE = "showSubtitle";
-    static final String ACTION_HIDE_SUBTITLE = "hideSubtitle";
+    static final String COMMAND_SHOW_SUBTITLE = "showSubtitle";
+    static final String COMMAND_HIDE_SUBTITLE = "hideSubtitle";
+    static final String COMMAND_SET_FULLSCREEN = "setFullscreen";
 
-    public MediaControlView2Impl(MediaControlView2 instance, ViewProvider superProvider) {
+    static final String ARGUMENT_KEY_FULLSCREEN = "fullScreen";
+
+    static final String KEY_STATE_CONTAINS_SUBTITLE = "StateContainsSubtitle";
+    static final String EVENT_UPDATE_SUBTITLE_STATUS = "UpdateSubtitleStatus";
+
+    private static final int MAX_PROGRESS = 1000;
+    private static final int DEFAULT_PROGRESS_UPDATE_TIME_MS = 1000;
+    private static final int DEFAULT_TIMEOUT_MS = 2000;
+
+    private static final int REWIND_TIME_MS = 10000;
+    private static final int FORWARD_TIME_MS = 30000;
+
+    private final AccessibilityManager mAccessibilityManager;
+
+    private MediaController mController;
+    private MediaController.TransportControls mControls;
+    private PlaybackState mPlaybackState;
+    private MediaMetadata mMetadata;
+    private ProgressBar mProgress;
+    private TextView mEndTime, mCurrentTime;
+    private TextView mTitleView;
+    private int mDuration;
+    private int mPrevState;
+    private long mPlaybackActions;
+    private boolean mShowing;
+    private boolean mDragging;
+    private boolean mIsFullScreen;
+    private boolean mOverflowExpanded;
+    private boolean mIsStopped;
+    private boolean mSubtitleIsEnabled;
+    private boolean mContainsSubtitle;
+    private boolean mSeekAvailable;
+    private View.OnClickListener mNextListener, mPrevListener;
+    private ImageButton mPlayPauseButton;
+    private ImageButton mFfwdButton;
+    private ImageButton mRewButton;
+    private ImageButton mNextButton;
+    private ImageButton mPrevButton;
+
+    private ViewGroup mBasicControls;
+    private ImageButton mSubtitleButton;
+    private ImageButton mFullScreenButton;
+    private ImageButton mOverflowButtonRight;
+
+    private ViewGroup mExtraControls;
+    private ImageButton mOverflowButtonLeft;
+    private ImageButton mMuteButton;
+    private ImageButton mAspectRationButton;
+    private ImageButton mSettingsButton;
+
+    private CharSequence mPlayDescription;
+    private CharSequence mPauseDescription;
+    private CharSequence mReplayDescription;
+
+    private StringBuilder mFormatBuilder;
+    private Formatter mFormatter;
+
+    public MediaControlView2Impl(
+            MediaControlView2 instance, ViewProvider superProvider) {
         mInstance = instance;
         mSuperProvider = superProvider;
+        mAccessibilityManager = AccessibilityManager.getInstance(mInstance.getContext());
 
-        // TODO: Implement
+        // Inflate MediaControlView2 from XML
+        View root = makeControllerView();
+        mInstance.addView(root);
     }
 
     @Override
     public void setController_impl(MediaController controller) {
-        // TODO: Implement
+        mController = controller;
+        if (controller != null) {
+            mControls = controller.getTransportControls();
+            // Set mMetadata and mPlaybackState to existing MediaSession variables since they may
+            // be called before the callback is called
+            mPlaybackState = mController.getPlaybackState();
+            mMetadata = mController.getMetadata();
+            updateDuration();
+            updateTitle();
+
+            mController.registerCallback(new MediaControllerCallback());
+        }
     }
 
     @Override
     public void show_impl() {
-        // TODO: Implement
+        mInstance.show(DEFAULT_TIMEOUT_MS);
     }
 
     @Override
     public void show_impl(int timeout) {
-        // TODO: Implement
+        if (!mShowing) {
+            setProgress();
+            if (mPlayPauseButton != null) {
+                mPlayPauseButton.requestFocus();
+            }
+            disableUnsupportedButtons();
+            mInstance.setVisibility(View.VISIBLE);
+            mShowing = true;
+        }
+        // cause the progress bar to be updated even if mShowing
+        // was already true.  This happens, for example, if we're
+        // paused with the progress bar showing the user hits play.
+        mInstance.post(mShowProgress);
+
+        if (timeout != 0 && !mAccessibilityManager.isTouchExplorationEnabled()) {
+            mInstance.removeCallbacks(mFadeOut);
+            mInstance.postDelayed(mFadeOut, timeout);
+        }
     }
 
     @Override
     public boolean isShowing_impl() {
-        // TODO: Implement
-        return false;
+        return mShowing;
     }
 
     @Override
     public void hide_impl() {
-        // TODO: Implement
-    }
-
-    @Override
-    public void showCCButton_impl() {
-        // TODO: Implement
-    }
-
-    @Override
-    public boolean isPlaying_impl() {
-        // TODO: Implement
-        return false;
-    }
-
-    @Override
-    public int getCurrentPosition_impl() {
-        // TODO: Implement
-        return 0;
-    }
-
-    @Override
-    public int getBufferPercentage_impl() {
-        // TODO: Implement
-        return 0;
-    }
-
-    @Override
-    public boolean canPause_impl() {
-        // TODO: Implement
-        return false;
-    }
-
-    @Override
-    public boolean canSeekBackward_impl() {
-        // TODO: Implement
-        return false;
-    }
-
-    @Override
-    public boolean canSeekForward_impl() {
-        // TODO: Implement
-        return false;
+        if (mShowing) {
+            try {
+                mInstance.removeCallbacks(mShowProgress);
+                // Remove existing call to mFadeOut to avoid from being called later.
+                mInstance.removeCallbacks(mFadeOut);
+                mInstance.setVisibility(View.GONE);
+            } catch (IllegalArgumentException ex) {
+                Log.w(TAG, "already removed");
+            }
+            mShowing = false;
+        }
     }
 
     @Override
     public void showSubtitle_impl() {
-        // TODO: Implement
+        mController.sendCommand(COMMAND_SHOW_SUBTITLE, null, null);
     }
 
     @Override
     public void hideSubtitle_impl() {
-        // TODO: Implement
+        mController.sendCommand(COMMAND_HIDE_SUBTITLE, null, null);
+    }
+
+    @Override
+    public void setPrevNextListeners_impl(View.OnClickListener next, View.OnClickListener prev) {
+        mNextListener = next;
+        mPrevListener = prev;
+
+        if (mNextButton != null) {
+            mNextButton.setOnClickListener(mNextListener);
+            mNextButton.setEnabled(mNextListener != null);
+            mNextButton.setVisibility(View.VISIBLE);
+        }
+        if (mPrevButton != null) {
+            mPrevButton.setOnClickListener(mPrevListener);
+            mPrevButton.setEnabled(mPrevListener != null);
+            mPrevButton.setVisibility(View.VISIBLE);
+        }
+    }
+
+    @Override
+    public void setButtonVisibility_impl(int button, boolean visible) {
+        switch (button) {
+            case MediaControlView2.BUTTON_PLAY_PAUSE:
+                if (mPlayPauseButton != null && canPause()) {
+                    mPlayPauseButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            case MediaControlView2.BUTTON_FFWD:
+                if (mFfwdButton != null && canSeekForward()) {
+                    mFfwdButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            case MediaControlView2.BUTTON_REW:
+                if (mRewButton != null && canSeekBackward()) {
+                    mRewButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            case MediaControlView2.BUTTON_NEXT:
+                // TODO: this button is not visible unless its listener is manually set. Should this
+                // function still be provided?
+                if (mNextButton != null) {
+                    mNextButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            case MediaControlView2.BUTTON_PREV:
+                // TODO: this button is not visible unless its listener is manually set. Should this
+                // function still be provided?
+                if (mPrevButton != null) {
+                    mPrevButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            case MediaControlView2.BUTTON_SUBTITLE:
+                if (mSubtitleButton != null && mContainsSubtitle) {
+                    mSubtitleButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            case MediaControlView2.BUTTON_FULL_SCREEN:
+                if (mFullScreenButton != null) {
+                    mFullScreenButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            case MediaControlView2.BUTTON_OVERFLOW:
+                if (mOverflowButtonRight != null) {
+                    mOverflowButtonRight.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            case MediaControlView2.BUTTON_MUTE:
+                if (mMuteButton != null) {
+                    mMuteButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            case MediaControlView2.BUTTON_ASPECT_RATIO:
+                if (mAspectRationButton != null) {
+                    mAspectRationButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            case MediaControlView2.BUTTON_SETTINGS:
+                if (mSettingsButton != null) {
+                    mSettingsButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void onAttachedToWindow_impl() {
+        mSuperProvider.onAttachedToWindow_impl();
+    }
+
+    @Override
+    public void onDetachedFromWindow_impl() {
+        mSuperProvider.onDetachedFromWindow_impl();
     }
 
     @Override
     public CharSequence getAccessibilityClassName_impl() {
-        // TODO: Implement
         return MediaControlView2.class.getName();
     }
 
     @Override
     public boolean onTouchEvent_impl(MotionEvent ev) {
-        // TODO: Implement
-        return mSuperProvider.onTouchEvent_impl(ev);
+        return false;
     }
 
+    // TODO: Should this function be removed?
     @Override
     public boolean onTrackballEvent_impl(MotionEvent ev) {
-        // TODO: Implement
-        return mSuperProvider.onTrackballEvent_impl(ev);
+        mInstance.show(DEFAULT_TIMEOUT_MS);
+        return false;
     }
 
     @Override
     public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
-        // TODO: Implement
         return mSuperProvider.onKeyDown_impl(keyCode, event);
     }
 
     @Override
     public void onFinishInflate_impl() {
         mSuperProvider.onFinishInflate_impl();
-        // TODO: Implement
     }
 
     @Override
     public boolean dispatchKeyEvent_impl(KeyEvent event) {
-        // TODO: Implement
+        int keyCode = event.getKeyCode();
+        final boolean uniqueDown = event.getRepeatCount() == 0
+                && event.getAction() == KeyEvent.ACTION_DOWN;
+        if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
+                || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                || keyCode == KeyEvent.KEYCODE_SPACE) {
+            if (uniqueDown) {
+                togglePausePlayState();
+                mInstance.show(DEFAULT_TIMEOUT_MS);
+                if (mPlayPauseButton != null) {
+                    mPlayPauseButton.requestFocus();
+                }
+            }
+            return true;
+        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
+            if (uniqueDown && !isPlaying()) {
+                togglePausePlayState();
+                mInstance.show(DEFAULT_TIMEOUT_MS);
+            }
+            return true;
+        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
+                || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
+            if (uniqueDown && isPlaying()) {
+                togglePausePlayState();
+                mInstance.show(DEFAULT_TIMEOUT_MS);
+            }
+            return true;
+        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+                || keyCode == KeyEvent.KEYCODE_VOLUME_UP
+                || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE
+                || keyCode == KeyEvent.KEYCODE_CAMERA) {
+            // don't show the controls for volume adjustment
+            return mSuperProvider.dispatchKeyEvent_impl(event);
+        } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
+            if (uniqueDown) {
+                mInstance.hide();
+            }
+            return true;
+        }
+
+        mInstance.show(DEFAULT_TIMEOUT_MS);
         return mSuperProvider.dispatchKeyEvent_impl(event);
     }
 
     @Override
     public void setEnabled_impl(boolean enabled) {
+        if (mPlayPauseButton != null) {
+            mPlayPauseButton.setEnabled(enabled);
+        }
+        if (mFfwdButton != null) {
+            mFfwdButton.setEnabled(enabled);
+        }
+        if (mRewButton != null) {
+            mRewButton.setEnabled(enabled);
+        }
+        if (mNextButton != null) {
+            mNextButton.setEnabled(enabled);
+        }
+        if (mPrevButton != null) {
+            mPrevButton.setEnabled(enabled);
+        }
+        if (mProgress != null) {
+            mProgress.setEnabled(enabled);
+        }
+        disableUnsupportedButtons();
         mSuperProvider.setEnabled_impl(enabled);
-        // TODO: Implement
+    }
+
+    ///////////////////////////////////////////////////
+    // Protected or private methods
+    ///////////////////////////////////////////////////
+
+    private boolean isPlaying() {
+        if (mPlaybackState != null) {
+            return mPlaybackState.getState() == PlaybackState.STATE_PLAYING;
+        }
+        return false;
+    }
+
+    private int getCurrentPosition() {
+        mPlaybackState = mController.getPlaybackState();
+        if (mPlaybackState != null) {
+            return (int) mPlaybackState.getPosition();
+        }
+        return 0;
+    }
+
+    private int getBufferPercentage() {
+        if (mDuration == 0) {
+            return 0;
+        }
+        mPlaybackState = mController.getPlaybackState();
+        if (mPlaybackState != null) {
+            return (int) (mPlaybackState.getBufferedPosition() * 100) / mDuration;
+        }
+        return 0;
+    }
+
+    private boolean canPause() {
+        if (mPlaybackState != null) {
+            return (mPlaybackState.getActions() & PlaybackState.ACTION_PAUSE) != 0;
+        }
+        return true;
+    }
+
+    private boolean canSeekBackward() {
+        if (mPlaybackState != null) {
+            return (mPlaybackState.getActions() & PlaybackState.ACTION_REWIND) != 0;
+        }
+        return true;
+    }
+
+    private boolean canSeekForward() {
+        if (mPlaybackState != null) {
+            return (mPlaybackState.getActions() & PlaybackState.ACTION_FAST_FORWARD) != 0;
+        }
+        return true;
+    }
+
+    /**
+     * Create the view that holds the widgets that control playback.
+     * Derived classes can override this to create their own.
+     *
+     * @return The controller view.
+     * @hide This doesn't work as advertised
+     */
+    protected View makeControllerView() {
+        View root = ApiHelper.inflateLibLayout(mInstance.getContext(), R.layout.media_controller);
+        initControllerView(root);
+        return root;
+    }
+
+    private void initControllerView(View v) {
+        Resources res = ApiHelper.getLibResources();
+        mPlayDescription = res.getText(R.string.lockscreen_play_button_content_description);
+        mPauseDescription = res.getText(R.string.lockscreen_pause_button_content_description);
+        mReplayDescription = res.getText(R.string.lockscreen_replay_button_content_description);
+
+        mPlayPauseButton = v.findViewById(R.id.pause);
+        if (mPlayPauseButton != null) {
+            mPlayPauseButton.requestFocus();
+            mPlayPauseButton.setOnClickListener(mPlayPauseListener);
+            mPlayPauseButton.setColorFilter(R.integer.gray);
+            mPlayPauseButton.setEnabled(false);
+        }
+        mFfwdButton = v.findViewById(R.id.ffwd);
+        if (mFfwdButton != null) {
+            mFfwdButton.setOnClickListener(mFfwdListener);
+            mFfwdButton.setColorFilter(R.integer.gray);
+            mFfwdButton.setEnabled(false);
+        }
+        mRewButton = v.findViewById(R.id.rew);
+        if (mRewButton != null) {
+            mRewButton.setOnClickListener(mRewListener);
+            mRewButton.setColorFilter(R.integer.gray);
+            mRewButton.setEnabled(false);
+        }
+        mNextButton = v.findViewById(R.id.next);
+        if (mNextButton != null) {
+            mNextButton.setVisibility(View.GONE);
+        }
+        mPrevButton = v.findViewById(R.id.prev);
+        if (mPrevButton != null) {
+            mPrevButton.setVisibility(View.GONE);
+        }
+
+        mBasicControls = v.findViewById(R.id.basic_controls);
+        mSubtitleButton = v.findViewById(R.id.subtitle);
+        if (mSubtitleButton != null) {
+            mSubtitleButton.setOnClickListener(mSubtitleListener);
+            mSubtitleButton.setColorFilter(R.integer.gray);
+            mSubtitleButton.setEnabled(false);
+        }
+        mFullScreenButton = v.findViewById(R.id.fullscreen);
+        if (mFullScreenButton != null) {
+            mFullScreenButton.setOnClickListener(mFullScreenListener);
+            // TODO: Show Fullscreen button when only it is possible.
+        }
+        mOverflowButtonRight = v.findViewById(R.id.overflow_right);
+        if (mOverflowButtonRight != null) {
+            mOverflowButtonRight.setOnClickListener(mOverflowRightListener);
+        }
+
+        // TODO: should these buttons be shown as default?
+        mExtraControls = v.findViewById(R.id.extra_controls);
+        mOverflowButtonLeft = v.findViewById(R.id.overflow_left);
+        if (mOverflowButtonLeft != null) {
+            mOverflowButtonLeft.setOnClickListener(mOverflowLeftListener);
+        }
+        mMuteButton = v.findViewById(R.id.mute);
+        mAspectRationButton = v.findViewById(R.id.aspect_ratio);
+        mSettingsButton = v.findViewById(R.id.settings);
+
+        mProgress = v.findViewById(R.id.mediacontroller_progress);
+        if (mProgress != null) {
+            if (mProgress instanceof SeekBar) {
+                SeekBar seeker = (SeekBar) mProgress;
+                seeker.setOnSeekBarChangeListener(mSeekListener);
+            }
+            mProgress.setMax(MAX_PROGRESS);
+        }
+
+        mTitleView = v.findViewById(R.id.title_text);
+
+        mEndTime = v.findViewById(R.id.time);
+        mCurrentTime = v.findViewById(R.id.time_current);
+        mFormatBuilder = new StringBuilder();
+        mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
+    }
+
+    /**
+     * Disable pause or seek buttons if the stream cannot be paused or seeked.
+     * This requires the control interface to be a MediaPlayerControlExt
+     */
+    private void disableUnsupportedButtons() {
+        try {
+            if (mPlayPauseButton != null && !canPause()) {
+                mPlayPauseButton.setEnabled(false);
+            }
+            if (mRewButton != null && !canSeekBackward()) {
+                mRewButton.setEnabled(false);
+            }
+            if (mFfwdButton != null && !canSeekForward()) {
+                mFfwdButton.setEnabled(false);
+            }
+            // TODO What we really should do is add a canSeek to the MediaPlayerControl interface;
+            // this scheme can break the case when applications want to allow seek through the
+            // progress bar but disable forward/backward buttons.
+            //
+            // However, currently the flags SEEK_BACKWARD_AVAILABLE, SEEK_FORWARD_AVAILABLE,
+            // and SEEK_AVAILABLE are all (un)set together; as such the aforementioned issue
+            // shouldn't arise in existing applications.
+            if (mProgress != null && !canSeekBackward() && !canSeekForward()) {
+                mProgress.setEnabled(false);
+            }
+        } catch (IncompatibleClassChangeError ex) {
+            // We were given an old version of the interface, that doesn't have
+            // the canPause/canSeekXYZ methods. This is OK, it just means we
+            // assume the media can be paused and seeked, and so we don't disable
+            // the buttons.
+        }
+    }
+
+    private final Runnable mFadeOut = new Runnable() {
+        @Override
+        public void run() {
+            if (isPlaying()) {
+                mInstance.hide();
+            }
+        }
+    };
+
+    private final Runnable mShowProgress = new Runnable() {
+        @Override
+        public void run() {
+            int pos = setProgress();
+            if (!mDragging && mShowing && isPlaying()) {
+                mInstance.postDelayed(mShowProgress,
+                        DEFAULT_PROGRESS_UPDATE_TIME_MS - (pos % DEFAULT_PROGRESS_UPDATE_TIME_MS));
+            }
+        }
+    };
+
+    private String stringForTime(int timeMs) {
+        int totalSeconds = timeMs / 1000;
+
+        int seconds = totalSeconds % 60;
+        int minutes = (totalSeconds / 60) % 60;
+        int hours = totalSeconds / 3600;
+
+        mFormatBuilder.setLength(0);
+        if (hours > 0) {
+            return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
+        } else {
+            return mFormatter.format("%02d:%02d", minutes, seconds).toString();
+        }
+    }
+
+    private int setProgress() {
+        if (mController == null || mDragging) {
+            return 0;
+        }
+        int positionOnProgressBar = 0;
+        int currentPosition = getCurrentPosition();
+        if (mDuration > 0) {
+            positionOnProgressBar = (int) (MAX_PROGRESS * (long) currentPosition / mDuration);
+        }
+        if (mProgress != null && currentPosition != mDuration) {
+            mProgress.setProgress(positionOnProgressBar);
+            mProgress.setSecondaryProgress(getBufferPercentage() * 10);
+        }
+
+        if (mEndTime != null) {
+            mEndTime.setText(stringForTime(mDuration));
+
+        }
+        if (mCurrentTime != null) {
+            mCurrentTime.setText(stringForTime(currentPosition));
+        }
+
+        return currentPosition;
+    }
+
+    private void togglePausePlayState() {
+        if (isPlaying()) {
+            mControls.pause();
+            mPlayPauseButton.setImageDrawable(
+                    ApiHelper.getLibResources().getDrawable(
+                            R.drawable.ic_play_circle_filled, null));
+            mPlayPauseButton.setContentDescription(mPlayDescription);
+        } else {
+            mControls.play();
+            mPlayPauseButton.setImageDrawable(
+                    ApiHelper.getLibResources().getDrawable(
+                            R.drawable.ic_pause_circle_filled, null));
+            mPlayPauseButton.setContentDescription(mPauseDescription);
+        }
+    }
+
+    // There are two scenarios that can trigger the seekbar listener to trigger:
+    //
+    // The first is the user using the touchpad to adjust the posititon of the
+    // seekbar's thumb. In this case onStartTrackingTouch is called followed by
+    // a number of onProgressChanged notifications, concluded by onStopTrackingTouch.
+    // We're setting the field "mDragging" to true for the duration of the dragging
+    // session to avoid jumps in the position in case of ongoing playback.
+    //
+    // The second scenario involves the user operating the scroll ball, in this
+    // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications,
+    // we will simply apply the updated position without suspending regular updates.
+    private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
+        @Override
+        public void onStartTrackingTouch(SeekBar bar) {
+            if (!mSeekAvailable) {
+                return;
+            }
+            mInstance.show(3600000);
+
+            mDragging = true;
+
+            // By removing these pending progress messages we make sure
+            // that a) we won't update the progress while the user adjusts
+            // the seekbar and b) once the user is done dragging the thumb
+            // we will post one of these messages to the queue again and
+            // this ensures that there will be exactly one message queued up.
+            mInstance.removeCallbacks(mShowProgress);
+
+            // Check if playback is currently stopped. In this case, update the pause button to
+            // show the play image instead of the replay image.
+            if (mIsStopped) {
+                mPlayPauseButton.setImageDrawable(
+                        ApiHelper.getLibResources().getDrawable(
+                                R.drawable.ic_play_circle_filled, null));
+                mPlayPauseButton.setContentDescription(mPlayDescription);
+                mIsStopped = false;
+            }
+        }
+
+        @Override
+        public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) {
+            if (!mSeekAvailable) {
+                return;
+            }
+            if (!fromUser) {
+                // We're not interested in programmatically generated changes to
+                // the progress bar's position.
+                return;
+            }
+            if (mDuration > 0) {
+                int newPosition = (int) (((long) mDuration * progress) / MAX_PROGRESS);
+                mControls.seekTo(newPosition);
+
+                if (mCurrentTime != null) {
+                    mCurrentTime.setText(stringForTime(newPosition));
+                }
+            }
+        }
+
+        @Override
+        public void onStopTrackingTouch(SeekBar bar) {
+            if (!mSeekAvailable) {
+                return;
+            }
+            mDragging = false;
+
+            setProgress();
+            mInstance.show(DEFAULT_TIMEOUT_MS);
+
+            // Ensure that progress is properly updated in the future,
+            // the call to show() does not guarantee this because it is a
+            // no-op if we are already showing.
+            mInstance.post(mShowProgress);
+        }
+    };
+
+    private final View.OnClickListener mPlayPauseListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            togglePausePlayState();
+            mInstance.show(DEFAULT_TIMEOUT_MS);
+        }
+    };
+
+    private final View.OnClickListener mRewListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            int pos = getCurrentPosition() - REWIND_TIME_MS;
+            mControls.seekTo(pos);
+            setProgress();
+
+            mInstance.show(DEFAULT_TIMEOUT_MS);
+        }
+    };
+
+    private final View.OnClickListener mFfwdListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            int pos = getCurrentPosition() + FORWARD_TIME_MS;
+            mControls.seekTo(pos);
+            setProgress();
+
+            mInstance.show(DEFAULT_TIMEOUT_MS);
+        }
+    };
+
+    private final View.OnClickListener mSubtitleListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            if (!mSubtitleIsEnabled) {
+                mSubtitleButton.setImageDrawable(
+                        ApiHelper.getLibResources().getDrawable(
+                                R.drawable.ic_media_subtitle_enabled, null));
+                mInstance.showSubtitle();
+                mSubtitleIsEnabled = true;
+            } else {
+                mSubtitleButton.setImageDrawable(
+                        ApiHelper.getLibResources().getDrawable(
+                                R.drawable.ic_media_subtitle_disabled, null));
+                mInstance.hideSubtitle();
+                mSubtitleIsEnabled = false;
+            }
+            mInstance.show(DEFAULT_TIMEOUT_MS);
+        }
+    };
+
+    private final View.OnClickListener mFullScreenListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            final boolean isEnteringFullScreen = !mIsFullScreen;
+            // TODO: Re-arrange the button layouts according to the UX.
+            if (isEnteringFullScreen) {
+                mFullScreenButton.setImageDrawable(
+                        ApiHelper.getLibResources().getDrawable(
+                                R.drawable.ic_fullscreen_exit, null));
+            } else {
+                mFullScreenButton.setImageDrawable(
+                        ApiHelper.getLibResources().getDrawable(R.drawable.ic_fullscreen, null));
+            }
+            Bundle args = new Bundle();
+            args.putBoolean(ARGUMENT_KEY_FULLSCREEN, isEnteringFullScreen);
+            mController.sendCommand(COMMAND_SET_FULLSCREEN, args, null);
+
+            mIsFullScreen = isEnteringFullScreen;
+            mInstance.show(DEFAULT_TIMEOUT_MS);
+        }
+    };
+
+    private final View.OnClickListener mOverflowRightListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            mBasicControls.setVisibility(View.GONE);
+            mExtraControls.setVisibility(View.VISIBLE);
+            mInstance.show(DEFAULT_TIMEOUT_MS);
+        }
+    };
+
+    private final View.OnClickListener mOverflowLeftListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            mBasicControls.setVisibility(View.VISIBLE);
+            mExtraControls.setVisibility(View.GONE);
+        }
+    };
+
+    private void updateDuration() {
+        if (mMetadata != null) {
+            if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
+                mDuration = (int) mMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
+                // update progress bar
+                setProgress();
+            }
+        }
+    }
+
+    private void updateTitle() {
+        if (mMetadata != null) {
+            if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_TITLE)) {
+                mTitleView.setText(mMetadata.getString(MediaMetadata.METADATA_KEY_TITLE));
+            }
+        }
+    }
+
+    private class MediaControllerCallback extends MediaController.Callback {
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            mPlaybackState = state;
+
+            // Update pause button depending on playback state for the following two reasons:
+            //   1) Need to handle case where app customizes playback state behavior when app
+            //      activity is resumed.
+            //   2) Need to handle case where the media file reaches end of duration.
+            if (mPlaybackState.getState() != mPrevState) {
+                switch (mPlaybackState.getState()) {
+                    case PlaybackState.STATE_PLAYING:
+                        mPlayPauseButton.setImageDrawable(
+                                ApiHelper.getLibResources().getDrawable(
+                                        R.drawable.ic_pause_circle_filled, null));
+                        mPlayPauseButton.setContentDescription(mPauseDescription);
+                        break;
+                    case PlaybackState.STATE_PAUSED:
+                        mPlayPauseButton.setImageDrawable(
+                                ApiHelper.getLibResources().getDrawable(
+                                        R.drawable.ic_play_circle_filled, null));
+                        mPlayPauseButton.setContentDescription(mPlayDescription);
+                        break;
+                    case PlaybackState.STATE_STOPPED:
+                        mPlayPauseButton.setImageDrawable(
+                                ApiHelper.getLibResources().getDrawable(
+                                        R.drawable.ic_replay, null));
+                        mPlayPauseButton.setContentDescription(mReplayDescription);
+                        mIsStopped = true;
+                        break;
+                    default:
+                        break;
+                }
+                mPrevState = mPlaybackState.getState();
+            }
+
+            if (mPlaybackActions != mPlaybackState.getActions()) {
+                long newActions = mPlaybackState.getActions();
+                if ((newActions & PlaybackState.ACTION_PAUSE) != 0) {
+                    mPlayPauseButton.clearColorFilter();
+                    mPlayPauseButton.setEnabled(true);
+                }
+                if ((newActions & PlaybackState.ACTION_REWIND) != 0) {
+                    mRewButton.clearColorFilter();
+                    mRewButton.setEnabled(true);
+                }
+                if ((newActions & PlaybackState.ACTION_FAST_FORWARD) != 0) {
+                    mFfwdButton.clearColorFilter();
+                    mFfwdButton.setEnabled(true);
+                }
+                if ((newActions & PlaybackState.ACTION_SEEK_TO) != 0) {
+                    mSeekAvailable = true;
+                } else {
+                    mSeekAvailable = false;
+                }
+                mPlaybackActions = newActions;
+            }
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            mMetadata = metadata;
+            updateDuration();
+            updateTitle();
+        }
+
+        @Override
+        public void onSessionEvent(String event, Bundle extras) {
+            if (event.equals(EVENT_UPDATE_SUBTITLE_STATUS)) {
+                boolean newSubtitleStatus = extras.getBoolean(KEY_STATE_CONTAINS_SUBTITLE);
+                if (newSubtitleStatus != mContainsSubtitle) {
+                    if (newSubtitleStatus) {
+                        mSubtitleButton.clearColorFilter();
+                        mSubtitleButton.setEnabled(true);
+                    } else {
+                        mSubtitleButton.setColorFilter(R.integer.gray);
+                        mSubtitleButton.setEnabled(false);
+                    }
+                    mContainsSubtitle = newSubtitleStatus;
+                }
+            }
+        }
     }
 }
diff --git a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
index 800be8e..8577123 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
@@ -162,57 +162,22 @@
         int height = getDefaultSize(videoHeight, heightMeasureSpec);
 
         if (videoWidth > 0 && videoHeight > 0) {
-            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
             int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
-            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
             int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
 
-            if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
-                // the size is fixed
-                width = widthSpecSize;
-                height = heightSpecSize;
+            width = widthSpecSize;
+            height = heightSpecSize;
 
-                // for compatibility, we adjust size based on aspect ratio
-                if (videoWidth * height < width * videoHeight) {
-                    if (DEBUG) {
-                        Log.d(TAG, "image too wide, correcting");
-                    }
-                    width = height * videoWidth / videoHeight;
-                } else if (videoWidth * height > width * videoHeight) {
-                    if (DEBUG) {
-                        Log.d(TAG, "image too tall, correcting");
-                    }
-                    height = width * videoHeight / videoWidth;
-                }
-            } else if (widthSpecMode == MeasureSpec.EXACTLY) {
-                // only the width is fixed, adjust the height to match aspect ratio if possible
-                width = widthSpecSize;
-                height = width * videoHeight / videoWidth;
-                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
-                    // couldn't match aspect ratio within the constraints
-                    height = heightSpecSize;
-                }
-            } else if (heightSpecMode == MeasureSpec.EXACTLY) {
-                // only the height is fixed, adjust the width to match aspect ratio if possible
-                height = heightSpecSize;
+            // for compatibility, we adjust size based on aspect ratio
+            if (videoWidth * height < width * videoHeight) {
                 width = height * videoWidth / videoHeight;
-                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
-                    // couldn't match aspect ratio within the constraints
-                    width = widthSpecSize;
+                if (DEBUG) {
+                    Log.d(TAG, "image too wide, correcting. width: " + width);
                 }
-            } else {
-                // neither the width nor the height are fixed, try to use actual video size
-                width = videoWidth;
-                height = videoHeight;
-                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
-                    // too tall, decrease both width and height
-                    height = heightSpecSize;
-                    width = height * videoWidth / videoHeight;
-                }
-                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
-                    // too wide, decrease both width and height
-                    width = widthSpecSize;
-                    height = width * videoHeight / videoWidth;
+            } else if (videoWidth * height > width * videoHeight) {
+                height = width * videoHeight / videoWidth;
+                if (DEBUG) {
+                    Log.d(TAG, "image too tall, correcting. height: " + height);
                 }
             }
         } else {
diff --git a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
index f240301..69a4ebe 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
@@ -173,57 +173,22 @@
         int height = getDefaultSize(videoHeight, heightMeasureSpec);
 
         if (videoWidth > 0 && videoHeight > 0) {
-            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
             int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
-            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
             int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
 
-            if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
-                // the size is fixed
-                width = widthSpecSize;
-                height = heightSpecSize;
+            width = widthSpecSize;
+            height = heightSpecSize;
 
-                // for compatibility, we adjust size based on aspect ratio
-                if (videoWidth * height  < width * videoHeight) {
-                    if (DEBUG) {
-                        Log.d(TAG, "image too wide, correcting");
-                    }
-                    width = height * videoWidth / videoHeight;
-                } else if (videoWidth * height  > width * videoHeight) {
-                    if (DEBUG) {
-                        Log.d(TAG, "image too tall, correcting");
-                    }
-                    height = width * videoHeight / videoWidth;
-                }
-            } else if (widthSpecMode == MeasureSpec.EXACTLY) {
-                // only the width is fixed, adjust the height to match aspect ratio if possible
-                width = widthSpecSize;
-                height = width * videoHeight / videoWidth;
-                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
-                    // couldn't match aspect ratio within the constraints
-                    height = heightSpecSize;
-                }
-            } else if (heightSpecMode == MeasureSpec.EXACTLY) {
-                // only the height is fixed, adjust the width to match aspect ratio if possible
-                height = heightSpecSize;
+            // for compatibility, we adjust size based on aspect ratio
+            if (videoWidth * height < width * videoHeight) {
                 width = height * videoWidth / videoHeight;
-                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
-                    // couldn't match aspect ratio within the constraints
-                    width = widthSpecSize;
+                if (DEBUG) {
+                    Log.d(TAG, "image too wide, correcting. width: " + width);
                 }
-            } else {
-                // neither the width nor the height are fixed, try to use actual video size
-                width = videoWidth;
-                height = videoHeight;
-                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
-                    // too tall, decrease both width and height
-                    height = heightSpecSize;
-                    width = height * videoWidth / videoHeight;
-                }
-                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
-                    // too wide, decrease both width and height
-                    width = widthSpecSize;
-                    height = width * videoHeight / videoWidth;
+            } else if (videoWidth * height > width * videoHeight) {
+                height = width * videoHeight / videoWidth;
+                if (DEBUG) {
+                    Log.d(TAG, "image too tall, correcting. height: " + height);
                 }
             }
         } else {
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 19a41de..4c312f8 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -25,15 +25,18 @@
 import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.MediaPlayer;
+import android.media.MediaPlayerBase;
 import android.media.Cea708CaptionRenderer;
 import android.media.ClosedCaptionRenderer;
+import android.media.MediaMetadata;
+import android.media.MediaPlayer;
 import android.media.Metadata;
 import android.media.PlaybackParams;
 import android.media.SubtitleController;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
 import android.media.TtmlRenderer;
 import android.media.WebVttRenderer;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
 import android.media.update.VideoView2Provider;
 import android.media.update.ViewProvider;
 import android.net.Uri;
@@ -50,14 +53,14 @@
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
 
+import com.android.media.update.ApiHelper;
+import com.android.media.update.R;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
-import com.android.media.update.ApiHelper;
-import com.android.media.update.R;
-
 public class VideoView2Impl implements VideoView2Provider, VideoViewInterface.SurfaceListener {
     private static final String TAG = "VideoView2";
     private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG);
@@ -83,6 +86,7 @@
     private VideoView2.OnErrorListener mOnErrorListener;
     private VideoView2.OnInfoListener mOnInfoListener;
     private VideoView2.OnViewTypeChangedListener mOnViewTypeChangedListener;
+    private VideoView2.OnFullScreenChangedListener mOnFullScreenChangedListener;
 
     private VideoViewInterface mCurrentView;
     private VideoTextureView mTextureView;
@@ -91,6 +95,8 @@
     private MediaPlayer mMediaPlayer;
     private MediaControlView2 mMediaControlView;
     private MediaSession mMediaSession;
+    private Metadata mMetadata;
+    private String mTitle;
 
     private PlaybackState.Builder mStateBuilder;
     private int mTargetState = STATE_IDLE;
@@ -98,8 +104,6 @@
     private int mCurrentBufferPercentage;
     private int mSeekWhenPrepared;  // recording the seek position while preparing
 
-    private int mSurfaceWidth;
-    private int mSurfaceHeight;
     private int mVideoWidth;
     private int mVideoHeight;
 
@@ -112,16 +116,13 @@
     // Refer: https://docs.google.com/document/d/1nzAfns6i2hJ3RkaUre3QMT6wsDedJ5ONLiA_OOBFFX8/edit
     private float mFallbackSpeed;  // keep the original speed before 'pause' is called.
 
-    public VideoView2Impl(
-            VideoView2 instance, ViewProvider superProvider,
+    public VideoView2Impl(VideoView2 instance, ViewProvider superProvider,
             @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         mInstance = instance;
         mSuperProvider = superProvider;
 
         mVideoWidth = 0;
         mVideoHeight = 0;
-        mSurfaceWidth = 0;
-        mSurfaceHeight = 0;
         mSpeed = 1.0f;
         mFallbackSpeed = mSpeed;
 
@@ -137,7 +138,8 @@
         mTextureView = new VideoTextureView(mInstance.getContext());
         mSurfaceView = new VideoSurfaceView(mInstance.getContext());
         LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
-                LayoutParams.WRAP_CONTENT);
+                LayoutParams.MATCH_PARENT);
+        params.gravity = Gravity.CENTER;
         mTextureView.setLayoutParams(params);
         mSurfaceView.setLayoutParams(params);
         mTextureView.setSurfaceListener(this);
@@ -158,16 +160,12 @@
         mSubtitleView.setBackgroundColor(0);
         mInstance.addView(mSubtitleView);
 
-        // Create MediaSession
-        mMediaSession = new MediaSession(mInstance.getContext(), "VideoView2MediaSession");
-        mMediaSession.setCallback(new MediaSessionCallback());
-
         // TODO: Need a common namespace for attributes those are defined in updatable library.
         boolean enableControlView = (attrs == null) || attrs.getAttributeBooleanValue(
-                "http://schemas.android.com/apk/com.android.media.api_provider",
+                "http://schemas.android.com/apk/com.android.media.update",
                 "enableControlView", true);
         if (enableControlView) {
-            setMediaControlView2_impl(new MediaControlView2(mInstance.getContext()));
+            mMediaControlView = new MediaControlView2(mInstance.getContext());
         }
     }
 
@@ -175,15 +173,9 @@
     public void setMediaControlView2_impl(MediaControlView2 mediaControlView) {
         mMediaControlView = mediaControlView;
 
-        // TODO: change this so that the CC button appears only where there is a subtitle track.
-        mMediaControlView.showCCButton();
-
-        // Get MediaController from MediaSession and set it inside MediaControlView2
-        mMediaControlView.setController(mMediaSession.getController());
-
-        LayoutParams params =
-                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-        mInstance.addView(mMediaControlView, params);
+        if (mInstance.isAttachedToWindow()) {
+            attachMediaControlView();
+        }
     }
 
     @Override
@@ -298,6 +290,13 @@
     }
 
     @Override
+    public void setFullScreen_impl(boolean fullScreen) {
+        if (mOnFullScreenChangedListener != null) {
+            mOnFullScreenChangedListener.onFullScreenChanged(fullScreen);
+        }
+    }
+
+    @Override
     public void setSpeed_impl(float speed) {
         if (speed <= 0.0f) {
             Log.e(TAG, "Unsupported speed (" + speed + ") is ignored.");
@@ -344,6 +343,11 @@
     }
 
     @Override
+    public void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player) {
+        // TODO: implement this.
+    }
+
+    @Override
     public void setVideoPath_impl(String path) {
         mInstance.setVideoURI(Uri.parse(path));
     }
@@ -415,6 +419,29 @@
     }
 
     @Override
+    public void setFullScreenChangedListener_impl(VideoView2.OnFullScreenChangedListener l) {
+        mOnFullScreenChangedListener = l;
+    }
+
+    @Override
+    public void onAttachedToWindow_impl() {
+        mSuperProvider.onAttachedToWindow_impl();
+
+        // Create MediaSession
+        mMediaSession = new MediaSession(mInstance.getContext(), "VideoView2MediaSession");
+        mMediaSession.setCallback(new MediaSessionCallback());
+
+        attachMediaControlView();
+    }
+
+    @Override
+    public void onDetachedFromWindow_impl() {
+        mSuperProvider.onDetachedFromWindow_impl();
+        mMediaSession.release();
+        mMediaSession = null;
+    }
+
+    @Override
     public CharSequence getAccessibilityClassName_impl() {
         return VideoView2.class.getName();
     }
@@ -509,9 +536,6 @@
                     + ", mTargetState=" + mTargetState + ", width/height: " + width + "/" + height
                     + ", " + view.toString());
         }
-        mSurfaceWidth = width;
-        mSurfaceHeight = height;
-
         if (needToStart()) {
             mInstance.start();
         }
@@ -535,8 +559,6 @@
             Log.d(TAG, "onSurfaceChanged(). width/height: " + width + "/" + height
                     + ", " + view.toString());
         }
-        mSurfaceWidth = width;
-        mSurfaceHeight = height;
     }
 
     @Override
@@ -557,6 +579,15 @@
     // Protected or private methods
     ///////////////////////////////////////////////////
 
+    private void attachMediaControlView() {
+        // Get MediaController from MediaSession and set it inside MediaControlView
+        mMediaControlView.setController(mMediaSession.getController());
+
+        LayoutParams params =
+                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+        mInstance.addView(mMediaControlView, params);
+    }
+
     private boolean isInPlaybackState() {
         return (mMediaPlayer != null
                 && mCurrentState != STATE_ERROR
@@ -619,6 +650,13 @@
             mCurrentState = STATE_PREPARING;
             mMediaPlayer.prepareAsync();
 
+            // Save file name as title since the file may not have a title Metadata.
+            mTitle = uri.getPath();
+            String scheme = uri.getScheme();
+            if (scheme != null && scheme.equals("file")) {
+                mTitle = uri.getLastPathSegment();
+            }
+
             if (DEBUG) {
                 Log.d(TAG, "openVideo(). mCurrentState=" + mCurrentState
                         + ", mTargetState=" + mTargetState);
@@ -660,8 +698,6 @@
                 mAudioManager.abandonAudioFocus(null);
             }
         }
-        mSurfaceWidth = 0;
-        mSurfaceHeight = 0;
         mVideoWidth = 0;
         mVideoHeight = 0;
     }
@@ -669,26 +705,26 @@
     private void updatePlaybackState() {
         if (mStateBuilder == null) {
             // Get the capabilities of the player for this stream
-            Metadata data = mMediaPlayer.getMetadata(MediaPlayer.METADATA_ALL,
+            mMetadata = mMediaPlayer.getMetadata(MediaPlayer.METADATA_ALL,
                     MediaPlayer.BYPASS_METADATA_FILTER);
 
             // Add Play action as default
             long playbackActions = PlaybackState.ACTION_PLAY;
-            if (data != null) {
-                if (!data.has(Metadata.PAUSE_AVAILABLE)
-                        || data.getBoolean(Metadata.PAUSE_AVAILABLE)) {
+            if (mMetadata != null) {
+                if (!mMetadata.has(Metadata.PAUSE_AVAILABLE)
+                        || mMetadata.getBoolean(Metadata.PAUSE_AVAILABLE)) {
                     playbackActions |= PlaybackState.ACTION_PAUSE;
                 }
-                if (!data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
-                        || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE)) {
+                if (!mMetadata.has(Metadata.SEEK_BACKWARD_AVAILABLE)
+                        || mMetadata.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE)) {
                     playbackActions |= PlaybackState.ACTION_REWIND;
                 }
-                if (!data.has(Metadata.SEEK_FORWARD_AVAILABLE)
-                        || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE)) {
+                if (!mMetadata.has(Metadata.SEEK_FORWARD_AVAILABLE)
+                        || mMetadata.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE)) {
                     playbackActions |= PlaybackState.ACTION_FAST_FORWARD;
                 }
-                if (!data.has(Metadata.SEEK_AVAILABLE)
-                        || data.getBoolean(Metadata.SEEK_AVAILABLE)) {
+                if (!mMetadata.has(Metadata.SEEK_AVAILABLE)
+                        || mMetadata.getBoolean(Metadata.SEEK_AVAILABLE)) {
                     playbackActions |= PlaybackState.ACTION_SEEK_TO;
                 }
             } else {
@@ -698,8 +734,8 @@
             }
             mStateBuilder = new PlaybackState.Builder();
             mStateBuilder.setActions(playbackActions);
-            mStateBuilder.addCustomAction(MediaControlView2Impl.ACTION_SHOW_SUBTITLE, null, -1);
-            mStateBuilder.addCustomAction(MediaControlView2Impl.ACTION_HIDE_SUBTITLE, null, -1);
+            mStateBuilder.addCustomAction(MediaControlView2Impl.COMMAND_SHOW_SUBTITLE, null, -1);
+            mStateBuilder.addCustomAction(MediaControlView2Impl.COMMAND_HIDE_SUBTITLE, null, -1);
         }
         mStateBuilder.setState(getCorrespondingPlaybackState(),
                 mInstance.getCurrentPosition(), 1.0f);
@@ -722,7 +758,7 @@
             case STATE_PREPARING:
                 return PlaybackState.STATE_CONNECTING;
             case STATE_PREPARED:
-                return PlaybackState.STATE_STOPPED;
+                return PlaybackState.STATE_PAUSED;
             case STATE_PLAYING:
                 return PlaybackState.STATE_PLAYING;
             case STATE_PAUSED:
@@ -797,8 +833,8 @@
             if (mMediaControlView != null) {
                 mMediaControlView.setEnabled(true);
             }
-            mVideoWidth = mp.getVideoWidth();
-            mVideoHeight = mp.getVideoHeight();
+            int videoWidth = mp.getVideoWidth();
+            int videoHeight = mp.getVideoHeight();
 
             // mSeekWhenPrepared may be changed after seekTo() call
             int seekToPosition = mSeekWhenPrepared;
@@ -806,49 +842,33 @@
                 mInstance.seekTo(seekToPosition);
             }
 
-            // Create and set playback state for MediaControlView2
-            updatePlaybackState();
-
-            // Get and set duration value as MediaMetadata for MediaControlView2
-            MediaMetadata.Builder builder = new MediaMetadata.Builder();
-            builder.putLong(MediaMetadata.METADATA_KEY_DURATION, mInstance.getDuration());
-            if (mMediaSession != null) {
-                mMediaSession.setMetadata(builder.build());
-            }
-
-            if (mVideoWidth != 0 && mVideoHeight != 0) {
-                if (mVideoWidth != mSurfaceWidth || mVideoHeight != mSurfaceHeight) {
+            if (videoWidth != 0 && videoHeight != 0) {
+                if (videoWidth != mVideoWidth || videoHeight != mVideoHeight) {
                     if (DEBUG) {
                         Log.i(TAG, "OnPreparedListener() : ");
-                        Log.i(TAG, " video size: " + mVideoWidth + "/" + mVideoHeight);
-                        Log.i(TAG, " surface size: " + mSurfaceWidth + "/" + mSurfaceHeight);
+                        Log.i(TAG, " video size: " + videoWidth + "/" + videoHeight);
                         Log.i(TAG, " measuredSize: " + mInstance.getMeasuredWidth() + "/"
                                 + mInstance.getMeasuredHeight());
                         Log.i(TAG, " viewSize: " + mInstance.getWidth() + "/"
                                 + mInstance.getHeight());
                     }
 
-                    // TODO: It seems like that overriding onMeasure() is needed like legacy code.
-                    mSurfaceWidth = mVideoWidth;
-                    mSurfaceHeight = mVideoHeight;
+                    mVideoWidth = videoWidth;
+                    mVideoHeight = videoHeight;
                     mInstance.requestLayout();
                 }
-                if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {
-                    // We didn't actually change the size (it was already at the size
-                    // we need), so we won't get a "surface changed" callback, so
-                    // start the video here instead of in the callback.
-                    if (needToStart()) {
-                        mInstance.start();
-                        if (mMediaControlView != null) {
-                            mMediaControlView.show();
-                        }
-                    } else if (!mInstance.isPlaying() && (seekToPosition != 0
-                            || mInstance.getCurrentPosition() > 0)) {
-                        if (mMediaControlView != null) {
-                            // Show the media controls when we're paused into a video and
-                            // make them stick.
-                            mMediaControlView.show(0);
-                        }
+
+                if (needToStart()) {
+                    mInstance.start();
+                    if (mMediaControlView != null) {
+                        mMediaControlView.show();
+                    }
+                } else if (!mInstance.isPlaying() && (seekToPosition != 0
+                        || mInstance.getCurrentPosition() > 0)) {
+                    if (mMediaControlView != null) {
+                        // Show the media controls when we're paused into a video and
+                        // make them stick.
+                        mMediaControlView.show(0);
                     }
                 }
             } else {
@@ -858,6 +878,20 @@
                     mInstance.start();
                 }
             }
+            // Create and set playback state for MediaControlView2
+            updatePlaybackState();
+
+            // Get and set duration and title values as MediaMetadata for MediaControlView2
+            MediaMetadata.Builder builder = new MediaMetadata.Builder();
+            if (mMetadata != null && mMetadata.has(Metadata.TITLE)) {
+                mTitle = mMetadata.getString(Metadata.TITLE);
+            }
+            builder.putString(MediaMetadata.METADATA_KEY_TITLE, mTitle);
+            builder.putLong(MediaMetadata.METADATA_KEY_DURATION, mInstance.getDuration());
+
+            if (mMediaSession != null) {
+                mMediaSession.setMetadata(builder.build());
+            }
         }
     };
 
@@ -868,9 +902,6 @@
                     mTargetState = STATE_PLAYBACK_COMPLETED;
                     updatePlaybackState();
 
-                    if (mMediaControlView != null) {
-                        mMediaControlView.hide();
-                    }
                     if (mOnCompletionListener != null) {
                         mOnCompletionListener.onCompletion();
                     }
@@ -932,7 +963,7 @@
                                 .setPositiveButton(res.getString(R.string.VideoView2_error_button),
                                         new DialogInterface.OnClickListener() {
                                             public void onClick(DialogInterface dialog,
-                                                                int whichButton) {
+                                                    int whichButton) {
                                                 /* If we get here, there is no onError listener, so
                                                 * at least inform them that the video is over.
                                                 */
@@ -960,12 +991,16 @@
         @Override
         public void onCommand(String command, Bundle args, ResultReceiver receiver) {
             switch (command) {
-                case MediaControlView2Impl.ACTION_SHOW_SUBTITLE:
+                case MediaControlView2Impl.COMMAND_SHOW_SUBTITLE:
                     mInstance.showSubtitle();
                     break;
-                case MediaControlView2Impl.ACTION_HIDE_SUBTITLE:
+                case MediaControlView2Impl.COMMAND_HIDE_SUBTITLE:
                     mInstance.hideSubtitle();
                     break;
+                case MediaControlView2Impl.COMMAND_SET_FULLSCREEN:
+                    mInstance.setFullScreen(
+                            args.getBoolean(MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN));
+                    break;
             }
         }
 
diff --git a/packages/MediaComponents/test/Android.mk b/packages/MediaComponents/test/Android.mk
new file mode 100644
index 0000000..8703b9f
--- /dev/null
+++ b/packages/MediaComponents/test/Android.mk
@@ -0,0 +1,28 @@
+# Copyright 2018 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# TODO(jaewan): Copy this to the CTS as well
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    mockito-target-minus-junit4 \
+    compatibility-device-util
+
+LOCAL_PACKAGE_NAME := MediaComponentsTest
+include $(BUILD_PACKAGE)
diff --git a/packages/MediaComponents/test/AndroidManifest.xml b/packages/MediaComponents/test/AndroidManifest.xml
new file mode 100644
index 0000000..30bac87
--- /dev/null
+++ b/packages/MediaComponents/test/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.media.test">
+
+    <application android:label="Media API Test">
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="android.widget2.VideoView2TestActivity"
+                  android:configChanges="keyboardHidden|orientation|screenSize"
+                  android:label="VideoView2TestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+          </activity>
+
+        <!-- Keep the test services synced together with the TestUtils.java -->
+        <service android:name="android.media.MockMediaSessionService2">
+            <intent-filter>
+                <action android:name="android.media.MediaSessionService2" />
+            </intent-filter>
+            <meta-data android:name="android.media.session" android:value="TestSession" />
+        </service>
+        <!-- Keep the test services synced together with the MockMediaLibraryService -->
+        <service android:name="android.media.MockMediaLibraryService2">
+            <intent-filter>
+                <action android:name="android.media.MediaLibraryService2" />
+            </intent-filter>
+            <meta-data android:name="android.media.session" android:value="TestBrowser" />
+        </service>
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.media.test"
+        android:label="Media API test" />
+
+</manifest>
diff --git a/packages/MediaComponents/test/runtest.sh b/packages/MediaComponents/test/runtest.sh
new file mode 100644
index 0000000..5c0ef51
--- /dev/null
+++ b/packages/MediaComponents/test/runtest.sh
@@ -0,0 +1,189 @@
+#!/bin/bash
+# Copyright 2018 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.
+
+# Usage '. runtest.sh'
+
+function _runtest_mediacomponent_usage() {
+  echo 'runtest-MediaComponents [option]: Run MediaComponents test'
+  echo '     -h|--help: This help'
+  echo '     --skip: Skip build. Just rerun-tests.'
+  echo '     --min: Only rebuild test apk and updatable library.'
+  echo '     -s [device_id]: Specify a device name to run test against.'
+  echo '                     You can define ${ADBHOST} instead.'
+  echo '     -r [count]: Repeat tests for given count. It will stop when fails.'
+  echo '     --ignore: Keep repeating tests even when it fails.'
+  echo '     -t [test]: Only run the specific test. Can be either a class or a method.'
+}
+
+function runtest-MediaComponents() {
+  # Edit here if you want to support other tests.
+  # List up libs and apks in the media_api needed for tests, and place test target at the last.
+  local TEST_PACKAGE_DIR=("frameworks/av/packages/MediaComponents/test")
+  local BUILD_TARGETS=("MediaComponents" "MediaComponentsTest")
+  local INSTALL_TARGETS=("MediaComponentsTest")
+  local TEST_RUNNER="android.support.test.runner.AndroidJUnitRunner"
+  local DEPENDENCIES=("mockito-target-minus-junit4" "android-support-test" "compatibility-device-util")
+
+  if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
+    echo "Needs to lunch a target first"
+    return
+  fi
+
+  local old_path=${OLDPWD}
+  while true; do
+    local OPTION_SKIP="false"
+    local OPTION_MIN="false"
+    local OPTION_REPEAT_COUNT="1"
+    local OPTION_IGNORE="false"
+    local OPTION_TEST_TARGET=""
+    local adbhost_local
+    while (( "$#" )); do
+      case "${1}" in
+        -h|--help)
+          _runtest_mediacomponent_usage
+          return
+          ;;
+        --skip)
+          OPTION_SKIP="true"
+          ;;
+        --min)
+          OPTION_MIN="true"
+          ;;
+        -s)
+          shift
+          adbhost_local=${1}
+          ;;
+        -r)
+          shift
+          OPTION_REPEAT_COUNT="${1}"
+          ;;
+        --ignore)
+          OPTION_IGNORE="true"
+          ;;
+        -t)
+          shift
+          OPTION_TEST_TARGET="${1}"
+      esac
+      shift
+    done
+
+    # Build adb command.
+    local adb
+    if [[ -z "${adbhost_local}" ]]; then
+      adbhost_local=${ADBHOST}
+    fi
+    if [[ -z "${adbhost_local}" ]]; then
+      local device_count=$(adb devices | sed '/^[[:space:]]*$/d' | wc -l)
+      if [[ "${device_count}" != "2" ]]; then
+        echo "Too many devices. Specify a device." && break
+      fi
+      adb="adb"
+    else
+      adb="adb -s ${adbhost_local}"
+    fi
+
+    local target_dir="${ANDROID_BUILD_TOP}/${TEST_PACKAGE_DIR}"
+    local TEST_PACKAGE=$(sed -n 's/^.*\bpackage\b="\([a-z0-9\.]*\)".*$/\1/p' ${target_dir}/AndroidManifest.xml)
+
+    if [[ "${OPTION_SKIP}" != "true" ]]; then
+      # Build dependencies if needed.
+      local dependency
+      local build_dependency=""
+      for dependency in ${DEPENDENCIES[@]}; do
+        if [[ "${dependency}" == "out/"* ]]; then
+          if [[ ! -f ${ANDROID_BUILD_TOP}/${dependency} ]]; then
+            build_dependency="true"
+            break
+          fi
+        else
+          if [[ "$(find ${OUT} -name ${dependency}_intermediates | wc -l)" == "0" ]]; then
+            build_dependency="true"
+            break
+          fi
+        fi
+      done
+      if [[ "${build_dependency}" == "true" ]]; then
+        echo "Building dependencies. Will only print stderr."
+        m ${DEPENDENCIES[@]} -j > /dev/null
+      fi
+
+      # Build test apk and required apk.
+      local build_targets="${BUILD_TARGETS[@]}"
+      if [[ "${OPTION_MIN}" != "true" ]]; then
+        build_targets="${build_targets} droid"
+      fi
+      m ${build_targets} -j || break
+
+      ${adb} root
+      ${adb} remount
+      ${adb} shell stop
+      ${adb} sync
+      ${adb} shell start
+      ${adb} wait-for-device || break
+      # Ensure package manager is loaded.
+      sleep 5
+
+      # Install apks
+      local install_failed="false"
+      for target in ${INSTALL_TARGETS[@]}; do
+        echo "${target}"
+        local target_dir=$(mgrep -l -e '^LOCAL_PACKAGE_NAME.*'"${target}$")
+        if [[ -z ${target_dir} ]]; then
+          continue
+        fi
+        target_dir=$(dirname ${target_dir})
+        local package=$(sed -n 's/^.*\bpackage\b="\([a-z0-9\._]*\)".*$/\1/p' ${target_dir}/AndroidManifest.xml)
+        local apk_path=$(find ${OUT} -name ${target}.apk)
+        if [[ -z "${apk_path}" ]]; then
+          echo "Cannot locate ${target}.apk" && break
+        fi
+        echo "Installing ${target}.apk. path=${apk_path}"
+        ${adb} install -r ${apk_path}
+        if [[ "${?}" != "0" ]]; then
+          install_failed="true"
+          break
+        fi
+      done
+      if [[ "${install_failed}" == "true" ]]; then
+        echo "Failed to install. Test wouldn't run."
+        break
+      fi
+    fi
+
+    local test_target=""
+    if [[ -n "${OPTION_TEST_TARGET}" ]]; then
+      test_target="-e class ${OPTION_TEST_TARGET}"
+    fi
+
+    local i
+    local tmpfile=$(tempfile)
+    for ((i=1; i <= ${OPTION_REPEAT_COUNT}; i++)); do
+      echo "Run test ${i}/${OPTION_REPEAT_COUNT}"
+      ${adb} shell am instrument ${test_target} -w ${TEST_PACKAGE}/${TEST_RUNNER} >& ${tmpfile}
+      cat ${tmpfile}
+      if [[ "${OPTION_IGNORE}" != "true" ]]; then
+        if [[ -n "$(grep ${tmpfile} -e 'FAILURE\|crashed')" ]]; then
+          # am instrument doesn't return error code so need to grep result message instead
+          break
+        fi
+      fi
+    done
+    rm ${tmpfile}
+    break
+  done
+}
+
+echo "Following functions are added to your environment:"
+_runtest_mediacomponent_usage
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
new file mode 100644
index 0000000..fe8aeb9
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2018 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;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaSession2.CommandGroup;
+import android.os.Bundle;
+import android.support.annotation.CallSuper;
+import android.support.annotation.NonNull;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests {@link MediaBrowser2}.
+ * <p>
+ * This test inherits {@link MediaController2Test} to ensure that inherited APIs from
+ * {@link MediaController2} works cleanly.
+ */
+// TODO(jaewan): Implement host-side test so browser and service can run in different processes.
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaBrowser2Test extends MediaController2Test {
+    private static final String TAG = "MediaBrowser2Test";
+
+    @Override
+    TestControllerInterface onCreateController(@NonNull SessionToken token,
+            @NonNull TestControllerCallbackInterface callback) {
+        return new TestMediaBrowser(mContext, token, new TestBrowserCallback(callback));
+    }
+
+    @Test
+    public void testGetBrowserRoot() throws InterruptedException {
+        final Bundle param = new Bundle();
+        param.putString(TAG, TAG);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
+                assertTrue(TestUtils.equals(param, rootHints));
+                assertEquals(MockMediaLibraryService2.ROOT_ID, rootMediaId);
+                assertTrue(TestUtils.equals(MockMediaLibraryService2.EXTRA, rootExtra));
+                latch.countDown();
+            }
+        };
+
+        final SessionToken token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser =
+                (MediaBrowser2) createController(token,true, callback);
+        browser.getBrowserRoot(param);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    public static class TestBrowserCallback extends BrowserCallback
+            implements WaitForConnectionInterface {
+        private final TestControllerCallbackInterface mCallbackProxy;
+        public final CountDownLatch connectLatch = new CountDownLatch(1);
+        public final CountDownLatch disconnectLatch = new CountDownLatch(1);
+
+        TestBrowserCallback(TestControllerCallbackInterface callbackProxy) {
+            mCallbackProxy = callbackProxy;
+        }
+
+        @CallSuper
+        @Override
+        public void onConnected(CommandGroup commands) {
+            super.onConnected(commands);
+            connectLatch.countDown();
+        }
+
+        @CallSuper
+        @Override
+        public void onDisconnected() {
+            super.onDisconnected();
+            disconnectLatch.countDown();
+        }
+
+        @Override
+        public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
+            mCallbackProxy.onGetRootResult(rootHints, rootMediaId, rootExtra);
+        }
+
+        @Override
+        public void waitForConnect(boolean expect) throws InterruptedException {
+            if (expect) {
+                assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+            } else {
+                assertFalse(connectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            }
+        }
+
+        @Override
+        public void waitForDisconnect(boolean expect) throws InterruptedException {
+            if (expect) {
+                assertTrue(disconnectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+            } else {
+                assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            }
+        }
+    }
+
+    public class TestMediaBrowser extends MediaBrowser2 implements TestControllerInterface {
+        private final BrowserCallback mCallback;
+
+        public TestMediaBrowser(@NonNull Context context, @NonNull SessionToken token,
+                @NonNull ControllerCallback callback) {
+            super(context, token, (BrowserCallback) callback, sHandlerExecutor);
+            mCallback = (BrowserCallback) callback;
+        }
+
+        @Override
+        public BrowserCallback getCallback() {
+            return mCallback;
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
new file mode 100644
index 0000000..f99935a
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright 2018 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;
+
+import android.media.MediaPlayerBase.PlaybackListener;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.TestUtils.SyncHandler;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static android.media.TestUtils.createPlaybackState;
+import static org.junit.Assert.*;
+
+/**
+ * Tests {@link MediaController2}.
+ */
+// TODO(jaewan): Implement host-side test so controller and session can run in different processes.
+// TODO(jaewan): Fix flaky failure -- see MediaController2Impl.getController()
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@FlakyTest
+public class MediaController2Test extends MediaSession2TestBase {
+    private static final String TAG = "MediaController2Test";
+
+    MediaSession2 mSession;
+    MediaController2 mController;
+    MockPlayer mPlayer;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        // Create this test specific MediaSession2 to use our own Handler.
+        sHandler.postAndSync(()->{
+            mPlayer = new MockPlayer(1);
+            mSession = new MediaSession2.Builder(mContext, mPlayer).setId(TAG).build();
+        });
+
+        mController = createController(mSession.getToken());
+        TestServiceRegistry.getInstance().setHandler(sHandler);
+    }
+
+    @After
+    @Override
+    public void cleanUp() throws Exception {
+        super.cleanUp();
+        sHandler.postAndSync(() -> {
+            if (mSession != null) {
+                mSession.close();
+            }
+        });
+        TestServiceRegistry.getInstance().cleanUp();
+    }
+
+    @Test
+    public void testPlay() throws InterruptedException {
+        mController.play();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mPlayCalled);
+    }
+
+    @Test
+    public void testPause() throws InterruptedException {
+        mController.pause();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mPauseCalled);
+    }
+
+
+    @Test
+    public void testSkipToPrevious() throws InterruptedException {
+        mController.skipToPrevious();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mSkipToPreviousCalled);
+    }
+
+    @Test
+    public void testSkipToNext() throws InterruptedException {
+        mController.skipToNext();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mSkipToNextCalled);
+    }
+
+    @Test
+    public void testStop() throws InterruptedException {
+        mController.stop();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mStopCalled);
+    }
+
+    @Test
+    public void testGetPackageName() {
+        assertEquals(mContext.getPackageName(), mController.getSessionToken().getPackageName());
+    }
+
+    @Test
+    public void testGetPlaybackState() throws InterruptedException {
+        // TODO(jaewan): add equivalent test later
+        /*
+        final CountDownLatch latch = new CountDownLatch(1);
+        final MediaPlayerBase.PlaybackListener listener = (state) -> {
+            assertEquals(PlaybackState.STATE_BUFFERING, state.getState());
+            latch.countDown();
+        };
+        assertNull(mController.getPlaybackState());
+        mController.addPlaybackListener(listener, sHandler);
+
+        mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_BUFFERING));
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertEquals(PlaybackState.STATE_BUFFERING, mController.getPlaybackState().getState());
+        */
+    }
+
+    // TODO(jaewan): add equivalent test later
+    /*
+    @Test
+    public void testAddPlaybackListener() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(2);
+        final MediaPlayerBase.PlaybackListener listener = (state) -> {
+            switch ((int) latch.getCount()) {
+                case 2:
+                    assertEquals(PlaybackState.STATE_PLAYING, state.getState());
+                    break;
+                case 1:
+                    assertEquals(PlaybackState.STATE_PAUSED, state.getState());
+                    break;
+            }
+            latch.countDown();
+        };
+
+        mController.addPlaybackListener(listener, sHandler);
+        sHandler.postAndSync(()->{
+            mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PLAYING));
+            mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PAUSED));
+        });
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testRemovePlaybackListener() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final MediaPlayerBase.PlaybackListener listener = (state) -> {
+            fail();
+            latch.countDown();
+        };
+        mController.addPlaybackListener(listener, sHandler);
+        mController.removePlaybackListener(listener);
+        mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PLAYING));
+        assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+    */
+
+    @Test
+    public void testControllerCallback_onConnected() throws InterruptedException {
+        // createController() uses controller callback to wait until the controller becomes
+        // available.
+        MediaController2 controller = createController(mSession.getToken());
+        assertNotNull(controller);
+    }
+
+    @Test
+    public void testControllerCallback_sessionRejects() throws InterruptedException {
+        final MediaSession2.SessionCallback sessionCallback = new SessionCallback() {
+            @Override
+            public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
+                return null;
+            }
+        };
+        sHandler.postAndSync(() -> {
+            mSession.close();
+            mSession = new MediaSession2.Builder(mContext, mPlayer)
+                    .setSessionCallback(sessionCallback).build();
+        });
+        MediaController2 controller =
+                createController(mSession.getToken(), false, null);
+        assertNotNull(controller);
+        waitForConnect(controller, false);
+        waitForDisconnect(controller, true);
+    }
+
+    @Test
+    public void testControllerCallback_releaseSession() throws InterruptedException {
+        sHandler.postAndSync(() -> {
+            mSession.close();
+        });
+        waitForDisconnect(mController, true);
+    }
+
+    @Test
+    public void testControllerCallback_release() throws InterruptedException {
+        mController.close();
+        waitForDisconnect(mController, true);
+    }
+
+    @Test
+    public void testIsConnected() throws InterruptedException {
+        assertTrue(mController.isConnected());
+        sHandler.postAndSync(()->{
+            mSession.close();
+        });
+        // postAndSync() to wait until the disconnection is propagated.
+        sHandler.postAndSync(()->{
+            assertFalse(mController.isConnected());
+        });
+    }
+
+    /**
+     * Test potential deadlock for calls between controller and session.
+     */
+    @Test
+    public void testDeadlock() throws InterruptedException {
+        sHandler.postAndSync(() -> {
+            mSession.close();
+            mSession = null;
+        });
+
+        // Two more threads are needed not to block test thread nor test wide thread (sHandler).
+        final HandlerThread sessionThread = new HandlerThread("testDeadlock_session");
+        final HandlerThread testThread = new HandlerThread("testDeadlock_test");
+        sessionThread.start();
+        testThread.start();
+        final SyncHandler sessionHandler = new SyncHandler(sessionThread.getLooper());
+        final Handler testHandler = new Handler(testThread.getLooper());
+        final CountDownLatch latch = new CountDownLatch(1);
+        try {
+            final MockPlayer player = new MockPlayer(0);
+            sessionHandler.postAndSync(() -> {
+                mSession = new MediaSession2.Builder(mContext, mPlayer)
+                        .setId("testDeadlock").build();
+            });
+            final MediaController2 controller = createController(mSession.getToken());
+            testHandler.post(() -> {
+                final PlaybackState state = createPlaybackState(PlaybackState.STATE_ERROR);
+                for (int i = 0; i < 100; i++) {
+                    // triggers call from session to controller.
+                    player.notifyPlaybackState(state);
+                    // triggers call from controller to session.
+                    controller.play();
+
+                    // Repeat above
+                    player.notifyPlaybackState(state);
+                    controller.pause();
+                    player.notifyPlaybackState(state);
+                    controller.stop();
+                    player.notifyPlaybackState(state);
+                    controller.skipToNext();
+                    player.notifyPlaybackState(state);
+                    controller.skipToPrevious();
+                }
+                // This may hang if deadlock happens.
+                latch.countDown();
+            });
+            assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } finally {
+            if (mSession != null) {
+                sessionHandler.postAndSync(() -> {
+                    // Clean up here because sessionHandler will be removed afterwards.
+                    mSession.close();
+                    mSession = null;
+                });
+            }
+            if (sessionThread != null) {
+                sessionThread.quitSafely();
+            }
+            if (testThread != null) {
+                testThread.quitSafely();
+            }
+        }
+    }
+
+    @Ignore
+    @Test
+    public void testGetServiceToken() {
+        SessionToken token = TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID);
+        assertNotNull(token);
+        assertEquals(mContext.getPackageName(), token.getPackageName());
+        assertEquals(MockMediaSessionService2.ID, token.getId());
+        assertNull(token.getSessionBinder());
+        assertEquals(SessionToken.TYPE_SESSION_SERVICE, token.getType());
+    }
+
+    private void connectToService(SessionToken token) throws InterruptedException {
+        mController = createController(token);
+        mSession = TestServiceRegistry.getInstance().getServiceInstance().getSession();
+        mPlayer = (MockPlayer) mSession.getPlayer();
+    }
+
+    // TODO(jaewan): Reenable when session manager detects app installs
+    @Ignore
+    @Test
+    public void testConnectToService_sessionService() throws InterruptedException {
+        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
+        testConnectToService();
+    }
+
+    // TODO(jaewan): Reenable when session manager detects app installs
+    @Ignore
+    @Test
+    public void testConnectToService_libraryService() throws InterruptedException {
+        connectToService(TestUtils.getServiceToken(mContext, MockMediaLibraryService2.ID));
+        testConnectToService();
+    }
+
+    public void testConnectToService() throws InterruptedException {
+        TestServiceRegistry serviceInfo = TestServiceRegistry.getInstance();
+        ControllerInfo info = serviceInfo.getOnConnectControllerInfo();
+        assertEquals(mContext.getPackageName(), info.getPackageName());
+        assertEquals(Process.myUid(), info.getUid());
+        assertFalse(info.isTrusted());
+
+        // Test command from controller to session service
+        mController.play();
+        assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertTrue(mPlayer.mPlayCalled);
+
+        // Test command from session service to controller
+        // TODO(jaewan): Add equivalent tests again
+        /*
+        final CountDownLatch latch = new CountDownLatch(1);
+        mController.addPlaybackListener((state) -> {
+            assertNotNull(state);
+            assertEquals(PlaybackState.STATE_REWINDING, state.getState());
+            latch.countDown();
+        }, sHandler);
+        mPlayer.notifyPlaybackState(
+                TestUtils.createPlaybackState(PlaybackState.STATE_REWINDING));
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        */
+    }
+
+    @Test
+    public void testControllerAfterSessionIsGone_session() throws InterruptedException {
+        testControllerAfterSessionIsGone(mSession.getToken().getId());
+    }
+
+    @Ignore
+    @Test
+    public void testControllerAfterSessionIsGone_sessionService() throws InterruptedException {
+        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
+        testControllerAfterSessionIsGone(MockMediaSessionService2.ID);
+    }
+
+    @Test
+    public void testClose_beforeConnected() throws InterruptedException {
+        MediaController2 controller =
+                createController(mSession.getToken(), false, null);
+        controller.close();
+    }
+
+    @Test
+    public void testClose_twice() throws InterruptedException {
+        mController.close();
+        mController.close();
+    }
+
+    @Test
+    public void testClose_session() throws InterruptedException {
+        final String id = mSession.getToken().getId();
+        mController.close();
+        // close is done immediately for session.
+        testNoInteraction();
+
+        // Test whether the controller is notified about later close of the session or
+        // re-creation.
+        testControllerAfterSessionIsGone(id);
+    }
+
+    // TODO(jaewan): Reenable when session manager detects app installs
+    @Ignore
+    @Test
+    public void testClose_sessionService() throws InterruptedException {
+        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
+        testCloseFromService();
+    }
+
+    // TODO(jaewan): Reenable when session manager detects app installs
+    @Ignore
+    @Test
+    public void testClose_libraryService() throws InterruptedException {
+        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
+        testCloseFromService();
+    }
+
+    private void testCloseFromService() throws InterruptedException {
+        final String id = mController.getSessionToken().getId();
+        final CountDownLatch latch = new CountDownLatch(1);
+        TestServiceRegistry.getInstance().setServiceInstanceChangedCallback((service) -> {
+            if (service == null) {
+                // Destroying..
+                latch.countDown();
+            }
+        });
+        mController.close();
+        // Wait until close triggers onDestroy() of the session service.
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertNull(TestServiceRegistry.getInstance().getServiceInstance());
+        testNoInteraction();
+
+        // Test whether the controller is notified about later close of the session or
+        // re-creation.
+        testControllerAfterSessionIsGone(id);
+    }
+
+    private void testControllerAfterSessionIsGone(final String id) throws InterruptedException {
+        sHandler.postAndSync(() -> {
+            // TODO(jaewan): Use Session.close later when we add the API.
+            mSession.close();
+        });
+        waitForDisconnect(mController, true);
+        testNoInteraction();
+
+        // Test with the newly created session.
+        sHandler.postAndSync(() -> {
+            // Recreated session has different session stub, so previously created controller
+            // shouldn't be available.
+            mSession = new MediaSession2.Builder(mContext, mPlayer).setId(id).build();
+        });
+        testNoInteraction();
+    }
+
+    private void testNoInteraction() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final PlaybackListener playbackListener = (state) -> {
+            fail("Controller shouldn't be notified about change in session after the close.");
+            latch.countDown();
+        };
+        // TODO(jaewan): Add equivalent tests again
+        /*
+        mController.addPlaybackListener(playbackListener, sHandler);
+        mPlayer.notifyPlaybackState(TestUtils.createPlaybackState(PlaybackState.STATE_BUFFERING));
+        assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        mController.removePlaybackListener(playbackListener);
+        */
+    }
+
+    // TODO(jaewan): Add  test for service connect rejection, when we differentiate session
+    //               active/inactive and connection accept/refuse
+}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
new file mode 100644
index 0000000..7b58f0e
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2018 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;
+
+import android.media.MediaPlayerBase.PlaybackListener;
+import android.media.MediaSession2.Builder;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.session.PlaybackState;
+import android.os.Process;
+import android.os.Looper;
+import android.support.annotation.NonNull;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.util.ArrayList;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static android.media.TestUtils.createPlaybackState;
+import static org.junit.Assert.*;
+
+/**
+ * Tests {@link MediaSession2}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaSession2Test extends MediaSession2TestBase {
+    private static final String TAG = "MediaSession2Test";
+
+    private MediaSession2 mSession;
+    private MockPlayer mPlayer;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        sHandler.postAndSync(() -> {
+            mPlayer = new MockPlayer(0);
+            mSession = new MediaSession2.Builder(mContext, mPlayer).build();
+        });
+    }
+
+    @After
+    @Override
+    public void cleanUp() throws Exception {
+        super.cleanUp();
+        sHandler.postAndSync(() -> {
+            mSession.close();
+        });
+    }
+
+    @Test
+    public void testBuilder() throws Exception {
+        try {
+            MediaSession2.Builder builder = new Builder(mContext, null);
+            fail("null player shouldn't be allowed");
+        } catch (IllegalArgumentException e) {
+            // expected. pass-through
+        }
+        MediaSession2.Builder builder = new Builder(mContext, mPlayer);
+        try {
+            builder.setId(null);
+            fail("null id shouldn't be allowed");
+        } catch (IllegalArgumentException e) {
+            // expected. pass-through
+        }
+    }
+
+    @Test
+    public void testSetPlayer() throws Exception {
+        sHandler.postAndSync(() -> {
+            MockPlayer player = new MockPlayer(0);
+            // Test if setPlayer doesn't crash with various situations.
+            mSession.setPlayer(mPlayer);
+            mSession.setPlayer(player);
+            mSession.close();
+        });
+    }
+
+    @Test
+    public void testPlay() throws Exception {
+        sHandler.postAndSync(() -> {
+            mSession.play();
+            assertTrue(mPlayer.mPlayCalled);
+        });
+    }
+
+    @Test
+    public void testPause() throws Exception {
+        sHandler.postAndSync(() -> {
+            mSession.pause();
+            assertTrue(mPlayer.mPauseCalled);
+        });
+    }
+
+    @Test
+    public void testStop() throws Exception {
+        sHandler.postAndSync(() -> {
+            mSession.stop();
+            assertTrue(mPlayer.mStopCalled);
+        });
+    }
+
+    @Test
+    public void testSkipToNext() throws Exception {
+        sHandler.postAndSync(() -> {
+            mSession.skipToNext();
+            assertTrue(mPlayer.mSkipToNextCalled);
+        });
+    }
+
+    @Test
+    public void testSkipToPrevious() throws Exception {
+        sHandler.postAndSync(() -> {
+            mSession.skipToPrevious();
+            assertTrue(mPlayer.mSkipToPreviousCalled);
+        });
+    }
+
+    @Test
+    public void testPlaybackStateChangedListener() throws InterruptedException {
+        // TODO(jaewan): Add equivalent tests again
+        /*
+        final CountDownLatch latch = new CountDownLatch(2);
+        final MockPlayer player = new MockPlayer(0);
+        final PlaybackListener listener = (state) -> {
+            assertEquals(sHandler.getLooper(), Looper.myLooper());
+            assertNotNull(state);
+            switch ((int) latch.getCount()) {
+                case 2:
+                    assertEquals(PlaybackState.STATE_PLAYING, state.getState());
+                    break;
+                case 1:
+                    assertEquals(PlaybackState.STATE_PAUSED, state.getState());
+                    break;
+                case 0:
+                    fail();
+            }
+            latch.countDown();
+        };
+        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PLAYING));
+        sHandler.postAndSync(() -> {
+            mSession.addPlaybackListener(listener, sHandler);
+            // When the player is set, listeners will be notified about the player's current state.
+            mSession.setPlayer(player);
+        });
+        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PAUSED));
+        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        */
+    }
+
+    @Test
+    public void testBadPlayer() throws InterruptedException {
+        // TODO(jaewan): Add equivalent tests again
+        /*
+        final CountDownLatch latch = new CountDownLatch(3); // expected call + 1
+        final BadPlayer player = new BadPlayer(0);
+        sHandler.postAndSync(() -> {
+            mSession.addPlaybackListener((state) -> {
+                // This will be called for every setPlayer() calls, but no more.
+                assertNull(state);
+                latch.countDown();
+            }, sHandler);
+            mSession.setPlayer(player);
+            mSession.setPlayer(mPlayer);
+        });
+        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PAUSED));
+        assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        */
+    }
+
+    private static class BadPlayer extends MockPlayer {
+        public BadPlayer(int count) {
+            super(count);
+        }
+
+        @Override
+        public void removePlaybackListener(@NonNull PlaybackListener listener) {
+            // No-op. This bad player will keep push notification to the listener that is previously
+            // registered by session.setPlayer().
+        }
+    }
+
+    @Test
+    public void testOnCommandCallback() throws InterruptedException {
+        final MockOnCommandCallback callback = new MockOnCommandCallback();
+        sHandler.postAndSync(() -> {
+            mSession.close();
+            mPlayer = new MockPlayer(1);
+            mSession = new MediaSession2.Builder(mContext, mPlayer)
+                    .setSessionCallback(callback).build();
+        });
+        MediaController2 controller = createController(mSession.getToken());
+        controller.pause();
+        assertFalse(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertFalse(mPlayer.mPauseCalled);
+        assertEquals(1, callback.commands.size());
+        assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE,
+                (long) callback.commands.get(0).getCommandCode());
+        controller.skipToNext();
+        assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertTrue(mPlayer.mSkipToNextCalled);
+        assertFalse(mPlayer.mPauseCalled);
+        assertEquals(2, callback.commands.size());
+        assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM,
+                (long) callback.commands.get(1).getCommandCode());
+    }
+
+    @Test
+    public void testOnConnectCallback() throws InterruptedException {
+        final MockOnConnectCallback sessionCallback = new MockOnConnectCallback();
+        sHandler.postAndSync(() -> {
+            mSession.close();
+            mSession = new MediaSession2.Builder(mContext, mPlayer)
+                    .setSessionCallback(sessionCallback).build();
+        });
+        MediaController2 controller =
+                createController(mSession.getToken(), false, null);
+        assertNotNull(controller);
+        waitForConnect(controller, false);
+        waitForDisconnect(controller, true);
+    }
+
+    public class MockOnConnectCallback extends SessionCallback {
+        @Override
+        public MediaSession2.CommandGroup onConnect(ControllerInfo controllerInfo) {
+            if (Process.myUid() != controllerInfo.getUid()) {
+                return null;
+            }
+            assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
+            assertEquals(Process.myUid(), controllerInfo.getUid());
+            assertFalse(controllerInfo.isTrusted());
+            // Reject all
+            return null;
+        }
+    }
+
+    public class MockOnCommandCallback extends SessionCallback {
+        public final ArrayList<MediaSession2.Command> commands = new ArrayList<>();
+
+        @Override
+        public boolean onCommandRequest(ControllerInfo controllerInfo, MediaSession2.Command command) {
+            assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
+            assertEquals(Process.myUid(), controllerInfo.getUid());
+            assertFalse(controllerInfo.isTrusted());
+            commands.add(command);
+            if (command.getCommandCode() == MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE) {
+                return false;
+            }
+            return true;
+        }
+    }
+}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
new file mode 100644
index 0000000..2965c82
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2018 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;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.media.MediaController2.ControllerCallback;
+import android.media.MediaSession2.CommandGroup;
+import android.os.Bundle;
+import android.os.HandlerThread;
+import android.support.annotation.CallSuper;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+/**
+ * Base class for session test.
+ */
+abstract class MediaSession2TestBase {
+    // Expected success
+    static final int WAIT_TIME_MS = 1000;
+
+    // Expected timeout
+    static final int TIMEOUT_MS = 500;
+
+    static TestUtils.SyncHandler sHandler;
+    static Executor sHandlerExecutor;
+
+    Context mContext;
+    private List<MediaController2> mControllers = new ArrayList<>();
+
+    interface TestControllerInterface {
+        ControllerCallback getCallback();
+    }
+
+    interface TestControllerCallbackInterface {
+        // Currently empty. Add methods in ControllerCallback/BrowserCallback that you want to test.
+
+        // Browser specific callbacks
+        default void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
+    }
+
+    interface WaitForConnectionInterface {
+        void waitForConnect(boolean expect) throws InterruptedException;
+        void waitForDisconnect(boolean expect) throws InterruptedException;
+    }
+
+    @BeforeClass
+    public static void setUpThread() {
+        if (sHandler == null) {
+            HandlerThread handlerThread = new HandlerThread("MediaSession2TestBase");
+            handlerThread.start();
+            sHandler = new TestUtils.SyncHandler(handlerThread.getLooper());
+            sHandlerExecutor = (runnable) -> {
+                sHandler.post(runnable);
+            };
+        }
+    }
+
+    @AfterClass
+    public static void cleanUpThread() {
+        if (sHandler != null) {
+            sHandler.getLooper().quitSafely();
+            sHandler = null;
+            sHandlerExecutor = null;
+        }
+    }
+
+    @CallSuper
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @CallSuper
+    public void cleanUp() throws Exception {
+        for (int i = 0; i < mControllers.size(); i++) {
+            mControllers.get(i).close();
+        }
+    }
+
+    final MediaController2 createController(SessionToken token) throws InterruptedException {
+        return createController(token, true, null);
+    }
+
+    final MediaController2 createController(@NonNull SessionToken token,
+            boolean waitForConnect, @Nullable TestControllerCallbackInterface callback)
+            throws InterruptedException {
+        TestControllerInterface instance = onCreateController(token, callback);
+        if (!(instance instanceof MediaController2)) {
+            throw new RuntimeException("Test has a bug. Expected MediaController2 but returned "
+                    + instance);
+        }
+        MediaController2 controller = (MediaController2) instance;
+        mControllers.add(controller);
+        if (waitForConnect) {
+            waitForConnect(controller, true);
+        }
+        return controller;
+    }
+
+    private static WaitForConnectionInterface getWaitForConnectionInterface(
+            MediaController2 controller) {
+        if (!(controller instanceof TestControllerInterface)) {
+            throw new RuntimeException("Test has a bug. Expected controller implemented"
+                    + " TestControllerInterface but got " + controller);
+        }
+        ControllerCallback callback = ((TestControllerInterface) controller).getCallback();
+        if (!(callback instanceof WaitForConnectionInterface)) {
+            throw new RuntimeException("Test has a bug. Expected controller with callback "
+                    + " implemented WaitForConnectionInterface but got " + controller);
+        }
+        return (WaitForConnectionInterface) callback;
+    }
+
+    public static void waitForConnect(MediaController2 controller, boolean expected)
+            throws InterruptedException {
+        getWaitForConnectionInterface(controller).waitForConnect(expected);
+    }
+
+    public static void waitForDisconnect(MediaController2 controller, boolean expected)
+            throws InterruptedException {
+        getWaitForConnectionInterface(controller).waitForDisconnect(expected);
+    }
+
+    TestControllerInterface onCreateController(@NonNull SessionToken token,
+            @NonNull TestControllerCallbackInterface callback) {
+        return new TestMediaController(mContext, token, new TestControllerCallback(callback));
+    }
+
+    public static class TestControllerCallback extends MediaController2.ControllerCallback
+            implements WaitForConnectionInterface {
+        public final TestControllerCallbackInterface mCallbackProxy;
+        public final CountDownLatch connectLatch = new CountDownLatch(1);
+        public final CountDownLatch disconnectLatch = new CountDownLatch(1);
+
+        TestControllerCallback(TestControllerCallbackInterface callbackProxy) {
+            mCallbackProxy = callbackProxy;
+        }
+
+        @CallSuper
+        @Override
+        public void onConnected(CommandGroup commands) {
+            super.onConnected(commands);
+            connectLatch.countDown();
+        }
+
+        @CallSuper
+        @Override
+        public void onDisconnected() {
+            super.onDisconnected();
+            disconnectLatch.countDown();
+        }
+
+        @Override
+        public void waitForConnect(boolean expect) throws InterruptedException {
+            if (expect) {
+                assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+            } else {
+                assertFalse(connectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            }
+        }
+
+        @Override
+        public void waitForDisconnect(boolean expect) throws InterruptedException {
+            if (expect) {
+                assertTrue(disconnectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+            } else {
+                assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            }
+        }
+    }
+
+    public class TestMediaController extends MediaController2 implements TestControllerInterface {
+        private final ControllerCallback mCallback;
+
+        public TestMediaController(@NonNull Context context, @NonNull SessionToken token,
+                @NonNull ControllerCallback callback) {
+            super(context, token, callback, sHandlerExecutor);
+            mCallback = callback;
+        }
+
+        @Override
+        public ControllerCallback getCallback() {
+            return mCallback;
+        }
+    }
+}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
new file mode 100644
index 0000000..bfed7d0
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2018 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;
+
+import android.content.Context;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static android.media.TestUtils.createPlaybackState;
+import static org.junit.Assert.*;
+
+/**
+ * Tests {@link MediaSessionManager} with {@link MediaSession2} specific APIs.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Ignore
+// TODO(jaewan): Reenable test when the media session service detects newly installed sesison
+//               service app.
+public class MediaSessionManager_MediaSession2 extends MediaSession2TestBase {
+    private static final String TAG = "MediaSessionManager_MediaSession2";
+
+    private MediaSessionManager mManager;
+    private MediaSession2 mSession;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+
+        // Specify TAG here so {@link MediaSession2.getInstance()} doesn't complaint about
+        // per test thread differs across the {@link MediaSession2} with the same TAG.
+        final MockPlayer player = new MockPlayer(1);
+        sHandler.postAndSync(() -> {
+            mSession = new MediaSession2.Builder(mContext, player).setId(TAG).build();
+        });
+        ensureChangeInSession();
+    }
+
+    @After
+    @Override
+    public void cleanUp() throws Exception {
+        super.cleanUp();
+        sHandler.removeCallbacksAndMessages(null);
+        sHandler.postAndSync(() -> {
+            mSession.close();
+        });
+    }
+
+    // TODO(jaewan): Make this host-side test to see per-user behavior.
+    @Test
+    public void testGetMediaSession2Tokens_hasMediaController() throws InterruptedException {
+        final MockPlayer player = (MockPlayer) mSession.getPlayer();
+        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_STOPPED));
+
+        MediaController2 controller = null;
+        List<SessionToken> tokens = mManager.getActiveSessionTokens();
+        assertNotNull(tokens);
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken token = tokens.get(i);
+            if (mContext.getPackageName().equals(token.getPackageName())
+                    && TAG.equals(token.getId())) {
+                assertNotNull(token.getSessionBinder());
+                assertNull(controller);
+                controller = createController(token);
+            }
+        }
+        assertNotNull(controller);
+
+        // Test if the found controller is correct one.
+        assertEquals(PlaybackState.STATE_STOPPED, controller.getPlaybackState().getState());
+        controller.play();
+
+        assertTrue(player.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertTrue(player.mPlayCalled);
+    }
+
+    /**
+     * Test if server recognizes session even if session refuses the connection from server.
+     *
+     * @throws InterruptedException
+     */
+    @Test
+    public void testGetSessionTokens_sessionRejected() throws InterruptedException {
+        sHandler.postAndSync(() -> {
+            mSession.close();
+            mSession = new MediaSession2.Builder(mContext, new MockPlayer(0)).setId(TAG)
+                    .setSessionCallback(new SessionCallback() {
+                        @Override
+                        public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
+                            // Reject all connection request.
+                            return null;
+                        }
+                    }).build();
+        });
+        ensureChangeInSession();
+
+        boolean foundSession = false;
+        List<SessionToken> tokens = mManager.getActiveSessionTokens();
+        assertNotNull(tokens);
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken token = tokens.get(i);
+            if (mContext.getPackageName().equals(token.getPackageName())
+                    && TAG.equals(token.getId())) {
+                assertFalse(foundSession);
+                foundSession = true;
+            }
+        }
+        assertTrue(foundSession);
+    }
+
+    @Test
+    public void testGetMediaSession2Tokens_playerRemoved() throws InterruptedException {
+        // Release
+        sHandler.postAndSync(() -> {
+            mSession.close();
+        });
+        ensureChangeInSession();
+
+        // When the mSession's player becomes null, it should lose binder connection between server.
+        // So server will forget the session.
+        List<SessionToken> tokens = mManager.getActiveSessionTokens();
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken token = tokens.get(i);
+            assertFalse(mContext.getPackageName().equals(token.getPackageName())
+                    && TAG.equals(token.getId()));
+        }
+    }
+
+    @Test
+    public void testGetMediaSessionService2Token() throws InterruptedException {
+        boolean foundTestSessionService = false;
+        boolean foundTestLibraryService = false;
+        List<SessionToken> tokens = mManager.getSessionServiceTokens();
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken token = tokens.get(i);
+            if (mContext.getPackageName().equals(token.getPackageName())
+                    && MockMediaSessionService2.ID.equals(token.getId())) {
+                assertFalse(foundTestSessionService);
+                assertEquals(SessionToken.TYPE_SESSION_SERVICE, token.getType());
+                assertNull(token.getSessionBinder());
+                foundTestSessionService = true;
+            } else if (mContext.getPackageName().equals(token.getPackageName())
+                    && MockMediaLibraryService2.ID.equals(token.getId())) {
+                assertFalse(foundTestLibraryService);
+                assertEquals(SessionToken.TYPE_LIBRARY_SERVICE, token.getType());
+                assertNull(token.getSessionBinder());
+                foundTestLibraryService = true;
+            }
+        }
+        assertTrue(foundTestSessionService);
+        assertTrue(foundTestLibraryService);
+    }
+
+    @Test
+    public void testGetAllSessionTokens() throws InterruptedException {
+        boolean foundTestSession = false;
+        boolean foundTestSessionService = false;
+        boolean foundTestLibraryService = false;
+        List<SessionToken> tokens = mManager.getAllSessionTokens();
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken token = tokens.get(i);
+            if (!mContext.getPackageName().equals(token.getPackageName())) {
+                continue;
+            }
+            switch (token.getId()) {
+                case TAG:
+                    assertFalse(foundTestSession);
+                    foundTestSession = true;
+                    break;
+                case MockMediaSessionService2.ID:
+                    assertFalse(foundTestSessionService);
+                    foundTestSessionService = true;
+                    assertEquals(SessionToken.TYPE_SESSION_SERVICE, token.getType());
+                    break;
+                case MockMediaLibraryService2.ID:
+                    assertFalse(foundTestLibraryService);
+                    assertEquals(SessionToken.TYPE_LIBRARY_SERVICE, token.getType());
+                    foundTestLibraryService = true;
+                    break;
+                default:
+                    fail("Unexpected session " + token + " exists in the package");
+            }
+        }
+        assertTrue(foundTestSession);
+        assertTrue(foundTestSessionService);
+        assertTrue(foundTestLibraryService);
+    }
+
+    // Ensures if the session creation/release is notified to the server.
+    private void ensureChangeInSession() throws InterruptedException {
+        // TODO(jaewan): Wait by listener.
+        Thread.sleep(WAIT_TIME_MS);
+    }
+}
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
new file mode 100644
index 0000000..acf733f
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -0,0 +1,98 @@
+/*
+* Copyright 2018 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;
+
+import static junit.framework.Assert.fail;
+
+import android.content.Context;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.TestUtils.SyncHandler;
+import android.os.Bundle;
+import android.os.Process;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * Mock implementation of {@link MediaLibraryService2} for testing.
+ */
+public class MockMediaLibraryService2 extends MediaLibraryService2 {
+    // Keep in sync with the AndroidManifest.xml
+    public static final String ID = "TestLibrary";
+
+    public static final String ROOT_ID = "rootId";
+    public static final Bundle EXTRA = new Bundle();
+    static {
+        EXTRA.putString(ROOT_ID, ROOT_ID);
+    }
+    @GuardedBy("MockMediaLibraryService2.class")
+    private static SessionToken sToken;
+
+    private MediaLibrarySession mSession;
+
+    @Override
+    public MediaLibrarySession onCreateSession(String sessionId) {
+        final MockPlayer player = new MockPlayer(1);
+        SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
+        try {
+            handler.postAndSync(() -> {
+                TestLibrarySessionCallback callback = new TestLibrarySessionCallback();
+                mSession = new MediaLibrarySessionBuilder(
+                        MockMediaLibraryService2.this, player, callback)
+                        .setId(sessionId).build();
+            });
+        } catch (InterruptedException e) {
+            fail(e.toString());
+        }
+        return mSession;
+    }
+
+    @Override
+    public void onDestroy() {
+        TestServiceRegistry.getInstance().cleanUp();
+        super.onDestroy();
+    }
+
+    public static SessionToken getToken(Context context) {
+        synchronized (MockMediaLibraryService2.class) {
+            if (sToken == null) {
+                sToken = new SessionToken(SessionToken.TYPE_LIBRARY_SERVICE,
+                        context.getPackageName(), ID,
+                        MockMediaLibraryService2.class.getName(), null);
+            }
+            return sToken;
+        }
+    }
+
+    private class TestLibrarySessionCallback extends MediaLibrarySessionCallback {
+        @Override
+        public CommandGroup onConnect(ControllerInfo controller) {
+            if (Process.myUid() != controller.getUid()) {
+                // It's system app wants to listen changes. Ignore.
+                return super.onConnect(controller);
+            }
+            TestServiceRegistry.getInstance().setServiceInstance(
+                    MockMediaLibraryService2.this, controller);
+            return super.onConnect(controller);
+        }
+
+        @Override
+        public BrowserRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
+            return new BrowserRoot(ROOT_ID, EXTRA);
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
new file mode 100644
index 0000000..9cf4911
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018 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;
+
+import static junit.framework.Assert.fail;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.media.TestUtils.SyncHandler;
+import android.media.session.PlaybackState;
+import android.os.Process;
+
+/**
+ * Mock implementation of {@link android.media.MediaSessionService2} for testing.
+ */
+public class MockMediaSessionService2 extends MediaSessionService2 {
+    // Keep in sync with the AndroidManifest.xml
+    public static final String ID = "TestSession";
+
+    private static final String DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID = "media_session_service";
+    private static final int DEFAULT_MEDIA_NOTIFICATION_ID = 1001;
+
+    private NotificationChannel mDefaultNotificationChannel;
+    private MediaSession2 mSession;
+    private NotificationManager mNotificationManager;
+
+    @Override
+    public MediaSession2 onCreateSession(String sessionId) {
+        final MockPlayer player = new MockPlayer(1);
+        SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
+        try {
+            handler.postAndSync(() -> {
+                mSession = new MediaSession2.Builder(MockMediaSessionService2.this, player)
+                        .setId(sessionId).setSessionCallback(new MySessionCallback()).build();
+            });
+        } catch (InterruptedException e) {
+            fail(e.toString());
+        }
+        return mSession;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+    }
+
+    @Override
+    public void onDestroy() {
+        TestServiceRegistry.getInstance().cleanUp();
+        super.onDestroy();
+    }
+
+    @Override
+    public MediaNotification onUpdateNotification(PlaybackState state) {
+        if (mDefaultNotificationChannel == null) {
+            mDefaultNotificationChannel = new NotificationChannel(
+                    DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID,
+                    DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID,
+                    NotificationManager.IMPORTANCE_DEFAULT);
+            mNotificationManager.createNotificationChannel(mDefaultNotificationChannel);
+        }
+        Notification notification = new Notification.Builder(
+                this, DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID)
+                .setContentTitle(getPackageName())
+                .setContentText("Playback state: " + state.getState())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon).build();
+        return MediaNotification.create(DEFAULT_MEDIA_NOTIFICATION_ID, notification);
+    }
+
+    private class MySessionCallback extends SessionCallback {
+        @Override
+        public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
+            if (Process.myUid() != controller.getUid()) {
+                // It's system app wants to listen changes. Ignore.
+                return super.onConnect(controller);
+            }
+            TestServiceRegistry.getInstance().setServiceInstance(
+                    MockMediaSessionService2.this, controller);
+            return super.onConnect(controller);
+        }
+    }
+}
diff --git a/packages/MediaComponents/test/src/android/media/MockPlayer.java b/packages/MediaComponents/test/src/android/media/MockPlayer.java
new file mode 100644
index 0000000..b0d7a69
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MockPlayer.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2018 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;
+
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A mock implementation of {@link MediaPlayerBase} for testing.
+ */
+public class MockPlayer extends MediaPlayerBase {
+    public final CountDownLatch mCountDownLatch;
+
+    public boolean mPlayCalled;
+    public boolean mPauseCalled;
+    public boolean mStopCalled;
+    public boolean mSkipToPreviousCalled;
+    public boolean mSkipToNextCalled;
+    public List<PlaybackListenerHolder> mListeners = new ArrayList<>();
+    private PlaybackState mLastPlaybackState;
+
+    public MockPlayer(int count) {
+        mCountDownLatch = (count > 0) ? new CountDownLatch(count) : null;
+    }
+
+    @Override
+    public void play() {
+        mPlayCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    @Override
+    public void pause() {
+        mPauseCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    @Override
+    public void stop() {
+        mStopCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    @Override
+    public void skipToPrevious() {
+        mSkipToPreviousCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    @Override
+    public void skipToNext() {
+        mSkipToNextCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    @Nullable
+    @Override
+    public PlaybackState getPlaybackState() {
+        return mLastPlaybackState;
+    }
+
+    @Override
+    public void addPlaybackListener(
+            @NonNull PlaybackListener listener, @NonNull Handler handler) {
+        mListeners.add(new PlaybackListenerHolder(listener, handler));
+    }
+
+    @Override
+    public void removePlaybackListener(@NonNull PlaybackListener listener) {
+        int index = PlaybackListenerHolder.indexOf(mListeners, listener);
+        if (index >= 0) {
+            mListeners.remove(index);
+        }
+    }
+
+    public void notifyPlaybackState(final PlaybackState state) {
+        mLastPlaybackState = state;
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).postPlaybackChange(state);
+        }
+    }
+}
diff --git a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
new file mode 100644
index 0000000..b0b87de
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2018 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;
+
+import android.media.MediaPlayerBase.PlaybackListener;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.os.Message;
+import android.support.annotation.NonNull;
+
+import java.util.List;
+
+/**
+ * Holds {@link PlaybackListener} with the {@link Handler}.
+ */
+public class PlaybackListenerHolder extends Handler {
+    private static final int ON_PLAYBACK_CHANGED = 1;
+
+    public final PlaybackListener listener;
+
+    public PlaybackListenerHolder(
+            @NonNull PlaybackListener listener, @NonNull Handler handler) {
+        super(handler.getLooper());
+        this.listener = listener;
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case ON_PLAYBACK_CHANGED:
+                listener.onPlaybackChanged((PlaybackState) msg.obj);
+                break;
+        }
+    }
+
+    public void postPlaybackChange(PlaybackState state) {
+        obtainMessage(ON_PLAYBACK_CHANGED, state).sendToTarget();
+    }
+
+    /**
+     * Returns {@code true} if the given list contains a {@link PlaybackListenerHolder} that holds
+     * the given listener.
+     *
+     * @param list list to check
+     * @param listener listener to check
+     * @return {@code true} if the given list contains listener. {@code false} otherwise.
+     */
+    public static <Holder extends PlaybackListenerHolder> boolean contains(
+            @NonNull List<Holder> list, PlaybackListener listener) {
+        return indexOf(list, listener) >= 0;
+    }
+
+    /**
+     * Returns the index of the {@link PlaybackListenerHolder} that contains the given listener.
+     *
+     * @param list list to check
+     * @param listener listener to check
+     * @return {@code index} of item if the given list contains listener. {@code -1} otherwise.
+     */
+    public static <Holder extends PlaybackListenerHolder> int indexOf(
+            @NonNull List<Holder> list, PlaybackListener listener) {
+        for (int i = 0; i < list.size(); i++) {
+            if (list.get(i).listener == listener) {
+                return i;
+            }
+        }
+        return -1;
+    }
+}
diff --git a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
new file mode 100644
index 0000000..6f5512e
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2018 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;
+
+import static org.junit.Assert.fail;
+
+import android.media.MediaSession2.ControllerInfo;
+import android.media.TestUtils.SyncHandler;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.GuardedBy;
+
+/**
+ * Keeps the instance of currently running {@link MockMediaSessionService2}. And also provides
+ * a way to control them in one place.
+ * <p>
+ * It only support only one service at a time.
+ */
+public class TestServiceRegistry {
+    public interface ServiceInstanceChangedCallback {
+        void OnServiceInstanceChanged(MediaSessionService2 service);
+    }
+
+    @GuardedBy("TestServiceRegistry.class")
+    private static TestServiceRegistry sInstance;
+    @GuardedBy("TestServiceRegistry.class")
+    private MediaSessionService2 mService;
+    @GuardedBy("TestServiceRegistry.class")
+    private SyncHandler mHandler;
+    @GuardedBy("TestServiceRegistry.class")
+    private ControllerInfo mOnConnectControllerInfo;
+    @GuardedBy("TestServiceRegistry.class")
+    private ServiceInstanceChangedCallback mCallback;
+
+    public static TestServiceRegistry getInstance() {
+        synchronized (TestServiceRegistry.class) {
+            if (sInstance == null) {
+                sInstance = new TestServiceRegistry();
+            }
+            return sInstance;
+        }
+    }
+
+    public void setHandler(Handler handler) {
+        synchronized (TestServiceRegistry.class) {
+            mHandler = new SyncHandler(handler.getLooper());
+        }
+    }
+
+    public void setServiceInstanceChangedCallback(ServiceInstanceChangedCallback callback) {
+        synchronized (TestServiceRegistry.class) {
+            mCallback = callback;
+        }
+    }
+
+    public Handler getHandler() {
+        synchronized (TestServiceRegistry.class) {
+            return mHandler;
+        }
+    }
+
+    public void setServiceInstance(MediaSessionService2 service, ControllerInfo controller) {
+        synchronized (TestServiceRegistry.class) {
+            if (mService != null) {
+                fail("Previous service instance is still running. Clean up manually to ensure"
+                        + " previoulsy running service doesn't break current test");
+            }
+            mService = service;
+            mOnConnectControllerInfo = controller;
+            if (mCallback != null) {
+                mCallback.OnServiceInstanceChanged(service);
+            }
+        }
+    }
+
+    public MediaSessionService2 getServiceInstance() {
+        synchronized (TestServiceRegistry.class) {
+            return mService;
+        }
+    }
+
+    public ControllerInfo getOnConnectControllerInfo() {
+        synchronized (TestServiceRegistry.class) {
+            return mOnConnectControllerInfo;
+        }
+    }
+
+
+    public void cleanUp() {
+        synchronized (TestServiceRegistry.class) {
+            final ServiceInstanceChangedCallback callback = mCallback;
+            if (mService != null) {
+                try {
+                    if (mHandler.getLooper() == Looper.myLooper()) {
+                        mService.getSession().close();
+                    } else {
+                        mHandler.postAndSync(() -> {
+                            mService.getSession().close();
+                        });
+                    }
+                } catch (InterruptedException e) {
+                    // No-op. Service containing session will die, but shouldn't be a huge issue.
+                }
+                // stopSelf() would not kill service while the binder connection established by
+                // bindService() exists, and close() above will do the job instead.
+                // So stopSelf() isn't really needed, but just for sure.
+                mService.stopSelf();
+                mService = null;
+            }
+            if (mHandler != null) {
+                mHandler.removeCallbacksAndMessages(null);
+            }
+            mCallback = null;
+            mOnConnectControllerInfo = null;
+
+            if (callback != null) {
+                callback.OnServiceInstanceChanged(null);
+            }
+        }
+    }
+}
diff --git a/packages/MediaComponents/test/src/android/media/TestUtils.java b/packages/MediaComponents/test/src/android/media/TestUtils.java
new file mode 100644
index 0000000..1372f01
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/TestUtils.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2018 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;
+
+import android.content.Context;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.os.Looper;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Utilities for tests.
+ */
+public final class TestUtils {
+    private static final int WAIT_TIME_MS = 1000;
+    private static final int WAIT_SERVICE_TIME_MS = 5000;
+
+    /**
+     * Creates a {@link android.media.session.PlaybackState} with the given state.
+     *
+     * @param state one of the PlaybackState.STATE_xxx.
+     * @return a PlaybackState
+     */
+    public static PlaybackState createPlaybackState(int state) {
+        return new PlaybackState.Builder().setState(state, 0, 1.0f).build();
+    }
+
+    /**
+     * Finds the session with id in this test package.
+     *
+     * @param context
+     * @param id
+     * @return
+     */
+    // TODO(jaewan): Currently not working.
+    public static SessionToken getServiceToken(Context context, String id) {
+        MediaSessionManager manager =
+                (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        List<SessionToken> tokens = manager.getSessionServiceTokens();
+        for (int i = 0; i < tokens.size(); i++) {
+            SessionToken token = tokens.get(i);
+            if (context.getPackageName().equals(token.getPackageName())
+                    && id.equals(token.getId())) {
+                return token;
+            }
+        }
+        fail("Failed to find service");
+        return null;
+    }
+
+    /**
+     * Compares contents of two bundles.
+     *
+     * @param a a bundle
+     * @param b another bundle
+     * @return {@code true} if two bundles are the same. {@code false} otherwise. This may be
+     *     incorrect if any bundle contains a bundle.
+     */
+    public static boolean equals(Bundle a, Bundle b) {
+        if (a == b) {
+            return true;
+        }
+        if (a == null || b == null) {
+            return false;
+        }
+        if (!a.keySet().containsAll(b.keySet())
+                || !b.keySet().containsAll(a.keySet())) {
+            return false;
+        }
+        for (String key : a.keySet()) {
+            if (!Objects.equals(a.get(key), b.get(key))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Handler that always waits until the Runnable finishes.
+     */
+    public static class SyncHandler extends Handler {
+        public SyncHandler(Looper looper) {
+            super(looper);
+        }
+
+        public void postAndSync(Runnable runnable) throws InterruptedException {
+            final CountDownLatch latch = new CountDownLatch(1);
+            if (getLooper() == Looper.myLooper()) {
+                runnable.run();
+            } else {
+                post(()->{
+                    runnable.run();
+                    latch.countDown();
+                });
+                assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+            }
+        }
+    }
+}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 98108f3..e362530 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -311,7 +311,7 @@
         fullConfig.format = config->format;
         ret = AudioSystem::getOutputForAttr(attr, &io,
                                             actualSessionId,
-                                            &streamType, client.clientUid,
+                                            &streamType, client.clientPid, client.clientUid,
                                             &fullConfig,
                                             (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
                                                     AUDIO_OUTPUT_FLAG_DIRECT),
@@ -692,7 +692,7 @@
     output.selectedDeviceId = input.selectedDeviceId;
 
     lStatus = AudioSystem::getOutputForAttr(&input.attr, &output.outputId, sessionId, &streamType,
-                                            clientUid, &input.config, input.flags,
+                                            clientPid, clientUid, &input.config, input.flags,
                                             &output.selectedDeviceId, &portId);
 
     if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
@@ -995,6 +995,21 @@
     return mute;
 }
 
+void AudioFlinger::setRecordSilenced(uid_t uid, bool silenced)
+{
+    ALOGV("AudioFlinger::setRecordSilenced(uid:%d, silenced:%d)", uid, silenced);
+
+    // TODO: Notify MmapThreads
+
+    AutoMutex lock(mLock);
+    for (size_t i = 0; i < mRecordThreads.size(); i++) {
+        sp<RecordThread> thread = mRecordThreads.valueAt(i);
+        if (thread != 0) {
+            thread->setRecordSilenced(uid, silenced);
+        }
+    }
+}
+
 status_t AudioFlinger::setMasterMute(bool muted)
 {
     status_t ret = initCheck();
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index fc8af08..296f092 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -141,6 +141,8 @@
     virtual     status_t    setMicMute(bool state);
     virtual     bool        getMicMute() const;
 
+    virtual     void        setRecordSilenced(uid_t uid, bool silenced);
+
     virtual     status_t    setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs);
     virtual     String8     getParameters(audio_io_handle_t ioHandle, const String8& keys) const;
 
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index b13e551..979290f 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1027,7 +1027,9 @@
                 || size > mInConversionBuffer->getSize())) {
             mInConversionBuffer.clear();
             ALOGV("%s: allocating mInConversionBuffer %zu", __func__, size);
-            (void)EffectBufferHalInterface::allocate(size, &mInConversionBuffer);
+            sp<AudioFlinger> audioFlinger = mAudioFlinger.promote();
+            LOG_ALWAYS_FATAL_IF(audioFlinger == nullptr, "EM could not retrieved audioFlinger");
+            (void)audioFlinger->mEffectsFactoryHal->allocateBuffer(size, &mInConversionBuffer);
         }
         if (mInConversionBuffer.get() != nullptr) {
             mInConversionBuffer->setFrameCount(inFrameCount);
@@ -1071,7 +1073,9 @@
                 || size > mOutConversionBuffer->getSize())) {
             mOutConversionBuffer.clear();
             ALOGV("%s: allocating mOutConversionBuffer %zu", __func__, size);
-            (void)EffectBufferHalInterface::allocate(size, &mOutConversionBuffer);
+            sp<AudioFlinger> audioFlinger = mAudioFlinger.promote();
+            LOG_ALWAYS_FATAL_IF(audioFlinger == nullptr, "EM could not retrieved audioFlinger");
+            (void)audioFlinger->mEffectsFactoryHal->allocateBuffer(size, &mOutConversionBuffer);
         }
         if (mOutConversionBuffer.get() != nullptr) {
             mOutConversionBuffer->setFrameCount(outFrameCount);
@@ -2020,10 +2024,10 @@
         size_t numSamples = thread->frameCount();
         sp<EffectBufferHalInterface> halBuffer;
 #ifdef FLOAT_EFFECT_CHAIN
-        status_t result = EffectBufferHalInterface::allocate(
+        status_t result = thread->mAudioFlinger->mEffectsFactoryHal->allocateBuffer(
                 numSamples * sizeof(float), &halBuffer);
 #else
-        status_t result = EffectBufferHalInterface::allocate(
+        status_t result = thread->mAudioFlinger->mEffectsFactoryHal->allocateBuffer(
                 numSamples * sizeof(int32_t), &halBuffer);
 #endif
         if (result != OK) return result;
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index f8da780..63a3d98 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -63,6 +63,9 @@
 
     virtual bool        isFastTrack() const { return (mFlags & AUDIO_INPUT_FLAG_FAST) != 0; }
 
+            void        setSilenced(bool silenced) { mSilenced = silenced; }
+            bool        isSilenced() const { return mSilenced; }
+
 private:
     friend class AudioFlinger;  // for mState
 
@@ -91,6 +94,8 @@
             // used by the record thread to convert frames to proper destination format
             RecordBufferConverter              *mRecordBufferConverter;
             audio_input_flags_t                mFlags;
+
+            bool                               mSilenced;
 };
 
 // playback track, used by PatchPanel
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index c1044ef..f08698e 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -153,4 +153,11 @@
     return ok;
 }
 
+bool modifyPhoneStateAllowed(pid_t pid, uid_t uid) {
+    static const String16 sModifyPhoneState("android.permission.MODIFY_PHONE_STATE");
+    bool ok = checkPermission(sModifyPhoneState, pid, uid);
+    if (!ok) ALOGE("Request requires android.permission.MODIFY_PHONE_STATE");
+    return ok;
+}
+
 } // namespace android
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index 04cb9cd..83533dd 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -26,4 +26,5 @@
 bool settingsAllowed();
 bool modifyAudioRoutingAllowed();
 bool dumpAllowed();
+bool modifyPhoneStateAllowed(pid_t pid, uid_t uid);
 }
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 3a41ac8..70ac32c 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -56,6 +56,8 @@
 
 #include <powermanager/PowerManager.h>
 
+#include <media/audiohal/EffectsFactoryHalInterface.h>
+
 #include "AudioFlinger.h"
 #include "FastMixer.h"
 #include "FastCapture.h"
@@ -2882,7 +2884,7 @@
 {
     audio_session_t session = chain->sessionId();
     sp<EffectBufferHalInterface> halInBuffer, halOutBuffer;
-    status_t result = EffectBufferHalInterface::mirror(
+    status_t result = mAudioFlinger->mEffectsFactoryHal->mirrorBuffer(
             mEffectBufferEnabled ? mEffectBuffer : mSinkBuffer,
             mEffectBufferEnabled ? mEffectBufferSize : mSinkBufferSize,
             &halInBuffer);
@@ -2895,7 +2897,7 @@
         // the sink buffer as input
         if (mType != DIRECT) {
             size_t numSamples = mNormalFrameCount * mChannelCount;
-            status_t result = EffectBufferHalInterface::allocate(
+            status_t result = mAudioFlinger->mEffectsFactoryHal->allocateBuffer(
                     numSamples * sizeof(effect_buffer_t),
                     &halInBuffer);
             if (result != OK) return result;
@@ -6532,6 +6534,7 @@
         rear = mRsmpInRear += framesRead;
 
         size = activeTracks.size();
+
         // loop over each active track
         for (size_t i = 0; i < size; i++) {
             activeTrack = activeTracks[i];
@@ -6588,6 +6591,11 @@
                 if (activeTrack->mFramesToDrop == 0) {
                     if (framesOut > 0) {
                         activeTrack->mSink.frameCount = framesOut;
+                        // Sanitize before releasing if the track has no access to the source data
+                        // An idle UID receives silence from non virtual devices until active
+                        if (activeTrack->isSilenced()) {
+                            memset(activeTrack->mSink.raw, 0, framesOut * mFrameSize);
+                        }
                         activeTrack->releaseBuffer(&activeTrack->mSink);
                     }
                 } else {
@@ -6927,7 +6935,9 @@
         status_t status = NO_ERROR;
         if (recordTrack->isExternalTrack()) {
             mLock.unlock();
-            status = AudioSystem::startInput(mId, recordTrack->sessionId());
+            bool silenced;
+            status = AudioSystem::startInput(mId, recordTrack->sessionId(),
+                    mInDevice, recordTrack->uid(), &silenced);
             mLock.lock();
             // FIXME should verify that recordTrack is still in mActiveTracks
             if (status != NO_ERROR) {
@@ -6936,6 +6946,7 @@
                 ALOGV("RecordThread::start error %d", status);
                 return status;
             }
+            recordTrack->setSilenced(silenced);
         }
         // Catch up with current buffer indices if thread is already running.
         // This is what makes a new client discard all buffered data.  If the track's mRsmpInFront
@@ -7139,6 +7150,16 @@
     write(fd, result.string(), result.size());
 }
 
+void AudioFlinger::RecordThread::setRecordSilenced(uid_t uid, bool silenced)
+{
+    Mutex::Autolock _l(mLock);
+    for (size_t i = 0; i < mTracks.size() ; i++) {
+        sp<RecordTrack> track = mTracks[i];
+        if (track != 0 && track->uid() == uid) {
+            track->setSilenced(silenced);
+        }
+    }
+}
 
 void AudioFlinger::RecordThread::ResamplerBufferProvider::reset()
 {
@@ -7800,6 +7821,7 @@
         ret = AudioSystem::getOutputForAttr(&mAttr, &io,
                                             mSessionId,
                                             &stream,
+                                            client.clientPid,
                                             client.clientUid,
                                             &config,
                                             flags,
@@ -7831,7 +7853,9 @@
     if (isOutput()) {
         ret = AudioSystem::startOutput(mId, streamType(), mSessionId);
     } else {
-        ret = AudioSystem::startInput(mId, mSessionId);
+        // TODO: Block recording for idle UIDs (b/72134552)
+        bool silenced;
+        ret = AudioSystem::startInput(mId, mSessionId, mInDevice, client.clientUid, &silenced);
     }
 
     // abort if start is rejected by audio policy manager
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 17f26c5..41d87a4 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1396,6 +1396,9 @@
 
             void        checkBtNrec();
 
+            // Sets the UID records silence
+            void        setRecordSilenced(uid_t uid, bool silenced);
+
 private:
             // Enter standby if not already in standby, and set mStandby flag
             void    standbyIfNotAlreadyInStandby();
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index f2cb25f..7f09e9b 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -65,15 +65,15 @@
         API_INPUT_TELEPHONY_RX, // used for capture from telephony RX path
     } input_type_t;
 
-   enum {
+    enum {
         API_INPUT_CONCURRENCY_NONE = 0,
         API_INPUT_CONCURRENCY_CALL = (1 << 0),      // Concurrency with a call
         API_INPUT_CONCURRENCY_CAPTURE = (1 << 1),   // Concurrency with another capture
 
         API_INPUT_CONCURRENCY_ALL = (API_INPUT_CONCURRENCY_CALL | API_INPUT_CONCURRENCY_CAPTURE),
-   };
+    };
 
-   typedef uint32_t concurrency_type__mask_t;
+    typedef uint32_t concurrency_type__mask_t;
 
 public:
     virtual ~AudioPolicyInterface() {}
@@ -116,7 +116,7 @@
                                         audio_stream_type_t *stream,
                                         uid_t uid,
                                         const audio_config_t *config,
-                                        audio_output_flags_t flags,
+                                        audio_output_flags_t *flags,
                                         audio_port_handle_t *selectedDeviceId,
                                         audio_port_handle_t *portId) = 0;
     // indicates to the audio policy manager that the output starts being used by corresponding stream.
@@ -145,6 +145,7 @@
     // indicates to the audio policy manager that the input starts being used.
     virtual status_t startInput(audio_io_handle_t input,
                                 audio_session_t session,
+                                bool silenced,
                                 concurrency_type__mask_t *concurrency) = 0;
     // indicates to the audio policy manager that the input stops being used.
     virtual status_t stopInput(audio_io_handle_t input,
@@ -239,6 +240,8 @@
 
     virtual float    getStreamVolumeDB(
                 audio_stream_type_t stream, int index, audio_devices_t device) = 0;
+
+    virtual void     setRecordSilenced(uid_t uid, bool silenced);
 };
 
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 2803ec1..cd2174d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -187,6 +187,15 @@
     bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
 
     /**
+     * return whether a stream is playing, but not on a "remote" device.
+     * Override to change the definition of a local/remote playback.
+     * Used for instance by policy manager to alter the speaker playback ("speaker safe" behavior)
+     * when media plays or not locally.
+     * For the base implementation, "remotely" means playing during screen mirroring.
+     */
+    bool isStreamActiveLocally(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
+
+    /**
      * returns the A2DP output handle if it is open or 0 otherwise
      */
     audio_io_handle_t getA2dpOutput() const;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
index 0d19373..dd5247d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
@@ -55,6 +55,8 @@
     void setUid(uid_t uid) { mRecordClientInfo.uid = uid; }
     bool matches(const sp<AudioSession> &other) const;
     bool isSoundTrigger() const { return mIsSoundTrigger; }
+    void setSilenced(bool silenced) { mSilenced = silenced; }
+    bool isSilenced() const { return mSilenced; }
     uint32_t openCount() const { return mOpenCount; } ;
     uint32_t activeCount() const { return mActiveCount; } ;
 
@@ -70,6 +72,7 @@
     const struct audio_config_base mConfig;
     const audio_input_flags_t mFlags;
     bool  mIsSoundTrigger;
+    bool mSilenced;
     uint32_t  mOpenCount;
     uint32_t  mActiveCount;
     AudioMix* mPolicyMix; // non NULL when used by a dynamic policy
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 044d6db..17fc272 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -333,6 +333,10 @@
             return true;
         }
     }
+    if (device == AUDIO_DEVICE_OUT_TELEPHONY_TX) {
+        ALOGV("max gain when output device is telephony tx");
+        return true;
+    }
     return false;
 }
 
@@ -575,6 +579,19 @@
     return false;
 }
 
+bool SwAudioOutputCollection::isStreamActiveLocally(audio_stream_type_t stream, uint32_t inPastMs) const
+{
+    nsecs_t sysTime = systemTime();
+    for (size_t i = 0; i < this->size(); i++) {
+        const sp<SwAudioOutputDescriptor> outputDesc = this->valueAt(i);
+        if (outputDesc->isStreamActive(stream, inPastMs, sysTime)
+                && ((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) == 0)) {
+            return true;
+        }
+    }
+    return false;
+}
+
 bool SwAudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream,
                                                    uint32_t inPastMs) const
 {
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 9bdb98c..5ec0475 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -238,18 +238,19 @@
     const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
 
     return getDeviceForStrategyInt(strategy, availableOutputDevices,
-                                   availableInputDevices, outputs);
+                                   availableInputDevices, outputs, (uint32_t)AUDIO_DEVICE_NONE);
 }
 
 
-
 audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy,
-                                                DeviceVector availableOutputDevices,
-                                                DeviceVector availableInputDevices,
-                                                const SwAudioOutputCollection &outputs) const
+        DeviceVector availableOutputDevices,
+        DeviceVector availableInputDevices,
+        const SwAudioOutputCollection &outputs,
+        uint32_t outputDeviceTypesToIgnore) const
 {
     uint32_t device = AUDIO_DEVICE_NONE;
-    uint32_t availableOutputDevicesType = availableOutputDevices.types();
+    uint32_t availableOutputDevicesType =
+            availableOutputDevices.types() & ~outputDeviceTypesToIgnore;
 
     switch (strategy) {
 
@@ -260,38 +261,24 @@
     case STRATEGY_SONIFICATION_RESPECTFUL:
         if (isInCall()) {
             device = getDeviceForStrategyInt(
-                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
-        } else if (outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC,
-                SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
-            // while media is playing on a remote device, use the the sonification behavior.
-            // Note that we test this usecase before testing if media is playing because
-            //   the isStreamActive() method only informs about the activity of a stream, not
-            //   if it's for local playback. Note also that we use the same delay between both tests
-            device = getDeviceForStrategyInt(
-                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
-            //user "safe" speaker if available instead of normal speaker to avoid triggering
-            //other acoustic safety mechanisms for notification
-            if ((device & AUDIO_DEVICE_OUT_SPEAKER) &&
-                    (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
-                device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
-                device &= ~AUDIO_DEVICE_OUT_SPEAKER;
-            }
-        } else if (outputs.isStreamActive(
-                                AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
-                    || outputs.isStreamActive(
-                            AUDIO_STREAM_ACCESSIBILITY, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY))
-        {
-            // while media/a11y is playing (or has recently played), use the same device
-            device = getDeviceForStrategyInt(
-                    STRATEGY_MEDIA, availableOutputDevices, availableInputDevices, outputs);
+                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs,
+                    outputDeviceTypesToIgnore);
         } else {
-            // when media is not playing anymore, fall back on the sonification behavior
-            device = getDeviceForStrategyInt(
-                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
-            //user "safe" speaker if available instead of normal speaker to avoid triggering
-            //other acoustic safety mechanisms for notification
-            if ((device & AUDIO_DEVICE_OUT_SPEAKER) &&
-                    (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+            bool media_active_locally =
+                    outputs.isStreamActiveLocally(
+                            AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)
+                    || outputs.isStreamActiveLocally(
+                            AUDIO_STREAM_ACCESSIBILITY, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY);
+            // routing is same as media without the "remote" device
+            device = getDeviceForStrategyInt(STRATEGY_MEDIA,
+                    availableOutputDevices,
+                    availableInputDevices, outputs,
+                    AUDIO_DEVICE_OUT_REMOTE_SUBMIX | outputDeviceTypesToIgnore);
+            // if no media is playing on the device, check for mandatory use of "safe" speaker
+            // when media would have played on speaker, and the safe speaker path is available
+            if (!media_active_locally
+                    && (device & AUDIO_DEVICE_OUT_SPEAKER)
+                    && (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
                 device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
                 device &= ~AUDIO_DEVICE_OUT_SPEAKER;
             }
@@ -302,7 +289,8 @@
         if (!isInCall()) {
             // when off call, DTMF strategy follows the same rules as MEDIA strategy
             device = getDeviceForStrategyInt(
-                    STRATEGY_MEDIA, availableOutputDevices, availableInputDevices, outputs);
+                    STRATEGY_MEDIA, availableOutputDevices, availableInputDevices, outputs,
+                    outputDeviceTypesToIgnore);
             break;
         }
         // when in call, DTMF and PHONE strategies follow the same rules
@@ -408,7 +396,8 @@
         // handleIncallSonification().
         if (isInCall()) {
             device = getDeviceForStrategyInt(
-                    STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
+                    STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs,
+                    outputDeviceTypesToIgnore);
             break;
         }
         // FALL THROUGH
@@ -463,11 +452,13 @@
             if (outputs.isStreamActive(AUDIO_STREAM_RING) ||
                     outputs.isStreamActive(AUDIO_STREAM_ALARM)) {
                 return getDeviceForStrategyInt(
-                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs);
+                    STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs,
+                    outputDeviceTypesToIgnore);
             }
             if (isInCall()) {
                 return getDeviceForStrategyInt(
-                        STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
+                        STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs,
+                        outputDeviceTypesToIgnore);
             }
         }
         // For other cases, STRATEGY_ACCESSIBILITY behaves like STRATEGY_MEDIA
@@ -486,7 +477,8 @@
         }
         if (isInCall() && (strategy == STRATEGY_MEDIA)) {
             device = getDeviceForStrategyInt(
-                    STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs);
+                    STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs,
+                    outputDeviceTypesToIgnore);
             break;
         }
         if ((device2 == AUDIO_DEVICE_NONE) &&
diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h
index 57538c4..06186c1 100644
--- a/services/audiopolicy/enginedefault/src/Engine.h
+++ b/services/audiopolicy/enginedefault/src/Engine.h
@@ -126,9 +126,10 @@
     routing_strategy getStrategyForUsage(audio_usage_t usage);
     audio_devices_t getDeviceForStrategy(routing_strategy strategy) const;
     audio_devices_t getDeviceForStrategyInt(routing_strategy strategy,
-                                            DeviceVector availableOutputDevices,
-                                            DeviceVector availableInputDevices,
-                                            const SwAudioOutputCollection &outputs) const;
+            DeviceVector availableOutputDevices,
+            DeviceVector availableInputDevices,
+            const SwAudioOutputCollection &outputs,
+            uint32_t outputDeviceTypesToIgnore) const;
     audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const;
     audio_mode_t mPhoneState;  /**< current phone state. */
 
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 33f31e0..7343601 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -741,7 +741,7 @@
                                               audio_stream_type_t *stream,
                                               uid_t uid,
                                               const audio_config_t *config,
-                                              audio_output_flags_t flags,
+                                              audio_output_flags_t *flags,
                                               audio_port_handle_t *selectedDeviceId,
                                               audio_port_handle_t *portId)
 {
@@ -801,12 +801,12 @@
     audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
 
     if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {
-        flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
+        *flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
     }
 
     ALOGV("getOutputForAttr() device 0x%x, sampling rate %d, format %#x, channel mask %#x, "
           "flags %#x",
-          device, config->sample_rate, config->format, config->channel_mask, flags);
+          device, config->sample_rate, config->format, config->channel_mask, *flags);
 
     *output = getOutputForDevice(device, session, *stream, config, flags);
     if (*output == AUDIO_IO_HANDLE_NONE) {
@@ -828,7 +828,7 @@
         audio_session_t session,
         audio_stream_type_t stream,
         const audio_config_t *config,
-        audio_output_flags_t flags)
+        audio_output_flags_t *flags)
 {
     audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
     status_t status;
@@ -837,35 +837,41 @@
     //force direct flag if offload flag is set: offloading implies a direct output stream
     // and all common behaviors are driven by checking only the direct flag
     // this should normally be set appropriately in the policy configuration file
-    if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
-        flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT);
+    if ((*flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
+        *flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT);
     }
-    if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) {
-        flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT);
+    if ((*flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) {
+        *flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT);
     }
     // only allow deep buffering for music stream type
     if (stream != AUDIO_STREAM_MUSIC) {
-        flags = (audio_output_flags_t)(flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);
+        *flags = (audio_output_flags_t)(*flags &~AUDIO_OUTPUT_FLAG_DEEP_BUFFER);
     } else if (/* stream == AUDIO_STREAM_MUSIC && */
-            flags == AUDIO_OUTPUT_FLAG_NONE &&
+            *flags == AUDIO_OUTPUT_FLAG_NONE &&
             property_get_bool("audio.deep_buffer.media", false /* default_value */)) {
         // use DEEP_BUFFER as default output for music stream type
-        flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+        *flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
     }
     if (stream == AUDIO_STREAM_TTS) {
-        flags = AUDIO_OUTPUT_FLAG_TTS;
+        *flags = AUDIO_OUTPUT_FLAG_TTS;
     } else if (stream == AUDIO_STREAM_VOICE_CALL &&
                audio_is_linear_pcm(config->format)) {
-        flags = (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_VOIP_RX |
+        *flags = (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_VOIP_RX |
                                        AUDIO_OUTPUT_FLAG_DIRECT);
         ALOGV("Set VoIP and Direct output flags for PCM format");
+    } else if (device == AUDIO_DEVICE_OUT_TELEPHONY_TX &&
+        stream == AUDIO_STREAM_MUSIC &&
+        audio_is_linear_pcm(config->format) &&
+        isInCall()) {
+        *flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
     }
 
+
     sp<IOProfile> profile;
 
     // skip direct output selection if the request can obviously be attached to a mixed output
     // and not explicitly requested
-    if (((flags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) &&
+    if (((*flags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) &&
             audio_is_linear_pcm(config->format) && config->sample_rate <= SAMPLE_RATE_HZ_MAX &&
             audio_channel_count_from_out_mask(config->channel_mask) <= 2) {
         goto non_direct_output;
@@ -878,13 +884,13 @@
     // This may prevent offloading in rare situations where effects are left active by apps
     // in the background.
 
-    if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) ||
+    if (((*flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) ||
             !(mEffects.isNonOffloadableEffectEnabled() || mMasterMono)) {
         profile = getProfileForDirectOutput(device,
                                            config->sample_rate,
                                            config->format,
                                            config->channel_mask,
-                                           (audio_output_flags_t)flags);
+                                           (audio_output_flags_t)*flags);
     }
 
     if (profile != 0) {
@@ -916,7 +922,7 @@
         String8 address = outputDevices.size() > 0 ? outputDevices.itemAt(0)->mAddress
                 : String8("");
 
-        status = outputDesc->open(config, device, address, stream, flags, &output);
+        status = outputDesc->open(config, device, address, stream, *flags, &output);
 
         // only accept an output with the requested parameters
         if (status != NO_ERROR ||
@@ -954,7 +960,7 @@
 
     // A request for HW A/V sync cannot fallback to a mixed output because time
     // stamps are embedded in audio data
-    if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) {
+    if ((*flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) {
         return AUDIO_IO_HANDLE_NONE;
     }
 
@@ -969,12 +975,12 @@
         SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs);
 
         // at this stage we should ignore the DIRECT flag as no direct output could be found earlier
-        flags = (audio_output_flags_t)(flags & ~AUDIO_OUTPUT_FLAG_DIRECT);
-        output = selectOutput(outputs, flags, config->format);
+        *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_DIRECT);
+        output = selectOutput(outputs, *flags, config->format);
     }
     ALOGW_IF((output == 0), "getOutputForDevice() could not find output for stream %d, "
             "sampling rate %d, format %#x, channels %#x, flags %#x",
-            stream, config->sample_rate, config->format, config->channel_mask, flags);
+            stream, config->sample_rate, config->format, config->channel_mask, *flags);
 
     return output;
 }
@@ -1801,10 +1807,15 @@
 
 status_t AudioPolicyManager::startInput(audio_io_handle_t input,
                                         audio_session_t session,
+                                        bool silenced,
                                         concurrency_type__mask_t *concurrency)
 {
-    ALOGV("startInput() input %d", input);
+
+    ALOGV("AudioPolicyManager::startInput(input:%d, session:%d, silenced:%d, concurrency:%d)",
+            input, session, silenced, *concurrency);
+
     *concurrency = API_INPUT_CONCURRENCY_NONE;
+
     ssize_t index = mInputs.indexOfKey(input);
     if (index < 0) {
         ALOGW("startInput() unknown input %d", input);
@@ -1839,12 +1850,33 @@
             return INVALID_OPERATION;
         }
 
-        Vector< sp<AudioInputDescriptor> > activeInputs = mInputs.getActiveInputs();
-        for (const auto& activeDesc : activeInputs) {
-            if (is_virtual_input_device(activeDesc->mDevice)) {
-                continue;
-            }
+        Vector<sp<AudioInputDescriptor>> activeInputs = mInputs.getActiveInputs();
 
+        // If a UID is idle and records silence and another not silenced recording starts
+        // from another UID (idle or active) we stop the current idle UID recording in
+        // favor of the new one - "There can be only one" TM
+        if (!silenced) {
+            for (const auto& activeDesc : activeInputs) {
+                if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
+                        activeDesc->getId() == inputDesc->getId()) {
+                     continue;
+                }
+
+                AudioSessionCollection activeSessions = activeDesc->getAudioSessions(
+                        true /*activeOnly*/);
+                sp<AudioSession> activeSession = activeSessions.valueAt(0);
+                if (activeSession->isSilenced()) {
+                    audio_io_handle_t activeInput = activeDesc->mIoHandle;
+                    audio_session_t activeSessionId = activeSession->session();
+                    stopInput(activeInput, activeSessionId);
+                    releaseInput(activeInput, activeSessionId);
+                    ALOGV("startInput(%d) stopping silenced input %d", input, activeInput);
+                    activeInputs = mInputs.getActiveInputs();
+                }
+            }
+        }
+
+        for (const auto& activeDesc : activeInputs) {
             if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
                     activeDesc->getId() == inputDesc->getId()) {
                 continue;
@@ -1881,10 +1913,6 @@
 
         // if capture is allowed, preempt currently active HOTWORD captures
         for (const auto& activeDesc : activeInputs) {
-            if (is_virtual_input_device(activeDesc->mDevice)) {
-                continue;
-            }
-
             if (allowConcurrentWithSoundTrigger && activeDesc->isSoundTrigger()) {
                 continue;
             }
@@ -1907,6 +1935,9 @@
     }
 #endif
 
+    // Make sure we start with the correct silence state
+    audioSession->setSilenced(silenced);
+
     // increment activity count before calling getNewInputDevice() below as only active sessions
     // are considered for device selection
     audioSession->changeActiveCount(1);
@@ -2039,7 +2070,6 @@
 void AudioPolicyManager::releaseInput(audio_io_handle_t input,
                                       audio_session_t session)
 {
-
     ALOGV("releaseInput() %d", input);
     ssize_t index = mInputs.indexOfKey(input);
     if (index < 0) {
@@ -2909,7 +2939,7 @@
                 sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[i], &patch->sinks[i]);
 
                 // create a software bridge in PatchPanel if:
-                // - source and sink devices are on differnt HW modules OR
+                // - source and sink devices are on different HW modules OR
                 // - audio HAL version is < 3.0
                 if (!srcDeviceDesc->hasSameHwModuleAs(sinkDeviceDesc) ||
                         (srcDeviceDesc->mModule->getHalVersionMajor() < 3)) {
@@ -3394,6 +3424,23 @@
     return computeVolume(stream, index, device);
 }
 
+void AudioPolicyManager::setRecordSilenced(uid_t uid, bool silenced)
+{
+    ALOGV("AudioPolicyManager:setRecordSilenced(uid:%d, silenced:%d)", uid, silenced);
+
+    Vector<sp<AudioInputDescriptor> > activeInputs = mInputs.getActiveInputs();
+    for (size_t i = 0; i < activeInputs.size(); i++) {
+        sp<AudioInputDescriptor> activeDesc = activeInputs[i];
+        AudioSessionCollection activeSessions = activeDesc->getAudioSessions(true);
+        for (size_t j = 0; j < activeSessions.size(); j++) {
+            sp<AudioSession> activeSession = activeSessions.valueAt(j);
+            if (activeSession->uid() == uid) {
+                activeSession->setSilenced(silenced);
+            }
+        }
+    }
+}
+
 status_t AudioPolicyManager::disconnectAudioSource(const sp<AudioSourceDescriptor>& sourceDesc)
 {
     ALOGV("%s handle %d", __FUNCTION__, sourceDesc->getHandle());
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 611edec..2b68882 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -110,7 +110,7 @@
                                           audio_stream_type_t *stream,
                                           uid_t uid,
                                           const audio_config_t *config,
-                                          audio_output_flags_t flags,
+                                          audio_output_flags_t *flags,
                                           audio_port_handle_t *selectedDeviceId,
                                           audio_port_handle_t *portId);
         virtual status_t startOutput(audio_io_handle_t output,
@@ -135,6 +135,7 @@
         // indicates to the audio policy manager that the input starts being used.
         virtual status_t startInput(audio_io_handle_t input,
                                     audio_session_t session,
+                                    bool silenced,
                                     concurrency_type__mask_t *concurrency);
 
         // indicates to the audio policy manager that the input stops being used.
@@ -235,6 +236,8 @@
         // return the strategy corresponding to a given stream type
         routing_strategy getStrategy(audio_stream_type_t stream) const;
 
+        virtual void setRecordSilenced(uid_t uid, bool silenced);
+
 protected:
         // A constructor that allows more fine-grained control over initialization process,
         // used in automatic tests.
@@ -626,7 +629,7 @@
                 audio_session_t session,
                 audio_stream_type_t stream,
                 const audio_config_t *config,
-                audio_output_flags_t flags);
+                audio_output_flags_t *flags);
         // internal method to return the input handle for the given device and format
         audio_io_handle_t getInputForDevice(audio_devices_t device,
                 String8 address,
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 7dd6d70..f1d7d86 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -158,6 +158,7 @@
                                               audio_io_handle_t *output,
                                               audio_session_t session,
                                               audio_stream_type_t *stream,
+                                              pid_t pid,
                                               uid_t uid,
                                               const audio_config_t *config,
                                               audio_output_flags_t flags,
@@ -176,9 +177,26 @@
                 "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid);
         uid = callingUid;
     }
-    return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
+    audio_output_flags_t originalFlags = flags;
+    status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
                                                  config,
-                                                 flags, selectedDeviceId, portId);
+                                                 &flags, selectedDeviceId, portId);
+
+    // FIXME: Introduce a way to check for the the telephony device before opening the output
+    if ((result == NO_ERROR) &&
+        (flags & AUDIO_OUTPUT_FLAG_INCALL_MUSIC) &&
+        !modifyPhoneStateAllowed(pid, uid)) {
+        // If the app tries to play music through the telephony device and doesn't have permission
+        // the fallback to the default output device.
+        mAudioPolicyManager->releaseOutput(*output, *stream, session);
+        flags = originalFlags;
+        *selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+        *portId = AUDIO_PORT_HANDLE_NONE;
+        result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
+                                                 config,
+                                                 &flags, selectedDeviceId, portId);
+    }
+    return result;
 }
 
 status_t AudioPolicyService::startOutput(audio_io_handle_t output,
@@ -362,14 +380,22 @@
 }
 
 status_t AudioPolicyService::startInput(audio_io_handle_t input,
-                                        audio_session_t session)
+                                        audio_session_t session,
+                                        audio_devices_t device,
+                                        uid_t uid,
+                                        bool *silenced)
 {
+    // If UID inactive it records silence until becoming active
+    *silenced = !mUidPolicy->isUidActive(uid) && !is_virtual_input_device(device);
+
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
     }
+
     Mutex::Autolock _l(mLock);
-    AudioPolicyInterface::concurrency_type__mask_t concurrency;
-    status_t status = mAudioPolicyManager->startInput(input, session, &concurrency);
+    AudioPolicyInterface::concurrency_type__mask_t concurrency =
+            AudioPolicyInterface::API_INPUT_CONCURRENCY_NONE;
+    status_t status = mAudioPolicyManager->startInput(input, session, *silenced, &concurrency);
 
     if (status == NO_ERROR) {
         LOG_ALWAYS_FATAL_IF(concurrency & ~AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL,
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index af0c823..e5aed9a 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -28,6 +28,9 @@
 #include <utils/Log.h>
 #include <cutils/properties.h>
 #include <binder/IPCThreadState.h>
+#include <binder/ActivityManager.h>
+#include <binder/PermissionController.h>
+#include <binder/IResultReceiver.h>
 #include <utils/String16.h>
 #include <utils/threads.h>
 #include "AudioPolicyService.h"
@@ -39,6 +42,8 @@
 #include <system/audio.h>
 #include <system/audio_policy.h>
 
+#include <private/android_filesystem_config.h>
+
 namespace android {
 
 static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n";
@@ -49,6 +54,7 @@
 
 static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds
 
+static const String16 sManageAudioPolicyPermission("android.permission.MANAGE_AUDIO_POLICY");
 
 // ----------------------------------------------------------------------------
 
@@ -79,6 +85,9 @@
         Mutex::Autolock _l(mLock);
         mAudioPolicyEffects = audioPolicyEffects;
     }
+
+    mUidPolicy = new UidPolicy(this);
+    mUidPolicy->registerSelf();
 }
 
 AudioPolicyService::~AudioPolicyService()
@@ -92,6 +101,9 @@
 
     mNotificationClients.clear();
     mAudioPolicyEffects.clear();
+
+    mUidPolicy->unregisterSelf();
+    mUidPolicy.clear();
 }
 
 // A notification client is always registered by AudioSystem when the client process
@@ -318,6 +330,20 @@
     return NO_ERROR;
 }
 
+void AudioPolicyService::setRecordSilenced(uid_t uid, bool silenced)
+{
+    {
+        Mutex::Autolock _l(mLock);
+        if (mAudioPolicyManager) {
+            mAudioPolicyManager->setRecordSilenced(uid, silenced);
+        }
+    }
+    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+    if (af) {
+        af->setRecordSilenced(uid, silenced);
+    }
+}
+
 status_t AudioPolicyService::dump(int fd, const Vector<String16>& args __unused)
 {
     if (!dumpAllowed()) {
@@ -361,11 +387,210 @@
 }
 
 status_t AudioPolicyService::onTransact(
-        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
+        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+    switch (code) {
+        case SHELL_COMMAND_TRANSACTION: {
+            int in = data.readFileDescriptor();
+            int out = data.readFileDescriptor();
+            int err = data.readFileDescriptor();
+            int argc = data.readInt32();
+            Vector<String16> args;
+            for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
+               args.add(data.readString16());
+            }
+            sp<IBinder> unusedCallback;
+            sp<IResultReceiver> resultReceiver;
+            status_t status;
+            if ((status = data.readNullableStrongBinder(&unusedCallback)) != NO_ERROR) {
+                return status;
+            }
+            if ((status = data.readNullableStrongBinder(&resultReceiver)) != NO_ERROR) {
+                return status;
+            }
+            status = shellCommand(in, out, err, args);
+            if (resultReceiver != nullptr) {
+                resultReceiver->send(status);
+            }
+            return NO_ERROR;
+        }
+    }
+
     return BnAudioPolicyService::onTransact(code, data, reply, flags);
 }
 
+// ------------------- Shell command implementation -------------------
+
+// NOTE: This is a remote API - make sure all args are validated
+status_t AudioPolicyService::shellCommand(int in, int out, int err, Vector<String16>& args) {
+    if (!checkCallingPermission(sManageAudioPolicyPermission, nullptr, nullptr)) {
+        return PERMISSION_DENIED;
+    }
+    if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
+        return BAD_VALUE;
+    }
+    if (args.size() == 3 && args[0] == String16("set-uid-state")) {
+        return handleSetUidState(args, err);
+    } else if (args.size() == 2 && args[0] == String16("reset-uid-state")) {
+        return handleResetUidState(args, err);
+    } else if (args.size() == 2 && args[0] == String16("get-uid-state")) {
+        return handleGetUidState(args, out, err);
+    } else if (args.size() == 1 && args[0] == String16("help")) {
+        printHelp(out);
+        return NO_ERROR;
+    }
+    printHelp(err);
+    return BAD_VALUE;
+}
+
+status_t AudioPolicyService::handleSetUidState(Vector<String16>& args, int err) {
+    PermissionController pc;
+    int uid = pc.getPackageUid(args[1], 0);
+    if (uid <= 0) {
+        ALOGE("Unknown package: '%s'", String8(args[1]).string());
+        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+        return BAD_VALUE;
+    }
+    bool active = false;
+    if (args[2] == String16("active")) {
+        active = true;
+    } else if ((args[2] != String16("idle"))) {
+        ALOGE("Expected active or idle but got: '%s'", String8(args[2]).string());
+        return BAD_VALUE;
+    }
+    mUidPolicy->addOverrideUid(uid, active);
+    return NO_ERROR;
+}
+
+status_t AudioPolicyService::handleResetUidState(Vector<String16>& args, int err) {
+    PermissionController pc;
+    int uid = pc.getPackageUid(args[1], 0);
+    if (uid < 0) {
+        ALOGE("Unknown package: '%s'", String8(args[1]).string());
+        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+        return BAD_VALUE;
+    }
+    mUidPolicy->removeOverrideUid(uid);
+    return NO_ERROR;
+}
+
+status_t AudioPolicyService::handleGetUidState(Vector<String16>& args, int out, int err) {
+    PermissionController pc;
+    int uid = pc.getPackageUid(args[1], 0);
+    if (uid < 0) {
+        ALOGE("Unknown package: '%s'", String8(args[1]).string());
+        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+        return BAD_VALUE;
+    }
+    if (mUidPolicy->isUidActive(uid)) {
+        return dprintf(out, "active\n");
+    } else {
+        return dprintf(out, "idle\n");
+    }
+}
+
+status_t AudioPolicyService::printHelp(int out) {
+    return dprintf(out, "Audio policy service commands:\n"
+        "  get-uid-state <PACKAGE> gets the uid state\n"
+        "  set-uid-state <PACKAGE> <active|idle> overrides the uid state\n"
+        "  reset-uid-state <PACKAGE> clears the uid state override\n"
+        "  help print this message\n");
+}
+
+// -----------  AudioPolicyService::UidPolicy implementation ----------
+
+void AudioPolicyService::UidPolicy::registerSelf() {
+    ActivityManager am;
+    am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
+            | ActivityManager::UID_OBSERVER_IDLE
+            | ActivityManager::UID_OBSERVER_ACTIVE,
+            ActivityManager::PROCESS_STATE_UNKNOWN,
+            String16("audioserver"));
+}
+
+void AudioPolicyService::UidPolicy::unregisterSelf() {
+    ActivityManager am;
+    am.unregisterUidObserver(this);
+}
+
+void AudioPolicyService::UidPolicy::onUidGone(uid_t uid, __unused bool disabled) {
+    onUidIdle(uid, disabled);
+}
+
+void AudioPolicyService::UidPolicy::onUidActive(uid_t uid) {
+    {
+        Mutex::Autolock _l(mUidLock);
+        mActiveUids.insert(uid);
+    }
+    sp<AudioPolicyService> service = mService.promote();
+    if (service != nullptr) {
+        service->setRecordSilenced(uid, false);
+    }
+}
+
+void AudioPolicyService::UidPolicy::onUidIdle(uid_t uid, __unused bool disabled) {
+    bool deleted = false;
+    {
+        Mutex::Autolock _l(mUidLock);
+        if (mActiveUids.erase(uid) > 0) {
+            deleted = true;
+        }
+    }
+    if (deleted) {
+        sp<AudioPolicyService> service = mService.promote();
+        if (service != nullptr) {
+            service->setRecordSilenced(uid, true);
+        }
+    }
+}
+
+void AudioPolicyService::UidPolicy::addOverrideUid(uid_t uid, bool active) {
+    updateOverrideUid(uid, active, true);
+}
+
+void AudioPolicyService::UidPolicy::removeOverrideUid(uid_t uid) {
+    updateOverrideUid(uid, false, false);
+}
+
+void AudioPolicyService::UidPolicy::updateOverrideUid(uid_t uid, bool active, bool insert) {
+    bool wasActive = false;
+    bool isActive = false;
+    {
+        Mutex::Autolock _l(mUidLock);
+        wasActive = isUidActiveLocked(uid);
+        mOverrideUids.erase(uid);
+        if (insert) {
+            mOverrideUids.insert(std::pair<uid_t, bool>(uid, active));
+        }
+        isActive = isUidActiveLocked(uid);
+    }
+    if (wasActive != isActive) {
+        sp<AudioPolicyService> service = mService.promote();
+        if (service != nullptr) {
+            service->setRecordSilenced(uid, !isActive);
+        }
+    }
+}
+
+bool AudioPolicyService::UidPolicy::isUidActive(uid_t uid) {
+    // Non-app UIDs are considered always active
+    if (uid < FIRST_APPLICATION_UID) {
+        return true;
+    }
+    Mutex::Autolock _l(mUidLock);
+    return isUidActiveLocked(uid);
+}
+
+bool AudioPolicyService::UidPolicy::isUidActiveLocked(uid_t uid) {
+    // Non-app UIDs are considered always active
+    if (uid < FIRST_APPLICATION_UID) {
+        return true;
+    }
+    auto it = mOverrideUids.find(uid);
+    if (it != mOverrideUids.end()) {
+        return it->second;
+    }
+    return mActiveUids.find(uid) != mActiveUids.end();
+}
 
 // -----------  AudioPolicyService::AudioCommandThread implementation ----------
 
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 833a230..c21aa58 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -24,6 +24,7 @@
 #include <utils/Vector.h>
 #include <utils/SortedVector.h>
 #include <binder/BinderService.h>
+#include <binder/IUidObserver.h>
 #include <system/audio.h>
 #include <system/audio_policy.h>
 #include <media/IAudioPolicyService.h>
@@ -33,9 +34,13 @@
 #include "AudioPolicyEffects.h"
 #include "managerdefault/AudioPolicyManager.h"
 
+#include <unordered_map>
+#include <unordered_set>
 
 namespace android {
 
+using namespace std;
+
 // ----------------------------------------------------------------------------
 
 class AudioPolicyService :
@@ -73,6 +78,7 @@
                                       audio_io_handle_t *output,
                                       audio_session_t session,
                                       audio_stream_type_t *stream,
+                                      pid_t pid,
                                       uid_t uid,
                                       const audio_config_t *config,
                                       audio_output_flags_t flags,
@@ -97,7 +103,10 @@
                                      audio_port_handle_t *selectedDeviceId = NULL,
                                      audio_port_handle_t *portId = NULL);
     virtual status_t startInput(audio_io_handle_t input,
-                                audio_session_t session);
+                                audio_session_t session,
+                                audio_devices_t device,
+                                uid_t uid,
+                                bool *silenced);
     virtual status_t stopInput(audio_io_handle_t input,
                                audio_session_t session);
     virtual void releaseInput(audio_io_handle_t input,
@@ -235,6 +244,57 @@
 
             status_t dumpInternals(int fd);
 
+    // Handles binder shell commands
+    virtual status_t shellCommand(int in, int out, int err, Vector<String16>& args);
+
+    // Sets whether the given UID records only silence
+    virtual void setRecordSilenced(uid_t uid, bool silenced);
+
+    // Overrides the UID state as if it is idle
+    status_t handleSetUidState(Vector<String16>& args, int err);
+
+    // Clears the override for the UID state
+    status_t handleResetUidState(Vector<String16>& args, int err);
+
+    // Gets the UID state
+    status_t handleGetUidState(Vector<String16>& args, int out, int err);
+
+    // Prints the shell command help
+    status_t printHelp(int out);
+
+    // If recording we need to make sure the UID is allowed to do that. If the UID is idle
+    // then it cannot record and gets buffers with zeros - silence. As soon as the UID
+    // transitions to an active state we will start reporting buffers with data. This approach
+    // transparently handles recording while the UID transitions between idle/active state
+    // avoiding to get stuck in a state receiving non-empty buffers while idle or in a state
+    // receiving empty buffers while active.
+    class UidPolicy : public BnUidObserver {
+    public:
+        explicit UidPolicy(wp<AudioPolicyService> service)
+                : mService(service) {}
+
+        void registerSelf();
+        void unregisterSelf();
+
+        bool isUidActive(uid_t uid);
+
+        void onUidGone(uid_t uid, bool disabled);
+        void onUidActive(uid_t uid);
+        void onUidIdle(uid_t uid, bool disabled);
+
+        void addOverrideUid(uid_t uid, bool active);
+        void removeOverrideUid(uid_t uid);
+
+    private:
+        bool isUidActiveLocked(uid_t uid);
+        void updateOverrideUid(uid_t uid, bool active, bool insert);
+
+        Mutex mUidLock;
+        wp<AudioPolicyService> mService;
+        std::unordered_set<uid_t> mActiveUids;
+        std::unordered_map<uid_t, bool> mOverrideUids;
+    };
+
     // Thread used for tone playback and to send audio config commands to audio flinger
     // For tone playback, using a separate thread is necessary to avoid deadlock with mLock because
     // startTone() and stopTone() are normally called with mLock locked and requesting a tone start
@@ -306,7 +366,6 @@
                                                         const audio_config_base_t *deviceConfig,
                                                         audio_patch_handle_t patchHandle);
                     void        insertCommand_l(AudioCommand *command, int delayMs = 0);
-
     private:
         class AudioCommandData;
 
@@ -575,6 +634,8 @@
     // Manage all effects configured in audio_effects.conf
     sp<AudioPolicyEffects> mAudioPolicyEffects;
     audio_mode_t mPhoneState;
+
+    sp<UidPolicy> mUidPolicy;
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 6abfa81..536b54d 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -33,14 +33,18 @@
 
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
+#include <binder/ActivityManager.h>
 #include <binder/AppOpsManager.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/MemoryBase.h>
 #include <binder/MemoryHeapBase.h>
+#include <binder/PermissionController.h>
 #include <binder/ProcessInfoService.h>
+#include <binder/IResultReceiver.h>
 #include <cutils/atomic.h>
 #include <cutils/properties.h>
+#include <cutils/misc.h>
 #include <gui/Surface.h>
 #include <hardware/hardware.h>
 #include <memunreachable/memunreachable.h>
@@ -165,6 +169,8 @@
 
 // ----------------------------------------------------------------------------
 
+static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA");
+
 CameraService::CameraService() :
         mEventLog(DEFAULT_EVENT_LOG_LENGTH),
         mNumberOfCameras(0), mNumberOfNormalCameras(0),
@@ -196,6 +202,9 @@
     }
 
     CameraService::pingCameraServiceProxy();
+
+    mUidPolicy = new UidPolicy(this);
+    mUidPolicy->registerSelf();
 }
 
 status_t CameraService::enumerateProviders() {
@@ -275,6 +284,7 @@
 
 CameraService::~CameraService() {
     VendorTagDescriptor::clearGlobalVendorTagDescriptor();
+    mUidPolicy->unregisterSelf();
 }
 
 void CameraService::onNewProviderRegistered() {
@@ -306,6 +316,17 @@
     logDeviceAdded(id, "Device added");
 }
 
+void CameraService::removeStates(const String8 id) {
+    if (mFlashlight->hasFlashUnit(id)) {
+        mTorchStatusMap.removeItem(id);
+    }
+
+    {
+        Mutex::Autolock lock(mCameraStatesLock);
+        mCameraStates.erase(id);
+    }
+}
+
 void CameraService::onDeviceStatusChanged(const String8& id,
         CameraDeviceStatus newHalStatus) {
     ALOGI("%s: Status changed for cameraId=%s, newStatus=%d", __FUNCTION__,
@@ -370,6 +391,7 @@
             clientToDisconnect->disconnect();
         }
 
+        removeStates(id);
     } else {
         if (oldStatus == StatusInternal::NOT_PRESENT) {
             logDeviceAdded(id, String8::format("Device status changed from %d to %d", oldStatus,
@@ -933,6 +955,15 @@
                 clientName8.string(), clientUid, clientPid, cameraId.string());
     }
 
+    // Make sure the UID is in an active state to use the camera
+    if (!mUidPolicy->isUidActive(callingUid)) {
+        ALOGE("Access Denial: can't use the camera from an idle UID pid=%d, uid=%d",
+            clientPid, clientUid);
+        return STATUS_ERROR_FMT(ERROR_DISABLED,
+                "Caller \"%s\" (PID %d, UID %d) cannot open camera \"%s\" from background",
+                clientName8.string(), clientUid, clientPid, cameraId.string());
+    }
+
     // Only use passed in clientPid to check permission. Use calling PID as the client PID that's
     // connected to camera service directly.
     originalClientPid = clientPid;
@@ -1969,6 +2000,30 @@
 
     // Permission checks
     switch (code) {
+        case SHELL_COMMAND_TRANSACTION: {
+            int in = data.readFileDescriptor();
+            int out = data.readFileDescriptor();
+            int err = data.readFileDescriptor();
+            int argc = data.readInt32();
+            Vector<String16> args;
+            for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
+               args.add(data.readString16());
+            }
+            sp<IBinder> unusedCallback;
+            sp<IResultReceiver> resultReceiver;
+            status_t status;
+            if ((status = data.readNullableStrongBinder(&unusedCallback)) != NO_ERROR) {
+                return status;
+            }
+            if ((status = data.readNullableStrongBinder(&resultReceiver)) != NO_ERROR) {
+                return status;
+            }
+            status = shellCommand(in, out, err, args);
+            if (resultReceiver != nullptr) {
+                resultReceiver->send(status);
+            }
+            return NO_ERROR;
+        }
         case BnCameraService::NOTIFYSYSTEMEVENT: {
             if (pid != selfPid) {
                 // Ensure we're being called by system_server, or similar process with
@@ -2286,15 +2341,21 @@
     if (res != AppOpsManager::MODE_ALLOWED) {
         ALOGI("Camera %s: Access for \"%s\" revoked", mCameraIdStr.string(),
                 myName.string());
-        // Reset the client PID to allow server-initiated disconnect,
-        // and to prevent further calls by client.
-        mClientPid = getCallingPid();
-        CaptureResultExtras resultExtras; // a dummy result (invalid)
-        notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_SERVICE, resultExtras);
-        disconnect();
+        block();
     }
 }
 
+void CameraService::BasicClient::block() {
+    ATRACE_CALL();
+
+    // Reset the client PID to allow server-initiated disconnect,
+    // and to prevent further calls by client.
+    mClientPid = getCallingPid();
+    CaptureResultExtras resultExtras; // a dummy result (invalid)
+    notifyError(hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISABLED, resultExtras);
+    disconnect();
+}
+
 // ----------------------------------------------------------------------------
 
 void CameraService::Client::notifyError(int32_t errorCode,
@@ -2331,6 +2392,98 @@
 }
 
 // ----------------------------------------------------------------------------
+//                  UidPolicy
+// ----------------------------------------------------------------------------
+
+void CameraService::UidPolicy::registerSelf() {
+    ActivityManager am;
+    am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
+            | ActivityManager::UID_OBSERVER_IDLE
+            | ActivityManager::UID_OBSERVER_ACTIVE,
+            ActivityManager::PROCESS_STATE_UNKNOWN,
+            String16("cameraserver"));
+}
+
+void CameraService::UidPolicy::unregisterSelf() {
+    ActivityManager am;
+    am.unregisterUidObserver(this);
+}
+
+void CameraService::UidPolicy::onUidGone(uid_t uid, bool disabled) {
+    onUidIdle(uid, disabled);
+}
+
+void CameraService::UidPolicy::onUidActive(uid_t uid) {
+    Mutex::Autolock _l(mUidLock);
+    mActiveUids.insert(uid);
+}
+
+void CameraService::UidPolicy::onUidIdle(uid_t uid, bool /* disabled */) {
+    bool deleted = false;
+    {
+        Mutex::Autolock _l(mUidLock);
+        if (mActiveUids.erase(uid) > 0) {
+            deleted = true;
+        }
+    }
+    if (deleted) {
+        sp<CameraService> service = mService.promote();
+        if (service != nullptr) {
+            service->blockClientsForUid(uid);
+        }
+    }
+}
+
+bool CameraService::UidPolicy::isUidActive(uid_t uid) {
+    // Non-app UIDs are considered always active
+    if (uid < FIRST_APPLICATION_UID) {
+        return true;
+    }
+    Mutex::Autolock _l(mUidLock);
+    return isUidActiveLocked(uid);
+}
+
+bool CameraService::UidPolicy::isUidActiveLocked(uid_t uid) {
+    // Non-app UIDs are considered always active
+    if (uid < FIRST_APPLICATION_UID) {
+        return true;
+    }
+    auto it = mOverrideUids.find(uid);
+    if (it != mOverrideUids.end()) {
+        return it->second;
+    }
+    return mActiveUids.find(uid) != mActiveUids.end();
+}
+
+void CameraService::UidPolicy::UidPolicy::addOverrideUid(uid_t uid, bool active) {
+    updateOverrideUid(uid, active, true);
+}
+
+void CameraService::UidPolicy::removeOverrideUid(uid_t uid) {
+    updateOverrideUid(uid, false, false);
+}
+
+void CameraService::UidPolicy::updateOverrideUid(uid_t uid, bool active, bool insert) {
+    bool wasActive = false;
+    bool isActive = false;
+    {
+        Mutex::Autolock _l(mUidLock);
+        wasActive = isUidActiveLocked(uid);
+        mOverrideUids.erase(uid);
+        if (insert) {
+            mOverrideUids.insert(std::pair<uid_t, bool>(uid, active));
+        }
+        isActive = isUidActiveLocked(uid);
+    }
+    if (wasActive != isActive && !isActive) {
+        sp<CameraService> service = mService.promote();
+        if (service != nullptr) {
+            service->blockClientsForUid(uid);
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
 //                  CameraState
 // ----------------------------------------------------------------------------
 
@@ -2791,4 +2944,92 @@
     return OK;
 }
 
+void CameraService::blockClientsForUid(uid_t uid) {
+    const auto clients = mActiveClientManager.getAll();
+    for (auto& current : clients) {
+        if (current != nullptr) {
+            const auto basicClient = current->getValue();
+            if (basicClient.get() != nullptr && basicClient->getClientUid() == uid) {
+                basicClient->block();
+            }
+        }
+    }
+}
+
+// NOTE: This is a remote API - make sure all args are validated
+status_t CameraService::shellCommand(int in, int out, int err, const Vector<String16>& args) {
+    if (!checkCallingPermission(sManageCameraPermission, nullptr, nullptr)) {
+        return PERMISSION_DENIED;
+    }
+    if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
+        return BAD_VALUE;
+    }
+    if (args.size() == 3 && args[0] == String16("set-uid-state")) {
+        return handleSetUidState(args, err);
+    } else if (args.size() == 2 && args[0] == String16("reset-uid-state")) {
+        return handleResetUidState(args, err);
+    } else if (args.size() == 2 && args[0] == String16("get-uid-state")) {
+        return handleGetUidState(args, out, err);
+    } else if (args.size() == 1 && args[0] == String16("help")) {
+        printHelp(out);
+        return NO_ERROR;
+    }
+    printHelp(err);
+    return BAD_VALUE;
+}
+
+status_t CameraService::handleSetUidState(const Vector<String16>& args, int err) {
+    PermissionController pc;
+    int uid = pc.getPackageUid(args[1], 0);
+    if (uid <= 0) {
+        ALOGE("Unknown package: '%s'", String8(args[1]).string());
+        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+        return BAD_VALUE;
+    }
+    bool active = false;
+    if (args[2] == String16("active")) {
+        active = true;
+    } else if ((args[2] != String16("idle"))) {
+        ALOGE("Expected active or idle but got: '%s'", String8(args[2]).string());
+        return BAD_VALUE;
+    }
+    mUidPolicy->addOverrideUid(uid, active);
+    return NO_ERROR;
+}
+
+status_t CameraService::handleResetUidState(const Vector<String16>& args, int err) {
+    PermissionController pc;
+    int uid = pc.getPackageUid(args[1], 0);
+    if (uid < 0) {
+        ALOGE("Unknown package: '%s'", String8(args[1]).string());
+        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+        return BAD_VALUE;
+    }
+    mUidPolicy->removeOverrideUid(uid);
+    return NO_ERROR;
+}
+
+status_t CameraService::handleGetUidState(const Vector<String16>& args, int out, int err) {
+    PermissionController pc;
+    int uid = pc.getPackageUid(args[1], 0);
+    if (uid <= 0) {
+        ALOGE("Unknown package: '%s'", String8(args[1]).string());
+        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+        return BAD_VALUE;
+    }
+    if (mUidPolicy->isUidActive(uid)) {
+        return dprintf(out, "active\n");
+    } else {
+        return dprintf(out, "idle\n");
+    }
+}
+
+status_t CameraService::printHelp(int out) {
+    return dprintf(out, "Camera service commands:\n"
+        "  get-uid-state <PACKAGE> gets the uid state\n"
+        "  set-uid-state <PACKAGE> <active|idle> overrides the uid state\n"
+        "  reset-uid-state <PACKAGE> clears the uid state override\n"
+        "  help print this message\n");
+}
+
 }; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index e9373a6..67db7ec 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -27,6 +27,7 @@
 #include <binder/AppOpsManager.h>
 #include <binder/BinderService.h>
 #include <binder/IAppOpsCallback.h>
+#include <binder/IUidObserver.h>
 #include <hardware/camera.h>
 
 #include <android/hardware/camera/common/1.0/types.h>
@@ -47,6 +48,8 @@
 #include <map>
 #include <memory>
 #include <utility>
+#include <unordered_map>
+#include <unordered_set>
 
 namespace android {
 
@@ -163,6 +166,8 @@
 
     virtual status_t    dump(int fd, const Vector<String16>& args);
 
+    virtual status_t    shellCommand(int in, int out, int err, const Vector<String16>& args);
+
     /////////////////////////////////////////////////////////////////////
     // Client functionality
 
@@ -233,6 +238,9 @@
         // Check what API level is used for this client. This is used to determine which
         // superclass this can be cast to.
         virtual bool canCastToApiClient(apiLevel level) const;
+
+        // Block the client form using the camera
+        virtual void block();
     protected:
         BasicClient(const sp<CameraService>& cameraService,
                 const sp<IBinder>& remoteCallback,
@@ -506,14 +514,46 @@
         CameraParameters mShimParams;
     }; // class CameraState
 
+    // Observer for UID lifecycle enforcing that UIDs in idle
+    // state cannot use the camera to protect user privacy.
+    class UidPolicy : public BnUidObserver {
+    public:
+        explicit UidPolicy(sp<CameraService> service)
+                : mService(service) {}
+
+        void registerSelf();
+        void unregisterSelf();
+
+        bool isUidActive(uid_t uid);
+
+        void onUidGone(uid_t uid, bool disabled);
+        void onUidActive(uid_t uid);
+        void onUidIdle(uid_t uid, bool disabled);
+
+        void addOverrideUid(uid_t uid, bool active);
+        void removeOverrideUid(uid_t uid);
+
+    private:
+        bool isUidActiveLocked(uid_t uid);
+        void updateOverrideUid(uid_t uid, bool active, bool insert);
+
+        Mutex mUidLock;
+        wp<CameraService> mService;
+        std::unordered_set<uid_t> mActiveUids;
+        std::unordered_map<uid_t, bool> mOverrideUids;
+    }; // class UidPolicy
+
+    sp<UidPolicy> mUidPolicy;
+
     // Delay-load the Camera HAL module
     virtual void onFirstRef();
 
     // Eumerate all camera providers in the system
     status_t enumerateProviders();
 
-    // Add a new camera to camera and torch state lists
+    // Add a new camera to camera and torch state lists or remove an unplugged one
     void addStates(const String8 id);
+    void removeStates(const String8 id);
 
     // Check if we can connect, before we acquire the service lock.
     // The returned originalClientPid is the PID of the original process that wants to connect to
@@ -755,6 +795,21 @@
      */
     binder::Status      getLegacyParametersLazy(int cameraId, /*out*/CameraParameters* parameters);
 
+    // Blocks all clients from the UID
+    void blockClientsForUid(uid_t uid);
+
+    // Overrides the UID state as if it is idle
+    status_t handleSetUidState(const Vector<String16>& args, int err);
+
+    // Clears the override for the UID state
+    status_t handleResetUidState(const Vector<String16>& args, int err);
+
+    // Gets the UID state
+    status_t handleGetUidState(const Vector<String16>& args, int out, int err);
+
+    // Prints the shell command help
+    status_t printHelp(int out);
+
     static int getCallingPid();
 
     static int getCallingUid();
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index 910dd78..e848a3f 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -508,7 +508,7 @@
 
 void CameraClient::releaseRecordingFrameHandle(native_handle_t *handle) {
     if (handle == nullptr) return;
-
+    Mutex::Autolock lock(mLock);
     sp<IMemory> dataPtr;
     {
         Mutex::Autolock l(mAvailableCallbackBuffersLock);
@@ -532,17 +532,22 @@
         return;
     }
 
-    VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->pointer());
-    metadata->eType = kMetadataBufferTypeNativeHandleSource;
-    metadata->pHandle = handle;
-
-    mHardware->releaseRecordingFrame(dataPtr);
+    if (mHardware != nullptr) {
+        VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->pointer());
+        metadata->eType = kMetadataBufferTypeNativeHandleSource;
+        metadata->pHandle = handle;
+        mHardware->releaseRecordingFrame(dataPtr);
+    }
 }
 
 void CameraClient::releaseRecordingFrameHandleBatch(const std::vector<native_handle_t*>& handles) {
+    Mutex::Autolock lock(mLock);
+    bool disconnected = (mHardware == nullptr);
     size_t n = handles.size();
     std::vector<sp<IMemory>> frames;
-    frames.reserve(n);
+    if (!disconnected) {
+        frames.reserve(n);
+    }
     bool error = false;
     for (auto& handle : handles) {
         sp<IMemory> dataPtr;
@@ -566,10 +571,12 @@
             break;
         }
 
-        VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->pointer());
-        metadata->eType = kMetadataBufferTypeNativeHandleSource;
-        metadata->pHandle = handle;
-        frames.push_back(dataPtr);
+        if (!disconnected) {
+            VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->pointer());
+            metadata->eType = kMetadataBufferTypeNativeHandleSource;
+            metadata->pHandle = handle;
+            frames.push_back(dataPtr);
+        }
     }
 
     if (error) {
@@ -577,7 +584,7 @@
             native_handle_close(handle);
             native_handle_delete(handle);
         }
-    } else {
+    } else if (!disconnected) {
         mHardware->releaseRecordingFrameBatch(frames);
     }
     return;
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index 0d2dba1..a71a732 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2012-2018 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.
@@ -154,7 +154,8 @@
                 callbackFormat, params.previewFormat);
         res = device->createStream(mCallbackWindow,
                 params.previewWidth, params.previewHeight, callbackFormat,
-                HAL_DATASPACE_V0_JFIF, CAMERA3_STREAM_ROTATION_0, &mCallbackStreamId);
+                HAL_DATASPACE_V0_JFIF, CAMERA3_STREAM_ROTATION_0, &mCallbackStreamId,
+                String8());
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for callbacks: "
                     "%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index d1bbdaf..cc4249f 100755
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2012-2018 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.
@@ -168,7 +168,8 @@
         res = device->createStream(mCaptureWindow,
                 params.pictureWidth, params.pictureHeight,
                 HAL_PIXEL_FORMAT_BLOB, HAL_DATASPACE_V0_JFIF,
-                CAMERA3_STREAM_ROTATION_0, &mCaptureStreamId);
+                CAMERA3_STREAM_ROTATION_0, &mCaptureStreamId,
+                String8());
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for capture: "
                     "%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index 73dca73..0786f53 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2012-2018 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.
@@ -194,7 +194,7 @@
         res = device->createStream(mPreviewWindow,
                 params.previewWidth, params.previewHeight,
                 CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_UNKNOWN,
-                CAMERA3_STREAM_ROTATION_0, &mPreviewStreamId);
+                CAMERA3_STREAM_ROTATION_0, &mPreviewStreamId, String8());
         if (res != OK) {
             ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)",
                     __FUNCTION__, mId, strerror(-res), res);
@@ -379,7 +379,8 @@
         res = device->createStream(mRecordingWindow,
                 params.videoWidth, params.videoHeight,
                 params.videoFormat, params.videoDataSpace,
-                CAMERA3_STREAM_ROTATION_0, &mRecordingStreamId);
+                CAMERA3_STREAM_ROTATION_0, &mRecordingStreamId,
+                String8());
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for recording: "
                     "%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index b0607fb..372a2c5 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -311,7 +311,8 @@
 
         res = device->createStream(outSurface, params.fastInfo.arrayWidth,
             params.fastInfo.arrayHeight, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
-            HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mZslStreamId);
+            HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mZslStreamId,
+            String8());
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create ZSL stream: "
                     "%s (%d)", __FUNCTION__, client->getCameraId(),
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 5cbc158..c36c7cf 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -175,7 +175,7 @@
         return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, "Empty request list");
     }
 
-    List<const CameraMetadata> metadataRequestList;
+    List<const CameraDeviceBase::PhysicalCameraSettingsList> metadataRequestList;
     std::list<const SurfaceMap> surfaceMapList;
     submitInfo->mRequestId = mRequestIdCounter;
     uint32_t loopCounter = 0;
@@ -193,28 +193,72 @@
                         mCameraIdStr.string());
                 return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
                         "Repeating reprocess requests not supported");
+            } else if (request.mPhysicalCameraSettings.size() > 1) {
+                ALOGE("%s: Camera %s: reprocess requests not supported for "
+                        "multiple physical cameras.", __FUNCTION__,
+                        mCameraIdStr.string());
+                return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+                        "Reprocess requests not supported for multiple cameras");
             }
         }
 
-        CameraMetadata metadata(request.mMetadata);
-        if (metadata.isEmpty()) {
-            ALOGE("%s: Camera %s: Sent empty metadata packet. Rejecting request.",
-                   __FUNCTION__, mCameraIdStr.string());
+        if (request.mPhysicalCameraSettings.empty()) {
+            ALOGE("%s: Camera %s: request doesn't contain any settings.", __FUNCTION__,
+                    mCameraIdStr.string());
             return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
-                    "Request settings are empty");
-        } else if (request.mSurfaceList.isEmpty() && request.mStreamIdxList.size() == 0) {
+                    "Request doesn't contain any settings");
+        }
+
+        //The first capture settings should always match the logical camera id
+        String8 logicalId(request.mPhysicalCameraSettings.begin()->id.c_str());
+        if (mDevice->getId() != logicalId) {
+            ALOGE("%s: Camera %s: Invalid camera request settings.", __FUNCTION__,
+                    mCameraIdStr.string());
+            return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+                    "Invalid camera request settings");
+        }
+
+        CameraDeviceBase::PhysicalCameraSettingsList physicalSettingsList;
+        for (const auto& it : request.mPhysicalCameraSettings) {
+            String8 physicalId(it.id.c_str());
+            if ((physicalId != mDevice->getId()) && !checkPhysicalCameraId(physicalId)) {
+                ALOGE("%s: Camera %s: Physical camera id: %s is invalid.", __FUNCTION__,
+                        mCameraIdStr.string(), physicalId.string());
+                return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+                        "Invalid physical camera id");
+            }
+
+            CameraMetadata metadata(it.settings);
+            if (metadata.isEmpty()) {
+                ALOGE("%s: Camera %s: Sent empty metadata packet. Rejecting request.",
+                        __FUNCTION__, mCameraIdStr.string());
+                return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+                        "Request settings are empty");
+            }
+
+            if (!enforceRequestPermissions(metadata)) {
+                // Callee logs
+                return STATUS_ERROR(CameraService::ERROR_PERMISSION_DENIED,
+                        "Caller does not have permission to change restricted controls");
+            }
+
+            physicalSettingsList.push_back({it.id, metadata});
+        }
+
+        if (streaming && (physicalSettingsList.size() > 1)) {
+            ALOGE("%s: Camera %s: Individual physical camera settings are not supported in "
+                    "streaming requests. Rejecting request.", __FUNCTION__, mCameraIdStr.string());
+            return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
+                    "Streaming request contains individual physical requests");
+        }
+
+        if (request.mSurfaceList.isEmpty() && request.mStreamIdxList.size() == 0) {
             ALOGE("%s: Camera %s: Requests must have at least one surface target. "
                     "Rejecting request.", __FUNCTION__, mCameraIdStr.string());
             return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
                     "Request has no output targets");
         }
 
-        if (!enforceRequestPermissions(metadata)) {
-            // Callee logs
-            return STATUS_ERROR(CameraService::ERROR_PERMISSION_DENIED,
-                    "Caller does not have permission to change restricted controls");
-        }
-
         /**
          * Write in the output stream IDs and map from stream ID to surface ID
          * which we calculate from the capture request's list of surface target
@@ -261,20 +305,22 @@
             }
         }
 
-        metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS, &outputStreamIds[0],
-                        outputStreamIds.size());
+        physicalSettingsList.begin()->metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS,
+                &outputStreamIds[0], outputStreamIds.size());
 
         if (request.mIsReprocess) {
-            metadata.update(ANDROID_REQUEST_INPUT_STREAMS, &mInputStream.id, 1);
+            physicalSettingsList.begin()->metadata.update(ANDROID_REQUEST_INPUT_STREAMS,
+                    &mInputStream.id, 1);
         }
 
-        metadata.update(ANDROID_REQUEST_ID, &(submitInfo->mRequestId), /*size*/1);
+        physicalSettingsList.begin()->metadata.update(ANDROID_REQUEST_ID,
+                &(submitInfo->mRequestId), /*size*/1);
         loopCounter++; // loopCounter starts from 1
         ALOGV("%s: Camera %s: Creating request with ID %d (%d of %zu)",
                 __FUNCTION__, mCameraIdStr.string(), submitInfo->mRequestId,
                 loopCounter, requests.size());
 
-        metadataRequestList.push_back(metadata);
+        metadataRequestList.push_back(physicalSettingsList);
         surfaceMapList.push_back(surfaceMap);
     }
     mRequestIdCounter++;
@@ -508,6 +554,7 @@
     size_t numBufferProducers = bufferProducers.size();
     bool deferredConsumer = outputConfiguration.isDeferred();
     bool isShared = outputConfiguration.isShared();
+    String8 physicalCameraId = String8(outputConfiguration.getPhysicalCameraId());
 
     if (numBufferProducers > MAX_SURFACES_PER_STREAM) {
         ALOGE("%s: GraphicBufferProducer count %zu for stream exceeds limit of %d",
@@ -529,6 +576,12 @@
         return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
     }
 
+    if (!checkPhysicalCameraId(physicalCameraId)) {
+        String8 msg = String8::format("Camera %s: Camera doesn't support physicalCameraId %s.",
+                    mCameraIdStr.string(), physicalCameraId.string());
+        ALOGE("%s: %s", __FUNCTION__, msg.string());
+        return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+    }
     std::vector<sp<Surface>> surfaces;
     std::vector<sp<IBinder>> binders;
     status_t err;
@@ -578,7 +631,8 @@
     err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width,
             streamInfo.height, streamInfo.format, streamInfo.dataSpace,
             static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
-            &streamId, &surfaceIds, outputConfiguration.getSurfaceSetID(), isShared);
+            &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(),
+            isShared);
 
     if (err != OK) {
         res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
@@ -640,10 +694,12 @@
     int streamId = camera3::CAMERA3_STREAM_ID_INVALID;
     std::vector<sp<Surface>> noSurface;
     std::vector<int> surfaceIds;
+    String8 physicalCameraId(outputConfiguration.getPhysicalCameraId());
     err = mDevice->createStream(noSurface, /*hasDeferredConsumer*/true, width,
             height, format, dataSpace,
             static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
-            &streamId, &surfaceIds, outputConfiguration.getSurfaceSetID(), isShared,
+            &streamId, physicalCameraId, &surfaceIds,
+            outputConfiguration.getSurfaceSetID(), isShared,
             consumerUsage);
 
     if (err != OK) {
@@ -1059,6 +1115,43 @@
     return binder::Status::ok();
 }
 
+bool CameraDeviceClient::checkPhysicalCameraId(const String8& physicalCameraId) {
+    if (0 == physicalCameraId.size()) {
+        return true;
+    }
+
+    CameraMetadata staticInfo = mDevice->info();
+    camera_metadata_entry_t entryCap;
+    bool isLogicalCam = false;
+
+    entryCap = staticInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+    for (size_t i = 0; i < entryCap.count; ++i) {
+        uint8_t capability = entryCap.data.u8[i];
+        if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
+            isLogicalCam = true;
+        }
+    }
+    if (!isLogicalCam) {
+        return false;
+    }
+
+    camera_metadata_entry_t entryIds = staticInfo.find(ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
+    const uint8_t* ids = entryIds.data.u8;
+    size_t start = 0;
+    for (size_t i = 0; i < entryIds.count; ++i) {
+        if (ids[i] == '\0') {
+            if (start != i) {
+                String8 currentId((const char*)ids+start);
+                if (currentId == physicalCameraId) {
+                    return true;
+                }
+            }
+            start = i+1;
+        }
+    }
+    return false;
+}
+
 bool CameraDeviceClient::roundBufferDimensionNearest(int32_t width, int32_t height,
         int32_t format, android_dataspace dataSpace, const CameraMetadata& info,
         /*out*/int32_t* outWidth, /*out*/int32_t* outHeight) {
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 4086c72..14aeed0 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -262,6 +262,10 @@
             /*out*/SurfaceMap* surfaceMap,
             /*out*/Vector<int32_t>* streamIds);
 
+    // Check that the physicalCameraId passed in is spported by the camera
+    // device.
+    bool checkPhysicalCameraId(const String8& physicalCameraId);
+
     // IGraphicsBufferProducer binder -> Stream ID + Surface ID for output streams
     KeyedVector<sp<IBinder>, StreamSurfaceId> mStreamMap;
 
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 3fd6921..7956be5 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -65,6 +65,12 @@
      */
     virtual const CameraMetadata& info() const = 0;
 
+    struct PhysicalCameraSettings {
+        std::string cameraId;
+        CameraMetadata metadata;
+    };
+    typedef List<PhysicalCameraSettings> PhysicalCameraSettingsList;
+
     /**
      * Submit request for capture. The CameraDevice takes ownership of the
      * passed-in buffer.
@@ -76,7 +82,7 @@
      * Submit a list of requests.
      * Output lastFrameNumber is the expected last frame number of the list of requests.
      */
-    virtual status_t captureList(const List<const CameraMetadata> &requests,
+    virtual status_t captureList(const List<const PhysicalCameraSettingsList> &requests,
                                  const std::list<const SurfaceMap> &surfaceMaps,
                                  int64_t *lastFrameNumber = NULL) = 0;
 
@@ -92,7 +98,7 @@
      * Submit a list of requests for streaming.
      * Output lastFrameNumber is the last frame number of the previous streaming request.
      */
-    virtual status_t setStreamingRequestList(const List<const CameraMetadata> &requests,
+    virtual status_t setStreamingRequestList(const List<const PhysicalCameraSettingsList> &requests,
                                              const std::list<const SurfaceMap> &surfaceMaps,
                                              int64_t *lastFrameNumber = NULL) = 0;
 
@@ -119,6 +125,7 @@
     virtual status_t createStream(sp<Surface> consumer,
             uint32_t width, uint32_t height, int format,
             android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+            const String8& physicalCameraId,
             std::vector<int> *surfaceIds = nullptr,
             int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
             bool isShared = false, uint64_t consumerUsage = 0) = 0;
@@ -133,6 +140,7 @@
     virtual status_t createStream(const std::vector<sp<Surface>>& consumers,
             bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
             android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+            const String8& physicalCameraId,
             std::vector<int> *surfaceIds = nullptr,
             int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
             bool isShared = false, uint64_t consumerUsage = 0) = 0;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 2ff200d..70e7761 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -35,6 +35,7 @@
 // Hardcoded name for the passthrough HAL implementation, since it can't be discovered via the
 // service manager
 const std::string kLegacyProviderName("legacy/0");
+const std::string kExternalProviderName("external/0");
 
 // Slash-separated list of provider types to consider for use via the old camera API
 const std::string kStandardProviderTypes("internal/legacy");
@@ -69,6 +70,7 @@
 
     // See if there's a passthrough HAL, but let's not complain if there's not
     addProviderLocked(kLegacyProviderName, /*expected*/ false);
+    addProviderLocked(kExternalProviderName, /*expected*/ false);
 
     return OK;
 }
@@ -601,6 +603,15 @@
     return OK;
 }
 
+void CameraProviderManager::ProviderInfo::removeDevice(std::string id) {
+    for (auto it = mDevices.begin(); it != mDevices.end(); it++) {
+        if ((*it)->mId == id) {
+            mDevices.erase(it);
+            break;
+        }
+    }
+}
+
 status_t CameraProviderManager::ProviderInfo::dump(int fd, const Vector<String16>&) const {
     dprintf(fd, "== Camera Provider HAL %s (v2.4, %s) static info: %zu devices: ==\n",
             mProviderName.c_str(), mInterface->isRemote() ? "remote" : "passthrough",
@@ -682,6 +693,8 @@
                 return hardware::Void();
             }
             addDevice(cameraDeviceName, newStatus, &id);
+        } else if (newStatus == CameraDeviceStatus::NOT_PRESENT) {
+            removeDevice(id);
         }
         listener = mManager->getStatusListener();
     }
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 0f1f07b..d02abb0 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -387,6 +387,8 @@
 
         // Generate vendor tag id
         static metadata_vendor_id_t generateVendorTagId(const std::string &name);
+
+        void removeDevice(std::string id);
     };
 
     // Utility to find a DeviceInfo by ID; pointer is only valid while mInterfaceMutex is held
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index d99fc1d..999e258 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -55,8 +55,6 @@
 #include "device3/Camera3SharedOutputStream.h"
 #include "CameraService.h"
 
-#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
-
 using namespace android::camera3;
 using namespace android::hardware::camera;
 using namespace android::hardware::camera::device::V3_2;
@@ -194,8 +192,14 @@
 
     mTagMonitor.initialize(mVendorTagId);
 
+    Vector<int32_t> sessionParamKeys;
+    camera_metadata_entry_t sessionKeysEntry = mDeviceInfo.find(
+            ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
+    if (sessionKeysEntry.count > 0) {
+        sessionParamKeys.insertArrayAt(sessionKeysEntry.data.i32, 0, sessionKeysEntry.count);
+    }
     /** Start up request queue thread */
-    mRequestThread = new RequestThread(this, mStatusTracker, mInterface);
+    mRequestThread = new RequestThread(this, mStatusTracker, mInterface, sessionParamKeys);
     res = mRequestThread->run(String8::format("C3Dev-%s-ReqQueue", mId.string()).string());
     if (res != OK) {
         SET_ERR_L("Unable to start request queue thread: %s (%d)",
@@ -663,13 +667,15 @@
     }
 
     if (dumpTemplates) {
-        const char *templateNames[] = {
+        const char *templateNames[CAMERA3_TEMPLATE_COUNT] = {
             "TEMPLATE_PREVIEW",
             "TEMPLATE_STILL_CAPTURE",
             "TEMPLATE_VIDEO_RECORD",
             "TEMPLATE_VIDEO_SNAPSHOT",
             "TEMPLATE_ZERO_SHUTTER_LAG",
-            "TEMPLATE_MANUAL"
+            "TEMPLATE_MANUAL",
+            "TEMPLATE_MOTION_TRACKING_PREVIEW",
+            "TEMPALTE_MOTION_TRACKING_BEST"
         };
 
         for (int i = 1; i < CAMERA3_TEMPLATE_COUNT; i++) {
@@ -735,7 +741,7 @@
 }
 
 status_t Camera3Device::convertMetadataListToRequestListLocked(
-        const List<const CameraMetadata> &metadataList,
+        const List<const PhysicalCameraSettingsList> &metadataList,
         const std::list<const SurfaceMap> &surfaceMaps,
         bool repeating,
         RequestList *requestList) {
@@ -745,7 +751,7 @@
     }
 
     int32_t burstId = 0;
-    List<const CameraMetadata>::const_iterator metadataIt = metadataList.begin();
+    List<const PhysicalCameraSettingsList>::const_iterator metadataIt = metadataList.begin();
     std::list<const SurfaceMap>::const_iterator surfaceMapIt = surfaceMaps.begin();
     for (; metadataIt != metadataList.end() && surfaceMapIt != surfaceMaps.end();
             ++metadataIt, ++surfaceMapIt) {
@@ -759,12 +765,13 @@
 
         // Setup burst Id and request Id
         newRequest->mResultExtras.burstId = burstId++;
-        if (metadataIt->exists(ANDROID_REQUEST_ID)) {
-            if (metadataIt->find(ANDROID_REQUEST_ID).count == 0) {
+        if (metadataIt->begin()->metadata.exists(ANDROID_REQUEST_ID)) {
+            if (metadataIt->begin()->metadata.find(ANDROID_REQUEST_ID).count == 0) {
                 CLOGE("RequestID entry exists; but must not be empty in metadata");
                 return BAD_VALUE;
             }
-            newRequest->mResultExtras.requestId = metadataIt->find(ANDROID_REQUEST_ID).data.i32[0];
+            newRequest->mResultExtras.requestId = metadataIt->begin()->metadata.find(
+                    ANDROID_REQUEST_ID).data.i32[0];
         } else {
             CLOGE("RequestID does not exist in metadata");
             return BAD_VALUE;
@@ -796,17 +803,19 @@
 status_t Camera3Device::capture(CameraMetadata &request, int64_t* /*lastFrameNumber*/) {
     ATRACE_CALL();
 
-    List<const CameraMetadata> requests;
+    List<const PhysicalCameraSettingsList> requestsList;
     std::list<const SurfaceMap> surfaceMaps;
-    convertToRequestList(requests, surfaceMaps, request);
+    convertToRequestList(requestsList, surfaceMaps, request);
 
-    return captureList(requests, surfaceMaps, /*lastFrameNumber*/NULL);
+    return captureList(requestsList, surfaceMaps, /*lastFrameNumber*/NULL);
 }
 
-void Camera3Device::convertToRequestList(List<const CameraMetadata>& requests,
+void Camera3Device::convertToRequestList(List<const PhysicalCameraSettingsList>& requestsList,
         std::list<const SurfaceMap>& surfaceMaps,
         const CameraMetadata& request) {
-    requests.push_back(request);
+    PhysicalCameraSettingsList requestList;
+    requestList.push_back({std::string(getId().string()), request});
+    requestsList.push_back(requestList);
 
     SurfaceMap surfaceMap;
     camera_metadata_ro_entry streams = request.find(ANDROID_REQUEST_OUTPUT_STREAMS);
@@ -819,7 +828,7 @@
 }
 
 status_t Camera3Device::submitRequestsHelper(
-        const List<const CameraMetadata> &requests,
+        const List<const PhysicalCameraSettingsList> &requests,
         const std::list<const SurfaceMap> &surfaceMaps,
         bool repeating,
         /*out*/
@@ -1069,42 +1078,43 @@
     notify(&m);
 }
 
-status_t Camera3Device::captureList(const List<const CameraMetadata> &requests,
+status_t Camera3Device::captureList(const List<const PhysicalCameraSettingsList> &requestsList,
                                     const std::list<const SurfaceMap> &surfaceMaps,
                                     int64_t *lastFrameNumber) {
     ATRACE_CALL();
 
-    return submitRequestsHelper(requests, surfaceMaps, /*repeating*/false, lastFrameNumber);
+    return submitRequestsHelper(requestsList, surfaceMaps, /*repeating*/false, lastFrameNumber);
 }
 
 status_t Camera3Device::setStreamingRequest(const CameraMetadata &request,
                                             int64_t* /*lastFrameNumber*/) {
     ATRACE_CALL();
 
-    List<const CameraMetadata> requests;
+    List<const PhysicalCameraSettingsList> requestsList;
     std::list<const SurfaceMap> surfaceMaps;
-    convertToRequestList(requests, surfaceMaps, request);
+    convertToRequestList(requestsList, surfaceMaps, request);
 
-    return setStreamingRequestList(requests, /*surfaceMap*/surfaceMaps,
+    return setStreamingRequestList(requestsList, /*surfaceMap*/surfaceMaps,
                                    /*lastFrameNumber*/NULL);
 }
 
-status_t Camera3Device::setStreamingRequestList(const List<const CameraMetadata> &requests,
-                                                const std::list<const SurfaceMap> &surfaceMaps,
-                                                int64_t *lastFrameNumber) {
+status_t Camera3Device::setStreamingRequestList(
+        const List<const PhysicalCameraSettingsList> &requestsList,
+        const std::list<const SurfaceMap> &surfaceMaps, int64_t *lastFrameNumber) {
     ATRACE_CALL();
 
-    return submitRequestsHelper(requests, surfaceMaps, /*repeating*/true, lastFrameNumber);
+    return submitRequestsHelper(requestsList, surfaceMaps, /*repeating*/true, lastFrameNumber);
 }
 
 sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked(
-        const CameraMetadata &request, const SurfaceMap &surfaceMap) {
+        const PhysicalCameraSettingsList &request, const SurfaceMap &surfaceMap) {
     status_t res;
 
     if (mStatus == STATUS_UNCONFIGURED || mNeedConfig) {
         // This point should only be reached via API1 (API2 must explicitly call configureStreams)
         // so unilaterally select normal operating mode.
-        res = configureStreamsLocked(CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE, mSessionParams);
+        res = filterParamsAndConfigureLocked(request.begin()->metadata,
+                CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE);
         // Stream configuration failed. Client might try other configuraitons.
         if (res != OK) {
             CLOGE("Can't set up streams: %s (%d)", strerror(-res), res);
@@ -1224,6 +1234,7 @@
 status_t Camera3Device::createStream(sp<Surface> consumer,
             uint32_t width, uint32_t height, int format,
             android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+            const String8& physicalCameraId,
             std::vector<int> *surfaceIds, int streamSetId, bool isShared, uint64_t consumerUsage) {
     ATRACE_CALL();
 
@@ -1236,12 +1247,14 @@
     consumers.push_back(consumer);
 
     return createStream(consumers, /*hasDeferredConsumer*/ false, width, height,
-            format, dataSpace, rotation, id, surfaceIds, streamSetId, isShared, consumerUsage);
+            format, dataSpace, rotation, id, physicalCameraId, surfaceIds, streamSetId,
+            isShared, consumerUsage);
 }
 
 status_t Camera3Device::createStream(const std::vector<sp<Surface>>& consumers,
         bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
         android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+        const String8& physicalCameraId,
         std::vector<int> *surfaceIds, int streamSetId, bool isShared, uint64_t consumerUsage) {
     ATRACE_CALL();
 
@@ -1249,8 +1262,9 @@
     nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
     Mutex::Autolock l(mLock);
     ALOGV("Camera %s: Creating new stream %d: %d x %d, format %d, dataspace %d rotation %d"
-            " consumer usage %" PRIu64 ", isShared %d", mId.string(), mNextStreamId, width, height, format,
-            dataSpace, rotation, consumerUsage, isShared);
+            " consumer usage %" PRIu64 ", isShared %d, physicalCameraId %s", mId.string(),
+            mNextStreamId, width, height, format, dataSpace, rotation, consumerUsage, isShared,
+            physicalCameraId.string());
 
     status_t res;
     bool wasActive = false;
@@ -1310,7 +1324,7 @@
         }
         newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
                 width, height, blobBufferSize, format, dataSpace, rotation,
-                mTimestampOffset, streamSetId);
+                mTimestampOffset, physicalCameraId, streamSetId);
     } else if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
         ssize_t rawOpaqueBufferSize = getRawOpaqueBufferSize(width, height);
         if (rawOpaqueBufferSize <= 0) {
@@ -1319,19 +1333,19 @@
         }
         newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
                 width, height, rawOpaqueBufferSize, format, dataSpace, rotation,
-                mTimestampOffset, streamSetId);
+                mTimestampOffset, physicalCameraId, streamSetId);
     } else if (isShared) {
         newStream = new Camera3SharedOutputStream(mNextStreamId, consumers,
                 width, height, format, consumerUsage, dataSpace, rotation,
-                mTimestampOffset, streamSetId);
+                mTimestampOffset, physicalCameraId, streamSetId);
     } else if (consumers.size() == 0 && hasDeferredConsumer) {
         newStream = new Camera3OutputStream(mNextStreamId,
                 width, height, format, consumerUsage, dataSpace, rotation,
-                mTimestampOffset, streamSetId);
+                mTimestampOffset, physicalCameraId, streamSetId);
     } else {
         newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
                 width, height, format, dataSpace, rotation,
-                mTimestampOffset, streamSetId);
+                mTimestampOffset, physicalCameraId, streamSetId);
     }
 
     size_t consumerCount = consumers.size();
@@ -1508,11 +1522,20 @@
     Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
+    return filterParamsAndConfigureLocked(sessionParams, operatingMode);
+}
+
+status_t Camera3Device::filterParamsAndConfigureLocked(const CameraMetadata& sessionParams,
+        int operatingMode) {
     //Filter out any incoming session parameters
     const CameraMetadata params(sessionParams);
-    CameraMetadata filteredParams;
     camera_metadata_entry_t availableSessionKeys = mDeviceInfo.find(
             ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
+    CameraMetadata filteredParams(availableSessionKeys.count);
+    camera_metadata_t *meta = const_cast<camera_metadata_t *>(
+            filteredParams.getAndLock());
+    set_camera_metadata_vendor_id(meta, mVendorTagId);
+    filteredParams.unlock(meta);
     if (availableSessionKeys.count > 0) {
         for (size_t i = 0; i < availableSessionKeys.count; i++) {
             camera_metadata_ro_entry entry = params.find(
@@ -1645,10 +1668,16 @@
     mStatusChanged.broadcast();
 }
 
+void Camera3Device::pauseStateNotify(bool enable) {
+    Mutex::Autolock il(mInterfaceLock);
+    Mutex::Autolock l(mLock);
+
+    mPauseStateNotify = enable;
+}
+
 // Pause to reconfigure
 status_t Camera3Device::internalPauseAndWaitLocked(nsecs_t maxExpectedDuration) {
     mRequestThread->setPaused(true);
-    mPauseStateNotify = true;
 
     ALOGV("%s: Camera %s: Internal wait until idle (% " PRIi64 " ns)", __FUNCTION__, mId.string(),
           maxExpectedDuration);
@@ -1667,6 +1696,8 @@
 
     mRequestThread->setPaused(false);
 
+    ALOGV("%s: Camera %s: Internal wait until active (% " PRIi64 " ns)", __FUNCTION__, mId.string(),
+            kActiveTimeout);
     res = waitUntilStateThenRelock(/*active*/ true, kActiveTimeout);
     if (res != OK) {
         SET_ERR_L("Can't transition to active in %f seconds!",
@@ -1947,8 +1978,8 @@
         if (mStatus != STATUS_ACTIVE && mStatus != STATUS_CONFIGURED) {
             return;
         }
-        ALOGV("%s: Camera %s: Now %s", __FUNCTION__, mId.string(),
-                idle ? "idle" : "active");
+        ALOGV("%s: Camera %s: Now %s, pauseState: %s", __FUNCTION__, mId.string(),
+                idle ? "idle" : "active", mPauseStateNotify ? "true" : "false");
         internalUpdateStatusLocked(idle ? STATUS_CONFIGURED : STATUS_ACTIVE);
 
         // Skip notifying listener if we're doing some user-transparent
@@ -2075,15 +2106,15 @@
  */
 
 sp<Camera3Device::CaptureRequest> Camera3Device::createCaptureRequest(
-        const CameraMetadata &request, const SurfaceMap &surfaceMap) {
+        const PhysicalCameraSettingsList &request, const SurfaceMap &surfaceMap) {
     ATRACE_CALL();
     status_t res;
 
     sp<CaptureRequest> newRequest = new CaptureRequest;
-    newRequest->mSettings = request;
+    newRequest->mSettingsList = request;
 
     camera_metadata_entry_t inputStreams =
-            newRequest->mSettings.find(ANDROID_REQUEST_INPUT_STREAMS);
+            newRequest->mSettingsList.begin()->metadata.find(ANDROID_REQUEST_INPUT_STREAMS);
     if (inputStreams.count > 0) {
         if (mInputStream == NULL ||
                 mInputStream->getId() != inputStreams.data.i32[0]) {
@@ -2109,11 +2140,11 @@
         }
 
         newRequest->mInputStream = mInputStream;
-        newRequest->mSettings.erase(ANDROID_REQUEST_INPUT_STREAMS);
+        newRequest->mSettingsList.begin()->metadata.erase(ANDROID_REQUEST_INPUT_STREAMS);
     }
 
     camera_metadata_entry_t streams =
-            newRequest->mSettings.find(ANDROID_REQUEST_OUTPUT_STREAMS);
+            newRequest->mSettingsList.begin()->metadata.find(ANDROID_REQUEST_OUTPUT_STREAMS);
     if (streams.count == 0) {
         CLOGE("Zero output streams specified!");
         return NULL;
@@ -2161,7 +2192,7 @@
 
         newRequest->mOutputStreams.push(stream);
     }
-    newRequest->mSettings.erase(ANDROID_REQUEST_OUTPUT_STREAMS);
+    newRequest->mSettingsList.begin()->metadata.erase(ANDROID_REQUEST_OUTPUT_STREAMS);
     newRequest->mBatchSize = 1;
 
     return newRequest;
@@ -2203,10 +2234,47 @@
     // properly clean things up
     internalUpdateStatusLocked(STATUS_UNCONFIGURED);
     mNeedConfig = true;
+
+    res = mPreparerThread->resume();
+    if (res != OK) {
+        ALOGE("%s: Camera %s: Preparer thread failed to resume!", __FUNCTION__, mId.string());
+    }
+}
+
+bool Camera3Device::reconfigureCamera(const CameraMetadata& sessionParams) {
+    ATRACE_CALL();
+    bool ret = false;
+
+    Mutex::Autolock il(mInterfaceLock);
+    nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
+
+    Mutex::Autolock l(mLock);
+    auto rc = internalPauseAndWaitLocked(maxExpectedDuration);
+    if (rc == NO_ERROR) {
+        mNeedConfig = true;
+        rc = configureStreamsLocked(mOperatingMode, sessionParams, /*notifyRequestThread*/ false);
+        if (rc == NO_ERROR) {
+            ret = true;
+            mPauseStateNotify = false;
+            //Moving to active state while holding 'mLock' is important.
+            //There could be pending calls to 'create-/deleteStream' which
+            //will trigger another stream configuration while the already
+            //present streams end up with outstanding buffers that will
+            //not get drained.
+            internalUpdateStatusLocked(STATUS_ACTIVE);
+        } else {
+            setErrorStateLocked("%s: Failed to re-configure camera: %d",
+                    __FUNCTION__, rc);
+        }
+    } else {
+        ALOGE("%s: Failed to pause streaming: %d", __FUNCTION__, rc);
+    }
+
+    return ret;
 }
 
 status_t Camera3Device::configureStreamsLocked(int operatingMode,
-        const CameraMetadata& sessionParams) {
+        const CameraMetadata& sessionParams, bool notifyRequestThread) {
     ATRACE_CALL();
     status_t res;
 
@@ -2247,6 +2315,8 @@
     // Start configuring the streams
     ALOGV("%s: Camera %s: Starting stream configuration", __FUNCTION__, mId.string());
 
+    mPreparerThread->pause();
+
     camera3_stream_configuration config;
     config.operation_mode = mOperatingMode;
     config.num_streams = (mInputStream != NULL) + mOutputStreams.size();
@@ -2338,7 +2408,9 @@
 
     // Request thread needs to know to avoid using repeat-last-settings protocol
     // across configure_streams() calls
-    mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration);
+    if (notifyRequestThread) {
+        mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration, sessionParams);
+    }
 
     char value[PROPERTY_VALUE_MAX];
     property_get("camera.fifo.disable", value, "0");
@@ -2376,6 +2448,12 @@
     // tear down the deleted streams after configure streams.
     mDeletedStreams.clear();
 
+    auto rc = mPreparerThread->resume();
+    if (rc != OK) {
+        SET_ERR_L("%s: Camera %s: Preparer thread failed to resume!", __FUNCTION__, mId.string());
+        return rc;
+    }
+
     return OK;
 }
 
@@ -3168,7 +3246,18 @@
             sp<ICameraDeviceSession> &session,
             std::shared_ptr<RequestMetadataQueue> queue) :
         mHidlSession(session),
-        mRequestMetadataQueue(queue) {}
+        mRequestMetadataQueue(queue) {
+    // Check with hardware service manager if we can downcast these interfaces
+    // Somewhat expensive, so cache the results at startup
+    auto castResult_3_4 = device::V3_4::ICameraDeviceSession::castFrom(mHidlSession);
+    if (castResult_3_4.isOk()) {
+        mHidlSession_3_4 = castResult_3_4;
+    }
+    auto castResult_3_3 = device::V3_3::ICameraDeviceSession::castFrom(mHidlSession);
+    if (castResult_3_3.isOk()) {
+        mHidlSession_3_3 = castResult_3_3;
+    }
+}
 
 Camera3Device::HalInterface::HalInterface() {}
 
@@ -3196,52 +3285,89 @@
     status_t res = OK;
 
     common::V1_0::Status status;
-    RequestTemplate id;
-    switch (templateId) {
-        case CAMERA3_TEMPLATE_PREVIEW:
-            id = RequestTemplate::PREVIEW;
-            break;
-        case CAMERA3_TEMPLATE_STILL_CAPTURE:
-            id = RequestTemplate::STILL_CAPTURE;
-            break;
-        case CAMERA3_TEMPLATE_VIDEO_RECORD:
-            id = RequestTemplate::VIDEO_RECORD;
-            break;
-        case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
-            id = RequestTemplate::VIDEO_SNAPSHOT;
-            break;
-        case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
-            id = RequestTemplate::ZERO_SHUTTER_LAG;
-            break;
-        case CAMERA3_TEMPLATE_MANUAL:
-            id = RequestTemplate::MANUAL;
-            break;
-        default:
-            // Unknown template ID
-            return BAD_VALUE;
-    }
-    auto err = mHidlSession->constructDefaultRequestSettings(id,
-            [&status, &requestTemplate]
+
+    auto requestCallback = [&status, &requestTemplate]
             (common::V1_0::Status s, const device::V3_2::CameraMetadata& request) {
-                status = s;
-                if (status == common::V1_0::Status::OK) {
-                    const camera_metadata *r =
-                            reinterpret_cast<const camera_metadata_t*>(request.data());
-                    size_t expectedSize = request.size();
-                    int ret = validate_camera_metadata_structure(r, &expectedSize);
-                    if (ret == OK || ret == CAMERA_METADATA_VALIDATION_SHIFTED) {
-                        *requestTemplate = clone_camera_metadata(r);
-                        if (*requestTemplate == nullptr) {
-                            ALOGE("%s: Unable to clone camera metadata received from HAL",
-                                    __FUNCTION__);
-                            status = common::V1_0::Status::INTERNAL_ERROR;
-                        }
-                    } else {
-                        ALOGE("%s: Malformed camera metadata received from HAL", __FUNCTION__);
+            status = s;
+            if (status == common::V1_0::Status::OK) {
+                const camera_metadata *r =
+                        reinterpret_cast<const camera_metadata_t*>(request.data());
+                size_t expectedSize = request.size();
+                int ret = validate_camera_metadata_structure(r, &expectedSize);
+                if (ret == OK || ret == CAMERA_METADATA_VALIDATION_SHIFTED) {
+                    *requestTemplate = clone_camera_metadata(r);
+                    if (*requestTemplate == nullptr) {
+                        ALOGE("%s: Unable to clone camera metadata received from HAL",
+                                __FUNCTION__);
                         status = common::V1_0::Status::INTERNAL_ERROR;
                     }
+                } else {
+                    ALOGE("%s: Malformed camera metadata received from HAL", __FUNCTION__);
+                    status = common::V1_0::Status::INTERNAL_ERROR;
                 }
-            });
+            }
+        };
+    hardware::Return<void> err;
+    if (mHidlSession_3_4 != nullptr) {
+        device::V3_4::RequestTemplate id;
+        switch (templateId) {
+            case CAMERA3_TEMPLATE_PREVIEW:
+                id = device::V3_4::RequestTemplate::PREVIEW;
+                break;
+            case CAMERA3_TEMPLATE_STILL_CAPTURE:
+                id = device::V3_4::RequestTemplate::STILL_CAPTURE;
+                break;
+            case CAMERA3_TEMPLATE_VIDEO_RECORD:
+                id = device::V3_4::RequestTemplate::VIDEO_RECORD;
+                break;
+            case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
+                id = device::V3_4::RequestTemplate::VIDEO_SNAPSHOT;
+                break;
+            case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+                id = device::V3_4::RequestTemplate::ZERO_SHUTTER_LAG;
+                break;
+            case CAMERA3_TEMPLATE_MANUAL:
+                id = device::V3_4::RequestTemplate::MANUAL;
+                break;
+            case CAMERA3_TEMPLATE_MOTION_TRACKING_PREVIEW:
+                id = device::V3_4::RequestTemplate::MOTION_TRACKING_PREVIEW;
+                break;
+            case CAMERA3_TEMPLATE_MOTION_TRACKING_BEST:
+                id = device::V3_4::RequestTemplate::MOTION_TRACKING_BEST;
+                break;
+            default:
+                // Unknown template ID
+                return BAD_VALUE;
+        }
+        err = mHidlSession_3_4->constructDefaultRequestSettings_3_4(id, requestCallback);
+    } else {
+        RequestTemplate id;
+        switch (templateId) {
+            case CAMERA3_TEMPLATE_PREVIEW:
+                id = RequestTemplate::PREVIEW;
+                break;
+            case CAMERA3_TEMPLATE_STILL_CAPTURE:
+                id = RequestTemplate::STILL_CAPTURE;
+                break;
+            case CAMERA3_TEMPLATE_VIDEO_RECORD:
+                id = RequestTemplate::VIDEO_RECORD;
+                break;
+            case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
+                id = RequestTemplate::VIDEO_SNAPSHOT;
+                break;
+            case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+                id = RequestTemplate::ZERO_SHUTTER_LAG;
+                break;
+            case CAMERA3_TEMPLATE_MANUAL:
+                id = RequestTemplate::MANUAL;
+                break;
+            default:
+                // Unknown template ID, or this HAL is too old to support it
+                return BAD_VALUE;
+        }
+        err = mHidlSession->constructDefaultRequestSettings(id, requestCallback);
+    }
+
     if (!err.isOk()) {
         ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
         res = DEAD_OBJECT;
@@ -3260,10 +3386,13 @@
 
     // Convert stream config to HIDL
     std::set<int> activeStreams;
-    device::V3_4::StreamConfiguration requestedConfiguration;
-    requestedConfiguration.v3_2.streams.resize(config->num_streams);
+    device::V3_2::StreamConfiguration requestedConfiguration3_2;
+    device::V3_4::StreamConfiguration requestedConfiguration3_4;
+    requestedConfiguration3_2.streams.resize(config->num_streams);
+    requestedConfiguration3_4.streams.resize(config->num_streams);
     for (size_t i = 0; i < config->num_streams; i++) {
-        Stream &dst = requestedConfiguration.v3_2.streams[i];
+        device::V3_2::Stream &dst3_2 = requestedConfiguration3_2.streams[i];
+        device::V3_4::Stream &dst3_4 = requestedConfiguration3_4.streams[i];
         camera3_stream_t *src = config->streams[i];
 
         Camera3Stream* cam3stream = Camera3Stream::cast(src);
@@ -3282,14 +3411,18 @@
                         __FUNCTION__, streamId, config->streams[i]->stream_type);
                 return BAD_VALUE;
         }
-        dst.id = streamId;
-        dst.streamType = streamType;
-        dst.width = src->width;
-        dst.height = src->height;
-        dst.format = mapToPixelFormat(src->format);
-        dst.usage = mapToConsumerUsage(cam3stream->getUsage());
-        dst.dataSpace = mapToHidlDataspace(src->data_space);
-        dst.rotation = mapToStreamRotation((camera3_stream_rotation_t) src->rotation);
+        dst3_2.id = streamId;
+        dst3_2.streamType = streamType;
+        dst3_2.width = src->width;
+        dst3_2.height = src->height;
+        dst3_2.format = mapToPixelFormat(src->format);
+        dst3_2.usage = mapToConsumerUsage(cam3stream->getUsage());
+        dst3_2.dataSpace = mapToHidlDataspace(src->data_space);
+        dst3_2.rotation = mapToStreamRotation((camera3_stream_rotation_t) src->rotation);
+        dst3_4.v3_2 = dst3_2;
+        if (src->physical_camera_id != nullptr) {
+            dst3_4.physicalCameraId = src->physical_camera_id;
+        }
 
         activeStreams.insert(streamId);
         // Create Buffer ID map if necessary
@@ -3308,52 +3441,46 @@
         }
     }
 
+    StreamConfigurationMode operationMode;
     res = mapToStreamConfigurationMode(
             (camera3_stream_configuration_mode_t) config->operation_mode,
-            /*out*/ &requestedConfiguration.v3_2.operationMode);
+            /*out*/ &operationMode);
     if (res != OK) {
         return res;
     }
-
-    requestedConfiguration.sessionParams.setToExternal(
+    requestedConfiguration3_2.operationMode = operationMode;
+    requestedConfiguration3_4.operationMode = operationMode;
+    requestedConfiguration3_4.sessionParams.setToExternal(
             reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(sessionParams)),
             get_camera_metadata_size(sessionParams));
 
     // Invoke configureStreams
-
     device::V3_3::HalStreamConfiguration finalConfiguration;
     common::V1_0::Status status;
 
     // See if we have v3.4 or v3.3 HAL
-    sp<device::V3_4::ICameraDeviceSession> hidlSession_3_4;
-    sp<device::V3_3::ICameraDeviceSession> hidlSession_3_3;
-    auto castResult_3_4 = device::V3_4::ICameraDeviceSession::castFrom(mHidlSession);
-    if (castResult_3_4.isOk()) {
-        hidlSession_3_4 = castResult_3_4;
-    } else {
-        auto castResult_3_3 = device::V3_3::ICameraDeviceSession::castFrom(mHidlSession);
-        if (castResult_3_3.isOk()) {
-            hidlSession_3_3 = castResult_3_3;
-        }
-    }
-
-    if (hidlSession_3_4 != nullptr) {
+    if (mHidlSession_3_4 != nullptr) {
         // We do; use v3.4 for the call
         ALOGV("%s: v3.4 device found", __FUNCTION__);
-        auto err = hidlSession_3_4->configureStreams_3_4(requestedConfiguration,
-            [&status, &finalConfiguration]
-            (common::V1_0::Status s, const device::V3_3::HalStreamConfiguration& halConfiguration) {
-                finalConfiguration = halConfiguration;
+        device::V3_4::HalStreamConfiguration finalConfiguration3_4;
+        auto err = mHidlSession_3_4->configureStreams_3_4(requestedConfiguration3_4,
+            [&status, &finalConfiguration3_4]
+            (common::V1_0::Status s, const device::V3_4::HalStreamConfiguration& halConfiguration) {
+                finalConfiguration3_4 = halConfiguration;
                 status = s;
             });
         if (!err.isOk()) {
             ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
             return DEAD_OBJECT;
         }
-    } else if (hidlSession_3_3 != nullptr) {
+        finalConfiguration.streams.resize(finalConfiguration3_4.streams.size());
+        for (size_t i = 0; i < finalConfiguration3_4.streams.size(); i++) {
+            finalConfiguration.streams[i] = finalConfiguration3_4.streams[i].v3_3;
+        }
+    } else if (mHidlSession_3_3 != nullptr) {
         // We do; use v3.3 for the call
         ALOGV("%s: v3.3 device found", __FUNCTION__);
-        auto err = hidlSession_3_3->configureStreams_3_3(requestedConfiguration.v3_2,
+        auto err = mHidlSession_3_3->configureStreams_3_3(requestedConfiguration3_2,
             [&status, &finalConfiguration]
             (common::V1_0::Status s, const device::V3_3::HalStreamConfiguration& halConfiguration) {
                 finalConfiguration = halConfiguration;
@@ -3367,7 +3494,7 @@
         // We don't; use v3.2 call and construct a v3.3 HalStreamConfiguration
         ALOGV("%s: v3.2 device found", __FUNCTION__);
         HalStreamConfiguration finalConfiguration_3_2;
-        auto err = mHidlSession->configureStreams(requestedConfiguration.v3_2,
+        auto err = mHidlSession->configureStreams(requestedConfiguration3_2,
                 [&status, &finalConfiguration_3_2]
                 (common::V1_0::Status s, const HalStreamConfiguration& halConfiguration) {
                     finalConfiguration_3_2 = halConfiguration;
@@ -3381,7 +3508,7 @@
         for (size_t i = 0; i < finalConfiguration_3_2.streams.size(); i++) {
             finalConfiguration.streams[i].v3_2 = finalConfiguration_3_2.streams[i];
             finalConfiguration.streams[i].overrideDataSpace =
-                    requestedConfiguration.v3_2.streams[i].dataSpace;
+                    requestedConfiguration3_2.streams[i].dataSpace;
         }
     }
 
@@ -3535,13 +3662,29 @@
     ATRACE_NAME("CameraHal::processBatchCaptureRequests");
     if (!valid()) return INVALID_OPERATION;
 
+    sp<device::V3_4::ICameraDeviceSession> hidlSession_3_4;
+    auto castResult_3_4 = device::V3_4::ICameraDeviceSession::castFrom(mHidlSession);
+    if (castResult_3_4.isOk()) {
+        hidlSession_3_4 = castResult_3_4;
+    }
+
     hardware::hidl_vec<device::V3_2::CaptureRequest> captureRequests;
+    hardware::hidl_vec<device::V3_4::CaptureRequest> captureRequests_3_4;
     size_t batchSize = requests.size();
-    captureRequests.resize(batchSize);
+    if (hidlSession_3_4 != nullptr) {
+        captureRequests_3_4.resize(batchSize);
+    } else {
+        captureRequests.resize(batchSize);
+    }
     std::vector<native_handle_t*> handlesCreated;
 
     for (size_t i = 0; i < batchSize; i++) {
-        wrapAsHidlRequest(requests[i], /*out*/&captureRequests[i], /*out*/&handlesCreated);
+        if (hidlSession_3_4 != nullptr) {
+            wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_4[i].v3_2,
+                    /*out*/&handlesCreated);
+        } else {
+            wrapAsHidlRequest(requests[i], /*out*/&captureRequests[i], /*out*/&handlesCreated);
+        }
     }
 
     std::vector<device::V3_2::BufferCache> cachesToRemove;
@@ -3562,7 +3705,12 @@
     // Write metadata to FMQ.
     for (size_t i = 0; i < batchSize; i++) {
         camera3_capture_request_t* request = requests[i];
-        device::V3_2::CaptureRequest* captureRequest = &captureRequests[i];
+        device::V3_2::CaptureRequest* captureRequest;
+        if (hidlSession_3_4 != nullptr) {
+            captureRequest = &captureRequests_3_4[i].v3_2;
+        } else {
+            captureRequest = &captureRequests[i];
+        }
 
         if (request->settings != nullptr) {
             size_t settingsSize = get_camera_metadata_size(request->settings);
@@ -3584,12 +3732,46 @@
             captureRequest->settings.resize(0);
             captureRequest->fmqSettingsSize = 0u;
         }
+
+        if (hidlSession_3_4 != nullptr) {
+            captureRequests_3_4[i].physicalCameraSettings.resize(request->num_physcam_settings);
+            for (size_t j = 0; j < request->num_physcam_settings; j++) {
+                size_t settingsSize = get_camera_metadata_size(request->physcam_settings[j]);
+                if (mRequestMetadataQueue != nullptr && mRequestMetadataQueue->write(
+                            reinterpret_cast<const uint8_t*>(request->physcam_settings[j]),
+                            settingsSize)) {
+                    captureRequests_3_4[i].physicalCameraSettings[j].settings.resize(0);
+                    captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize = settingsSize;
+                } else {
+                    if (mRequestMetadataQueue != nullptr) {
+                        ALOGW("%s: couldn't utilize fmq, fallback to hwbinder", __FUNCTION__);
+                    }
+                    captureRequests_3_4[i].physicalCameraSettings[j].settings.setToExternal(
+                            reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(
+                                    request->physcam_settings[j])),
+                            get_camera_metadata_size(request->physcam_settings[j]));
+                    captureRequests_3_4[i].physicalCameraSettings[j].fmqSettingsSize = 0u;
+                }
+                captureRequests_3_4[i].physicalCameraSettings[j].physicalCameraId =
+                    request->physcam_id[j];
+            }
+        }
     }
-    auto err = mHidlSession->processCaptureRequest(captureRequests, cachesToRemove,
+
+    hardware::details::return_status err;
+    if (hidlSession_3_4 != nullptr) {
+        err = hidlSession_3_4->processCaptureRequest_3_4(captureRequests_3_4, cachesToRemove,
             [&status, &numRequestProcessed] (auto s, uint32_t n) {
                 status = s;
                 *numRequestProcessed = n;
             });
+    } else {
+        err = mHidlSession->processCaptureRequest(captureRequests, cachesToRemove,
+            [&status, &numRequestProcessed] (auto s, uint32_t n) {
+                status = s;
+                *numRequestProcessed = n;
+            });
+    }
     if (!err.isOk()) {
         ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
         return DEAD_OBJECT;
@@ -3747,7 +3929,7 @@
 
 Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
         sp<StatusTracker> statusTracker,
-        sp<HalInterface> interface) :
+        sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys) :
         Thread(/*canCallJava*/false),
         mParent(parent),
         mStatusTracker(statusTracker),
@@ -3764,7 +3946,9 @@
         mRepeatingLastFrameNumber(
             hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES),
         mPrepareVideoStream(false),
-        mRequestLatency(kRequestLatencyBinSize) {
+        mRequestLatency(kRequestLatencyBinSize),
+        mSessionParamKeys(sessionParamKeys),
+        mLatestSessionParams(sessionParamKeys.size()) {
     mStatusId = statusTracker->addComponent();
 }
 
@@ -3777,10 +3961,12 @@
     mListener = listener;
 }
 
-void Camera3Device::RequestThread::configurationComplete(bool isConstrainedHighSpeed) {
+void Camera3Device::RequestThread::configurationComplete(bool isConstrainedHighSpeed,
+        const CameraMetadata& sessionParams) {
     ATRACE_CALL();
     Mutex::Autolock l(mRequestLock);
     mReconfigured = true;
+    mLatestSessionParams = sessionParams;
     // Prepare video stream for high speed recording.
     mPrepareVideoStream = isConstrainedHighSpeed;
 }
@@ -4068,9 +4254,12 @@
         }
 
         if (nextRequest.halRequest.settings != NULL) {
-            nextRequest.captureRequest->mSettings.unlock(nextRequest.halRequest.settings);
+            nextRequest.captureRequest->mSettingsList.begin()->metadata.unlock(
+                    nextRequest.halRequest.settings);
         }
 
+        cleanupPhysicalSettings(nextRequest.captureRequest, &nextRequest.halRequest);
+
         if (!triggerRemoveFailed) {
             // Remove any previously queued triggers (after unlock)
             status_t removeTriggerRes = removeTriggers(mPrevRequest);
@@ -4139,9 +4328,12 @@
         }
 
         if (nextRequest.halRequest.settings != NULL) {
-            nextRequest.captureRequest->mSettings.unlock(nextRequest.halRequest.settings);
+            nextRequest.captureRequest->mSettingsList.begin()->metadata.unlock(
+                    nextRequest.halRequest.settings);
         }
 
+        cleanupPhysicalSettings(nextRequest.captureRequest, &nextRequest.halRequest);
+
         // Remove any previously queued triggers (after unlock)
         res = removeTriggers(mPrevRequest);
         if (res != OK) {
@@ -4191,6 +4383,52 @@
     return maxExpectedDuration;
 }
 
+bool Camera3Device::RequestThread::updateSessionParameters(const CameraMetadata& settings) {
+    ATRACE_CALL();
+    bool updatesDetected = false;
+
+    for (auto tag : mSessionParamKeys) {
+        camera_metadata_ro_entry entry = settings.find(tag);
+        camera_metadata_entry lastEntry = mLatestSessionParams.find(tag);
+
+        if (entry.count > 0) {
+            bool isDifferent = false;
+            if (lastEntry.count > 0) {
+                // Have a last value, compare to see if changed
+                if (lastEntry.type == entry.type &&
+                        lastEntry.count == entry.count) {
+                    // Same type and count, compare values
+                    size_t bytesPerValue = camera_metadata_type_size[lastEntry.type];
+                    size_t entryBytes = bytesPerValue * lastEntry.count;
+                    int cmp = memcmp(entry.data.u8, lastEntry.data.u8, entryBytes);
+                    if (cmp != 0) {
+                        isDifferent = true;
+                    }
+                } else {
+                    // Count or type has changed
+                    isDifferent = true;
+                }
+            } else {
+                // No last entry, so always consider to be different
+                isDifferent = true;
+            }
+
+            if (isDifferent) {
+                ALOGV("%s: Session parameter tag id %d changed", __FUNCTION__, tag);
+                mLatestSessionParams.update(entry);
+                updatesDetected = true;
+            }
+        } else if (lastEntry.count > 0) {
+            // Value has been removed
+            ALOGV("%s: Session parameter tag id %d removed", __FUNCTION__, tag);
+            mLatestSessionParams.erase(tag);
+            updatesDetected = true;
+        }
+    }
+
+    return updatesDetected;
+}
+
 bool Camera3Device::RequestThread::threadLoop() {
     ATRACE_CALL();
     status_t res;
@@ -4209,7 +4447,7 @@
     // Get the latest request ID, if any
     int latestRequestId;
     camera_metadata_entry_t requestIdEntry = mNextRequests[mNextRequests.size() - 1].
-            captureRequest->mSettings.find(ANDROID_REQUEST_ID);
+            captureRequest->mSettingsList.begin()->metadata.find(ANDROID_REQUEST_ID);
     if (requestIdEntry.count > 0) {
         latestRequestId = requestIdEntry.data.i32[0];
     } else {
@@ -4217,6 +4455,53 @@
         latestRequestId = NAME_NOT_FOUND;
     }
 
+    // 'mNextRequests' will at this point contain either a set of HFR batched requests
+    //  or a single request from streaming or burst. In either case the first element
+    //  should contain the latest camera settings that we need to check for any session
+    //  parameter updates.
+    if (updateSessionParameters(mNextRequests[0].captureRequest->mSettingsList.begin()->metadata)) {
+        res = OK;
+
+        //Input stream buffers are already acquired at this point so an input stream
+        //will not be able to move to idle state unless we force it.
+        if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
+            res = mNextRequests[0].captureRequest->mInputStream->forceToIdle();
+            if (res != OK) {
+                ALOGE("%s: Failed to force idle input stream: %d", __FUNCTION__, res);
+                cleanUpFailedRequests(/*sendRequestError*/ false);
+                return false;
+            }
+        }
+
+        if (res == OK) {
+            sp<StatusTracker> statusTracker = mStatusTracker.promote();
+            if (statusTracker != 0) {
+                sp<Camera3Device> parent = mParent.promote();
+                if (parent != nullptr) {
+                    parent->pauseStateNotify(true);
+                }
+
+                statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
+
+                if (parent != nullptr) {
+                    mReconfigured |= parent->reconfigureCamera(mLatestSessionParams);
+                }
+
+                statusTracker->markComponentActive(mStatusId);
+                setPaused(false);
+            }
+
+            if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
+                mNextRequests[0].captureRequest->mInputStream->restoreConfiguredState();
+                if (res != OK) {
+                    ALOGE("%s: Failed to restore configured input stream: %d", __FUNCTION__, res);
+                    cleanUpFailedRequests(/*sendRequestError*/ false);
+                    return false;
+                }
+            }
+        }
+    }
+
     // Prepare a batch of HAL requests and output buffers.
     res = prepareHalRequests();
     if (res == TIMED_OUT) {
@@ -4320,8 +4605,8 @@
              * The request should be presorted so accesses in HAL
              *   are O(logn). Sidenote, sorting a sorted metadata is nop.
              */
-            captureRequest->mSettings.sort();
-            halRequest->settings = captureRequest->mSettings.getAndLock();
+            captureRequest->mSettingsList.begin()->metadata.sort();
+            halRequest->settings = captureRequest->mSettingsList.begin()->metadata.getAndLock();
             mPrevRequest = captureRequest;
             ALOGVV("%s: Request settings are NEW", __FUNCTION__);
 
@@ -4345,6 +4630,20 @@
                    __FUNCTION__);
         }
 
+        if (captureRequest->mSettingsList.size() > 1) {
+            halRequest->num_physcam_settings = captureRequest->mSettingsList.size() - 1;
+            halRequest->physcam_id = new const char* [halRequest->num_physcam_settings];
+            halRequest->physcam_settings =
+                new const camera_metadata* [halRequest->num_physcam_settings];
+            auto it = ++captureRequest->mSettingsList.begin();
+            size_t i = 0;
+            for (; it != captureRequest->mSettingsList.end(); it++, i++) {
+                halRequest->physcam_id[i] = it->cameraId.c_str();
+                it->metadata.sort();
+                halRequest->physcam_settings[i] = it->metadata.getAndLock();
+            }
+        }
+
         uint32_t totalNumBuffers = 0;
 
         // Fill in buffers
@@ -4515,6 +4814,30 @@
             mExpectedInflightDuration : kMinInflightDuration;
 }
 
+void Camera3Device::RequestThread::cleanupPhysicalSettings(sp<CaptureRequest> request,
+        camera3_capture_request_t *halRequest) {
+    if ((request == nullptr) || (halRequest == nullptr)) {
+        ALOGE("%s: Invalid request!", __FUNCTION__);
+        return;
+    }
+
+    if (halRequest->num_physcam_settings > 0) {
+        if (halRequest->physcam_id != nullptr) {
+            delete [] halRequest->physcam_id;
+            halRequest->physcam_id = nullptr;
+        }
+        if (halRequest->physcam_settings != nullptr) {
+            auto it = ++(request->mSettingsList.begin());
+            size_t i = 0;
+            for (; it != request->mSettingsList.end(); it++, i++) {
+                it->metadata.unlock(halRequest->physcam_settings[i]);
+            }
+            delete [] halRequest->physcam_settings;
+            halRequest->physcam_settings = nullptr;
+        }
+    }
+}
+
 void Camera3Device::RequestThread::cleanUpFailedRequests(bool sendRequestError) {
     if (mNextRequests.empty()) {
         return;
@@ -4531,9 +4854,11 @@
         Vector<camera3_stream_buffer_t>* outputBuffers = &nextRequest.outputBuffers;
 
         if (halRequest->settings != NULL) {
-            captureRequest->mSettings.unlock(halRequest->settings);
+            captureRequest->mSettingsList.begin()->metadata.unlock(halRequest->settings);
         }
 
+        cleanupPhysicalSettings(captureRequest, halRequest);
+
         if (captureRequest->mInputStream != NULL) {
             captureRequest->mInputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
             captureRequest->mInputStream->returnInputBuffer(captureRequest->mInputBuffer);
@@ -4797,7 +5122,7 @@
         return DEAD_OBJECT;
     }
 
-    CameraMetadata &metadata = request->mSettings;
+    CameraMetadata &metadata = request->mSettingsList.begin()->metadata;
     size_t count = mTriggerMap.size();
 
     for (size_t i = 0; i < count; ++i) {
@@ -4880,7 +5205,7 @@
     ATRACE_CALL();
     Mutex::Autolock al(mTriggerMutex);
 
-    CameraMetadata &metadata = request->mSettings;
+    CameraMetadata &metadata = request->mSettingsList.begin()->metadata;
 
     /**
      * Replace all old entries with their old values.
@@ -4945,7 +5270,7 @@
     static const int32_t dummyTriggerId = 1;
     status_t res;
 
-    CameraMetadata &metadata = request->mSettings;
+    CameraMetadata &metadata = request->mSettingsList.begin()->metadata;
 
     // If AF trigger is active, insert a dummy AF trigger ID if none already
     // exists
@@ -4980,7 +5305,7 @@
 
 Camera3Device::PreparerThread::PreparerThread() :
         Thread(/*canCallJava*/false), mListener(nullptr),
-        mActive(false), mCancelNow(false) {
+        mActive(false), mCancelNow(false), mCurrentMaxCount(0), mCurrentPrepareComplete(false) {
 }
 
 Camera3Device::PreparerThread::~PreparerThread() {
@@ -5031,18 +5356,101 @@
     }
 
     // queue up the work
-    mPendingStreams.push_back(stream);
+    mPendingStreams.emplace(maxCount, stream);
     ALOGV("%s: Stream %d queued for preparing", __FUNCTION__, stream->getId());
 
     return OK;
 }
 
+void Camera3Device::PreparerThread::pause() {
+    ATRACE_CALL();
+
+    Mutex::Autolock l(mLock);
+
+    std::unordered_map<int, sp<camera3::Camera3StreamInterface> > pendingStreams;
+    pendingStreams.insert(mPendingStreams.begin(), mPendingStreams.end());
+    sp<camera3::Camera3StreamInterface> currentStream = mCurrentStream;
+    int currentMaxCount = mCurrentMaxCount;
+    mPendingStreams.clear();
+    mCancelNow = true;
+    while (mActive) {
+        auto res = mThreadActiveSignal.waitRelative(mLock, kActiveTimeout);
+        if (res == TIMED_OUT) {
+            ALOGE("%s: Timed out waiting on prepare thread!", __FUNCTION__);
+            return;
+        } else if (res != OK) {
+            ALOGE("%s: Encountered an error: %d waiting on prepare thread!", __FUNCTION__, res);
+            return;
+        }
+    }
+
+    //Check whether the prepare thread was able to complete the current
+    //stream. In case work is still pending emplace it along with the rest
+    //of the streams in the pending list.
+    if (currentStream != nullptr) {
+        if (!mCurrentPrepareComplete) {
+            pendingStreams.emplace(currentMaxCount, currentStream);
+        }
+    }
+
+    mPendingStreams.insert(pendingStreams.begin(), pendingStreams.end());
+    for (const auto& it : mPendingStreams) {
+        it.second->cancelPrepare();
+    }
+}
+
+status_t Camera3Device::PreparerThread::resume() {
+    ATRACE_CALL();
+    status_t res;
+
+    Mutex::Autolock l(mLock);
+    sp<NotificationListener> listener = mListener.promote();
+
+    if (mActive) {
+        ALOGE("%s: Trying to resume an already active prepare thread!", __FUNCTION__);
+        return NO_INIT;
+    }
+
+    auto it = mPendingStreams.begin();
+    for (; it != mPendingStreams.end();) {
+        res = it->second->startPrepare(it->first);
+        if (res == OK) {
+            if (listener != NULL) {
+                listener->notifyPrepared(it->second->getId());
+            }
+            it = mPendingStreams.erase(it);
+        } else if (res != NOT_ENOUGH_DATA) {
+            ALOGE("%s: Unable to start preparer stream: %d (%s)", __FUNCTION__,
+                    res, strerror(-res));
+            it = mPendingStreams.erase(it);
+        } else {
+            it++;
+        }
+    }
+
+    if (mPendingStreams.empty()) {
+        return OK;
+    }
+
+    res = Thread::run("C3PrepThread", PRIORITY_BACKGROUND);
+    if (res != OK) {
+        ALOGE("%s: Unable to start preparer stream: %d (%s)",
+                __FUNCTION__, res, strerror(-res));
+        return res;
+    }
+    mCancelNow = false;
+    mActive = true;
+    ALOGV("%s: Preparer stream started", __FUNCTION__);
+
+    return OK;
+}
+
 status_t Camera3Device::PreparerThread::clear() {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
 
-    for (const auto& stream : mPendingStreams) {
-        stream->cancelPrepare();
+    for (const auto& it : mPendingStreams) {
+        it.second->cancelPrepare();
     }
     mPendingStreams.clear();
     mCancelNow = true;
@@ -5067,12 +5475,15 @@
                 // threadLoop _must not_ re-acquire mLock after it sets mActive to false; would
                 // cause deadlock with prepare()'s requestExitAndWait triggered by !mActive.
                 mActive = false;
+                mThreadActiveSignal.signal();
                 return false;
             }
 
             // Get next stream to prepare
             auto it = mPendingStreams.begin();
-            mCurrentStream = *it;
+            mCurrentStream = it->second;
+            mCurrentMaxCount = it->first;
+            mCurrentPrepareComplete = false;
             mPendingStreams.erase(it);
             ATRACE_ASYNC_BEGIN("stream prepare", mCurrentStream->getId());
             ALOGV("%s: Preparing stream %d", __FUNCTION__, mCurrentStream->getId());
@@ -5107,6 +5518,7 @@
 
     ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId());
     mCurrentStream.clear();
+    mCurrentPrepareComplete = true;
 
     return true;
 }
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index cc7eb35..bf2a577 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -31,6 +31,7 @@
 #include <android/hardware/camera/device/3.2/ICameraDevice.h>
 #include <android/hardware/camera/device/3.2/ICameraDeviceSession.h>
 #include <android/hardware/camera/device/3.3/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
 #include <android/hardware/camera/device/3.2/ICameraDeviceCallback.h>
 #include <fmq/MessageQueue.h>
 #include <hardware/camera3.h>
@@ -99,12 +100,12 @@
     // Capture and setStreamingRequest will configure streams if currently in
     // idle state
     status_t capture(CameraMetadata &request, int64_t *lastFrameNumber = NULL) override;
-    status_t captureList(const List<const CameraMetadata> &requests,
+    status_t captureList(const List<const PhysicalCameraSettingsList> &requestsList,
             const std::list<const SurfaceMap> &surfaceMaps,
             int64_t *lastFrameNumber = NULL) override;
     status_t setStreamingRequest(const CameraMetadata &request,
             int64_t *lastFrameNumber = NULL) override;
-    status_t setStreamingRequestList(const List<const CameraMetadata> &requests,
+    status_t setStreamingRequestList(const List<const PhysicalCameraSettingsList> &requestsList,
             const std::list<const SurfaceMap> &surfaceMaps,
             int64_t *lastFrameNumber = NULL) override;
     status_t clearStreamingRequest(int64_t *lastFrameNumber = NULL) override;
@@ -119,12 +120,14 @@
     status_t createStream(sp<Surface> consumer,
             uint32_t width, uint32_t height, int format,
             android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+            const String8& physicalCameraId,
             std::vector<int> *surfaceIds = nullptr,
             int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
             bool isShared = false, uint64_t consumerUsage = 0) override;
     status_t createStream(const std::vector<sp<Surface>>& consumers,
             bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
             android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id,
+            const String8& physicalCameraId,
             std::vector<int> *surfaceIds = nullptr,
             int streamSetId = camera3::CAMERA3_STREAM_SET_ID_INVALID,
             bool isShared = false, uint64_t consumerUsage = 0) override;
@@ -295,7 +298,13 @@
         void getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out);
 
       private:
+        // Always valid
         sp<hardware::camera::device::V3_2::ICameraDeviceSession> mHidlSession;
+        // Valid if ICameraDeviceSession is @3.3 or newer
+        sp<hardware::camera::device::V3_3::ICameraDeviceSession> mHidlSession_3_3;
+        // Valid if ICameraDeviceSession is @3.4 or newer
+        sp<hardware::camera::device::V3_4::ICameraDeviceSession> mHidlSession_3_4;
+
         std::shared_ptr<RequestMetadataQueue> mRequestMetadataQueue;
 
         std::mutex mInflightLock;
@@ -426,7 +435,7 @@
 
     class CaptureRequest : public LightRefBase<CaptureRequest> {
       public:
-        CameraMetadata                      mSettings;
+        PhysicalCameraSettingsList          mSettingsList;
         sp<camera3::Camera3Stream>          mInputStream;
         camera3_stream_buffer_t             mInputBuffer;
         Vector<sp<camera3::Camera3OutputStreamInterface> >
@@ -446,17 +455,17 @@
     status_t checkStatusOkToCaptureLocked();
 
     status_t convertMetadataListToRequestListLocked(
-            const List<const CameraMetadata> &metadataList,
+            const List<const PhysicalCameraSettingsList> &metadataList,
             const std::list<const SurfaceMap> &surfaceMaps,
             bool repeating,
             /*out*/
             RequestList *requestList);
 
-    void convertToRequestList(List<const CameraMetadata>& requests,
+    void convertToRequestList(List<const PhysicalCameraSettingsList>& requestsList,
             std::list<const SurfaceMap>& surfaceMaps,
             const CameraMetadata& request);
 
-    status_t submitRequestsHelper(const List<const CameraMetadata> &requests,
+    status_t submitRequestsHelper(const List<const PhysicalCameraSettingsList> &requestsList,
                                   const std::list<const SurfaceMap> &surfaceMaps,
                                   bool repeating,
                                   int64_t *lastFrameNumber = NULL);
@@ -541,22 +550,41 @@
      * Do common work for setting up a streaming or single capture request.
      * On success, will transition to ACTIVE if in IDLE.
      */
-    sp<CaptureRequest> setUpRequestLocked(const CameraMetadata &request,
+    sp<CaptureRequest> setUpRequestLocked(const PhysicalCameraSettingsList &request,
                                           const SurfaceMap &surfaceMap);
 
     /**
      * Build a CaptureRequest request from the CameraDeviceBase request
      * settings.
      */
-    sp<CaptureRequest> createCaptureRequest(const CameraMetadata &request,
+    sp<CaptureRequest> createCaptureRequest(const PhysicalCameraSettingsList &request,
                                             const SurfaceMap &surfaceMap);
 
     /**
+     * Pause state updates to the client application.  Needed to mask out idle/active
+     * transitions during internal reconfigure
+     */
+    void pauseStateNotify(bool enable);
+
+    /**
+     * Internally re-configure camera device using new session parameters.
+     * This will get triggered by the request thread. Be sure to call
+     * pauseStateNotify(true) before going idle in the requesting location.
+     */
+    bool reconfigureCamera(const CameraMetadata& sessionParams);
+
+    /**
+     * Filter stream session parameters and configure camera HAL.
+     */
+    status_t filterParamsAndConfigureLocked(const CameraMetadata& sessionParams,
+            int operatingMode);
+
+    /**
      * Take the currently-defined set of streams and configure the HAL to use
      * them. This is a long-running operation (may be several hundered ms).
      */
     status_t           configureStreamsLocked(int operatingMode,
-            const CameraMetadata& sessionParams);
+            const CameraMetadata& sessionParams, bool notifyRequestThread = true);
 
     /**
      * Cancel stream configuration that did not finish successfully.
@@ -655,7 +683,7 @@
 
         RequestThread(wp<Camera3Device> parent,
                 sp<camera3::StatusTracker> statusTracker,
-                sp<HalInterface> interface);
+                sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys);
         ~RequestThread();
 
         void     setNotificationListener(wp<NotificationListener> listener);
@@ -663,7 +691,8 @@
         /**
          * Call after stream (re)-configuration is completed.
          */
-        void     configurationComplete(bool isConstrainedHighSpeed);
+        void     configurationComplete(bool isConstrainedHighSpeed,
+                const CameraMetadata& sessionParams);
 
         /**
          * Set or clear the list of repeating requests. Does not block
@@ -790,6 +819,10 @@
         // Stop the repeating request if any of its output streams is abandoned.
         void checkAndStopRepeatingRequest();
 
+        // Release physical camera settings and camera id resources.
+        void cleanupPhysicalSettings(sp<CaptureRequest> request,
+                /*out*/camera3_capture_request_t *halRequest);
+
         // Pause handling
         bool               waitIfPaused();
         void               unpauseForNewRequests();
@@ -812,6 +845,12 @@
         // Calculate the expected maximum duration for a request
         nsecs_t calculateMaxExpectedDuration(const camera_metadata_t *request);
 
+        // Check and update latest session parameters based on the current request settings.
+        bool updateSessionParameters(const CameraMetadata& settings);
+
+        // Re-configure camera using the latest session parameters.
+        bool reconfigureCamera();
+
         wp<Camera3Device>  mParent;
         wp<camera3::StatusTracker>  mStatusTracker;
         sp<HalInterface>   mInterface;
@@ -869,6 +908,9 @@
 
         static const int32_t kRequestLatencyBinSize = 40; // in ms
         CameraLatencyHistogram mRequestLatency;
+
+        Vector<int32_t>    mSessionParamKeys;
+        CameraMetadata     mLatestSessionParams;
     };
     sp<RequestThread> mRequestThread;
 
@@ -1006,21 +1048,34 @@
          */
         status_t clear();
 
+        /**
+         * Pause all preparation activities
+         */
+        void pause();
+
+        /**
+         * Resume preparation activities
+         */
+        status_t resume();
+
       private:
         Mutex mLock;
+        Condition mThreadActiveSignal;
 
         virtual bool threadLoop();
 
         // Guarded by mLock
 
         wp<NotificationListener> mListener;
-        List<sp<camera3::Camera3StreamInterface> > mPendingStreams;
+        std::unordered_map<int, sp<camera3::Camera3StreamInterface> > mPendingStreams;
         bool mActive;
         bool mCancelNow;
 
         // Only accessed by threadLoop and the destructor
 
         sp<camera3::Camera3StreamInterface> mCurrentStream;
+        int mCurrentMaxCount;
+        bool mCurrentPrepareComplete;
     };
     sp<PreparerThread> mPreparerThread;
 
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 0a245c4..44eb68a 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2014-2018 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.
@@ -26,9 +26,12 @@
 
 namespace camera3 {
 
+const String8 Camera3DummyStream::DUMMY_ID;
+
 Camera3DummyStream::Camera3DummyStream(int id) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, DUMMY_WIDTH, DUMMY_HEIGHT,
-                /*maxSize*/0, DUMMY_FORMAT, DUMMY_DATASPACE, DUMMY_ROTATION) {
+                /*maxSize*/0, DUMMY_FORMAT, DUMMY_DATASPACE, DUMMY_ROTATION,
+                DUMMY_ID) {
 
 }
 
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index 684f4b0..dcf9160 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2014-2018 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.
@@ -114,6 +114,7 @@
     static const android_dataspace DUMMY_DATASPACE = HAL_DATASPACE_UNKNOWN;
     static const camera3_stream_rotation_t DUMMY_ROTATION = CAMERA3_STREAM_ROTATION_0;
     static const uint64_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER;
+    static const String8 DUMMY_ID;
 
     /**
      * Internal Camera3Stream interface
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index a52422d..3c1e43d 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -31,9 +31,11 @@
 
 Camera3IOStreamBase::Camera3IOStreamBase(int id, camera3_stream_type_t type,
         uint32_t width, uint32_t height, size_t maxSize, int format,
-        android_dataspace dataSpace, camera3_stream_rotation_t rotation, int setId) :
+        android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+        const String8& physicalCameraId, int setId) :
         Camera3Stream(id, type,
-                width, height, maxSize, format, dataSpace, rotation, setId),
+                width, height, maxSize, format, dataSpace, rotation,
+                physicalCameraId, setId),
         mTotalBufferCount(0),
         mHandoutTotalBufferCount(0),
         mHandoutOutputBufferCount(0),
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 2376058..0a31d44 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -35,6 +35,7 @@
     Camera3IOStreamBase(int id, camera3_stream_type_t type,
             uint32_t width, uint32_t height, size_t maxSize, int format,
             android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+            const String8& physicalCameraId,
             int setId = CAMERA3_STREAM_SET_ID_INVALID);
 
   public:
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 2cb1ea7..017d7be 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -27,10 +27,13 @@
 
 namespace camera3 {
 
+const String8 Camera3InputStream::DUMMY_ID;
+
 Camera3InputStream::Camera3InputStream(int id,
         uint32_t width, uint32_t height, int format) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height, /*maxSize*/0,
-                            format, HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0) {
+                            format, HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0,
+                            DUMMY_ID) {
 
     if (format == HAL_PIXEL_FORMAT_BLOB) {
         ALOGE("%s: Bad format, BLOB not supported", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index 81226f8..0732464 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -53,6 +53,8 @@
     sp<IGraphicBufferProducer> mProducer;
     Vector<BufferItem> mBuffersInFlight;
 
+    static const String8 DUMMY_ID;
+
     /**
      * Camera3IOStreamBase
      */
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index e79eecc..b73e256 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -35,9 +35,11 @@
         sp<Surface> consumer,
         uint32_t width, uint32_t height, int format,
         android_dataspace dataSpace, camera3_stream_rotation_t rotation,
-        nsecs_t timestampOffset, int setId) :
+        nsecs_t timestampOffset, const String8& physicalCameraId,
+        int setId) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height,
-                            /*maxSize*/0, format, dataSpace, rotation, setId),
+                            /*maxSize*/0, format, dataSpace, rotation,
+                            physicalCameraId, setId),
         mConsumer(consumer),
         mTransform(0),
         mTraceFirstBuffer(true),
@@ -61,9 +63,9 @@
         sp<Surface> consumer,
         uint32_t width, uint32_t height, size_t maxSize, int format,
         android_dataspace dataSpace, camera3_stream_rotation_t rotation,
-        nsecs_t timestampOffset, int setId) :
+        nsecs_t timestampOffset, const String8& physicalCameraId, int setId) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, maxSize,
-                            format, dataSpace, rotation, setId),
+                            format, dataSpace, rotation, physicalCameraId, setId),
         mConsumer(consumer),
         mTransform(0),
         mTraceFirstBuffer(true),
@@ -93,9 +95,11 @@
 Camera3OutputStream::Camera3OutputStream(int id,
         uint32_t width, uint32_t height, int format,
         uint64_t consumerUsage, android_dataspace dataSpace,
-        camera3_stream_rotation_t rotation, nsecs_t timestampOffset, int setId) :
+        camera3_stream_rotation_t rotation, nsecs_t timestampOffset,
+        const String8& physicalCameraId, int setId) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height,
-                            /*maxSize*/0, format, dataSpace, rotation, setId),
+                            /*maxSize*/0, format, dataSpace, rotation,
+                            physicalCameraId, setId),
         mConsumer(nullptr),
         mTransform(0),
         mTraceFirstBuffer(true),
@@ -131,11 +135,13 @@
                                          int format,
                                          android_dataspace dataSpace,
                                          camera3_stream_rotation_t rotation,
+                                         const String8& physicalCameraId,
                                          uint64_t consumerUsage, nsecs_t timestampOffset,
                                          int setId) :
         Camera3IOStreamBase(id, type, width, height,
                             /*maxSize*/0,
-                            format, dataSpace, rotation, setId),
+                            format, dataSpace, rotation,
+                            physicalCameraId, setId),
         mTransform(0),
         mTraceFirstBuffer(true),
         mUseMonoTimestamp(false),
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 18b1901..824aef7 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -82,7 +82,8 @@
     Camera3OutputStream(int id, sp<Surface> consumer,
             uint32_t width, uint32_t height, int format,
             android_dataspace dataSpace, camera3_stream_rotation_t rotation,
-            nsecs_t timestampOffset, int setId = CAMERA3_STREAM_SET_ID_INVALID);
+            nsecs_t timestampOffset, const String8& physicalCameraId,
+            int setId = CAMERA3_STREAM_SET_ID_INVALID);
 
     /**
      * Set up a stream for formats that have a variable buffer size for the same
@@ -93,7 +94,8 @@
     Camera3OutputStream(int id, sp<Surface> consumer,
             uint32_t width, uint32_t height, size_t maxSize, int format,
             android_dataspace dataSpace, camera3_stream_rotation_t rotation,
-            nsecs_t timestampOffset, int setId = CAMERA3_STREAM_SET_ID_INVALID);
+            nsecs_t timestampOffset, const String8& physicalCameraId,
+            int setId = CAMERA3_STREAM_SET_ID_INVALID);
 
     /**
      * Set up a stream with deferred consumer for formats that have 2 dimensions, such as
@@ -103,6 +105,7 @@
     Camera3OutputStream(int id, uint32_t width, uint32_t height, int format,
             uint64_t consumerUsage, android_dataspace dataSpace,
             camera3_stream_rotation_t rotation, nsecs_t timestampOffset,
+            const String8& physicalCameraId,
             int setId = CAMERA3_STREAM_SET_ID_INVALID);
 
     virtual ~Camera3OutputStream();
@@ -194,6 +197,7 @@
     Camera3OutputStream(int id, camera3_stream_type_t type,
             uint32_t width, uint32_t height, int format,
             android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+            const String8& physicalCameraId,
             uint64_t consumerUsage = 0, nsecs_t timestampOffset = 0,
             int setId = CAMERA3_STREAM_SET_ID_INVALID);
 
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 1c9417b..2bb9ff7 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2016-2018 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.
@@ -27,10 +27,11 @@
         uint32_t width, uint32_t height, int format,
         uint64_t consumerUsage, android_dataspace dataSpace,
         camera3_stream_rotation_t rotation,
-        nsecs_t timestampOffset, int setId) :
+        nsecs_t timestampOffset, const String8& physicalCameraId,
+        int setId) :
         Camera3OutputStream(id, CAMERA3_STREAM_OUTPUT, width, height,
-                            format, dataSpace, rotation, consumerUsage,
-                            timestampOffset, setId) {
+                            format, dataSpace, rotation, physicalCameraId,
+                            consumerUsage, timestampOffset, setId) {
     size_t consumerCount = std::min(surfaces.size(), kMaxOutputs);
     if (surfaces.size() > consumerCount) {
         ALOGE("%s: Trying to add more consumers than the maximum ", __func__);
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index 6eab8bd..02b1c09 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2016-2018 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.
@@ -36,6 +36,7 @@
             uint32_t width, uint32_t height, int format,
             uint64_t consumerUsage, android_dataspace dataSpace,
             camera3_stream_rotation_t rotation, nsecs_t timestampOffset,
+            const String8& physicalCameraId,
             int setId = CAMERA3_STREAM_SET_ID_INVALID);
 
     virtual ~Camera3SharedOutputStream();
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index fbe8f4f..0d91620 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -47,7 +47,8 @@
 Camera3Stream::Camera3Stream(int id,
         camera3_stream_type type,
         uint32_t width, uint32_t height, size_t maxSize, int format,
-        android_dataspace dataSpace, camera3_stream_rotation_t rotation, int setId) :
+        android_dataspace dataSpace, camera3_stream_rotation_t rotation,
+        const String8& physicalCameraId, int setId) :
     camera3_stream(),
     mId(id),
     mSetId(setId),
@@ -64,7 +65,8 @@
     mLastMaxCount(Camera3StreamInterface::ALLOCATE_PIPELINE_MAX),
     mBufferLimitLatency(kBufferLimitLatencyBinSize),
     mFormatOverridden(false),
-    mOriginalFormat(-1) {
+    mOriginalFormat(-1),
+    mPhysicalCameraId(physicalCameraId) {
 
     camera3_stream::stream_type = type;
     camera3_stream::width = width;
@@ -74,6 +76,7 @@
     camera3_stream::rotation = rotation;
     camera3_stream::max_buffers = 0;
     camera3_stream::priv = NULL;
+    camera3_stream::physical_camera_id = mPhysicalCameraId.string();
 
     if ((format == HAL_PIXEL_FORMAT_BLOB || format == HAL_PIXEL_FORMAT_RAW_OPAQUE) &&
             maxSize == 0) {
@@ -140,6 +143,75 @@
     return mOriginalDataSpace;
 }
 
+status_t Camera3Stream::forceToIdle() {
+    ATRACE_CALL();
+    Mutex::Autolock l(mLock);
+    status_t res;
+
+    switch (mState) {
+        case STATE_ERROR:
+        case STATE_CONSTRUCTED:
+        case STATE_IN_CONFIG:
+        case STATE_PREPARING:
+        case STATE_IN_RECONFIG:
+            ALOGE("%s: Invalid state: %d", __FUNCTION__, mState);
+            res = NO_INIT;
+            break;
+        case STATE_CONFIGURED:
+            if (hasOutstandingBuffersLocked()) {
+                sp<StatusTracker> statusTracker = mStatusTracker.promote();
+                if (statusTracker != 0) {
+                    statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
+                }
+            }
+
+            mState = STATE_IN_IDLE;
+            res = OK;
+
+            break;
+        default:
+            ALOGE("%s: Unknown state %d", __FUNCTION__, mState);
+            res = NO_INIT;
+    }
+
+    return res;
+}
+
+status_t Camera3Stream::restoreConfiguredState() {
+    ATRACE_CALL();
+    Mutex::Autolock l(mLock);
+    status_t res;
+
+    switch (mState) {
+        case STATE_ERROR:
+        case STATE_CONSTRUCTED:
+        case STATE_IN_CONFIG:
+        case STATE_PREPARING:
+        case STATE_IN_RECONFIG:
+        case STATE_CONFIGURED:
+            ALOGE("%s: Invalid state: %d", __FUNCTION__, mState);
+            res = NO_INIT;
+            break;
+        case STATE_IN_IDLE:
+            if (hasOutstandingBuffersLocked()) {
+                sp<StatusTracker> statusTracker = mStatusTracker.promote();
+                if (statusTracker != 0) {
+                    statusTracker->markComponentActive(mStatusId);
+                }
+            }
+
+            mState = STATE_CONFIGURED;
+            res = OK;
+
+            break;
+        default:
+            ALOGE("%s: Unknown state %d", __FUNCTION__, mState);
+            res = NO_INIT;
+    }
+
+    return res;
+}
+
 camera3_stream* Camera3Stream::startConfiguration() {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
@@ -150,6 +222,7 @@
             ALOGE("%s: In error state", __FUNCTION__);
             return NULL;
         case STATE_CONSTRUCTED:
+        case STATE_IN_IDLE:
             // OK
             break;
         case STATE_IN_CONFIG:
@@ -179,6 +252,11 @@
         return NULL;
     }
 
+    if (mState == STATE_IN_IDLE) {
+        // Skip configuration.
+        return this;
+    }
+
     // Stop tracking if currently doing so
     if (mStatusId != StatusTracker::NO_STATUS_ID) {
         sp<StatusTracker> statusTracker = mStatusTracker.promote();
@@ -219,6 +297,9 @@
             ALOGE("%s: Cannot finish configuration that hasn't been started",
                     __FUNCTION__);
             return INVALID_OPERATION;
+        case STATE_IN_IDLE:
+            //Skip configuration in this state
+            return OK;
         default:
             ALOGE("%s: Unknown state", __FUNCTION__);
             return INVALID_OPERATION;
@@ -267,6 +348,7 @@
             return INVALID_OPERATION;
         case STATE_IN_CONFIG:
         case STATE_IN_RECONFIG:
+        case STATE_IN_IDLE:
             // OK
             break;
         case STATE_CONSTRUCTED:
@@ -282,7 +364,9 @@
     mUsage = mOldUsage;
     camera3_stream::max_buffers = mOldMaxBuffers;
 
-    mState = (mState == STATE_IN_RECONFIG) ? STATE_CONFIGURED : STATE_CONSTRUCTED;
+    mState = ((mState == STATE_IN_RECONFIG) || (mState == STATE_IN_IDLE)) ? STATE_CONFIGURED :
+            STATE_CONSTRUCTED;
+
     return OK;
 }
 
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 6e7912e..f85dff2 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2018 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.
@@ -68,6 +68,12 @@
  *    duration.  In this state, only prepareNextBuffer() and cancelPrepare()
  *    may be called.
  *
+ *  STATE_IN_IDLE: This is a temporary state only intended to be used for input
+ *    streams and only for the case where we need to re-configure the camera device
+ *    while the input stream has an outstanding buffer. All other streams should not
+ *    be able to switch to this state. For them this is invalid and should be handled
+ *    as an unknown state.
+ *
  * Transition table:
  *
  *    <none>               => STATE_CONSTRUCTED:
@@ -98,6 +104,11 @@
  *        all stream buffers, or cancelPrepare is called.
  *    STATE_CONFIGURED     => STATE_ABANDONED:
  *        When the buffer queue of the stream is abandoned.
+ *    STATE_CONFIGURED     => STATE_IN_IDLE:
+ *        Only for an input stream which has an outstanding buffer.
+ *    STATE_IN_IDLE     => STATE_CONFIGURED:
+ *        After the internal re-configuration, the input should revert back to
+ *        the configured state.
  *
  * Status Tracking:
  *    Each stream is tracked by StatusTracker as a separate component,
@@ -108,7 +119,9 @@
  *
  *    - ACTIVE: One or more buffers have been handed out (with #getBuffer).
  *    - IDLE: All buffers have been returned (with #returnBuffer), and their
- *          respective release_fence(s) have been signaled.
+ *          respective release_fence(s) have been signaled. The only exception to this
+ *          rule is an input stream that moves to "STATE_IN_IDLE" during internal
+ *          re-configuration.
  *
  *    A typical use case is output streams. When the HAL has any buffers
  *    dequeued, the stream is marked ACTIVE. When the HAL returns all buffers
@@ -386,6 +399,19 @@
      */
     bool             isAbandoned() const;
 
+    /**
+     * Switch a configured stream with possibly outstanding buffers in idle
+     * state. Configuration for such streams will be skipped assuming there
+     * are no changes to the stream parameters.
+     */
+    status_t         forceToIdle();
+
+    /**
+     * Restore a forced idle stream to configured state, marking it active
+     * in case it contains outstanding buffers.
+     */
+    status_t         restoreConfiguredState();
+
   protected:
     const int mId;
     /**
@@ -414,7 +440,8 @@
         STATE_IN_RECONFIG,
         STATE_CONFIGURED,
         STATE_PREPARING,
-        STATE_ABANDONED
+        STATE_ABANDONED,
+        STATE_IN_IDLE
     } mState;
 
     mutable Mutex mLock;
@@ -422,7 +449,7 @@
     Camera3Stream(int id, camera3_stream_type type,
             uint32_t width, uint32_t height, size_t maxSize, int format,
             android_dataspace dataSpace, camera3_stream_rotation_t rotation,
-            int setId);
+            const String8& physicalCameraId, int setId);
 
     wp<Camera3StreamBufferFreedListener> mBufferFreedListener;
 
@@ -529,6 +556,7 @@
     bool mDataSpaceOverridden;
     android_dataspace mOriginalDataSpace;
 
+    String8 mPhysicalCameraId;
 }; // class Camera3Stream
 
 }; // namespace camera3
diff --git a/services/mediaanalytics/Android.mk b/services/mediaanalytics/Android.mk
index 9e2813e..2eeb7fa 100644
--- a/services/mediaanalytics/Android.mk
+++ b/services/mediaanalytics/Android.mk
@@ -6,11 +6,6 @@
 
 LOCAL_SRC_FILES:= \
     main_mediametrics.cpp              \
-    MetricsSummarizerCodec.cpp         \
-    MetricsSummarizerExtractor.cpp     \
-    MetricsSummarizerPlayer.cpp        \
-    MetricsSummarizerRecorder.cpp      \
-    MetricsSummarizer.cpp              \
     MediaAnalyticsService.cpp
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 2954b3b..7f52802 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -74,26 +74,11 @@
 
 #include "MediaAnalyticsService.h"
 
-#include "MetricsSummarizer.h"
-#include "MetricsSummarizerCodec.h"
-#include "MetricsSummarizerExtractor.h"
-#include "MetricsSummarizerPlayer.h"
-#include "MetricsSummarizerRecorder.h"
-
-
 namespace android {
 
     using namespace android::base;
     using namespace android::content::pm;
 
-
-
-// summarized records
-// up to 36 sets, each covering an hour -- so at least 1.5 days
-// (will be longer if there are hours without any media action)
-static const nsecs_t kNewSetIntervalNs = 3600*(1000*1000*1000ll);
-static const int kMaxRecordSets = 36;
-
 // individual records kept in memory: age or count
 // age: <= 36 hours (1.5 days)
 // count: hard limit of # records
@@ -108,57 +93,9 @@
             String16(kServiceName), new MediaAnalyticsService());
 }
 
-// handle sets of summarizers
-MediaAnalyticsService::SummarizerSet::SummarizerSet() {
-    mSummarizers = new List<MetricsSummarizer *>();
-}
-
-MediaAnalyticsService::SummarizerSet::~SummarizerSet() {
-    // empty the list
-    List<MetricsSummarizer *> *l = mSummarizers;
-    while (l->size() > 0) {
-        MetricsSummarizer *summarizer = *(l->begin());
-        l->erase(l->begin());
-        delete summarizer;
-    }
-}
-
-void MediaAnalyticsService::newSummarizerSet() {
-    ALOGD("MediaAnalyticsService::newSummarizerSet");
-    MediaAnalyticsService::SummarizerSet *set = new MediaAnalyticsService::SummarizerSet();
-    nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
-    set->setStarted(now);
-
-    set->appendSummarizer(new MetricsSummarizerExtractor("extractor"));
-    set->appendSummarizer(new MetricsSummarizerCodec("codec"));
-    set->appendSummarizer(new MetricsSummarizerPlayer("nuplayer"));
-    set->appendSummarizer(new MetricsSummarizerRecorder("recorder"));
-
-    // ALWAYS at the end, since it catches everything
-    set->appendSummarizer(new MetricsSummarizer(NULL));
-
-    // inject this set at the BACK of the list.
-    mSummarizerSets->push_back(set);
-    mCurrentSet = set;
-
-    // limit the # that we have
-    if (mMaxRecordSets > 0) {
-        List<SummarizerSet *> *l = mSummarizerSets;
-        while (l->size() > (size_t) mMaxRecordSets) {
-            ALOGD("Deleting oldest record set....");
-            MediaAnalyticsService::SummarizerSet *oset = *(l->begin());
-            l->erase(l->begin());
-            delete oset;
-            mSetsDiscarded++;
-        }
-    }
-}
-
 MediaAnalyticsService::MediaAnalyticsService()
         : mMaxRecords(kMaxRecords),
           mMaxRecordAgeNs(kMaxRecordAgeNs),
-          mMaxRecordSets(kMaxRecordSets),
-          mNewSetInterval(kNewSetIntervalNs),
           mDumpProto(MediaAnalyticsItem::PROTO_V1),
           mDumpProtoDefault(MediaAnalyticsItem::PROTO_V1) {
 
@@ -167,9 +104,6 @@
     mOpen = new List<MediaAnalyticsItem *>();
     mFinalized = new List<MediaAnalyticsItem *>();
 
-    mSummarizerSets = new List<MediaAnalyticsService::SummarizerSet *>();
-    newSummarizerSet();
-
     mItemsSubmitted = 0;
     mItemsFinalized = 0;
     mItemsDiscarded = 0;
@@ -204,8 +138,6 @@
     }
     delete mFinalized;
     mFinalized = NULL;
-
-    // XXX: clean out the summaries
 }
 
 
@@ -315,13 +247,11 @@
                 oitem = NULL;
             } else {
                 oitem->setFinalized(true);
-                summarize(oitem);
                 saveItem(mFinalized, oitem, 0);
             }
             // new record could itself be marked finalized...
             id = item->getSessionID();
             if (finalizing) {
-                summarize(item);
                 saveItem(mFinalized, item, 0);
                 mItemsFinalized++;
             } else {
@@ -332,7 +262,6 @@
             oitem->merge(item);
             id = oitem->getSessionID();
             if (finalizing) {
-                summarize(oitem);
                 saveItem(mFinalized, oitem, 0);
                 mItemsFinalized++;
             }
@@ -350,7 +279,6 @@
                 delete item;
                 item = NULL;
             } else {
-                summarize(item);
                 saveItem(mFinalized, item, 0);
                 mItemsFinalized++;
             }
@@ -379,8 +307,6 @@
     }
 
     // crack any parameters
-    String16 summaryOption("-summary");
-    bool summary = false;
     String16 protoOption("-proto");
     int chosenProto = mDumpProtoDefault;
     String16 clearOption("-clear");
@@ -396,8 +322,6 @@
         String8 myarg(args[i]);
         if (args[i] == clearOption) {
             clear = true;
-        } else if (args[i] == summaryOption) {
-            summary = true;
         } else if (args[i] == protoOption) {
             i++;
             if (i < n) {
@@ -444,7 +368,6 @@
             result.append("Recognized parameters:\n");
             result.append("-help        this help message\n");
             result.append("-proto #     dump using protocol #");
-            result.append("-summary     show summary info\n");
             result.append("-clear       clears out saved records\n");
             result.append("-only X      process records for component X\n");
             result.append("-since X     include records since X\n");
@@ -464,12 +387,7 @@
 
     dumpHeaders(result, ts_since);
 
-    // want exactly 1, to avoid confusing folks that parse the output
-    if (summary) {
-        dumpSummaries(result, ts_since, only.c_str());
-    } else {
-        dumpRecent(result, ts_since, only.c_str());
-    }
+    dumpRecent(result, ts_since, only.c_str());
 
 
     if (clear) {
@@ -526,40 +444,6 @@
     }
 }
 
-// dump summary info
-void MediaAnalyticsService::dumpSummaries(String8 &result, nsecs_t ts_since, const char *only) {
-    const size_t SIZE = 512;
-    char buffer[SIZE];
-    int slot = 0;
-
-    snprintf(buffer, SIZE, "\nSummarized Metrics:\n");
-    result.append(buffer);
-
-    if (only != NULL && *only == '\0') {
-        only = NULL;
-    }
-
-    // have each of the distillers dump records
-    if (mSummarizerSets != NULL) {
-        List<SummarizerSet *>::iterator itSet = mSummarizerSets->begin();
-        for (; itSet != mSummarizerSets->end(); itSet++) {
-            nsecs_t when = (*itSet)->getStarted();
-            if (when < ts_since) {
-                continue;
-            }
-            List<MetricsSummarizer *> *list = (*itSet)->getSummarizers();
-            List<MetricsSummarizer *>::iterator it = list->begin();
-            for (; it != list->end(); it++) {
-                if (only != NULL && strcmp(only, (*it)->getKey()) != 0) {
-                    ALOGV("Told to omit '%s'", (*it)->getKey());
-                }
-                std::string distilled = (*it)->dumpSummary(slot, only);
-                result.append(distilled.c_str());
-            }
-        }
-    }
-}
-
 // the recent, detailed queues
 void MediaAnalyticsService::dumpRecent(String8 &result, nsecs_t ts_since, const char * only) {
     const size_t SIZE = 512;
@@ -785,45 +669,6 @@
     return false;
 }
 
-// insert into the appropriate summarizer.
-// we make our own copy to save/summarize
-void MediaAnalyticsService::summarize(MediaAnalyticsItem *item) {
-
-    ALOGV("MediaAnalyticsService::summarize()");
-
-    if (item == NULL) {
-        return;
-    }
-
-    nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
-    if (mCurrentSet == NULL
-        || (mCurrentSet->getStarted() + mNewSetInterval < now)) {
-        newSummarizerSet();
-    }
-
-    if (mCurrentSet == NULL) {
-        return;
-    }
-
-    List<MetricsSummarizer *> *summarizers = mCurrentSet->getSummarizers();
-    List<MetricsSummarizer *>::iterator it = summarizers->begin();
-    for (; it != summarizers->end(); it++) {
-        if ((*it)->isMine(*item)) {
-            break;
-        }
-    }
-    if (it == summarizers->end()) {
-        ALOGD("no handler for type %s", item->getKey().c_str());
-        return;               // no handler
-    }
-
-    // invoke the summarizer. summarizer will make whatever copies
-    // it wants; the caller retains ownership of item.
-
-    (*it)->handleRecord(item);
-
-}
-
 // how long we hold package info before we re-fetch it
 #define PKG_EXPIRATION_NS (30*60*1000000000ll)   // 30 minutes, in nsecs
 
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index 1287835..484339c 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -28,9 +28,6 @@
 
 #include <media/IMediaAnalyticsService.h>
 
-#include "MetricsSummarizer.h"
-
-
 namespace android {
 
 class MediaAnalyticsService : public BnMediaAnalyticsService
@@ -89,36 +86,6 @@
     MediaAnalyticsItem *findItem(List<MediaAnalyticsItem *> *,
                                      MediaAnalyticsItem *, bool removeit);
 
-    // summarizers
-    void summarize(MediaAnalyticsItem *item);
-    class SummarizerSet {
-        nsecs_t mStarted;
-        List<MetricsSummarizer *> *mSummarizers;
-
-      public:
-        void appendSummarizer(MetricsSummarizer *s) {
-            if (s) {
-                mSummarizers->push_back(s);
-            }
-        };
-        nsecs_t getStarted() { return mStarted;}
-        void setStarted(nsecs_t started) {mStarted = started;}
-        List<MetricsSummarizer *> *getSummarizers() { return mSummarizers;}
-
-        SummarizerSet();
-        ~SummarizerSet();
-    };
-    void newSummarizerSet();
-    List<SummarizerSet *> *mSummarizerSets;
-    SummarizerSet *mCurrentSet;
-    List<MetricsSummarizer *> *getFirstSet() {
-        List<SummarizerSet *>::iterator first = mSummarizerSets->begin();
-        if (first != mSummarizerSets->end()) {
-            return (*first)->getSummarizers();
-        }
-        return NULL;
-    }
-
     void saveItem(MediaAnalyticsItem);
     void saveItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *, int);
     void deleteItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *);
diff --git a/services/mediaanalytics/MetricsSummarizer.cpp b/services/mediaanalytics/MetricsSummarizer.cpp
deleted file mode 100644
index e7c26e3..0000000
--- a/services/mediaanalytics/MetricsSummarizer.cpp
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "MetricsSummarizer"
-#include <utils/Log.h>
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <string>
-#include <inttypes.h>
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/List.h>
-
-#include <media/IMediaAnalyticsService.h>
-
-#include "MetricsSummarizer.h"
-
-
-namespace android {
-
-#define DEBUG_SORT      0
-#define DEBUG_QUEUE     0
-
-
-MetricsSummarizer::MetricsSummarizer(const char *key)
-    : mIgnorables(NULL)
-{
-    ALOGV("MetricsSummarizer::MetricsSummarizer");
-
-    if (key == NULL) {
-        mKey = key;
-    } else {
-        mKey = strdup(key);
-    }
-
-    mSummaries = new List<MediaAnalyticsItem *>();
-}
-
-MetricsSummarizer::~MetricsSummarizer()
-{
-    ALOGV("MetricsSummarizer::~MetricsSummarizer");
-    if (mKey) {
-        free((void *)mKey);
-        mKey = NULL;
-    }
-
-    // clear the list of items we have saved
-    while (mSummaries->size() > 0) {
-        MediaAnalyticsItem * oitem = *(mSummaries->begin());
-        if (DEBUG_QUEUE) {
-            ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
-                oitem->getKey().c_str(), oitem->getSessionID(),
-                oitem->getTimestamp());
-        }
-        mSummaries->erase(mSummaries->begin());
-        delete oitem;
-    }
-}
-
-// so we know what summarizer we were using
-const char *MetricsSummarizer::getKey() {
-    const char *value = mKey;
-    if (value == NULL) {
-        value = "unknown";
-    }
-    return value;
-}
-
-// should the record be given to this summarizer
-bool MetricsSummarizer::isMine(MediaAnalyticsItem &item)
-{
-    if (mKey == NULL)
-        return true;
-    std::string itemKey = item.getKey();
-    if (strcmp(mKey, itemKey.c_str()) != 0) {
-        return false;
-    }
-    return true;
-}
-
-std::string MetricsSummarizer::dumpSummary(int &slot)
-{
-    return dumpSummary(slot, NULL);
-}
-
-std::string MetricsSummarizer::dumpSummary(int &slot, const char *only)
-{
-    std::string value;
-
-    List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
-    if (it != mSummaries->end()) {
-        char buf[16];   // enough for "#####: "
-        for (; it != mSummaries->end(); it++) {
-            if (only != NULL && strcmp(only, (*it)->getKey().c_str()) != 0) {
-                continue;
-            }
-            std::string entry = (*it)->toString();
-            snprintf(buf, sizeof(buf), "%5d: ", slot);
-            value.append(buf);
-            value.append(entry.c_str());
-            value.append("\n");
-            slot++;
-        }
-    }
-    return value;
-}
-
-void MetricsSummarizer::setIgnorables(const char **ignorables) {
-    mIgnorables = ignorables;
-}
-
-const char **MetricsSummarizer::getIgnorables() {
-    return mIgnorables;
-}
-
-void MetricsSummarizer::handleRecord(MediaAnalyticsItem *item) {
-
-    ALOGV("MetricsSummarizer::handleRecord() for %s",
-                item == NULL ? "<nothing>" : item->toString().c_str());
-
-    if (item == NULL) {
-        return;
-    }
-
-    List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
-    for (; it != mSummaries->end(); it++) {
-        bool good = sameAttributes((*it), item, getIgnorables());
-        ALOGV("Match against %s says %d", (*it)->toString().c_str(), good);
-        if (good)
-            break;
-    }
-    if (it == mSummaries->end()) {
-            ALOGV("save new record");
-            MediaAnalyticsItem *nitem = item->dup();
-            if (nitem == NULL) {
-                ALOGE("unable to save MediaMetrics record");
-            }
-            sortProps(nitem);
-            nitem->setInt32("aggregated",1);
-            mergeRecord(*nitem, *item);
-            mSummaries->push_back(nitem);
-    } else {
-            ALOGV("increment existing record");
-            (*it)->addInt32("aggregated",1);
-            mergeRecord(*(*it), *item);
-    }
-}
-
-void MetricsSummarizer::mergeRecord(MediaAnalyticsItem &/*have*/, MediaAnalyticsItem &/*item*/) {
-    // default is no further massaging.
-    ALOGV("MetricsSummarizer::mergeRecord() [default]");
-    return;
-}
-
-// keep some stats for things: sums, counts, standard deviation
-// the integer version -- all of these pieces are in 64 bits
-void MetricsSummarizer::minMaxVar64(MediaAnalyticsItem &summation, const char *key, int64_t value) {
-    if (key == NULL)
-        return;
-    int len = strlen(key) + 32;
-    char *tmpKey = (char *)malloc(len);
-
-    if (tmpKey == NULL) {
-        return;
-    }
-
-    // N - count of samples
-    snprintf(tmpKey, len, "%s.n", key);
-    summation.addInt64(tmpKey, 1);
-
-    // zero - count of samples that are zero
-    if (value == 0) {
-        snprintf(tmpKey, len, "%s.zero", key);
-        int64_t zero = 0;
-        (void) summation.getInt64(tmpKey,&zero);
-        zero++;
-        summation.setInt64(tmpKey, zero);
-    }
-
-    // min
-    snprintf(tmpKey, len, "%s.min", key);
-    int64_t min = value;
-    if (summation.getInt64(tmpKey,&min)) {
-        if (min > value) {
-            summation.setInt64(tmpKey, value);
-        }
-    } else {
-        summation.setInt64(tmpKey, value);
-    }
-
-    // max
-    snprintf(tmpKey, len, "%s.max", key);
-    int64_t max = value;
-    if (summation.getInt64(tmpKey,&max)) {
-        if (max < value) {
-            summation.setInt64(tmpKey, value);
-        }
-    } else {
-        summation.setInt64(tmpKey, value);
-    }
-
-    // components for mean, stddev;
-    // stddev = sqrt(1/4*(sumx2 - (2*sumx*sumx/n) + ((sumx/n)^2)))
-    // sum x
-    snprintf(tmpKey, len, "%s.sumX", key);
-    summation.addInt64(tmpKey, value);
-    // sum x^2
-    snprintf(tmpKey, len, "%s.sumX2", key);
-    summation.addInt64(tmpKey, value*value);
-
-
-    // last thing we do -- remove the base key from the summation
-    // record so we won't get confused about it having both individual
-    // and summary information in there.
-    summation.removeProp(key);
-
-    free(tmpKey);
-}
-
-
-//
-// Comparators
-//
-
-// testing that all of 'single' is in 'summ'
-// and that the values match.
-// 'summ' may have extra fields.
-// 'ignorable' is a set of things that we don't worry about matching up
-// (usually time- or count-based values we'll sum elsewhere)
-bool MetricsSummarizer::sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) {
-
-    if (single == NULL || summ == NULL) {
-        return false;
-    }
-    ALOGV("MetricsSummarizer::sameAttributes(): summ %s", summ->toString().c_str());
-    ALOGV("MetricsSummarizer::sameAttributes(): single %s", single->toString().c_str());
-
-    // keep different sources/users separate
-    if (single->mUid != summ->mUid) {
-        return false;
-    }
-
-    // this can be made better.
-    for(size_t i=0;i<single->mPropCount;i++) {
-        MediaAnalyticsItem::Prop *prop1 = &(single->mProps[i]);
-        const char *attrName = prop1->mName;
-
-        // is it something we should ignore
-        if (ignorable != NULL) {
-            const char **ig = ignorable;
-            for (;*ig; ig++) {
-                if (strcmp(*ig, attrName) == 0) {
-                    break;
-                }
-            }
-            if (*ig) {
-                ALOGV("we don't mind that it has attr '%s'", attrName);
-                continue;
-            }
-        }
-
-        MediaAnalyticsItem::Prop *prop2 = summ->findProp(attrName);
-        if (prop2 == NULL) {
-            ALOGV("summ doesn't have this attr");
-            return false;
-        }
-        if (prop1->mType != prop2->mType) {
-            ALOGV("mismatched attr types");
-            return false;
-        }
-        switch (prop1->mType) {
-            case MediaAnalyticsItem::kTypeInt32:
-                if (prop1->u.int32Value != prop2->u.int32Value) {
-                    ALOGV("mismatch values");
-                    return false;
-                }
-                break;
-            case MediaAnalyticsItem::kTypeInt64:
-                if (prop1->u.int64Value != prop2->u.int64Value) {
-                    ALOGV("mismatch values");
-                    return false;
-                }
-                break;
-            case MediaAnalyticsItem::kTypeDouble:
-                // XXX: watch out for floating point comparisons!
-                if (prop1->u.doubleValue != prop2->u.doubleValue) {
-                    ALOGV("mismatch values");
-                    return false;
-                }
-                break;
-            case MediaAnalyticsItem::kTypeCString:
-                if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0) {
-                    ALOGV("mismatch values");
-                    return false;
-                }
-                break;
-            case MediaAnalyticsItem::kTypeRate:
-                if (prop1->u.rate.count != prop2->u.rate.count) {
-                    ALOGV("mismatch values");
-                    return false;
-                }
-                if (prop1->u.rate.duration != prop2->u.rate.duration) {
-                    ALOGV("mismatch values");
-                    return false;
-                }
-                break;
-            default:
-                ALOGV("mismatch values in default type");
-                return false;
-        }
-    }
-
-    return true;
-}
-
-
-int MetricsSummarizer::PropSorter(const void *a, const void *b) {
-    MediaAnalyticsItem::Prop *ai = (MediaAnalyticsItem::Prop *)a;
-    MediaAnalyticsItem::Prop *bi = (MediaAnalyticsItem::Prop *)b;
-    return strcmp(ai->mName, bi->mName);
-}
-
-// we sort in the summaries so that it looks pretty in the dumpsys
-void MetricsSummarizer::sortProps(MediaAnalyticsItem *item) {
-    if (item->mPropCount != 0) {
-        qsort(item->mProps, item->mPropCount,
-              sizeof(MediaAnalyticsItem::Prop), MetricsSummarizer::PropSorter);
-    }
-}
-
-} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizer.h b/services/mediaanalytics/MetricsSummarizer.h
deleted file mode 100644
index a16c7bc..0000000
--- a/services/mediaanalytics/MetricsSummarizer.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef ANDROID_METRICSSUMMARIZER_H
-#define ANDROID_METRICSSUMMARIZER_H
-
-#include <string>
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/List.h>
-
-#include <media/IMediaAnalyticsService.h>
-
-
-namespace android {
-
-class MetricsSummarizer
-{
-
- public:
-
-    MetricsSummarizer(const char *key);
-    virtual ~MetricsSummarizer();
-
-    // show the key
-    const char * getKey();
-
-    // should the record be given to this summarizer
-    bool isMine(MediaAnalyticsItem &item);
-
-    // hand the record to this summarizer
-    void handleRecord(MediaAnalyticsItem *item);
-
-    virtual void mergeRecord(MediaAnalyticsItem &have, MediaAnalyticsItem &incoming);
-
-    // dump the summarized records (for dumpsys)
-    std::string dumpSummary(int &slot);
-    std::string dumpSummary(int &slot, const char *only);
-
-    void setIgnorables(const char **);
-    const char **getIgnorables();
-
- protected:
-
-    // various comparators
-    // "do these records have same attributes and values in those attrs"
-    bool sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignoreables);
-
-    void minMaxVar64(MediaAnalyticsItem &summ, const char *key, int64_t value);
-
-    static int PropSorter(const void *a, const void *b);
-    void sortProps(MediaAnalyticsItem *item);
-
- private:
-    const char *mKey;
-    const char **mIgnorables;
-    List<MediaAnalyticsItem *> *mSummaries;
-
-
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_METRICSSUMMARIZER_H
diff --git a/services/mediaanalytics/MetricsSummarizerCodec.cpp b/services/mediaanalytics/MetricsSummarizerCodec.cpp
deleted file mode 100644
index 6af3c9a..0000000
--- a/services/mediaanalytics/MetricsSummarizerCodec.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "MetricsSummarizerCodec"
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <inttypes.h>
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/List.h>
-
-#include <media/IMediaAnalyticsService.h>
-
-#include "MetricsSummarizer.h"
-#include "MetricsSummarizerCodec.h"
-
-
-
-
-namespace android {
-
-MetricsSummarizerCodec::MetricsSummarizerCodec(const char *key)
-    : MetricsSummarizer(key)
-{
-    ALOGV("MetricsSummarizerCodec::MetricsSummarizerCodec");
-}
-
-} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerCodec.h b/services/mediaanalytics/MetricsSummarizerCodec.h
deleted file mode 100644
index c01196f..0000000
--- a/services/mediaanalytics/MetricsSummarizerCodec.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef ANDROID_METRICSSUMMARIZERCODEC_H
-#define ANDROID_METRICSSUMMARIZERCODEC_H
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/List.h>
-
-#include <media/IMediaAnalyticsService.h>
-#include "MetricsSummarizer.h"
-
-
-namespace android {
-
-class MetricsSummarizerCodec : public MetricsSummarizer
-{
-
- public:
-
-    MetricsSummarizerCodec(const char *key);
-    virtual ~MetricsSummarizerCodec() {};
-
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_METRICSSUMMARIZERCODEC_H
diff --git a/services/mediaanalytics/MetricsSummarizerExtractor.cpp b/services/mediaanalytics/MetricsSummarizerExtractor.cpp
deleted file mode 100644
index 190f87d..0000000
--- a/services/mediaanalytics/MetricsSummarizerExtractor.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "MetricsSummarizerExtractor"
-#include <utils/Log.h>
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/List.h>
-
-#include <media/IMediaAnalyticsService.h>
-
-#include "MetricsSummarizer.h"
-#include "MetricsSummarizerExtractor.h"
-
-
-
-
-namespace android {
-
-MetricsSummarizerExtractor::MetricsSummarizerExtractor(const char *key)
-    : MetricsSummarizer(key)
-{
-    ALOGV("MetricsSummarizerExtractor::MetricsSummarizerExtractor");
-}
-
-} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerExtractor.h b/services/mediaanalytics/MetricsSummarizerExtractor.h
deleted file mode 100644
index eee052b..0000000
--- a/services/mediaanalytics/MetricsSummarizerExtractor.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef ANDROID_METRICSSUMMARIZEREXTRACTOR_H
-#define ANDROID_METRICSSUMMARIZEREXTRACTOR_H
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/List.h>
-
-#include <media/IMediaAnalyticsService.h>
-#include "MetricsSummarizer.h"
-
-
-namespace android {
-
-class MetricsSummarizerExtractor : public MetricsSummarizer
-{
-
- public:
-
-    MetricsSummarizerExtractor(const char *key);
-    virtual ~MetricsSummarizerExtractor() {};
-
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_METRICSSUMMARIZEREXTRACTOR_H
diff --git a/services/mediaanalytics/MetricsSummarizerPlayer.cpp b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
deleted file mode 100644
index f882cb9..0000000
--- a/services/mediaanalytics/MetricsSummarizerPlayer.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "MetricsSummarizerPlayer"
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <inttypes.h>
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/List.h>
-
-#include <media/IMediaAnalyticsService.h>
-
-#include "MetricsSummarizer.h"
-#include "MetricsSummarizerPlayer.h"
-
-
-
-
-namespace android {
-
-static const char *player_ignorable[] = {
-    "android.media.mediaplayer.durationMs",
-    "android.media.mediaplayer.playingMs",
-    "android.media.mediaplayer.frames",
-    "android.media.mediaplayer.dropped",
-    0
-};
-
-MetricsSummarizerPlayer::MetricsSummarizerPlayer(const char *key)
-    : MetricsSummarizer(key)
-{
-    ALOGV("MetricsSummarizerPlayer::MetricsSummarizerPlayer");
-    setIgnorables(player_ignorable);
-}
-
-// NB: this is also called for the first time -- so summation == item
-// Not sure if we need a flag for that or not.
-// In this particular mergeRecord() code -- we're' ok for this.
-void MetricsSummarizerPlayer::mergeRecord(MediaAnalyticsItem &summation, MediaAnalyticsItem &item) {
-
-    ALOGV("MetricsSummarizerPlayer::mergeRecord()");
-
-
-    int64_t duration = 0;
-    if (item.getInt64("android.media.mediaplayer.durationMs", &duration)) {
-        ALOGV("found durationMs of %" PRId64, duration);
-        minMaxVar64(summation, "android.media.mediaplayer.durationMs", duration);
-    }
-
-    int64_t playing = 0;
-    if (item.getInt64("android.media.mediaplayer.playingMs", &playing)) {
-        ALOGV("found playingMs of %" PRId64, playing);
-    }
-    if (playing >= 0) {
-        minMaxVar64(summation,"android.media.mediaplayer.playingMs",playing);
-    }
-
-    int64_t frames = 0;
-    if (item.getInt64("android.media.mediaplayer.frames", &frames)) {
-        ALOGV("found framess of %" PRId64, frames);
-    }
-    if (frames >= 0) {
-        minMaxVar64(summation,"android.media.mediaplayer.frames",frames);
-    }
-
-    int64_t dropped = 0;
-    if (item.getInt64("android.media.mediaplayer.dropped", &dropped)) {
-        ALOGV("found dropped of %" PRId64, dropped);
-    }
-    if (dropped >= 0) {
-        minMaxVar64(summation,"android.media.mediaplayer.dropped",dropped);
-    }
-}
-
-} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerPlayer.h b/services/mediaanalytics/MetricsSummarizerPlayer.h
deleted file mode 100644
index ad1bf74..0000000
--- a/services/mediaanalytics/MetricsSummarizerPlayer.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef ANDROID_METRICSSUMMARIZERPLAYER_H
-#define ANDROID_METRICSSUMMARIZERPLAYER_H
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/List.h>
-
-#include <media/IMediaAnalyticsService.h>
-#include "MetricsSummarizer.h"
-
-
-namespace android {
-
-class MetricsSummarizerPlayer : public MetricsSummarizer
-{
-
- public:
-
-    MetricsSummarizerPlayer(const char *key);
-    virtual ~MetricsSummarizerPlayer() {};
-
-    virtual void mergeRecord(MediaAnalyticsItem &have, MediaAnalyticsItem &incoming);
-
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_METRICSSUMMARIZERPLAYER_H
diff --git a/services/mediaanalytics/MetricsSummarizerRecorder.cpp b/services/mediaanalytics/MetricsSummarizerRecorder.cpp
deleted file mode 100644
index c2919c3..0000000
--- a/services/mediaanalytics/MetricsSummarizerRecorder.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "MetricsSummarizerRecorder"
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <inttypes.h>
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/List.h>
-
-#include <media/IMediaAnalyticsService.h>
-
-#include "MetricsSummarizer.h"
-#include "MetricsSummarizerRecorder.h"
-
-
-
-
-namespace android {
-
-MetricsSummarizerRecorder::MetricsSummarizerRecorder(const char *key)
-    : MetricsSummarizer(key)
-{
-    ALOGV("MetricsSummarizerRecorder::MetricsSummarizerRecorder");
-}
-
-} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerRecorder.h b/services/mediaanalytics/MetricsSummarizerRecorder.h
deleted file mode 100644
index 963baab..0000000
--- a/services/mediaanalytics/MetricsSummarizerRecorder.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef ANDROID_METRICSSUMMARIZERRECORDER_H
-#define ANDROID_METRICSSUMMARIZERRECORDER_H
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/List.h>
-
-#include <media/IMediaAnalyticsService.h>
-#include "MetricsSummarizer.h"
-
-
-namespace android {
-
-class MetricsSummarizerRecorder : public MetricsSummarizer
-{
-
- public:
-
-    MetricsSummarizerRecorder(const char *key);
-    virtual ~MetricsSummarizerRecorder() {};
-
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_METRICSSUMMARIZERRECORDER_H
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index 8e5b260..97eaf77 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -1,5 +1,28 @@
 LOCAL_PATH := $(call my-dir)
 
+_software_codecs := \
+    libstagefright_soft_aacdec \
+    libstagefright_soft_aacenc \
+    libstagefright_soft_amrdec \
+    libstagefright_soft_amrnbenc \
+    libstagefright_soft_amrwbenc \
+    libstagefright_soft_avcdec \
+    libstagefright_soft_avcenc \
+    libstagefright_soft_flacdec \
+    libstagefright_soft_flacenc \
+    libstagefright_soft_g711dec \
+    libstagefright_soft_gsmdec \
+    libstagefright_soft_hevcdec \
+    libstagefright_soft_mp3dec \
+    libstagefright_soft_mpeg2dec \
+    libstagefright_soft_mpeg4dec \
+    libstagefright_soft_mpeg4enc \
+    libstagefright_soft_opusdec \
+    libstagefright_soft_rawdec \
+    libstagefright_soft_vorbisdec \
+    libstagefright_soft_vpxdec \
+    libstagefright_soft_vpxenc \
+
 # service executable
 include $(CLEAR_VARS)
 # seccomp is not required for coverage build.
@@ -27,6 +50,15 @@
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_VENDOR_MODULE := true
 LOCAL_32_BIT_ONLY := true
+# Since this is 32-bit-only module, only 32-bit version of the codecs are installed.
+# TODO(b/72343507): eliminate the need for manually adding .vendor suffix. This should be done
+# by the build system.
+LOCAL_REQUIRED_MODULES := \
+$(foreach codec,$(_software_codecs),\
+  $(eval _vendor_suffix := $(if $(BOARD_VNDK_VERSION),.vendor))\
+  $(codec)$(_vendor_suffix)\
+)
+_software_codecs :=
 LOCAL_INIT_RC := android.hardware.media.omx@1.0-service.rc
 
 include $(BUILD_EXECUTABLE)
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
index 2daa829..e870965 100644
--- a/services/mediadrm/Android.mk
+++ b/services/mediadrm/Android.mk
@@ -28,7 +28,8 @@
     libhidlbase \
     libhidlmemory \
     libhidltransport \
-    android.hardware.drm@1.0
+    android.hardware.drm@1.0 \
+    android.hardware.drm@1.1
 
 LOCAL_CFLAGS += -Wall -Wextra -Werror
 
diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk
index 4980316..9f3746f 100644
--- a/services/mediaextractor/Android.mk
+++ b/services/mediaextractor/Android.mk
@@ -21,6 +21,16 @@
 
 # extractor libraries
 LOCAL_REQUIRED_MODULES := \
+    libaacextractor \
+    libamrextractor \
+    libflacextractor \
+    libmidiextractor \
+    libmkvextractor \
+    libmp3extractor \
+    libmp4extractor \
+    libmpeg2extractor \
+    liboggextractor \
+    libwavextractor \
     MediaComponents \
 
 LOCAL_SRC_FILES := main_extractorservice.cpp
diff --git a/services/mediaextractor/main_extractorservice.cpp b/services/mediaextractor/main_extractorservice.cpp
index 0dc5e29..8d3359a 100644
--- a/services/mediaextractor/main_extractorservice.cpp
+++ b/services/mediaextractor/main_extractorservice.cpp
@@ -25,6 +25,7 @@
 #include <string>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <utils/misc.h>
 
 // from LOCAL_C_INCLUDES
@@ -65,14 +66,10 @@
     sp<IServiceManager> sm = defaultServiceManager();
     MediaExtractorService::instantiate();
 
-    // TODO: Uncomment below once sepolicy change is landed.
-    /*
-    char value[PROPERTY_VALUE_MAX];
-    property_get("ro.build.type", value, "unknown");
-    if (strcmp(value, "userdebug") == 0 || strcmp(value, "eng") == 0) {
+    std::string value = base::GetProperty("ro.build.type", "unknown");
+    if (value == "userdebug" || value == "eng") {
         media::MediaExtractorUpdateService::instantiate();
     }
-    */
 
     ProcessState::self()->startThreadPool();
     IPCThreadState::self()->joinThreadPool();
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 864fc35..e8c9e41 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -271,11 +271,11 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::flush() {
-    if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
-        ALOGE("flush() stream not paused, state = %s",
-              AAudio_convertStreamStateToText(mState));
-        return AAUDIO_ERROR_INVALID_STATE;
+    aaudio_result_t result = AAudio_isFlushAllowed(getState());
+    if (result != AAUDIO_OK) {
+        return result;
     }
+
     // Data will get flushed when the client receives the FLUSHED event.
     sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
     setState(AAUDIO_STREAM_STATE_FLUSHED);
diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk
index ca44737..ad3666e 100644
--- a/services/soundtrigger/Android.mk
+++ b/services/soundtrigger/Android.mk
@@ -53,6 +53,7 @@
     libhidltransport \
     libbase \
     libaudiohal \
+    libaudiohal_deathhandler \
     android.hardware.soundtrigger@2.0 \
     android.hardware.soundtrigger@2.1 \
     android.hardware.audio.common@2.0 \