Merge "Fix memory leak"
diff --git a/apex/Android.bp b/apex/Android.bp
new file mode 100644
index 0000000..bb73ead
--- /dev/null
+++ b/apex/Android.bp
@@ -0,0 +1,40 @@
+// 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.
+
+apex {
+    name: "com.android.media",
+    manifest: "manifest.json",
+    native_shared_libs: [
+        // Extractor plugins
+        "libaacextractor",
+        "libamrextractor",
+        "libflacextractor",
+        "libmidiextractor",
+        "libmkvextractor",
+        "libmp3extractor",
+        "libmp4extractor",
+        "libmpeg2extractor",
+        "liboggextractor",
+        "libwavextractor",
+        // MediaPlayer2
+        "libmedia2_jni",
+    ],
+    key: "com.android.media.key",
+}
+
+apex_key {
+    name: "com.android.media.key",
+    public_key: "media.avbpubkey",
+    private_key: "media.pem",
+}
diff --git a/apex/OWNERS b/apex/OWNERS
new file mode 100644
index 0000000..5587f5f
--- /dev/null
+++ b/apex/OWNERS
@@ -0,0 +1,6 @@
+chz@google.com
+dwkang@google.com
+jiyong@google.com
+lajos@google.com
+marcone@google.com
+wjia@google.com
diff --git a/apex/manifest.json b/apex/manifest.json
new file mode 100644
index 0000000..e2df3a3
--- /dev/null
+++ b/apex/manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.media",
+  "version": 1
+}
diff --git a/apex/media.avbpubkey b/apex/media.avbpubkey
new file mode 100644
index 0000000..c0c8fd3
--- /dev/null
+++ b/apex/media.avbpubkey
Binary files differ
diff --git a/apex/media.pem b/apex/media.pem
new file mode 100644
index 0000000..8daa50e
--- /dev/null
+++ b/apex/media.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEA06dKiF+xQp36Xcosmac+DzJTXC9nbHy0Yqfy+zEC5hlwXbHZ
+1gAZZu8zL9p7kbBkmtSCulU0M+cTHr74gkG9UDkM/S7Z+957FzHMqWXY03gupFP7
+lcCnKtpkzsyQrABavynoxyY6dfmKZNtEFQrikK1zs80CppRoMwZS2dLogX8qO5LU
+gLe7/0PZBdbQSVA5AARE+AO6pR5Px/8QAere9TCLcm1aK9BUVOJvaAZAf7bD2f8s
+3J/lANQ1tvXXZrFL1i26H6sNja11u5/M0odg3SfqKI0x/317nLkYx8QSSHVKEjBs
+nzsyoFry4INEh/q7zSEX5+S1VA6ORjyof3u7CrGavrYwI2k6x3t+Dkc2dfNDaNY9
+9vGYD1nMyRqUzSIqaOz8q78tc1A391Lua8SB1E0Tx/FnsPjxPee0wZ1taGddkZxD
+cvMdQJhLyE6EloimFiOhkVjnAnlPYiiPEQkwJomE9kCsP9aMmyhwBOpbbRISj1ua
+edESrpTC5DHpt1owjtAfHvD8TfmPWT1KSN1iCQAuh5hnEM5LLDljc/AYvJV4L5uR
+l/6t5dE8deg9ksY6lVRrUwHsXxUtBPhM82PWSrpAPNJCHLuBBVx+zDD//kOmt5oe
+OyYJ2RcDsnLmDfFLHbPcAwuyfmxQ+pbFBfiL4NaKoUWRy0viR4/3zulysCsCAwEA
+AQKCAgAphaNIl8VVtVpdtgED79xr7MqPxjj6/ogA5sPzZY0VCR6TMwXyRri1Ce43
+0Bv32+wQt+ohlf+UwxtsJ7jnDPCP4XFb5iobkG0DguCMxw8/hU9ZK6Sqn03sSUYH
+j/g91h/3ashg8W38oQT2flGf8y+5hF2zg1+mwGykvfPZCdhVN1ZYs5h+3AzEqlHU
+JG1eRJ+6EhxZr5mZNRYfvTkttx8gaPKiczOCbu9sa7PBa6CRrZBEnxv0+GVbwUX8
+a8RjQBsJnJTsC4mwJrx3H4V2M9rb6C224ORTJBHxEBr9bcjMcD4kzV0x69IlxVHq
+m7YBGz5morxm4OZ15BkjTFkeEW8C8bdpRrYoY8ocmybWUf6g5IxpE33M699lWzdn
++xwPloJOA5sqsDIXGXt4+KPb2hjHLpqS4V9Rw1JQErBgB9/0EHqK4u1kwI1/djea
+Ny26esGgjmupq+M+G3vQysKEX8m/KhYZKw8yqG4LrzUKp1uosEXEeE+FBnPW1fwU
+OapJTAKLDAeItz2YsZ+82oTMREKR6gNoAw3yF3dxo/E4sk3IDG4y3w7A8D5dlBBM
+hx2fDqnieS/OffGndbbbIIGH0Sb1MBURNlZlXwXz1hbACc8FMYmn4iyQUJfKlCfU
+Rp1jOR4silFxEGYhSi0Jw7+AJe1prZRyZYp2ZQQ+trGvqQNhwQKCAQEA7CsaDxvP
+L9GI8yFVznAG5SZvIut3/lLA7hd4F0LKJ277hW8YMENLjvmrtEQW1dyQqQXV+CpL
+QErvgRuVj9DyV6qOKDimmpqhT+YZlbxp98N9Ba8RJ4ckw07vPNvglTjAeIZyUbRP
+VX5Onr/OFw66GLzOIhsOnRlqviOKg2wm1kfBF3OBAwVczqh4PJ4gc3rbW9jPN3pn
+eaouV4CdGIooXckV6XtQCGjWFNNzxmuknn1GbLbHslGuDUX89rQsRsk9qZGpi1M1
+Cf2yaQY0d+AjDdPslXKZ92EbDAmo3KGtoxy2hWWytZHbUhtWgguEwxDMkJhJ0yCM
+ZSLQZ2TXlC/iCwKCAQEA5W0xuNTozb2UrwqEaDCkogxgdv5NdQvlvBM7+fD4RpOY
+ItDFcudhuapnpxdbPrdl3F5bgTXZJ4jKMIKePVveugI7wNCQX7aSET3U0Qj47RzA
+vVVHb0K3SZsicpK7K067Ejk1esxrPreaTnUj14jUle20Cq+3iysJnwMFs2DkmE1B
+UK6MiBJ1+MbFuBATw7TWxu8yPyfa3JzUAEsfP8NqqIlXrK26DdGefd8C0hysiq4X
+3matZ3SDck1mCk4LS9PCZZlDqdxLM6/UIoy4cj0qbSPdFMop8zjB8lF/1OABowkT
+e/9L0dpG08G35aEJJ6vKSGnFLbstgly165PlGapeYQKCAQEAwoShuw4BsXYZIYA0
+Z4sX8secRBvDwoKwi6pi7G3DiYU8v2OIfb//zOxRg3GNiWpY8A5xdSyIvJS7/hAV
+ONY1tQUyf2hhuPdhpCh2rED62up14CeYroD+Q6uRGwRTTzTmOp8qK6eirF0TLmf2
+vEESAGwKMEcu2zBjHeayIJsExftlzAYDndRd440ZM3xeaB8p69WAn0Y/UhNchg/V
+1K9+nfiRBrTdb3/BzHd5ZVWlyjCOv94wTuw9uosJ1r0Btu/rzO2/wpSvG+KMfzpw
+HshKtwn1VAaHUBz4JQsTvV2hYba1ktv3vNs81LzVnNkV6YC9rN7x92ZYnLh3BKIn
+edOSjwKCAQAP51zeAixNLsoixCjfjBetgAwj04cNCREY04CB1/lt8wdFypEVYQK+
+OxjKVW0m0NHHz+ap81ClU+8oI7XSbQ7oeAUqXYrUh7Ria5XYE7Ylwat+tG2qQcaw
+3IcryA4fd2qyXbLeW1NH2rRgofAlHcAW0I59eybPB+G32x7HC31tLVXMwPzO5fC1
+mRnVo4+rLlsBGU2zYRDj4B82Ef8NjX9URYkFWFmgYZqKAS6R4Bj52A2hhh6ZIFOI
+VeMv7a8Mx5YfMtuk57dy0spyxqx2htTtEeJecZEs4g9Xu9yPpiOW6KcoHk9kMaxd
+O32C9oHK9TalhGd9vw7tjX2y4eKsv8mhAoIBABEtJSjQ571YUbELvJgnVILWKExj
+nURnbQ8C2j2lLdb20fi8UGJDei9g2b2oCs3SfCaMcRqoOs4uy8ZJzofPelwWY1B7
+SZezn/2d1Q/cXpaXew++krhFvJFVxya6eLD7pvEVQAsGtkp9BTi2CFIhcGo5UQdc
+pym7XjFsW0LCI8mOAleuYe7DEpEU79RdsfxgQpeh1NgFJRpupHT6I2BkVijD/TI4
+kbwz11SuznkUTB4QePIwnEzV+gqDtlny4M1Qvsk8hqcU6JUOu+MOT1qnI6ZGSsdS
+D6RhPukordC9y/cbiV6qqKBVZ39O8ydWvpto/g7G7fSL5SddFuVRnwAV7Vg=
+-----END RSA PRIVATE KEY-----
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index a86cc87..38ff37f 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -315,6 +315,10 @@
     return OK;
 }
 
+ssize_t VendorTagDescriptor::getSectionIndex(uint32_t tag) const {
+    return mTagToSectionMap.valueFor(tag);
+}
+
 void VendorTagDescriptor::dump(int fd, int verbosity, int indentation) const {
 
     size_t size = mTagToNameMap.size();
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index feb04c2..321eb08 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -175,6 +175,12 @@
     mIsShared = isShared;
 }
 
+OutputConfiguration::OutputConfiguration(
+        const std::vector<sp<IGraphicBufferProducer>>& gbps,
+    int rotation, int surfaceSetID,  int surfaceType, int width, int height, bool isShared)
+  : mGbps(gbps), mRotation(rotation), mSurfaceSetID(surfaceSetID), mSurfaceType(surfaceType),
+    mWidth(width), mHeight(height), mIsDeferred(false), mIsShared(isShared) { }
+
 status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const {
 
     if (parcel == nullptr) return BAD_VALUE;
diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp
new file mode 100644
index 0000000..ef6930c
--- /dev/null
+++ b/camera/cameraserver/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+cc_binary {
+    name: "cameraserver",
+
+    srcs: ["main_cameraserver.cpp"],
+
+    shared_libs: [
+        "libcameraservice",
+        "liblog",
+        "libutils",
+        "libui",
+        "libgui",
+        "libbinder",
+        "libhidltransport",
+        "android.hardware.camera.common@1.0",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+    ],
+    compile_multilib: "32",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+
+    init_rc: ["cameraserver.rc"],
+
+    vintf_fragments: [
+        "manifest_android.frameworks.cameraservice.service@2.0.xml",
+    ],
+}
diff --git a/camera/cameraserver/Android.mk b/camera/cameraserver/Android.mk
deleted file mode 100644
index b8c94e6..0000000
--- a/camera/cameraserver/Android.mk
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2015 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)
-
-LOCAL_SRC_FILES:= \
-	main_cameraserver.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	libcameraservice \
-	liblog \
-	libutils \
-	libui \
-	libgui \
-	libbinder \
-	libhidltransport \
-	android.hardware.camera.common@1.0 \
-	android.hardware.camera.provider@2.4 \
-	android.hardware.camera.device@1.0 \
-	android.hardware.camera.device@3.2
-
-LOCAL_MODULE:= cameraserver
-LOCAL_32_BIT_ONLY := true
-
-LOCAL_CFLAGS += -Wall -Wextra -Werror -Wno-unused-parameter
-
-LOCAL_INIT_RC := cameraserver.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/camera/cameraserver/main_cameraserver.cpp b/camera/cameraserver/main_cameraserver.cpp
index 3972436..53b3d84 100644
--- a/camera/cameraserver/main_cameraserver.cpp
+++ b/camera/cameraserver/main_cameraserver.cpp
@@ -26,8 +26,9 @@
 {
     signal(SIGPIPE, SIG_IGN);
 
-    // Set 3 threads for HIDL calls
-    hardware::configureRpcThreadpool(3, /*willjoin*/ false);
+    // Set 5 threads for HIDL calls. Now cameraserver will serve HIDL calls in
+    // addition to consuming them from the Camera HAL as well.
+    hardware::configureRpcThreadpool(5, /*willjoin*/ false);
 
     sp<ProcessState> proc(ProcessState::self());
     sp<IServiceManager> sm = defaultServiceManager();
diff --git a/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.0.xml b/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.0.xml
new file mode 100644
index 0000000..601c717
--- /dev/null
+++ b/camera/cameraserver/manifest_android.frameworks.cameraservice.service@2.0.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+    <hal>
+        <name>android.frameworks.cameraservice.service</name>
+        <transport>hwbinder</transport>
+        <version>2.0</version>
+        <interface>
+            <name>ICameraService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/camera/include/camera/VendorTagDescriptor.h b/camera/include/camera/VendorTagDescriptor.h
index 904fba2..c718c93 100644
--- a/camera/include/camera/VendorTagDescriptor.h
+++ b/camera/include/camera/VendorTagDescriptor.h
@@ -99,6 +99,11 @@
         void dump(int fd, int verbosity, int indentation) const;
 
         /**
+         * Get Section for corresponding tag.
+         */
+        ssize_t getSectionIndex(uint32_t tag) const;
+
+        /**
          * Read values VendorTagDescriptor object from the given parcel.
          *
          * Returns OK on success, or a negative error code.
diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h
index a80f44b..5b117fb 100644
--- a/camera/include/camera/camera2/OutputConfiguration.h
+++ b/camera/include/camera/camera2/OutputConfiguration.h
@@ -67,6 +67,11 @@
     OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation,
             int surfaceSetID = INVALID_SET_ID, bool isShared = false);
 
+    OutputConfiguration(const std::vector<sp<IGraphicBufferProducer>>& gbps,
+                        int rotation, int surfaceSetID = INVALID_SET_ID,
+                        int surfaceType = OutputConfiguration::SURFACE_TYPE_UNKNOWN, int width = 0,
+                        int height = 0, bool isShared = false);
+
     bool operator == (const OutputConfiguration& other) const {
         return ( mRotation == other.mRotation &&
                 mSurfaceSetID == other.mSurfaceSetID &&
diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp
index 7c3a2f0..60c9f85 100644
--- a/camera/ndk/Android.bp
+++ b/camera/ndk/Android.bp
@@ -32,7 +32,7 @@
 }
 
 cc_library_shared {
-    name: "libcamera2",
+    name: "libcamera2ndk",
     srcs: [
         "NdkCameraManager.cpp",
         "NdkCameraMetadata.cpp",
@@ -70,3 +70,81 @@
     ],
     version_script: "libcamera2ndk.map.txt",
 }
+
+cc_library_shared {
+    name: "libcamera2ndk_vendor",
+    vendor_available: true,
+    srcs: [
+        "ndk_vendor/impl/ACameraDevice.cpp",
+        "ndk_vendor/impl/ACameraManager.cpp",
+        "ndk_vendor/impl/utils.cpp",
+        "impl/ACameraMetadata.cpp",
+        "impl/ACameraCaptureSession.cpp",
+        "NdkCameraMetadata.cpp",
+        "NdkCameraCaptureSession.cpp",
+        "NdkCameraManager.cpp",
+        "NdkCameraDevice.cpp",
+        "NdkCaptureRequest.cpp",
+    ],
+
+    export_include_dirs: ["include"],
+    export_shared_lib_headers: [
+        "libcutils",
+    ],
+    local_include_dirs: [
+        ".",
+        "include",
+        "impl",
+    ],
+    cflags: [
+        "-fvisibility=hidden",
+        "-DEXPORT=__attribute__((visibility(\"default\")))",
+        "-D__ANDROID_VNDK__",
+    ],
+
+    shared_libs: [
+        "libhwbinder",
+        "libfmq",
+        "libhidlbase",
+        "libhardware",
+        "libnativewindow",
+        "liblog",
+        "libutils",
+        "libstagefright_foundation",
+        "libcutils",
+        "libcamera_metadata",
+        "libmediandk",
+        "android.frameworks.cameraservice.device@2.0",
+        "android.frameworks.cameraservice.common@2.0",
+        "android.frameworks.cameraservice.service@2.0",
+    ],
+
+    static_libs: [
+        "android.hardware.camera.common@1.0-helper",
+        "libarect",
+    ],
+    product_variables: {
+        pdk: {
+            enabled: false,
+        },
+    },
+}
+
+cc_test {
+    name: "AImageReaderVendorTest",
+    vendor: true,
+    srcs: ["ndk_vendor/tests/AImageReaderVendorTest.cpp"],
+    shared_libs: [
+        "libhwbinder",
+        "libcamera2ndk_vendor",
+        "libmediandk",
+        "libnativewindow",
+        "libutils",
+        "libui",
+        "libcutils",
+        "liblog",
+    ],
+    cflags: [
+        "-D__ANDROID_VNDK__",
+    ],
+}
diff --git a/camera/ndk/Android.mk b/camera/ndk/Android.mk
deleted file mode 100644
index 508f930..0000000
--- a/camera/ndk/Android.mk
+++ /dev/null
@@ -1,60 +0,0 @@
-#
-# Copyright (C) 2015 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.
-#
-
-# TODO(b/118434782): Remove this file and change name of the libcamera2
-# module in the existing Android.bp file to libcamera2ndk.
-LOCAL_PATH:= $(call my-dir)
-
-ifneq ($(TARGET_BUILD_PDK), true)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=                  \
-    NdkCameraManager.cpp           \
-    NdkCameraMetadata.cpp          \
-    NdkCameraDevice.cpp            \
-    NdkCaptureRequest.cpp          \
-    NdkCameraCaptureSession.cpp    \
-    impl/ACameraManager.cpp        \
-    impl/ACameraMetadata.cpp       \
-    impl/ACameraDevice.cpp         \
-    impl/ACameraCaptureSession.cpp
-
-LOCAL_MODULE:= libcamera2ndk
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-LOCAL_CFLAGS += -fvisibility=hidden -D EXPORT='__attribute__ ((visibility ("default")))'
-LOCAL_CFLAGS += -Wall -Wextra -Werror
-
-LOCAL_LDFLAGS += -Wl,--version-script=$(LOCAL_PATH)/libcamera2ndk.map.txt
-
-LOCAL_SHARED_LIBRARIES := \
-    libbinder \
-    liblog \
-    libgui \
-    libutils \
-    libandroid_runtime \
-    libcamera_client \
-    libstagefright_foundation \
-    libcutils \
-    libcamera_metadata \
-    libmediandk
-
-include $(BUILD_SHARED_LIBRARY)
-
-endif
diff --git a/camera/ndk/NdkCameraDevice.cpp b/camera/ndk/NdkCameraDevice.cpp
index 812a312..ef05e0b 100644
--- a/camera/ndk/NdkCameraDevice.cpp
+++ b/camera/ndk/NdkCameraDevice.cpp
@@ -24,7 +24,15 @@
 #include <camera/NdkCameraDevice.h>
 #include "impl/ACameraCaptureSession.h"
 
-using namespace android;
+using namespace android::acam;
+
+bool areWindowTypesEqual(ACameraWindowType *a, ACameraWindowType *b) {
+#ifdef __ANDROID_VNDK__
+    return utils::isWindowNativeHandleEqual(a, b);
+#else
+    return a == b;
+#endif
+}
 
 EXPORT
 camera_status_t ACameraDevice_close(ACameraDevice* device) {
@@ -96,7 +104,7 @@
 
 EXPORT
 camera_status_t ACaptureSessionOutput_create(
-        ANativeWindow* window, /*out*/ACaptureSessionOutput** out) {
+        ACameraWindowType* window, /*out*/ACaptureSessionOutput** out) {
     ATRACE_CALL();
     if (window == nullptr || out == nullptr) {
         ALOGE("%s: Error: bad argument. window %p, out %p",
@@ -109,7 +117,7 @@
 
 EXPORT
 camera_status_t ACaptureSessionSharedOutput_create(
-        ANativeWindow* window, /*out*/ACaptureSessionOutput** out) {
+        ACameraWindowType* window, /*out*/ACaptureSessionOutput** out) {
     ATRACE_CALL();
     if (window == nullptr || out == nullptr) {
         ALOGE("%s: Error: bad argument. window %p, out %p",
@@ -122,7 +130,7 @@
 
 EXPORT
 camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *out,
-        ANativeWindow* window) {
+        ACameraWindowType* window) {
     ATRACE_CALL();
     if ((window == nullptr) || (out == nullptr)) {
         ALOGE("%s: Error: bad argument. window %p, out %p",
@@ -134,7 +142,7 @@
                 __FUNCTION__);
         return ACAMERA_ERROR_INVALID_OPERATION;
     }
-    if (out->mWindow == window) {
+    if (areWindowTypesEqual(out->mWindow, window)) {
         ALOGE("%s: Error trying to add the same window associated with the output configuration",
                 __FUNCTION__);
         return ACAMERA_ERROR_INVALID_PARAMETER;
@@ -147,7 +155,7 @@
 
 EXPORT
 camera_status_t ACaptureSessionSharedOutput_remove(ACaptureSessionOutput *out,
-        ANativeWindow* window) {
+        ACameraWindowType* window) {
     ATRACE_CALL();
     if ((window == nullptr) || (out == nullptr)) {
         ALOGE("%s: Error: bad argument. window %p, out %p",
@@ -159,7 +167,7 @@
                 __FUNCTION__);
         return ACAMERA_ERROR_INVALID_OPERATION;
     }
-    if (out->mWindow == window) {
+    if (areWindowTypesEqual(out->mWindow, window)) {
         ALOGE("%s: Error trying to remove the same window associated with the output configuration",
                 __FUNCTION__);
         return ACAMERA_ERROR_INVALID_PARAMETER;
diff --git a/camera/ndk/NdkCameraManager.cpp b/camera/ndk/NdkCameraManager.cpp
index f14a4c9..8742d9c 100644
--- a/camera/ndk/NdkCameraManager.cpp
+++ b/camera/ndk/NdkCameraManager.cpp
@@ -22,10 +22,15 @@
 #include <utils/Trace.h>
 
 #include <camera/NdkCameraManager.h>
+
+#ifdef __ANDROID_VNDK__
+#include "ndk_vendor/impl/ACameraManager.h"
+#else
 #include "impl/ACameraManager.h"
+#endif
 #include "impl/ACameraMetadata.h"
 
-using namespace android;
+using namespace android::acam;
 
 EXPORT
 ACameraManager* ACameraManager_create() {
diff --git a/camera/ndk/NdkCaptureRequest.cpp b/camera/ndk/NdkCaptureRequest.cpp
index ddb69d7..c64de3e 100644
--- a/camera/ndk/NdkCaptureRequest.cpp
+++ b/camera/ndk/NdkCaptureRequest.cpp
@@ -27,7 +27,7 @@
 
 EXPORT
 camera_status_t ACameraOutputTarget_create(
-        ANativeWindow* window, ACameraOutputTarget** out) {
+        ACameraWindowType* window, ACameraOutputTarget** out) {
     ATRACE_CALL();
     if (window == nullptr) {
         ALOGE("%s: Error: input window is null", __FUNCTION__);
diff --git a/camera/ndk/impl/ACameraCaptureSession.cpp b/camera/ndk/impl/ACameraCaptureSession.cpp
index f60e5fd..fb72bdb 100644
--- a/camera/ndk/impl/ACameraCaptureSession.cpp
+++ b/camera/ndk/impl/ACameraCaptureSession.cpp
@@ -23,7 +23,7 @@
 
 ACameraCaptureSession::~ACameraCaptureSession() {
     ALOGV("~ACameraCaptureSession: %p notify device end of life", this);
-    sp<CameraDevice> dev = getDeviceSp();
+    sp<acam::CameraDevice> dev = getDeviceSp();
     if (dev != nullptr && !dev->isClosed()) {
         dev->lockDeviceForSessionOps();
         {
@@ -48,7 +48,7 @@
         mClosedByApp = true;
     }
 
-    sp<CameraDevice> dev = getDeviceSp();
+    sp<acam::CameraDevice> dev = getDeviceSp();
     if (dev != nullptr) {
         dev->lockDeviceForSessionOps();
     }
@@ -73,7 +73,7 @@
 
 camera_status_t
 ACameraCaptureSession::stopRepeating() {
-    sp<CameraDevice> dev = getDeviceSp();
+    sp<acam::CameraDevice> dev = getDeviceSp();
     if (dev == nullptr) {
         ALOGE("Error: Device associated with session %p has been closed!", this);
         return ACAMERA_ERROR_SESSION_CLOSED;
@@ -91,7 +91,7 @@
 
 camera_status_t
 ACameraCaptureSession::abortCaptures() {
-    sp<CameraDevice> dev = getDeviceSp();
+    sp<acam::CameraDevice> dev = getDeviceSp();
     if (dev == nullptr) {
         ALOGE("Error: Device associated with session %p has been closed!", this);
         return ACAMERA_ERROR_SESSION_CLOSED;
@@ -112,7 +112,7 @@
         /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
         int numRequests, ACaptureRequest** requests,
         /*optional*/int* captureSequenceId) {
-    sp<CameraDevice> dev = getDeviceSp();
+    sp<acam::CameraDevice> dev = getDeviceSp();
     if (dev == nullptr) {
         ALOGE("Error: Device associated with session %p has been closed!", this);
         return ACAMERA_ERROR_SESSION_CLOSED;
@@ -133,7 +133,7 @@
         /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
         int numRequests, ACaptureRequest** requests,
         /*optional*/int* captureSequenceId) {
-    sp<CameraDevice> dev = getDeviceSp();
+    sp<acam::CameraDevice> dev = getDeviceSp();
     if (dev == nullptr) {
         ALOGE("Error: Device associated with session %p has been closed!", this);
         return ACAMERA_ERROR_SESSION_CLOSED;
@@ -149,7 +149,7 @@
 }
 
 camera_status_t ACameraCaptureSession::updateOutputConfiguration(ACaptureSessionOutput *output) {
-    sp<CameraDevice> dev = getDeviceSp();
+    sp<acam::CameraDevice> dev = getDeviceSp();
     if (dev == nullptr) {
         ALOGE("Error: Device associated with session %p has been closed!", this);
         return ACAMERA_ERROR_SESSION_CLOSED;
@@ -168,7 +168,7 @@
 ACameraDevice*
 ACameraCaptureSession::getDevice() {
     Mutex::Autolock _l(mSessionLock);
-    sp<CameraDevice> dev = getDeviceSp();
+    sp<acam::CameraDevice> dev = getDeviceSp();
     if (dev == nullptr) {
         ALOGE("Error: Device associated with session %p has been closed!", this);
         return nullptr;
@@ -182,9 +182,9 @@
     mIsClosed = true;
 }
 
-sp<CameraDevice>
+sp<acam::CameraDevice>
 ACameraCaptureSession::getDeviceSp() {
-    sp<CameraDevice> device = mDevice.promote();
+    sp<acam::CameraDevice> device = mDevice.promote();
     if (device == nullptr || device->isClosed()) {
         ALOGW("Device is closed but session %d is not notified", mId);
         return nullptr;
diff --git a/camera/ndk/impl/ACameraCaptureSession.h b/camera/ndk/impl/ACameraCaptureSession.h
index a2068e7..133c2c8 100644
--- a/camera/ndk/impl/ACameraCaptureSession.h
+++ b/camera/ndk/impl/ACameraCaptureSession.h
@@ -19,12 +19,17 @@
 #include <set>
 #include <hardware/camera3.h>
 #include <camera/NdkCameraDevice.h>
+
+#ifdef __ANDROID_VNDK__
+#include "ndk_vendor/impl/ACameraDevice.h"
+#include "ndk_vendor/impl/ACameraCaptureSessionVendor.h"
+#else
 #include "ACameraDevice.h"
 
 using namespace android;
 
 struct ACaptureSessionOutput {
-    explicit ACaptureSessionOutput(ANativeWindow* window, bool isShared = false) :
+    explicit ACaptureSessionOutput(ACameraWindowType* window, bool isShared = false) :
             mWindow(window), mIsShared(isShared) {};
 
     bool operator == (const ACaptureSessionOutput& other) const {
@@ -40,11 +45,12 @@
         return mWindow > other.mWindow;
     }
 
-    ANativeWindow* mWindow;
-    std::set<ANativeWindow *> mSharedWindows;
+    ACameraWindowType* mWindow;
+    std::set<ACameraWindowType *> mSharedWindows;
     bool           mIsShared;
     int            mRotation = CAMERA3_STREAM_ROTATION_0;
 };
+#endif
 
 struct ACaptureSessionOutputContainer {
     std::set<ACaptureSessionOutput> mOutputs;
@@ -60,7 +66,7 @@
             int id,
             const ACaptureSessionOutputContainer* outputs,
             const ACameraCaptureSession_stateCallbacks* cb,
-            CameraDevice* device) :
+            android::acam::CameraDevice* device) :
             mId(id), mOutput(*outputs), mUserSessionCallback(*cb),
             mDevice(device) {}
 
@@ -97,18 +103,18 @@
     ACameraDevice* getDevice();
 
   private:
-    friend class CameraDevice;
+    friend class android::acam::CameraDevice;
 
     // Close session because app close camera device, camera device got ERROR_DISCONNECTED,
     // or a new session is replacing this session.
     void closeByDevice();
 
-    sp<CameraDevice> getDeviceSp();
+    sp<android::acam::CameraDevice> getDeviceSp();
 
     const int mId;
     const ACaptureSessionOutputContainer mOutput;
     const ACameraCaptureSession_stateCallbacks mUserSessionCallback;
-    const wp<CameraDevice> mDevice;
+    const wp<android::acam::CameraDevice> mDevice;
     bool  mIsClosed = false;
     bool  mClosedByApp = false;
     Mutex mSessionLock;
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index ac3be25..00da54e 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -27,9 +27,9 @@
 #include "ACaptureRequest.h"
 #include "ACameraCaptureSession.h"
 
-using namespace android;
-
 namespace android {
+namespace acam {
+
 // Static member definitions
 const char* CameraDevice::kContextKey        = "Context";
 const char* CameraDevice::kDeviceKey         = "Device";
@@ -1513,5 +1513,5 @@
     return ret;
 }
 
-
+} // namespace acam
 } // namespace android
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 7d64081..8f56d3f 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -41,6 +41,7 @@
 #include "ACameraMetadata.h"
 
 namespace android {
+namespace acam {
 
 // Wrap ACameraCaptureFailure so it can be ref-counted
 struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure {};
@@ -286,6 +287,7 @@
 
 };
 
+} // namespace acam;
 } // namespace android;
 
 /**
@@ -295,7 +297,7 @@
 struct ACameraDevice {
     ACameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
                   sp<ACameraMetadata> chars) :
-            mDevice(new CameraDevice(id, cb, chars, this)) {}
+            mDevice(new android::acam::CameraDevice(id, cb, chars, this)) {}
 
     ~ACameraDevice() {};
 
@@ -331,7 +333,7 @@
     }
 
   private:
-    android::sp<android::CameraDevice> mDevice;
+    android::sp<android::acam::CameraDevice> mDevice;
 };
 
 #endif // _ACAMERA_DEVICE_H
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index ee67677..7d6ecac 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -26,9 +26,10 @@
 #include <stdlib.h>
 #include <camera/VendorTagDescriptor.h>
 
-using namespace android;
+using namespace android::acam;
 
 namespace android {
+namespace acam {
 // Static member definitions
 const char* CameraManagerGlobal::kCameraIdKey   = "CameraId";
 const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp";
@@ -345,6 +346,7 @@
     }
 }
 
+} // namespace acam
 } // namespace android
 
 /**
diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h
index ce65769..55bfa7e 100644
--- a/camera/ndk/impl/ACameraManager.h
+++ b/camera/ndk/impl/ACameraManager.h
@@ -35,6 +35,7 @@
 #include <map>
 
 namespace android {
+namespace acam {
 
 /**
  * Per-process singleton instance of CameraManger. Shared by all ACameraManager
@@ -172,6 +173,7 @@
     ~CameraManagerGlobal();
 };
 
+} // namespace acam;
 } // namespace android;
 
 /**
@@ -180,7 +182,7 @@
  */
 struct ACameraManager {
     ACameraManager() :
-            mGlobalManager(&(android::CameraManagerGlobal::getInstance())) {}
+            mGlobalManager(&(android::acam::CameraManagerGlobal::getInstance())) {}
     ~ACameraManager();
     camera_status_t getCameraIdList(ACameraIdList** cameraIdList);
     static void     deleteCameraIdList(ACameraIdList* cameraIdList);
@@ -196,7 +198,7 @@
         kCameraIdListNotInit = -1
     };
     android::Mutex         mLock;
-    android::sp<android::CameraManagerGlobal> mGlobalManager;
+    android::sp<android::acam::CameraManagerGlobal> mGlobalManager;
 };
 
 #endif //_ACAMERA_MANAGER_H
diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h
index e76b80c..f21dbaf 100644
--- a/camera/ndk/impl/ACameraMetadata.h
+++ b/camera/ndk/impl/ACameraMetadata.h
@@ -22,7 +22,13 @@
 #include <utils/Mutex.h>
 #include <utils/RefBase.h>
 #include <utils/Vector.h>
+
+#ifdef __ANDROID_VNDK__
+#include <CameraMetadata.h>
+using CameraMetadata = android::hardware::camera::common::V1_0::helper::CameraMetadata;
+#else
 #include <camera/CameraMetadata.h>
+#endif
 
 #include <camera/NdkCameraMetadata.h>
 
diff --git a/camera/ndk/impl/ACaptureRequest.h b/camera/ndk/impl/ACaptureRequest.h
index b11dafb..5c82ab7 100644
--- a/camera/ndk/impl/ACaptureRequest.h
+++ b/camera/ndk/impl/ACaptureRequest.h
@@ -21,8 +21,11 @@
 
 using namespace android;
 
+#ifdef __ANDROID_VNDK__
+#include "ndk_vendor/impl/ACaptureRequestVendor.h"
+#else
 struct ACameraOutputTarget {
-    explicit ACameraOutputTarget(ANativeWindow* window) : mWindow(window) {};
+    explicit ACameraOutputTarget(ACameraWindowType* window) : mWindow(window) {};
 
     bool operator == (const ACameraOutputTarget& other) const {
         return mWindow == other.mWindow;
@@ -37,8 +40,9 @@
         return mWindow > other.mWindow;
     }
 
-    ANativeWindow* mWindow;
+    ACameraWindowType* mWindow;
 };
+#endif
 
 struct ACameraOutputTargets {
     std::set<ACameraOutputTarget> mOutputs;
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index 5e0db60..1244582 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -35,10 +35,10 @@
 #include <sys/cdefs.h>
 #include <stdbool.h>
 
-#include <android/native_window.h>
 #include "NdkCameraError.h"
 #include "NdkCameraMetadata.h"
 #include "NdkCaptureRequest.h"
+#include "NdkCameraWindowType.h"
 
 #ifndef _NDK_CAMERA_CAPTURE_SESSION_H
 #define _NDK_CAMERA_CAPTURE_SESSION_H
@@ -246,7 +246,7 @@
  */
 typedef void (*ACameraCaptureSession_captureCallback_bufferLost)(
         void* context, ACameraCaptureSession* session,
-        ACaptureRequest* request, ANativeWindow* window, int64_t frameNumber);
+        ACaptureRequest* request, ACameraWindowType* window, int64_t frameNumber);
 
 typedef struct ACameraCaptureSession_captureCallbacks {
     /// optional application context.
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index 7c13b34..4fe43d5 100644
--- a/camera/ndk/include/camera/NdkCameraDevice.h
+++ b/camera/ndk/include/camera/NdkCameraDevice.h
@@ -34,10 +34,10 @@
  */
 #include <sys/cdefs.h>
 
-#include <android/native_window.h>
 #include "NdkCameraError.h"
 #include "NdkCaptureRequest.h"
 #include "NdkCameraCaptureSession.h"
+#include "NdkCameraWindowType.h"
 
 #ifndef _NDK_CAMERA_DEVICE_H
 #define _NDK_CAMERA_DEVICE_H
@@ -345,7 +345,7 @@
  * @see ACaptureSessionOutputContainer_add
  */
 camera_status_t ACaptureSessionOutput_create(
-        ANativeWindow* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(24);
+        ACameraWindowType* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(24);
 
 /**
  * Free a ACaptureSessionOutput object.
@@ -694,7 +694,7 @@
  * @see ACaptureSessionOutputContainer_add
  */
 camera_status_t ACaptureSessionSharedOutput_create(
-        ANativeWindow* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(28);
+        ACameraWindowType* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(28);
 
 /**
  * Add a native window to shared ACaptureSessionOutput.
@@ -712,7 +712,7 @@
  *             ACaptureSessionOutput.</li></ul>
  */
 camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *output,
-        ANativeWindow *anw) __INTRODUCED_IN(28);
+        ACameraWindowType *anw) __INTRODUCED_IN(28);
 
 /**
  * Remove a native window from shared ACaptureSessionOutput.
@@ -728,7 +728,7 @@
  *             ACaptureSessionOutput.</li></ul>
  */
 camera_status_t ACaptureSessionSharedOutput_remove(ACaptureSessionOutput *output,
-        ANativeWindow* anw) __INTRODUCED_IN(28);
+        ACameraWindowType* anw) __INTRODUCED_IN(28);
 
 /**
  * Create a new camera capture session similar to {@link ACameraDevice_createCaptureSession}. This
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 297d11b..cb474f4 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -3524,6 +3524,8 @@
      * <p>Some devices may choose to provide a second set of calibration
      * information for improved quality, including
      * ACAMERA_SENSOR_REFERENCE_ILLUMINANT2 and its corresponding matrices.</p>
+     * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+     * the camera device has RAW capability.</p>
      *
      * @see ACAMERA_SENSOR_CALIBRATION_TRANSFORM1
      * @see ACAMERA_SENSOR_COLOR_TRANSFORM1
@@ -3553,6 +3555,8 @@
      * <p>If this key is present, then ACAMERA_SENSOR_COLOR_TRANSFORM2,
      * ACAMERA_SENSOR_CALIBRATION_TRANSFORM2, and
      * ACAMERA_SENSOR_FORWARD_MATRIX2 will also be present.</p>
+     * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+     * the camera device has RAW capability.</p>
      *
      * @see ACAMERA_SENSOR_CALIBRATION_TRANSFORM2
      * @see ACAMERA_SENSOR_COLOR_TRANSFORM2
@@ -3580,6 +3584,8 @@
      * colorspace) into this camera device's native sensor color
      * space under the first reference illuminant
      * (ACAMERA_SENSOR_REFERENCE_ILLUMINANT1).</p>
+     * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+     * the camera device has RAW capability.</p>
      *
      * @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
      */
@@ -3607,6 +3613,8 @@
      * (ACAMERA_SENSOR_REFERENCE_ILLUMINANT2).</p>
      * <p>This matrix will only be present if the second reference
      * illuminant is present.</p>
+     * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+     * the camera device has RAW capability.</p>
      *
      * @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT2
      */
@@ -3635,6 +3643,8 @@
      * and the CIE XYZ colorspace when calculating this transform will
      * match the standard white point for the first reference illuminant
      * (i.e. no chromatic adaptation will be applied by this transform).</p>
+     * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+     * the camera device has RAW capability.</p>
      *
      * @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
      */
@@ -3665,6 +3675,8 @@
      * (i.e. no chromatic adaptation will be applied by this transform).</p>
      * <p>This matrix will only be present if the second reference
      * illuminant is present.</p>
+     * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+     * the camera device has RAW capability.</p>
      *
      * @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT2
      */
@@ -3691,6 +3703,8 @@
      * this matrix is chosen so that the standard white point for this reference
      * illuminant in the reference sensor colorspace is mapped to D50 in the
      * CIE XYZ colorspace.</p>
+     * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+     * the camera device has RAW capability.</p>
      *
      * @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
      */
@@ -3719,6 +3733,8 @@
      * CIE XYZ colorspace.</p>
      * <p>This matrix will only be present if the second reference
      * illuminant is present.</p>
+     * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+     * the camera device has RAW capability.</p>
      *
      * @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT2
      */
@@ -3751,6 +3767,7 @@
      * level values. For raw capture in particular, it is recommended to use
      * pixels from ACAMERA_SENSOR_OPTICAL_BLACK_REGIONS to calculate black
      * level values for each frame.</p>
+     * <p>For a MONOCHROME camera device, all of the 2x2 channels must have the same values.</p>
      *
      * @see ACAMERA_SENSOR_DYNAMIC_BLACK_LEVEL
      * @see ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
@@ -3845,6 +3862,8 @@
      * used to interpolate between the provided color transforms when
      * processing raw sensor data.</p>
      * <p>The order of the values is R, G, B; where R is in the lowest index.</p>
+     * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+     * the camera device has RAW capability.</p>
      */
     ACAMERA_SENSOR_NEUTRAL_COLOR_POINT =                        // rational[3]
             ACAMERA_SENSOR_START + 18,
@@ -3875,6 +3894,8 @@
      * that channel.</p>
      * <p>A more detailed description of the noise model can be found in the
      * Adobe DNG specification for the NoiseProfile tag.</p>
+     * <p>For a MONOCHROME camera, there is only one color channel. So the noise model coefficients
+     * will only contain one S and one O.</p>
      *
      * @see ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
      */
@@ -3920,6 +3941,8 @@
      * <li>R &gt; 1.20 will require strong software correction to produce
      * a usuable image (&gt;20% divergence).</li>
      * </ul>
+     * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+     * the camera device has RAW capability.</p>
      */
     ACAMERA_SENSOR_GREEN_SPLIT =                                // float
             ACAMERA_SENSOR_START + 22,
@@ -4072,6 +4095,7 @@
      * layout key (see ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT), i.e. the
      * nth value given corresponds to the black level offset for the nth
      * color channel listed in the CFA.</p>
+     * <p>For a MONOCHROME camera, all of the 2x2 channels must have the same values.</p>
      * <p>This key will be available if ACAMERA_SENSOR_OPTICAL_BLACK_REGIONS is available or the
      * camera device advertises this key via {@link ACAMERA_REQUEST_AVAILABLE_RESULT_KEYS }.</p>
      *
@@ -4174,7 +4198,8 @@
     /**
      * <p>The arrangement of color filters on sensor;
      * represents the colors in the top-left 2x2 section of
-     * the sensor, in reading order.</p>
+     * the sensor, in reading order, for a Bayer camera, or the
+     * light spectrum it captures for MONOCHROME camera.</p>
      *
      * <p>Type: byte (acamera_metadata_enum_android_sensor_info_color_filter_arrangement_t)</p>
      *
@@ -4643,13 +4668,13 @@
      * (x,y) ϵ (0 ... N-1, 0 ... M-1) is the value of the shading map at
      * pixel ( ((W-1)/(N-1)) * x, ((H-1)/(M-1)) * y) for the four color channels.
      * The map is assumed to be bilinearly interpolated between the sample points.</p>
-     * <p>The channel order is [R, Geven, Godd, B], where Geven is the green
-     * channel for the even rows of a Bayer pattern, and Godd is the odd rows.
+     * <p>For a Bayer camera, the channel order is [R, Geven, Godd, B], where Geven is
+     * the green channel for the even rows of a Bayer pattern, and Godd is the odd rows.
      * The shading map is stored in a fully interleaved format, and its size
      * is provided in the camera static metadata by ACAMERA_LENS_INFO_SHADING_MAP_SIZE.</p>
      * <p>The shading map will generally have on the order of 30-40 rows and columns,
      * and will be smaller than 64x64.</p>
-     * <p>As an example, given a very small map defined as:</p>
+     * <p>As an example, given a very small map for a Bayer camera defined as:</p>
      * <pre><code>ACAMERA_LENS_INFO_SHADING_MAP_SIZE = [ 4, 3 ]
      * ACAMERA_STATISTICS_LENS_SHADING_MAP =
      * [ 1.3, 1.2, 1.15, 1.2,  1.2, 1.2, 1.15, 1.2,
@@ -4669,6 +4694,17 @@
      * image of a gray wall (using bicubic interpolation for visual quality)
      * as captured by the sensor gives:</p>
      * <p><img alt="Image of a uniform white wall (inverse shading map)" src="../images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
+     * <p>For a MONOCHROME camera, all of the 2x2 channels must have the same values. An example
+     * shading map for such a camera is defined as:</p>
+     * <pre><code>ACAMERA_LENS_INFO_SHADING_MAP_SIZE = [ 4, 3 ]
+     * ACAMERA_STATISTICS_LENS_SHADING_MAP =
+     * [ 1.3, 1.3, 1.3, 1.3,  1.2, 1.2, 1.2, 1.2,
+     *     1.1, 1.1, 1.1, 1.1,  1.3, 1.3, 1.3, 1.3,
+     *   1.2, 1.2, 1.2, 1.2,  1.1, 1.1, 1.1, 1.1,
+     *     1.0, 1.0, 1.0, 1.0,  1.2, 1.2, 1.2, 1.2,
+     *   1.3, 1.3, 1.3, 1.3,   1.2, 1.2, 1.2, 1.2,
+     *     1.2, 1.2, 1.2, 1.2,  1.3, 1.3, 1.3, 1.3 ]
+     * </code></pre>
      * <p>Note that the RAW image data might be subject to lens shading
      * correction not reported on this map. Query
      * ACAMERA_SENSOR_INFO_LENS_SHADING_APPLIED to see if RAW image data has subject
@@ -5012,8 +5048,8 @@
      * of points can be less than max (that is, the request doesn't have to
      * always provide a curve with number of points equivalent to
      * ACAMERA_TONEMAP_MAX_CURVE_POINTS).</p>
-     * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
-     * are ignored.</p>
+     * <p>For devices with MONOCHROME capability, all three channels must have the same set of
+     * control points.</p>
      * <p>A few examples, and their corresponding graphical mappings; these
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
@@ -7373,11 +7409,15 @@
 
     /**
      * <p>The camera device is a monochrome camera that doesn't contain a color filter array,
-     * and the pixel values on U and V planes are all 128.</p>
+     * and for YUV_420_888 stream, the pixel values on U and V planes are all 128.</p>
      * <p>A MONOCHROME camera must support the guaranteed stream combinations required for
      * its device level and capabilities. Additionally, if the monochrome camera device
      * supports Y8 format, all mandatory stream combination requirements related to {@link AIMAGE_FORMAT_YUV_420_888 YUV_420_888} apply
-     * to {@link AIMAGE_FORMAT_Y8 Y8} as well.</p>
+     * to {@link AIMAGE_FORMAT_Y8 Y8} as well. There are no
+     * mandatory stream combination requirements with regard to
+     * {@link AIMAGE_FORMAT_Y8 Y8} for Bayer camera devices.</p>
+     * <p>Starting from Android Q, the SENSOR_INFO_COLOR_FILTER_ARRANGEMENT of a MONOCHROME
+     * camera will be either MONO or NIR.</p>
      */
     ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME                = 12,
 
@@ -7643,6 +7683,21 @@
      */
     ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB                 = 4,
 
+    /**
+     * <p>Sensor doesn't have any Bayer color filter.
+     * Such sensor captures visible light in monochrome. The exact weighting and
+     * wavelengths captured is not specified, but generally only includes the visible
+     * frequencies. This value implies a MONOCHROME camera.</p>
+     */
+    ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO                = 5,
+
+    /**
+     * <p>Sensor has a near infrared filter capturing light with wavelength between
+     * roughly 750nm and 1400nm, and the same filter covers the whole sensor array. This
+     * value implies a MONOCHROME camera.</p>
+     */
+    ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR                 = 6,
+
 } acamera_metadata_enum_android_sensor_info_color_filter_arrangement_t;
 
 // ACAMERA_SENSOR_INFO_TIMESTAMP_SOURCE
diff --git a/camera/ndk/include/camera/NdkCameraWindowType.h b/camera/ndk/include/camera/NdkCameraWindowType.h
new file mode 100644
index 0000000..99f67e9
--- /dev/null
+++ b/camera/ndk/include/camera/NdkCameraWindowType.h
@@ -0,0 +1,53 @@
+/*
+ * 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 _NDK_CAMERA_WINDOW_TYPE_H
+#define _NDK_CAMERA_WINDOW_TYPE_H
+
+/**
+ * @addtogroup Camera
+ * @{
+ */
+
+/**
+ * @file NdkCameraWindowType.h
+ */
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+/**
+ * This file defines the window type used by NDK and the VNDK variants of the
+ * camera2 NDK. This enables us to share the api definition headers and avoid
+ * code duplication (since the VNDK variant doesn't use ANativeWindow unlike the
+ * NDK variant).
+ */
+#ifdef __ANDROID_VNDK__
+#include <cutils/native_handle.h>
+typedef native_handle_t ACameraWindowType;
+#else
+#include <android/native_window.h>
+typedef ANativeWindow ACameraWindowType;
+#endif
+
+#endif //_NDK_CAMERA_WINDOW_TYPE_H
diff --git a/camera/ndk/include/camera/NdkCaptureRequest.h b/camera/ndk/include/camera/NdkCaptureRequest.h
index 5340e76..136989a 100644
--- a/camera/ndk/include/camera/NdkCaptureRequest.h
+++ b/camera/ndk/include/camera/NdkCaptureRequest.h
@@ -35,9 +35,9 @@
 
 #include <sys/cdefs.h>
 
-#include <android/native_window.h>
 #include "NdkCameraError.h"
 #include "NdkCameraMetadata.h"
+#include "NdkCameraWindowType.h"
 
 #ifndef _NDK_CAPTURE_REQUEST_H
 #define _NDK_CAPTURE_REQUEST_H
@@ -101,7 +101,7 @@
  *
  * @see ACaptureRequest_addTarget
  */
-camera_status_t ACameraOutputTarget_create(ANativeWindow* window,
+camera_status_t ACameraOutputTarget_create(ACameraWindowType* window,
         ACameraOutputTarget** output) __INTRODUCED_IN(24);
 
 /**
diff --git a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h
new file mode 100644
index 0000000..8d9e90c
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h
@@ -0,0 +1,45 @@
+/*
+ * 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 "utils.h"
+
+struct ACaptureSessionOutput {
+    explicit ACaptureSessionOutput(native_handle_t* window, bool isShared = false) :
+            mWindow(window), mIsShared(isShared) {};
+
+    bool operator == (const ACaptureSessionOutput& other) const {
+        return (mWindow == other.mWindow);
+    }
+
+    bool operator != (const ACaptureSessionOutput& other) const {
+        return mWindow != other.mWindow;
+    }
+
+    bool operator < (const ACaptureSessionOutput& other) const {
+        return mWindow < other.mWindow;
+    }
+
+    bool operator > (const ACaptureSessionOutput& other) const {
+        return mWindow > other.mWindow;
+    }
+
+    android::acam::utils::native_handle_ptr_wrapper mWindow;
+    std::set<android::acam::utils::native_handle_ptr_wrapper> mSharedWindows;
+    bool           mIsShared;
+    int            mRotation = CAMERA3_STREAM_ROTATION_0;
+};
+
+
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
new file mode 100644
index 0000000..b86f854
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
@@ -0,0 +1,1594 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ACameraDeviceVendor"
+
+#include <vector>
+#include <inttypes.h>
+#include <android/frameworks/cameraservice/service/2.0/ICameraService.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <CameraMetadata.h>
+
+#include "ndk_vendor/impl/ACameraDevice.h"
+#include "ACameraCaptureSession.h"
+#include "ACameraMetadata.h"
+#include "ACaptureRequest.h"
+#include "utils.h"
+
+using namespace android;
+
+namespace android {
+namespace acam {
+
+using HCameraMetadata = frameworks::cameraservice::device::V2_0::CameraMetadata;
+using OutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration;
+using hardware::Void;
+
+// Static member definitions
+const char* CameraDevice::kContextKey        = "Context";
+const char* CameraDevice::kDeviceKey         = "Device";
+const char* CameraDevice::kErrorCodeKey      = "ErrorCode";
+const char* CameraDevice::kCallbackFpKey     = "Callback";
+const char* CameraDevice::kSessionSpKey      = "SessionSp";
+const char* CameraDevice::kCaptureRequestKey = "CaptureRequest";
+const char* CameraDevice::kTimeStampKey      = "TimeStamp";
+const char* CameraDevice::kCaptureResultKey  = "CaptureResult";
+const char* CameraDevice::kCaptureFailureKey = "CaptureFailure";
+const char* CameraDevice::kSequenceIdKey     = "SequenceId";
+const char* CameraDevice::kFrameNumberKey    = "FrameNumber";
+const char* CameraDevice::kAnwKey            = "Anw";
+
+/**
+ * CameraDevice Implementation
+ */
+CameraDevice::CameraDevice(
+        const char* id,
+        ACameraDevice_StateCallbacks* cb,
+        sp<ACameraMetadata> chars,
+        ACameraDevice* wrapper) :
+        mCameraId(id),
+        mAppCallbacks(*cb),
+        mChars(std::move(chars)),
+        mServiceCallback(new ServiceCallback(this)),
+        mWrapper(wrapper),
+        mInError(false),
+        mError(ACAMERA_OK),
+        mIdle(true),
+        mCurrentSession(nullptr) {
+    mClosing = false;
+    // Setup looper thread to perfrom device callbacks to app
+    mCbLooper = new ALooper;
+    mCbLooper->setName("C2N-dev-looper");
+    status_t err = mCbLooper->start(
+            /*runOnCallingThread*/false,
+            /*canCallJava*/       true,
+            PRIORITY_DEFAULT);
+    if (err != OK) {
+        ALOGE("%s: Unable to start camera device callback looper: %s (%d)",
+                __FUNCTION__, strerror(-err), err);
+        setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+    }
+    mHandler = new CallbackHandler();
+    mCbLooper->registerHandler(mHandler);
+
+    const CameraMetadata& metadata = mChars->getInternalData();
+    camera_metadata_ro_entry entry = metadata.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
+    if (entry.count != 1) {
+        ALOGW("%s: bad count %zu for partial result count", __FUNCTION__, entry.count);
+        mPartialResultCount = 1;
+    } else {
+        mPartialResultCount = entry.data.i32[0];
+    }
+
+    entry = metadata.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
+    if (entry.count != 2) {
+        ALOGW("%s: bad count %zu for shading map size", __FUNCTION__, entry.count);
+        mShadingMapSize[0] = 0;
+        mShadingMapSize[1] = 0;
+    } else {
+        mShadingMapSize[0] = entry.data.i32[0];
+        mShadingMapSize[1] = entry.data.i32[1];
+    }
+}
+
+// Device close implementaiton
+CameraDevice::~CameraDevice() {
+    sp<ACameraCaptureSession> session = mCurrentSession.promote();
+    {
+        Mutex::Autolock _l(mDeviceLock);
+        if (!isClosed()) {
+            disconnectLocked(session);
+        }
+        mCurrentSession = nullptr;
+        if (mCbLooper != nullptr) {
+            mCbLooper->unregisterHandler(mHandler->id());
+            mCbLooper->stop();
+        }
+    }
+    mCbLooper.clear();
+    mHandler.clear();
+}
+
+void
+CameraDevice::postSessionMsgAndCleanup(sp<AMessage>& msg) {
+    msg->post();
+    msg.clear();
+    sp<AMessage> cleanupMsg = new AMessage(kWhatCleanUpSessions, mHandler);
+    cleanupMsg->post();
+}
+
+// TODO: cached created request?
+camera_status_t
+CameraDevice::createCaptureRequest(
+        ACameraDevice_request_template templateId,
+        ACaptureRequest** request) const {
+    Mutex::Autolock _l(mDeviceLock);
+    camera_status_t ret = checkCameraClosedOrErrorLocked();
+    if (ret != ACAMERA_OK) {
+        return ret;
+    }
+    if (mRemote == nullptr) {
+        return ACAMERA_ERROR_CAMERA_DISCONNECTED;
+    }
+    CameraMetadata rawRequest;
+    Status status = Status::NO_ERROR;
+    auto remoteRet = mRemote->createDefaultRequest(
+        utils::convertToHidl(templateId),
+        [&status, &rawRequest](auto s, const hidl_vec<uint8_t> &metadata) {
+            status = s;
+            if (status == Status::NO_ERROR && utils::convertFromHidlCloned(metadata, &rawRequest)) {
+            } else {
+                ALOGE("%s: Couldn't create default request", __FUNCTION__);
+            }
+        });
+    if (!remoteRet.isOk()) {
+        ALOGE("%s: Transaction error while trying to create default request %s", __FUNCTION__,
+              remoteRet.description().c_str());
+        return ACAMERA_ERROR_UNKNOWN;
+    }
+    if (status != Status::NO_ERROR) {
+        return utils::convertFromHidl(status);
+    }
+    ACaptureRequest* outReq = new ACaptureRequest();
+    outReq->settings = new ACameraMetadata(rawRequest.release(), ACameraMetadata::ACM_REQUEST);
+    outReq->targets  = new ACameraOutputTargets();
+    *request = outReq;
+    return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::createCaptureSession(
+        const ACaptureSessionOutputContainer*       outputs,
+        const ACaptureRequest* sessionParameters,
+        const ACameraCaptureSession_stateCallbacks* callbacks,
+        /*out*/ACameraCaptureSession** session) {
+    sp<ACameraCaptureSession> currentSession = mCurrentSession.promote();
+    Mutex::Autolock _l(mDeviceLock);
+    camera_status_t ret = checkCameraClosedOrErrorLocked();
+    if (ret != ACAMERA_OK) {
+        return ret;
+    }
+
+    if (currentSession != nullptr) {
+        currentSession->closeByDevice();
+        stopRepeatingLocked();
+    }
+
+    // Create new session
+    ret = configureStreamsLocked(outputs, sessionParameters);
+    if (ret != ACAMERA_OK) {
+        ALOGE("Fail to create new session. cannot configure streams");
+        return ret;
+    }
+
+    ACameraCaptureSession* newSession = new ACameraCaptureSession(
+            mNextSessionId++, outputs, callbacks, this);
+
+    // set new session as current session
+    newSession->incStrong((void *) ACameraDevice_createCaptureSession);
+    mCurrentSession = newSession;
+    mFlushing = false;
+    *session = newSession;
+    return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::captureLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    return submitRequestsLocked(
+            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false);
+}
+
+camera_status_t
+CameraDevice::setRepeatingRequestsLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    return submitRequestsLocked(
+            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true);
+}
+
+void addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest,
+                                sp<CaptureRequest> &req) {
+    CameraMetadata metadataCopy = aCaptureRequest->settings->getInternalData();
+    const camera_metadata_t *camera_metadata = metadataCopy.getAndLock();
+    HCameraMetadata hCameraMetadata;
+    utils::convertToHidl(camera_metadata, &hCameraMetadata);
+    metadataCopy.unlock(camera_metadata);
+    req->mPhysicalCameraSettings.resize(1);
+    req->mPhysicalCameraSettings[0].settings.metadata(std::move(hCameraMetadata));
+}
+
+camera_status_t
+CameraDevice::submitRequestsLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId,
+        bool isRepeating) {
+    camera_status_t ret = checkCameraClosedOrErrorLocked();
+    if (ret != ACAMERA_OK) {
+        ALOGE("Camera %s submit capture request failed! ret %d", getId(), ret);
+        return ret;
+    }
+
+    // Form two vectors of capture request, one for internal tracking
+    std::vector<frameworks::cameraservice::device::V2_0::CaptureRequest> requestList;
+    Vector<sp<CaptureRequest>> requestsV;
+    requestsV.setCapacity(numRequests);
+    for (int i = 0; i < numRequests; i++) {
+        sp<CaptureRequest> req;
+        ret = allocateCaptureRequest(requests[i], req);
+        // We need to call this method since after submitRequestList is called,
+        // the request metadata queue might have removed the capture request
+        // metadata. Therefore we simply add the metadata to its wrapper class,
+        // so that it can be retrived later.
+        addRequestSettingsMetadata(requests[i], req);
+        if (ret != ACAMERA_OK) {
+            ALOGE("Convert capture request to internal format failure! ret %d", ret);
+            return ret;
+        }
+        if (req->mCaptureRequest.streamAndWindowIds.size() == 0) {
+            ALOGE("Capture request without output target cannot be submitted!");
+            return ACAMERA_ERROR_INVALID_PARAMETER;
+        }
+        requestList.push_back(utils::convertToHidl(req.get()));
+        requestsV.push_back(req);
+    }
+    if (isRepeating) {
+        ret = stopRepeatingLocked();
+        if (ret != ACAMERA_OK) {
+            ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret);
+            return ret;
+        }
+    }
+
+    SubmitInfo info;
+    Status status;
+    auto remoteRet = mRemote->submitRequestList(requestList, isRepeating,
+                                                [&status, &info](auto s, auto &submitInfo) {
+                                                    status = s;
+                                                    info = submitInfo;
+                                                });
+    if (!remoteRet.isOk()) {
+        ALOGE("%s: Transaction error for submitRequestList call: %s", __FUNCTION__,
+              remoteRet.description().c_str());
+    }
+    if (status != Status::NO_ERROR) {
+        return utils::convertFromHidl(status);
+    }
+    int32_t sequenceId = info.requestId;
+    int64_t lastFrameNumber = info.lastFrameNumber;
+    if (sequenceId < 0) {
+        ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId);
+        return ACAMERA_ERROR_UNKNOWN;
+    }
+
+    CallbackHolder cbHolder(session, requestsV, isRepeating, cbs);
+    mSequenceCallbackMap.insert(std::make_pair(sequenceId, cbHolder));
+
+    if (isRepeating) {
+        // stopRepeating above should have cleanup repeating sequence id
+        if (mRepeatingSequenceId != REQUEST_ID_NONE) {
+            setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+            return ACAMERA_ERROR_CAMERA_DEVICE;
+        }
+        mRepeatingSequenceId = sequenceId;
+    } else {
+        mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber));
+    }
+
+    if (mIdle) {
+        sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
+        msg->setPointer(kContextKey, session->mUserSessionCallback.context);
+        msg->setObject(kSessionSpKey, session);
+        msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive);
+        postSessionMsgAndCleanup(msg);
+    }
+    mIdle = false;
+    mBusySession = session;
+
+    if (captureSequenceId) {
+        *captureSequenceId = sequenceId;
+    }
+    return ACAMERA_OK;
+}
+
+camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOutput *output) {
+    camera_status_t ret = checkCameraClosedOrErrorLocked();
+    if (ret != ACAMERA_OK) {
+        return ret;
+    }
+
+    if (output == nullptr) {
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+
+    if (!output->mIsShared) {
+        ALOGE("Error output configuration is not shared");
+        return ACAMERA_ERROR_INVALID_OPERATION;
+    }
+
+    int32_t streamId = -1;
+    for (auto& kvPair : mConfiguredOutputs) {
+        if (utils::isWindowNativeHandleEqual(kvPair.second.first, output->mWindow)) {
+            streamId = kvPair.first;
+            break;
+        }
+    }
+    if (streamId < 0) {
+        ALOGE("Error: Invalid output configuration");
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+
+    OutputConfigurationWrapper outConfigW;
+    OutputConfiguration &outConfig = outConfigW.mOutputConfiguration;
+    outConfig.rotation = utils::convertToHidl(output->mRotation);
+    outConfig.windowGroupId = -1; // ndk doesn't support inter OutputConfiguration buffer sharing.
+    outConfig.windowHandles.resize(output->mSharedWindows.size() + 1);
+    outConfig.windowHandles[0] = output->mWindow;
+    int i = 1;
+    for (auto& anw : output->mSharedWindows) {
+        outConfig.windowHandles[i++] = anw;
+    }
+
+    auto remoteRet = mRemote->updateOutputConfiguration(streamId, outConfig);
+    if (!remoteRet.isOk()) {
+        ALOGE("%s: Transaction error in updating OutputConfiguration: %s", __FUNCTION__,
+              remoteRet.description().c_str());
+        return ACAMERA_ERROR_UNKNOWN;
+    }
+
+    switch (remoteRet) {
+            case Status::NO_ERROR:
+                break;
+            case Status::INVALID_OPERATION:
+                ALOGE("Camera device %s invalid operation", getId());
+                return ACAMERA_ERROR_INVALID_OPERATION;
+            case Status::ALREADY_EXISTS:
+                ALOGE("Camera device %s output surface already exists", getId());
+                return ACAMERA_ERROR_INVALID_PARAMETER;
+            case Status::ILLEGAL_ARGUMENT:
+                ALOGE("Camera device %s invalid input argument", getId());
+                return ACAMERA_ERROR_INVALID_PARAMETER;
+            default:
+                ALOGE("Camera device %s failed to add shared output", getId());
+                return ACAMERA_ERROR_UNKNOWN;
+    }
+
+    mConfiguredOutputs[streamId] = std::make_pair(output->mWindow, outConfigW);
+
+    return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::allocateCaptureRequest(
+        const ACaptureRequest* request, /*out*/sp<CaptureRequest> &outReq) {
+    sp<CaptureRequest> req(new CaptureRequest());
+    req->mCaptureRequest.physicalCameraSettings.resize(1);
+    req->mCaptureRequest.physicalCameraSettings[0].id = mCameraId;
+    // TODO: Do we really need to copy the metadata here ?
+    CameraMetadata metadataCopy = request->settings->getInternalData();
+    const camera_metadata_t *cameraMetadata = metadataCopy.getAndLock();
+    HCameraMetadata hCameraMetadata;
+    utils::convertToHidl(cameraMetadata, &hCameraMetadata);
+    metadataCopy.unlock(cameraMetadata);
+    if (request->settings != nullptr) {
+        if (hCameraMetadata.data() != nullptr &&
+            mCaptureRequestMetadataQueue != nullptr &&
+            mCaptureRequestMetadataQueue->write(
+                reinterpret_cast<const uint8_t *>(hCameraMetadata.data()),
+                hCameraMetadata.size())) {
+            // The metadata field of the union would've been destructued, so no need
+            // to re-size it.
+            req->mCaptureRequest.physicalCameraSettings[0].settings.fmqMetadataSize(
+                hCameraMetadata.size());
+        } else {
+            ALOGE("Fmq write capture result failed, falling back to hwbinder");
+            req->mCaptureRequest.physicalCameraSettings[0].settings.metadata(
+                std::move(hCameraMetadata));
+        }
+    }
+
+    std::vector<int32_t> requestStreamIdxList;
+    std::vector<int32_t> requestSurfaceIdxList;
+    for (auto outputTarget : request->targets->mOutputs) {
+        native_handle_t* anw = outputTarget.mWindow;
+        bool found = false;
+        req->mSurfaceList.push_back(anw);
+        // lookup stream/surface ID
+        for (const auto& kvPair : mConfiguredOutputs) {
+            int streamId = kvPair.first;
+            const OutputConfigurationWrapper& outConfig = kvPair.second.second;
+            const auto& windowHandles = outConfig.mOutputConfiguration.windowHandles;
+            for (int surfaceId = 0; surfaceId < (int) windowHandles.size(); surfaceId++) {
+                // If two native handles are equivalent, so are their surfaces.
+                if (utils::isWindowNativeHandleEqual(windowHandles[surfaceId].getNativeHandle(),
+                                                      anw)) {
+                    found = true;
+                    requestStreamIdxList.push_back(streamId);
+                    requestSurfaceIdxList.push_back(surfaceId);
+                    break;
+                }
+            }
+            if (found) {
+                break;
+            }
+        }
+        if (!found) {
+            ALOGE("Unconfigured output target %p in capture request!", anw);
+            return ACAMERA_ERROR_INVALID_PARAMETER;
+        }
+    }
+    req->mCaptureRequest.streamAndWindowIds.resize(requestStreamIdxList.size());
+    for (int i = 0; i < requestStreamIdxList.size(); i++) {
+        req->mCaptureRequest.streamAndWindowIds[i].streamId = requestStreamIdxList[i];
+        req->mCaptureRequest.streamAndWindowIds[i].windowId = requestSurfaceIdxList[i];
+    }
+    outReq = req;
+    return ACAMERA_OK;
+}
+
+ACaptureRequest*
+CameraDevice::allocateACaptureRequest(sp<CaptureRequest>& req) {
+    ACaptureRequest* pRequest = new ACaptureRequest();
+    CameraMetadata clone;
+    utils::convertFromHidlCloned(req->mPhysicalCameraSettings[0].settings.metadata(), &clone);
+    pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST);
+    pRequest->targets  = new ACameraOutputTargets();
+    for (size_t i = 0; i < req->mSurfaceList.size(); i++) {
+        native_handle_t* anw = req->mSurfaceList[i];
+        ACameraOutputTarget outputTarget(anw);
+        pRequest->targets->mOutputs.insert(outputTarget);
+    }
+    return pRequest;
+}
+
+void
+CameraDevice::freeACaptureRequest(ACaptureRequest* req) {
+    if (req == nullptr) {
+        return;
+    }
+    req->settings.clear();
+    delete req->targets;
+    delete req;
+}
+
+void
+CameraDevice::notifySessionEndOfLifeLocked(ACameraCaptureSession* session) {
+    if (isClosed()) {
+        // Device is closing already. do nothing
+        return;
+    }
+
+    if (mCurrentSession != session) {
+        // Session has been replaced by other seesion or device is closed
+        return;
+    }
+    mCurrentSession = nullptr;
+
+    // Should not happen
+    if (!session->mIsClosed) {
+        ALOGE("Error: unclosed session %p reaches end of life!", session);
+        setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+        return;
+    }
+
+    // No new session, unconfigure now
+    camera_status_t ret = configureStreamsLocked(nullptr, nullptr);
+    if (ret != ACAMERA_OK) {
+        ALOGE("Unconfigure stream failed. Device might still be configured! ret %d", ret);
+    }
+}
+
+void
+CameraDevice::disconnectLocked(sp<ACameraCaptureSession>& session) {
+    if (mClosing.exchange(true)) {
+        // Already closing, just return
+        ALOGW("Camera device %s is already closing.", getId());
+        return;
+    }
+
+    if (mRemote != nullptr) {
+        auto ret = mRemote->disconnect();
+        if (!ret.isOk()) {
+            ALOGE("%s: Transaction error while disconnecting device %s", __FUNCTION__,
+                  ret.description().c_str());
+        }
+    }
+    mRemote = nullptr;
+
+    if (session != nullptr) {
+        session->closeByDevice();
+    }
+}
+
+camera_status_t
+CameraDevice::stopRepeatingLocked() {
+    camera_status_t ret = checkCameraClosedOrErrorLocked();
+    if (ret != ACAMERA_OK) {
+        ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret);
+        return ret;
+    }
+    if (mRepeatingSequenceId != REQUEST_ID_NONE) {
+        int repeatingSequenceId = mRepeatingSequenceId;
+        mRepeatingSequenceId = REQUEST_ID_NONE;
+
+        int64_t lastFrameNumber;
+        Status status = Status::NO_ERROR;
+        auto remoteRet = mRemote->cancelRepeatingRequest(
+                [&status, &lastFrameNumber](Status s, auto frameNumber) {
+                    status = s;
+                    lastFrameNumber = frameNumber;
+                });
+        if (!remoteRet.isOk() || status != Status::NO_ERROR) {
+            ALOGE("%s: Unable to cancel active repeating request", __FUNCTION__);
+            return utils::convertFromHidl(status);
+        }
+        checkRepeatingSequenceCompleteLocked(repeatingSequenceId, lastFrameNumber);
+    }
+    return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::flushLocked(ACameraCaptureSession* session) {
+    camera_status_t ret = checkCameraClosedOrErrorLocked();
+    if (ret != ACAMERA_OK) {
+        ALOGE("Camera %s abort captures failed! ret %d", getId(), ret);
+        return ret;
+    }
+
+    // This should never happen because creating a new session will close
+    // previous one and thus reject any API call from previous session.
+    // But still good to check here in case something unexpected happen.
+    if (mCurrentSession != session) {
+        ALOGE("Camera %s session %p is not current active session!", getId(), session);
+        return ACAMERA_ERROR_INVALID_OPERATION;
+    }
+
+    if (mFlushing) {
+        ALOGW("Camera %s is already aborting captures", getId());
+        return ACAMERA_OK;
+    }
+
+    mFlushing = true;
+
+    // Send onActive callback to guarantee there is always active->ready transition
+    sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
+    msg->setPointer(kContextKey, session->mUserSessionCallback.context);
+    msg->setObject(kSessionSpKey, session);
+    msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onActive);
+    postSessionMsgAndCleanup(msg);
+
+    // If device is already idling, send callback and exit early
+    if (mIdle) {
+        sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
+        msg->setPointer(kContextKey, session->mUserSessionCallback.context);
+        msg->setObject(kSessionSpKey, session);
+        msg->setPointer(kCallbackFpKey, (void*) session->mUserSessionCallback.onReady);
+        postSessionMsgAndCleanup(msg);
+        mFlushing = false;
+        return ACAMERA_OK;
+    }
+
+    int64_t lastFrameNumber;
+    Status status;
+    auto remoteRet = mRemote->flush([&status, &lastFrameNumber](auto s, auto frameNumber) {
+                                        status = s;
+                                        lastFrameNumber = frameNumber;
+                                    });
+    if (!remoteRet.isOk() || status != Status::NO_ERROR) {
+        ALOGE("%s: Abort captures failed", __FUNCTION__);
+        return utils::convertFromHidl(status);
+    }
+    if (mRepeatingSequenceId != REQUEST_ID_NONE) {
+        checkRepeatingSequenceCompleteLocked(mRepeatingSequenceId, lastFrameNumber);
+    }
+    return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::waitUntilIdleLocked() {
+    camera_status_t ret = checkCameraClosedOrErrorLocked();
+    if (ret != ACAMERA_OK) {
+        ALOGE("Wait until camera %s idle failed! ret %d", getId(), ret);
+        return ret;
+    }
+
+    if (mRepeatingSequenceId != REQUEST_ID_NONE) {
+        ALOGE("Camera device %s won't go to idle when there is repeating request!", getId());
+        return ACAMERA_ERROR_INVALID_OPERATION;
+    }
+
+    auto remoteRet = mRemote->waitUntilIdle();
+    if (!remoteRet.isOk()) {
+        ALOGE("%s: Transaction waitUntilIdle failed", __FUNCTION__);
+        return utils::convertFromHidl(remoteRet);
+    }
+    return ACAMERA_OK;
+}
+
+camera_status_t
+CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outputs,
+        const ACaptureRequest* sessionParameters) {
+    ACaptureSessionOutputContainer emptyOutput;
+    if (outputs == nullptr) {
+        outputs = &emptyOutput;
+    }
+
+    camera_status_t ret = checkCameraClosedOrErrorLocked();
+    if (ret != ACAMERA_OK) {
+        return ret;
+    }
+
+    std::set<std::pair<native_handle_ptr_wrapper, OutputConfigurationWrapper>> outputSet;
+    for (auto outConfig : outputs->mOutputs) {
+        native_handle_t* anw = outConfig.mWindow;
+        OutputConfigurationWrapper outConfigInsertW;
+        OutputConfiguration &outConfigInsert = outConfigInsertW.mOutputConfiguration;
+        outConfigInsert.rotation = utils::convertToHidl(outConfig.mRotation);
+        outConfigInsert.windowGroupId = -1;
+        outConfigInsert.windowHandles.resize(outConfig.mSharedWindows.size() + 1);
+        outConfigInsert.windowHandles[0] = anw;
+        native_handle_ptr_wrapper wrap(anw);
+        outputSet.insert(std::make_pair(anw, outConfigInsertW));
+    }
+    std::set<std::pair<native_handle_ptr_wrapper, OutputConfigurationWrapper>> addSet = outputSet;
+    std::vector<int32_t> deleteList;
+
+    // Determine which streams need to be created, which to be deleted
+    for (auto& kvPair : mConfiguredOutputs) {
+        int32_t streamId = kvPair.first;
+        auto& outputPair = kvPair.second;
+        if (outputSet.count(outputPair)) {
+            deleteList.push_back(streamId); // Need to delete a no longer needed stream
+        } else {
+            addSet.erase(outputPair);        // No need to add already existing stream
+        }
+    }
+
+    ret = stopRepeatingLocked();
+    if (ret != ACAMERA_OK) {
+        ALOGE("Camera device %s stop repeating failed, ret %d", getId(), ret);
+        return ret;
+    }
+
+    ret = waitUntilIdleLocked();
+    if (ret != ACAMERA_OK) {
+        ALOGE("Camera device %s wait until idle failed, ret %d", getId(), ret);
+        return ret;
+    }
+
+    // Send onReady to previous session
+    // CurrentSession will be updated after configureStreamLocked, so here
+    // mCurrentSession is the session to be replaced by a new session
+    if (!mIdle && mCurrentSession != nullptr) {
+        if (mBusySession != mCurrentSession) {
+            ALOGE("Current session != busy session");
+            setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+            return ACAMERA_ERROR_CAMERA_DEVICE;
+        }
+        sp<AMessage> msg = new AMessage(kWhatSessionStateCb, mHandler);
+        msg->setPointer(kContextKey, mBusySession->mUserSessionCallback.context);
+        msg->setObject(kSessionSpKey, mBusySession);
+        msg->setPointer(kCallbackFpKey, (void*) mBusySession->mUserSessionCallback.onReady);
+        mBusySession.clear();
+        postSessionMsgAndCleanup(msg);
+    }
+    mIdle = true;
+
+    auto remoteRet = mRemote->beginConfigure();
+    if (!remoteRet.isOk()|| remoteRet != Status::NO_ERROR) {
+        ALOGE("Camera device %s begin configure failed", getId());
+        return utils::convertFromHidl(remoteRet);
+    }
+
+    // delete to-be-deleted streams
+    for (auto streamId : deleteList) {
+        remoteRet = mRemote->deleteStream(streamId);
+        if (!remoteRet.isOk() || remoteRet != Status::NO_ERROR) {
+            ALOGE("Camera device %s failed to remove stream %d", getId(), streamId);
+            return utils::convertFromHidl(remoteRet);
+        }
+        mConfiguredOutputs.erase(streamId);
+    }
+
+    // add new streams
+    for (auto outputPair : addSet) {
+        int streamId;
+        Status status;
+        auto ret = mRemote->createStream(outputPair.second,
+                                         [&status, &streamId](Status s, auto stream_id) {
+                                             status = s;
+                                             streamId = stream_id;
+                                         });
+        if (!remoteRet.isOk() || status != Status::NO_ERROR) {
+            ALOGE("Camera device %s failed to create stream", getId());
+            return utils::convertFromHidl(status);
+        }
+        mConfiguredOutputs.insert(std::make_pair(streamId, outputPair));
+    }
+
+    CameraMetadata params;
+    HCameraMetadata hidlParams;
+    if ((sessionParameters != nullptr) && (sessionParameters->settings != nullptr)) {
+        params.append(sessionParameters->settings->getInternalData());
+        const camera_metadata_t *params_metadata = params.getAndLock();
+        utils::convertToHidl(params_metadata, &hidlParams);
+        params.unlock(params_metadata);
+        remoteRet = mRemote->endConfigure(StreamConfigurationMode::NORMAL_MODE, hidlParams);
+    }
+    if (!remoteRet.isOk()) {
+        ALOGE("Transaction error: endConfigure failed %s", remoteRet.description().c_str());
+    }
+
+    return utils::convertFromHidl(remoteRet);
+}
+
+void
+CameraDevice::setRemoteDevice(sp<ICameraDeviceUser> remote) {
+    Mutex::Autolock _l(mDeviceLock);
+    mRemote = remote;
+}
+
+bool
+CameraDevice::setDeviceMetadataQueues() {
+        if (mRemote == nullptr) {
+          ALOGE("mRemote must not be null while trying to fetch metadata queues");
+          return false;
+        }
+        std::shared_ptr<RequestMetadataQueue> &reqQueue = mCaptureRequestMetadataQueue;
+        auto ret =
+            mRemote->getCaptureRequestMetadataQueue(
+                [&reqQueue](const auto &mqDescriptor) {
+                    reqQueue = std::make_shared<RequestMetadataQueue>(mqDescriptor);
+                    if (!reqQueue->isValid() || reqQueue->availableToWrite() <=0) {
+                        ALOGE("Empty fmq from cameraserver");
+                        reqQueue = nullptr;
+                    }
+                });
+        if (!ret.isOk()) {
+            ALOGE("Transaction error trying to get capture request metadata queue");
+            return false;
+        }
+        std::shared_ptr<ResultMetadataQueue> &resQueue = mCaptureResultMetadataQueue;
+        ret =
+                mRemote->getCaptureResultMetadataQueue(
+                        [&resQueue](const auto &mqDescriptor) {
+                            resQueue = std::make_shared<ResultMetadataQueue>(mqDescriptor);
+                            if (!resQueue->isValid() || resQueue->availableToWrite() <=0) {
+                                ALOGE("Empty fmq from cameraserver");
+                            }
+                        });
+        if (!ret.isOk()) {
+            ALOGE("Transaction error trying to get capture result metadata queue");
+            return false;
+        }
+        return true;
+}
+
+camera_status_t
+CameraDevice::checkCameraClosedOrErrorLocked() const {
+    if (mRemote == nullptr) {
+        ALOGE("%s: camera device already closed", __FUNCTION__);
+        return ACAMERA_ERROR_CAMERA_DISCONNECTED;
+    }
+    if (mInError) {// triggered by onDeviceError
+        ALOGE("%s: camera device has encountered a serious error", __FUNCTION__);
+        return mError;
+    }
+    return ACAMERA_OK;
+}
+
+void
+CameraDevice::setCameraDeviceErrorLocked(camera_status_t error) {
+    mInError = true;
+    mError = error;
+    return;
+}
+
+void
+CameraDevice::FrameNumberTracker::updateTracker(int64_t frameNumber, bool isError) {
+    ALOGV("updateTracker frame %" PRId64 " isError %d", frameNumber, isError);
+    if (isError) {
+        mFutureErrorSet.insert(frameNumber);
+    } else if (frameNumber <= mCompletedFrameNumber) {
+        ALOGE("Frame number %" PRId64 " decreased! current fn %" PRId64,
+                frameNumber, mCompletedFrameNumber);
+        return;
+    } else {
+        if (frameNumber != mCompletedFrameNumber + 1) {
+            ALOGE("Frame number out of order. Expect %" PRId64 " but get %" PRId64,
+                    mCompletedFrameNumber + 1, frameNumber);
+            // Do not assert as in java implementation
+        }
+        mCompletedFrameNumber = frameNumber;
+    }
+    update();
+}
+
+void
+CameraDevice::FrameNumberTracker::update() {
+    for (auto it = mFutureErrorSet.begin(); it != mFutureErrorSet.end();) {
+        int64_t errorFrameNumber = *it;
+        if (errorFrameNumber == mCompletedFrameNumber + 1) {
+            mCompletedFrameNumber++;
+            it = mFutureErrorSet.erase(it);
+        } else if (errorFrameNumber <= mCompletedFrameNumber) {
+            // This should not happen, but deal with it anyway
+            ALOGE("Completd frame number passed through current frame number!");
+            // erase the old error since it's no longer useful
+            it = mFutureErrorSet.erase(it);
+        } else {
+            // Normal requests hasn't catched up error frames, just break
+            break;
+        }
+    }
+    ALOGV("Update complete frame %" PRId64, mCompletedFrameNumber);
+}
+
+void
+CameraDevice::onCaptureErrorLocked(
+        ErrorCode errorCode,
+        const CaptureResultExtras& resultExtras) {
+    int sequenceId = resultExtras.requestId;
+    int64_t frameNumber = resultExtras.frameNumber;
+    int32_t burstId = resultExtras.burstId;
+    auto it = mSequenceCallbackMap.find(sequenceId);
+    if (it == mSequenceCallbackMap.end()) {
+        ALOGE("%s: Error: capture sequence index %d not found!",
+                __FUNCTION__, sequenceId);
+        setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+        return;
+    }
+
+    CallbackHolder cbh = (*it).second;
+    sp<ACameraCaptureSession> session = cbh.mSession;
+    if ((size_t) burstId >= cbh.mRequests.size()) {
+        ALOGE("%s: Error: request index %d out of bound (size %zu)",
+                __FUNCTION__, burstId, cbh.mRequests.size());
+        setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+        return;
+    }
+    sp<CaptureRequest> request = cbh.mRequests[burstId];
+
+    // Handle buffer error
+    if (errorCode == ErrorCode::CAMERA_BUFFER) {
+        int32_t streamId = resultExtras.errorStreamId;
+        ACameraCaptureSession_captureCallback_bufferLost onBufferLost =
+                cbh.mCallbacks.onCaptureBufferLost;
+        auto outputPairIt = mConfiguredOutputs.find(streamId);
+        if (outputPairIt == mConfiguredOutputs.end()) {
+            ALOGE("%s: Error: stream id %d does not exist", __FUNCTION__, streamId);
+            setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+            return;
+        }
+
+        const auto& windowHandles = outputPairIt->second.second.mOutputConfiguration.windowHandles;
+        for (const auto& outHandle : windowHandles) {
+            for (auto streamAndWindowId : request->mCaptureRequest.streamAndWindowIds) {
+                int32_t windowId = streamAndWindowId.windowId;
+                if (utils::isWindowNativeHandleEqual(windowHandles[windowId],outHandle)) {
+                    native_handle_t* anw =
+                        const_cast<native_handle_t *>(windowHandles[windowId].getNativeHandle());
+                    ALOGV("Camera %s Lost output buffer for ANW %p frame %" PRId64,
+                            getId(), anw, frameNumber);
+
+                    sp<AMessage> msg = new AMessage(kWhatCaptureBufferLost, mHandler);
+                    msg->setPointer(kContextKey, cbh.mCallbacks.context);
+                    msg->setObject(kSessionSpKey, session);
+                    msg->setPointer(kCallbackFpKey, (void*) onBufferLost);
+                    msg->setObject(kCaptureRequestKey, request);
+                    msg->setPointer(kAnwKey, (void*) anw);
+                    msg->setInt64(kFrameNumberKey, frameNumber);
+                    postSessionMsgAndCleanup(msg);
+                }
+            }
+        }
+    } else { // Handle other capture failures
+        // Fire capture failure callback if there is one registered
+        ACameraCaptureSession_captureCallback_failed onError = cbh.mCallbacks.onCaptureFailed;
+        sp<CameraCaptureFailure> failure(new CameraCaptureFailure());
+        failure->frameNumber = frameNumber;
+        // TODO: refine this when implementing flush
+        failure->reason      = CAPTURE_FAILURE_REASON_ERROR;
+        failure->sequenceId  = sequenceId;
+        failure->wasImageCaptured = (errorCode == ErrorCode::CAMERA_RESULT);
+
+        sp<AMessage> msg = new AMessage(kWhatCaptureFail, mHandler);
+        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setObject(kSessionSpKey, session);
+        msg->setPointer(kCallbackFpKey, (void*) onError);
+        msg->setObject(kCaptureRequestKey, request);
+        msg->setObject(kCaptureFailureKey, failure);
+        postSessionMsgAndCleanup(msg);
+
+        // Update tracker
+        mFrameNumberTracker.updateTracker(frameNumber, /*isError*/true);
+        checkAndFireSequenceCompleteLocked();
+    }
+    return;
+}
+
+void CameraDevice::CallbackHandler::onMessageReceived(
+        const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatOnDisconnected:
+        case kWhatOnError:
+        case kWhatSessionStateCb:
+        case kWhatCaptureStart:
+        case kWhatCaptureResult:
+        case kWhatCaptureFail:
+        case kWhatCaptureSeqEnd:
+        case kWhatCaptureSeqAbort:
+        case kWhatCaptureBufferLost:
+            ALOGV("%s: Received msg %d", __FUNCTION__, msg->what());
+            break;
+        case kWhatCleanUpSessions:
+            mCachedSessions.clear();
+            return;
+        default:
+            ALOGE("%s:Error: unknown device callback %d", __FUNCTION__, msg->what());
+            return;
+    }
+    // Check the common part of all message
+    void* context;
+    bool found = msg->findPointer(kContextKey, &context);
+    if (!found) {
+        ALOGE("%s: Cannot find callback context!", __FUNCTION__);
+        return;
+    }
+    switch (msg->what()) {
+        case kWhatOnDisconnected:
+        {
+            ACameraDevice* dev;
+            found = msg->findPointer(kDeviceKey, (void**) &dev);
+            if (!found || dev == nullptr) {
+                ALOGE("%s: Cannot find device pointer!", __FUNCTION__);
+                return;
+            }
+            ACameraDevice_StateCallback onDisconnected;
+            found = msg->findPointer(kCallbackFpKey, (void**) &onDisconnected);
+            if (!found) {
+                ALOGE("%s: Cannot find onDisconnected!", __FUNCTION__);
+                return;
+            }
+            if (onDisconnected == nullptr) {
+                return;
+            }
+            (*onDisconnected)(context, dev);
+            break;
+        }
+        case kWhatOnError:
+        {
+            ACameraDevice* dev;
+            found = msg->findPointer(kDeviceKey, (void**) &dev);
+            if (!found || dev == nullptr) {
+                ALOGE("%s: Cannot find device pointer!", __FUNCTION__);
+                return;
+            }
+            ACameraDevice_ErrorStateCallback onError;
+            found = msg->findPointer(kCallbackFpKey, (void**) &onError);
+            if (!found) {
+                ALOGE("%s: Cannot find onError!", __FUNCTION__);
+                return;
+            }
+            int errorCode;
+            found = msg->findInt32(kErrorCodeKey, &errorCode);
+            if (!found) {
+                ALOGE("%s: Cannot find error code!", __FUNCTION__);
+                return;
+            }
+            if (onError == nullptr) {
+                return;
+            }
+            (*onError)(context, dev, errorCode);
+            break;
+        }
+        case kWhatSessionStateCb:
+        case kWhatCaptureStart:
+        case kWhatCaptureResult:
+        case kWhatCaptureFail:
+        case kWhatCaptureSeqEnd:
+        case kWhatCaptureSeqAbort:
+        case kWhatCaptureBufferLost:
+        {
+            sp<RefBase> obj;
+            found = msg->findObject(kSessionSpKey, &obj);
+            if (!found || obj == nullptr) {
+                ALOGE("%s: Cannot find session pointer!", __FUNCTION__);
+                return;
+            }
+            sp<ACameraCaptureSession> session(static_cast<ACameraCaptureSession*>(obj.get()));
+            mCachedSessions.push(session);
+            sp<CaptureRequest> requestSp = nullptr;
+            switch (msg->what()) {
+                case kWhatCaptureStart:
+                case kWhatCaptureResult:
+                case kWhatCaptureFail:
+                case kWhatCaptureBufferLost:
+                    found = msg->findObject(kCaptureRequestKey, &obj);
+                    if (!found) {
+                        ALOGE("%s: Cannot find capture request!", __FUNCTION__);
+                        return;
+                    }
+                    requestSp = static_cast<CaptureRequest*>(obj.get());
+                    break;
+            }
+
+            switch (msg->what()) {
+                case kWhatSessionStateCb:
+                {
+                    ACameraCaptureSession_stateCallback onState;
+                    found = msg->findPointer(kCallbackFpKey, (void**) &onState);
+                    if (!found) {
+                        ALOGE("%s: Cannot find state callback!", __FUNCTION__);
+                        return;
+                    }
+                    if (onState == nullptr) {
+                        return;
+                    }
+                    (*onState)(context, session.get());
+                    break;
+                }
+                case kWhatCaptureStart:
+                {
+                    ACameraCaptureSession_captureCallback_start onStart;
+                    found = msg->findPointer(kCallbackFpKey, (void**) &onStart);
+                    if (!found) {
+                        ALOGE("%s: Cannot find capture start callback!", __FUNCTION__);
+                        return;
+                    }
+                    if (onStart == nullptr) {
+                        return;
+                    }
+                    int64_t timestamp;
+                    found = msg->findInt64(kTimeStampKey, &timestamp);
+                    if (!found) {
+                        ALOGE("%s: Cannot find timestamp!", __FUNCTION__);
+                        return;
+                    }
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp);
+                    (*onStart)(context, session.get(), request, timestamp);
+                    freeACaptureRequest(request);
+                    break;
+                }
+                case kWhatCaptureResult:
+                {
+                    ACameraCaptureSession_captureCallback_result onResult;
+                    found = msg->findPointer(kCallbackFpKey, (void**) &onResult);
+                    if (!found) {
+                        ALOGE("%s: Cannot find capture result callback!", __FUNCTION__);
+                        return;
+                    }
+                    if (onResult == nullptr) {
+                        return;
+                    }
+
+                    found = msg->findObject(kCaptureResultKey, &obj);
+                    if (!found) {
+                        ALOGE("%s: Cannot find capture result!", __FUNCTION__);
+                        return;
+                    }
+                    sp<ACameraMetadata> result(static_cast<ACameraMetadata*>(obj.get()));
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp);
+                    (*onResult)(context, session.get(), request, result.get());
+                    freeACaptureRequest(request);
+                    break;
+                }
+                case kWhatCaptureFail:
+                {
+                    ACameraCaptureSession_captureCallback_failed onFail;
+                    found = msg->findPointer(kCallbackFpKey, (void**) &onFail);
+                    if (!found) {
+                        ALOGE("%s: Cannot find capture fail callback!", __FUNCTION__);
+                        return;
+                    }
+                    if (onFail == nullptr) {
+                        return;
+                    }
+
+                    found = msg->findObject(kCaptureFailureKey, &obj);
+                    if (!found) {
+                        ALOGE("%s: Cannot find capture failure!", __FUNCTION__);
+                        return;
+                    }
+                    sp<CameraCaptureFailure> failureSp(
+                            static_cast<CameraCaptureFailure*>(obj.get()));
+                    ACameraCaptureFailure* failure =
+                            static_cast<ACameraCaptureFailure*>(failureSp.get());
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp);
+                    (*onFail)(context, session.get(), request, failure);
+                    freeACaptureRequest(request);
+                    break;
+                }
+                case kWhatCaptureSeqEnd:
+                {
+                    ACameraCaptureSession_captureCallback_sequenceEnd onSeqEnd;
+                    found = msg->findPointer(kCallbackFpKey, (void**) &onSeqEnd);
+                    if (!found) {
+                        ALOGE("%s: Cannot find sequence end callback!", __FUNCTION__);
+                        return;
+                    }
+                    if (onSeqEnd == nullptr) {
+                        return;
+                    }
+                    int seqId;
+                    found = msg->findInt32(kSequenceIdKey, &seqId);
+                    if (!found) {
+                        ALOGE("%s: Cannot find frame number!", __FUNCTION__);
+                        return;
+                    }
+                    int64_t frameNumber;
+                    found = msg->findInt64(kFrameNumberKey, &frameNumber);
+                    if (!found) {
+                        ALOGE("%s: Cannot find frame number!", __FUNCTION__);
+                        return;
+                    }
+                    (*onSeqEnd)(context, session.get(), seqId, frameNumber);
+                    break;
+                }
+                case kWhatCaptureSeqAbort:
+                {
+                    ACameraCaptureSession_captureCallback_sequenceAbort onSeqAbort;
+                    found = msg->findPointer(kCallbackFpKey, (void**) &onSeqAbort);
+                    if (!found) {
+                        ALOGE("%s: Cannot find sequence end callback!", __FUNCTION__);
+                        return;
+                    }
+                    if (onSeqAbort == nullptr) {
+                        return;
+                    }
+                    int seqId;
+                    found = msg->findInt32(kSequenceIdKey, &seqId);
+                    if (!found) {
+                        ALOGE("%s: Cannot find frame number!", __FUNCTION__);
+                        return;
+                    }
+                    (*onSeqAbort)(context, session.get(), seqId);
+                    break;
+                }
+                case kWhatCaptureBufferLost:
+                {
+                    ACameraCaptureSession_captureCallback_bufferLost onBufferLost;
+                    found = msg->findPointer(kCallbackFpKey, (void**) &onBufferLost);
+                    if (!found) {
+                        ALOGE("%s: Cannot find buffer lost callback!", __FUNCTION__);
+                        return;
+                    }
+                    if (onBufferLost == nullptr) {
+                        return;
+                    }
+
+                    native_handle_t* anw;
+                    found = msg->findPointer(kAnwKey, (void**) &anw);
+                    if (!found) {
+                        ALOGE("%s: Cannot find native_handle_t!", __FUNCTION__);
+                        return;
+                    }
+
+                    int64_t frameNumber;
+                    found = msg->findInt64(kFrameNumberKey, &frameNumber);
+                    if (!found) {
+                        ALOGE("%s: Cannot find frame number!", __FUNCTION__);
+                        return;
+                    }
+
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp);
+                    (*onBufferLost)(context, session.get(), request, anw, frameNumber);
+                    freeACaptureRequest(request);
+                    break;
+                }
+            }
+            break;
+        }
+    }
+}
+
+CameraDevice::CallbackHolder::CallbackHolder(
+    sp<ACameraCaptureSession>          session,
+    const Vector<sp<CaptureRequest> >& requests,
+    bool                               isRepeating,
+    ACameraCaptureSession_captureCallbacks* cbs) :
+    mSession(session), mRequests(requests),
+    mIsRepeating(isRepeating), mCallbacks(fillCb(cbs)) {}
+
+void
+CameraDevice::checkRepeatingSequenceCompleteLocked(
+    const int sequenceId, const int64_t lastFrameNumber) {
+    ALOGV("Repeating seqId %d lastFrameNumer %" PRId64, sequenceId, lastFrameNumber);
+    if (lastFrameNumber == NO_FRAMES_CAPTURED) {
+        if (mSequenceCallbackMap.count(sequenceId) == 0) {
+            ALOGW("No callback found for sequenceId %d", sequenceId);
+            return;
+        }
+        // remove callback holder from callback map
+        auto cbIt = mSequenceCallbackMap.find(sequenceId);
+        CallbackHolder cbh = cbIt->second;
+        mSequenceCallbackMap.erase(cbIt);
+        // send seq aborted callback
+        sp<AMessage> msg = new AMessage(kWhatCaptureSeqAbort, mHandler);
+        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setObject(kSessionSpKey, cbh.mSession);
+        msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceAborted);
+        msg->setInt32(kSequenceIdKey, sequenceId);
+        postSessionMsgAndCleanup(msg);
+    } else {
+        // Use mSequenceLastFrameNumberMap to track
+        mSequenceLastFrameNumberMap.insert(std::make_pair(sequenceId, lastFrameNumber));
+
+        // Last frame might have arrived. Check now
+        checkAndFireSequenceCompleteLocked();
+    }
+}
+
+void
+CameraDevice::checkAndFireSequenceCompleteLocked() {
+    int64_t completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
+    auto it = mSequenceLastFrameNumberMap.begin();
+    while (it != mSequenceLastFrameNumberMap.end()) {
+        int sequenceId = it->first;
+        int64_t lastFrameNumber = it->second;
+        bool seqCompleted = false;
+        bool hasCallback  = true;
+
+        if (mRemote == nullptr) {
+            ALOGW("Camera %s closed while checking sequence complete", getId());
+            return;
+        }
+
+        // Check if there is callback for this sequence
+        // This should not happen because we always register callback (with nullptr inside)
+        if (mSequenceCallbackMap.count(sequenceId) == 0) {
+            ALOGW("No callback found for sequenceId %d", sequenceId);
+            hasCallback = false;
+        }
+
+        if (lastFrameNumber <= completedFrameNumber) {
+            ALOGV("seq %d reached last frame %" PRId64 ", completed %" PRId64,
+                  sequenceId, lastFrameNumber, completedFrameNumber);
+            seqCompleted = true;
+        }
+
+        if (seqCompleted && hasCallback) {
+            // remove callback holder from callback map
+            auto cbIt = mSequenceCallbackMap.find(sequenceId);
+            CallbackHolder cbh = cbIt->second;
+            mSequenceCallbackMap.erase(cbIt);
+            // send seq complete callback
+            sp<AMessage> msg = new AMessage(kWhatCaptureSeqEnd, mHandler);
+            msg->setPointer(kContextKey, cbh.mCallbacks.context);
+            msg->setObject(kSessionSpKey, cbh.mSession);
+            msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceCompleted);
+            msg->setInt32(kSequenceIdKey, sequenceId);
+            msg->setInt64(kFrameNumberKey, lastFrameNumber);
+
+            // Clear the session sp before we send out the message
+            // This will guarantee the rare case where the message is processed
+            // before cbh goes out of scope and causing we call the session
+            // destructor while holding device lock
+            cbh.mSession.clear();
+            postSessionMsgAndCleanup(msg);
+        }
+
+        // No need to track sequence complete if there is no callback registered
+        if (seqCompleted || !hasCallback) {
+            it = mSequenceLastFrameNumberMap.erase(it);
+        } else {
+            ++it;
+        }
+    }
+}
+
+/**
+  * Camera service callback implementation
+  */
+android::hardware::Return<void>
+CameraDevice::ServiceCallback::onDeviceError(
+        ErrorCode errorCode,
+        const CaptureResultExtras& resultExtras) {
+    ALOGD("Device error received, code %d, frame number %" PRId64 ", request ID %d, subseq ID %d",
+            errorCode, resultExtras.frameNumber, resultExtras.requestId, resultExtras.burstId);
+    auto ret = Void();
+    sp<CameraDevice> dev = mDevice.promote();
+    if (dev == nullptr) {
+        return ret; // device has been closed
+    }
+
+    sp<ACameraCaptureSession> session = dev->mCurrentSession.promote();
+    Mutex::Autolock _l(dev->mDeviceLock);
+    if (dev->mRemote == nullptr) {
+        return ret; // device has been closed
+    }
+    switch (errorCode) {
+        case ErrorCode::CAMERA_DISCONNECTED:
+        {
+            // Camera is disconnected, close the session and expect no more callbacks
+            if (session != nullptr) {
+                session->closeByDevice();
+            }
+            dev->mCurrentSession = nullptr;
+            sp<AMessage> msg = new AMessage(kWhatOnDisconnected, dev->mHandler);
+            msg->setPointer(kContextKey, dev->mAppCallbacks.context);
+            msg->setPointer(kDeviceKey, (void*) dev->getWrapper());
+            msg->setPointer(kCallbackFpKey, (void*) dev->mAppCallbacks.onDisconnected);
+            msg->post();
+            break;
+        }
+        default:
+            ALOGE("Unknown error from camera device: %d", errorCode);
+            [[fallthrough]];
+        case ErrorCode::CAMERA_DEVICE:
+        case ErrorCode::CAMERA_SERVICE:
+        {
+            int32_t errorVal = ::ERROR_CAMERA_DEVICE;
+            // We keep this switch since this block might be encountered with
+            // more than just 2 states. The default fallthrough could have us
+            // handling more unmatched error cases.
+            switch (errorCode) {
+                case ErrorCode::CAMERA_DEVICE:
+                    dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+                    break;
+                case ErrorCode::CAMERA_SERVICE:
+                    dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+                    errorVal = ::ERROR_CAMERA_SERVICE;
+                    break;
+                default:
+                    dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_UNKNOWN);
+                    break;
+            }
+            sp<AMessage> msg = new AMessage(kWhatOnError, dev->mHandler);
+            msg->setPointer(kContextKey, dev->mAppCallbacks.context);
+            msg->setPointer(kDeviceKey, (void*) dev->getWrapper());
+            msg->setPointer(kCallbackFpKey, (void*) dev->mAppCallbacks.onError);
+            msg->setInt32(kErrorCodeKey, errorVal);
+            msg->post();
+            break;
+        }
+        case ErrorCode::CAMERA_REQUEST:
+        case ErrorCode::CAMERA_RESULT:
+        case ErrorCode::CAMERA_BUFFER:
+            dev->onCaptureErrorLocked(errorCode, resultExtras);
+            break;
+    }
+    return ret;
+}
+
+android::hardware::Return<void>
+CameraDevice::ServiceCallback::onDeviceIdle() {
+    ALOGV("Camera is now idle");
+    auto ret = Void();
+    sp<CameraDevice> dev = mDevice.promote();
+    if (dev == nullptr) {
+        return ret; // device has been closed
+    }
+
+    Mutex::Autolock _l(dev->mDeviceLock);
+    if (dev->isClosed() || dev->mRemote == nullptr) {
+        return ret;
+    }
+
+    if (dev->mIdle) {
+        // Already in idle state. Possibly other thread did waitUntilIdle
+        return ret;
+    }
+
+    if (dev->mCurrentSession != nullptr) {
+        ALOGE("onDeviceIdle sending state cb");
+        if (dev->mBusySession != dev->mCurrentSession) {
+            ALOGE("Current session != busy session");
+            dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
+            return ret;
+        }
+
+        sp<AMessage> msg = new AMessage(kWhatSessionStateCb, dev->mHandler);
+        msg->setPointer(kContextKey, dev->mBusySession->mUserSessionCallback.context);
+        msg->setObject(kSessionSpKey, dev->mBusySession);
+        msg->setPointer(kCallbackFpKey, (void*) dev->mBusySession->mUserSessionCallback.onReady);
+        // Make sure we clear the sp first so the session destructor can
+        // only happen on handler thread (where we don't hold device/session lock)
+        dev->mBusySession.clear();
+        dev->postSessionMsgAndCleanup(msg);
+    }
+    dev->mIdle = true;
+    dev->mFlushing = false;
+    return ret;
+}
+
+android::hardware::Return<void>
+CameraDevice::ServiceCallback::onCaptureStarted(
+        const CaptureResultExtras& resultExtras,
+        uint64_t timestamp) {
+    auto ret = Void();
+
+    sp<CameraDevice> dev = mDevice.promote();
+    if (dev == nullptr) {
+        return ret; // device has been closed
+    }
+    Mutex::Autolock _l(dev->mDeviceLock);
+    if (dev->isClosed() || dev->mRemote == nullptr) {
+        return ret;
+    }
+
+    int32_t sequenceId = resultExtras.requestId;
+    int32_t burstId = resultExtras.burstId;
+
+    auto it = dev->mSequenceCallbackMap.find(sequenceId);
+    if (it != dev->mSequenceCallbackMap.end()) {
+        CallbackHolder cbh = (*it).second;
+        ACameraCaptureSession_captureCallback_start onStart = cbh.mCallbacks.onCaptureStarted;
+        sp<ACameraCaptureSession> session = cbh.mSession;
+        if ((size_t) burstId >= cbh.mRequests.size()) {
+            ALOGE("%s: Error: request index %d out of bound (size %zu)",
+                    __FUNCTION__, burstId, cbh.mRequests.size());
+            dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+        }
+        sp<CaptureRequest> request = cbh.mRequests[burstId];
+        sp<AMessage> msg = new AMessage(kWhatCaptureStart, dev->mHandler);
+        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setObject(kSessionSpKey, session);
+        msg->setPointer(kCallbackFpKey, (void*) onStart);
+        msg->setObject(kCaptureRequestKey, request);
+        msg->setInt64(kTimeStampKey, timestamp);
+        dev->postSessionMsgAndCleanup(msg);
+    }
+    return ret;
+}
+
+android::hardware::Return<void>
+CameraDevice::ServiceCallback::onResultReceived(
+        const FmqSizeOrMetadata& resultMetadata,
+        const CaptureResultExtras& resultExtras,
+        const hidl_vec<PhysicalCaptureResultInfo>& physicalResultInfos) {
+    (void) physicalResultInfos;
+    auto ret = Void();
+
+    sp<CameraDevice> dev = mDevice.promote();
+    if (dev == nullptr) {
+        return ret; // device has been closed
+    }
+    int32_t sequenceId = resultExtras.requestId;
+    int64_t frameNumber = resultExtras.frameNumber;
+    int32_t burstId = resultExtras.burstId;
+    bool    isPartialResult = (resultExtras.partialResultCount < dev->mPartialResultCount);
+
+    if (!isPartialResult) {
+        ALOGV("SeqId %d frame %" PRId64 " result arrive.", sequenceId, frameNumber);
+    }
+
+    Mutex::Autolock _l(dev->mDeviceLock);
+    if (dev->mRemote == nullptr) {
+        return ret; // device has been disconnected
+    }
+
+    if (dev->isClosed()) {
+        if (!isPartialResult) {
+            dev->mFrameNumberTracker.updateTracker(frameNumber, /*isError*/false);
+        }
+        // early return to avoid callback sent to closed devices
+        return ret;
+    }
+
+    CameraMetadata metadataCopy;
+    HCameraMetadata hCameraMetadata;
+    bool converted = false;
+    if (resultMetadata.getDiscriminator() ==
+        FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) {
+        hCameraMetadata.resize(resultMetadata.fmqMetadataSize());
+        bool read = dev->mCaptureResultMetadataQueue->read(hCameraMetadata.data(),
+                                                           resultMetadata.fmqMetadataSize());
+            if (!read) {
+                ALOGE("%s capture request settings could't be read from fmq",
+                      __FUNCTION__);
+                return ret;
+            }
+            // TODO: Do we actually need to clone here ?
+            converted = utils::convertFromHidlCloned(hCameraMetadata, &metadataCopy);
+
+    } else {
+          converted = utils::convertFromHidlCloned(resultMetadata.metadata(), &metadataCopy);
+    }
+
+    if (!converted) {
+        ALOGE("%s result metadata couldn't be converted", __FUNCTION__);
+        return ret;
+    }
+
+    metadataCopy.update(ANDROID_LENS_INFO_SHADING_MAP_SIZE, dev->mShadingMapSize, /*data_count*/2);
+    metadataCopy.update(ANDROID_SYNC_FRAME_NUMBER, &frameNumber, /*data_count*/1);
+
+    auto it = dev->mSequenceCallbackMap.find(sequenceId);
+    if (it != dev->mSequenceCallbackMap.end()) {
+        CallbackHolder cbh = (*it).second;
+        ACameraCaptureSession_captureCallback_result onResult = isPartialResult ?
+                cbh.mCallbacks.onCaptureProgressed :
+                cbh.mCallbacks.onCaptureCompleted;
+        sp<ACameraCaptureSession> session = cbh.mSession;
+        if ((size_t) burstId >= cbh.mRequests.size()) {
+            ALOGE("%s: Error: request index %d out of bound (size %zu)",
+                    __FUNCTION__, burstId, cbh.mRequests.size());
+            dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+        }
+        sp<CaptureRequest> request = cbh.mRequests[burstId];
+        sp<ACameraMetadata> result(new ACameraMetadata(
+                metadataCopy.release(), ACameraMetadata::ACM_RESULT));
+
+        sp<AMessage> msg = new AMessage(kWhatCaptureResult, dev->mHandler);
+        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setObject(kSessionSpKey, session);
+        msg->setPointer(kCallbackFpKey, (void*) onResult);
+        msg->setObject(kCaptureRequestKey, request);
+        msg->setObject(kCaptureResultKey, result);
+        dev->postSessionMsgAndCleanup(msg);
+    }
+
+    if (!isPartialResult) {
+        dev->mFrameNumberTracker.updateTracker(frameNumber, /*isError*/false);
+        dev->checkAndFireSequenceCompleteLocked();
+    }
+
+    return ret;
+}
+
+android::hardware::Return<void>
+CameraDevice::ServiceCallback::onRepeatingRequestError(
+        uint64_t lastFrameNumber, int32_t stoppedSequenceId) {
+    auto ret = Void();
+
+    sp<CameraDevice> dev = mDevice.promote();
+    if (dev == nullptr) {
+        return ret; // device has been closed
+    }
+
+    Mutex::Autolock _l(dev->mDeviceLock);
+
+    int repeatingSequenceId = dev->mRepeatingSequenceId;
+    if (stoppedSequenceId == repeatingSequenceId) {
+        dev->mRepeatingSequenceId = REQUEST_ID_NONE;
+    }
+
+    dev->checkRepeatingSequenceCompleteLocked(repeatingSequenceId, lastFrameNumber);
+
+    return ret;
+}
+
+} // namespace acam
+} // namespace android
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
new file mode 100644
index 0000000..01a219f
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
@@ -0,0 +1,356 @@
+/*
+ * 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 _ACAMERA_DEVICE_H
+#define _ACAMERA_DEVICE_H
+
+#include <memory>
+#include <map>
+#include <set>
+#include <atomic>
+#include <utility>
+#include <utils/StrongPointer.h>
+#include <utils/Mutex.h>
+#include <utils/List.h>
+#include <utils/Vector.h>
+#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceUser.h>
+#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceCallback.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <fmq/MessageQueue.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <camera/NdkCameraManager.h>
+#include <camera/NdkCameraCaptureSession.h>
+#include "ACameraMetadata.h"
+#include "utils.h"
+
+namespace android {
+namespace acam {
+
+using ICameraDeviceCallback = frameworks::cameraservice::device::V2_0::ICameraDeviceCallback;
+using ICameraDeviceUser = frameworks::cameraservice::device::V2_0::ICameraDeviceUser;
+using CaptureResultExtras = frameworks::cameraservice::device::V2_0::CaptureResultExtras;
+using PhysicalCaptureResultInfo = frameworks::cameraservice::device::V2_0::PhysicalCaptureResultInfo;
+using PhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings;
+using SubmitInfo = frameworks::cameraservice::device::V2_0::SubmitInfo;
+using CaptureResultExtras = frameworks::cameraservice::device::V2_0::CaptureResultExtras;
+using ErrorCode = frameworks::cameraservice::device::V2_0::ErrorCode;
+using FmqSizeOrMetadata = frameworks::cameraservice::device::V2_0::FmqSizeOrMetadata;
+using StreamConfigurationMode = frameworks::cameraservice::device::V2_0::StreamConfigurationMode;
+using Status = frameworks::cameraservice::common::V2_0::Status;
+using ResultMetadataQueue = hardware::MessageQueue<uint8_t, hardware::kSynchronizedReadWrite>;
+using RequestMetadataQueue = hardware::MessageQueue<uint8_t, hardware::kSynchronizedReadWrite>;
+using CameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId;
+
+using hardware::hidl_vec;
+using hardware::hidl_string;
+using utils::native_handle_ptr_wrapper;
+using utils::CaptureRequest;
+using utils::OutputConfigurationWrapper;
+
+// Wrap ACameraCaptureFailure so it can be ref-counted
+struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure { };
+
+class CameraDevice final : public RefBase {
+  public:
+    CameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
+                  sp<ACameraMetadata> chars,
+                  ACameraDevice* wrapper);
+    ~CameraDevice();
+
+    inline const char* getId() const { return mCameraId.c_str(); }
+
+    camera_status_t createCaptureRequest(
+            ACameraDevice_request_template templateId,
+            ACaptureRequest** request) const;
+
+    camera_status_t createCaptureSession(
+            const ACaptureSessionOutputContainer*       outputs,
+            const ACaptureRequest* sessionParameters,
+            const ACameraCaptureSession_stateCallbacks* callbacks,
+            /*out*/ACameraCaptureSession** session);
+
+    // Callbacks from camera service
+    class ServiceCallback : public ICameraDeviceCallback {
+      public:
+        explicit ServiceCallback(CameraDevice* device) : mDevice(device) {}
+        android::hardware::Return<void> onDeviceError(ErrorCode errorCode,
+                           const CaptureResultExtras& resultExtras) override;
+        android::hardware::Return<void> onDeviceIdle() override;
+        android::hardware::Return<void> onCaptureStarted(const CaptureResultExtras& resultExtras,
+                              uint64_t timestamp) override;
+        android::hardware::Return<void> onResultReceived(const FmqSizeOrMetadata& result,
+                              const CaptureResultExtras& resultExtras,
+                              const hidl_vec<PhysicalCaptureResultInfo>& physicalResultInfos) override;
+        android::hardware::Return<void> onRepeatingRequestError(uint64_t lastFrameNumber,
+                int32_t stoppedSequenceId) override;
+      private:
+        const wp<CameraDevice> mDevice;
+    };
+    inline sp<ICameraDeviceCallback> getServiceCallback() {
+        return mServiceCallback;
+    };
+
+    // Camera device is only functional after remote being set
+    void setRemoteDevice(sp<ICameraDeviceUser> remote);
+
+    bool setDeviceMetadataQueues();
+    inline ACameraDevice* getWrapper() const { return mWrapper; };
+
+  private:
+    friend ACameraCaptureSession;
+
+    camera_status_t checkCameraClosedOrErrorLocked() const;
+
+    // device goes into fatal error state after this
+    void setCameraDeviceErrorLocked(camera_status_t error);
+
+    void disconnectLocked(sp<ACameraCaptureSession>& session); // disconnect from camera service
+
+    camera_status_t stopRepeatingLocked();
+
+    camera_status_t flushLocked(ACameraCaptureSession*);
+
+    camera_status_t waitUntilIdleLocked();
+
+
+    camera_status_t captureLocked(sp<ACameraCaptureSession> session,
+            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            int numRequests, ACaptureRequest** requests,
+            /*optional*/int* captureSequenceId);
+
+    camera_status_t setRepeatingRequestsLocked(sp<ACameraCaptureSession> session,
+            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            int numRequests, ACaptureRequest** requests,
+            /*optional*/int* captureSequenceId);
+
+    camera_status_t submitRequestsLocked(
+            sp<ACameraCaptureSession> session,
+            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            int numRequests, ACaptureRequest** requests,
+            /*out*/int* captureSequenceId,
+            bool isRepeating);
+
+    camera_status_t updateOutputConfigurationLocked(ACaptureSessionOutput *output);
+
+    camera_status_t allocateCaptureRequest(
+            const ACaptureRequest* request, sp<CaptureRequest>& outReq);
+
+    static ACaptureRequest* allocateACaptureRequest(sp<CaptureRequest>& req);
+    static void freeACaptureRequest(ACaptureRequest*);
+
+    // only For session to hold device lock
+    // Always grab device lock before grabbing session lock
+    void lockDeviceForSessionOps() const { mDeviceLock.lock(); };
+    void unlockDevice() const { mDeviceLock.unlock(); };
+
+    // For capture session to notify its end of life
+    void notifySessionEndOfLifeLocked(ACameraCaptureSession* session);
+
+    camera_status_t configureStreamsLocked(const ACaptureSessionOutputContainer* outputs,
+           const ACaptureRequest* sessionParameters);
+
+    // Input message will be posted and cleared after this returns
+    void postSessionMsgAndCleanup(sp<AMessage>& msg);
+
+    mutable Mutex mDeviceLock;
+    const hidl_string mCameraId;                          // Camera ID
+    const ACameraDevice_StateCallbacks mAppCallbacks; // Callback to app
+    const sp<ACameraMetadata> mChars;    // Camera characteristics
+    const sp<ServiceCallback> mServiceCallback;
+    ACameraDevice* mWrapper;
+
+    // stream id -> pair of (ACameraWindowType* from application, OutputConfiguration used for
+    // camera service)
+    std::map<int, std::pair<native_handle_ptr_wrapper, OutputConfigurationWrapper>> mConfiguredOutputs;
+
+    // TODO: maybe a bool will suffice for synchronous implementation?
+    std::atomic_bool mClosing;
+    inline bool isClosed() { return mClosing; }
+
+    bool mInError = false;
+    camera_status_t mError = ACAMERA_OK;
+    void onCaptureErrorLocked(
+            ErrorCode errorCode,
+            const CaptureResultExtras& resultExtras);
+
+    bool mIdle = true;
+    // This will avoid a busy session being deleted before it's back to idle state
+    sp<ACameraCaptureSession> mBusySession;
+
+    sp<ICameraDeviceUser> mRemote;
+
+    // Looper thread to handle callback to app
+    sp<ALooper> mCbLooper;
+    // definition of handler and message
+    enum {
+        // Device state callbacks
+        kWhatOnDisconnected,   // onDisconnected
+        kWhatOnError,          // onError
+        // Session state callbacks
+        kWhatSessionStateCb,   // onReady, onActive
+        // Capture callbacks
+        kWhatCaptureStart,     // onCaptureStarted
+        kWhatCaptureResult,    // onCaptureProgressed, onCaptureCompleted
+        kWhatCaptureFail,      // onCaptureFailed
+        kWhatCaptureSeqEnd,    // onCaptureSequenceCompleted
+        kWhatCaptureSeqAbort,  // onCaptureSequenceAborted
+        kWhatCaptureBufferLost,// onCaptureBufferLost
+        // Internal cleanup
+        kWhatCleanUpSessions   // Cleanup cached sp<ACameraCaptureSession>
+    };
+    static const char* kContextKey;
+    static const char* kDeviceKey;
+    static const char* kErrorCodeKey;
+    static const char* kCallbackFpKey;
+    static const char* kSessionSpKey;
+    static const char* kCaptureRequestKey;
+    static const char* kTimeStampKey;
+    static const char* kCaptureResultKey;
+    static const char* kCaptureFailureKey;
+    static const char* kSequenceIdKey;
+    static const char* kFrameNumberKey;
+    static const char* kAnwKey;
+
+    class CallbackHandler : public AHandler {
+      public:
+        void onMessageReceived(const sp<AMessage> &msg) override;
+
+      private:
+        // This handler will cache all capture session sp until kWhatCleanUpSessions
+        // is processed. This is used to guarantee the last session reference is always
+        // being removed in callback thread without holding camera device lock
+        Vector<sp<ACameraCaptureSession>> mCachedSessions;
+    };
+    sp<CallbackHandler> mHandler;
+
+    /***********************************
+     * Capture session related members *
+     ***********************************/
+    // The current active session
+    wp<ACameraCaptureSession> mCurrentSession;
+    bool mFlushing = false;
+
+    int mNextSessionId = 0;
+    // TODO: might need another looper/handler to handle callbacks from service
+
+    static const int REQUEST_ID_NONE = -1;
+    int mRepeatingSequenceId = REQUEST_ID_NONE;
+
+    // sequence id -> last frame number map
+    std::map<int32_t, int64_t> mSequenceLastFrameNumberMap;
+
+    struct CallbackHolder {
+        CallbackHolder(sp<ACameraCaptureSession>          session,
+                       const Vector<sp<CaptureRequest>>&  requests,
+                       bool                               isRepeating,
+                       ACameraCaptureSession_captureCallbacks* cbs);
+
+        static ACameraCaptureSession_captureCallbacks fillCb(
+                ACameraCaptureSession_captureCallbacks* cbs) {
+            if (cbs != nullptr) {
+                return *cbs;
+            }
+            return { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
+        }
+
+        sp<ACameraCaptureSession>   mSession;
+        Vector<sp<CaptureRequest>> mRequests;
+        const bool                  mIsRepeating;
+        ACameraCaptureSession_captureCallbacks mCallbacks;
+    };
+    // sequence id -> callbacks map
+    std::map<int, CallbackHolder> mSequenceCallbackMap;
+
+    static const int64_t NO_FRAMES_CAPTURED = -1;
+    class FrameNumberTracker {
+      public:
+        // TODO: Called in onResultReceived and onCaptureErrorLocked
+        void updateTracker(int64_t frameNumber, bool isError);
+        inline int64_t getCompletedFrameNumber() { return mCompletedFrameNumber; }
+      private:
+        void update();
+        void updateCompletedFrameNumber(int64_t frameNumber);
+
+        int64_t mCompletedFrameNumber = NO_FRAMES_CAPTURED;
+        List<int64_t> mSkippedFrameNumbers;
+        std::set<int64_t> mFutureErrorSet;
+    };
+    FrameNumberTracker mFrameNumberTracker;
+
+    void checkRepeatingSequenceCompleteLocked(const int sequenceId, const int64_t lastFrameNumber);
+    void checkAndFireSequenceCompleteLocked();
+
+    // Misc variables
+    int32_t mShadingMapSize[2];   // const after constructor
+    int32_t mPartialResultCount;  // const after constructor
+    std::shared_ptr<ResultMetadataQueue> mCaptureRequestMetadataQueue = nullptr;
+    std::shared_ptr<ResultMetadataQueue> mCaptureResultMetadataQueue = nullptr;
+};
+
+} // namespace acam;
+} // namespace android;
+
+/**
+ * ACameraDevice opaque struct definition
+ * Leave outside of android namespace because it's NDK struct
+ */
+struct ACameraDevice {
+    ACameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
+                  sp<ACameraMetadata> chars) :
+            mDevice(new android::acam::CameraDevice(id, cb, std::move(chars), this)) {}
+
+    ~ACameraDevice() {};
+
+    /*******************
+     * NDK public APIs *
+     *******************/
+    inline const char* getId() const { return mDevice->getId(); }
+
+    camera_status_t createCaptureRequest(
+            ACameraDevice_request_template templateId,
+            ACaptureRequest** request) const {
+        return mDevice->createCaptureRequest(templateId, request);
+    }
+
+    camera_status_t createCaptureSession(
+            const ACaptureSessionOutputContainer*       outputs,
+            const ACaptureRequest* sessionParameters,
+            const ACameraCaptureSession_stateCallbacks* callbacks,
+            /*out*/ACameraCaptureSession** session) {
+        return mDevice->createCaptureSession(outputs, sessionParameters, callbacks, session);
+    }
+
+    /***********************
+     * Device interal APIs *
+     ***********************/
+    inline android::sp<android::acam::ICameraDeviceCallback> getServiceCallback() {
+        return mDevice->getServiceCallback();
+    };
+
+    // Camera device is only functional after remote being set
+    inline void setRemoteDevice(android::sp<android::acam::ICameraDeviceUser> remote) {
+        mDevice->setRemoteDevice(remote);
+    }
+    inline bool setDeviceMetadataQueues() {
+        return mDevice->setDeviceMetadataQueues();
+    }
+  private:
+    android::sp<android::acam::CameraDevice> mDevice;
+};
+
+#endif // _ACAMERA_DEVICE_H
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
new file mode 100644
index 0000000..f395b44
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
@@ -0,0 +1,458 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ACameraManagerVendor"
+
+#include <memory>
+#include "ndk_vendor/impl/ACameraManager.h"
+#include "ACameraMetadata.h"
+#include "ndk_vendor/impl/ACameraDevice.h"
+#include "utils.h"
+
+#include <utils/Vector.h>
+#include <cutils/properties.h>
+#include <stdlib.h>
+
+#include <VendorTagDescriptor.h>
+
+using namespace android::acam;
+
+namespace android {
+namespace acam {
+
+using CameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId;
+
+// Static member definitions
+const char* CameraManagerGlobal::kCameraIdKey   = "CameraId";
+const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp";
+const char* CameraManagerGlobal::kContextKey    = "CallbackContext";
+Mutex                CameraManagerGlobal::sLock;
+CameraManagerGlobal* CameraManagerGlobal::sInstance = nullptr;
+
+CameraManagerGlobal&
+CameraManagerGlobal::getInstance() {
+    Mutex::Autolock _l(sLock);
+    CameraManagerGlobal* instance = sInstance;
+    if (instance == nullptr) {
+        instance = new CameraManagerGlobal();
+        sInstance = instance;
+    }
+    return *instance;
+}
+
+CameraManagerGlobal::~CameraManagerGlobal() {
+    // clear sInstance so next getInstance call knows to create a new one
+    Mutex::Autolock _sl(sLock);
+    sInstance = nullptr;
+    Mutex::Autolock _l(mLock);
+    if (mCameraService != nullptr) {
+        mCameraService->unlinkToDeath(mDeathNotifier);
+        mCameraService->removeListener(mCameraServiceListener);
+    }
+    mDeathNotifier.clear();
+    if (mCbLooper != nullptr) {
+        mCbLooper->unregisterHandler(mHandler->id());
+        mCbLooper->stop();
+    }
+    mCbLooper.clear();
+    mHandler.clear();
+    mCameraServiceListener.clear();
+    mCameraService.clear();
+}
+
+static bool isCameraServiceDisabled() {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("config.disable_cameraservice", value, "0");
+    return (strncmp(value, "0", 2) != 0 && strncasecmp(value, "false", 6) != 0);
+}
+
+// TODO: Add back when vendor tags are supported for libcamera2ndk_vendor when
+//       the HIDL interface supports querying by vendor id.
+
+sp<ICameraService> CameraManagerGlobal::getCameraService() {
+    Mutex::Autolock _l(mLock);
+    if (mCameraService.get() == nullptr) {
+        if (isCameraServiceDisabled()) {
+            return mCameraService;
+        }
+
+        sp<ICameraService> cameraServiceBinder;
+        do {
+            cameraServiceBinder = ICameraService::getService();
+            if (cameraServiceBinder != nullptr) {
+                break;
+            }
+            ALOGW("CameraService not published, waiting...");
+            usleep(kCameraServicePollDelay);
+        } while(true);
+        if (mDeathNotifier == nullptr) {
+            mDeathNotifier = new DeathNotifier(this);
+        }
+        cameraServiceBinder->linkToDeath(mDeathNotifier, 0);
+        mCameraService = cameraServiceBinder;
+
+        // Setup looper thread to perfrom availiability callbacks
+        if (mCbLooper == nullptr) {
+            mCbLooper = new ALooper;
+            mCbLooper->setName("C2N-mgr-looper");
+            status_t err = mCbLooper->start(
+                    /*runOnCallingThread*/false,
+                    /*canCallJava*/       true,
+                    PRIORITY_DEFAULT);
+            if (err != OK) {
+                ALOGE("%s: Unable to start camera service listener looper: %s (%d)",
+                        __FUNCTION__, strerror(-err), err);
+                mCbLooper.clear();
+                return nullptr;
+            }
+            if (mHandler == nullptr) {
+                mHandler = new CallbackHandler();
+            }
+            mCbLooper->registerHandler(mHandler);
+        }
+
+        // register ICameraServiceListener
+        if (mCameraServiceListener == nullptr) {
+            mCameraServiceListener = new CameraServiceListener(this);
+        }
+        hidl_vec<CameraStatusAndId> cameraStatuses{};
+        Status status = Status::NO_ERROR;
+        auto remoteRet = mCameraService->addListener(mCameraServiceListener,
+                                                     [&status, &cameraStatuses](Status s,
+                                                                                auto &retStatuses) {
+                                                         status = s;
+                                                         cameraStatuses = retStatuses;
+                                                     });
+        if (!remoteRet.isOk() || status != Status::NO_ERROR) {
+            ALOGE("Failed to add listener to camera service %s", remoteRet.description().c_str());
+        }
+        for (auto& c : cameraStatuses) {
+            onStatusChangedLocked(c);
+        }
+    }
+    return mCameraService;
+}
+
+void CameraManagerGlobal::DeathNotifier::serviceDied(uint64_t cookie, const wp<IBase> &who) {
+    (void) cookie;
+    (void) who;
+    ALOGE("Camera service binderDied!");
+    sp<CameraManagerGlobal> cm = mCameraManager.promote();
+    if (cm != nullptr) {
+        AutoMutex lock(cm->mLock);
+        for (auto& pair : cm->mDeviceStatusMap) {
+            CameraStatusAndId cameraStatusAndId;
+            cameraStatusAndId.cameraId = pair.first;
+            cameraStatusAndId.deviceStatus = pair.second;
+            cm->onStatusChangedLocked(cameraStatusAndId);
+        }
+        cm->mCameraService.clear();
+        // TODO: consider adding re-connect call here?
+    }
+}
+
+void CameraManagerGlobal::registerAvailabilityCallback(
+        const ACameraManager_AvailabilityCallbacks *callback) {
+    Mutex::Autolock _l(mLock);
+    Callback cb(callback);
+    auto pair = mCallbacks.insert(cb);
+    // Send initial callbacks if callback is newly registered
+    if (pair.second) {
+        for (auto& pair : mDeviceStatusMap) {
+            const hidl_string& cameraId = pair.first;
+            CameraDeviceStatus status = pair.second;
+
+            sp<AMessage> msg = new AMessage(kWhatSendSingleCallback, mHandler);
+            ACameraManager_AvailabilityCallback cb = isStatusAvailable(status) ?
+                    callback->onCameraAvailable : callback->onCameraUnavailable;
+            msg->setPointer(kCallbackFpKey, (void *) cb);
+            msg->setPointer(kContextKey, callback->context);
+            msg->setString(kCameraIdKey, AString(cameraId.c_str()));
+            msg->post();
+        }
+    }
+}
+
+void CameraManagerGlobal::unregisterAvailabilityCallback(
+        const ACameraManager_AvailabilityCallbacks *callback) {
+    Mutex::Autolock _l(mLock);
+    Callback cb(callback);
+    mCallbacks.erase(cb);
+}
+
+void CameraManagerGlobal::getCameraIdList(std::vector<hidl_string>* cameraIds) {
+    // Ensure that we have initialized/refreshed the list of available devices
+    auto cs = getCameraService();
+    Mutex::Autolock _l(mLock);
+
+    for(auto& deviceStatus : mDeviceStatusMap) {
+        if (deviceStatus.second == CameraDeviceStatus::STATUS_NOT_PRESENT ||
+                deviceStatus.second == CameraDeviceStatus::STATUS_ENUMERATING) {
+            continue;
+        }
+        cameraIds->push_back(deviceStatus.first);
+    }
+}
+
+bool CameraManagerGlobal::validStatus(CameraDeviceStatus status) {
+    switch (status) {
+        case CameraDeviceStatus::STATUS_NOT_PRESENT:
+        case CameraDeviceStatus::STATUS_PRESENT:
+        case CameraDeviceStatus::STATUS_ENUMERATING:
+        case CameraDeviceStatus::STATUS_NOT_AVAILABLE:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool CameraManagerGlobal::isStatusAvailable(CameraDeviceStatus status) {
+    switch (status) {
+        case CameraDeviceStatus::STATUS_PRESENT:
+            return true;
+        default:
+            return false;
+    }
+}
+
+void CameraManagerGlobal::CallbackHandler::onMessageReceived(
+        const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatSendSingleCallback:
+        {
+            ACameraManager_AvailabilityCallback cb;
+            void* context;
+            AString cameraId;
+            bool found = msg->findPointer(kCallbackFpKey, (void**) &cb);
+            if (!found) {
+                ALOGE("%s: Cannot find camera callback fp!", __FUNCTION__);
+                return;
+            }
+            found = msg->findPointer(kContextKey, &context);
+            if (!found) {
+                ALOGE("%s: Cannot find callback context!", __FUNCTION__);
+                return;
+            }
+            found = msg->findString(kCameraIdKey, &cameraId);
+            if (!found) {
+                ALOGE("%s: Cannot find camera ID!", __FUNCTION__);
+                return;
+            }
+            (*cb)(context, cameraId.c_str());
+            break;
+        }
+        default:
+            ALOGE("%s: unknown message type %d", __FUNCTION__, msg->what());
+            break;
+    }
+}
+
+hardware::Return<void> CameraManagerGlobal::CameraServiceListener::onStatusChanged(
+        const CameraStatusAndId &statusAndId) {
+    sp<CameraManagerGlobal> cm = mCameraManager.promote();
+    if (cm != nullptr) {
+        cm->onStatusChanged(statusAndId);
+    } else {
+        ALOGE("Cannot deliver status change. Global camera manager died");
+    }
+    return Void();
+}
+
+void CameraManagerGlobal::onStatusChanged(
+        const CameraStatusAndId &statusAndId) {
+    Mutex::Autolock _l(mLock);
+    onStatusChangedLocked(statusAndId);
+}
+
+void CameraManagerGlobal::onStatusChangedLocked(
+        const CameraStatusAndId &statusAndId) {
+    hidl_string cameraId = statusAndId.cameraId;
+    CameraDeviceStatus status = statusAndId.deviceStatus;
+    if (!validStatus(status)) {
+        ALOGE("%s: Invalid status %d", __FUNCTION__, status);
+        return;
+    }
+
+    bool firstStatus = (mDeviceStatusMap.count(cameraId) == 0);
+    CameraDeviceStatus oldStatus = firstStatus ?
+            status : // first status
+            mDeviceStatusMap[cameraId];
+
+    if (!firstStatus &&
+            isStatusAvailable(status) == isStatusAvailable(oldStatus)) {
+        // No status update. No need to send callback
+        return;
+    }
+
+    // Iterate through all registered callbacks
+    mDeviceStatusMap[cameraId] = status;
+    for (auto cb : mCallbacks) {
+        sp<AMessage> msg = new AMessage(kWhatSendSingleCallback, mHandler);
+        ACameraManager_AvailabilityCallback cbFp = isStatusAvailable(status) ?
+                cb.mAvailable : cb.mUnavailable;
+        msg->setPointer(kCallbackFpKey, (void *) cbFp);
+        msg->setPointer(kContextKey, cb.mContext);
+        msg->setString(kCameraIdKey, AString(cameraId.c_str()));
+        msg->post();
+    }
+    if (status == CameraDeviceStatus::STATUS_NOT_PRESENT) {
+        mDeviceStatusMap.erase(cameraId);
+    }
+}
+
+} // namespace acam
+} // namespace android
+
+/**
+ * ACameraManger Implementation
+ */
+camera_status_t
+ACameraManager::getCameraIdList(ACameraIdList** cameraIdList) {
+    Mutex::Autolock _l(mLock);
+
+    std::vector<hidl_string> idList;
+    CameraManagerGlobal::getInstance().getCameraIdList(&idList);
+
+    int numCameras = idList.size();
+    ACameraIdList *out = new ACameraIdList;
+    if (!out) {
+        ALOGE("Allocate memory for ACameraIdList failed!");
+        return ACAMERA_ERROR_NOT_ENOUGH_MEMORY;
+    }
+    out->numCameras = numCameras;
+    out->cameraIds = new const char*[numCameras];
+    if (!out->cameraIds) {
+        ALOGE("Allocate memory for ACameraIdList failed!");
+        deleteCameraIdList(out);
+        return ACAMERA_ERROR_NOT_ENOUGH_MEMORY;
+    }
+    for (int i = 0; i < numCameras; i++) {
+        const char* src = idList[i].c_str();
+        size_t dstSize = strlen(src) + 1;
+        char* dst = new char[dstSize];
+        if (!dst) {
+            ALOGE("Allocate memory for ACameraIdList failed!");
+            deleteCameraIdList(out);
+            return ACAMERA_ERROR_NOT_ENOUGH_MEMORY;
+        }
+        strlcpy(dst, src, dstSize);
+        out->cameraIds[i] = dst;
+    }
+    *cameraIdList = out;
+    return ACAMERA_OK;
+}
+
+void
+ACameraManager::deleteCameraIdList(ACameraIdList* cameraIdList) {
+    if (cameraIdList != nullptr) {
+        if (cameraIdList->cameraIds != nullptr) {
+            for (int i = 0; i < cameraIdList->numCameras; i ++) {
+                if (cameraIdList->cameraIds[i] != nullptr) {
+                    delete[] cameraIdList->cameraIds[i];
+                }
+            }
+            delete[] cameraIdList->cameraIds;
+        }
+        delete cameraIdList;
+    }
+}
+
+camera_status_t ACameraManager::getCameraCharacteristics(
+        const char *cameraIdStr, sp<ACameraMetadata> *characteristics) {
+    Mutex::Autolock _l(mLock);
+
+    sp<ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService();
+    if (cs == nullptr) {
+        ALOGE("%s: Cannot reach camera service!", __FUNCTION__);
+        return ACAMERA_ERROR_CAMERA_DISCONNECTED;
+    }
+    CameraMetadata rawMetadata;
+    Status status = Status::NO_ERROR;
+    auto serviceRet =
+        cs->getCameraCharacteristics(cameraIdStr,
+                                     [&status, &rawMetadata] (auto s ,
+                                                              const hidl_vec<uint8_t> &metadata) {
+                                          status = s;
+                                          if (status == Status::NO_ERROR) {
+                                              utils::convertFromHidlCloned(metadata, &rawMetadata);
+                                          }
+                                     });
+    if (!serviceRet.isOk() || status != Status::NO_ERROR) {
+        ALOGE("Get camera characteristics from camera service failed");
+        return ACAMERA_ERROR_UNKNOWN; // should not reach here
+    }
+
+    *characteristics = new ACameraMetadata(
+            rawMetadata.release(), ACameraMetadata::ACM_CHARACTERISTICS);
+    return ACAMERA_OK;
+}
+
+camera_status_t
+ACameraManager::openCamera(
+        const char* cameraId,
+        ACameraDevice_StateCallbacks* callback,
+        /*out*/ACameraDevice** outDevice) {
+    sp<ACameraMetadata> rawChars;
+    camera_status_t ret = getCameraCharacteristics(cameraId, &rawChars);
+    Mutex::Autolock _l(mLock);
+    if (ret != ACAMERA_OK) {
+        ALOGE("%s: cannot get camera characteristics for camera %s. err %d",
+                __FUNCTION__, cameraId, ret);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+
+    ACameraDevice* device = new ACameraDevice(cameraId, callback, std::move(rawChars));
+
+    sp<ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService();
+    if (cs == nullptr) {
+        ALOGE("%s: Cannot reach camera service!", __FUNCTION__);
+        delete device;
+        return ACAMERA_ERROR_CAMERA_DISCONNECTED;
+    }
+
+    sp<ICameraDeviceCallback> callbacks = device->getServiceCallback();
+    sp<ICameraDeviceUser> deviceRemote;
+
+    // No way to get package name from native.
+    // Send a zero length package name and let camera service figure it out from UID
+    Status status = Status::NO_ERROR;
+    auto serviceRet = cs->connectDevice(
+            callbacks, cameraId, [&status, &deviceRemote](auto s, auto &device) {
+                                     status = s;
+                                     deviceRemote = device;
+                                 });
+
+    if (!serviceRet.isOk() || status != Status::NO_ERROR) {
+        ALOGE("%s: connect camera device failed", __FUNCTION__);
+        // TODO: Convert serviceRet to camera_status_t
+        delete device;
+        return ACAMERA_ERROR_UNKNOWN;
+    }
+    if (deviceRemote == nullptr) {
+        ALOGE("%s: connect camera device failed! remote device is null", __FUNCTION__);
+        delete device;
+        return ACAMERA_ERROR_CAMERA_DISCONNECTED;
+    }
+    device->setRemoteDevice(deviceRemote);
+    device->setDeviceMetadataQueues();
+    *outDevice = device;
+    return ACAMERA_OK;
+}
+
+ACameraManager::~ACameraManager() {
+
+}
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.h b/camera/ndk/ndk_vendor/impl/ACameraManager.h
new file mode 100644
index 0000000..c8d640f
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.h
@@ -0,0 +1,209 @@
+/*
+ * 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 _ACAMERA_MANAGER_H
+#define _ACAMERA_MANAGER_H
+
+#include <camera/NdkCameraManager.h>
+
+#include <android-base/parseint.h>
+#include <android/frameworks/cameraservice/service/2.0/ICameraService.h>
+
+#include <CameraMetadata.h>
+#include <utils/StrongPointer.h>
+#include <utils/Mutex.h>
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <set>
+#include <map>
+
+namespace android {
+namespace acam {
+
+using ICameraService = frameworks::cameraservice::service::V2_0::ICameraService;
+using CameraDeviceStatus = frameworks::cameraservice::service::V2_0::CameraDeviceStatus;
+using ICameraServiceListener = frameworks::cameraservice::service::V2_0::ICameraServiceListener;
+using CameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId;
+using Status = frameworks::cameraservice::common::V2_0::Status;
+using VendorTagSection = frameworks::cameraservice::common::V2_0::VendorTagSection;
+using VendorTag = frameworks::cameraservice::common::V2_0::VendorTag;
+using IBase = android::hidl::base::V1_0::IBase;
+using android::hardware::hidl_string;
+using hardware::Void;
+
+/**
+ * Per-process singleton instance of CameraManger. Shared by all ACameraManager
+ * instances. Created when first ACameraManager is created and destroyed when
+ * all ACameraManager instances are deleted.
+ *
+ * TODO: maybe CameraManagerGlobal is better suited in libcameraclient?
+ */
+class CameraManagerGlobal final : public RefBase {
+  public:
+    static CameraManagerGlobal& getInstance();
+    sp<ICameraService> getCameraService();
+
+    void registerAvailabilityCallback(
+            const ACameraManager_AvailabilityCallbacks *callback);
+    void unregisterAvailabilityCallback(
+            const ACameraManager_AvailabilityCallbacks *callback);
+
+    /**
+     * Return camera IDs that support camera2
+     */
+    void getCameraIdList(std::vector<hidl_string> *cameraIds);
+
+  private:
+    sp<ICameraService> mCameraService;
+    const int          kCameraServicePollDelay = 500000; // 0.5s
+    Mutex              mLock;
+    class DeathNotifier : public android::hardware::hidl_death_recipient {
+      public:
+        explicit DeathNotifier(CameraManagerGlobal* cm) : mCameraManager(cm) {}
+      protected:
+        // IBinder::DeathRecipient implementation
+        virtual void serviceDied(uint64_t cookie, const wp<IBase> &who);
+      private:
+        const wp<CameraManagerGlobal> mCameraManager;
+    };
+    sp<DeathNotifier> mDeathNotifier;
+
+    class CameraServiceListener final : public ICameraServiceListener {
+      public:
+        explicit CameraServiceListener(CameraManagerGlobal* cm) : mCameraManager(cm) {}
+        android::hardware::Return<void> onStatusChanged(
+            const CameraStatusAndId &statusAndId) override;
+
+      private:
+        const wp<CameraManagerGlobal> mCameraManager;
+    };
+    sp<CameraServiceListener> mCameraServiceListener;
+
+    // Wrapper of ACameraManager_AvailabilityCallbacks so we can store it in std::set
+    struct Callback {
+        explicit Callback(const ACameraManager_AvailabilityCallbacks *callback) :
+            mAvailable(callback->onCameraAvailable),
+            mUnavailable(callback->onCameraUnavailable),
+            mContext(callback->context) {}
+
+        bool operator == (const Callback& other) const {
+            return (mAvailable == other.mAvailable &&
+                    mUnavailable == other.mUnavailable &&
+                    mContext == other.mContext);
+        }
+        bool operator != (const Callback& other) const {
+            return !(*this == other);
+        }
+        bool operator < (const Callback& other) const {
+            if (*this == other) return false;
+            if (mContext != other.mContext) return mContext < other.mContext;
+            if (mAvailable != other.mAvailable) return mAvailable < other.mAvailable;
+            return mUnavailable < other.mUnavailable;
+        }
+        bool operator > (const Callback& other) const {
+            return (*this != other && !(*this < other));
+        }
+        ACameraManager_AvailabilityCallback mAvailable;
+        ACameraManager_AvailabilityCallback mUnavailable;
+        void*                               mContext;
+    };
+    std::set<Callback> mCallbacks;
+
+    // definition of handler and message
+    enum {
+        kWhatSendSingleCallback
+    };
+    static const char* kCameraIdKey;
+    static const char* kCallbackFpKey;
+    static const char* kContextKey;
+    class CallbackHandler : public AHandler {
+      public:
+        CallbackHandler() {}
+        void onMessageReceived(const sp<AMessage> &msg) override;
+    };
+    sp<CallbackHandler> mHandler;
+    sp<ALooper>         mCbLooper; // Looper thread where callbacks actually happen on
+
+    void onStatusChanged(const CameraStatusAndId &statusAndId);
+    void onStatusChangedLocked(const CameraStatusAndId &statusAndId);
+    // Utils for status
+    static bool validStatus(CameraDeviceStatus status);
+    static bool isStatusAvailable(CameraDeviceStatus status);
+
+    // The sort logic must match the logic in
+    // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
+    struct CameraIdComparator {
+        bool operator()(const hidl_string& a, const hidl_string& b) const {
+            uint32_t aUint = 0, bUint = 0;
+            bool aIsUint = base::ParseUint(a.c_str(), &aUint);
+            bool bIsUint = base::ParseUint(b.c_str(), &bUint);
+
+            // Uint device IDs first
+            if (aIsUint && bIsUint) {
+                return aUint < bUint;
+            } else if (aIsUint) {
+                return true;
+            } else if (bIsUint) {
+                return false;
+            }
+            // Simple string compare if both id are not uint
+            return a < b;
+        }
+    };
+
+    // Map camera_id -> status
+    std::map<hidl_string, CameraDeviceStatus, CameraIdComparator> mDeviceStatusMap;
+
+    // For the singleton instance
+    static Mutex sLock;
+    static CameraManagerGlobal* sInstance;
+    CameraManagerGlobal() {};
+    ~CameraManagerGlobal();
+};
+
+} // namespace acam;
+} // namespace android;
+
+/**
+ * ACameraManager opaque struct definition
+ * Leave outside of android namespace because it's NDK struct
+ */
+struct ACameraManager {
+    ACameraManager() :
+            mGlobalManager(&(android::acam::CameraManagerGlobal::getInstance())) {}
+    ~ACameraManager();
+    camera_status_t getCameraIdList(ACameraIdList** cameraIdList);
+    static void     deleteCameraIdList(ACameraIdList* cameraIdList);
+
+    camera_status_t getCameraCharacteristics(
+            const char* cameraId, android::sp<ACameraMetadata>* characteristics);
+
+    camera_status_t openCamera(const char* cameraId,
+                               ACameraDevice_StateCallbacks* callback,
+                               /*out*/ACameraDevice** device);
+
+  private:
+    enum {
+        kCameraIdListNotInit = -1
+    };
+    android::Mutex         mLock;
+    android::sp<android::acam::CameraManagerGlobal> mGlobalManager;
+};
+
+#endif //_ACAMERA_MANAGER_H
diff --git a/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h b/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h
new file mode 100644
index 0000000..ed67615
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACaptureRequestVendor.h
@@ -0,0 +1,36 @@
+/*
+ * 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 "utils.h"
+
+struct ACameraOutputTarget {
+    explicit ACameraOutputTarget(native_handle_t* window) : mWindow(window) {};
+
+    bool operator == (const ACameraOutputTarget& other) const {
+        return mWindow == other.mWindow;
+    }
+    bool operator != (const ACameraOutputTarget& other) const {
+        return mWindow != other.mWindow;
+    }
+    bool operator < (const ACameraOutputTarget& other) const {
+        return mWindow < other.mWindow;
+    }
+    bool operator > (const ACameraOutputTarget& other) const {
+        return mWindow > other.mWindow;
+    }
+
+    android::acam::utils::native_handle_ptr_wrapper mWindow;
+};
diff --git a/camera/ndk/ndk_vendor/impl/utils.cpp b/camera/ndk/ndk_vendor/impl/utils.cpp
new file mode 100644
index 0000000..7193006
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/utils.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ACameraVendorUtils"
+
+#include <utils/Log.h>
+
+#include "utils.h"
+
+namespace android {
+namespace acam {
+namespace utils {
+
+// Convert CaptureRequest wrappable by sp<> to hidl CaptureRequest.
+frameworks::cameraservice::device::V2_0::CaptureRequest
+convertToHidl(const CaptureRequest *captureRequest) {
+    frameworks::cameraservice::device::V2_0::CaptureRequest hCaptureRequest;
+    hCaptureRequest.physicalCameraSettings = captureRequest->mCaptureRequest.physicalCameraSettings;
+    hCaptureRequest.streamAndWindowIds = captureRequest->mCaptureRequest.streamAndWindowIds;
+    return hCaptureRequest;
+}
+
+HRotation convertToHidl(int rotation) {
+    HRotation hRotation = HRotation::R0;
+    switch(rotation) {
+        case CAMERA3_STREAM_ROTATION_90:
+            hRotation = HRotation::R90;
+            break;
+        case CAMERA3_STREAM_ROTATION_180:
+            hRotation = HRotation::R180;
+            break;
+        case CAMERA3_STREAM_ROTATION_270:
+            hRotation = HRotation::R270;
+            break;
+        default:
+            break;
+    }
+    return hRotation;
+}
+
+bool convertFromHidlCloned(const HCameraMetadata &metadata, CameraMetadata *rawMetadata) {
+    const camera_metadata *buffer = (camera_metadata_t*)(metadata.data());
+    size_t expectedSize = metadata.size();
+    int ret = validate_camera_metadata_structure(buffer, &expectedSize);
+    if (ret == OK || ret == CAMERA_METADATA_VALIDATION_SHIFTED) {
+        *rawMetadata = buffer;
+    } else {
+        ALOGE("%s: Malformed camera metadata received from caller", __FUNCTION__);
+        return false;
+    }
+    return true;
+}
+
+// Note: existing data in dst will be gone. Caller still owns the memory of src
+void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst) {
+    if (src == nullptr) {
+        return;
+    }
+    size_t size = get_camera_metadata_size(src);
+    ALOGE("Converting metadata size: %d", (int)size);
+    dst->setToExternal((uint8_t *) src, size);
+    return;
+}
+
+TemplateId convertToHidl(ACameraDevice_request_template templateId) {
+    switch(templateId) {
+        case TEMPLATE_STILL_CAPTURE:
+            return TemplateId::STILL_CAPTURE;
+        case TEMPLATE_RECORD:
+            return TemplateId::RECORD;
+        case TEMPLATE_VIDEO_SNAPSHOT:
+            return TemplateId::VIDEO_SNAPSHOT;
+        case TEMPLATE_ZERO_SHUTTER_LAG:
+            return TemplateId::ZERO_SHUTTER_LAG;
+        case TEMPLATE_MANUAL:
+            return TemplateId::MANUAL;
+        default:
+            return TemplateId::PREVIEW;
+    }
+}
+
+camera_status_t convertFromHidl(Status status) {
+    camera_status_t ret = ACAMERA_OK;
+    switch(status) {
+        case Status::NO_ERROR:
+            break;
+        case Status::DISCONNECTED:
+            ret = ACAMERA_ERROR_CAMERA_DISCONNECTED;
+            break;
+        case Status::CAMERA_IN_USE:
+            ret = ACAMERA_ERROR_CAMERA_IN_USE;
+            break;
+        case Status::MAX_CAMERAS_IN_USE:
+            ret = ACAMERA_ERROR_MAX_CAMERA_IN_USE;
+            break;
+        case Status::ILLEGAL_ARGUMENT:
+            ret = ACAMERA_ERROR_INVALID_PARAMETER;
+            break;
+        case Status::DEPRECATED_HAL:
+            // Should not reach here since we filtered legacy HALs earlier
+            ret = ACAMERA_ERROR_INVALID_PARAMETER;
+            break;
+        case Status::DISABLED:
+            ret = ACAMERA_ERROR_CAMERA_DISABLED;
+            break;
+        case Status::PERMISSION_DENIED:
+            ret = ACAMERA_ERROR_PERMISSION_DENIED;
+            break;
+        case Status::INVALID_OPERATION:
+            ret = ACAMERA_ERROR_INVALID_OPERATION;
+            break;
+        default:
+            ret = ACAMERA_ERROR_UNKNOWN;
+            break;
+    }
+    return ret;
+}
+
+bool isWindowNativeHandleEqual(const native_handle_t *nh1, const native_handle_t *nh2) {
+    if (nh1->numFds !=0 || nh2->numFds !=0) {
+        ALOGE("Invalid window native handles being compared");
+        return false;
+    }
+    if (nh1->version != nh2->version || nh1->numFds != nh2->numFds ||
+        nh1->numInts != nh2->numInts) {
+        return false;
+    }
+    for (int i = 0; i < nh1->numInts; i++) {
+        if(nh1->data[i] != nh2->data[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool isWindowNativeHandleLessThan(const native_handle_t *nh1, const native_handle_t *nh2) {
+    if (isWindowNativeHandleEqual(nh1, nh2)) {
+        return false;
+    }
+    if (nh1->numInts != nh2->numInts) {
+        return nh1->numInts < nh2->numInts;
+    }
+
+    for (int i = 0; i < nh1->numInts; i++) {
+        if (nh1->data[i] != nh2->data[i]) {
+            return nh1->data[i] < nh2->data[i];
+        }
+    }
+    return false;
+}
+
+bool isWindowNativeHandleGreaterThan(const native_handle_t *nh1, const native_handle_t *nh2) {
+    return !isWindowNativeHandleLessThan(nh1, nh2) && !isWindowNativeHandleEqual(nh1, nh2);
+}
+
+bool areWindowNativeHandlesEqual(hidl_vec<hidl_handle> handles1, hidl_vec<hidl_handle> handles2) {
+    if (handles1.size() != handles2.size()) {
+        return false;
+    }
+    for (int i = 0; i < handles1.size(); i++) {
+        if (!isWindowNativeHandleEqual(handles1[i], handles2[i])) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool areWindowNativeHandlesLessThan(hidl_vec<hidl_handle> handles1, hidl_vec<hidl_handle>handles2) {
+    if (handles1.size() != handles2.size()) {
+        return handles1.size() < handles2.size();
+    }
+    for (int i = 0; i < handles1.size(); i++) {
+        const native_handle_t *handle1 = handles1[i].getNativeHandle();
+        const native_handle_t *handle2 = handles2[i].getNativeHandle();
+        if (!isWindowNativeHandleEqual(handle1, handle2)) {
+            return isWindowNativeHandleLessThan(handle1, handle2);
+        }
+    }
+    return false;
+}
+
+} // namespace utils
+} // namespace acam
+} // namespace android
diff --git a/camera/ndk/ndk_vendor/impl/utils.h b/camera/ndk/ndk_vendor/impl/utils.h
new file mode 100644
index 0000000..2f1006d
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/utils.h
@@ -0,0 +1,174 @@
+/*
+ * 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 <android/frameworks/cameraservice/service/2.0/ICameraService.h>
+#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceUser.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <camera/NdkCameraDevice.h>
+#include <CameraMetadata.h>
+#include <hardware/camera3.h>
+
+#ifndef CAMERA_NDK_VENDOR_H
+#define CAMERA_NDK_VENDOR_H
+
+using android::hardware::hidl_vec;
+using android::hardware::hidl_handle;
+
+namespace android {
+namespace acam {
+namespace utils {
+
+using CameraMetadata = hardware::camera::common::V1_0::helper::CameraMetadata;
+using HCameraMetadata  = frameworks::cameraservice::service::V2_0::CameraMetadata;
+using Status = frameworks::cameraservice::common::V2_0::Status;
+using TemplateId = frameworks::cameraservice::device::V2_0::TemplateId;
+using PhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings;
+using HRotation = frameworks::cameraservice::device::V2_0::OutputConfiguration::Rotation;
+using OutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration;
+
+// Utility class so that CaptureRequest can be stored by sp<>
+struct CaptureRequest : public RefBase {
+  frameworks::cameraservice::device::V2_0::CaptureRequest mCaptureRequest;
+  std::vector<native_handle_t *> mSurfaceList;
+  //Physical camera settings metadata is stored here, since the capture request
+  //might not contain it. That's since, fmq might have consumed it.
+  hidl_vec<PhysicalCameraSettings> mPhysicalCameraSettings;
+};
+
+bool areWindowNativeHandlesEqual(hidl_vec<hidl_handle> handles1, hidl_vec<hidl_handle>handles2);
+
+bool areWindowNativeHandlesLessThan(hidl_vec<hidl_handle> handles1, hidl_vec<hidl_handle>handles2);
+
+bool isWindowNativeHandleEqual(const native_handle_t *nh1, const native_handle_t *nh2);
+
+bool isWindowNativeHandleLessThan(const native_handle_t *nh1, const native_handle_t *nh2);
+
+// Convenience wrapper over isWindowNativeHandleLessThan and isWindowNativeHandleEqual
+bool isWindowNativeHandleGreaterThan(const native_handle_t *nh1, const native_handle_t *nh2);
+
+// Utility class so the native_handle_t can be compared with  its contents instead
+// of just raw pointer comparisons.
+struct native_handle_ptr_wrapper {
+    native_handle_t *mWindow = nullptr;
+
+    native_handle_ptr_wrapper(native_handle_t *nh) : mWindow(nh) { }
+
+    native_handle_ptr_wrapper() = default;
+
+    operator native_handle_t *() const { return mWindow; }
+
+    bool operator ==(const native_handle_ptr_wrapper other) const {
+        return isWindowNativeHandleEqual(mWindow, other.mWindow);
+    }
+
+    bool operator != (const native_handle_ptr_wrapper& other) const {
+        return !isWindowNativeHandleEqual(mWindow, other.mWindow);
+    }
+
+    bool operator < (const native_handle_ptr_wrapper& other) const {
+        return isWindowNativeHandleLessThan(mWindow, other.mWindow);
+    }
+
+    bool operator > (const native_handle_ptr_wrapper& other) const {
+        return !isWindowNativeHandleGreaterThan(mWindow, other.mWindow);
+    }
+
+};
+
+// Wrapper around OutputConfiguration. This is needed since HIDL
+// OutputConfiguration is auto-generated and marked final. Therefore, operator
+// overloads outside the class, will not get picked by clang while trying to
+// store OutputConfiguration in maps/sets.
+struct OutputConfigurationWrapper {
+    OutputConfiguration mOutputConfiguration;
+
+    operator const OutputConfiguration &() const {
+        return mOutputConfiguration;
+    }
+
+    OutputConfigurationWrapper() = default;
+
+    OutputConfigurationWrapper(OutputConfiguration &outputConfiguration)
+            : mOutputConfiguration((outputConfiguration)) { }
+
+    bool operator ==(const OutputConfiguration &other) const {
+        const OutputConfiguration &self = mOutputConfiguration;
+        return self.rotation == other.rotation && self.windowGroupId == other.windowGroupId &&
+                self.physicalCameraId == other.physicalCameraId && self.width == other.width &&
+                self.height == other.height && self.isDeferred == other.isDeferred &&
+                areWindowNativeHandlesEqual(self.windowHandles, other.windowHandles);
+    }
+
+    bool operator < (const OutputConfiguration &other) const {
+        if (*this == other) {
+            return false;
+        }
+        const OutputConfiguration &self = mOutputConfiguration;
+        if (self.windowGroupId != other.windowGroupId) {
+            return self.windowGroupId < other.windowGroupId;
+        }
+
+        if (self.width != other.width) {
+            return self.width < other.width;
+        }
+
+        if (self.height != other.height) {
+            return self.height < other.height;
+        }
+
+        if (self.rotation != other.rotation) {
+            return static_cast<uint32_t>(self.rotation) < static_cast<uint32_t>(other.rotation);
+        }
+
+        if (self.isDeferred != other.isDeferred) {
+            return self.isDeferred < other.isDeferred;
+        }
+
+        if (self.physicalCameraId != other.physicalCameraId) {
+            return self.physicalCameraId < other.physicalCameraId;
+        }
+        return areWindowNativeHandlesLessThan(self.windowHandles, other.windowHandles);
+    }
+
+    bool operator != (const OutputConfiguration &other) const {
+        return !(*this == other);
+    }
+
+    bool operator > (const OutputConfiguration &other) const {
+        return (*this != other) && !(*this < other);
+    }
+};
+
+// Convert CaptureRequest wrappable by sp<> to hidl CaptureRequest.
+frameworks::cameraservice::device::V2_0::CaptureRequest convertToHidl(
+    const CaptureRequest *captureRequest);
+
+HRotation convertToHidl(int rotation);
+
+bool convertFromHidlCloned(const HCameraMetadata &metadata, CameraMetadata *rawMetadata);
+
+// Note: existing data in dst will be gone. Caller still owns the memory of src
+void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst);
+
+TemplateId convertToHidl(ACameraDevice_request_template templateId);
+
+camera_status_t convertFromHidl(Status status);
+
+} // namespace utils
+} // namespace acam
+} // namespace android
+
+#endif // CAMERA_NDK_VENDOR_H
diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
new file mode 100644
index 0000000..b1e501b
--- /dev/null
+++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
@@ -0,0 +1,525 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AImageReaderVendorTest"
+//#define LOG_NDEBUG 0
+
+#include <stdint.h>
+#include <unistd.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <mutex>
+#include <string>
+#include <vector>
+#include <stdio.h>
+#include <stdio.h>
+#include <stdio.h>
+
+#include <android/log.h>
+#include <camera/NdkCameraError.h>
+#include <camera/NdkCameraManager.h>
+#include <camera/NdkCameraDevice.h>
+#include <camera/NdkCameraCaptureSession.h>
+#include <media/NdkImage.h>
+#include <media/NdkImageReader.h>
+#include <cutils/native_handle.h>
+
+//#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+//#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+namespace {
+
+static constexpr int kDummyFenceFd = -1;
+static constexpr int kCaptureWaitUs = 100 * 1000;
+static constexpr int kCaptureWaitRetry = 10;
+static constexpr int kTestImageWidth = 640;
+static constexpr int kTestImageHeight = 480;
+static constexpr int kTestImageFormat = AIMAGE_FORMAT_YUV_420_888;
+
+class CameraHelper {
+   public:
+    CameraHelper(native_handle_t* imgReaderAnw) : mImgReaderAnw(imgReaderAnw) {}
+    ~CameraHelper() { closeCamera(); }
+
+    int initCamera() {
+        if (mImgReaderAnw == nullptr) {
+            ALOGE("Cannot initialize camera before image reader get initialized.");
+            return -1;
+        }
+        int ret;
+
+        mCameraManager = ACameraManager_create();
+        if (mCameraManager == nullptr) {
+            ALOGE("Failed to create ACameraManager.");
+            return -1;
+        }
+
+        ret = ACameraManager_getCameraIdList(mCameraManager, &mCameraIdList);
+        if (ret != AMEDIA_OK) {
+            ALOGE("Failed to get cameraIdList: ret=%d", ret);
+            return ret;
+        }
+        if (mCameraIdList->numCameras < 1) {
+            ALOGW("Device has no camera on board.");
+            return 0;
+        }
+
+        // We always use the first camera.
+        mCameraId = mCameraIdList->cameraIds[0];
+        if (mCameraId == nullptr) {
+            ALOGE("Failed to get cameraId.");
+            return -1;
+        }
+
+        ret = ACameraManager_openCamera(mCameraManager, mCameraId, &mDeviceCb, &mDevice);
+        if (ret != AMEDIA_OK || mDevice == nullptr) {
+            ALOGE("Failed to open camera, ret=%d, mDevice=%p.", ret, mDevice);
+            return -1;
+        }
+
+        ret = ACameraManager_getCameraCharacteristics(mCameraManager, mCameraId, &mCameraMetadata);
+        if (ret != ACAMERA_OK || mCameraMetadata == nullptr) {
+            ALOGE("Get camera %s characteristics failure. ret %d, metadata %p", mCameraId, ret,
+                  mCameraMetadata);
+            return -1;
+        }
+
+        if (!isCapabilitySupported(ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)) {
+            ALOGW("Camera does not support BACKWARD_COMPATIBLE.");
+            return 0;
+        }
+
+        // Create capture session
+        ret = ACaptureSessionOutputContainer_create(&mOutputs);
+        if (ret != AMEDIA_OK) {
+            ALOGE("ACaptureSessionOutputContainer_create failed, ret=%d", ret);
+            return ret;
+        }
+        ret = ACaptureSessionOutput_create(mImgReaderAnw, &mImgReaderOutput);
+        if (ret != AMEDIA_OK) {
+            ALOGE("ACaptureSessionOutput_create failed, ret=%d", ret);
+            return ret;
+        }
+        ret = ACaptureSessionOutputContainer_add(mOutputs, mImgReaderOutput);
+        if (ret != AMEDIA_OK) {
+            ALOGE("ACaptureSessionOutputContainer_add failed, ret=%d", ret);
+            return ret;
+        }
+        ret = ACameraDevice_createCaptureSession(mDevice, mOutputs, &mSessionCb, &mSession);
+        if (ret != AMEDIA_OK) {
+            ALOGE("ACameraDevice_createCaptureSession failed, ret=%d", ret);
+            return ret;
+        }
+
+        // Create capture request
+        ret = ACameraDevice_createCaptureRequest(mDevice, TEMPLATE_STILL_CAPTURE, &mStillRequest);
+        if (ret != AMEDIA_OK) {
+            ALOGE("ACameraDevice_createCaptureRequest failed, ret=%d", ret);
+            return ret;
+        }
+        ret = ACameraOutputTarget_create(mImgReaderAnw, &mReqImgReaderOutput);
+        if (ret != AMEDIA_OK) {
+            ALOGE("ACameraOutputTarget_create failed, ret=%d", ret);
+            return ret;
+        }
+        ret = ACaptureRequest_addTarget(mStillRequest, mReqImgReaderOutput);
+        if (ret != AMEDIA_OK) {
+            ALOGE("ACaptureRequest_addTarget failed, ret=%d", ret);
+            return ret;
+        }
+
+        mIsCameraReady = true;
+        return 0;
+    }
+
+    bool isCapabilitySupported(acamera_metadata_enum_android_request_available_capabilities_t cap) {
+        ACameraMetadata_const_entry entry;
+        ACameraMetadata_getConstEntry(
+                mCameraMetadata, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry);
+        for (uint32_t i = 0; i < entry.count; i++) {
+            if (entry.data.u8[i] == cap) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool isCameraReady() { return mIsCameraReady; }
+
+    void closeCamera() {
+        // Destroy capture request
+        if (mReqImgReaderOutput) {
+            ACameraOutputTarget_free(mReqImgReaderOutput);
+            mReqImgReaderOutput = nullptr;
+        }
+        if (mStillRequest) {
+            ACaptureRequest_free(mStillRequest);
+            mStillRequest = nullptr;
+        }
+        // Destroy capture session
+        if (mSession != nullptr) {
+            ACameraCaptureSession_close(mSession);
+            mSession = nullptr;
+        }
+        if (mImgReaderOutput) {
+            ACaptureSessionOutput_free(mImgReaderOutput);
+            mImgReaderOutput = nullptr;
+        }
+        if (mOutputs) {
+            ACaptureSessionOutputContainer_free(mOutputs);
+            mOutputs = nullptr;
+        }
+        // Destroy camera device
+        if (mDevice) {
+            ACameraDevice_close(mDevice);
+            mDevice = nullptr;
+        }
+        if (mCameraMetadata) {
+          ACameraMetadata_free(mCameraMetadata);
+          mCameraMetadata = nullptr;
+        }
+        // Destroy camera manager
+        if (mCameraIdList) {
+            ACameraManager_deleteCameraIdList(mCameraIdList);
+            mCameraIdList = nullptr;
+        }
+        if (mCameraManager) {
+            ACameraManager_delete(mCameraManager);
+            mCameraManager = nullptr;
+        }
+        mIsCameraReady = false;
+    }
+
+    int takePicture() {
+        int seqId;
+        return ACameraCaptureSession_capture(mSession, nullptr, 1, &mStillRequest, &seqId);
+    }
+
+    static void onDeviceDisconnected(void* /*obj*/, ACameraDevice* /*device*/) {}
+
+    static void onDeviceError(void* /*obj*/, ACameraDevice* /*device*/, int /*errorCode*/) {}
+
+    static void onSessionClosed(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
+
+    static void onSessionReady(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
+
+    static void onSessionActive(void* /*obj*/, ACameraCaptureSession* /*session*/) {}
+
+   private:
+    ACameraDevice_StateCallbacks mDeviceCb{this, onDeviceDisconnected,
+                                           onDeviceError};
+    ACameraCaptureSession_stateCallbacks mSessionCb{
+        this, onSessionClosed, onSessionReady, onSessionActive};
+
+    native_handle_t* mImgReaderAnw = nullptr;  // not owned by us.
+
+    // Camera manager
+    ACameraManager* mCameraManager = nullptr;
+    ACameraIdList* mCameraIdList = nullptr;
+    // Camera device
+    ACameraMetadata* mCameraMetadata = nullptr;
+    ACameraDevice* mDevice = nullptr;
+    // Capture session
+    ACaptureSessionOutputContainer* mOutputs = nullptr;
+    ACaptureSessionOutput* mImgReaderOutput = nullptr;
+    ACameraCaptureSession* mSession = nullptr;
+    // Capture request
+    ACaptureRequest* mStillRequest = nullptr;
+    ACameraOutputTarget* mReqImgReaderOutput = nullptr;
+
+    bool mIsCameraReady = false;
+    const char* mCameraId;
+};
+
+class ImageReaderTestCase {
+   public:
+    ImageReaderTestCase(int32_t width,
+                        int32_t height,
+                        int32_t format,
+                        uint64_t usage,
+                        int32_t maxImages,
+                        bool async)
+        : mWidth(width),
+          mHeight(height),
+          mFormat(format),
+          mUsage(usage),
+          mMaxImages(maxImages),
+          mAsync(async) {}
+
+    ~ImageReaderTestCase() {
+        if (mImgReaderAnw) {
+            AImageReader_delete(mImgReader);
+            // No need to call native_handle_t_release on imageReaderAnw
+        }
+    }
+
+    int initImageReader() {
+        if (mImgReader != nullptr || mImgReaderAnw != nullptr) {
+            ALOGE("Cannot re-initalize image reader, mImgReader=%p, mImgReaderAnw=%p", mImgReader,
+                  mImgReaderAnw);
+            return -1;
+        }
+
+        media_status_t ret = AImageReader_newWithUsage(
+                mWidth, mHeight, mFormat, mUsage, mMaxImages, &mImgReader);
+        if (ret != AMEDIA_OK || mImgReader == nullptr) {
+            ALOGE("Failed to create new AImageReader, ret=%d, mImgReader=%p", ret, mImgReader);
+            return -1;
+        }
+
+        ret = AImageReader_setImageListener(mImgReader, &mReaderAvailableCb);
+        if (ret != AMEDIA_OK) {
+            ALOGE("Failed to set image available listener, ret=%d.", ret);
+            return ret;
+        }
+
+        ret = AImageReader_setBufferRemovedListener(mImgReader, &mReaderDetachedCb);
+        if (ret != AMEDIA_OK) {
+            ALOGE("Failed to set buffer detaching listener, ret=%d.", ret);
+            return ret;
+        }
+
+        ret = AImageReader_getWindowNativeHandle(mImgReader, &mImgReaderAnw);
+        if (ret != AMEDIA_OK || mImgReaderAnw == nullptr) {
+            ALOGE("Failed to get native_handle_t from AImageReader, ret=%d, mImgReaderAnw=%p.", ret,
+                  mImgReaderAnw);
+            return -1;
+        }
+
+        return 0;
+    }
+
+    native_handle_t* getNativeWindow() { return mImgReaderAnw; }
+
+    int getAcquiredImageCount() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mAcquiredImageCount;
+    }
+
+    void HandleImageAvailable(AImageReader* reader) {
+        std::lock_guard<std::mutex> lock(mMutex);
+
+        AImage* outImage = nullptr;
+        media_status_t ret;
+
+        // Make sure AImage will be deleted automatically when it goes out of
+        // scope.
+        auto imageDeleter = [this](AImage* img) {
+            if (mAsync) {
+                AImage_deleteAsync(img, kDummyFenceFd);
+            } else {
+                AImage_delete(img);
+            }
+        };
+        std::unique_ptr<AImage, decltype(imageDeleter)> img(nullptr, imageDeleter);
+
+        if (mAsync) {
+            int outFenceFd = 0;
+            // Verity that outFenceFd's value will be changed by
+            // AImageReader_acquireNextImageAsync.
+            ret = AImageReader_acquireNextImageAsync(reader, &outImage, &outFenceFd);
+            if (ret != AMEDIA_OK || outImage == nullptr || outFenceFd == 0) {
+                ALOGE("Failed to acquire image, ret=%d, outIamge=%p, outFenceFd=%d.", ret, outImage,
+                      outFenceFd);
+                return;
+            }
+            img.reset(outImage);
+        } else {
+            ret = AImageReader_acquireNextImage(reader, &outImage);
+            if (ret != AMEDIA_OK || outImage == nullptr) {
+                ALOGE("Failed to acquire image, ret=%d, outIamge=%p.", ret, outImage);
+                return;
+            }
+            img.reset(outImage);
+        }
+
+        AHardwareBuffer* outBuffer = nullptr;
+        ret = AImage_getHardwareBuffer(img.get(), &outBuffer);
+        if (ret != AMEDIA_OK || outBuffer == nullptr) {
+            ALOGE("Faild to get hardware buffer, ret=%d, outBuffer=%p.", ret, outBuffer);
+            return;
+        }
+
+        // No need to release AHardwareBuffer, since we don't acquire additional
+        // reference to it.
+        AHardwareBuffer_Desc outDesc;
+        AHardwareBuffer_describe(outBuffer, &outDesc);
+        int32_t imageWidth = 0;
+        int32_t imageHeight = 0;
+        int32_t bufferWidth = static_cast<int32_t>(outDesc.width);
+        int32_t bufferHeight = static_cast<int32_t>(outDesc.height);
+
+        AImage_getWidth(outImage, &imageWidth);
+        AImage_getHeight(outImage, &imageHeight);
+        if (imageWidth != mWidth || imageHeight != mHeight) {
+            ALOGE("Mismatched output image dimension: expected=%dx%d, actual=%dx%d", mWidth,
+                  mHeight, imageWidth, imageHeight);
+            return;
+        }
+
+        if (mFormat == AIMAGE_FORMAT_RGBA_8888 ||
+            mFormat == AIMAGE_FORMAT_RGBX_8888 ||
+            mFormat == AIMAGE_FORMAT_RGB_888 ||
+            mFormat == AIMAGE_FORMAT_RGB_565 ||
+            mFormat == AIMAGE_FORMAT_RGBA_FP16 ||
+            mFormat == AIMAGE_FORMAT_YUV_420_888 ||
+            mFormat == AIMAGE_FORMAT_Y8) {
+            // Check output buffer dimension for certain formats. Don't do this for blob based
+            // formats.
+            if (bufferWidth != mWidth || bufferHeight != mHeight) {
+                ALOGE("Mismatched output buffer dimension: expected=%dx%d, actual=%dx%d", mWidth,
+                      mHeight, bufferWidth, bufferHeight);
+                return;
+            }
+        }
+
+        if ((outDesc.usage & mUsage) != mUsage) {
+            ALOGE("Mismatched output buffer usage: actual (%" PRIu64 "), expected (%" PRIu64 ")",
+                  outDesc.usage, mUsage);
+            return;
+        }
+
+        uint8_t* data = nullptr;
+        int dataLength = 0;
+        ret = AImage_getPlaneData(img.get(), 0, &data, &dataLength);
+        if (mUsage & AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN) {
+            // When we have CPU_READ_OFTEN usage bits, we can lock the image.
+            if (ret != AMEDIA_OK || data == nullptr || dataLength < 0) {
+                ALOGE("Failed to access CPU data, ret=%d, data=%p, dataLength=%d", ret, data,
+                      dataLength);
+                return;
+            }
+        } else {
+            if (ret != AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE || data != nullptr || dataLength != 0) {
+                ALOGE("Shouldn't be able to access CPU data, ret=%d, data=%p, dataLength=%d", ret,
+                      data, dataLength);
+                return;
+            }
+        }
+        // Only increase mAcquiredImageCount if all checks pass.
+        mAcquiredImageCount++;
+    }
+
+    static void onImageAvailable(void* obj, AImageReader* reader) {
+        ImageReaderTestCase* thiz = reinterpret_cast<ImageReaderTestCase*>(obj);
+        thiz->HandleImageAvailable(reader);
+    }
+
+    static void
+    onBufferRemoved(void* /*obj*/, AImageReader* /*reader*/, AHardwareBuffer* /*buffer*/) {
+        // No-op, just to check the listener can be set properly.
+    }
+
+   private:
+    int32_t mWidth;
+    int32_t mHeight;
+    int32_t mFormat;
+    uint64_t mUsage;
+    int32_t mMaxImages;
+    bool mAsync;
+
+    std::mutex mMutex;
+    int mAcquiredImageCount{0};
+
+    AImageReader* mImgReader = nullptr;
+    native_handle_t* mImgReaderAnw = nullptr;
+
+    AImageReader_ImageListener mReaderAvailableCb{this, onImageAvailable};
+    AImageReader_BufferRemovedListener mReaderDetachedCb{this, onBufferRemoved};
+};
+
+int takePictures(uint64_t readerUsage, int readerMaxImages, bool readerAsync, int pictureCount) {
+    int ret = 0;
+
+    ImageReaderTestCase testCase(
+            kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage, readerMaxImages,
+            readerAsync);
+    ret = testCase.initImageReader();
+    if (ret < 0) {
+        return ret;
+    }
+
+    CameraHelper cameraHelper(testCase.getNativeWindow());
+    ret = cameraHelper.initCamera();
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (!cameraHelper.isCameraReady()) {
+        ALOGW("Camera is not ready after successful initialization. It's either due to camera on "
+              "board lacks BACKWARDS_COMPATIBLE capability or the device does not have camera on "
+              "board.");
+        return 0;
+    }
+
+    for (int i = 0; i < pictureCount; i++) {
+        ret = cameraHelper.takePicture();
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    // Sleep until all capture finished
+    for (int i = 0; i < kCaptureWaitRetry * pictureCount; i++) {
+        usleep(kCaptureWaitUs);
+        if (testCase.getAcquiredImageCount() == pictureCount) {
+            ALOGI("Session take ~%d ms to capture %d images", i * kCaptureWaitUs / 1000,
+                  pictureCount);
+            break;
+        }
+    }
+    return testCase.getAcquiredImageCount() == pictureCount ? 0 : -1;
+}
+
+class AImageReaderWindowHandleTest : public ::testing::Test {
+   public:
+    void SetUp() override {
+    }
+    void TearDown() override {
+
+    }
+
+};
+
+bool testTakePicturesNative() {
+    for (auto& readerUsage :
+         {AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}) {
+        for (auto& readerMaxImages : {1, 4, 8}) {
+            for (auto& readerAsync : {true, false}) {
+                for (auto& pictureCount : {1, 4, 8}) {
+                    if (takePictures(readerUsage, readerMaxImages, readerAsync, pictureCount)) {
+                        ALOGE("Test takePictures failed for test case usage=%" PRIu64 ", maxImages=%d, "
+                              "async=%d, pictureCount=%d",
+                              readerUsage, readerMaxImages, readerAsync, pictureCount);
+                        return false;
+                    }
+                }
+            }
+        }
+    }
+    return true;
+}
+
+
+TEST_F(AImageReaderWindowHandleTest, CreateWindowNativeHandle) {
+    EXPECT_TRUE(testTakePicturesNative());
+}
+
+}  // namespace
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 7d3fbea..7803ccc 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -191,7 +191,7 @@
     format->setFloat("frame-rate", displayFps);
     format->setInt32("i-frame-interval", 10);
 
-    sp<ALooper> looper = new ALooper;
+    sp<android::ALooper> looper = new android::ALooper;
     looper->setName("screenrecord_looper");
     looper->start();
     ALOGV("Creating codec");
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index 980d1d2..a463ec5 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -138,7 +138,7 @@
 
     CHECK(!stateByTrack.isEmpty());
 
-    int64_t startTimeUs = ALooper::GetNowUs();
+    int64_t startTimeUs = android::ALooper::GetNowUs();
     int64_t startTimeRender = -1;
 
     for (size_t i = 0; i < stateByTrack.size(); ++i) {
@@ -307,7 +307,7 @@
         }
     }
 
-    int64_t elapsedTimeUs = ALooper::GetNowUs() - startTimeUs;
+    int64_t elapsedTimeUs = android::ALooper::GetNowUs() - startTimeUs;
 
     for (size_t i = 0; i < stateByTrack.size(); ++i) {
         CodecState *state = &stateByTrack.editValueAt(i);
@@ -400,7 +400,7 @@
 
     ProcessState::self()->startThreadPool();
 
-    sp<ALooper> looper = new ALooper;
+    sp<android::ALooper> looper = new android::ALooper;
     looper->start();
 
     sp<SurfaceComposerClient> composerClient;
diff --git a/cmds/stagefright/mediafilter.cpp b/cmds/stagefright/mediafilter.cpp
index 630de25..f0ee0e1 100644
--- a/cmds/stagefright/mediafilter.cpp
+++ b/cmds/stagefright/mediafilter.cpp
@@ -310,7 +310,7 @@
 }
 
 static int decode(
-        const sp<ALooper> &looper,
+        const sp<android::ALooper> &looper,
         const char *path,
         const sp<Surface> &surface,
         bool renderSurface,
@@ -465,7 +465,7 @@
     filterState->mSignalledInputEOS = false;
     filterState->mSawOutputEOS = false;
 
-    int64_t startTimeUs = ALooper::GetNowUs();
+    int64_t startTimeUs = android::ALooper::GetNowUs();
     int64_t startTimeRender = -1;
 
     for (size_t i = 0; i < stateByTrack.size(); ++i) {
@@ -643,7 +643,7 @@
                 useTimestamp, &startTimeRender);
     }
 
-    int64_t elapsedTimeUs = ALooper::GetNowUs() - startTimeUs;
+    int64_t elapsedTimeUs = android::ALooper::GetNowUs() - startTimeUs;
 
     for (size_t i = 0; i < stateByTrack.size(); ++i) {
         CodecState *state = &stateByTrack.editValueAt(i);
@@ -737,7 +737,7 @@
 
     ProcessState::self()->startThreadPool();
 
-    android::sp<ALooper> looper = new ALooper;
+    android::sp<android::ALooper> looper = new android::ALooper;
     looper->start();
 
     android::sp<SurfaceComposerClient> composerClient;
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 0d331df..28a218c 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -716,7 +716,7 @@
     gWriteMP4 = false;
     gDisplayHistogram = false;
 
-    sp<ALooper> looper;
+    sp<android::ALooper> looper;
 
     int res;
     while ((res = getopt(argc, argv, "vhaqn:lm:b:ptsrow:kN:xSTd:D:")) >= 0) {
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 4991e50..94f9e02 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -32,6 +32,7 @@
         "libutils",
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
         "libhidlallocatorutils",
         "libhidlbase",
         "libhidltransport",
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index ce9dc38..14ff493 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -23,7 +23,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 
-#include <android/hardware/drm/1.0/types.h>
+#include <android/hardware/drm/1.2/types.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <hidl/ServiceManagement.h>
 
@@ -43,12 +43,13 @@
 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::SecureStopRelease;
 using drm::V1_0::SecureStopId;
-using drm::V1_1::SecurityLevel;
 using drm::V1_0::Status;
+using drm::V1_1::HdcpLevel;
+using drm::V1_1::SecureStopRelease;
+using drm::V1_1::SecurityLevel;
+using drm::V1_2::KeySetId;
 using ::android::hardware::drm::V1_1::DrmMetricGroup;
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_string;
@@ -139,6 +140,18 @@
     }
 }
 
+static DrmPlugin::OfflineLicenseState toOfflineLicenseState(
+        OfflineLicenseState licenseState) {
+    switch(licenseState) {
+    case OfflineLicenseState::USABLE:
+        return DrmPlugin::kOfflineLicenseStateUsable;
+    case OfflineLicenseState::INACTIVE:
+        return DrmPlugin::kOfflineLicenseStateInactive;
+    default:
+        return DrmPlugin::kOfflineLicenseStateUnknown;
+    }
+}
+
 static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel level) {
     switch(level) {
     case HdcpLevel::HDCP_NONE:
@@ -199,6 +212,15 @@
     return secureStopIds;
 }
 
+static List<Vector<uint8_t>> toKeySetIds(const hidl_vec<KeySetId>&
+        hKeySetIds) {
+    List<Vector<uint8_t>> keySetIds;
+    for (size_t i = 0; i < hKeySetIds.size(); i++) {
+        keySetIds.push_back(toVector(hKeySetIds[i]));
+    }
+    return keySetIds;
+}
+
 static status_t toStatusT(Status status) {
     switch (status) {
     case Status::OK:
@@ -305,6 +327,7 @@
     }
     mPlugin.clear();
     mPluginV1_1.clear();
+    mPluginV1_2.clear();
 }
 
 Vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
@@ -333,6 +356,16 @@
                     }
                 }
             );
+        manager->listByInterface(drm::V1_2::IDrmFactory::descriptor,
+                [&factories](const hidl_vec<hidl_string> &registered) {
+                    for (const auto &instance : registered) {
+                        auto factory = drm::V1_2::IDrmFactory::getService(instance);
+                        if (factory != NULL) {
+                            factories.push_back(factory);
+                        }
+                    }
+                }
+            );
     }
 
     if (factories.size() == 0) {
@@ -525,6 +558,7 @@
             mPlugin = makeDrmPlugin(mFactories[i], uuid, appPackageName);
             if (mPlugin != NULL) {
                 mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin);
+                mPluginV1_2 = drm::V1_2::IDrmPlugin::castFrom(mPlugin);
             }
         }
     }
@@ -1063,6 +1097,73 @@
     return hResult.isOk() ? err : DEAD_OBJECT;
 }
 
+status_t DrmHal::getOfflineLicenseKeySetIds(List<Vector<uint8_t>> &keySetIds) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPluginV1_2 == NULL) {
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult = mPluginV1_2->getOfflineLicenseKeySetIds(
+            [&](Status status, const hidl_vec<KeySetId>& hKeySetIds) {
+                if (status == Status::OK) {
+                    keySetIds = toKeySetIds(hKeySetIds);
+                }
+                err = toStatusT(status);
+            }
+    );
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHal::removeOfflineLicense(Vector<uint8_t> const &keySetId) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPluginV1_2 == NULL) {
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    Return<Status> status = mPluginV1_2->removeOfflineLicense(toHidlVec(keySetId));
+    return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
+}
+
+status_t DrmHal::getOfflineLicenseState(Vector<uint8_t> const &keySetId,
+        DrmPlugin::OfflineLicenseState *licenseState) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPluginV1_2 == NULL) {
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+    *licenseState = DrmPlugin::kOfflineLicenseStateUnknown;
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult = mPluginV1_2->getOfflineLicenseState(toHidlVec(keySetId),
+            [&](Status status, OfflineLicenseState hLicenseState) {
+                if (status == Status::OK) {
+                    *licenseState = toOfflineLicenseState(hLicenseState);
+                }
+                err = toStatusT(status);
+            }
+    );
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
 status_t DrmHal::getPropertyString(String8 const &name, String8 &value ) const {
     Mutex::Autolock autoLock(mLock);
     return getPropertyStringInternal(name, value);
diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp
index 509961f..8c26317 100644
--- a/drm/libmediadrm/IDrm.cpp
+++ b/drm/libmediadrm/IDrm.cpp
@@ -61,7 +61,10 @@
     GET_NUMBER_OF_SESSIONS,
     GET_SECURITY_LEVEL,
     REMOVE_SECURE_STOP,
-    GET_SECURE_STOP_IDS
+    GET_SECURE_STOP_IDS,
+    GET_OFFLINE_LICENSE_KEYSET_IDS,
+    REMOVE_OFFLINE_LICENSE,
+    GET_OFFLINE_LICENSE_STATE
 };
 
 struct BpDrm : public BpInterface<IDrm> {
@@ -376,6 +379,52 @@
         return reply.readInt32();
     }
 
+    virtual status_t getOfflineLicenseKeySetIds(List<Vector<uint8_t> > &keySetIds) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        status_t status = remote()->transact(GET_OFFLINE_LICENSE_KEYSET_IDS, data, &reply);
+        if (status != OK) {
+            return status;
+        }
+
+        keySetIds.clear();
+        uint32_t count = reply.readInt32();
+        for (size_t i = 0; i < count; i++) {
+            Vector<uint8_t> keySetId;
+            readVector(reply, keySetId);
+            keySetIds.push_back(keySetId);
+        }
+        return reply.readInt32();
+    }
+
+    virtual status_t removeOfflineLicense(Vector<uint8_t> const &keySetId) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, keySetId);
+        status_t status = remote()->transact(REMOVE_OFFLINE_LICENSE, data, &reply);
+        if (status != OK) {
+            return status;
+        }
+        return reply.readInt32();
+    }
+
+    virtual status_t getOfflineLicenseState(Vector<uint8_t> const &keySetId,
+            DrmPlugin::OfflineLicenseState *licenseState) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        writeVector(data, keySetId);
+        status_t status = remote()->transact(GET_OFFLINE_LICENSE_STATE, data, &reply);
+        if (status != OK) {
+            *licenseState = DrmPlugin::OfflineLicenseState::kOfflineLicenseStateUnknown;
+            return status;
+        }
+        *licenseState = static_cast<DrmPlugin::OfflineLicenseState>(reply.readInt32());
+        return reply.readInt32();
+    }
+
     virtual status_t getPropertyString(String8 const &name, String8 &value) const {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
@@ -980,6 +1029,45 @@
             return OK;
         }
 
+        case GET_OFFLINE_LICENSE_KEYSET_IDS:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            List<Vector<uint8_t> > keySetIds;
+            status_t result = getOfflineLicenseKeySetIds(keySetIds);
+            size_t count = keySetIds.size();
+            reply->writeInt32(count);
+            List<Vector<uint8_t> >::iterator iter = keySetIds.begin();
+            while(iter != keySetIds.end()) {
+                size_t size = iter->size();
+                reply->writeInt32(size);
+                reply->write(iter->array(), iter->size());
+                iter++;
+            }
+            reply->writeInt32(result);
+            return OK;
+        }
+
+        case REMOVE_OFFLINE_LICENSE:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> keySetId;
+            readVector(data, keySetId);
+            reply->writeInt32(removeOfflineLicense(keySetId));
+            return OK;
+        }
+
+        case GET_OFFLINE_LICENSE_STATE:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            Vector<uint8_t> keySetId;
+            readVector(data, keySetId);
+            DrmPlugin::OfflineLicenseState state;
+            status_t result = getOfflineLicenseState(keySetId, &state);
+            reply->writeInt32(static_cast<DrmPlugin::OfflineLicenseState>(state));
+            reply->writeInt32(result);
+            return OK;
+        }
+
         case GET_PROPERTY_STRING:
         {
             CHECK_INTERFACE(IDrm, data, reply);
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
index 66c906f..dcd59b7 100644
--- a/drm/libmediadrm/tests/Android.bp
+++ b/drm/libmediadrm/tests/Android.bp
@@ -17,6 +17,7 @@
     shared_libs: [
       "android.hardware.drm@1.0",
       "android.hardware.drm@1.1",
+      "android.hardware.drm@1.2",
       "libbinder",
       "libhidlbase",
       "liblog",
diff --git a/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp
index 2fce0790..0ac879c 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp
@@ -26,7 +26,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::SubSample;
@@ -79,7 +79,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
index edf818e..b44a6c7 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
@@ -15,7 +15,7 @@
 //
 
 cc_binary {
-    name: "android.hardware.drm@1.1-service.clearkey",
+    name: "android.hardware.drm@1.2-service.clearkey",
     vendor: true,
 
     srcs: [
@@ -39,11 +39,12 @@
     relative_install_path: "hw",
 
     cflags: ["-Wall", "-Werror"],
-    init_rc: ["android.hardware.drm@1.1-service.clearkey.rc"],
+    init_rc: ["android.hardware.drm@1.2-service.clearkey.rc"],
 
     shared_libs: [
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
         "libbase",
         "libbinder",
         "libcrypto",
@@ -69,7 +70,6 @@
         integer_overflow: true,
     },
 }
-
 cc_library_static {
     name: "libclearkeydevicefiles-protos",
     vendor: true,
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp
index c2ed751..657a42f 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp
@@ -21,7 +21,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 sp<Buffer> decodeBase64(const std::string &s) {
@@ -169,7 +169,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp
index e58f58a..75f8395 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp
@@ -21,7 +21,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 Buffer::Buffer(size_t capacity)
@@ -47,7 +47,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp
index 1ba5c6a..1410d77 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp
@@ -22,7 +22,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 extern "C" {
@@ -38,7 +38,7 @@
 } // extern "C"
 
 }  // namespace clearkey
-}  // namespace V1_1
+}  // namespace V1_2
 }  // namespace drm
 }  // namespace hardware
 }  // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp
index 0848cef..2a48db6 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp
@@ -27,7 +27,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 Return<bool> CryptoFactory::isCryptoSchemeSupported(
@@ -60,7 +60,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
index f33f94e..a488f86 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
@@ -27,7 +27,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::BufferType;
@@ -179,7 +179,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp b/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp
index e23502c..2415b6f 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp
@@ -13,11 +13,11 @@
 #include <openssl/sha.h>
 
 // Protobuf generated classes.
-using android::hardware::drm::V1_1::clearkey::OfflineFile;
-using android::hardware::drm::V1_1::clearkey::HashedFile;
-using android::hardware::drm::V1_1::clearkey::License;
-using android::hardware::drm::V1_1::clearkey::License_LicenseState_ACTIVE;
-using android::hardware::drm::V1_1::clearkey::License_LicenseState_RELEASING;
+using android::hardware::drm::V1_2::clearkey::OfflineFile;
+using android::hardware::drm::V1_2::clearkey::HashedFile;
+using android::hardware::drm::V1_2::clearkey::License;
+using android::hardware::drm::V1_2::clearkey::License_LicenseState_ACTIVE;
+using android::hardware::drm::V1_2::clearkey::License_LicenseState_RELEASING;
 
 namespace {
 const char kLicenseFileNameExt[] = ".lic";
@@ -38,7 +38,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 bool DeviceFiles::StoreLicense(
@@ -158,6 +158,15 @@
     return mFileHandle.FileExists(keySetId + kLicenseFileNameExt);
 }
 
+std::vector<std::string> DeviceFiles::ListLicenses() const {
+    std::vector<std::string> licenses = mFileHandle.ListFiles();
+    for (size_t i = 0; i < licenses.size(); i++) {
+        std::string& license = licenses[i];
+        license = license.substr(0, license.size() - strlen(kLicenseFileNameExt));
+    }
+    return licenses;
+}
+
 bool DeviceFiles::RetrieveHashedFile(const std::string& fileName, OfflineFile* deSerializedFile) {
     if (!deSerializedFile) {
         ALOGE("RetrieveHashedFile: invalid file parameter");
@@ -237,7 +246,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
index 77557f9..9d040a8 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
@@ -30,7 +30,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::Status;
@@ -71,7 +71,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index 55cbcf9..7184b53 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -58,9 +58,12 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
+using ::android::hardware::drm::V1_2::KeySetId;
+using ::android::hardware::drm::V1_2::OfflineLicenseState;
+
 DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary)
         : mSessionLibrary(sessionLibrary),
           mOpenSessionOkCount(0),
@@ -625,6 +628,51 @@
     return Void();
 }
 
+Return<void> DrmPlugin::getOfflineLicenseKeySetIds(getOfflineLicenseKeySetIds_cb _hidl_cb) {
+    std::vector<std::string> licenseNames = mFileHandle.ListLicenses();
+    std::vector<KeySetId> keySetIds;
+    for (const auto& name : licenseNames) {
+        std::vector<uint8_t> keySetId(name.begin(), name.end());
+        keySetIds.push_back(keySetId);
+    }
+    _hidl_cb(Status::OK, keySetIds);
+    return Void();
+}
+
+
+Return<Status> DrmPlugin::removeOfflineLicense(const KeySetId& keySetId) {
+    std::string licenseName(keySetId.begin(), keySetId.end());
+    if (mFileHandle.DeleteLicense(licenseName)) {
+        return Status::OK;
+    }
+    return Status::BAD_VALUE;
+}
+
+Return<void> DrmPlugin::getOfflineLicenseState(const KeySetId& keySetId,
+        getOfflineLicenseState_cb _hidl_cb) {
+    std::string licenseName(keySetId.begin(), keySetId.end());
+    DeviceFiles::LicenseState state;
+    std::string license;
+    OfflineLicenseState hLicenseState;
+    if (mFileHandle.RetrieveLicense(licenseName, &state, &license)) {
+        switch (state) {
+        case DeviceFiles::kLicenseStateActive:
+            hLicenseState = OfflineLicenseState::USABLE;
+            break;
+        case DeviceFiles::kLicenseStateReleasing:
+            hLicenseState = OfflineLicenseState::INACTIVE;
+            break;
+        case DeviceFiles::kLicenseStateUnknown:
+            hLicenseState = OfflineLicenseState::UNKNOWN;
+            break;
+        }
+        _hidl_cb(Status::OK, hLicenseState);
+    } else {
+        _hidl_cb(Status::BAD_VALUE, OfflineLicenseState::UNKNOWN);
+    }
+    return Void();
+}
+
 Return<void> DrmPlugin::getSecureStops(getSecureStops_cb _hidl_cb) {
     std::vector<SecureStop> stops;
     for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) {
@@ -727,7 +775,7 @@
 }
 
 }  // namespace clearkey
-}  // namespace V1_1
+}  // namespace V1_2
 }  // namespace drm
 }  // namespace hardware
 }  // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
index deb5d97..8ebb42b 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp
@@ -31,7 +31,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 namespace {
@@ -169,7 +169,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
index 1c32b1b..d93777d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
@@ -36,7 +36,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 JsonWebKey::JsonWebKey() {
@@ -271,7 +271,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
index 43fecbb..2dcd00f 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
@@ -11,7 +11,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 std::string MemoryFileSystem::GetFileName(const std::string& path) {
@@ -38,6 +38,14 @@
     }
 }
 
+std::vector<std::string> MemoryFileSystem::ListFiles() const {
+    std::vector<std::string> list;
+    for (const auto& filename : mMemoryFileSystem) {
+        list.push_back(filename.first);
+    }
+    return list;
+}
+
 size_t MemoryFileSystem::Read(const std::string& path, std::string* buffer) {
     std::string key = GetFileName(path);
     auto result = mMemoryFileSystem.find(key);
@@ -78,7 +86,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp
index 41a8374..f4c49b9 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp
@@ -28,7 +28,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::KeyValue;
@@ -90,7 +90,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp
index d262763..99fb30f 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp
@@ -24,7 +24,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::hidl_string;
@@ -86,7 +86,7 @@
 }
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc
deleted file mode 100644
index ffe856a..0000000
--- a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-service vendor.drm-clearkey-hal-1-1 /vendor/bin/hw/android.hardware.drm@1.1-service.clearkey
-    class hal
-    user media
-    group media mediadrm
-    ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc
new file mode 100644
index 0000000..ac184f7
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc
@@ -0,0 +1,6 @@
+service vendor.drm-clearkey-hal-1-2 /vendor/bin/hw/android.hardware.drm@1.2-service.clearkey
+    class hal
+    user media
+    group media mediadrm
+    ioprio rt 4
+    writepid /dev/cpuset/foreground/tasks
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h
index 0c7ef20..721f4c0 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h
@@ -22,7 +22,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::Status;
@@ -42,7 +42,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h
index 4a385bd..ec30cc1 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h
@@ -25,7 +25,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::sp;
@@ -38,7 +38,7 @@
 void encodeBase64Url(const void *data, size_t size, std::string *out);
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h
index 5bbb28a..c497e37 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h
@@ -25,7 +25,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::sp;
@@ -54,7 +54,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
index 3d6a4a9..f83903c 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
@@ -22,7 +22,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 static const std::string kVendorKey("vendor");
@@ -49,7 +49,7 @@
 static const uint8_t kMetricsData[] = { 0 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h
index 46cb5e4..7e9b6bd 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h
@@ -24,7 +24,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::KeyValue;
@@ -47,7 +47,7 @@
   void operator=(const TypeName&) = delete;
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h
index 9952027..6368f3d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h
@@ -17,17 +17,17 @@
 #ifndef CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
 #define CLEARKEY_CREATE_PLUGIN_FACTORIES_H_
 
-#include <android/hardware/drm/1.1/ICryptoFactory.h>
-#include <android/hardware/drm/1.1/IDrmFactory.h>
+#include <android/hardware/drm/1.2/ICryptoFactory.h>
+#include <android/hardware/drm/1.2/IDrmFactory.h>
 
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
-using ::android::hardware::drm::V1_1::ICryptoFactory;
-using ::android::hardware::drm::V1_1::IDrmFactory;
+using ::android::hardware::drm::V1_2::ICryptoFactory;
+using ::android::hardware::drm::V1_2::IDrmFactory;
 
 extern "C" {
     IDrmFactory* createDrmFactory();
@@ -35,7 +35,7 @@
 }
 
 }  // namespace clearkey
-}  // namespace V1_1
+}  // namespace V1_2
 }  // namespace drm
 }  // namespace hardware
 }  // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h
index 175ab76..203bb2d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h
@@ -18,17 +18,17 @@
 #define CLEARKEY_CRYPTO_FACTORY_H_
 
 #include <android/hardware/drm/1.0/ICryptoPlugin.h>
-#include <android/hardware/drm/1.1/ICryptoFactory.h>
+#include <android/hardware/drm/1.2/ICryptoFactory.h>
 
 #include "ClearKeyTypes.h"
 
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
-using ::android::hardware::drm::V1_1::ICryptoFactory;
+using ::android::hardware::drm::V1_2::ICryptoFactory;
 using ::android::hardware::drm::V1_0::ICryptoPlugin;
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_string;
@@ -52,7 +52,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
index 6a73806..480dc7e 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
@@ -32,7 +32,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::DestinationBuffer;
@@ -96,7 +96,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h b/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h
index ac13d23..554ae59 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h
@@ -20,7 +20,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 class DeviceFiles {
@@ -42,6 +42,8 @@
 
     virtual bool LicenseExists(const std::string& keySetId);
 
+    virtual std::vector<std::string> ListLicenses() const;
+
     virtual bool DeleteLicense(const std::string& keySetId);
 
     virtual bool DeleteAllLicenses();
@@ -61,7 +63,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
index 6f58195..ff715ea 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
@@ -17,15 +17,15 @@
 #ifndef CLEARKEY_DRM_FACTORY_H_
 #define CLEARKEY_DRM_FACTORY_H_
 
-#include <android/hardware/drm/1.1/IDrmPlugin.h>
-#include <android/hardware/drm/1.1/IDrmFactory.h>
+#include <android/hardware/drm/1.2/IDrmPlugin.h>
+#include <android/hardware/drm/1.2/IDrmFactory.h>
 
 #include "ClearKeyTypes.h"
 
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::hidl_array;
@@ -52,7 +52,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
index 8e6092f..256c5d6 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -17,10 +17,12 @@
 #ifndef CLEARKEY_DRM_PLUGIN_H_
 #define CLEARKEY_DRM_PLUGIN_H_
 
-#include <android/hardware/drm/1.1/IDrmPlugin.h>
+#include <android/hardware/drm/1.2/IDrmPlugin.h>
 
-#include <stdio.h>
 #include <map>
+#include <stdio.h>
+
+#include <utils/List.h>
 
 #include "DeviceFiles.h"
 #include "SessionLibrary.h"
@@ -29,7 +31,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::EventType;
@@ -42,8 +44,12 @@
 using ::android::hardware::drm::V1_0::SessionId;
 using ::android::hardware::drm::V1_0::Status;
 using ::android::hardware::drm::V1_1::DrmMetricGroup;
-using ::android::hardware::drm::V1_1::IDrmPlugin;
+using ::android::hardware::drm::V1_1::HdcpLevel;
 using ::android::hardware::drm::V1_1::KeyRequestType;
+using ::android::hardware::drm::V1_1::SecureStopRelease;
+using ::android::hardware::drm::V1_1::SecurityLevel;
+using ::android::hardware::drm::V1_2::IDrmPlugin;
+using ::android::hardware::drm::V1_2::OfflineLicenseState;
 
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
@@ -136,6 +142,13 @@
 
     Return<void> getMetrics(getMetrics_cb _hidl_cb) override;
 
+    Return<void> getOfflineLicenseKeySetIds(getOfflineLicenseKeySetIds_cb _hidl_cb) override;
+
+    Return<Status> removeOfflineLicense(const KeySetId &keySetId) override;
+
+    Return<void> getOfflineLicenseState(const KeySetId &keySetId,
+            getOfflineLicenseState_cb _hidl_cb) override;
+
     Return<void> getPropertyString(
         const hidl_string& name,
         getPropertyString_cb _hidl_cb) override;
@@ -334,7 +347,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h
index 7d7abf2..889f511 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h
@@ -24,7 +24,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::Status;
@@ -49,7 +49,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h
index 4ab034c..e57470c 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h
@@ -23,7 +23,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 class JsonWebKey {
@@ -54,7 +54,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
index 22f8779..bcd9fd6 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
@@ -13,7 +13,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 // Using android file system requires clearkey plugin to update
@@ -42,6 +42,7 @@
 
     bool FileExists(const std::string& fileName) const;
     ssize_t GetFileSize(const std::string& fileName) const;
+    std::vector<std::string> ListFiles() const;
     size_t Read(const std::string& pathName, std::string* buffer);
     bool RemoveAllFiles();
     bool RemoveFile(const std::string& fileName);
@@ -59,7 +60,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
index 1064dc7..f35560d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
@@ -27,7 +27,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::Status;
@@ -64,7 +64,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h
index 326a0c1..1e567b8 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h
@@ -26,7 +26,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::sp;
@@ -58,7 +58,7 @@
 };
 
 } // namespace clearkey
-} // namespace V1_1
+} // namespace V1_2
 } // namespace drm
 } // namespace hardware
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h
index cc06329..f6d30c9 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h
@@ -24,7 +24,7 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::hidl_array;
@@ -69,7 +69,7 @@
 }
 
 }  // namespace clearkey
-}  // namespace V1_1
+}  // namespace V1_2
 }  // namespace drm
 }  // namespace hardware
 }  // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/protos/DeviceFiles.proto b/drm/mediadrm/plugins/clearkey/hidl/protos/DeviceFiles.proto
index 145ac5b..3e11f0b 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/protos/DeviceFiles.proto
+++ b/drm/mediadrm/plugins/clearkey/hidl/protos/DeviceFiles.proto
@@ -10,7 +10,7 @@
 //
 syntax = "proto2";
 
-package android.hardware.drm.V1_1.clearkey;
+package android.hardware.drm.V1_2.clearkey;
 
 // need this if we are using libprotobuf-cpp-2.3.0-lite
 option optimize_for = LITE_RUNTIME;
diff --git a/drm/mediadrm/plugins/clearkey/hidl/service.cpp b/drm/mediadrm/plugins/clearkey/hidl/service.cpp
index 6a97b72..4ca31f3 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/service.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/service.cpp
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#define LOG_TAG "android.hardware.drm@1.1-service.clearkey"
+#define LOG_TAG "android.hardware.drm@1.2-service.clearkey"
 
 #include <CryptoFactory.h>
 #include <DrmFactory.h>
@@ -26,14 +26,14 @@
 using ::android::hardware::joinRpcThreadpool;
 using ::android::sp;
 
-using android::hardware::drm::V1_1::ICryptoFactory;
-using android::hardware::drm::V1_1::IDrmFactory;
-using android::hardware::drm::V1_1::clearkey::CryptoFactory;
-using android::hardware::drm::V1_1::clearkey::DrmFactory;
+using android::hardware::drm::V1_2::ICryptoFactory;
+using android::hardware::drm::V1_2::IDrmFactory;
+using android::hardware::drm::V1_2::clearkey::CryptoFactory;
+using android::hardware::drm::V1_2::clearkey::DrmFactory;
 
 
 int main(int /* argc */, char** /* argv */) {
-    ALOGD("android.hardware.drm@1.1-service.clearkey starting...");
+    ALOGD("android.hardware.drm@1.2-service.clearkey starting...");
 
     // The DRM HAL may communicate to other vendor components via
     // /dev/vndbinder
diff --git a/include/soundtrigger/ISoundTrigger.h b/include/soundtrigger/ISoundTrigger.h
index ea1aea6..c357caa 100644
--- a/include/soundtrigger/ISoundTrigger.h
+++ b/include/soundtrigger/ISoundTrigger.h
@@ -40,8 +40,7 @@
     virtual status_t startRecognition(sound_model_handle_t handle,
                                       const sp<IMemory>& dataMemory) = 0;
     virtual status_t stopRecognition(sound_model_handle_t handle) = 0;
-    virtual status_t getModelState(sound_model_handle_t handle,
-                                   sp<IMemory>& eventMemory) = 0;
+    virtual status_t getModelState(sound_model_handle_t handle) = 0;
 
 };
 
diff --git a/include/soundtrigger/SoundTrigger.h b/include/soundtrigger/SoundTrigger.h
index dcf9ce8..2e2ff7a 100644
--- a/include/soundtrigger/SoundTrigger.h
+++ b/include/soundtrigger/SoundTrigger.h
@@ -52,7 +52,7 @@
 
             status_t startRecognition(sound_model_handle_t handle, const sp<IMemory>& dataMemory);
             status_t stopRecognition(sound_model_handle_t handle);
-            status_t getModelState(sound_model_handle_t handle, sp<IMemory>& eventMemory);
+            status_t getModelState(sound_model_handle_t handle);
 
             // BpSoundTriggerClient
             virtual void onRecognitionEvent(const sp<IMemory>& eventMemory);
diff --git a/media/bufferpool/1.0/Accessor.cpp b/media/bufferpool/1.0/Accessor.cpp
new file mode 100644
index 0000000..b1dfc08
--- /dev/null
+++ b/media/bufferpool/1.0/Accessor.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "BufferPoolConnection"
+
+#include "Accessor.h"
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+void ConnectionDeathRecipient::add(
+        int64_t connectionId,
+        const sp<Accessor> &accessor) {
+    std::lock_guard<std::mutex> lock(mLock);
+    if (mAccessors.find(connectionId) == mAccessors.end()) {
+        mAccessors.insert(std::make_pair(connectionId, accessor));
+    }
+}
+
+void ConnectionDeathRecipient::remove(int64_t connectionId) {
+    std::lock_guard<std::mutex> lock(mLock);
+    mAccessors.erase(connectionId);
+    auto it = mConnectionToCookie.find(connectionId);
+    if (it != mConnectionToCookie.end()) {
+        uint64_t cookie = it->second;
+        mConnectionToCookie.erase(it);
+        auto cit = mCookieToConnections.find(cookie);
+        if (cit != mCookieToConnections.end()) {
+            cit->second.erase(connectionId);
+            if (cit->second.size() == 0) {
+                mCookieToConnections.erase(cit);
+            }
+        }
+    }
+}
+
+void ConnectionDeathRecipient::addCookieToConnection(
+        uint64_t cookie,
+        int64_t connectionId) {
+    std::lock_guard<std::mutex> lock(mLock);
+    if (mAccessors.find(connectionId) == mAccessors.end()) {
+        return;
+    }
+    mConnectionToCookie.insert(std::make_pair(connectionId, cookie));
+    auto it = mCookieToConnections.find(cookie);
+    if (it != mCookieToConnections.end()) {
+        it->second.insert(connectionId);
+    } else {
+        mCookieToConnections.insert(std::make_pair(
+                cookie, std::set<int64_t>{connectionId}));
+    }
+}
+
+void ConnectionDeathRecipient::serviceDied(
+        uint64_t cookie,
+        const wp<::android::hidl::base::V1_0::IBase>& /* who */
+        ) {
+    std::map<int64_t, const wp<Accessor>> connectionsToClose;
+    {
+        std::lock_guard<std::mutex> lock(mLock);
+
+        auto it = mCookieToConnections.find(cookie);
+        if (it != mCookieToConnections.end()) {
+            for (auto conIt = it->second.begin(); conIt != it->second.end(); ++conIt) {
+                auto accessorIt = mAccessors.find(*conIt);
+                if (accessorIt != mAccessors.end()) {
+                    connectionsToClose.insert(std::make_pair(*conIt, accessorIt->second));
+                    mAccessors.erase(accessorIt);
+                }
+                mConnectionToCookie.erase(*conIt);
+            }
+            mCookieToConnections.erase(it);
+        }
+    }
+
+    if (connectionsToClose.size() > 0) {
+        sp<Accessor> accessor;
+        for (auto it = connectionsToClose.begin(); it != connectionsToClose.end(); ++it) {
+            accessor = it->second.promote();
+
+            if (accessor) {
+                accessor->close(it->first);
+                ALOGD("connection %lld closed on death", (long long)it->first);
+            }
+        }
+    }
+}
+
+namespace {
+static sp<ConnectionDeathRecipient> sConnectionDeathRecipient =
+        new ConnectionDeathRecipient();
+}
+
+sp<ConnectionDeathRecipient> Accessor::getConnectionDeathRecipient() {
+    return sConnectionDeathRecipient;
+}
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
+Return<void> Accessor::connect(connect_cb _hidl_cb) {
+    sp<Connection> connection;
+    ConnectionId connectionId;
+    const QueueDescriptor* fmqDesc;
+
+    ResultStatus status = connect(&connection, &connectionId, &fmqDesc, false);
+    if (status == ResultStatus::OK) {
+        _hidl_cb(status, connection, connectionId, *fmqDesc);
+    } else {
+        _hidl_cb(status, nullptr, -1LL,
+                 android::hardware::MQDescriptorSync<BufferStatusMessage>(
+                         std::vector<android::hardware::GrantorDescriptor>(),
+                         nullptr /* nhandle */, 0 /* size */));
+    }
+    return Void();
+}
+
+Accessor::Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator)
+    : mImpl(new Impl(allocator)) {}
+
+Accessor::~Accessor() {
+}
+
+bool Accessor::isValid() {
+    return (bool)mImpl;
+}
+
+ResultStatus Accessor::allocate(
+        ConnectionId connectionId,
+        const std::vector<uint8_t> &params,
+        BufferId *bufferId, const native_handle_t** handle) {
+    if (mImpl) {
+        return mImpl->allocate(connectionId, params, bufferId, handle);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::fetch(
+        ConnectionId connectionId, TransactionId transactionId,
+        BufferId bufferId, const native_handle_t** handle) {
+    if (mImpl) {
+        return mImpl->fetch(connectionId, transactionId, bufferId, handle);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::connect(
+        sp<Connection> *connection, ConnectionId *pConnectionId,
+        const QueueDescriptor** fmqDescPtr, bool local) {
+    if (mImpl) {
+        ResultStatus status = mImpl->connect(this, connection, pConnectionId, fmqDescPtr);
+        if (!local && status == ResultStatus::OK) {
+            sp<Accessor> accessor(this);
+            sConnectionDeathRecipient->add(*pConnectionId, accessor);
+        }
+        return status;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::close(ConnectionId connectionId) {
+    if (mImpl) {
+        ResultStatus status = mImpl->close(connectionId);
+        sConnectionDeathRecipient->remove(connectionId);
+        return status;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+void Accessor::cleanUp(bool clearCache) {
+    if (mImpl) {
+        mImpl->cleanUp(clearCache);
+    }
+}
+
+//IAccessor* HIDL_FETCH_IAccessor(const char* /* name */) {
+//    return new Accessor();
+//}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/1.0/Accessor.h b/media/bufferpool/1.0/Accessor.h
new file mode 100644
index 0000000..2f86f7b
--- /dev/null
+++ b/media/bufferpool/1.0/Accessor.h
@@ -0,0 +1,188 @@
+/*
+ * 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_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
+
+#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "BufferStatus.h"
+
+#include <set>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Accessor;
+struct Connection;
+
+/**
+ * Receives death notifications from remote connections.
+ * On death notifications, the connections are closed and used resources
+ * are released.
+ */
+struct ConnectionDeathRecipient : public hardware::hidl_death_recipient {
+    /**
+     * Registers a newly connected connection from remote processes.
+     */
+    void add(int64_t connectionId, const sp<Accessor> &accessor);
+
+    /**
+     * Removes a connection.
+     */
+    void remove(int64_t connectionId);
+
+    void addCookieToConnection(uint64_t cookie, int64_t connectionId);
+
+    virtual void serviceDied(
+            uint64_t /* cookie */,
+            const wp<::android::hidl::base::V1_0::IBase>& /* who */
+            ) override;
+
+private:
+    std::mutex mLock;
+    std::map<uint64_t, std::set<int64_t>>  mCookieToConnections;
+    std::map<int64_t, uint64_t> mConnectionToCookie;
+    std::map<int64_t, const wp<Accessor>> mAccessors;
+};
+
+/**
+ * A buffer pool accessor which enables a buffer pool to communicate with buffer
+ * pool clients. 1:1 correspondense holds between a buffer pool and an accessor.
+ */
+struct Accessor : public IAccessor {
+    // Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
+    Return<void> connect(connect_cb _hidl_cb) override;
+
+    /**
+     * Creates a buffer pool accessor which uses the specified allocator.
+     *
+     * @param allocator buffer allocator.
+     */
+    explicit Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator);
+
+    /** Destructs a buffer pool accessor. */
+    ~Accessor();
+
+    /** Returns whether the accessor is valid. */
+    bool isValid();
+
+    /** Allocates a buffer from a buffer pool.
+     *
+     * @param connectionId  the connection id of the client.
+     * @param params        the allocation parameters.
+     * @param bufferId      the id of the allocated buffer.
+     * @param handle        the native handle of the allocated buffer.
+     *
+     * @return OK when a buffer is successfully allocated.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus allocate(
+            ConnectionId connectionId,
+            const std::vector<uint8_t>& params,
+            BufferId *bufferId,
+            const native_handle_t** handle);
+
+    /**
+     * Fetches a buffer for the specified transaction.
+     *
+     * @param connectionId  the id of receiving connection(client).
+     * @param transactionId the id of the transfer transaction.
+     * @param bufferId      the id of the buffer to be fetched.
+     * @param handle        the native handle of the fetched buffer.
+     *
+     * @return OK when a buffer is successfully fetched.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus fetch(
+            ConnectionId connectionId,
+            TransactionId transactionId,
+            BufferId bufferId,
+            const native_handle_t** handle);
+
+    /**
+     * Makes a connection to the buffer pool. The buffer pool client uses the
+     * created connection in order to communicate with the buffer pool. An
+     * FMQ for buffer status message is also created for the client.
+     *
+     * @param connection    created connection
+     * @param pConnectionId the id of the created connection
+     * @param fmqDescPtr    FMQ descriptor for shared buffer status message
+     *                      queue between a buffer pool and the client.
+     * @param local         true when a connection request comes from local process,
+     *                      false otherwise.
+     *
+     * @return OK when a connection is successfully made.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus connect(
+            sp<Connection> *connection, ConnectionId *pConnectionId,
+            const QueueDescriptor** fmqDescPtr, bool local);
+
+    /**
+     * Closes the specified connection to the client.
+     *
+     * @param connectionId  the id of the connection.
+     *
+     * @return OK when the connection is closed.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus close(ConnectionId connectionId);
+
+    /**
+     * Processes pending buffer status messages and perfoms periodic cache
+     * cleaning.
+     *
+     * @param clearCache    if clearCache is true, it frees all buffers waiting
+     *                      to be recycled.
+     */
+    void cleanUp(bool clearCache);
+
+    /**
+     * Gets a hidl_death_recipient for remote connection death.
+     */
+    static sp<ConnectionDeathRecipient> getConnectionDeathRecipient();
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> mImpl;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
diff --git a/media/bufferpool/1.0/AccessorImpl.cpp b/media/bufferpool/1.0/AccessorImpl.cpp
new file mode 100644
index 0000000..fa17f15
--- /dev/null
+++ b/media/bufferpool/1.0/AccessorImpl.cpp
@@ -0,0 +1,543 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BufferPoolAccessor"
+//#define LOG_NDEBUG 0
+
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+namespace {
+    static constexpr int64_t kCleanUpDurationUs = 500000; // TODO tune 0.5 sec
+    static constexpr int64_t kLogDurationUs = 5000000; // 5 secs
+
+    static constexpr size_t kMinAllocBytesForEviction = 1024*1024*15;
+    static constexpr size_t kMinBufferCountForEviction = 40;
+}
+
+// Buffer structure in bufferpool process
+struct InternalBuffer {
+    BufferId mId;
+    size_t mOwnerCount;
+    size_t mTransactionCount;
+    const std::shared_ptr<BufferPoolAllocation> mAllocation;
+    const size_t mAllocSize;
+    const std::vector<uint8_t> mConfig;
+
+    InternalBuffer(
+            BufferId id,
+            const std::shared_ptr<BufferPoolAllocation> &alloc,
+            const size_t allocSize,
+            const std::vector<uint8_t> &allocConfig)
+            : mId(id), mOwnerCount(0), mTransactionCount(0),
+            mAllocation(alloc), mAllocSize(allocSize), mConfig(allocConfig) {}
+
+    const native_handle_t *handle() {
+        return mAllocation->handle();
+    }
+};
+
+struct TransactionStatus {
+    TransactionId mId;
+    BufferId mBufferId;
+    ConnectionId mSender;
+    ConnectionId mReceiver;
+    BufferStatus mStatus;
+    int64_t mTimestampUs;
+    bool mSenderValidated;
+
+    TransactionStatus(const BufferStatusMessage &message, int64_t timestampUs) {
+        mId = message.transactionId;
+        mBufferId = message.bufferId;
+        mStatus = message.newStatus;
+        mTimestampUs = timestampUs;
+        if (mStatus == BufferStatus::TRANSFER_TO) {
+            mSender = message.connectionId;
+            mReceiver = message.targetConnectionId;
+            mSenderValidated = true;
+        } else {
+            mSender = -1LL;
+            mReceiver = message.connectionId;
+            mSenderValidated = false;
+        }
+    }
+};
+
+// Helper template methods for handling map of set.
+template<class T, class U>
+bool insert(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+    auto iter = mapOfSet->find(key);
+    if (iter == mapOfSet->end()) {
+        std::set<U> valueSet{value};
+        mapOfSet->insert(std::make_pair(key, valueSet));
+        return true;
+    } else if (iter->second.find(value)  == iter->second.end()) {
+        iter->second.insert(value);
+        return true;
+    }
+    return false;
+}
+
+template<class T, class U>
+bool erase(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+    bool ret = false;
+    auto iter = mapOfSet->find(key);
+    if (iter != mapOfSet->end()) {
+        if (iter->second.erase(value) > 0) {
+            ret = true;
+        }
+        if (iter->second.size() == 0) {
+            mapOfSet->erase(iter);
+        }
+    }
+    return ret;
+}
+
+template<class T, class U>
+bool contains(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+    auto iter = mapOfSet->find(key);
+    if (iter != mapOfSet->end()) {
+        auto setIter = iter->second.find(value);
+        return setIter != iter->second.end();
+    }
+    return false;
+}
+
+int32_t Accessor::Impl::sPid = getpid();
+uint32_t Accessor::Impl::sSeqId = time(nullptr);
+
+Accessor::Impl::Impl(
+        const std::shared_ptr<BufferPoolAllocator> &allocator)
+        : mAllocator(allocator) {}
+
+Accessor::Impl::~Impl() {
+}
+
+ResultStatus Accessor::Impl::connect(
+        const sp<Accessor> &accessor, sp<Connection> *connection,
+        ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr) {
+    sp<Connection> newConnection = new Connection();
+    ResultStatus status = ResultStatus::CRITICAL_ERROR;
+    {
+        std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+        if (newConnection) {
+            ConnectionId id = (int64_t)sPid << 32 | sSeqId;
+            status = mBufferPool.mObserver.open(id, fmqDescPtr);
+            if (status == ResultStatus::OK) {
+                newConnection->initialize(accessor, id);
+                *connection = newConnection;
+                *pConnectionId = id;
+                ++sSeqId;
+            }
+        }
+        mBufferPool.processStatusMessages();
+        mBufferPool.cleanUp();
+    }
+    return status;
+}
+
+ResultStatus Accessor::Impl::close(ConnectionId connectionId) {
+    std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+    mBufferPool.processStatusMessages();
+    mBufferPool.handleClose(connectionId);
+    mBufferPool.mObserver.close(connectionId);
+    // Since close# will be called after all works are finished, it is OK to
+    // evict unused buffers.
+    mBufferPool.cleanUp(true);
+    return ResultStatus::OK;
+}
+
+ResultStatus Accessor::Impl::allocate(
+        ConnectionId connectionId, const std::vector<uint8_t>& params,
+        BufferId *bufferId, const native_handle_t** handle) {
+    std::unique_lock<std::mutex> lock(mBufferPool.mMutex);
+    mBufferPool.processStatusMessages();
+    ResultStatus status = ResultStatus::OK;
+    if (!mBufferPool.getFreeBuffer(mAllocator, params, bufferId, handle)) {
+        lock.unlock();
+        std::shared_ptr<BufferPoolAllocation> alloc;
+        size_t allocSize;
+        status = mAllocator->allocate(params, &alloc, &allocSize);
+        lock.lock();
+        if (status == ResultStatus::OK) {
+            status = mBufferPool.addNewBuffer(alloc, allocSize, params, bufferId, handle);
+        }
+        ALOGV("create a buffer %d : %u %p",
+              status == ResultStatus::OK, *bufferId, *handle);
+    }
+    if (status == ResultStatus::OK) {
+        // TODO: handle ownBuffer failure
+        mBufferPool.handleOwnBuffer(connectionId, *bufferId);
+    }
+    mBufferPool.cleanUp();
+    return status;
+}
+
+ResultStatus Accessor::Impl::fetch(
+        ConnectionId connectionId, TransactionId transactionId,
+        BufferId bufferId, const native_handle_t** handle) {
+    std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+    mBufferPool.processStatusMessages();
+    auto found = mBufferPool.mTransactions.find(transactionId);
+    if (found != mBufferPool.mTransactions.end() &&
+            contains(&mBufferPool.mPendingTransactions,
+                     connectionId, transactionId)) {
+        if (found->second->mSenderValidated &&
+                found->second->mStatus == BufferStatus::TRANSFER_FROM &&
+                found->second->mBufferId == bufferId) {
+            found->second->mStatus = BufferStatus::TRANSFER_FETCH;
+            auto bufferIt = mBufferPool.mBuffers.find(bufferId);
+            if (bufferIt != mBufferPool.mBuffers.end()) {
+                mBufferPool.mStats.onBufferFetched();
+                *handle = bufferIt->second->handle();
+                return ResultStatus::OK;
+            }
+        }
+    }
+    mBufferPool.cleanUp();
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+void Accessor::Impl::cleanUp(bool clearCache) {
+    // transaction timeout, buffer cacheing TTL handling
+    std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+    mBufferPool.processStatusMessages();
+    mBufferPool.cleanUp(clearCache);
+}
+
+Accessor::Impl::Impl::BufferPool::BufferPool()
+    : mTimestampUs(getTimestampNow()),
+      mLastCleanUpUs(mTimestampUs),
+      mLastLogUs(mTimestampUs),
+      mSeq(0) {}
+
+
+// Statistics helper
+template<typename T, typename S>
+int percentage(T base, S total) {
+    return int(total ? 0.5 + 100. * static_cast<S>(base) / total : 0);
+}
+
+Accessor::Impl::Impl::BufferPool::~BufferPool() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    ALOGD("Destruction - bufferpool %p "
+          "cached: %zu/%zuM, %zu/%d%% in use; "
+          "allocs: %zu, %d%% recycled; "
+          "transfers: %zu, %d%% unfetced",
+          this, mStats.mBuffersCached, mStats.mSizeCached >> 20,
+          mStats.mBuffersInUse, percentage(mStats.mBuffersInUse, mStats.mBuffersCached),
+          mStats.mTotalAllocations, percentage(mStats.mTotalRecycles, mStats.mTotalAllocations),
+          mStats.mTotalTransfers,
+          percentage(mStats.mTotalTransfers - mStats.mTotalFetches, mStats.mTotalTransfers));
+}
+
+bool Accessor::Impl::BufferPool::handleOwnBuffer(
+        ConnectionId connectionId, BufferId bufferId) {
+
+    bool added = insert(&mUsingBuffers, connectionId, bufferId);
+    if (added) {
+        auto iter = mBuffers.find(bufferId);
+        iter->second->mOwnerCount++;
+    }
+    insert(&mUsingConnections, bufferId, connectionId);
+    return added;
+}
+
+bool Accessor::Impl::BufferPool::handleReleaseBuffer(
+        ConnectionId connectionId, BufferId bufferId) {
+    bool deleted = erase(&mUsingBuffers, connectionId, bufferId);
+    if (deleted) {
+        auto iter = mBuffers.find(bufferId);
+        iter->second->mOwnerCount--;
+        if (iter->second->mOwnerCount == 0 &&
+                iter->second->mTransactionCount == 0) {
+            mStats.onBufferUnused(iter->second->mAllocSize);
+            mFreeBuffers.insert(bufferId);
+        }
+    }
+    erase(&mUsingConnections, bufferId, connectionId);
+    ALOGV("release buffer %u : %d", bufferId, deleted);
+    return deleted;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferTo(const BufferStatusMessage &message) {
+    auto completed = mCompletedTransactions.find(
+            message.transactionId);
+    if (completed != mCompletedTransactions.end()) {
+        // already completed
+        mCompletedTransactions.erase(completed);
+        return true;
+    }
+    // the buffer should exist and be owned.
+    auto bufferIter = mBuffers.find(message.bufferId);
+    if (bufferIter == mBuffers.end() ||
+            !contains(&mUsingBuffers, message.connectionId, message.bufferId)) {
+        return false;
+    }
+    auto found = mTransactions.find(message.transactionId);
+    if (found != mTransactions.end()) {
+        // transfer_from was received earlier.
+        found->second->mSender = message.connectionId;
+        found->second->mSenderValidated = true;
+        return true;
+    }
+    // TODO: verify there is target connection Id
+    mStats.onBufferSent();
+    mTransactions.insert(std::make_pair(
+            message.transactionId,
+            std::make_unique<TransactionStatus>(message, mTimestampUs)));
+    insert(&mPendingTransactions, message.targetConnectionId,
+           message.transactionId);
+    bufferIter->second->mTransactionCount++;
+    return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferFrom(const BufferStatusMessage &message) {
+    auto found = mTransactions.find(message.transactionId);
+    if (found == mTransactions.end()) {
+        // TODO: is it feasible to check ownership here?
+        mStats.onBufferSent();
+        mTransactions.insert(std::make_pair(
+                message.transactionId,
+                std::make_unique<TransactionStatus>(message, mTimestampUs)));
+        insert(&mPendingTransactions, message.connectionId,
+               message.transactionId);
+        auto bufferIter = mBuffers.find(message.bufferId);
+        bufferIter->second->mTransactionCount++;
+    } else {
+        if (message.connectionId == found->second->mReceiver) {
+            found->second->mStatus = BufferStatus::TRANSFER_FROM;
+        }
+    }
+    return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferResult(const BufferStatusMessage &message) {
+    auto found = mTransactions.find(message.transactionId);
+    if (found != mTransactions.end()) {
+        bool deleted = erase(&mPendingTransactions, message.connectionId,
+                             message.transactionId);
+        if (deleted) {
+            if (!found->second->mSenderValidated) {
+                mCompletedTransactions.insert(message.transactionId);
+            }
+            auto bufferIter = mBuffers.find(message.bufferId);
+            if (message.newStatus == BufferStatus::TRANSFER_OK) {
+                handleOwnBuffer(message.connectionId, message.bufferId);
+            }
+            bufferIter->second->mTransactionCount--;
+            if (bufferIter->second->mOwnerCount == 0
+                && bufferIter->second->mTransactionCount == 0) {
+                mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                mFreeBuffers.insert(message.bufferId);
+            }
+            mTransactions.erase(found);
+        }
+        ALOGV("transfer finished %llu %u - %d", (unsigned long long)message.transactionId,
+              message.bufferId, deleted);
+        return deleted;
+    }
+    ALOGV("transfer not found %llu %u", (unsigned long long)message.transactionId,
+          message.bufferId);
+    return false;
+}
+
+void Accessor::Impl::BufferPool::processStatusMessages() {
+    std::vector<BufferStatusMessage> messages;
+    mObserver.getBufferStatusChanges(messages);
+    mTimestampUs = getTimestampNow();
+    for (BufferStatusMessage& message: messages) {
+        bool ret = false;
+        switch (message.newStatus) {
+            case BufferStatus::NOT_USED:
+                ret = handleReleaseBuffer(
+                        message.connectionId, message.bufferId);
+                break;
+            case BufferStatus::USED:
+                // not happening
+                break;
+            case BufferStatus::TRANSFER_TO:
+                ret = handleTransferTo(message);
+                break;
+            case BufferStatus::TRANSFER_FROM:
+                ret = handleTransferFrom(message);
+                break;
+            case BufferStatus::TRANSFER_TIMEOUT:
+                // TODO
+                break;
+            case BufferStatus::TRANSFER_LOST:
+                // TODO
+                break;
+            case BufferStatus::TRANSFER_FETCH:
+                // not happening
+                break;
+            case BufferStatus::TRANSFER_OK:
+            case BufferStatus::TRANSFER_ERROR:
+                ret = handleTransferResult(message);
+                break;
+        }
+        if (ret == false) {
+            ALOGW("buffer status message processing failure - message : %d connection : %lld",
+                  message.newStatus, (long long)message.connectionId);
+        }
+    }
+    messages.clear();
+}
+
+bool Accessor::Impl::BufferPool::handleClose(ConnectionId connectionId) {
+    // Cleaning buffers
+    auto buffers = mUsingBuffers.find(connectionId);
+    if (buffers != mUsingBuffers.end()) {
+        for (const BufferId& bufferId : buffers->second) {
+            bool deleted = erase(&mUsingConnections, bufferId, connectionId);
+            if (deleted) {
+                auto bufferIter = mBuffers.find(bufferId);
+                bufferIter->second->mOwnerCount--;
+                if (bufferIter->second->mOwnerCount == 0 &&
+                        bufferIter->second->mTransactionCount == 0) {
+                    // TODO: handle freebuffer insert fail
+                    mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                    mFreeBuffers.insert(bufferId);
+                }
+            }
+        }
+        mUsingBuffers.erase(buffers);
+    }
+
+    // Cleaning transactions
+    auto pending = mPendingTransactions.find(connectionId);
+    if (pending != mPendingTransactions.end()) {
+        for (const TransactionId& transactionId : pending->second) {
+            auto iter = mTransactions.find(transactionId);
+            if (iter != mTransactions.end()) {
+                if (!iter->second->mSenderValidated) {
+                    mCompletedTransactions.insert(transactionId);
+                }
+                BufferId bufferId = iter->second->mBufferId;
+                auto bufferIter = mBuffers.find(bufferId);
+                bufferIter->second->mTransactionCount--;
+                if (bufferIter->second->mOwnerCount == 0 &&
+                    bufferIter->second->mTransactionCount == 0) {
+                    // TODO: handle freebuffer insert fail
+                    mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                    mFreeBuffers.insert(bufferId);
+                }
+                mTransactions.erase(iter);
+            }
+        }
+    }
+    return true;
+}
+
+bool Accessor::Impl::BufferPool::getFreeBuffer(
+        const std::shared_ptr<BufferPoolAllocator> &allocator,
+        const std::vector<uint8_t> &params, BufferId *pId,
+        const native_handle_t** handle) {
+    auto bufferIt = mFreeBuffers.begin();
+    for (;bufferIt != mFreeBuffers.end(); ++bufferIt) {
+        BufferId bufferId = *bufferIt;
+        if (allocator->compatible(params, mBuffers[bufferId]->mConfig)) {
+            break;
+        }
+    }
+    if (bufferIt != mFreeBuffers.end()) {
+        BufferId id = *bufferIt;
+        mFreeBuffers.erase(bufferIt);
+        mStats.onBufferRecycled(mBuffers[id]->mAllocSize);
+        *handle = mBuffers[id]->handle();
+        *pId = id;
+        ALOGV("recycle a buffer %u %p", id, *handle);
+        return true;
+    }
+    return false;
+}
+
+ResultStatus Accessor::Impl::BufferPool::addNewBuffer(
+        const std::shared_ptr<BufferPoolAllocation> &alloc,
+        const size_t allocSize,
+        const std::vector<uint8_t> &params,
+        BufferId *pId,
+        const native_handle_t** handle) {
+
+    BufferId bufferId = mSeq++;
+    if (mSeq == Connection::SYNC_BUFFERID) {
+        mSeq = 0;
+    }
+    std::unique_ptr<InternalBuffer> buffer =
+            std::make_unique<InternalBuffer>(
+                    bufferId, alloc, allocSize, params);
+    if (buffer) {
+        auto res = mBuffers.insert(std::make_pair(
+                bufferId, std::move(buffer)));
+        if (res.second) {
+            mStats.onBufferAllocated(allocSize);
+            *handle = alloc->handle();
+            *pId = bufferId;
+            return ResultStatus::OK;
+        }
+    }
+    return ResultStatus::NO_MEMORY;
+}
+
+void Accessor::Impl::BufferPool::cleanUp(bool clearCache) {
+    if (clearCache || mTimestampUs > mLastCleanUpUs + kCleanUpDurationUs) {
+        mLastCleanUpUs = mTimestampUs;
+        if (mTimestampUs > mLastLogUs + kLogDurationUs) {
+            mLastLogUs = mTimestampUs;
+            ALOGD("bufferpool %p : %zu(%zu size) total buffers - "
+                  "%zu(%zu size) used buffers - %zu/%zu (recycle/alloc) - "
+                  "%zu/%zu (fetch/transfer)",
+                  this, mStats.mBuffersCached, mStats.mSizeCached,
+                  mStats.mBuffersInUse, mStats.mSizeInUse,
+                  mStats.mTotalRecycles, mStats.mTotalAllocations,
+                  mStats.mTotalFetches, mStats.mTotalTransfers);
+        }
+        for (auto freeIt = mFreeBuffers.begin(); freeIt != mFreeBuffers.end();) {
+            if (!clearCache && mStats.mSizeCached < kMinAllocBytesForEviction
+                    && mBuffers.size() < kMinBufferCountForEviction) {
+                break;
+            }
+            auto it = mBuffers.find(*freeIt);
+            if (it != mBuffers.end() &&
+                    it->second->mOwnerCount == 0 && it->second->mTransactionCount == 0) {
+                mStats.onBufferEvicted(it->second->mAllocSize);
+                mBuffers.erase(it);
+                freeIt = mFreeBuffers.erase(freeIt);
+            } else {
+                ++freeIt;
+                ALOGW("bufferpool inconsistent!");
+            }
+        }
+    }
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/1.0/AccessorImpl.h b/media/bufferpool/1.0/AccessorImpl.h
new file mode 100644
index 0000000..c04dbf3
--- /dev/null
+++ b/media/bufferpool/1.0/AccessorImpl.h
@@ -0,0 +1,300 @@
+/*
+ * 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_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
+
+#include <map>
+#include <set>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+struct InternalBuffer;
+struct TransactionStatus;
+
+/**
+ * An implementation of a buffer pool accessor(or a buffer pool implementation.) */
+class Accessor::Impl {
+public:
+    Impl(const std::shared_ptr<BufferPoolAllocator> &allocator);
+
+    ~Impl();
+
+    ResultStatus connect(
+            const sp<Accessor> &accessor, sp<Connection> *connection,
+            ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr);
+
+    ResultStatus close(ConnectionId connectionId);
+
+    ResultStatus allocate(ConnectionId connectionId,
+                          const std::vector<uint8_t>& params,
+                          BufferId *bufferId,
+                          const native_handle_t** handle);
+
+    ResultStatus fetch(ConnectionId connectionId,
+                       TransactionId transactionId,
+                       BufferId bufferId,
+                       const native_handle_t** handle);
+
+    void cleanUp(bool clearCache);
+
+private:
+    // ConnectionId = pid : (timestamp_created + seqId)
+    // in order to guarantee uniqueness for each connection
+    static uint32_t sSeqId;
+    static int32_t sPid;
+
+    const std::shared_ptr<BufferPoolAllocator> mAllocator;
+
+    /**
+     * Buffer pool implementation.
+     *
+     * Handles buffer status messages. Handles buffer allocation/recycling.
+     * Handles buffer transfer between buffer pool clients.
+     */
+    struct BufferPool {
+    private:
+        std::mutex mMutex;
+        int64_t mTimestampUs;
+        int64_t mLastCleanUpUs;
+        int64_t mLastLogUs;
+        BufferId mSeq;
+        BufferStatusObserver mObserver;
+
+        std::map<ConnectionId, std::set<BufferId>> mUsingBuffers;
+        std::map<BufferId, std::set<ConnectionId>> mUsingConnections;
+
+        std::map<ConnectionId, std::set<TransactionId>> mPendingTransactions;
+        // Transactions completed before TRANSFER_TO message arrival.
+        // Fetch does not occur for the transactions.
+        // Only transaction id is kept for the transactions in short duration.
+        std::set<TransactionId> mCompletedTransactions;
+        // Currently active(pending) transations' status & information.
+        std::map<TransactionId, std::unique_ptr<TransactionStatus>>
+                mTransactions;
+
+        std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
+        std::set<BufferId> mFreeBuffers;
+
+        /// Buffer pool statistics which tracks allocation and transfer statistics.
+        struct Stats {
+            /// Total size of allocations which are used or available to use.
+            /// (bytes or pixels)
+            size_t mSizeCached;
+            /// # of cached buffers which are used or available to use.
+            size_t mBuffersCached;
+            /// Total size of allocations which are currently used. (bytes or pixels)
+            size_t mSizeInUse;
+            /// # of currently used buffers
+            size_t mBuffersInUse;
+
+            /// # of allocations called on bufferpool. (# of fetched from BlockPool)
+            size_t mTotalAllocations;
+            /// # of allocations that were served from the cache.
+            /// (# of allocator alloc prevented)
+            size_t mTotalRecycles;
+            /// # of buffer transfers initiated.
+            size_t mTotalTransfers;
+            /// # of transfers that had to be fetched.
+            size_t mTotalFetches;
+
+            Stats()
+                : mSizeCached(0), mBuffersCached(0), mSizeInUse(0), mBuffersInUse(0),
+                  mTotalAllocations(0), mTotalRecycles(0), mTotalTransfers(0), mTotalFetches(0) {}
+
+            /// A new buffer is allocated on an allocation request.
+            void onBufferAllocated(size_t allocSize) {
+                mSizeCached += allocSize;
+                mBuffersCached++;
+
+                mSizeInUse += allocSize;
+                mBuffersInUse++;
+
+                mTotalAllocations++;
+            }
+
+            /// A buffer is evicted and destroyed.
+            void onBufferEvicted(size_t allocSize) {
+                mSizeCached -= allocSize;
+                mBuffersCached--;
+            }
+
+            /// A buffer is recycled on an allocation request.
+            void onBufferRecycled(size_t allocSize) {
+                mSizeInUse += allocSize;
+                mBuffersInUse++;
+
+                mTotalAllocations++;
+                mTotalRecycles++;
+            }
+
+            /// A buffer is available to be recycled.
+            void onBufferUnused(size_t allocSize) {
+                mSizeInUse -= allocSize;
+                mBuffersInUse--;
+            }
+
+            /// A buffer transfer is initiated.
+            void onBufferSent() {
+                mTotalTransfers++;
+            }
+
+            /// A buffer fetch is invoked by a buffer transfer.
+            void onBufferFetched() {
+                mTotalFetches++;
+            }
+        } mStats;
+
+    public:
+        /** Creates a buffer pool. */
+        BufferPool();
+
+        /** Destroys a buffer pool. */
+        ~BufferPool();
+
+        /**
+         * Processes all pending buffer status messages, and returns the result.
+         * Each status message is handled by methods with 'handle' prefix.
+         */
+        void processStatusMessages();
+
+        /**
+         * Handles a buffer being owned by a connection.
+         *
+         * @param connectionId  the id of the buffer owning connection.
+         * @param bufferId      the id of the buffer.
+         *
+         * @return {@code true} when the buffer is owned,
+         *         {@code false} otherwise.
+         */
+        bool handleOwnBuffer(ConnectionId connectionId, BufferId bufferId);
+
+        /**
+         * Handles a buffer being released by a connection.
+         *
+         * @param connectionId  the id of the buffer owning connection.
+         * @param bufferId      the id of the buffer.
+         *
+         * @return {@code true} when the buffer ownership is released,
+         *         {@code false} otherwise.
+         */
+        bool handleReleaseBuffer(ConnectionId connectionId, BufferId bufferId);
+
+        /**
+         * Handles a transfer transaction start message from the sender.
+         *
+         * @param message   a buffer status message for the transaction.
+         *
+         * @result {@code true} when transfer_to message is acknowledged,
+         *         {@code false} otherwise.
+         */
+        bool handleTransferTo(const BufferStatusMessage &message);
+
+        /**
+         * Handles a transfer transaction being acked by the receiver.
+         *
+         * @param message   a buffer status message for the transaction.
+         *
+         * @result {@code true} when transfer_from message is acknowledged,
+         *         {@code false} otherwise.
+         */
+        bool handleTransferFrom(const BufferStatusMessage &message);
+
+        /**
+         * Handles a transfer transaction result message from the receiver.
+         *
+         * @param message   a buffer status message for the transaction.
+         *
+         * @result {@code true} when the exisitng transaction is finished,
+         *         {@code false} otherwise.
+         */
+        bool handleTransferResult(const BufferStatusMessage &message);
+
+        /**
+         * Handles a connection being closed, and returns the result. All the
+         * buffers and transactions owned by the connection will be cleaned up.
+         * The related FMQ will be cleaned up too.
+         *
+         * @param connectionId  the id of the connection.
+         *
+         * @result {@code true} when the connection existed,
+         *         {@code false} otherwise.
+         */
+        bool handleClose(ConnectionId connectionId);
+
+        /**
+         * Recycles a existing free buffer if it is possible.
+         *
+         * @param allocator the buffer allocator
+         * @param params    the allocation parameters.
+         * @param pId       the id of the recycled buffer.
+         * @param handle    the native handle of the recycled buffer.
+         *
+         * @return {@code true} when a buffer is recycled, {@code false}
+         *         otherwise.
+         */
+        bool getFreeBuffer(
+                const std::shared_ptr<BufferPoolAllocator> &allocator,
+                const std::vector<uint8_t> &params,
+                BufferId *pId, const native_handle_t **handle);
+
+        /**
+         * Adds a newly allocated buffer to bufferpool.
+         *
+         * @param alloc     the newly allocated buffer.
+         * @param allocSize the size of the newly allocated buffer.
+         * @param params    the allocation parameters.
+         * @param pId       the buffer id for the newly allocated buffer.
+         * @param handle    the native handle for the newly allocated buffer.
+         *
+         * @return OK when an allocation is successfully allocated.
+         *         NO_MEMORY when there is no memory.
+         *         CRITICAL_ERROR otherwise.
+         */
+        ResultStatus addNewBuffer(
+                const std::shared_ptr<BufferPoolAllocation> &alloc,
+                const size_t allocSize,
+                const std::vector<uint8_t> &params,
+                BufferId *pId,
+                const native_handle_t **handle);
+
+        /**
+         * Processes pending buffer status messages and performs periodic cache
+         * cleaning.
+         *
+         * @param clearCache    if clearCache is true, it frees all buffers
+         *                      waiting to be recycled.
+         */
+        void cleanUp(bool clearCache = false);
+
+        friend class Accessor::Impl;
+    } mBufferPool;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace ufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
diff --git a/media/bufferpool/1.0/Android.bp b/media/bufferpool/1.0/Android.bp
new file mode 100644
index 0000000..c7ea70f
--- /dev/null
+++ b/media/bufferpool/1.0/Android.bp
@@ -0,0 +1,29 @@
+cc_library {
+    name: "libstagefright_bufferpool@1.0",
+    vendor_available: true,
+    srcs: [
+        "Accessor.cpp",
+        "AccessorImpl.cpp",
+        "BufferPoolClient.cpp",
+        "BufferStatus.cpp",
+        "ClientManager.cpp",
+        "Connection.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhwbinder",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+        "android.hardware.media.bufferpool@1.0",
+    ],
+    export_shared_lib_headers: [
+        "libfmq",
+        "android.hardware.media.bufferpool@1.0",
+    ],
+}
diff --git a/media/bufferpool/1.0/BufferPoolClient.cpp b/media/bufferpool/1.0/BufferPoolClient.cpp
new file mode 100644
index 0000000..41520ca
--- /dev/null
+++ b/media/bufferpool/1.0/BufferPoolClient.cpp
@@ -0,0 +1,708 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BufferPoolClient"
+//#define LOG_NDEBUG 0
+
+#include <thread>
+#include <utils/Log.h>
+#include "BufferPoolClient.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+static constexpr int64_t kReceiveTimeoutUs = 1000000; // 100ms
+static constexpr int kPostMaxRetry = 3;
+static constexpr int kCacheTtlUs = 1000000; // TODO: tune
+
+class BufferPoolClient::Impl
+        : public std::enable_shared_from_this<BufferPoolClient::Impl> {
+public:
+    explicit Impl(const sp<Accessor> &accessor);
+
+    explicit Impl(const sp<IAccessor> &accessor);
+
+    bool isValid() {
+        return mValid;
+    }
+
+    bool isLocal() {
+        return mValid && mLocal;
+    }
+
+    ConnectionId getConnectionId() {
+        return mConnectionId;
+    }
+
+    sp<IAccessor> &getAccessor() {
+        return mAccessor;
+    }
+
+    bool isActive(int64_t *lastTransactionUs, bool clearCache);
+
+    ResultStatus allocate(const std::vector<uint8_t> &params,
+                          native_handle_t **handle,
+                          std::shared_ptr<BufferPoolData> *buffer);
+
+    ResultStatus receive(
+            TransactionId transactionId, BufferId bufferId,
+            int64_t timestampUs,
+            native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer);
+
+    void postBufferRelease(BufferId bufferId);
+
+    bool postSend(
+            BufferId bufferId, ConnectionId receiver,
+            TransactionId *transactionId, int64_t *timestampUs);
+private:
+
+    bool postReceive(
+            BufferId bufferId, TransactionId transactionId,
+            int64_t timestampUs);
+
+    bool postReceiveResult(
+            BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync);
+
+    void trySyncFromRemote();
+
+    bool syncReleased();
+
+    void evictCaches(bool clearCache = false);
+
+    ResultStatus allocateBufferHandle(
+            const std::vector<uint8_t>& params, BufferId *bufferId,
+            native_handle_t **handle);
+
+    ResultStatus fetchBufferHandle(
+            TransactionId transactionId, BufferId bufferId,
+            native_handle_t **handle);
+
+    struct BlockPoolDataDtor;
+    struct ClientBuffer;
+
+    bool mLocal;
+    bool mValid;
+    sp<IAccessor> mAccessor;
+    sp<Connection> mLocalConnection;
+    sp<IConnection> mRemoteConnection;
+    uint32_t mSeqId;
+    ConnectionId mConnectionId;
+    int64_t mLastEvictCacheUs;
+
+    // CachedBuffers
+    struct BufferCache {
+        std::mutex mLock;
+        bool mCreating;
+        std::condition_variable mCreateCv;
+        std::map<BufferId, std::unique_ptr<ClientBuffer>> mBuffers;
+        int mActive;
+        int64_t mLastChangeUs;
+
+        BufferCache() : mCreating(false), mActive(0), mLastChangeUs(getTimestampNow()) {}
+
+        void incActive_l() {
+            ++mActive;
+            mLastChangeUs = getTimestampNow();
+        }
+
+        void decActive_l() {
+            --mActive;
+            mLastChangeUs = getTimestampNow();
+        }
+    } mCache;
+
+    // FMQ - release notifier
+    struct {
+        std::mutex mLock;
+        // TODO: use only one list?(using one list may dealy sending messages?)
+        std::list<BufferId> mReleasingIds;
+        std::list<BufferId> mReleasedIds;
+        std::unique_ptr<BufferStatusChannel> mStatusChannel;
+    } mReleasing;
+
+    // This lock is held during synchronization from remote side.
+    // In order to minimize remote calls and locking durtaion, this lock is held
+    // by best effort approach using try_lock().
+    std::mutex mRemoteSyncLock;
+};
+
+struct BufferPoolClient::Impl::BlockPoolDataDtor {
+    BlockPoolDataDtor(const std::shared_ptr<BufferPoolClient::Impl> &impl)
+            : mImpl(impl) {}
+
+    void operator()(BufferPoolData *buffer) {
+        BufferId id = buffer->mId;
+        delete buffer;
+
+        auto impl = mImpl.lock();
+        if (impl && impl->isValid()) {
+            impl->postBufferRelease(id);
+        }
+    }
+    const std::weak_ptr<BufferPoolClient::Impl> mImpl;
+};
+
+struct BufferPoolClient::Impl::ClientBuffer {
+private:
+    bool mInvalidated; // TODO: implement
+    int64_t mExpireUs;
+    bool mHasCache;
+    ConnectionId mConnectionId;
+    BufferId mId;
+    native_handle_t *mHandle;
+    std::weak_ptr<BufferPoolData> mCache;
+
+    void updateExpire() {
+        mExpireUs = getTimestampNow() + kCacheTtlUs;
+    }
+
+public:
+    ClientBuffer(
+            ConnectionId connectionId, BufferId id, native_handle_t *handle)
+            : mInvalidated(false), mHasCache(false),
+              mConnectionId(connectionId), mId(id), mHandle(handle) {
+        (void)mInvalidated;
+        mExpireUs = getTimestampNow() + kCacheTtlUs;
+    }
+
+    ~ClientBuffer() {
+        if (mHandle) {
+            native_handle_close(mHandle);
+            native_handle_delete(mHandle);
+        }
+    }
+
+    bool expire() const {
+        int64_t now = getTimestampNow();
+        return now >= mExpireUs;
+    }
+
+    bool hasCache() const {
+        return mHasCache;
+    }
+
+    std::shared_ptr<BufferPoolData> fetchCache(native_handle_t **pHandle) {
+        if (mHasCache) {
+            std::shared_ptr<BufferPoolData> cache = mCache.lock();
+            if (cache) {
+                *pHandle = mHandle;
+            }
+            return cache;
+        }
+        return nullptr;
+    }
+
+    std::shared_ptr<BufferPoolData> createCache(
+            const std::shared_ptr<BufferPoolClient::Impl> &impl,
+            native_handle_t **pHandle) {
+        if (!mHasCache) {
+            // Allocates a raw ptr in order to avoid sending #postBufferRelease
+            // from deleter, in case of native_handle_clone failure.
+            BufferPoolData *ptr = new BufferPoolData(mConnectionId, mId);
+            if (ptr) {
+                std::shared_ptr<BufferPoolData> cache(ptr, BlockPoolDataDtor(impl));
+                if (cache) {
+                    mCache = cache;
+                    mHasCache = true;
+                    *pHandle = mHandle;
+                    return cache;
+                }
+            }
+            if (ptr) {
+                delete ptr;
+            }
+        }
+        return nullptr;
+    }
+
+    bool onCacheRelease() {
+        if (mHasCache) {
+            // TODO: verify mCache is not valid;
+            updateExpire();
+            mHasCache = false;
+            return true;
+        }
+        return false;
+    }
+};
+
+BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor)
+    : mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0),
+      mLastEvictCacheUs(getTimestampNow()) {
+    const QueueDescriptor *fmqDesc;
+    ResultStatus status = accessor->connect(
+            &mLocalConnection, &mConnectionId, &fmqDesc, true);
+    if (status == ResultStatus::OK) {
+        mReleasing.mStatusChannel =
+                std::make_unique<BufferStatusChannel>(*fmqDesc);
+        mValid = mReleasing.mStatusChannel &&
+                mReleasing.mStatusChannel->isValid();
+    }
+}
+
+BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor)
+    : mLocal(false), mValid(false), mAccessor(accessor), mSeqId(0),
+      mLastEvictCacheUs(getTimestampNow()) {
+    bool valid = false;
+    sp<IConnection>& outConnection = mRemoteConnection;
+    ConnectionId& id = mConnectionId;
+    std::unique_ptr<BufferStatusChannel>& outChannel =
+            mReleasing.mStatusChannel;
+    Return<void> transResult = accessor->connect(
+            [&valid, &outConnection, &id, &outChannel]
+            (ResultStatus status, sp<IConnection> connection,
+             ConnectionId connectionId, const QueueDescriptor& desc) {
+                if (status == ResultStatus::OK) {
+                    outConnection = connection;
+                    id = connectionId;
+                    outChannel = std::make_unique<BufferStatusChannel>(desc);
+                    if (outChannel && outChannel->isValid()) {
+                        valid = true;
+                    }
+                }
+            });
+    mValid = transResult.isOk() && valid;
+}
+
+bool BufferPoolClient::Impl::isActive(int64_t *lastTransactionUs, bool clearCache) {
+    bool active = false;
+    {
+        std::lock_guard<std::mutex> lock(mCache.mLock);
+        syncReleased();
+        evictCaches(clearCache);
+        *lastTransactionUs = mCache.mLastChangeUs;
+        active = mCache.mActive > 0;
+    }
+    if (mValid && mLocal && mLocalConnection) {
+        mLocalConnection->cleanUp(clearCache);
+        return true;
+    }
+    return active;
+}
+
+ResultStatus BufferPoolClient::Impl::allocate(
+        const std::vector<uint8_t> &params,
+        native_handle_t **pHandle,
+        std::shared_ptr<BufferPoolData> *buffer) {
+    if (!mLocal || !mLocalConnection || !mValid) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    BufferId bufferId;
+    native_handle_t *handle = nullptr;
+    buffer->reset();
+    ResultStatus status = allocateBufferHandle(params, &bufferId, &handle);
+    if (status == ResultStatus::OK) {
+        if (handle) {
+            std::unique_lock<std::mutex> lock(mCache.mLock);
+            syncReleased();
+            evictCaches();
+            auto cacheIt = mCache.mBuffers.find(bufferId);
+            if (cacheIt != mCache.mBuffers.end()) {
+                // TODO: verify it is recycled. (not having active ref)
+                mCache.mBuffers.erase(cacheIt);
+            }
+            auto clientBuffer = std::make_unique<ClientBuffer>(
+                    mConnectionId, bufferId, handle);
+            if (clientBuffer) {
+                auto result = mCache.mBuffers.insert(std::make_pair(
+                        bufferId, std::move(clientBuffer)));
+                if (result.second) {
+                    *buffer = result.first->second->createCache(
+                            shared_from_this(), pHandle);
+                    if (*buffer) {
+                        mCache.incActive_l();
+                    }
+                }
+            }
+        }
+        if (!*buffer) {
+            ALOGV("client cache creation failure %d: %lld",
+                  handle != nullptr, (long long)mConnectionId);
+            status = ResultStatus::NO_MEMORY;
+            postBufferRelease(bufferId);
+        }
+    }
+    return status;
+}
+
+ResultStatus BufferPoolClient::Impl::receive(
+        TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+        native_handle_t **pHandle,
+        std::shared_ptr<BufferPoolData> *buffer) {
+    if (!mValid) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    if (timestampUs != 0) {
+        timestampUs += kReceiveTimeoutUs;
+    }
+    if (!postReceive(bufferId, transactionId, timestampUs)) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    ResultStatus status = ResultStatus::CRITICAL_ERROR;
+    buffer->reset();
+    while(1) {
+        std::unique_lock<std::mutex> lock(mCache.mLock);
+        syncReleased();
+        evictCaches();
+        auto cacheIt = mCache.mBuffers.find(bufferId);
+        if (cacheIt != mCache.mBuffers.end()) {
+            if (cacheIt->second->hasCache()) {
+                *buffer = cacheIt->second->fetchCache(pHandle);
+                if (!*buffer) {
+                    // check transfer time_out
+                    lock.unlock();
+                    std::this_thread::yield();
+                    continue;
+                }
+                ALOGV("client receive from reference %lld", (long long)mConnectionId);
+                break;
+            } else {
+                *buffer = cacheIt->second->createCache(shared_from_this(), pHandle);
+                if (*buffer) {
+                    mCache.incActive_l();
+                }
+                ALOGV("client receive from cache %lld", (long long)mConnectionId);
+                break;
+            }
+        } else {
+            if (!mCache.mCreating) {
+                mCache.mCreating = true;
+                lock.unlock();
+                native_handle_t* handle = nullptr;
+                status = fetchBufferHandle(transactionId, bufferId, &handle);
+                lock.lock();
+                if (status == ResultStatus::OK) {
+                    if (handle) {
+                        auto clientBuffer = std::make_unique<ClientBuffer>(
+                                mConnectionId, bufferId, handle);
+                        if (clientBuffer) {
+                            auto result = mCache.mBuffers.insert(
+                                    std::make_pair(bufferId, std::move(
+                                            clientBuffer)));
+                            if (result.second) {
+                                *buffer = result.first->second->createCache(
+                                        shared_from_this(), pHandle);
+                                if (*buffer) {
+                                    mCache.incActive_l();
+                                }
+                            }
+                        }
+                    }
+                    if (!*buffer) {
+                        status = ResultStatus::NO_MEMORY;
+                    }
+                }
+                mCache.mCreating = false;
+                lock.unlock();
+                mCache.mCreateCv.notify_all();
+                break;
+            }
+            mCache.mCreateCv.wait(lock);
+        }
+    }
+    bool needsSync = false;
+    bool posted = postReceiveResult(bufferId, transactionId,
+                                      *buffer ? true : false, &needsSync);
+    ALOGV("client receive %lld - %u : %s (%d)", (long long)mConnectionId, bufferId,
+          *buffer ? "ok" : "fail", posted);
+    if (mValid && mLocal && mLocalConnection) {
+        mLocalConnection->cleanUp(false);
+    }
+    if (needsSync && mRemoteConnection) {
+        trySyncFromRemote();
+    }
+    if (*buffer) {
+        if (!posted) {
+            buffer->reset();
+            return ResultStatus::CRITICAL_ERROR;
+        }
+        return ResultStatus::OK;
+    }
+    return status;
+}
+
+
+void BufferPoolClient::Impl::postBufferRelease(BufferId bufferId) {
+    std::lock_guard<std::mutex> lock(mReleasing.mLock);
+    mReleasing.mReleasingIds.push_back(bufferId);
+    mReleasing.mStatusChannel->postBufferRelease(
+            mConnectionId, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+}
+
+// TODO: revise ad-hoc posting data structure
+bool BufferPoolClient::Impl::postSend(
+        BufferId bufferId, ConnectionId receiver,
+        TransactionId *transactionId, int64_t *timestampUs) {
+    bool ret = false;
+    bool needsSync = false;
+    {
+        std::lock_guard<std::mutex> lock(mReleasing.mLock);
+        *timestampUs = getTimestampNow();
+        *transactionId = (mConnectionId << 32) | mSeqId++;
+        // TODO: retry, add timeout, target?
+        ret =  mReleasing.mStatusChannel->postBufferStatusMessage(
+                *transactionId, bufferId, BufferStatus::TRANSFER_TO, mConnectionId,
+                receiver, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+        needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
+    }
+    if (mValid && mLocal && mLocalConnection) {
+        mLocalConnection->cleanUp(false);
+    }
+    if (needsSync && mRemoteConnection) {
+        trySyncFromRemote();
+    }
+    return ret;
+}
+
+bool BufferPoolClient::Impl::postReceive(
+        BufferId bufferId, TransactionId transactionId, int64_t timestampUs) {
+    for (int i = 0; i < kPostMaxRetry; ++i) {
+        std::unique_lock<std::mutex> lock(mReleasing.mLock);
+        int64_t now = getTimestampNow();
+        if (timestampUs == 0 || now < timestampUs) {
+            bool result = mReleasing.mStatusChannel->postBufferStatusMessage(
+                    transactionId, bufferId, BufferStatus::TRANSFER_FROM,
+                    mConnectionId, -1, mReleasing.mReleasingIds,
+                    mReleasing.mReleasedIds);
+            if (result) {
+                return true;
+            }
+            lock.unlock();
+            std::this_thread::yield();
+        } else {
+            mReleasing.mStatusChannel->postBufferStatusMessage(
+                    transactionId, bufferId, BufferStatus::TRANSFER_TIMEOUT,
+                    mConnectionId, -1, mReleasing.mReleasingIds,
+                    mReleasing.mReleasedIds);
+            return false;
+        }
+    }
+    return false;
+}
+
+bool BufferPoolClient::Impl::postReceiveResult(
+        BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync) {
+    std::lock_guard<std::mutex> lock(mReleasing.mLock);
+    // TODO: retry, add timeout
+    bool ret = mReleasing.mStatusChannel->postBufferStatusMessage(
+            transactionId, bufferId,
+            result ? BufferStatus::TRANSFER_OK : BufferStatus::TRANSFER_ERROR,
+            mConnectionId, -1, mReleasing.mReleasingIds,
+            mReleasing.mReleasedIds);
+    *needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
+    return ret;
+}
+
+void BufferPoolClient::Impl::trySyncFromRemote() {
+    if (mRemoteSyncLock.try_lock()) {
+        bool needsSync = false;
+        {
+            std::lock_guard<std::mutex> lock(mReleasing.mLock);
+            needsSync = mReleasing.mStatusChannel->needsSync();
+        }
+        if (needsSync) {
+            TransactionId transactionId = (mConnectionId << 32);
+            BufferId bufferId = Connection::SYNC_BUFFERID;
+            Return<void> transResult = mRemoteConnection->fetch(
+                    transactionId, bufferId,
+                    []
+                    (ResultStatus outStatus, Buffer outBuffer) {
+                        (void) outStatus;
+                        (void) outBuffer;
+                    });
+        }
+        mRemoteSyncLock.unlock();
+    }
+}
+
+// should have mCache.mLock
+bool BufferPoolClient::Impl::syncReleased() {
+    std::lock_guard<std::mutex> lock(mReleasing.mLock);
+    if (mReleasing.mReleasingIds.size() > 0) {
+        mReleasing.mStatusChannel->postBufferRelease(
+                mConnectionId, mReleasing.mReleasingIds,
+                mReleasing.mReleasedIds);
+    }
+    if (mReleasing.mReleasedIds.size() > 0) {
+        for (BufferId& id: mReleasing.mReleasedIds) {
+            ALOGV("client release buffer %lld - %u", (long long)mConnectionId, id);
+            auto found = mCache.mBuffers.find(id);
+            if (found != mCache.mBuffers.end()) {
+                if (found->second->onCacheRelease()) {
+                    mCache.decActive_l();
+                } else {
+                    // should not happen!
+                    ALOGW("client %lld cache release status inconsitent!",
+                          (long long)mConnectionId);
+                }
+            } else {
+                // should not happen!
+                ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId);
+            }
+        }
+        mReleasing.mReleasedIds.clear();
+        return true;
+    }
+    return false;
+}
+
+// should have mCache.mLock
+void BufferPoolClient::Impl::evictCaches(bool clearCache) {
+    int64_t now = getTimestampNow();
+    if (now >= mLastEvictCacheUs + kCacheTtlUs || clearCache) {
+        size_t evicted = 0;
+        for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) {
+            if (!it->second->hasCache() && (it->second->expire() || clearCache)) {
+                it = mCache.mBuffers.erase(it);
+                ++evicted;
+            } else {
+                ++it;
+            }
+        }
+        ALOGV("cache count %lld : total %zu, active %d, evicted %zu",
+              (long long)mConnectionId, mCache.mBuffers.size(), mCache.mActive, evicted);
+        mLastEvictCacheUs = now;
+    }
+}
+
+ResultStatus BufferPoolClient::Impl::allocateBufferHandle(
+        const std::vector<uint8_t>& params, BufferId *bufferId,
+        native_handle_t** handle) {
+    if (mLocalConnection) {
+        const native_handle_t* allocHandle = nullptr;
+        ResultStatus status = mLocalConnection->allocate(
+                params, bufferId, &allocHandle);
+        if (status == ResultStatus::OK) {
+            *handle = native_handle_clone(allocHandle);
+        }
+        ALOGV("client allocate result %lld %d : %u clone %p",
+              (long long)mConnectionId, status == ResultStatus::OK,
+              *handle ? *bufferId : 0 , *handle);
+        return status;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::Impl::fetchBufferHandle(
+        TransactionId transactionId, BufferId bufferId,
+        native_handle_t **handle) {
+    sp<IConnection> connection;
+    if (mLocal) {
+        connection = mLocalConnection;
+    } else {
+        connection = mRemoteConnection;
+    }
+    ResultStatus status;
+    Return<void> transResult = connection->fetch(
+            transactionId, bufferId,
+            [&status, &handle]
+            (ResultStatus outStatus, Buffer outBuffer) {
+                status = outStatus;
+                if (status == ResultStatus::OK) {
+                    *handle = native_handle_clone(
+                            outBuffer.buffer.getNativeHandle());
+                }
+            });
+    return transResult.isOk() ? status : ResultStatus::CRITICAL_ERROR;
+}
+
+
+BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) {
+    mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) {
+    mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::~BufferPoolClient() {
+    // TODO: how to handle orphaned buffers?
+}
+
+bool BufferPoolClient::isValid() {
+    return mImpl && mImpl->isValid();
+}
+
+bool BufferPoolClient::isLocal() {
+    return mImpl && mImpl->isLocal();
+}
+
+bool BufferPoolClient::isActive(int64_t *lastTransactionUs, bool clearCache) {
+    if (!isValid()) {
+        *lastTransactionUs = 0;
+        return false;
+    }
+    return mImpl->isActive(lastTransactionUs, clearCache);
+}
+
+ConnectionId BufferPoolClient::getConnectionId() {
+    if (isValid()) {
+        return mImpl->getConnectionId();
+    }
+    return -1;
+}
+
+ResultStatus BufferPoolClient::getAccessor(sp<IAccessor> *accessor) {
+    if (isValid()) {
+        *accessor = mImpl->getAccessor();
+        return ResultStatus::OK;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::allocate(
+        const std::vector<uint8_t> &params,
+        native_handle_t **handle,
+        std::shared_ptr<BufferPoolData> *buffer) {
+    if (isValid()) {
+        return mImpl->allocate(params, handle, buffer);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::receive(
+        TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+        native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+    if (isValid()) {
+        return mImpl->receive(transactionId, bufferId, timestampUs, handle, buffer);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::postSend(
+        ConnectionId receiverId,
+        const std::shared_ptr<BufferPoolData> &buffer,
+        TransactionId *transactionId,
+        int64_t *timestampUs) {
+    if (isValid()) {
+        bool result = mImpl->postSend(
+                buffer->mId, receiverId, transactionId, timestampUs);
+        return result ? ResultStatus::OK : ResultStatus::CRITICAL_ERROR;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/1.0/BufferPoolClient.h b/media/bufferpool/1.0/BufferPoolClient.h
new file mode 100644
index 0000000..577efed
--- /dev/null
+++ b/media/bufferpool/1.0/BufferPoolClient.h
@@ -0,0 +1,102 @@
+/*
+ * 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_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
+
+#include <memory>
+#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
+#include <android/hardware/media/bufferpool/1.0/IConnection.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <cutils/native_handle.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::bufferpool::V1_0::IAccessor;
+using ::android::hardware::media::bufferpool::V1_0::IConnection;
+using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
+using ::android::sp;
+
+/**
+ * A buffer pool client for a buffer pool. For a specific buffer pool, at most
+ * one buffer pool client exists per process. This class will not be exposed
+ * outside. A buffer pool client will be used via ClientManager.
+ */
+class BufferPoolClient {
+public:
+    /**
+     * Creates a buffer pool client from a local buffer pool
+     * (via ClientManager#create).
+     */
+    explicit BufferPoolClient(const sp<Accessor> &accessor);
+
+    /**
+     * Creates a buffer pool client from a remote buffer pool
+     * (via ClientManager#registerSender).
+     * Note: A buffer pool client created with remote buffer pool cannot
+     * allocate a buffer.
+     */
+    explicit BufferPoolClient(const sp<IAccessor> &accessor);
+
+    /** Destructs a buffer pool client. */
+    ~BufferPoolClient();
+
+private:
+    bool isValid();
+
+    bool isLocal();
+
+    bool isActive(int64_t *lastTransactionUs, bool clearCache);
+
+    ConnectionId getConnectionId();
+
+    ResultStatus getAccessor(sp<IAccessor> *accessor);
+
+    ResultStatus allocate(const std::vector<uint8_t> &params,
+                          native_handle_t **handle,
+                          std::shared_ptr<BufferPoolData> *buffer);
+
+    ResultStatus receive(TransactionId transactionId,
+                         BufferId bufferId,
+                         int64_t timestampUs,
+                         native_handle_t **handle,
+                         std::shared_ptr<BufferPoolData> *buffer);
+
+    ResultStatus postSend(ConnectionId receiver,
+                          const std::shared_ptr<BufferPoolData> &buffer,
+                          TransactionId *transactionId,
+                          int64_t *timestampUs);
+
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+
+    friend struct ClientManager;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
diff --git a/media/bufferpool/1.0/BufferStatus.cpp b/media/bufferpool/1.0/BufferStatus.cpp
new file mode 100644
index 0000000..169abce
--- /dev/null
+++ b/media/bufferpool/1.0/BufferStatus.cpp
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BufferPoolStatus"
+//#define LOG_NDEBUG 0
+
+#include <time.h>
+#include "BufferStatus.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+int64_t getTimestampNow() {
+    int64_t stamp;
+    struct timespec ts;
+    // TODO: CLOCK_MONOTONIC_COARSE?
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    stamp = ts.tv_nsec / 1000;
+    stamp += (ts.tv_sec * 1000000LL);
+    return stamp;
+}
+
+static constexpr int kNumElementsInQueue = 1024*16;
+static constexpr int kMinElementsToSyncInQueue = 128;
+
+ResultStatus BufferStatusObserver::open(
+        ConnectionId id, const QueueDescriptor** fmqDescPtr) {
+    if (mBufferStatusQueues.find(id) != mBufferStatusQueues.end()) {
+        // TODO: id collision log?
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    std::unique_ptr<BufferStatusQueue> queue =
+            std::make_unique<BufferStatusQueue>(kNumElementsInQueue);
+    if (!queue || queue->isValid() == false) {
+        *fmqDescPtr = nullptr;
+        return ResultStatus::NO_MEMORY;
+    } else {
+        *fmqDescPtr = queue->getDesc();
+    }
+    auto result = mBufferStatusQueues.insert(
+            std::make_pair(id, std::move(queue)));
+    if (!result.second) {
+        *fmqDescPtr = nullptr;
+        return ResultStatus::NO_MEMORY;
+    }
+    return ResultStatus::OK;
+}
+
+ResultStatus BufferStatusObserver::close(ConnectionId id) {
+    if (mBufferStatusQueues.find(id) == mBufferStatusQueues.end()) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    mBufferStatusQueues.erase(id);
+    return ResultStatus::OK;
+}
+
+void BufferStatusObserver::getBufferStatusChanges(std::vector<BufferStatusMessage> &messages) {
+    for (auto it = mBufferStatusQueues.begin(); it != mBufferStatusQueues.end(); ++it) {
+        BufferStatusMessage message;
+        size_t avail = it->second->availableToRead();
+        while (avail > 0) {
+            if (!it->second->read(&message, 1)) {
+                // Since avaliable # of reads are already confirmed,
+                // this should not happen.
+                // TODO: error handling (spurious client?)
+                ALOGW("FMQ message cannot be read from %lld", (long long)it->first);
+                return;
+            }
+            message.connectionId = it->first;
+            messages.push_back(message);
+            --avail;
+        }
+    }
+}
+
+BufferStatusChannel::BufferStatusChannel(
+        const QueueDescriptor &fmqDesc) {
+    std::unique_ptr<BufferStatusQueue> queue =
+            std::make_unique<BufferStatusQueue>(fmqDesc);
+    if (!queue || queue->isValid() == false) {
+        mValid = false;
+        return;
+    }
+    mValid  = true;
+    mBufferStatusQueue = std::move(queue);
+}
+
+bool BufferStatusChannel::isValid() {
+    return mValid;
+}
+
+bool BufferStatusChannel::needsSync() {
+    if (mValid) {
+        size_t avail = mBufferStatusQueue->availableToWrite();
+        return avail + kMinElementsToSyncInQueue < kNumElementsInQueue;
+    }
+    return false;
+}
+
+void BufferStatusChannel::postBufferRelease(
+        ConnectionId connectionId,
+        std::list<BufferId> &pending, std::list<BufferId> &posted) {
+    if (mValid && pending.size() > 0) {
+        size_t avail = mBufferStatusQueue->availableToWrite();
+        avail = std::min(avail, pending.size());
+        BufferStatusMessage message;
+        for (size_t i = 0 ; i < avail; ++i) {
+            BufferId id = pending.front();
+            message.newStatus = BufferStatus::NOT_USED;
+            message.bufferId = id;
+            message.connectionId = connectionId;
+            if (!mBufferStatusQueue->write(&message, 1)) {
+                // Since avaliable # of writes are already confirmed,
+                // this should not happen.
+                // TODO: error handing?
+                ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+                return;
+            }
+            pending.pop_front();
+            posted.push_back(id);
+        }
+    }
+}
+
+bool BufferStatusChannel::postBufferStatusMessage(
+        TransactionId transactionId, BufferId bufferId,
+        BufferStatus status, ConnectionId connectionId, ConnectionId targetId,
+        std::list<BufferId> &pending, std::list<BufferId> &posted) {
+    if (mValid) {
+        size_t avail = mBufferStatusQueue->availableToWrite();
+        size_t numPending = pending.size();
+        if (avail >= numPending + 1) {
+            BufferStatusMessage release, message;
+            for (size_t i = 0; i < numPending; ++i) {
+                BufferId id = pending.front();
+                release.newStatus = BufferStatus::NOT_USED;
+                release.bufferId = id;
+                release.connectionId = connectionId;
+                if (!mBufferStatusQueue->write(&release, 1)) {
+                    // Since avaliable # of writes are already confirmed,
+                    // this should not happen.
+                    // TODO: error handling?
+                    ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+                    return false;
+                }
+                pending.pop_front();
+                posted.push_back(id);
+            }
+            message.transactionId = transactionId;
+            message.bufferId = bufferId;
+            message.newStatus = status;
+            message.connectionId = connectionId;
+            message.targetConnectionId = targetId;
+            // TODO : timesatamp
+            message.timestampUs = 0;
+            if (!mBufferStatusQueue->write(&message, 1)) {
+                // Since avaliable # of writes are already confirmed,
+                // this should not happen.
+                ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+                return false;
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
diff --git a/media/bufferpool/1.0/BufferStatus.h b/media/bufferpool/1.0/BufferStatus.h
new file mode 100644
index 0000000..a18a921
--- /dev/null
+++ b/media/bufferpool/1.0/BufferStatus.h
@@ -0,0 +1,143 @@
+/*
+ * 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_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
+
+#include <android/hardware/media/bufferpool/1.0/types.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <memory>
+#include <mutex>
+#include <vector>
+#include <list>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+/** Returns monotonic timestamp in Us since fixed point in time. */
+int64_t getTimestampNow();
+
+/**
+ * A collection of FMQ for a buffer pool. buffer ownership/status change
+ * messages are sent via the FMQs from the clients.
+ */
+class BufferStatusObserver {
+private:
+    std::map<ConnectionId, std::unique_ptr<BufferStatusQueue>>
+            mBufferStatusQueues;
+
+public:
+    /** Creates an FMQ for the specified connection(client).
+     *
+     * @param connectionId  connection Id of the specified client.
+     * @param fmqDescPtr    double ptr of created FMQ's descriptor.
+     *
+     * @return OK if FMQ is created successfully.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus open(ConnectionId id, const QueueDescriptor** fmqDescPtr);
+
+    /** Closes an FMQ for the specified connection(client).
+     *
+     * @param connectionId  connection Id of the specified client.
+     *
+     * @return OK if the specified connection is closed successfully.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus close(ConnectionId id);
+
+    /** Retrieves all pending FMQ buffer status messages from clients.
+     *
+     * @param messages  retrieved pending messages.
+     */
+    void getBufferStatusChanges(std::vector<BufferStatusMessage> &messages);
+};
+
+/**
+ * An FMQ for a buffer pool client. Buffer ownership/status change messages
+ * are sent via the fmq to the buffer pool.
+ */
+class BufferStatusChannel {
+private:
+    bool mValid;
+    std::unique_ptr<BufferStatusQueue> mBufferStatusQueue;
+
+public:
+    /**
+     * Connects to an FMQ from a descriptor of the created FMQ.
+     *
+     * @param fmqDesc   Descriptor of the created FMQ.
+     */
+    BufferStatusChannel(const QueueDescriptor &fmqDesc);
+
+    /** Returns whether the FMQ is connected successfully. */
+    bool isValid();
+
+    /** Returns whether the FMQ needs to be synced from the buffer pool */
+    bool needsSync();
+
+    /**
+     * Posts a buffer release message to the buffer pool.
+     *
+     * @param connectionId  connection Id of the client.
+     * @param pending       currently pending buffer release messages.
+     * @param posted        posted buffer release messages.
+     */
+    void postBufferRelease(
+            ConnectionId connectionId,
+            std::list<BufferId> &pending, std::list<BufferId> &posted);
+
+    /**
+     * Posts a buffer status message regarding the specified buffer
+     * transfer transaction.
+     *
+     * @param transactionId Id of the specified transaction.
+     * @param bufferId      buffer Id of the specified transaction.
+     * @param status        new status of the buffer.
+     * @param connectionId  connection Id of the client.
+     * @param targetId      connection Id of the receiver(only when the sender
+     *                      posts a status message).
+     * @param pending       currently pending buffer release messages.
+     * @param posted        posted buffer release messages.
+     *
+     * @return {@code true} when the specified message is posted,
+     *         {@code false} otherwise.
+     */
+    bool postBufferStatusMessage(
+            TransactionId transactionId,
+            BufferId bufferId,
+            BufferStatus status,
+            ConnectionId connectionId,
+            ConnectionId targetId,
+            std::list<BufferId> &pending, std::list<BufferId> &posted);
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
diff --git a/media/bufferpool/1.0/ClientManager.cpp b/media/bufferpool/1.0/ClientManager.cpp
new file mode 100644
index 0000000..ecea0a4
--- /dev/null
+++ b/media/bufferpool/1.0/ClientManager.cpp
@@ -0,0 +1,504 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "BufferPoolManager"
+//#define LOG_NDEBUG 0
+
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlTransportSupport.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include "BufferPoolClient.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+static constexpr int64_t kRegisterTimeoutUs = 500000; // 0.5 sec
+static constexpr int64_t kCleanUpDurationUs = 1000000; // TODO: 1 sec tune
+static constexpr int64_t kClientTimeoutUs = 5000000; // TODO: 5 secs tune
+
+/**
+ * The holder of the cookie of remote IClientManager.
+ * The cookie is process locally unique for each IClientManager.
+ * (The cookie is used to notify death of clients to bufferpool process.)
+ */
+class ClientManagerCookieHolder {
+public:
+    /**
+     * Creates a cookie holder for remote IClientManager(s).
+     */
+    ClientManagerCookieHolder();
+
+    /**
+     * Gets a cookie for a remote IClientManager.
+     *
+     * @param manager   the specified remote IClientManager.
+     * @param added     true when the specified remote IClientManager is added
+     *                  newly, false otherwise.
+     *
+     * @return the process locally unique cookie for the specified IClientManager.
+     */
+    uint64_t getCookie(const sp<IClientManager> &manager, bool *added);
+
+private:
+    uint64_t mSeqId;
+    std::mutex mLock;
+    std::list<std::pair<const wp<IClientManager>, uint64_t>> mManagers;
+};
+
+ClientManagerCookieHolder::ClientManagerCookieHolder() : mSeqId(0){}
+
+uint64_t ClientManagerCookieHolder::getCookie(
+        const sp<IClientManager> &manager,
+        bool *added) {
+    std::lock_guard<std::mutex> lock(mLock);
+    for (auto it = mManagers.begin(); it != mManagers.end();) {
+        const sp<IClientManager> key = it->first.promote();
+        if (key) {
+            if (interfacesEqual(key, manager)) {
+                *added = false;
+                return it->second;
+            }
+            ++it;
+        } else {
+            it = mManagers.erase(it);
+        }
+    }
+    uint64_t id = mSeqId++;
+    *added = true;
+    mManagers.push_back(std::make_pair(manager, id));
+    return id;
+}
+
+class ClientManager::Impl {
+public:
+    Impl();
+
+    // BnRegisterSender
+    ResultStatus registerSender(const sp<IAccessor> &accessor,
+                                ConnectionId *pConnectionId);
+
+    // BpRegisterSender
+    ResultStatus registerSender(const sp<IClientManager> &receiver,
+                                ConnectionId senderId,
+                                ConnectionId *receiverId);
+
+    ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
+                        ConnectionId *pConnectionId);
+
+    ResultStatus close(ConnectionId connectionId);
+
+    ResultStatus allocate(ConnectionId connectionId,
+                          const std::vector<uint8_t> &params,
+                          native_handle_t **handle,
+                          std::shared_ptr<BufferPoolData> *buffer);
+
+    ResultStatus receive(ConnectionId connectionId,
+                         TransactionId transactionId,
+                         BufferId bufferId,
+                         int64_t timestampUs,
+                         native_handle_t **handle,
+                         std::shared_ptr<BufferPoolData> *buffer);
+
+    ResultStatus postSend(ConnectionId receiverId,
+                          const std::shared_ptr<BufferPoolData> &buffer,
+                          TransactionId *transactionId,
+                          int64_t *timestampUs);
+
+    ResultStatus getAccessor(ConnectionId connectionId,
+                             sp<IAccessor> *accessor);
+
+    void cleanUp(bool clearCache = false);
+
+private:
+    // In order to prevent deadlock between multiple locks,
+    // always lock ClientCache.lock before locking ActiveClients.lock.
+    struct ClientCache {
+        // This lock is held for brief duration.
+        // Blocking operation is not performed while holding the lock.
+        std::mutex mMutex;
+        std::list<std::pair<const wp<IAccessor>, const std::weak_ptr<BufferPoolClient>>>
+                mClients;
+        std::condition_variable mConnectCv;
+        bool mConnecting;
+        int64_t mLastCleanUpUs;
+
+        ClientCache() : mConnecting(false), mLastCleanUpUs(getTimestampNow()) {}
+    } mCache;
+
+    // Active clients which can be retrieved via ConnectionId
+    struct ActiveClients {
+        // This lock is held for brief duration.
+        // Blocking operation is not performed holding the lock.
+        std::mutex mMutex;
+        std::map<ConnectionId, const std::shared_ptr<BufferPoolClient>>
+                mClients;
+    } mActive;
+
+    ClientManagerCookieHolder mRemoteClientCookies;
+};
+
+ClientManager::Impl::Impl() {}
+
+ResultStatus ClientManager::Impl::registerSender(
+        const sp<IAccessor> &accessor, ConnectionId *pConnectionId) {
+    cleanUp();
+    int64_t timeoutUs = getTimestampNow() + kRegisterTimeoutUs;
+    do {
+        std::unique_lock<std::mutex> lock(mCache.mMutex);
+        for (auto it = mCache.mClients.begin(); it != mCache.mClients.end(); ++it) {
+            sp<IAccessor> sAccessor = it->first.promote();
+            if (sAccessor && interfacesEqual(sAccessor, accessor)) {
+                const std::shared_ptr<BufferPoolClient> client = it->second.lock();
+                if (client) {
+                    std::lock_guard<std::mutex> lock(mActive.mMutex);
+                    *pConnectionId = client->getConnectionId();
+                    if (mActive.mClients.find(*pConnectionId) != mActive.mClients.end()) {
+                        ALOGV("register existing connection %lld", (long long)*pConnectionId);
+                        return ResultStatus::ALREADY_EXISTS;
+                    }
+                }
+                mCache.mClients.erase(it);
+                break;
+            }
+        }
+        if (!mCache.mConnecting) {
+            mCache.mConnecting = true;
+            lock.unlock();
+            ResultStatus result = ResultStatus::OK;
+            const std::shared_ptr<BufferPoolClient> client =
+                    std::make_shared<BufferPoolClient>(accessor);
+            lock.lock();
+            if (!client) {
+                result = ResultStatus::NO_MEMORY;
+            } else if (!client->isValid()) {
+                result = ResultStatus::CRITICAL_ERROR;
+            }
+            if (result == ResultStatus::OK) {
+                // TODO: handle insert fail. (malloc fail)
+                const std::weak_ptr<BufferPoolClient> wclient = client;
+                mCache.mClients.push_back(std::make_pair(accessor, wclient));
+                ConnectionId conId = client->getConnectionId();
+                {
+                    std::lock_guard<std::mutex> lock(mActive.mMutex);
+                    mActive.mClients.insert(std::make_pair(conId, client));
+                }
+                *pConnectionId = conId;
+                ALOGV("register new connection %lld", (long long)*pConnectionId);
+            }
+            mCache.mConnecting = false;
+            lock.unlock();
+            mCache.mConnectCv.notify_all();
+            return result;
+        }
+        mCache.mConnectCv.wait_for(
+                lock, std::chrono::microseconds(kRegisterTimeoutUs));
+    } while (getTimestampNow() < timeoutUs);
+    // TODO: return timeout error
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::Impl::registerSender(
+        const sp<IClientManager> &receiver,
+        ConnectionId senderId,
+        ConnectionId *receiverId) {
+    sp<IAccessor> accessor;
+    bool local = false;
+    {
+        std::lock_guard<std::mutex> lock(mActive.mMutex);
+        auto it = mActive.mClients.find(senderId);
+        if (it == mActive.mClients.end()) {
+            return ResultStatus::NOT_FOUND;
+        }
+        it->second->getAccessor(&accessor);
+        local = it->second->isLocal();
+    }
+    ResultStatus rs = ResultStatus::CRITICAL_ERROR;
+    if (accessor) {
+       Return<void> transResult = receiver->registerSender(
+                accessor,
+                [&rs, receiverId](
+                        ResultStatus status,
+                        int64_t connectionId) {
+                    rs = status;
+                    *receiverId = connectionId;
+                });
+        if (!transResult.isOk()) {
+            return ResultStatus::CRITICAL_ERROR;
+        } else if (local && rs == ResultStatus::OK) {
+            sp<ConnectionDeathRecipient> recipient = Accessor::getConnectionDeathRecipient();
+            if (recipient)  {
+                ALOGV("client death recipient registered %lld", (long long)*receiverId);
+                bool added;
+                uint64_t cookie = mRemoteClientCookies.getCookie(receiver, &added);
+                recipient->addCookieToConnection(cookie, *receiverId);
+                if (added) {
+                    Return<bool> transResult = receiver->linkToDeath(recipient, cookie);
+                }
+            }
+        }
+    }
+    return rs;
+}
+
+ResultStatus ClientManager::Impl::create(
+        const std::shared_ptr<BufferPoolAllocator> &allocator,
+        ConnectionId *pConnectionId) {
+    const sp<Accessor> accessor = new Accessor(allocator);
+    if (!accessor || !accessor->isValid()) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    std::shared_ptr<BufferPoolClient> client =
+            std::make_shared<BufferPoolClient>(accessor);
+    if (!client || !client->isValid()) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    // Since a new bufferpool is created, evict memories which are used by
+    // existing bufferpools and clients.
+    cleanUp(true);
+    {
+        // TODO: handle insert fail. (malloc fail)
+        std::lock_guard<std::mutex> lock(mCache.mMutex);
+        const std::weak_ptr<BufferPoolClient> wclient = client;
+        mCache.mClients.push_back(std::make_pair(accessor, wclient));
+        ConnectionId conId = client->getConnectionId();
+        {
+            std::lock_guard<std::mutex> lock(mActive.mMutex);
+            mActive.mClients.insert(std::make_pair(conId, client));
+        }
+        *pConnectionId = conId;
+        ALOGV("create new connection %lld", (long long)*pConnectionId);
+    }
+    return ResultStatus::OK;
+}
+
+ResultStatus ClientManager::Impl::close(ConnectionId connectionId) {
+    std::lock_guard<std::mutex> lock1(mCache.mMutex);
+    std::lock_guard<std::mutex> lock2(mActive.mMutex);
+    auto it = mActive.mClients.find(connectionId);
+    if (it != mActive.mClients.end()) {
+        sp<IAccessor> accessor;
+        it->second->getAccessor(&accessor);
+        mActive.mClients.erase(connectionId);
+        for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
+            // clean up dead client caches
+            sp<IAccessor> cAccessor = cit->first.promote();
+            if (!cAccessor || (accessor && interfacesEqual(cAccessor, accessor))) {
+                cit = mCache.mClients.erase(cit);
+            } else {
+                cit++;
+            }
+        }
+        return ResultStatus::OK;
+    }
+    return ResultStatus::NOT_FOUND;
+}
+
+ResultStatus ClientManager::Impl::allocate(
+        ConnectionId connectionId, const std::vector<uint8_t> &params,
+        native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+    std::shared_ptr<BufferPoolClient> client;
+    {
+        std::lock_guard<std::mutex> lock(mActive.mMutex);
+        auto it = mActive.mClients.find(connectionId);
+        if (it == mActive.mClients.end()) {
+            return ResultStatus::NOT_FOUND;
+        }
+        client = it->second;
+    }
+    return client->allocate(params, handle, buffer);
+}
+
+ResultStatus ClientManager::Impl::receive(
+        ConnectionId connectionId, TransactionId transactionId,
+        BufferId bufferId, int64_t timestampUs,
+        native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+    std::shared_ptr<BufferPoolClient> client;
+    {
+        std::lock_guard<std::mutex> lock(mActive.mMutex);
+        auto it = mActive.mClients.find(connectionId);
+        if (it == mActive.mClients.end()) {
+            return ResultStatus::NOT_FOUND;
+        }
+        client = it->second;
+    }
+    return client->receive(transactionId, bufferId, timestampUs, handle, buffer);
+}
+
+ResultStatus ClientManager::Impl::postSend(
+        ConnectionId receiverId, const std::shared_ptr<BufferPoolData> &buffer,
+        TransactionId *transactionId, int64_t *timestampUs) {
+    ConnectionId connectionId = buffer->mConnectionId;
+    std::shared_ptr<BufferPoolClient> client;
+    {
+        std::lock_guard<std::mutex> lock(mActive.mMutex);
+        auto it = mActive.mClients.find(connectionId);
+        if (it == mActive.mClients.end()) {
+            return ResultStatus::NOT_FOUND;
+        }
+        client = it->second;
+    }
+    return client->postSend(receiverId, buffer, transactionId, timestampUs);
+}
+
+ResultStatus ClientManager::Impl::getAccessor(
+        ConnectionId connectionId, sp<IAccessor> *accessor) {
+    std::shared_ptr<BufferPoolClient> client;
+    {
+        std::lock_guard<std::mutex> lock(mActive.mMutex);
+        auto it = mActive.mClients.find(connectionId);
+        if (it == mActive.mClients.end()) {
+            return ResultStatus::NOT_FOUND;
+        }
+        client = it->second;
+    }
+    return client->getAccessor(accessor);
+}
+
+void ClientManager::Impl::cleanUp(bool clearCache) {
+    int64_t now = getTimestampNow();
+    int64_t lastTransactionUs;
+    std::lock_guard<std::mutex> lock1(mCache.mMutex);
+    if (clearCache || mCache.mLastCleanUpUs + kCleanUpDurationUs < now) {
+        std::lock_guard<std::mutex> lock2(mActive.mMutex);
+        int cleaned = 0;
+        for (auto it = mActive.mClients.begin(); it != mActive.mClients.end();) {
+            if (!it->second->isActive(&lastTransactionUs, clearCache)) {
+                if (lastTransactionUs + kClientTimeoutUs < now) {
+                    sp<IAccessor> accessor;
+                    it->second->getAccessor(&accessor);
+                    it = mActive.mClients.erase(it);
+                    ++cleaned;
+                    continue;
+                }
+            }
+            ++it;
+        }
+        for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
+            // clean up dead client caches
+            sp<IAccessor> cAccessor = cit->first.promote();
+            if (!cAccessor) {
+                cit = mCache.mClients.erase(cit);
+            } else {
+                ++cit;
+            }
+        }
+        ALOGV("# of cleaned connections: %d", cleaned);
+        mCache.mLastCleanUpUs = now;
+    }
+}
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
+Return<void> ClientManager::registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) {
+    if (mImpl) {
+        ConnectionId connectionId = -1;
+        ResultStatus status = mImpl->registerSender(bufferPool, &connectionId);
+        _hidl_cb(status, connectionId);
+    } else {
+        _hidl_cb(ResultStatus::CRITICAL_ERROR, -1);
+    }
+    return Void();
+}
+
+// Methods for local use.
+sp<ClientManager> ClientManager::sInstance;
+std::mutex ClientManager::sInstanceLock;
+
+sp<ClientManager> ClientManager::getInstance() {
+    std::lock_guard<std::mutex> lock(sInstanceLock);
+    if (!sInstance) {
+        sInstance = new ClientManager();
+    }
+    return sInstance;
+}
+
+ClientManager::ClientManager() : mImpl(new Impl()) {}
+
+ClientManager::~ClientManager() {
+}
+
+ResultStatus ClientManager::create(
+        const std::shared_ptr<BufferPoolAllocator> &allocator,
+        ConnectionId *pConnectionId) {
+    if (mImpl) {
+        return mImpl->create(allocator, pConnectionId);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::registerSender(
+        const sp<IClientManager> &receiver,
+        ConnectionId senderId,
+        ConnectionId *receiverId) {
+    if (mImpl) {
+        return mImpl->registerSender(receiver, senderId, receiverId);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::close(ConnectionId connectionId) {
+    if (mImpl) {
+        return mImpl->close(connectionId);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::allocate(
+        ConnectionId connectionId, const std::vector<uint8_t> &params,
+        native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+    if (mImpl) {
+        return mImpl->allocate(connectionId, params, handle, buffer);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::receive(
+        ConnectionId connectionId, TransactionId transactionId,
+        BufferId bufferId, int64_t timestampUs,
+        native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+    if (mImpl) {
+        return mImpl->receive(connectionId, transactionId, bufferId,
+                              timestampUs, handle, buffer);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::postSend(
+        ConnectionId receiverId, const std::shared_ptr<BufferPoolData> &buffer,
+        TransactionId *transactionId, int64_t* timestampUs) {
+    if (mImpl && buffer) {
+        return mImpl->postSend(receiverId, buffer, transactionId, timestampUs);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+void ClientManager::cleanUp() {
+    if (mImpl) {
+        mImpl->cleanUp(true);
+    }
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/1.0/Connection.cpp b/media/bufferpool/1.0/Connection.cpp
new file mode 100644
index 0000000..e58f595
--- /dev/null
+++ b/media/bufferpool/1.0/Connection.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 "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
+Return<void> Connection::fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) {
+    ResultStatus status = ResultStatus::CRITICAL_ERROR;
+    if (mInitialized && mAccessor) {
+        if (bufferId != SYNC_BUFFERID) {
+            const native_handle_t *handle = nullptr;
+            status = mAccessor->fetch(
+                    mConnectionId, transactionId, bufferId, &handle);
+            if (status == ResultStatus::OK) {
+                _hidl_cb(status, Buffer{bufferId, handle});
+                return Void();
+            }
+        } else {
+            mAccessor->cleanUp(false);
+        }
+    }
+    _hidl_cb(status, Buffer{0, nullptr});
+    return Void();
+}
+
+Connection::Connection() : mInitialized(false), mConnectionId(-1LL) {}
+
+Connection::~Connection() {
+    if (mInitialized && mAccessor) {
+        mAccessor->close(mConnectionId);
+    }
+}
+
+void Connection::initialize(
+        const sp<Accessor>& accessor, ConnectionId connectionId) {
+    if (!mInitialized) {
+        mAccessor = accessor;
+        mConnectionId = connectionId;
+        mInitialized = true;
+    }
+}
+
+ResultStatus Connection::allocate(
+        const std::vector<uint8_t> &params, BufferId *bufferId,
+        const native_handle_t **handle) {
+    if (mInitialized && mAccessor) {
+        return mAccessor->allocate(mConnectionId, params, bufferId, handle);
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+void Connection::cleanUp(bool clearCache) {
+    if (mInitialized && mAccessor) {
+        mAccessor->cleanUp(clearCache);
+    }
+}
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+//IConnection* HIDL_FETCH_IConnection(const char* /* name */) {
+//    return new Connection();
+//}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/1.0/Connection.h b/media/bufferpool/1.0/Connection.h
new file mode 100644
index 0000000..e19cb67
--- /dev/null
+++ b/media/bufferpool/1.0/Connection.h
@@ -0,0 +1,103 @@
+/*
+ * 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_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
+
+#include <android/hardware/media/bufferpool/1.0/IConnection.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::media::bufferpool::V1_0::implementation::Accessor;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Connection : public IConnection {
+    // Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
+    Return<void> fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override;
+
+    /**
+     * Allocates a buffer using the specified parameters. Recycles a buffer if
+     * it is possible. The returned buffer can be transferred to other remote
+     * clients(Connection).
+     *
+     * @param params    allocation parameters.
+     * @param bufferId  Id of the allocated buffer.
+     * @param handle    native handle of the allocated buffer.
+     *
+     * @return OK if a buffer is successfully allocated.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus allocate(const std::vector<uint8_t> &params,
+                          BufferId *bufferId, const native_handle_t **handle);
+
+    /**
+     * Processes pending buffer status messages and performs periodic cache cleaning
+     * from bufferpool.
+     *
+     * @param clearCache    if clearCache is true, bufferpool frees all buffers
+     *                      waiting to be recycled.
+     */
+    void cleanUp(bool clearCache);
+
+    /** Destructs a connection. */
+    ~Connection();
+
+    /** Creates a connection. */
+    Connection();
+
+    /**
+     * Initializes with the specified buffer pool and the connection id.
+     * The connection id should be unique in the whole system.
+     *
+     * @param accessor      the specified buffer pool.
+     * @param connectionId  Id.
+     */
+    void initialize(const sp<Accessor> &accessor, ConnectionId connectionId);
+
+    enum : uint32_t {
+        SYNC_BUFFERID = UINT32_MAX,
+    };
+
+private:
+    bool mInitialized;
+    sp<Accessor> mAccessor;
+    ConnectionId mConnectionId;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
diff --git a/media/bufferpool/1.0/include/bufferpool/BufferPoolTypes.h b/media/bufferpool/1.0/include/bufferpool/BufferPoolTypes.h
new file mode 100644
index 0000000..710f015
--- /dev/null
+++ b/media/bufferpool/1.0/include/bufferpool/BufferPoolTypes.h
@@ -0,0 +1,118 @@
+/*
+ * 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_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
+
+#include <android/hardware/media/bufferpool/1.0/types.h>
+#include <cutils/native_handle.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+
+struct BufferPoolData {
+    // For local use, to specify a bufferpool (client connection) for buffers.
+    // Return value from connect#IAccessor(android.hardware.media.bufferpool@1.0).
+    int64_t mConnectionId;
+    // BufferId
+    uint32_t mId;
+
+    BufferPoolData() : mConnectionId(0), mId(0) {}
+
+    BufferPoolData(
+            int64_t connectionId, uint32_t id)
+            : mConnectionId(connectionId), mId(id) {}
+
+    ~BufferPoolData() {}
+};
+
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+
+typedef uint32_t BufferId;
+typedef uint64_t TransactionId;
+typedef int64_t ConnectionId;
+
+enum : ConnectionId {
+    INVALID_CONNECTIONID = 0,
+};
+
+typedef android::hardware::MessageQueue<BufferStatusMessage, kSynchronizedReadWrite> BufferStatusQueue;
+typedef BufferStatusQueue::Descriptor QueueDescriptor;
+
+/**
+ * Allocation wrapper class for buffer pool.
+ */
+struct BufferPoolAllocation {
+    const native_handle_t *mHandle;
+
+    const native_handle_t *handle() {
+        return mHandle;
+    }
+
+    BufferPoolAllocation(const native_handle_t *handle) : mHandle(handle) {}
+
+    ~BufferPoolAllocation() {};
+};
+
+/**
+ * Allocator wrapper class for buffer pool.
+ */
+class BufferPoolAllocator {
+public:
+
+    /**
+     * Allocate an allocation(buffer) for buffer pool.
+     *
+     * @param params    allocation parameters
+     * @param alloc     created allocation
+     * @param allocSize size of created allocation
+     *
+     * @return OK when an allocation is created successfully.
+     */
+    virtual ResultStatus allocate(
+            const std::vector<uint8_t> &params,
+            std::shared_ptr<BufferPoolAllocation> *alloc,
+            size_t *allocSize) = 0;
+
+    /**
+     * Returns whether allocation parameters of an old allocation are
+     * compatible with new allocation parameters.
+     */
+    virtual bool compatible(const std::vector<uint8_t> &newParams,
+                            const std::vector<uint8_t> &oldParams) = 0;
+
+protected:
+    BufferPoolAllocator() = default;
+
+    virtual ~BufferPoolAllocator() = default;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
diff --git a/media/bufferpool/1.0/include/bufferpool/ClientManager.h b/media/bufferpool/1.0/include/bufferpool/ClientManager.h
new file mode 100644
index 0000000..be5779f
--- /dev/null
+++ b/media/bufferpool/1.0/include/bufferpool/ClientManager.h
@@ -0,0 +1,179 @@
+/*
+ * 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_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
+
+#include <android/hardware/media/bufferpool/1.0/IClientManager.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <memory>
+#include "BufferPoolTypes.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::media::bufferpool::V1_0::IAccessor;
+using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ClientManager : public IClientManager {
+    // Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
+    Return<void> registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) override;
+
+    /** Gets an instance. */
+    static sp<ClientManager> getInstance();
+
+    /**
+     * Creates a local connection with a newly created buffer pool.
+     *
+     * @param allocator     for new buffer allocation.
+     * @param pConnectionId Id of the created connection. This is
+     *                      system-wide unique.
+     *
+     * @return OK when a buffer pool and a local connection is successfully
+     *         created.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
+                        ConnectionId *pConnectionId);
+
+    /**
+     * Register a created connection as sender for remote process.
+     *
+     * @param receiver      The remote receiving process.
+     * @param senderId      A local connection which will send buffers to.
+     * @param receiverId    Id of the created receiving connection on the receiver
+     *                      process.
+     *
+     * @return OK when the receiving connection is successfully created on the
+     *         receiver process.
+     *         NOT_FOUND when the sender connection was not found.
+     *         ALREADY_EXISTS the receiving connection is already made.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus registerSender(const sp<IClientManager> &receiver,
+                                ConnectionId senderId,
+                                ConnectionId *receiverId);
+
+    /**
+     * Closes the specified connection.
+     *
+     * @param connectionId  The id of the connection.
+     *
+     * @return OK when the connection is closed.
+     *         NOT_FOUND when the specified connection was not found.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus close(ConnectionId connectionId);
+
+    /**
+     * Allocates a buffer from the specified connection.
+     *
+     * @param connectionId  The id of the connection.
+     * @param params        The allocation parameters.
+     * @param handle        The native handle to the allocated buffer. handle
+     *                      should be cloned before use.
+     * @param buffer        The allocated buffer.
+     *
+     * @return OK when a buffer was allocated successfully.
+     *         NOT_FOUND when the specified connection was not found.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus allocate(ConnectionId connectionId,
+                          const std::vector<uint8_t> &params,
+                          native_handle_t **handle,
+                          std::shared_ptr<BufferPoolData> *buffer);
+
+    /**
+     * Receives a buffer for the transaction.
+     *
+     * @param connectionId  The id of the receiving connection.
+     * @param transactionId The id for the transaction.
+     * @param bufferId      The id for the buffer.
+     * @param timestampUs   The timestamp of the buffer is being sent.
+     * @param handle        The native handle to the allocated buffer. handle
+     *                      should be cloned before use.
+     * @param buffer        The received buffer.
+     *
+     * @return OK when a buffer was received successfully.
+     *         NOT_FOUND when the specified connection was not found.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus receive(ConnectionId connectionId,
+                         TransactionId transactionId,
+                         BufferId bufferId,
+                         int64_t timestampUs,
+                          native_handle_t **handle,
+                         std::shared_ptr<BufferPoolData> *buffer);
+
+    /**
+     * Posts a buffer transfer transaction to the buffer pool. Sends a buffer
+     * to other remote clients(connection) after this call has been succeeded.
+     *
+     * @param receiverId    The id of the receiving connection.
+     * @param buffer        to transfer
+     * @param transactionId Id of the transfer transaction.
+     * @param timestampUs   The timestamp of the buffer transaction is being
+     *                      posted.
+     *
+     * @return OK when a buffer transaction was posted successfully.
+     *         NOT_FOUND when the sending connection was not found.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus postSend(ConnectionId receiverId,
+                          const std::shared_ptr<BufferPoolData> &buffer,
+                          TransactionId *transactionId,
+                          int64_t *timestampUs);
+
+    /**
+     *  Time out inactive lingering connections and close.
+     */
+    void cleanUp();
+
+    /** Destructs the manager of buffer pool clients.  */
+    ~ClientManager();
+private:
+    static sp<ClientManager> sInstance;
+    static std::mutex sInstanceLock;
+
+    class Impl;
+    const std::unique_ptr<Impl> mImpl;
+
+    ClientManager();
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
diff --git a/media/bufferpool/1.0/vts/Android.bp b/media/bufferpool/1.0/vts/Android.bp
new file mode 100644
index 0000000..ee5a757
--- /dev/null
+++ b/media/bufferpool/1.0/vts/Android.bp
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+cc_test {
+    name: "VtsVndkHidlBufferpoolV1_0TargetSingleTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "allocator.cpp",
+        "single.cpp",
+    ],
+    static_libs: [
+        "android.hardware.media.bufferpool@1.0",
+        "libion",
+        "libstagefright_bufferpool@1.0",
+    ],
+    shared_libs: [
+        "libfmq",
+    ],
+    compile_multilib: "both",
+}
+
+cc_test {
+    name: "VtsVndkHidlBufferpoolV1_0TargetMultiTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "allocator.cpp",
+        "multi.cpp",
+    ],
+    static_libs: [
+        "android.hardware.media.bufferpool@1.0",
+        "libion",
+        "libstagefright_bufferpool@1.0",
+    ],
+    shared_libs: [
+        "libfmq",
+    ],
+    compile_multilib: "both",
+}
diff --git a/media/bufferpool/1.0/vts/OWNERS b/media/bufferpool/1.0/vts/OWNERS
new file mode 100644
index 0000000..6733e0c
--- /dev/null
+++ b/media/bufferpool/1.0/vts/OWNERS
@@ -0,0 +1,9 @@
+# Media team
+lajos@google.com
+pawin@google.com
+taklee@google.com
+wonsik@google.com
+
+# VTS team
+yim@google.com
+zhuoyao@google.com
diff --git a/media/bufferpool/1.0/vts/allocator.cpp b/media/bufferpool/1.0/vts/allocator.cpp
new file mode 100644
index 0000000..843f7ea
--- /dev/null
+++ b/media/bufferpool/1.0/vts/allocator.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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 <cutils/ashmem.h>
+#include <sys/mman.h>
+#include "allocator.h"
+
+union Params {
+  struct {
+    uint32_t capacity;
+  } data;
+  uint8_t array[0];
+  Params() : data{0} {}
+  Params(uint32_t size)
+      : data{size} {}
+};
+
+
+namespace {
+
+struct HandleAshmem : public native_handle_t {
+  HandleAshmem(int ashmemFd, size_t size)
+    : native_handle_t(cHeader),
+    mFds{ ashmemFd },
+    mInts{ int (size & 0xFFFFFFFF), int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic } {}
+
+  int ashmemFd() const { return mFds.mAshmem; }
+  size_t size() const {
+    return size_t(unsigned(mInts.mSizeLo))
+        | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32);
+  }
+
+  static bool isValid(const native_handle_t * const o);
+
+protected:
+  struct {
+    int mAshmem;
+  } mFds;
+  struct {
+    int mSizeLo;
+    int mSizeHi;
+    int mMagic;
+  } mInts;
+
+private:
+  enum {
+    kMagic = 'ahm\x00',
+    numFds = sizeof(mFds) / sizeof(int),
+    numInts = sizeof(mInts) / sizeof(int),
+    version = sizeof(native_handle_t)
+  };
+  const static native_handle_t cHeader;
+};
+
+const native_handle_t HandleAshmem::cHeader = {
+  HandleAshmem::version,
+  HandleAshmem::numFds,
+  HandleAshmem::numInts,
+  {}
+};
+
+bool HandleAshmem::isValid(const native_handle_t * const o) {
+  if (!o || memcmp(o, &cHeader, sizeof(cHeader))) {
+    return false;
+  }
+  const HandleAshmem *other = static_cast<const HandleAshmem*>(o);
+  return other->mInts.mMagic == kMagic;
+}
+
+class AllocationAshmem {
+private:
+  AllocationAshmem(int ashmemFd, size_t capacity, bool res)
+    : mHandle(ashmemFd, capacity),
+      mInit(res) {}
+
+public:
+  static AllocationAshmem *Alloc(size_t size) {
+    constexpr static const char *kAllocationTag = "bufferpool_test";
+    int ashmemFd = ashmem_create_region(kAllocationTag, size);
+    return new AllocationAshmem(ashmemFd, size, ashmemFd >= 0);
+  }
+
+  ~AllocationAshmem() {
+    if (mInit) {
+      native_handle_close(&mHandle);
+    }
+  }
+
+  const HandleAshmem *handle() {
+    return &mHandle;
+  }
+
+private:
+  HandleAshmem mHandle;
+  bool mInit;
+  // TODO: mapping and map fd
+};
+
+struct AllocationDtor {
+  AllocationDtor(const std::shared_ptr<AllocationAshmem> &alloc)
+      : mAlloc(alloc) {}
+
+  void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; }
+
+  const std::shared_ptr<AllocationAshmem> mAlloc;
+};
+
+}
+
+
+ResultStatus TestBufferPoolAllocator::allocate(
+    const std::vector<uint8_t> &params,
+    std::shared_ptr<BufferPoolAllocation> *alloc,
+    size_t *allocSize) {
+  Params ashmemParams;
+  memcpy(&ashmemParams, params.data(), std::min(sizeof(Params), params.size()));
+
+  std::shared_ptr<AllocationAshmem> ashmemAlloc =
+      std::shared_ptr<AllocationAshmem>(
+          AllocationAshmem::Alloc(ashmemParams.data.capacity));
+  if (ashmemAlloc) {
+    BufferPoolAllocation *ptr = new BufferPoolAllocation(ashmemAlloc->handle());
+    if (ptr) {
+      *alloc = std::shared_ptr<BufferPoolAllocation>(ptr, AllocationDtor(ashmemAlloc));
+      if (*alloc) {
+          *allocSize = ashmemParams.data.capacity;
+          return ResultStatus::OK;
+      }
+      delete ptr;
+      return ResultStatus::NO_MEMORY;
+    }
+  }
+  return ResultStatus::CRITICAL_ERROR;
+}
+
+bool TestBufferPoolAllocator::compatible(const std::vector<uint8_t> &newParams,
+                                        const std::vector<uint8_t> &oldParams) {
+  size_t newSize = newParams.size();
+  size_t oldSize = oldParams.size();
+  if (newSize == oldSize) {
+    for (size_t i = 0; i < newSize; ++i) {
+      if (newParams[i] != oldParams[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+  return false;
+}
+
+bool TestBufferPoolAllocator::Fill(const native_handle_t *handle, const unsigned char val) {
+  if (!HandleAshmem::isValid(handle)) {
+    return false;
+  }
+  const HandleAshmem *o = static_cast<const HandleAshmem*>(handle);
+  unsigned char *ptr = (unsigned char *)mmap(
+      NULL, o->size(), PROT_READ|PROT_WRITE, MAP_SHARED, o->ashmemFd(), 0);
+
+  if (ptr != MAP_FAILED) {
+    for (size_t i = 0; i < o->size(); ++i) {
+      ptr[i] = val;
+    }
+    munmap(ptr, o->size());
+    return true;
+  }
+  return false;
+}
+
+bool TestBufferPoolAllocator::Verify(const native_handle_t *handle, const unsigned char val) {
+  if (!HandleAshmem::isValid(handle)) {
+    return false;
+  }
+  const HandleAshmem *o = static_cast<const HandleAshmem*>(handle);
+  unsigned char *ptr = (unsigned char *)mmap(
+      NULL, o->size(), PROT_READ, MAP_SHARED, o->ashmemFd(), 0);
+
+  if (ptr != MAP_FAILED) {
+    bool res = true;
+    for (size_t i = 0; i < o->size(); ++i) {
+      if (ptr[i] != val) {
+        res = false;
+        break;
+      }
+    }
+    munmap(ptr, o->size());
+    return res;
+  }
+  return false;
+}
+
+void getTestAllocatorParams(std::vector<uint8_t> *params) {
+  constexpr static int kAllocationSize = 1024 * 10;
+  Params ashmemParams(kAllocationSize);
+
+  params->assign(ashmemParams.array, ashmemParams.array + sizeof(ashmemParams));
+}
diff --git a/media/bufferpool/1.0/vts/allocator.h b/media/bufferpool/1.0/vts/allocator.h
new file mode 100644
index 0000000..886e5f2
--- /dev/null
+++ b/media/bufferpool/1.0/vts/allocator.h
@@ -0,0 +1,51 @@
+/*
+ * 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 VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
+#define VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
+
+#include <bufferpool/BufferPoolTypes.h>
+
+using android::hardware::media::bufferpool::V1_0::ResultStatus;
+using android::hardware::media::bufferpool::V1_0::implementation::
+    BufferPoolAllocation;
+using android::hardware::media::bufferpool::V1_0::implementation::
+    BufferPoolAllocator;
+
+// buffer allocator for the tests
+class TestBufferPoolAllocator : public BufferPoolAllocator {
+ public:
+  TestBufferPoolAllocator() {}
+
+  ~TestBufferPoolAllocator() override {}
+
+  ResultStatus allocate(const std::vector<uint8_t> &params,
+                        std::shared_ptr<BufferPoolAllocation> *alloc,
+                        size_t *allocSize) override;
+
+  bool compatible(const std::vector<uint8_t> &newParams,
+                  const std::vector<uint8_t> &oldParams) override;
+
+  static bool Fill(const native_handle_t *handle, const unsigned char val);
+
+  static bool Verify(const native_handle_t *handle, const unsigned char val);
+
+};
+
+// retrieve buffer allocator paramters
+void getTestAllocatorParams(std::vector<uint8_t> *params);
+
+#endif  // VNDK_HIDL_BUFFERPOOL_V1_0_ALLOCATOR_H
diff --git a/media/bufferpool/1.0/vts/multi.cpp b/media/bufferpool/1.0/vts/multi.cpp
new file mode 100644
index 0000000..1796819
--- /dev/null
+++ b/media/bufferpool/1.0/vts/multi.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "buffferpool_unit_test"
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+#include <hidl/Status.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+#include "allocator.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::hidl_handle;
+using android::hardware::media::bufferpool::V1_0::IClientManager;
+using android::hardware::media::bufferpool::V1_0::ResultStatus;
+using android::hardware::media::bufferpool::V1_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V1_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V1_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V1_0::implementation::TransactionId;
+using android::hardware::media::bufferpool::BufferPoolData;
+
+namespace {
+
+// communication message types between processes.
+enum PipeCommand : int32_t {
+    INIT_OK = 0,
+    INIT_ERROR,
+    SEND,
+    RECEIVE_OK,
+    RECEIVE_ERROR,
+};
+
+// communication message between processes.
+union PipeMessage {
+    struct  {
+        int32_t command;
+        BufferId bufferId;
+        ConnectionId connectionId;
+        TransactionId transactionId;
+        int64_t  timestampUs;
+    } data;
+    char array[0];
+};
+
+// media.bufferpool test setup
+class BufferpoolMultiTest : public ::testing::Test {
+ public:
+  virtual void SetUp() override {
+    ResultStatus status;
+    mReceiverPid = -1;
+    mConnectionValid = false;
+
+    ASSERT_TRUE(pipe(mCommandPipeFds) == 0);
+    ASSERT_TRUE(pipe(mResultPipeFds) == 0);
+
+    mReceiverPid = fork();
+    ASSERT_TRUE(mReceiverPid >= 0);
+
+    if (mReceiverPid == 0) {
+      doReceiver();
+      // In order to ignore gtest behaviour, wait for being killed from
+      // tearDown
+      pause();
+    }
+
+    mManager = ClientManager::getInstance();
+    ASSERT_NE(mManager, nullptr);
+
+    mAllocator = std::make_shared<TestBufferPoolAllocator>();
+    ASSERT_TRUE((bool)mAllocator);
+
+    status = mManager->create(mAllocator, &mConnectionId);
+    ASSERT_TRUE(status == ResultStatus::OK);
+    mConnectionValid = true;
+  }
+
+  virtual void TearDown() override {
+    if (mReceiverPid > 0) {
+      kill(mReceiverPid, SIGKILL);
+      int wstatus;
+      wait(&wstatus);
+    }
+
+    if (mConnectionValid) {
+      mManager->close(mConnectionId);
+    }
+  }
+
+ protected:
+  static void description(const std::string& description) {
+    RecordProperty("description", description);
+  }
+
+  android::sp<ClientManager> mManager;
+  std::shared_ptr<BufferPoolAllocator> mAllocator;
+  bool mConnectionValid;
+  ConnectionId mConnectionId;
+  pid_t mReceiverPid;
+  int mCommandPipeFds[2];
+  int mResultPipeFds[2];
+
+  bool sendMessage(int *pipes, const PipeMessage &message) {
+    int ret = write(pipes[1], message.array, sizeof(PipeMessage));
+    return ret == sizeof(PipeMessage);
+  }
+
+  bool receiveMessage(int *pipes, PipeMessage *message) {
+    int ret = read(pipes[0], message->array, sizeof(PipeMessage));
+    return ret == sizeof(PipeMessage);
+  }
+
+  void doReceiver() {
+    configureRpcThreadpool(1, false);
+    PipeMessage message;
+    mManager = ClientManager::getInstance();
+    if (!mManager) {
+      message.data.command = PipeCommand::INIT_ERROR;
+      sendMessage(mResultPipeFds, message);
+      return;
+    }
+    android::status_t status = mManager->registerAsService();
+    if (status != android::OK) {
+      message.data.command = PipeCommand::INIT_ERROR;
+      sendMessage(mResultPipeFds, message);
+      return;
+    }
+    message.data.command = PipeCommand::INIT_OK;
+    sendMessage(mResultPipeFds, message);
+
+    receiveMessage(mCommandPipeFds, &message);
+    {
+      native_handle_t *rhandle = nullptr;
+      std::shared_ptr<BufferPoolData> rbuffer;
+      ResultStatus status = mManager->receive(
+          message.data.connectionId, message.data.transactionId,
+          message.data.bufferId, message.data.timestampUs, &rhandle, &rbuffer);
+      mManager->close(message.data.connectionId);
+      if (status != ResultStatus::OK) {
+        if (!TestBufferPoolAllocator::Verify(rhandle, 0x77)) {
+          message.data.command = PipeCommand::RECEIVE_ERROR;
+          sendMessage(mResultPipeFds, message);
+          return;
+        }
+      }
+    }
+    message.data.command = PipeCommand::RECEIVE_OK;
+    sendMessage(mResultPipeFds, message);
+  }
+};
+
+// Buffer transfer test between processes.
+TEST_F(BufferpoolMultiTest, TransferBuffer) {
+  ResultStatus status;
+  PipeMessage message;
+
+  ASSERT_TRUE(receiveMessage(mResultPipeFds, &message));
+
+  android::sp<IClientManager> receiver = IClientManager::getService();
+  ConnectionId receiverId;
+  ASSERT_TRUE((bool)receiver);
+
+  status = mManager->registerSender(receiver, mConnectionId, &receiverId);
+  ASSERT_TRUE(status == ResultStatus::OK);
+  {
+    native_handle_t *shandle = nullptr;
+    std::shared_ptr<BufferPoolData> sbuffer;
+    TransactionId transactionId;
+    int64_t postUs;
+    std::vector<uint8_t> vecParams;
+
+    getTestAllocatorParams(&vecParams);
+    status = mManager->allocate(mConnectionId, vecParams, &shandle, &sbuffer);
+    ASSERT_TRUE(status == ResultStatus::OK);
+
+    ASSERT_TRUE(TestBufferPoolAllocator::Fill(shandle, 0x77));
+
+    status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs);
+    ASSERT_TRUE(status == ResultStatus::OK);
+
+    message.data.command = PipeCommand::SEND;
+    message.data.bufferId = sbuffer->mId;
+    message.data.connectionId = receiverId;
+    message.data.transactionId = transactionId;
+    message.data.timestampUs = postUs;
+    sendMessage(mCommandPipeFds, message);
+  }
+  EXPECT_TRUE(receiveMessage(mResultPipeFds, &message));
+}
+
+}  // anonymous namespace
+
+int main(int argc, char** argv) {
+  setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+  ::testing::InitGoogleTest(&argc, argv);
+  int status = RUN_ALL_TESTS();
+  LOG(INFO) << "Test result = " << status;
+  return status;
+}
diff --git a/media/bufferpool/1.0/vts/single.cpp b/media/bufferpool/1.0/vts/single.cpp
new file mode 100644
index 0000000..f73eb62
--- /dev/null
+++ b/media/bufferpool/1.0/vts/single.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "buffferpool_unit_test"
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+#include <hidl/Status.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+#include "allocator.h"
+
+using android::hardware::hidl_handle;
+using android::hardware::media::bufferpool::V1_0::ResultStatus;
+using android::hardware::media::bufferpool::V1_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V1_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V1_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V1_0::implementation::TransactionId;
+using android::hardware::media::bufferpool::BufferPoolData;
+
+namespace {
+
+// Number of iteration for buffer allocation test.
+constexpr static int kNumAllocationTest = 3;
+
+// Number of iteration for buffer recycling test.
+constexpr static int kNumRecycleTest = 3;
+
+// media.bufferpool test setup
+class BufferpoolSingleTest : public ::testing::Test {
+ public:
+  virtual void SetUp() override {
+    ResultStatus status;
+    mConnectionValid = false;
+
+    mManager = ClientManager::getInstance();
+    ASSERT_NE(mManager, nullptr);
+
+    mAllocator = std::make_shared<TestBufferPoolAllocator>();
+    ASSERT_TRUE((bool)mAllocator);
+
+    status = mManager->create(mAllocator, &mConnectionId);
+    ASSERT_TRUE(status == ResultStatus::OK);
+
+    mConnectionValid = true;
+
+    status = mManager->registerSender(mManager, mConnectionId, &mReceiverId);
+    ASSERT_TRUE(status == ResultStatus::ALREADY_EXISTS &&
+                mReceiverId == mConnectionId);
+  }
+
+  virtual void TearDown() override {
+    if (mConnectionValid) {
+      mManager->close(mConnectionId);
+    }
+  }
+
+ protected:
+  static void description(const std::string& description) {
+    RecordProperty("description", description);
+  }
+
+  android::sp<ClientManager> mManager;
+  std::shared_ptr<BufferPoolAllocator> mAllocator;
+  bool mConnectionValid;
+  ConnectionId mConnectionId;
+  ConnectionId mReceiverId;
+
+};
+
+// Buffer allocation test.
+// Check whether each buffer allocation is done successfully with
+// unique buffer id.
+TEST_F(BufferpoolSingleTest, AllocateBuffer) {
+  ResultStatus status;
+  std::vector<uint8_t> vecParams;
+  getTestAllocatorParams(&vecParams);
+
+  std::shared_ptr<BufferPoolData> buffer[kNumAllocationTest];
+  native_handle_t *allocHandle = nullptr;
+  for (int i = 0; i < kNumAllocationTest; ++i) {
+    status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer[i]);
+    ASSERT_TRUE(status == ResultStatus::OK);
+  }
+  for (int i = 0; i < kNumAllocationTest; ++i) {
+    for (int j = i + 1; j < kNumAllocationTest; ++j) {
+      ASSERT_TRUE(buffer[i]->mId != buffer[j]->mId);
+    }
+  }
+  EXPECT_TRUE(kNumAllocationTest > 1);
+}
+
+// Buffer recycle test.
+// Check whether de-allocated buffers are recycled.
+TEST_F(BufferpoolSingleTest, RecycleBuffer) {
+  ResultStatus status;
+  std::vector<uint8_t> vecParams;
+  getTestAllocatorParams(&vecParams);
+
+  BufferId bid[kNumRecycleTest];
+  for (int i = 0; i < kNumRecycleTest; ++i) {
+    std::shared_ptr<BufferPoolData> buffer;
+    native_handle_t *allocHandle = nullptr;
+    status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer);
+    ASSERT_TRUE(status == ResultStatus::OK);
+    bid[i] = buffer->mId;
+  }
+  for (int i = 1; i < kNumRecycleTest; ++i) {
+    ASSERT_TRUE(bid[i - 1] == bid[i]);
+  }
+  EXPECT_TRUE(kNumRecycleTest > 1);
+}
+
+// Buffer transfer test.
+// Check whether buffer is transferred to another client successfully.
+TEST_F(BufferpoolSingleTest, TransferBuffer) {
+  ResultStatus status;
+  std::vector<uint8_t> vecParams;
+  getTestAllocatorParams(&vecParams);
+  std::shared_ptr<BufferPoolData> sbuffer, rbuffer;
+  native_handle_t *allocHandle = nullptr;
+  native_handle_t *recvHandle = nullptr;
+
+  TransactionId transactionId;
+  int64_t postUs;
+
+  status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &sbuffer);
+  ASSERT_TRUE(status == ResultStatus::OK);
+  ASSERT_TRUE(TestBufferPoolAllocator::Fill(allocHandle, 0x77));
+  status = mManager->postSend(mReceiverId, sbuffer, &transactionId, &postUs);
+  ASSERT_TRUE(status == ResultStatus::OK);
+  status = mManager->receive(mReceiverId, transactionId, sbuffer->mId, postUs,
+                             &recvHandle, &rbuffer);
+  EXPECT_TRUE(status == ResultStatus::OK);
+  ASSERT_TRUE(TestBufferPoolAllocator::Verify(recvHandle, 0x77));
+}
+
+}  // anonymous namespace
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  int status = RUN_ALL_TESTS();
+  LOG(INFO) << "Test result = " << status;
+  return status;
+}
diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp
index eda7b61..4d0a4cc 100644
--- a/media/extractors/flac/Android.bp
+++ b/media/extractors/flac/Android.bp
@@ -8,12 +8,14 @@
     ],
 
     shared_libs: [
+        "libbinder_ndk",
         "liblog",
         "libmediaextractor",
         "libmediandk",
     ],
 
     static_libs: [
+        "libaudioutils",
         "libFLAC",
         "libstagefright_foundation",
         "libstagefright_metadatautils",
diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp
index be6d9fd..d54aaef 100644
--- a/media/extractors/flac/FLACExtractor.cpp
+++ b/media/extractors/flac/FLACExtractor.cpp
@@ -24,6 +24,8 @@
 // libFLAC parser
 #include "FLAC/stream_decoder.h"
 
+#include <android/binder_ibinder.h> // for AIBinder_getCallingUid
+#include <audio_utils/primitives.h>
 #include <media/MediaExtractorPluginApi.h>
 #include <media/NdkMediaFormat.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -35,9 +37,20 @@
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/MetaDataUtils.h>
 #include <media/stagefright/MediaBufferBase.h>
+#include <private/android_filesystem_config.h> // for AID_MEDIA
+#include <system/audio.h>
 
 namespace android {
 
+// MediaServer is capable of handling float extractor output, but general processes
+// may not be able to do so.
+// TODO: Improve API to set extractor float output.
+// (Note: duplicated with WAVExtractor.cpp)
+static inline bool shouldExtractorOutputFloat(int bitsPerSample)
+{
+    return bitsPerSample > 16 && AIBinder_getCallingUid() == AID_MEDIA;
+}
+
 class FLACParser;
 
 class FLACSource : public MediaTrackHelperV2 {
@@ -45,7 +58,8 @@
 public:
     FLACSource(
             DataSourceHelper *dataSource,
-            AMediaFormat *meta);
+            AMediaFormat *meta,
+            bool outputFloat);
 
     virtual media_status_t start();
     virtual media_status_t stop();
@@ -60,6 +74,7 @@
 private:
     DataSourceHelper *mDataSource;
     AMediaFormat *mTrackMetadata;
+    const bool mOutputFloat;
     FLACParser *mParser;
     bool mInitCheck;
     bool mStarted;
@@ -76,11 +91,12 @@
 
 public:
     enum {
-        kMaxChannels = 8,
+        kMaxChannels = FCC_8,
     };
 
     explicit FLACParser(
         DataSourceHelper *dataSource,
+        bool outputFloat,
         // If metadata pointers aren't provided, we don't fill them
         AMediaFormat *fileMetadata = 0,
         AMediaFormat *trackMetadata = 0);
@@ -120,6 +136,7 @@
 
 private:
     DataSourceHelper *mDataSource;
+    const bool mOutputFloat;
     AMediaFormat *mFileMetadata;
     AMediaFormat *mTrackMetadata;
     bool mInitCheck;
@@ -170,6 +187,7 @@
             const FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
     void metadataCallback(const FLAC__StreamMetadata *metadata);
     void errorCallback(FLAC__StreamDecoderErrorStatus status);
+    size_t getOutputSampleSize() const { return mOutputFloat ? sizeof(float) : sizeof(int16_t); }
 
     // FLAC parser callbacks as C-callable functions
     static FLAC__StreamDecoderReadStatus read_callback(
@@ -381,122 +399,51 @@
     mErrorStatus = status;
 }
 
-// Copy samples from FLAC native 32-bit non-interleaved to 16-bit interleaved.
+// Copy samples from FLAC native 32-bit non-interleaved to 16-bit signed
+// or 32-bit float interleaved.
 // These are candidates for optimization if needed.
-
-static void copyMono8(
-        int16_t *dst,
-        const int * src[FLACParser::kMaxChannels],
+static void copyTo16Signed(
+        short *dst,
+        const int *const *src,
         unsigned nSamples,
-        unsigned /* nChannels */) {
-    for (unsigned i = 0; i < nSamples; ++i) {
-        *dst++ = src[0][i] << 8;
-    }
-}
-
-static void copyStereo8(
-        int16_t *dst,
-        const int * src[FLACParser::kMaxChannels],
-        unsigned nSamples,
-        unsigned /* nChannels */) {
-    for (unsigned i = 0; i < nSamples; ++i) {
-        *dst++ = src[0][i] << 8;
-        *dst++ = src[1][i] << 8;
-    }
-}
-
-static void copyMultiCh8(int16_t *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
-{
+        unsigned nChannels,
+        unsigned bitsPerSample) {
+    const unsigned leftShift = 16 - bitsPerSample;
     for (unsigned i = 0; i < nSamples; ++i) {
         for (unsigned c = 0; c < nChannels; ++c) {
-            *dst++ = src[c][i] << 8;
+            *dst++ = src[c][i] << leftShift;
         }
     }
 }
 
-static void copyMono16(
-        int16_t *dst,
-        const int * src[FLACParser::kMaxChannels],
+static void copyToFloat(
+        float *dst,
+        const int *const *src,
         unsigned nSamples,
-        unsigned /* nChannels */) {
-    for (unsigned i = 0; i < nSamples; ++i) {
-        *dst++ = src[0][i];
-    }
-}
-
-static void copyStereo16(
-        int16_t *dst,
-        const int * src[FLACParser::kMaxChannels],
-        unsigned nSamples,
-        unsigned /* nChannels */) {
-    for (unsigned i = 0; i < nSamples; ++i) {
-        *dst++ = src[0][i];
-        *dst++ = src[1][i];
-    }
-}
-
-static void copyMultiCh16(int16_t *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
-{
+        unsigned nChannels,
+        unsigned bitsPerSample) {
+    const unsigned leftShift = 32 - bitsPerSample;
     for (unsigned i = 0; i < nSamples; ++i) {
         for (unsigned c = 0; c < nChannels; ++c) {
-            *dst++ = src[c][i];
+            *dst++ = float_from_i32(src[c][i] << leftShift);
         }
     }
 }
 
-// 24-bit versions should do dithering or noise-shaping, here or in AudioFlinger
-
-static void copyMono24(
-        int16_t *dst,
-        const int * src[FLACParser::kMaxChannels],
-        unsigned nSamples,
-        unsigned /* nChannels */) {
-    for (unsigned i = 0; i < nSamples; ++i) {
-        *dst++ = src[0][i] >> 8;
-    }
-}
-
-static void copyStereo24(
-        int16_t *dst,
-        const int * src[FLACParser::kMaxChannels],
-        unsigned nSamples,
-        unsigned /* nChannels */) {
-    for (unsigned i = 0; i < nSamples; ++i) {
-        *dst++ = src[0][i] >> 8;
-        *dst++ = src[1][i] >> 8;
-    }
-}
-
-static void copyMultiCh24(int16_t *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
-{
-    for (unsigned i = 0; i < nSamples; ++i) {
-        for (unsigned c = 0; c < nChannels; ++c) {
-            *dst++ = src[c][i] >> 8;
-        }
-    }
-}
-
-static void copyTrespass(
-        int16_t * /* dst */,
-        const int *[FLACParser::kMaxChannels] /* src */,
-        unsigned /* nSamples */,
-        unsigned /* nChannels */) {
-    TRESPASS();
-}
-
 // FLACParser
 
 FLACParser::FLACParser(
         DataSourceHelper *dataSource,
+        bool outputFloat,
         AMediaFormat *fileMetadata,
         AMediaFormat *trackMetadata)
     : mDataSource(dataSource),
+      mOutputFloat(outputFloat),
       mFileMetadata(fileMetadata),
       mTrackMetadata(trackMetadata),
       mInitCheck(false),
       mMaxBufferSize(0),
       mGroup(NULL),
-      mCopy(copyTrespass),
       mDecoder(NULL),
       mCurrentPos(0LL),
       mEOF(false),
@@ -590,29 +537,6 @@
             ALOGE("unsupported sample rate %u", getSampleRate());
             return NO_INIT;
         }
-        // configure the appropriate copy function, defaulting to trespass
-        static const struct {
-            unsigned mChannels;
-            unsigned mBitsPerSample;
-            void (*mCopy)(int16_t *dst, const int * src[kMaxChannels], unsigned nSamples, unsigned nChannels);
-        } table[] = {
-            { 1,  8, copyMono8    },
-            { 2,  8, copyStereo8  },
-            { 8,  8, copyMultiCh8  },
-            { 1, 16, copyMono16   },
-            { 2, 16, copyStereo16 },
-            { 8, 16, copyMultiCh16 },
-            { 1, 24, copyMono24   },
-            { 2, 24, copyStereo24 },
-            { 8, 24, copyMultiCh24 },
-        };
-        for (unsigned i = 0; i < sizeof(table)/sizeof(table[0]); ++i) {
-            if (table[i].mChannels >= getChannels() &&
-                    table[i].mBitsPerSample == getBitsPerSample()) {
-                mCopy = table[i].mCopy;
-                break;
-            }
-        }
         // populate track metadata
         if (mTrackMetadata != 0) {
             AMediaFormat_setString(mTrackMetadata,
@@ -623,8 +547,6 @@
                     AMEDIAFORMAT_KEY_SAMPLE_RATE, getSampleRate());
             AMediaFormat_setInt32(mTrackMetadata,
                     AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, getBitsPerSample());
-            AMediaFormat_setInt32(mTrackMetadata,
-                    AMEDIAFORMAT_KEY_PCM_ENCODING, kAudioEncodingPcm16bit);
             // sample rate is non-zero, so division by zero not possible
             AMediaFormat_setInt64(mTrackMetadata,
                     AMEDIAFORMAT_KEY_DURATION, (getTotalSamples() * 1000000LL) / getSampleRate());
@@ -644,7 +566,7 @@
 {
     CHECK(mGroup == NULL);
     mGroup = new MediaBufferGroup;
-    mMaxBufferSize = getMaxBlockSize() * getChannels() * sizeof(int16_t);
+    mMaxBufferSize = getMaxBlockSize() * getChannels() * getOutputSampleSize();
     mGroup->add_buffer(MediaBufferBase::Create(mMaxBufferSize));
 }
 
@@ -697,12 +619,24 @@
     if (err != OK) {
         return NULL;
     }
-    size_t bufferSize = blocksize * getChannels() * sizeof(int16_t);
+    const size_t bufferSize = blocksize * getChannels() * getOutputSampleSize();
     CHECK(bufferSize <= mMaxBufferSize);
-    int16_t *data = (int16_t *) buffer->data();
     buffer->set_range(0, bufferSize);
     // copy PCM from FLAC write buffer to our media buffer, with interleaving
-    (*mCopy)(data, mWriteBuffer, blocksize, getChannels());
+    const unsigned bitsPerSample = getBitsPerSample();
+    if (mOutputFloat) {
+        copyToFloat(reinterpret_cast<float*>(buffer->data()),
+                    mWriteBuffer,
+                    blocksize,
+                    getChannels(),
+                    bitsPerSample);
+    } else {
+        copyTo16Signed(reinterpret_cast<short*>(buffer->data()),
+                       mWriteBuffer,
+                       blocksize,
+                       getChannels(),
+                       bitsPerSample);
+    }
     // fill in buffer metadata
     CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
     FLAC__uint64 sampleNumber = mWriteHeader.number.sample_number;
@@ -716,17 +650,16 @@
 
 FLACSource::FLACSource(
         DataSourceHelper *dataSource,
-        AMediaFormat *trackMetadata)
+        AMediaFormat *trackMetadata,
+        bool outputFloat)
     : mDataSource(dataSource),
       mTrackMetadata(trackMetadata),
-      mParser(0),
-      mInitCheck(false),
+      mOutputFloat(outputFloat),
+      mParser(new FLACParser(mDataSource, outputFloat)),
+      mInitCheck(mParser->initCheck()),
       mStarted(false)
 {
     ALOGV("FLACSource::FLACSource");
-    // re-use the same track metadata passed into constructor from FLACExtractor
-    mParser = new FLACParser(mDataSource);
-    mInitCheck  = mParser->initCheck();
 }
 
 FLACSource::~FLACSource()
@@ -762,7 +695,12 @@
 
 media_status_t FLACSource::getFormat(AMediaFormat *meta)
 {
-    return AMediaFormat_copy(meta, mTrackMetadata);
+    const media_status_t status = AMediaFormat_copy(meta, mTrackMetadata);
+    if (status == OK) {
+        AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_PCM_ENCODING,
+                mOutputFloat ? kAudioEncodingPcmFloat : kAudioEncodingPcm16bit);
+    }
+    return status;
 }
 
 media_status_t FLACSource::read(
@@ -804,7 +742,7 @@
     // FLACParser will fill in the metadata for us
     mFileMetadata = AMediaFormat_new();
     mTrackMetadata = AMediaFormat_new();
-    mParser = new FLACParser(mDataSource, mFileMetadata, mTrackMetadata);
+    mParser = new FLACParser(mDataSource, false /* outputFloat */, mFileMetadata, mTrackMetadata);
     mInitCheck = mParser->initCheck();
 }
 
@@ -827,7 +765,9 @@
     if (mInitCheck != OK || index > 0) {
         return NULL;
     }
-    return new FLACSource(mDataSource, mTrackMetadata);
+
+    return new FLACSource(
+            mDataSource, mTrackMetadata, shouldExtractorOutputFloat(mParser->getBitsPerSample()));
 }
 
 media_status_t FLACExtractor::getTrackMetaData(
@@ -836,7 +776,13 @@
     if (mInitCheck != OK || index > 0) {
         return AMEDIA_ERROR_UNKNOWN;
     }
-    return AMediaFormat_copy(meta, mTrackMetadata);
+    const media_status_t status = AMediaFormat_copy(meta, mTrackMetadata);
+    if (status == OK) {
+        AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_PCM_ENCODING,
+                shouldExtractorOutputFloat(mParser->getBitsPerSample())
+                        ? kAudioEncodingPcmFloat : kAudioEncodingPcm16bit);
+    }
+    return status;
 }
 
 media_status_t FLACExtractor::getMetaData(AMediaFormat *meta)
diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp
index 621fd03..0e1ffb4 100644
--- a/media/extractors/mp3/MP3Extractor.cpp
+++ b/media/extractors/mp3/MP3Extractor.cpp
@@ -546,7 +546,8 @@
             buffer->release();
             buffer = NULL;
 
-            return (n < 0 ? AMEDIA_ERROR_UNKNOWN : AMEDIA_ERROR_END_OF_STREAM);
+            return ((n < 0 && n != ERROR_END_OF_STREAM) ?
+                    AMEDIA_ERROR_UNKNOWN : AMEDIA_ERROR_END_OF_STREAM);
         }
 
         uint32_t header = U32_AT((const uint8_t *)buffer->data());
@@ -590,7 +591,8 @@
         buffer->release();
         buffer = NULL;
 
-        return (n < 0 ? AMEDIA_ERROR_UNKNOWN : AMEDIA_ERROR_END_OF_STREAM);
+        return ((n < 0 && n != ERROR_END_OF_STREAM) ?
+                AMEDIA_ERROR_UNKNOWN : AMEDIA_ERROR_END_OF_STREAM);
     }
 
     buffer->set_range(0, frame_size);
diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp
index 40b2c97..47dc718 100644
--- a/media/extractors/mp4/Android.bp
+++ b/media/extractors/mp4/Android.bp
@@ -16,6 +16,7 @@
     shared_libs: [
         "liblog",
         "libmediaextractor",
+        "libmediandk"
     ],
 
     static_libs: [
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index a61e60a..55a0c47 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -1477,7 +1477,7 @@
     return mImageItemsValid ? mDisplayables.size() : 0;
 }
 
-sp<MetaData> ItemTable::getImageMeta(const uint32_t imageIndex) {
+AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) {
     if (!mImageItemsValid) {
         return NULL;
     }
@@ -1502,28 +1502,31 @@
         }
     }
 
-    sp<MetaData> meta = new MetaData;
-    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
+    AMediaFormat *meta = AMediaFormat_new();
+    AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
 
     if (image->itemId == mPrimaryItemId) {
-        meta->setInt32(kKeyTrackIsDefault, 1);
+        AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_DEFAULT, 1);
     }
 
     ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
 
-    meta->setInt32(kKeyWidth, image->width);
-    meta->setInt32(kKeyHeight, image->height);
+    AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, image->width);
+    AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, image->height);
     if (image->rotation != 0) {
         // Rotation angle in HEIF is CCW, convert to CW here to be
         // consistent with the other media formats.
         switch(image->rotation) {
-            case 90: meta->setInt32(kKeyRotation, 270); break;
-            case 180: meta->setInt32(kKeyRotation, 180); break;
-            case 270: meta->setInt32(kKeyRotation, 90); break;
+            case 90:
+            case 180:
+            case 270:
+                AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_ROTATION, 360 - image->rotation);
+                break;
             default: break; // don't set if invalid
         }
     }
-    meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
+    AMediaFormat_setInt32(meta,
+            AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
 
     if (!image->thumbnails.empty()) {
         ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
@@ -1531,10 +1534,12 @@
             const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
 
             if (thumbnail.hvcc != NULL) {
-                meta->setInt32(kKeyThumbnailWidth, thumbnail.width);
-                meta->setInt32(kKeyThumbnailHeight, thumbnail.height);
-                meta->setData(kKeyThumbnailHVCC, kTypeHVCC,
-                        thumbnail.hvcc->data(), thumbnail.hvcc->size());
+                AMediaFormat_setInt32(meta,
+                        AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH, thumbnail.width);
+                AMediaFormat_setInt32(meta,
+                        AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT, thumbnail.height);
+                AMediaFormat_setBuffer(meta,
+                        AMEDIAFORMAT_KEY_CSD_HEVC, thumbnail.hvcc->data(), thumbnail.hvcc->size());
                 ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
                         imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
             } else {
@@ -1546,24 +1551,30 @@
     }
 
     if (image->isGrid()) {
-        meta->setInt32(kKeyGridRows, image->rows);
-        meta->setInt32(kKeyGridCols, image->columns);
-
+        AMediaFormat_setInt32(meta,
+                AMEDIAFORMAT_KEY_GRID_ROWS, image->rows);
+        AMediaFormat_setInt32(meta,
+                AMEDIAFORMAT_KEY_GRID_COLUMNS, image->columns);
         // point image to the first tile for grid size and HVCC
         image = &mItemIdToItemMap.editValueAt(tileItemIndex);
-        meta->setInt32(kKeyTileWidth, image->width);
-        meta->setInt32(kKeyTileHeight, image->height);
-        meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
+        AMediaFormat_setInt32(meta,
+                AMEDIAFORMAT_KEY_TILE_WIDTH, image->width);
+        AMediaFormat_setInt32(meta,
+                AMEDIAFORMAT_KEY_TILE_HEIGHT, image->height);
+        AMediaFormat_setInt32(meta,
+                AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 1.5);
     }
 
     if (image->hvcc == NULL) {
         ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
         return NULL;
     }
-    meta->setData(kKeyHVCC, kTypeHVCC, image->hvcc->data(), image->hvcc->size());
+    AMediaFormat_setBuffer(meta,
+            AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
 
     if (image->icc != NULL) {
-        meta->setData(kKeyIccProfile, 0, image->icc->data(), image->icc->size());
+        AMediaFormat_setBuffer(meta,
+                AMEDIAFORMAT_KEY_ICC_PROFILE, image->icc->data(), image->icc->size());
     }
     return meta;
 }
diff --git a/media/extractors/mp4/ItemTable.h b/media/extractors/mp4/ItemTable.h
index 650b3f3..be81b59 100644
--- a/media/extractors/mp4/ItemTable.h
+++ b/media/extractors/mp4/ItemTable.h
@@ -19,6 +19,8 @@
 
 #include <set>
 
+#include <media/NdkMediaFormat.h>
+
 #include <media/stagefright/foundation/ADebug.h>
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
@@ -51,7 +53,7 @@
 
     bool isValid() { return mImageItemsValid; }
     uint32_t countImages() const;
-    sp<MetaData> getImageMeta(const uint32_t imageIndex);
+    AMediaFormat *getImageMeta(const uint32_t imageIndex);
     status_t findImageItem(const uint32_t imageIndex, uint32_t *itemIndex);
     status_t findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex);
     status_t getImageOffsetAndSize(
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 7323f43..a11acc1 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -69,11 +69,11 @@
     kMaxAtomSize = 64 * 1024 * 1024,
 };
 
-class MPEG4Source : public MediaTrackHelper {
+class MPEG4Source : public MediaTrackHelperV2 {
 static const size_t  kMaxPcmFrameSize = 8192;
 public:
     // Caller retains ownership of both "dataSource" and "sampleTable".
-    MPEG4Source(MetaDataBase &format,
+    MPEG4Source(AMediaFormat *format,
                 DataSourceHelper *dataSource,
                 int32_t timeScale,
                 const sp<SampleTable> &sampleTable,
@@ -83,21 +83,22 @@
                 const sp<ItemTable> &itemTable);
     virtual status_t init();
 
-    virtual status_t start();
-    virtual status_t stop();
+    virtual media_status_t start();
+    virtual media_status_t stop();
 
-    virtual status_t getFormat(MetaDataBase &);
+    virtual media_status_t getFormat(AMediaFormat *);
 
-    virtual status_t read(MediaBufferBase **buffer, const ReadOptions *options = NULL);
+    virtual media_status_t read(MediaBufferBase **buffer, const ReadOptions *options = NULL);
     virtual bool supportNonblockingRead() { return true; }
-    virtual status_t fragmentedRead(MediaBufferBase **buffer, const ReadOptions *options = NULL);
+    virtual media_status_t fragmentedRead(
+            MediaBufferBase **buffer, const ReadOptions *options = NULL);
 
     virtual ~MPEG4Source();
 
 private:
     Mutex mLock;
 
-    MetaDataBase &mFormat;
+    AMediaFormat *mFormat;
     DataSourceHelper *mDataSource;
     int32_t mTimescale;
     sp<SampleTable> mSampleTable;
@@ -379,6 +380,7 @@
       mFirstTrack(NULL),
       mLastTrack(NULL) {
     ALOGV("mime=%s, mPreferHeif=%d", mime, mPreferHeif);
+    mFileMetaData = AMediaFormat_new();
 }
 
 MPEG4Extractor::~MPEG4Extractor() {
@@ -386,6 +388,7 @@
     while (track) {
         Track *next = track->next;
 
+        AMediaFormat_delete(track->meta);
         delete track;
         track = next;
     }
@@ -397,6 +400,7 @@
     mPssh.clear();
 
     delete mDataSource;
+    AMediaFormat_delete(mFileMetaData);
 }
 
 uint32_t MPEG4Extractor::flags() const {
@@ -405,13 +409,13 @@
                     (CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK) : 0);
 }
 
-status_t MPEG4Extractor::getMetaData(MetaDataBase &meta) {
+media_status_t MPEG4Extractor::getMetaData(AMediaFormat *meta) {
     status_t err;
     if ((err = readMetaData()) != OK) {
-        return UNKNOWN_ERROR;
+        return AMEDIA_ERROR_UNKNOWN;
     }
-    meta = mFileMetaData;
-    return OK;
+    AMediaFormat_copy(meta, mFileMetaData);
+    return AMEDIA_OK;
 }
 
 size_t MPEG4Extractor::countTracks() {
@@ -432,18 +436,18 @@
     return n;
 }
 
-status_t MPEG4Extractor::getTrackMetaData(
-        MetaDataBase &meta,
+media_status_t MPEG4Extractor::getTrackMetaData(
+        AMediaFormat *meta,
         size_t index, uint32_t flags) {
     status_t err;
     if ((err = readMetaData()) != OK) {
-        return UNKNOWN_ERROR;
+        return AMEDIA_ERROR_UNKNOWN;
     }
 
     Track *track = mFirstTrack;
     while (index > 0) {
         if (track == NULL) {
-            return UNKNOWN_ERROR;
+            return AMEDIA_ERROR_UNKNOWN;
         }
 
         track = track->next;
@@ -451,16 +455,17 @@
     }
 
     if (track == NULL) {
-        return UNKNOWN_ERROR;
+        return AMEDIA_ERROR_UNKNOWN;
     }
 
     [=] {
         int64_t duration;
         int32_t samplerate;
         if (track->has_elst && mHeaderTimescale != 0 &&
-                track->meta.findInt64(kKeyDuration, &duration) &&
-                track->meta.findInt32(kKeySampleRate, &samplerate)) {
+                AMediaFormat_getInt64(track->meta, AMEDIAFORMAT_KEY_DURATION, &duration) &&
+                AMediaFormat_getInt32(track->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &samplerate)) {
 
+            // elst has to be processed only the first time this function is called
             track->has_elst = false;
 
             if (track->elst_segment_duration > INT64_MAX) {
@@ -468,32 +473,29 @@
             }
             int64_t segment_duration = track->elst_segment_duration;
             int64_t media_time = track->elst_media_time;
-            int64_t halfscale = mHeaderTimescale / 2;
+            int64_t halfscale = track->timescale / 2;
+
             ALOGV("segment_duration = %" PRId64 ", media_time = %" PRId64
-                  ", halfscale = %" PRId64 ", timescale = %d",
-                  segment_duration,
-                  media_time,
-                  halfscale,
-                  mHeaderTimescale);
+                  ", halfscale = %" PRId64 ", mdhd_timescale = %d, track_timescale = %u",
+                  segment_duration, media_time,
+                  halfscale, mHeaderTimescale, track->timescale);
+
+            if ((uint32_t)samplerate != track->timescale){
+                ALOGV("samplerate:%" PRId32 ", track->timescale and samplerate are different!", samplerate);
+            }
 
             int64_t delay;
-            // delay = ((media_time * samplerate) + halfscale) / mHeaderTimescale;
+            // delay = ((media_time * samplerate) + halfscale) / track->timescale;
             if (__builtin_mul_overflow(media_time, samplerate, &delay) ||
                     __builtin_add_overflow(delay, halfscale, &delay) ||
-                    (delay /= mHeaderTimescale, false) ||
-                    /* the calculated delay should be small, but some apps
-                     * appear to write a bogus edit list that causes a really
-                     * large delay, resulting in playback problems.
-                     * Ignore such edit lists.
-                     * (4096 is enough to drop 4 full samples)
-                     */
-                    delay > 4096 ||
-                    delay < 0) {
+                    (delay /= track->timescale, false) ||
+                    delay > INT32_MAX ||
+                    delay < INT32_MIN) {
                 ALOGW("ignoring edit list with bogus values");
                 return;
             }
             ALOGV("delay = %" PRId64, delay);
-            track->meta.setInt32(kKeyEncoderDelay, delay);
+            AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_ENCODER_DELAY, delay);
 
             int64_t scaled_duration;
             // scaled_duration = duration * mHeaderTimescale;
@@ -504,36 +506,46 @@
 
             int64_t segment_end;
             int64_t padding;
-            // padding = scaled_duration - ((segment_duration + media_time) * 1000000);
-            if (__builtin_add_overflow(segment_duration, media_time, &segment_end) ||
-                    __builtin_mul_overflow(segment_end, 1000000, &segment_end) ||
-                    __builtin_sub_overflow(scaled_duration, segment_end, &padding)) {
+            int64_t segment_duration_e6;
+            int64_t media_time_scaled_e6;
+            int64_t media_time_scaled;
+            // padding = scaled_duration - ((segment_duration * 1000000) +
+            //                  ((media_time * mHeaderTimeScale * 1000000)/track->timescale) )
+            // segment_duration is based on timescale in movie header box(mdhd)
+            // media_time is based on timescale track header/media timescale
+            if (__builtin_mul_overflow(segment_duration, 1000000, &segment_duration_e6) ||
+                __builtin_mul_overflow(media_time, mHeaderTimescale, &media_time_scaled) ||
+                __builtin_mul_overflow(media_time_scaled, 1000000, &media_time_scaled_e6)) {
+                return;
+            }
+            media_time_scaled_e6 /= track->timescale;
+            if(__builtin_add_overflow(segment_duration_e6, media_time_scaled_e6, &segment_end) ||
+                __builtin_sub_overflow(scaled_duration, segment_end, &padding)) {
                 return;
             }
             ALOGV("segment_end = %" PRId64 ", padding = %" PRId64, segment_end, padding);
-
+            int64_t paddingsamples = 0;
             if (padding < 0) {
-                // track duration from media header (which is what kKeyDuration is) might
-                // be slightly shorter than the segment duration, which would make the
+                // track duration from media header (which is what AMEDIAFORMAT_KEY_DURATION is)
+                // might be slightly shorter than the segment duration, which would make the
                 // padding negative. Clamp to zero.
                 padding = 0;
-            }
-
-            int64_t paddingsamples;
-            int64_t halfscale_e6;
-            int64_t timescale_e6;
-            // paddingsamples = ((padding * samplerate) + (halfscale * 1000000))
-            //                / (mHeaderTimescale * 1000000);
-            if (__builtin_mul_overflow(padding, samplerate, &paddingsamples) ||
-                    __builtin_mul_overflow(halfscale, 1000000, &halfscale_e6) ||
-                    __builtin_mul_overflow(mHeaderTimescale, 1000000, &timescale_e6) ||
-                    __builtin_add_overflow(paddingsamples, halfscale_e6, &paddingsamples) ||
-                    (paddingsamples /= timescale_e6, false) ||
-                    paddingsamples > INT32_MAX) {
-                return;
+            } else {
+                int64_t halfscale_e6;
+                int64_t timescale_e6;
+                // paddingsamples = ((padding * samplerate) + (halfscale * 1000000))
+                //                / (mHeaderTimescale * 1000000);
+                if (__builtin_mul_overflow(padding, samplerate, &paddingsamples) ||
+                        __builtin_mul_overflow(halfscale, 1000000, &halfscale_e6) ||
+                        __builtin_mul_overflow(mHeaderTimescale, 1000000, &timescale_e6) ||
+                        __builtin_add_overflow(paddingsamples, halfscale_e6, &paddingsamples) ||
+                        (paddingsamples /= timescale_e6, false) ||
+                        paddingsamples > INT32_MAX) {
+                    return;
+                }
             }
             ALOGV("paddingsamples = %" PRId64, paddingsamples);
-            track->meta.setInt32(kKeyEncoderPadding, paddingsamples);
+            AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_ENCODER_PADDING, paddingsamples);
         }
     }();
 
@@ -542,7 +554,7 @@
         track->includes_expensive_metadata = true;
 
         const char *mime;
-        CHECK(track->meta.findCString(kKeyMIMEType, &mime));
+        CHECK(AMediaFormat_getString(track->meta, AMEDIAFORMAT_KEY_MIME, &mime));
         if (!strncasecmp("video/", mime, 6)) {
             // MPEG2 tracks do not provide CSD, so read the stream header
             if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2)) {
@@ -555,17 +567,19 @@
                     }
                     uint8_t header[kMaxTrackHeaderSize];
                     if (mDataSource->readAt(offset, &header, size) == (ssize_t)size) {
-                        track->meta.setData(kKeyStreamHeader, 'mdat', header, size);
+                        AMediaFormat_setBuffer(track->meta,
+                                AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER, header, size);
                     }
                 }
             }
 
             if (mMoofOffset > 0) {
                 int64_t duration;
-                if (track->meta.findInt64(kKeyDuration, &duration)) {
+                if (AMediaFormat_getInt64(track->meta,
+                        AMEDIAFORMAT_KEY_DURATION, &duration)) {
                     // nothing fancy, just pick a frame near 1/4th of the duration
-                    track->meta.setInt64(
-                            kKeyThumbnailTime, duration / 4);
+                    AMediaFormat_setInt64(track->meta,
+                            AMEDIAFORMAT_KEY_THUMBNAIL_TIME, duration / 4);
                 }
             } else {
                 uint32_t sampleIndex;
@@ -575,16 +589,16 @@
                         && track->sampleTable->getMetaDataForSample(
                             sampleIndex, NULL /* offset */, NULL /* size */,
                             &sampleTime) == OK) {
-                    track->meta.setInt64(
-                            kKeyThumbnailTime,
-                            ((int64_t)sampleTime * 1000000) / track->timescale);
+                        AMediaFormat_setInt64(track->meta,
+                                AMEDIAFORMAT_KEY_THUMBNAIL_TIME,
+                                ((int64_t)sampleTime * 1000000) / track->timescale);
                 }
             }
         }
     }
 
-    meta = track->meta;
-    return OK;
+    AMediaFormat_copy(meta, track->meta);
+    return AMEDIA_OK;
 }
 
 status_t MPEG4Extractor::readMetaData() {
@@ -619,12 +633,14 @@
         off64_t exifOffset;
         size_t exifSize;
         if (mItemTable->getExifOffsetAndSize(&exifOffset, &exifSize) == OK) {
-            mFileMetaData.setInt64(kKeyExifOffset, (int64_t)exifOffset);
-            mFileMetaData.setInt64(kKeyExifSize, (int64_t)exifSize);
+            AMediaFormat_setInt64(mFileMetaData,
+                    AMEDIAFORMAT_KEY_EXIF_OFFSET, (int64_t)exifOffset);
+            AMediaFormat_setInt64(mFileMetaData,
+                    AMEDIAFORMAT_KEY_EXIF_SIZE, (int64_t)exifSize);
         }
         for (uint32_t imageIndex = 0;
                 imageIndex < mItemTable->countImages(); imageIndex++) {
-            sp<MetaData> meta = mItemTable->getImageMeta(imageIndex);
+            AMediaFormat *meta = mItemTable->getImageMeta(imageIndex);
             if (meta == NULL) {
                 ALOGE("heif image %u has no meta!", imageIndex);
                 continue;
@@ -649,8 +665,8 @@
             }
             mLastTrack = track;
 
-            track->meta = *(meta.get());
-            track->meta.setInt32(kKeyTrackID, imageIndex);
+            track->meta = meta;
+            AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_TRACK_ID, imageIndex);
             track->includes_expensive_metadata = false;
             track->skipTrack = false;
             track->timescale = 1000000;
@@ -659,16 +675,18 @@
 
     if (mInitCheck == OK) {
         if (findTrackByMimePrefix("video/") != NULL) {
-            mFileMetaData.setCString(
-                    kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
+            AMediaFormat_setString(mFileMetaData,
+                    AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_CONTAINER_MPEG4);
         } else if (findTrackByMimePrefix("audio/") != NULL) {
-            mFileMetaData.setCString(kKeyMIMEType, "audio/mp4");
+            AMediaFormat_setString(mFileMetaData,
+                    AMEDIAFORMAT_KEY_MIME, "audio/mp4");
         } else if (findTrackByMimePrefix(
                 MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) != NULL) {
-            mFileMetaData.setCString(
-                    kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_HEIF);
+            AMediaFormat_setString(mFileMetaData,
+                    AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_CONTAINER_HEIF);
         } else {
-            mFileMetaData.setCString(kKeyMIMEType, "application/octet-stream");
+            AMediaFormat_setString(mFileMetaData,
+                    AMEDIAFORMAT_KEY_MIME, "application/octet-stream");
         }
     } else {
         mInitCheck = err;
@@ -693,7 +711,7 @@
             memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen);
             ptr += (20 + mPssh[i].datalen);
         }
-        mFileMetaData.setData(kKeyPssh, 'pssh', buf, psshsize);
+        AMediaFormat_setBuffer(mFileMetaData, AMEDIAFORMAT_KEY_PSSH, buf, psshsize);
         free(buf);
     }
 
@@ -944,7 +962,9 @@
                 track->includes_expensive_metadata = false;
                 track->skipTrack = false;
                 track->timescale = 0;
-                track->meta.setCString(kKeyMIMEType, "application/octet-stream");
+                track->meta = AMediaFormat_new();
+                AMediaFormat_setString(track->meta,
+                        AMEDIAFORMAT_KEY_MIME, "application/octet-stream");
                 track->has_elst = false;
                 track->subsample_encryption = false;
             }
@@ -968,8 +988,10 @@
 
             if (isTrack) {
                 int32_t trackId;
-                // There must be exact one track header per track.
-                if (!mLastTrack->meta.findInt32(kKeyTrackID, &trackId)) {
+                // There must be exactly one track header per track.
+
+                if (!AMediaFormat_getInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_TRACK_ID, &trackId)) {
                     mLastTrack->skipTrack = true;
                 }
 
@@ -1045,7 +1067,7 @@
                 }
             }
             if (mode != kCryptoModeUnencrypted) {
-                mLastTrack->meta.setInt32(kKeyCryptoMode, mode);
+                AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CRYPTO_MODE, mode);
             }
             break;
         }
@@ -1121,12 +1143,15 @@
                 return ERROR_MALFORMED;
             }
 
-            mLastTrack->meta.setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
+            AMediaFormat_setString(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_MIME, FourCC2MIME(original_fourcc));
             uint32_t num_channels = 0;
             uint32_t sample_rate = 0;
             if (AdjustChannelsAndRate(original_fourcc, &num_channels, &sample_rate)) {
-                mLastTrack->meta.setInt32(kKeyChannelCount, num_channels);
-                mLastTrack->meta.setInt32(kKeySampleRate, sample_rate);
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_CHANNEL_COUNT, num_channels);
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_SAMPLE_RATE, sample_rate);
             }
 
             // If format type is 'alac', it is necessary to get the parameters
@@ -1155,11 +1180,14 @@
                 }
 
                 uint8_t bitsPerSample = cookie[5];
-                mLastTrack->meta.setInt32(kKeyBitsPerSample, bitsPerSample);
-                mLastTrack->meta.setInt32(kKeyChannelCount, cookie[9]);
-                mLastTrack->meta.setInt32(kKeySampleRate, U32_AT(&cookie[20]));
-                mLastTrack->meta.setData(
-                    kKeyAlacMagicCookie, MetaData::TYPE_NONE, cookie, sizeof(cookie));
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, bitsPerSample);
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_CHANNEL_COUNT, cookie[9]);
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_SAMPLE_RATE, U32_AT(&cookie[20]));
+                AMediaFormat_setBuffer(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_CSD_0, cookie, sizeof(cookie));
 
                 // Add the size of ALAC Specific Info (36 bytes) and terminator
                 // atom (8 bytes).
@@ -1260,16 +1288,23 @@
             }
 
             int32_t tmpAlgorithmId;
-            if (!mLastTrack->meta.findInt32(kKeyCryptoMode, &tmpAlgorithmId)) {
-                mLastTrack->meta.setInt32(kKeyCryptoMode, defaultAlgorithmId);
+            if (!AMediaFormat_getInt32(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_CRYPTO_MODE, &tmpAlgorithmId)) {
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_CRYPTO_MODE, defaultAlgorithmId);
             }
 
-            mLastTrack->meta.setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
-            mLastTrack->meta.setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
-            mLastTrack->meta.setInt32(kKeyEncryptedByteBlock, defaultEncryptedByteBlock);
-            mLastTrack->meta.setInt32(kKeySkipByteBlock, defaultSkipByteBlock);
+            AMediaFormat_setInt32(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE, defaultIVSize);
+            AMediaFormat_setBuffer(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_CRYPTO_KEY, defaultKeyId, 16);
+            AMediaFormat_setInt32(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK, defaultEncryptedByteBlock);
+            AMediaFormat_setInt32(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK, defaultSkipByteBlock);
             if (defaultConstantIv != NULL) {
-                mLastTrack->meta.setData(kKeyCryptoIV, 'dciv', defaultConstantIv->data(), defaultConstantIv->size());
+                AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CRYPTO_IV,
+                        defaultConstantIv->data(), defaultConstantIv->size());
             }
             break;
         }
@@ -1418,8 +1453,8 @@
                 }
             }
             if (duration != 0 && mLastTrack->timescale != 0) {
-                mLastTrack->meta.setInt64(
-                        kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
+                AMediaFormat_setInt64(mLastTrack->meta, AMEDIAFORMAT_KEY_DURATION,
+                        (duration * 1000000) / mLastTrack->timescale);
             }
 
             uint8_t lang[2];
@@ -1446,8 +1481,7 @@
             lang_code[2] = (lang[1] & 0x1f) + 0x60;
             lang_code[3] = '\0';
 
-            mLastTrack->meta.setCString(
-                    kKeyMediaLanguage, lang_code);
+            AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_LANGUAGE, lang_code);
 
             break;
         }
@@ -1481,7 +1515,7 @@
                 if (mLastTrack == NULL)
                     return ERROR_MALFORMED;
 
-                CHECK(mLastTrack->meta.findCString(kKeyMIMEType, &mime));
+                CHECK(AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mime));
                 if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
                         strcasecmp(mime, "application/octet-stream")) {
                     // For now we only support a single type of media per track.
@@ -1522,7 +1556,7 @@
             }
 
             String8 mimeFormat((const char *)(buffer.get()), chunk_data_size);
-            mLastTrack->meta.setCString(kKeyMIMEType, mimeFormat.string());
+            AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, mimeFormat.string());
 
             break;
         }
@@ -1601,20 +1635,23 @@
 
             if (chunk_type != FOURCC('e', 'n', 'c', 'a')) {
                 // if the chunk type is enca, we'll get the type from the frma box later
-                mLastTrack->meta.setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+                AMediaFormat_setString(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_MIME, FourCC2MIME(chunk_type));
                 AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate);
 
                 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, FourCC2MIME(chunk_type))) {
-                    mLastTrack->meta.setInt32(kKeyBitsPerSample, sample_size);
+                    AMediaFormat_setInt32(mLastTrack->meta,
+                            AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, sample_size);
                     if (chunk_type == FOURCC('t', 'w', 'o', 's')) {
-                        mLastTrack->meta.setInt32(kKeyPcmBigEndian, 1);
+                        AMediaFormat_setInt32(mLastTrack->meta,
+                                AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN, 1);
                     }
                 }
             }
             ALOGV("*** coding='%s' %d channels, size %d, rate %d\n",
                    chunk, num_channels, sample_size, sample_rate);
-            mLastTrack->meta.setInt32(kKeyChannelCount, num_channels);
-            mLastTrack->meta.setInt32(kKeySampleRate, sample_rate);
+            AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, num_channels);
+            AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sample_rate);
 
             if (chunk_type == FOURCC('a', 'l', 'a', 'c')) {
 
@@ -1640,11 +1677,14 @@
                 }
 
                 uint8_t bitsPerSample = cookie[5];
-                mLastTrack->meta.setInt32(kKeyBitsPerSample, bitsPerSample);
-                mLastTrack->meta.setInt32(kKeyChannelCount, cookie[9]);
-                mLastTrack->meta.setInt32(kKeySampleRate, U32_AT(&cookie[20]));
-                mLastTrack->meta.setData(
-                        kKeyAlacMagicCookie, MetaData::TYPE_NONE, cookie, sizeof(cookie));
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, bitsPerSample);
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_CHANNEL_COUNT, cookie[9]);
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_SAMPLE_RATE, U32_AT(&cookie[20]));
+                AMediaFormat_setBuffer(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_CSD_0, cookie, sizeof(cookie));
                 data_offset += sizeof(cookie);
                 *offset = data_offset;
                 CHECK_EQ(*offset, stop_offset);
@@ -1702,10 +1742,11 @@
 
             if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
                 // if the chunk type is encv, we'll get the type from the frma box later
-                mLastTrack->meta.setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+                AMediaFormat_setString(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_MIME, FourCC2MIME(chunk_type));
             }
-            mLastTrack->meta.setInt32(kKeyWidth, width);
-            mLastTrack->meta.setInt32(kKeyHeight, height);
+            AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_WIDTH, width);
+            AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_HEIGHT, height);
 
             off64_t stop_offset = *offset + chunk_size;
             *offset = data_offset + sizeof(buffer);
@@ -1793,12 +1834,15 @@
                     ALOGE("max sample size too big: %zu", max_size);
                     return ERROR_MALFORMED;
                 }
-                mLastTrack->meta.setInt32(kKeyMaxInputSize, max_size + 10 * 2);
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, max_size + 10 * 2);
             } else {
                 // No size was specified. Pick a conservatively large size.
                 uint32_t width, height;
-                if (!mLastTrack->meta.findInt32(kKeyWidth, (int32_t*)&width) ||
-                    !mLastTrack->meta.findInt32(kKeyHeight,(int32_t*) &height)) {
+                if (!AMediaFormat_getInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_WIDTH, (int32_t*)&width) ||
+                    !AMediaFormat_getInt32(mLastTrack->meta,
+                            AMEDIAFORMAT_KEY_HEIGHT,(int32_t*) &height)) {
                     ALOGE("No width or height, assuming worst case 1080p");
                     width = 1920;
                     height = 1080;
@@ -1813,7 +1857,7 @@
                 }
 
                 const char *mime;
-                CHECK(mLastTrack->meta.findCString(kKeyMIMEType, &mime));
+                CHECK(AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mime));
                 if (!strncmp(mime, "audio/", 6)) {
                     // for audio, use 128KB
                     max_size = 1024 * 128;
@@ -1830,26 +1874,28 @@
                 // HACK: allow 10% overhead
                 // TODO: read sample size from traf atom for fragmented MPEG4.
                 max_size += max_size / 10;
-                mLastTrack->meta.setInt32(kKeyMaxInputSize, max_size);
+                AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, max_size);
             }
 
             // NOTE: setting another piece of metadata invalidates any pointers (such as the
             // mimetype) previously obtained, so don't cache them.
             const char *mime;
-            CHECK(mLastTrack->meta.findCString(kKeyMIMEType, &mime));
+            CHECK(AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mime));
             // Calculate average frame rate.
             if (!strncasecmp("video/", mime, 6)) {
                 size_t nSamples = mLastTrack->sampleTable->countSamples();
                 if (nSamples == 0) {
                     int32_t trackId;
-                    if (mLastTrack->meta.findInt32(kKeyTrackID, &trackId)) {
+                    if (AMediaFormat_getInt32(mLastTrack->meta,
+                            AMEDIAFORMAT_KEY_TRACK_ID, &trackId)) {
                         for (size_t i = 0; i < mTrex.size(); i++) {
                             Trex *t = &mTrex.editItemAt(i);
                             if (t->track_ID == (uint32_t) trackId) {
                                 if (t->default_sample_duration > 0) {
                                     int32_t frameRate =
                                             mLastTrack->timescale / t->default_sample_duration;
-                                    mLastTrack->meta.setInt32(kKeyFrameRate, frameRate);
+                                    AMediaFormat_setInt32(mLastTrack->meta,
+                                            AMEDIAFORMAT_KEY_FRAME_RATE, frameRate);
                                 }
                                 break;
                             }
@@ -1857,15 +1903,18 @@
                     }
                 } else {
                     int64_t durationUs;
-                    if (mLastTrack->meta.findInt64(kKeyDuration, &durationUs)) {
+                    if (AMediaFormat_getInt64(mLastTrack->meta,
+                            AMEDIAFORMAT_KEY_DURATION, &durationUs)) {
                         if (durationUs > 0) {
                             int32_t frameRate = (nSamples * 1000000LL +
                                         (durationUs >> 1)) / durationUs;
-                            mLastTrack->meta.setInt32(kKeyFrameRate, frameRate);
+                            AMediaFormat_setInt32(mLastTrack->meta,
+                                    AMEDIAFORMAT_KEY_FRAME_RATE, frameRate);
                         }
                     }
                     ALOGV("setting frame count %zu", nSamples);
-                    mLastTrack->meta.setInt32(kKeyFrameCount, nSamples);
+                    AMediaFormat_setInt32(mLastTrack->meta,
+                            AMEDIAFORMAT_KEY_FRAME_COUNT, nSamples);
                 }
             }
 
@@ -1973,7 +2022,7 @@
             if (buffer[len - 1] != '/') {
                 buffer[len] = '/';
             }
-            mFileMetaData.setCString(kKeyLocation, &buffer[0]);
+            AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_LOCATION, &buffer[0]);
             break;
         }
 
@@ -2003,8 +2052,8 @@
             if (mLastTrack == NULL)
                 return ERROR_MALFORMED;
 
-            mLastTrack->meta.setData(
-                    kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
+            AMediaFormat_setBuffer(mLastTrack->meta, 
+                    AMEDIAFORMAT_KEY_ESDS, &buffer[4], chunk_data_size - 4);
 
             if (mPath.size() >= 2
                     && mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'a')) {
@@ -2028,7 +2077,8 @@
                 uint8_t objectTypeIndication;
                 if (esds.getObjectTypeIndication(&objectTypeIndication) == OK) {
                     if (objectTypeIndication >= 0x60 && objectTypeIndication <= 0x65) {
-                        mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2);
+                        AMediaFormat_setString(mLastTrack->meta,
+                                AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_MPEG2);
                     }
                 }
             }
@@ -2055,10 +2105,12 @@
             uint32_t maxBitrate = U32_AT(&buffer[4]);
             uint32_t avgBitrate = U32_AT(&buffer[8]);
             if (maxBitrate > 0 && maxBitrate < INT32_MAX) {
-                mLastTrack->meta.setInt32(kKeyMaxBitRate, (int32_t)maxBitrate);
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_MAX_BIT_RATE, (int32_t)maxBitrate);
             }
             if (avgBitrate > 0 && avgBitrate < INT32_MAX) {
-                mLastTrack->meta.setInt32(kKeyBitRate, (int32_t)avgBitrate);
+                AMediaFormat_setInt32(mLastTrack->meta,
+                        AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)avgBitrate);
             }
             break;
         }
@@ -2082,8 +2134,8 @@
             if (mLastTrack == NULL)
                 return ERROR_MALFORMED;
 
-            mLastTrack->meta.setData(
-                    kKeyAVCC, kTypeAVCC, buffer.get(), chunk_data_size);
+            AMediaFormat_setBuffer(mLastTrack->meta, 
+                    AMEDIAFORMAT_KEY_CSD_AVC, buffer.get(), chunk_data_size);
 
             break;
         }
@@ -2104,8 +2156,8 @@
             if (mLastTrack == NULL)
                 return ERROR_MALFORMED;
 
-            mLastTrack->meta.setData(
-                    kKeyHVCC, kTypeHVCC, buffer.get(), chunk_data_size);
+            AMediaFormat_setBuffer(mLastTrack->meta, 
+                    AMEDIAFORMAT_KEY_CSD_HEVC, buffer.get(), chunk_data_size);
 
             *offset += chunk_size;
             break;
@@ -2140,7 +2192,8 @@
             if (mLastTrack == NULL)
                 return ERROR_MALFORMED;
 
-            mLastTrack->meta.setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
+            AMediaFormat_setBuffer(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_D263, buffer, chunk_data_size);
 
             break;
         }
@@ -2268,15 +2321,15 @@
                 duration = d32;
             }
             if (duration != 0 && mHeaderTimescale != 0 && duration < UINT64_MAX / 1000000) {
-                mFileMetaData.setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
+                AMediaFormat_setInt64(mFileMetaData,
+                        AMEDIAFORMAT_KEY_DURATION, duration * 1000000 / mHeaderTimescale);
             }
 
             String8 s;
             if (convertTimeToDate(creationTime, &s)) {
-                mFileMetaData.setCString(kKeyDate, s.string());
+                AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_DATE, s.string());
             }
 
-
             break;
         }
 
@@ -2318,7 +2371,8 @@
             }
 
             if (duration != 0 && mHeaderTimescale != 0) {
-                mFileMetaData.setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
+                AMediaFormat_setInt64(mFileMetaData,
+                        AMEDIAFORMAT_KEY_DURATION, duration * 1000000 / mHeaderTimescale);
             }
 
             break;
@@ -2352,7 +2406,8 @@
             // for a practical reason as various MPEG4 containers use it.
             if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) {
                 if (mLastTrack != NULL) {
-                    mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
+                    AMediaFormat_setString(mLastTrack->meta,
+                            AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_TEXT_3GPP);
                 }
             }
 
@@ -2396,11 +2451,10 @@
             if (mLastTrack == NULL)
                 return ERROR_MALFORMED;
 
-            uint32_t type;
-            const void *data;
+            void *data;
             size_t size = 0;
-            if (!mLastTrack->meta.findData(
-                    kKeyTextFormatData, &type, &data, &size)) {
+            if (!AMediaFormat_getBuffer(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA, &data, &size)) {
                 size = 0;
             }
 
@@ -2427,8 +2481,8 @@
                 return ERROR_IO;
             }
 
-            mLastTrack->meta.setData(
-                    kKeyTextFormatData, 0, buffer, size + chunk_size);
+            AMediaFormat_setBuffer(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA, buffer, size + chunk_size);
 
             delete[] buffer;
 
@@ -2460,8 +2514,8 @@
                 return ERROR_MALFORMED;
             }
 
-            mFileMetaData.setData(
-                kKeyAlbumArt, MetaData::TYPE_NONE,
+            AMediaFormat_setBuffer(mFileMetaData,
+                AMEDIAFORMAT_KEY_ALBUMART,
                 buffer.get() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
 
             break;
@@ -2696,9 +2750,9 @@
         return ERROR_MALFORMED;
     }
 
-    mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4);
-    mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
-    mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
+    AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AC4);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
     return OK;
 }
 
@@ -2857,9 +2911,9 @@
             }
         }
     }
-    mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_EAC3);
-    mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
-    mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
+    AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_EAC3);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
 
     delete[] chunk;
     return OK;
@@ -2929,9 +2983,9 @@
     unsigned lfeon = br.getBits(1);
     unsigned channelCount = channelCountTable[acmod] + lfeon;
 
-    mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3);
-    mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
-    mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
+    AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AC3);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
     return OK;
 }
 
@@ -3051,8 +3105,9 @@
         return ERROR_MALFORMED;
 
     int64_t metaDuration;
-    if (!mLastTrack->meta.findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) {
-        mLastTrack->meta.setInt64(kKeyDuration, sidxDuration);
+    if (!AMediaFormat_getInt64(mLastTrack->meta,
+                AMEDIAFORMAT_KEY_DURATION, &metaDuration) || metaDuration == 0) {
+        AMediaFormat_setInt64(mLastTrack->meta, AMEDIAFORMAT_KEY_DURATION, sidxDuration);
     }
     return OK;
 }
@@ -3150,7 +3205,7 @@
             return ERROR_MALFORMED;
         }
         if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.capture.fps")) {
-            mFileMetaData.setFloat(kKeyCaptureFramerate, *(float *)&val);
+            AMediaFormat_setFloat(mFileMetaData, AMEDIAFORMAT_KEY_CAPTURE_RATE, *(float *)&val);
         }
     } else if (dataType == 67 && dataSize >= 4) {
         // BE signed int32
@@ -3159,7 +3214,8 @@
             return ERROR_MALFORMED;
         }
         if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.video.temporal_layers_count")) {
-            mFileMetaData.setInt32(kKeyTemporalLayerCount, val);
+            AMediaFormat_setInt32(mFileMetaData,
+                    AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT, val);
         }
     } else {
         // add more keys if needed
@@ -3213,7 +3269,7 @@
     if (mLastTrack == NULL)
         return ERROR_MALFORMED;
 
-    mLastTrack->meta.setInt32(kKeyTrackID, id);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_TRACK_ID, id);
 
     size_t matrixOffset = dynSize + 16;
     int32_t a00 = U32_AT(&buffer[matrixOffset]);
@@ -3249,15 +3305,15 @@
     }
 
     if (rotationDegrees != 0) {
-        mLastTrack->meta.setInt32(kKeyRotation, rotationDegrees);
+        AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_ROTATION, rotationDegrees);
     }
 
     // Handle presentation display size, which could be different
-    // from the image size indicated by kKeyWidth and kKeyHeight.
+    // from the image size indicated by AMEDIAFORMAT_KEY_WIDTH and AMEDIAFORMAT_KEY_HEIGHT.
     uint32_t width = U32_AT(&buffer[dynSize + 52]);
     uint32_t height = U32_AT(&buffer[dynSize + 56]);
-    mLastTrack->meta.setInt32(kKeyDisplayWidth, width >> 16);
-    mLastTrack->meta.setInt32(kKeyDisplayHeight, height >> 16);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_DISPLAY_WIDTH, width >> 16);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, height >> 16);
 
     return OK;
 }
@@ -3285,54 +3341,50 @@
 
     uint32_t flags = U32_AT(buffer);
 
-    uint32_t metadataKey = 0;
+    const char *metadataKey = nullptr;
     char chunk[5];
     MakeFourCCString(mPath[4], chunk);
     ALOGV("meta: %s @ %lld", chunk, (long long)offset);
     switch ((int32_t)mPath[4]) {
         case FOURCC(0xa9, 'a', 'l', 'b'):
         {
-            metadataKey = kKeyAlbum;
+            metadataKey = "album";
             break;
         }
         case FOURCC(0xa9, 'A', 'R', 'T'):
         {
-            metadataKey = kKeyArtist;
+            metadataKey = "artist";
             break;
         }
         case FOURCC('a', 'A', 'R', 'T'):
         {
-            metadataKey = kKeyAlbumArtist;
+            metadataKey = "albumartist";
             break;
         }
         case FOURCC(0xa9, 'd', 'a', 'y'):
         {
-            metadataKey = kKeyYear;
+            metadataKey = "year";
             break;
         }
         case FOURCC(0xa9, 'n', 'a', 'm'):
         {
-            metadataKey = kKeyTitle;
+            metadataKey = "title";
             break;
         }
         case FOURCC(0xa9, 'w', 'r', 't'):
         {
-            metadataKey = kKeyWriter;
+            metadataKey = "writer";
             break;
         }
         case FOURCC('c', 'o', 'v', 'r'):
         {
-            metadataKey = kKeyAlbumArt;
+            metadataKey = "albumart";
             break;
         }
         case FOURCC('g', 'n', 'r', 'e'):
-        {
-            metadataKey = kKeyGenre;
-            break;
-        }
         case FOURCC(0xa9, 'g', 'e', 'n'):
         {
-            metadataKey = kKeyGenre;
+            metadataKey = "genre";
             break;
         }
         case FOURCC('c', 'p', 'i', 'l'):
@@ -3342,7 +3394,7 @@
                 sprintf(tmp, "%d",
                         (int)buffer[size - 1]);
 
-                mFileMetaData.setCString(kKeyCompilation, tmp);
+                AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_COMPILATION, tmp);
             }
             break;
         }
@@ -3354,7 +3406,7 @@
                 uint16_t* pTotalTracks = (uint16_t*)&buffer[12];
                 sprintf(tmp, "%d/%d", ntohs(*pTrack), ntohs(*pTotalTracks));
 
-                mFileMetaData.setCString(kKeyCDTrackNumber, tmp);
+                AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_CDTRACKNUMBER, tmp);
             }
             break;
         }
@@ -3366,7 +3418,7 @@
                 uint16_t* pTotalDiscs = (uint16_t*)&buffer[12];
                 sprintf(tmp, "%d/%d", ntohs(*pDisc), ntohs(*pTotalDiscs));
 
-                mFileMetaData.setCString(kKeyDiscNumber, tmp);
+                AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_DISCNUMBER, tmp);
             }
             break;
         }
@@ -3410,8 +3462,10 @@
                             return ERROR_MALFORMED;
                         }
 
-                        mLastTrack->meta.setInt32(kKeyEncoderDelay, delay);
-                        mLastTrack->meta.setInt32(kKeyEncoderPadding, padding);
+                        AMediaFormat_setInt32(mLastTrack->meta,
+                                AMEDIAFORMAT_KEY_ENCODER_DELAY, delay);
+                        AMediaFormat_setInt32(mLastTrack->meta,
+                                AMEDIAFORMAT_KEY_ENCODER_PADDING, padding);
                     }
                 }
 
@@ -3426,12 +3480,14 @@
             break;
     }
 
-    if (size >= 8 && metadataKey && !mFileMetaData.hasData(metadataKey)) {
-        if (metadataKey == kKeyAlbumArt) {
-            mFileMetaData.setData(
-                    kKeyAlbumArt, MetaData::TYPE_NONE,
+    void *tmpData;
+    size_t tmpDataSize;
+    if (size >= 8 && metadataKey &&
+            !AMediaFormat_getBuffer(mFileMetaData, metadataKey, &tmpData, &tmpDataSize)) {
+        if (!strcmp(metadataKey, "albumart")) {
+            AMediaFormat_setBuffer(mFileMetaData, metadataKey,
                     buffer + 8, size - 8);
-        } else if (metadataKey == kKeyGenre) {
+        } else if (!strcmp(metadataKey, "genre")) {
             if (flags == 0) {
                 // uint8_t genre code, iTunes genre codes are
                 // the standard id3 codes, except they start
@@ -3445,18 +3501,18 @@
                 char genre[10];
                 sprintf(genre, "%d", genrecode);
 
-                mFileMetaData.setCString(metadataKey, genre);
+                AMediaFormat_setString(mFileMetaData, metadataKey, genre);
             } else if (flags == 1) {
                 // custom genre string
                 buffer[size] = '\0';
 
-                mFileMetaData.setCString(
+                AMediaFormat_setString(mFileMetaData, 
                         metadataKey, (const char *)buffer + 8);
             }
         } else {
             buffer[size] = '\0';
 
-            mFileMetaData.setCString(
+            AMediaFormat_setString(mFileMetaData, 
                     metadataKey, (const char *)buffer + 8);
         }
     }
@@ -3486,21 +3542,31 @@
     int32_t type = U32_AT(&buffer[0]);
     if ((type == FOURCC('n', 'c', 'l', 'x') && size >= 11)
             || (type == FOURCC('n', 'c', 'l', 'c') && size >= 10)) {
-        int32_t primaries = U16_AT(&buffer[4]);
-        int32_t transfer = U16_AT(&buffer[6]);
-        int32_t coeffs = U16_AT(&buffer[8]);
-        bool fullRange = (type == FOURCC('n', 'c', 'l', 'x')) && (buffer[10] & 128);
-
-        ColorAspects aspects;
-        ColorUtils::convertIsoColorAspectsToCodecAspects(
-                primaries, transfer, coeffs, fullRange, aspects);
-
         // only store the first color specification
-        if (!mLastTrack->meta.hasData(kKeyColorPrimaries)) {
-            mLastTrack->meta.setInt32(kKeyColorPrimaries, aspects.mPrimaries);
-            mLastTrack->meta.setInt32(kKeyTransferFunction, aspects.mTransfer);
-            mLastTrack->meta.setInt32(kKeyColorMatrix, aspects.mMatrixCoeffs);
-            mLastTrack->meta.setInt32(kKeyColorRange, aspects.mRange);
+        int32_t existingColor;
+        if (!AMediaFormat_getInt32(mLastTrack->meta,
+                AMEDIAFORMAT_KEY_COLOR_RANGE, &existingColor)) {
+            int32_t primaries = U16_AT(&buffer[4]);
+            int32_t isotransfer = U16_AT(&buffer[6]);
+            int32_t coeffs = U16_AT(&buffer[8]);
+            bool fullRange = (type == FOURCC('n', 'c', 'l', 'x')) && (buffer[10] & 128);
+
+            int32_t range = 0;
+            int32_t standard = 0;
+            int32_t transfer = 0;
+            ColorUtils::convertIsoColorAspectsToPlatformAspects(
+                    primaries, isotransfer, coeffs, fullRange,
+                    &range, &standard, &transfer);
+
+            if (range != 0) {
+                AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_COLOR_RANGE, range);
+            }
+            if (standard != 0) {
+                AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_COLOR_STANDARD, standard);
+            }
+            if (transfer != 0) {
+                AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_COLOR_TRANSFER, transfer);
+            }
         }
     }
 
@@ -3527,26 +3593,26 @@
         return ERROR_IO;
     }
 
-    uint32_t metadataKey = 0;
+    const char *metadataKey = nullptr;
     switch (mPath[depth]) {
         case FOURCC('t', 'i', 't', 'l'):
         {
-            metadataKey = kKeyTitle;
+            metadataKey = "title";
             break;
         }
         case FOURCC('p', 'e', 'r', 'f'):
         {
-            metadataKey = kKeyArtist;
+            metadataKey = "artist";
             break;
         }
         case FOURCC('a', 'u', 't', 'h'):
         {
-            metadataKey = kKeyWriter;
+            metadataKey = "writer";
             break;
         }
         case FOURCC('g', 'n', 'r', 'e'):
         {
-            metadataKey = kKeyGenre;
+            metadataKey = "genre";
             break;
         }
         case FOURCC('a', 'l', 'b', 'm'):
@@ -3555,10 +3621,10 @@
               char tmp[4];
               sprintf(tmp, "%u", buffer[size - 1]);
 
-              mFileMetaData.setCString(kKeyCDTrackNumber, tmp);
+              AMediaFormat_setString(mFileMetaData, AMEDIAFORMAT_KEY_CDTRACKNUMBER, tmp);
             }
 
-            metadataKey = kKeyAlbum;
+            metadataKey = "album";
             break;
         }
         case FOURCC('y', 'r', 'r', 'c'):
@@ -3576,7 +3642,7 @@
             if (year < 10000) {
                 sprintf(tmp, "%u", year);
 
-                mFileMetaData.setCString(kKeyYear, tmp);
+                AMediaFormat_setString(mFileMetaData, "year", tmp);
             }
             break;
         }
@@ -3585,7 +3651,7 @@
             break;
     }
 
-    if (metadataKey > 0) {
+    if (metadataKey) {
         bool isUTF8 = true; // Common case
         char16_t *framedata = NULL;
         int len16 = 0; // Number of UTF-16 characters
@@ -3621,11 +3687,11 @@
 
         if (isUTF8) {
             buffer[size] = 0;
-            mFileMetaData.setCString(metadataKey, (const char *)buffer + 6);
+            AMediaFormat_setString(mFileMetaData, metadataKey, (const char *)buffer + 6);
         } else {
             // Convert from UTF-16 string to UTF-8 string.
             String8 tmpUTF8str(framedata, len16);
-            mFileMetaData.setCString(metadataKey, tmpUTF8str.string());
+            AMediaFormat_setString(mFileMetaData, metadataKey, tmpUTF8str.string());
         }
     }
 
@@ -3640,27 +3706,29 @@
 
     if (id3.isValid()) {
         struct Map {
-            int key;
+            const char *key;
             const char *tag1;
             const char *tag2;
         };
         static const Map kMap[] = {
-            { kKeyAlbum, "TALB", "TAL" },
-            { kKeyArtist, "TPE1", "TP1" },
-            { kKeyAlbumArtist, "TPE2", "TP2" },
-            { kKeyComposer, "TCOM", "TCM" },
-            { kKeyGenre, "TCON", "TCO" },
-            { kKeyTitle, "TIT2", "TT2" },
-            { kKeyYear, "TYE", "TYER" },
-            { kKeyAuthor, "TXT", "TEXT" },
-            { kKeyCDTrackNumber, "TRK", "TRCK" },
-            { kKeyDiscNumber, "TPA", "TPOS" },
-            { kKeyCompilation, "TCP", "TCMP" },
+            { AMEDIAFORMAT_KEY_ALBUM, "TALB", "TAL" },
+            { AMEDIAFORMAT_KEY_ARTIST, "TPE1", "TP1" },
+            { AMEDIAFORMAT_KEY_ALBUMARTIST, "TPE2", "TP2" },
+            { AMEDIAFORMAT_KEY_COMPOSER, "TCOM", "TCM" },
+            { AMEDIAFORMAT_KEY_GENRE, "TCON", "TCO" },
+            { AMEDIAFORMAT_KEY_TITLE, "TIT2", "TT2" },
+            { AMEDIAFORMAT_KEY_YEAR, "TYE", "TYER" },
+            { AMEDIAFORMAT_KEY_AUTHOR, "TXT", "TEXT" },
+            { AMEDIAFORMAT_KEY_CDTRACKNUMBER, "TRK", "TRCK" },
+            { AMEDIAFORMAT_KEY_DISCNUMBER, "TPA", "TPOS" },
+            { AMEDIAFORMAT_KEY_COMPILATION, "TCP", "TCMP" },
         };
         static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
 
+        void *tmpData;
+        size_t tmpDataSize;
         for (size_t i = 0; i < kNumMapEntries; ++i) {
-            if (!mFileMetaData.hasData(kMap[i].key)) {
+            if (!AMediaFormat_getBuffer(mFileMetaData, kMap[i].key, &tmpData, &tmpDataSize)) {
                 ID3::Iterator *it = new ID3::Iterator(id3, kMap[i].tag1);
                 if (it->done()) {
                     delete it;
@@ -3676,7 +3744,7 @@
                 it->getString(&s);
                 delete it;
 
-                mFileMetaData.setCString(kMap[i].key, s);
+                AMediaFormat_setString(mFileMetaData, kMap[i].key, s);
             }
         }
 
@@ -3685,12 +3753,12 @@
         const void *data = id3.getAlbumArt(&dataSize, &mime);
 
         if (data) {
-            mFileMetaData.setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
+            AMediaFormat_setBuffer(mFileMetaData, AMEDIAFORMAT_KEY_ALBUMART, data, dataSize);
         }
     }
 }
 
-MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) {
+MediaTrackHelperV2 *MPEG4Extractor::getTrack(size_t index) {
     status_t err;
     if ((err = readMetaData()) != OK) {
         return NULL;
@@ -3713,7 +3781,7 @@
 
     Trex *trex = NULL;
     int32_t trackId;
-    if (track->meta.findInt32(kKeyTrackID, &trackId)) {
+    if (AMediaFormat_getInt32(track->meta, AMEDIAFORMAT_KEY_TRACK_ID, &trackId)) {
         for (size_t i = 0; i < mTrex.size(); i++) {
             Trex *t = &mTrex.editItemAt(i);
             if (t->track_ID == (uint32_t) trackId) {
@@ -3729,16 +3797,15 @@
     ALOGV("getTrack called, pssh: %zu", mPssh.size());
 
     const char *mime;
-    if (!track->meta.findCString(kKeyMIMEType, &mime)) {
+    if (!AMediaFormat_getString(track->meta, AMEDIAFORMAT_KEY_MIME, &mime)) {
         return NULL;
     }
 
     sp<ItemTable> itemTable;
     if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
-        uint32_t type;
-        const void *data;
+        void *data;
         size_t size;
-        if (!track->meta.findData(kKeyAVCC, &type, &data, &size)) {
+        if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_AVC, &data, &size)) {
             return NULL;
         }
 
@@ -3749,10 +3816,9 @@
         }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)
             || !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
-        uint32_t type;
-        const void *data;
+        void *data;
         size_t size;
-        if (!track->meta.findData(kKeyHVCC, &type, &data, &size)) {
+        if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size)) {
             return NULL;
         }
 
@@ -3779,26 +3845,22 @@
 // static
 status_t MPEG4Extractor::verifyTrack(Track *track) {
     const char *mime;
-    CHECK(track->meta.findCString(kKeyMIMEType, &mime));
+    CHECK(AMediaFormat_getString(track->meta, AMEDIAFORMAT_KEY_MIME, &mime));
 
-    uint32_t type;
-    const void *data;
+    void *data;
     size_t size;
     if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
-        if (!track->meta.findData(kKeyAVCC, &type, &data, &size)
-                || type != kTypeAVCC) {
+        if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_AVC, &data, &size)) {
             return ERROR_MALFORMED;
         }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
-        if (!track->meta.findData(kKeyHVCC, &type, &data, &size)
-                    || type != kTypeHVCC) {
+        if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size)) {
             return ERROR_MALFORMED;
         }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
             || !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2)
             || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
-        if (!track->meta.findData(kKeyESDS, &type, &data, &size)
-                || type != kTypeESDS) {
+        if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_ESDS, &data, &size)) {
             return ERROR_MALFORMED;
         }
     }
@@ -3883,7 +3945,7 @@
         if (mLastTrack == NULL)
             return ERROR_MALFORMED;
 
-        mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
+        AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_QCELP);
         return OK;
     }
 
@@ -3900,10 +3962,12 @@
         uint32_t avgBitrate = 0;
         esds.getBitRate(&maxBitrate, &avgBitrate);
         if (maxBitrate > 0 && maxBitrate < INT32_MAX) {
-            mLastTrack->meta.setInt32(kKeyMaxBitRate, (int32_t)maxBitrate);
+            AMediaFormat_setInt32(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_MAX_BIT_RATE, (int32_t)maxBitrate);
         }
         if (avgBitrate > 0 && avgBitrate < INT32_MAX) {
-            mLastTrack->meta.setInt32(kKeyBitRate, (int32_t)avgBitrate);
+            AMediaFormat_setInt32(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_BIT_RATE, (int32_t)avgBitrate);
         }
     }
 
@@ -3947,7 +4011,7 @@
         return ERROR_MALFORMED;
 
     //keep AOT type
-    mLastTrack->meta.setInt32(kKeyAACAOT, objectType);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_AAC_PROFILE, objectType);
 
     uint32_t freqIndex = br.getBits(4);
 
@@ -3968,7 +4032,7 @@
         sampleRate = kSamplingRate[freqIndex];
     }
 
-    if (objectType == AOT_SBR || objectType == AOT_PS) {//SBR specific config per 14496-3 table 1.13
+    if (objectType == AOT_SBR || objectType == AOT_PS) {//SBR specific config per 14496-3 tbl 1.13
         if (br.numBitsLeft() < 4) return ERROR_MALFORMED;
         uint32_t extFreqIndex = br.getBits(4);
         int32_t extSampleRate __unused;
@@ -3985,7 +4049,7 @@
             extSampleRate = kSamplingRate[extFreqIndex];
         }
         //TODO: save the extension sampling rate value in meta data =>
-        //      mLastTrack->meta.setInt32(kKeyExtSampleRate, extSampleRate);
+        //      AMediaFormat_setInt32(mLastTrack->meta, kKeyExtSampleRate, extSampleRate);
     }
 
     switch (numChannels) {
@@ -4138,24 +4202,25 @@
         return ERROR_MALFORMED;
 
     int32_t prevSampleRate;
-    CHECK(mLastTrack->meta.findInt32(kKeySampleRate, &prevSampleRate));
+    CHECK(AMediaFormat_getInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &prevSampleRate));
 
     if (prevSampleRate != sampleRate) {
         ALOGV("mpeg4 audio sample rate different from previous setting. "
              "was: %d, now: %d", prevSampleRate, sampleRate);
     }
 
-    mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
 
     int32_t prevChannelCount;
-    CHECK(mLastTrack->meta.findInt32(kKeyChannelCount, &prevChannelCount));
+    CHECK(AMediaFormat_getInt32(mLastTrack->meta,
+            AMEDIAFORMAT_KEY_CHANNEL_COUNT, &prevChannelCount));
 
     if (prevChannelCount != numChannels) {
         ALOGV("mpeg4 audio channel count different from previous setting. "
              "was: %d, now: %d", prevChannelCount, numChannels);
     }
 
-    mLastTrack->meta.setInt32(kKeyChannelCount, numChannels);
+    AMediaFormat_setInt32(mLastTrack->meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, numChannels);
 
     return OK;
 }
@@ -4163,7 +4228,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 MPEG4Source::MPEG4Source(
-        MetaDataBase &format,
+        AMediaFormat *format,
         DataSourceHelper *dataSource,
         int32_t timeScale,
         const sp<SampleTable> &sampleTable,
@@ -4203,23 +4268,27 @@
 
     memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));
 
-    mFormat.findInt32(kKeyCryptoMode, &mCryptoMode);
+    AMediaFormat_getInt32(mFormat,
+            AMEDIAFORMAT_KEY_CRYPTO_MODE, &mCryptoMode);
     mDefaultIVSize = 0;
-    mFormat.findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize);
-    uint32_t keytype;
-    const void *key;
+    AMediaFormat_getInt32(mFormat,
+            AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE, &mDefaultIVSize);
+    void *key;
     size_t keysize;
-    if (mFormat.findData(kKeyCryptoKey, &keytype, &key, &keysize)) {
+    if (AMediaFormat_getBuffer(mFormat,
+            AMEDIAFORMAT_KEY_CRYPTO_KEY, &key, &keysize)) {
         CHECK(keysize <= 16);
         memset(mCryptoKey, 0, 16);
         memcpy(mCryptoKey, key, keysize);
     }
 
-    mFormat.findInt32(kKeyEncryptedByteBlock, &mDefaultEncryptedByteBlock);
-    mFormat.findInt32(kKeySkipByteBlock, &mDefaultSkipByteBlock);
+    AMediaFormat_getInt32(mFormat,
+            AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK, &mDefaultEncryptedByteBlock);
+    AMediaFormat_getInt32(mFormat,
+            AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK, &mDefaultSkipByteBlock);
 
     const char *mime;
-    bool success = mFormat.findCString(kKeyMIMEType, &mime);
+    bool success = AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mime);
     CHECK(success);
 
     mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
@@ -4228,10 +4297,9 @@
     mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
 
     if (mIsAVC) {
-        uint32_t type;
-        const void *data;
+        void *data;
         size_t size;
-        CHECK(format.findData(kKeyAVCC, &type, &data, &size));
+        CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_AVC, &data, &size));
 
         const uint8_t *ptr = (const uint8_t *)data;
 
@@ -4241,10 +4309,9 @@
         // The number of bytes used to encode the length of a NAL unit.
         mNALLengthSize = 1 + (ptr[4] & 3);
     } else if (mIsHEVC) {
-        uint32_t type;
-        const void *data;
+        void *data;
         size_t size;
-        CHECK(format.findData(kKeyHVCC, &type, &data, &size));
+        CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size));
 
         const uint8_t *ptr = (const uint8_t *)data;
 
@@ -4259,23 +4326,25 @@
     if (mIsPcm) {
         int32_t numChannels = 0;
         int32_t bitsPerSample = 0;
-        CHECK(mFormat.findInt32(kKeyBitsPerSample, &bitsPerSample));
-        CHECK(mFormat.findInt32(kKeyChannelCount, &numChannels));
+        CHECK(AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, &bitsPerSample));
+        CHECK(AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &numChannels));
 
         int32_t bytesPerSample = bitsPerSample >> 3;
         int32_t pcmSampleSize = bytesPerSample * numChannels;
 
         size_t maxSampleSize;
         status_t err = mSampleTable->getMaxSampleSize(&maxSampleSize);
-        if (err != OK || maxSampleSize != static_cast<size_t>(pcmSampleSize) || bitsPerSample != 16) {
+        if (err != OK || maxSampleSize != static_cast<size_t>(pcmSampleSize)
+               || bitsPerSample != 16) {
             // Not supported
             mIsPcm = false;
         } else {
-            mFormat.setInt32(kKeyMaxInputSize, pcmSampleSize * kMaxPcmFrameSize);
+            AMediaFormat_setInt32(mFormat,
+                    AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, pcmSampleSize * kMaxPcmFrameSize);
         }
     }
 
-    CHECK(format.findInt32(kKeyTrackID, &mTrackId));
+    CHECK(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_TRACK_ID, &mTrackId));
 
 }
 
@@ -4295,13 +4364,13 @@
     free(mCurrentSampleInfoOffsets);
 }
 
-status_t MPEG4Source::start() {
+media_status_t MPEG4Source::start() {
     Mutex::Autolock autoLock(mLock);
 
     CHECK(!mStarted);
 
     int32_t tmp;
-    CHECK(mFormat.findInt32(kKeyMaxInputSize, &tmp));
+    CHECK(AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, &tmp));
     size_t max_size = tmp;
 
     // A somewhat arbitrary limit that should be sufficient for 8k video frames
@@ -4309,11 +4378,11 @@
     const size_t kMaxBufferSize = 64 * 1024 * 1024;
     if (max_size > kMaxBufferSize) {
         ALOGE("bogus max input size: %zu > %zu", max_size, kMaxBufferSize);
-        return ERROR_MALFORMED;
+        return AMEDIA_ERROR_MALFORMED;
     }
     if (max_size == 0) {
         ALOGE("zero max input size");
-        return ERROR_MALFORMED;
+        return AMEDIA_ERROR_MALFORMED;
     }
 
     // Allow up to kMaxBuffers, but not if the total exceeds kMaxBufferSize.
@@ -4326,15 +4395,15 @@
         // file probably specified a bad max size
         delete mGroup;
         mGroup = NULL;
-        return ERROR_MALFORMED;
+        return AMEDIA_ERROR_MALFORMED;
     }
 
     mStarted = true;
 
-    return OK;
+    return AMEDIA_OK;
 }
 
-status_t MPEG4Source::stop() {
+media_status_t MPEG4Source::stop() {
     Mutex::Autolock autoLock(mLock);
 
     CHECK(mStarted);
@@ -4353,7 +4422,7 @@
     mStarted = false;
     mCurrentSampleIndex = 0;
 
-    return OK;
+    return AMEDIA_OK;
 }
 
 status_t MPEG4Source::parseChunk(off64_t *offset) {
@@ -4471,7 +4540,8 @@
         }
         case FOURCC('s', 'a', 'i', 'o'): {
             status_t err;
-            if ((err = parseSampleAuxiliaryInformationOffsets(data_offset, chunk_data_size)) != OK) {
+            if ((err = parseSampleAuxiliaryInformationOffsets(data_offset, chunk_data_size))
+                    != OK) {
                 return err;
             }
             *offset += chunk_size;
@@ -4602,7 +4672,8 @@
     if (entrycount > mCurrentSampleInfoOffsetsAllocSize) {
         uint64_t *newPtr = (uint64_t *)realloc(mCurrentSampleInfoOffsets, entrycount * 8);
         if (newPtr == NULL) {
-            ALOGE("failed to realloc %u -> %u", mCurrentSampleInfoOffsetsAllocSize, entrycount * 8);
+            ALOGE("failed to realloc %u -> %u",
+                    mCurrentSampleInfoOffsetsAllocSize, entrycount * 8);
             return NO_MEMORY;
         }
         mCurrentSampleInfoOffsets = newPtr;
@@ -4641,10 +4712,11 @@
     return parseClearEncryptedSizes(drmoffset, false, 0);
 }
 
-status_t MPEG4Source::parseClearEncryptedSizes(off64_t offset, bool isSubsampleEncryption, uint32_t flags) {
+status_t MPEG4Source::parseClearEncryptedSizes(
+        off64_t offset, bool isSubsampleEncryption, uint32_t flags) {
 
-    int ivlength;
-    CHECK(mFormat.findInt32(kKeyCryptoDefaultIVSize, &ivlength));
+    int32_t ivlength;
+    CHECK(AMediaFormat_getInt32(mFormat, "crypto-defaultivsize", &ivlength));
 
     // only 0, 8 and 16 byte initialization vectors are supported
     if (ivlength != 0 && ivlength != 8 && ivlength != 16) {
@@ -4988,10 +5060,10 @@
     return OK;
 }
 
-status_t MPEG4Source::getFormat(MetaDataBase &meta) {
+media_status_t MPEG4Source::getFormat(AMediaFormat *meta) {
     Mutex::Autolock autoLock(mLock);
-    meta = mFormat;
-    return OK;
+    AMediaFormat_copy(meta, mFormat);
+    return AMEDIA_OK;
 }
 
 size_t MPEG4Source::parseNALSize(const uint8_t *data) const {
@@ -5042,7 +5114,7 @@
     return 0;
 }
 
-status_t MPEG4Source::read(
+media_status_t MPEG4Source::read(
         MediaBufferBase **out, const ReadOptions *options) {
     Mutex::Autolock autoLock(mLock);
 
@@ -5050,7 +5122,7 @@
 
     if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) {
         *out = nullptr;
-        return WOULD_BLOCK;
+        return AMEDIA_ERROR_WOULD_BLOCK;
     }
 
     if (mFirstMoofOffset > 0) {
@@ -5068,8 +5140,8 @@
             CHECK(mSampleTable == NULL);
             CHECK(mItemTable != NULL);
             int32_t imageIndex;
-            if (!mFormat.findInt32(kKeyTrackID, &imageIndex)) {
-                return ERROR_MALFORMED;
+            if (!AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_TRACK_ID, &imageIndex)) {
+                return AMEDIA_ERROR_MALFORMED;
             }
 
             status_t err;
@@ -5079,7 +5151,7 @@
                 err = mItemTable->findThumbnailItem(imageIndex, &mCurrentSampleIndex);
             }
             if (err != OK) {
-                return err;
+                return AMEDIA_ERROR_UNKNOWN;
             }
         } else {
             uint32_t findFlags = 0;
@@ -5134,10 +5206,10 @@
                     // this all the way to the MediaPlayer would cause abnormal
                     // termination. Legacy behaviour appears to be to behave as if
                     // we had seeked to the end of stream, ending normally.
-                    err = ERROR_END_OF_STREAM;
+                    return AMEDIA_ERROR_END_OF_STREAM;
                 }
                 ALOGV("end of stream");
-                return err;
+                return AMEDIA_ERROR_UNKNOWN;
             }
 
             if (mode == ReadOptions::SEEK_CLOSEST
@@ -5191,20 +5263,23 @@
         }
 
         if (err != OK) {
-            return err;
+            if (err == ERROR_END_OF_STREAM) {
+                return AMEDIA_ERROR_END_OF_STREAM;
+            }
+            return AMEDIA_ERROR_UNKNOWN;
         }
 
         err = mGroup->acquire_buffer(&mBuffer);
 
         if (err != OK) {
             CHECK(mBuffer == NULL);
-            return err;
+            return AMEDIA_ERROR_UNKNOWN;
         }
         if (size > mBuffer->size()) {
             ALOGE("buffer too small: %zu > %zu", size, mBuffer->size());
             mBuffer->release();
             mBuffer = NULL;
-            return ERROR_BUFFER_TOO_SMALL;
+            return AMEDIA_ERROR_UNKNOWN; // ERROR_BUFFER_TOO_SMALL
         }
     }
 
@@ -5230,7 +5305,7 @@
                     mBuffer->release();
                     mBuffer = NULL;
 
-                    return ERROR_IO;
+                    return AMEDIA_ERROR_IO;
                 }
 
                 mBuffer->meta_data().clear();
@@ -5238,7 +5313,8 @@
                 mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
 
                 int32_t byteOrder;
-                mFormat.findInt32(kKeyPcmBigEndian, &byteOrder);
+                AMediaFormat_getInt32(mFormat,
+                        AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN, &byteOrder);
 
                 if (byteOrder == 1) {
                     // Big-endian -> little-endian
@@ -5260,7 +5336,7 @@
                     mBuffer->release();
                     mBuffer = NULL;
 
-                    return ERROR_IO;
+                    return AMEDIA_ERROR_IO;
                 }
 
                 CHECK(mBuffer != NULL);
@@ -5288,14 +5364,14 @@
             *out = mBuffer;
             mBuffer = NULL;
 
-            return OK;
+            return AMEDIA_OK;
         }
 
         if (mIsAC4) {
             mBuffer->release();
             mBuffer = NULL;
 
-            return ERROR_IO;
+            return AMEDIA_ERROR_IO;
         }
 
         // Each NAL unit is split up into its constituent fragments and
@@ -5316,7 +5392,7 @@
             mBuffer->release();
             mBuffer = NULL;
 
-            return ERROR_MALFORMED;
+            return AMEDIA_ERROR_MALFORMED;
         }
 
         MediaBufferBase *clone = mBuffer->clone();
@@ -5335,7 +5411,7 @@
 
         *out = clone;
 
-        return OK;
+        return AMEDIA_OK;
     } else if (mIsAC4) {
         CHECK(mBuffer != NULL);
         // Make sure there is enough space to write the sync header and the raw frame
@@ -5343,7 +5419,7 @@
             mBuffer->release();
             mBuffer = NULL;
 
-            return ERROR_IO;
+            return AMEDIA_ERROR_IO;
         }
 
         uint8_t *dstData = (uint8_t *)mBuffer->data();
@@ -5363,7 +5439,7 @@
             mBuffer->release();
             mBuffer = NULL;
 
-            return ERROR_IO;
+            return AMEDIA_ERROR_IO;
         }
 
         mBuffer->set_range(0, dstOffset + size);
@@ -5387,7 +5463,7 @@
         *out = mBuffer;
         mBuffer = NULL;
 
-        return OK;
+        return AMEDIA_OK;
     } else {
         // Whole NAL units are returned but each fragment is prefixed by
         // the start code (0x00 00 00 01).
@@ -5398,7 +5474,7 @@
             mBuffer->release();
             mBuffer = NULL;
 
-            return ERROR_IO;
+            return AMEDIA_ERROR_IO;
         }
 
         uint8_t *dstData = (uint8_t *)mBuffer->data();
@@ -5418,7 +5494,7 @@
                 ALOGE("Video is malformed");
                 mBuffer->release();
                 mBuffer = NULL;
-                return ERROR_MALFORMED;
+                return AMEDIA_ERROR_MALFORMED;
             }
 
             if (nalLength == 0) {
@@ -5432,7 +5508,7 @@
                 android_errorWriteLog(0x534e4554, "27208621");
                 mBuffer->release();
                 mBuffer = NULL;
-                return ERROR_MALFORMED;
+                return AMEDIA_ERROR_MALFORMED;
             }
 
             dstData[dstOffset++] = 0;
@@ -5479,11 +5555,11 @@
         *out = mBuffer;
         mBuffer = NULL;
 
-        return OK;
+        return AMEDIA_OK;
     }
 }
 
-status_t MPEG4Source::fragmentedRead(
+media_status_t MPEG4Source::fragmentedRead(
         MediaBufferBase **out, const ReadOptions *options) {
 
     ALOGV("MPEG4Source::fragmentedRead");
@@ -5525,7 +5601,7 @@
             mCurrentSampleIndex = 0;
             status_t err = parseChunk(&totalOffset);
             if (err != OK) {
-                return err;
+                return AMEDIA_ERROR_UNKNOWN;
             }
             mCurrentTime = totalTime * mTimescale / 1000000ll;
         } else {
@@ -5537,7 +5613,7 @@
             off64_t tmp = mCurrentMoofOffset;
             status_t err = parseChunk(&tmp);
             if (err != OK) {
-                return err;
+                return AMEDIA_ERROR_UNKNOWN;
             }
             mCurrentTime = 0;
         }
@@ -5565,7 +5641,7 @@
         if (mCurrentSampleIndex >= mCurrentSamples.size()) {
             // move to next fragment if there is one
             if (mNextMoofOffset <= mCurrentMoofOffset) {
-                return ERROR_END_OF_STREAM;
+                return AMEDIA_ERROR_END_OF_STREAM;
             }
             off64_t nextMoof = mNextMoofOffset;
             mCurrentMoofOffset = nextMoof;
@@ -5573,10 +5649,10 @@
             mCurrentSampleIndex = 0;
             status_t err = parseChunk(&nextMoof);
             if (err != OK) {
-                return err;
+                return AMEDIA_ERROR_UNKNOWN;
             }
             if (mCurrentSampleIndex >= mCurrentSamples.size()) {
-                return ERROR_END_OF_STREAM;
+                return AMEDIA_ERROR_END_OF_STREAM;
             }
         }
 
@@ -5585,20 +5661,20 @@
         size = smpl->size;
         cts = mCurrentTime + smpl->compositionOffset;
         mCurrentTime += smpl->duration;
-        isSyncSample = (mCurrentSampleIndex == 0); // XXX
+        isSyncSample = (mCurrentSampleIndex == 0);
 
         status_t err = mGroup->acquire_buffer(&mBuffer);
 
         if (err != OK) {
             CHECK(mBuffer == NULL);
             ALOGV("acquire_buffer returned %d", err);
-            return err;
+            return AMEDIA_ERROR_UNKNOWN;
         }
         if (size > mBuffer->size()) {
             ALOGE("buffer too small: %zu > %zu", size, mBuffer->size());
             mBuffer->release();
             mBuffer = NULL;
-            return ERROR_BUFFER_TOO_SMALL;
+            return AMEDIA_ERROR_UNKNOWN;
         }
     }
 
@@ -5617,12 +5693,11 @@
         bufmeta.setInt32(kKeyEncryptedByteBlock, mDefaultEncryptedByteBlock);
         bufmeta.setInt32(kKeySkipByteBlock, mDefaultSkipByteBlock);
 
-        uint32_t type = 0;
-        const void *iv = NULL;
+        void *iv = NULL;
         size_t ivlength = 0;
-        if (!mFormat.findData(
-                kKeyCryptoIV, &type, &iv, &ivlength)) {
-            iv = smpl->iv;
+        if (!AMediaFormat_getBuffer(mFormat,
+                "crypto-iv", &iv, &ivlength)) {
+            iv = (void *) smpl->iv;
             ivlength = 16; // use 16 or the actual size?
         }
         bufmeta.setData(kKeyCryptoIV, 0, iv, ivlength);
@@ -5636,7 +5711,7 @@
                 mBuffer = NULL;
 
                 ALOGE("fragmentedRead ERROR_MALFORMED size %zu", size);
-                return ERROR_MALFORMED;
+                return AMEDIA_ERROR_MALFORMED;
             }
 
             ssize_t num_bytes_read =
@@ -5647,7 +5722,7 @@
                 mBuffer = NULL;
 
                 ALOGE("i/o error");
-                return ERROR_IO;
+                return AMEDIA_ERROR_IO;
             }
 
             CHECK(mBuffer != NULL);
@@ -5685,7 +5760,7 @@
             *out = mBuffer;
             mBuffer = NULL;
 
-            return OK;
+            return AMEDIA_OK;
         }
 
         // Each NAL unit is split up into its constituent fragments and
@@ -5707,7 +5782,7 @@
             mBuffer->release();
             mBuffer = NULL;
 
-            return ERROR_MALFORMED;
+            return AMEDIA_ERROR_MALFORMED;
         }
 
         MediaBufferBase *clone = mBuffer->clone();
@@ -5726,7 +5801,7 @@
 
         *out = clone;
 
-        return OK;
+        return AMEDIA_OK;
     } else {
         ALOGV("whole NAL");
         // Whole NAL units are returned but each fragment is prefixed by
@@ -5735,7 +5810,7 @@
         void *data = NULL;
         bool isMalFormed = false;
         int32_t max_size;
-        if (!mFormat.findInt32(kKeyMaxInputSize, &max_size)
+        if (!AMediaFormat_getInt32(mFormat, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, &max_size)
                 || !isInRange((size_t)0u, (size_t)max_size, size)) {
             isMalFormed = true;
         } else {
@@ -5748,7 +5823,7 @@
                 mBuffer->release();
                 mBuffer = NULL;
             }
-            return ERROR_MALFORMED;
+            return AMEDIA_ERROR_MALFORMED;
         }
         num_bytes_read = mDataSource->readAt(offset, data, size);
 
@@ -5757,7 +5832,7 @@
             mBuffer = NULL;
 
             ALOGE("i/o error");
-            return ERROR_IO;
+            return AMEDIA_ERROR_IO;
         }
 
         uint8_t *dstData = (uint8_t *)mBuffer->data();
@@ -5779,7 +5854,7 @@
                 ALOGE("Video is malformed; nalLength %zu", nalLength);
                 mBuffer->release();
                 mBuffer = NULL;
-                return ERROR_MALFORMED;
+                return AMEDIA_ERROR_MALFORMED;
             }
 
             if (nalLength == 0) {
@@ -5793,7 +5868,7 @@
                 android_errorWriteLog(0x534e4554, "26365349");
                 mBuffer->release();
                 mBuffer = NULL;
-                return ERROR_MALFORMED;
+                return AMEDIA_ERROR_MALFORMED;
             }
 
             dstData[dstOffset++] = 0;
@@ -5827,17 +5902,17 @@
         *out = mBuffer;
         mBuffer = NULL;
 
-        return OK;
+        return AMEDIA_OK;
     }
 
-    return OK;
+    return AMEDIA_OK;
 }
 
 MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix(
         const char *mimePrefix) {
     for (Track *track = mFirstTrack; track != NULL; track = track->next) {
         const char *mime;
-        if (track->meta.findCString(kKeyMIMEType, &mime)
+        if (AMediaFormat_getString(track->meta, AMEDIAFORMAT_KEY_MIME, &mime)
                 && !strncasecmp(mime, mimePrefix, strlen(mimePrefix))) {
             return track;
         }
@@ -5957,7 +6032,8 @@
 
         char chunkstring[5];
         MakeFourCCString(chunkType, chunkstring);
-        ALOGV("saw chunk type %s, size %" PRIu64 " @ %lld", chunkstring, chunkSize, (long long)offset);
+        ALOGV("saw chunk type %s, size %" PRIu64 " @ %lld",
+                chunkstring, chunkSize, (long long)offset);
         switch (chunkType) {
             case FOURCC('f', 't', 'y', 'p'):
             {
@@ -6018,11 +6094,11 @@
     return true;
 }
 
-static CMediaExtractor* CreateExtractor(CDataSource *source, void *) {
-    return wrap(new MPEG4Extractor(new DataSourceHelper(source)));
+static CMediaExtractorV2* CreateExtractor(CDataSource *source, void *) {
+    return wrapV2(new MPEG4Extractor(new DataSourceHelper(source)));
 }
 
-static CreatorFunc Sniff(
+static CreatorFuncV2 Sniff(
         CDataSource *source, float *confidence, void **,
         FreeMetaFunc *) {
     DataSourceHelper helper(source);
@@ -6043,11 +6119,11 @@
 __attribute__ ((visibility ("default")))
 ExtractorDef GETEXTRACTORDEF() {
     return {
-        EXTRACTORDEF_VERSION,
+        EXTRACTORDEF_VERSION_CURRENT,
         UUID("27575c67-4417-4c54-8d3d-8e626985a164"),
-        1, // version
+        2, // version
         "MP4 Extractor",
-        { Sniff }
+        { .v2 = Sniff }
     };
 }
 
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index 9b8de20..56b641d 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -22,7 +22,7 @@
 
 #include <media/MediaExtractorPluginApi.h>
 #include <media/MediaExtractorPluginHelper.h>
-#include <media/stagefright/MetaDataBase.h>
+#include <media/NdkMediaFormat.h>
 #include <media/stagefright/foundation/AString.h>
 #include <utils/KeyedVector.h>
 #include <utils/List.h>
@@ -53,15 +53,15 @@
     uint32_t default_sample_flags;
 };
 
-class MPEG4Extractor : public MediaExtractorPluginHelper {
+class MPEG4Extractor : public MediaExtractorPluginHelperV2 {
 public:
     explicit MPEG4Extractor(DataSourceHelper *source, const char *mime = NULL);
 
     virtual size_t countTracks();
-    virtual MediaTrackHelper *getTrack(size_t index);
-    virtual status_t getTrackMetaData(MetaDataBase& meta, size_t index, uint32_t flags);
+    virtual MediaTrackHelperV2 *getTrack(size_t index);
+    virtual media_status_t getTrackMetaData(AMediaFormat *meta, size_t index, uint32_t flags);
 
-    virtual status_t getMetaData(MetaDataBase& meta);
+    virtual media_status_t getMetaData(AMediaFormat *meta);
     virtual uint32_t flags() const;
     virtual const char * name() { return "MPEG4Extractor"; }
 
@@ -77,7 +77,7 @@
     };
     struct Track {
         Track *next;
-        MetaDataBase meta;
+        AMediaFormat *meta;
         uint32_t timescale;
         sp<SampleTable> sampleTable;
         bool includes_expensive_metadata;
@@ -107,7 +107,7 @@
 
     Track *mFirstTrack, *mLastTrack;
 
-    MetaDataBase mFileMetaData;
+    AMediaFormat *mFileMetaData;
 
     Vector<uint32_t> mPath;
     String8 mLastCommentMean;
diff --git a/media/extractors/wav/Android.bp b/media/extractors/wav/Android.bp
index d8b4144..185bb32 100644
--- a/media/extractors/wav/Android.bp
+++ b/media/extractors/wav/Android.bp
@@ -7,12 +7,14 @@
     ],
 
     shared_libs: [
+        "libbinder_ndk",
         "liblog",
         "libmediaextractor",
         "libmediandk",
     ],
 
     static_libs: [
+        "libaudioutils",
         "libfifo",
         "libstagefright_foundation",
     ],
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index ddc7031..86500ef 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -20,6 +20,7 @@
 
 #include "WAVExtractor.h"
 
+#include <android/binder_ibinder.h> // for AIBinder_getCallingUid
 #include <audio_utils/primitives.h>
 #include <media/DataSourceBase.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -27,13 +28,26 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
+#include <private/android_filesystem_config.h> // for AID_MEDIA
+#include <system/audio.h>
 #include <utils/String8.h>
 #include <cutils/bitops.h>
 
 #define CHANNEL_MASK_USE_CHANNEL_ORDER 0
 
+// NOTE: This code assumes the device processor is little endian.
+
 namespace android {
 
+// MediaServer is capable of handling float extractor output, but general processes
+// may not be able to do so.
+// TODO: Improve API to set extractor float output.
+// (Note: duplicated with FLACExtractor.cpp)
+static inline bool shouldExtractorOutputFloat(int bitsPerSample)
+{
+    return bitsPerSample > 16 && AIBinder_getCallingUid() == AID_MEDIA;
+}
+
 enum {
     WAVE_FORMAT_PCM        = 0x0001,
     WAVE_FORMAT_IEEE_FLOAT = 0x0003,
@@ -59,7 +73,7 @@
             DataSourceHelper *dataSource,
             AMediaFormat *meta,
             uint16_t waveFormat,
-            int32_t bitsPerSample,
+            bool outputFloat,
             off64_t offset, size_t size);
 
     virtual media_status_t start();
@@ -80,6 +94,7 @@
     DataSourceHelper *mDataSource;
     AMediaFormat *mMeta;
     uint16_t mWaveFormat;
+    const bool mOutputFloat;
     int32_t mSampleRate;
     int32_t mNumChannels;
     int32_t mBitsPerSample;
@@ -126,7 +141,7 @@
 
     return new WAVSource(
             mDataSource, mTrackMeta,
-            mWaveFormat, mBitsPerSample, mDataOffset, mDataSize);
+            mWaveFormat, shouldExtractorOutputFloat(mBitsPerSample), mDataOffset, mDataSize);
 }
 
 media_status_t WAVExtractor::getTrackMetaData(
@@ -136,7 +151,13 @@
         return AMEDIA_ERROR_UNKNOWN;
     }
 
-    return AMediaFormat_copy(meta, mTrackMeta);
+    const media_status_t status = AMediaFormat_copy(meta, mTrackMeta);
+    if (status == OK) {
+        AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_PCM_ENCODING,
+                shouldExtractorOutputFloat(mBitsPerSample)
+                        ? kAudioEncodingPcmFloat : kAudioEncodingPcm16bit);
+    }
+    return status;
 }
 
 status_t WAVExtractor::init() {
@@ -199,13 +220,13 @@
 
             mNumChannels = U16_LE_AT(&formatSpec[2]);
 
-            if (mNumChannels < 1 || mNumChannels > 8) {
+            if (mNumChannels < 1 || mNumChannels > FCC_8) {
                 ALOGE("Unsupported number of channels (%d)", mNumChannels);
                 return AMEDIA_ERROR_UNSUPPORTED;
             }
 
             if (mWaveFormat != WAVE_FORMAT_EXTENSIBLE) {
-                if (mNumChannels != 1 && mNumChannels != 2) {
+                if (mNumChannels != 1 && mNumChannels != FCC_2) {
                     ALOGW("More than 2 channels (%d) in non-WAVE_EXT, unknown channel mask",
                             mNumChannels);
                 }
@@ -312,9 +333,6 @@
                 AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_CHANNEL_MASK, mChannelMask);
                 AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, mSampleRate);
                 AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, mBitsPerSample);
-                AMediaFormat_setInt32(mTrackMeta, AMEDIAFORMAT_KEY_PCM_ENCODING,
-                        kAudioEncodingPcm16bit);
-
                 int64_t durationUs = 0;
                 if (mWaveFormat == WAVE_FORMAT_MSGSM) {
                     // 65 bytes decode to 320 8kHz samples
@@ -353,22 +371,19 @@
         DataSourceHelper *dataSource,
         AMediaFormat *meta,
         uint16_t waveFormat,
-        int32_t bitsPerSample,
+        bool outputFloat,
         off64_t offset, size_t size)
     : mDataSource(dataSource),
       mMeta(meta),
       mWaveFormat(waveFormat),
-      mSampleRate(0),
-      mNumChannels(0),
-      mBitsPerSample(bitsPerSample),
+      mOutputFloat(outputFloat),
       mOffset(offset),
       mSize(size),
       mStarted(false),
       mGroup(NULL) {
     CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate));
     CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &mNumChannels));
-
-    AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, kMaxFrameSize);
+    CHECK(AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_BITS_PER_SAMPLE, &mBitsPerSample));
 }
 
 WAVSource::~WAVSource() {
@@ -385,11 +400,6 @@
     // some WAV files may have large audio buffers that use shared memory transfer.
     mGroup = new MediaBufferGroup(4 /* buffers */, kMaxFrameSize);
 
-    if (mBitsPerSample == 8) {
-        // As a temporary buffer for 8->16 bit conversion.
-        mGroup->add_buffer(MediaBufferBase::Create(kMaxFrameSize));
-    }
-
     mCurrentPos = mOffset;
 
     mStarted = true;
@@ -413,7 +423,13 @@
 media_status_t WAVSource::getFormat(AMediaFormat *meta) {
     ALOGV("WAVSource::getFormat");
 
-    return AMediaFormat_copy(meta, mMeta);
+    const media_status_t status = AMediaFormat_copy(meta, mMeta);
+    if (status == OK) {
+        AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, kMaxFrameSize);
+        AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_PCM_ENCODING,
+                mOutputFloat ? kAudioEncodingPcmFloat : kAudioEncodingPcm16bit);
+    }
+    return status;
 }
 
 media_status_t WAVSource::read(
@@ -449,12 +465,16 @@
         return AMEDIA_ERROR_UNKNOWN;
     }
 
-    // make sure that maxBytesToRead is multiple of 3, in 24-bit case
-    size_t maxBytesToRead =
-        mBitsPerSample == 8 ? kMaxFrameSize / 2 : 
-        (mBitsPerSample == 24 ? 3*(kMaxFrameSize/3): kMaxFrameSize);
+    // maxBytesToRead may be reduced so that in-place data conversion will fit in buffer size.
+    const size_t bufferSize = buffer->size();
+    size_t maxBytesToRead;
+    if (mOutputFloat) { // destination is float at 4 bytes per sample, source may be less.
+        maxBytesToRead = (mBitsPerSample / 8) * (bufferSize / 4);
+    } else { // destination is int16_t at 2 bytes per sample, only source of 8 bits is less.
+        maxBytesToRead = mBitsPerSample == 8 ? bufferSize / 2 : bufferSize;
+    }
 
-    size_t maxBytesAvailable =
+    const size_t maxBytesAvailable =
         (mCurrentPos - mOffset >= (off64_t)mSize)
             ? 0 : mSize - (mCurrentPos - mOffset);
 
@@ -490,38 +510,57 @@
 
     // TODO: add capability to return data as float PCM instead of 16 bit PCM.
     if (mWaveFormat == WAVE_FORMAT_PCM) {
-        if (mBitsPerSample == 8) {
-            // Convert 8-bit unsigned samples to 16-bit signed.
-
-            // Create new buffer with 2 byte wide samples
-            MediaBufferBase *tmp;
-            CHECK_EQ(mGroup->acquire_buffer(&tmp), (status_t)OK);
-            tmp->set_range(0, 2 * n);
-
-            memcpy_to_i16_from_u8((int16_t *)tmp->data(), (const uint8_t *)buffer->data(), n);
-            buffer->release();
-            buffer = tmp;
-        } else if (mBitsPerSample == 24) {
-            // Convert 24-bit signed samples to 16-bit signed in place
-            const size_t numSamples = n / 3;
-
-            memcpy_to_i16_from_p24((int16_t *)buffer->data(), (const uint8_t *)buffer->data(), numSamples);
-            buffer->set_range(0, 2 * numSamples);
-        }  else if (mBitsPerSample == 32) {
-            // Convert 32-bit signed samples to 16-bit signed in place
-            const size_t numSamples = n / 4;
-
-            memcpy_to_i16_from_i32((int16_t *)buffer->data(), (const int32_t *)buffer->data(), numSamples);
-            buffer->set_range(0, 2 * numSamples);
+        if (mOutputFloat) {
+            float *fdest = (float *)buffer->data();
+            switch (mBitsPerSample) {
+            case 8: {
+                buffer->set_range(0, 4 * n);
+                memcpy_to_float_from_u8(fdest, (const uint8_t *)buffer->data(), n);
+            } break;
+            case 16: {
+                const size_t numSamples = n / 2;
+                buffer->set_range(0, 4 * numSamples);
+                memcpy_to_float_from_i16(fdest, (const int16_t *)buffer->data(), numSamples);
+            } break;
+            case 24: {
+                const size_t numSamples = n / 3;
+                buffer->set_range(0, 4 * numSamples);
+                memcpy_to_float_from_p24(fdest, (const uint8_t *)buffer->data(), numSamples);
+            } break;
+            case 32: { // buffer range is correct
+                const size_t numSamples = n / 4;
+                memcpy_to_float_from_i32(fdest, (const int32_t *)buffer->data(), numSamples);
+            } break;
+            }
+        } else {
+            int16_t *idest = (int16_t *)buffer->data();
+            switch (mBitsPerSample) {
+            case 8: {
+                buffer->set_range(0, 2 * n);
+                memcpy_to_i16_from_u8(idest, (const uint8_t *)buffer->data(), n);
+            } break;
+            case 16:
+                break; // no translation needed
+            case 24: {
+                const size_t numSamples = n / 3;
+                buffer->set_range(0, 2 * numSamples);
+                memcpy_to_i16_from_p24(idest, (const uint8_t *)buffer->data(), numSamples);
+            } break;
+            case 32: {
+                const size_t numSamples = n / 4;
+                buffer->set_range(0, 2 * numSamples);
+                memcpy_to_i16_from_i32(idest, (const int32_t *)buffer->data(), numSamples);
+            } break;
+            }
         }
     } else if (mWaveFormat == WAVE_FORMAT_IEEE_FLOAT) {
-        if (mBitsPerSample == 32) {
-            // Convert 32-bit float samples to 16-bit signed in place
+        if (!mOutputFloat) { // mBitsPerSample == 32
+            int16_t *idest = (int16_t *)buffer->data();
             const size_t numSamples = n / 4;
-
-            memcpy_to_i16_from_float((int16_t *)buffer->data(), (const float *)buffer->data(), numSamples);
+            memcpy_to_i16_from_float(idest, (const float *)buffer->data(), numSamples);
             buffer->set_range(0, 2 * numSamples);
         }
+        // Note: if output encoding is float, no need to convert if source is float.
     }
 
     int64_t timeStampUs = 0;
diff --git a/media/img_utils/include/img_utils/DngUtils.h b/media/img_utils/include/img_utils/DngUtils.h
index de8f120..8819f87 100644
--- a/media/img_utils/include/img_utils/DngUtils.h
+++ b/media/img_utils/include/img_utils/DngUtils.h
@@ -49,6 +49,7 @@
             CFA_RGGB,
             CFA_BGGR,
             CFA_GBRG,
+            CFA_NONE,
         };
 
         OpcodeListBuilder();
@@ -89,7 +90,6 @@
                                                 CfaLayout cfa,
                                                 const float* lensShadingMap);
 
-
         /**
          * Add a GainMap opcode with the given fields.  The mapGains array
          * must have mapPointsV * mapPointsH * mapPlanes elements.
@@ -197,6 +197,33 @@
 
         status_t addOpcodePreamble(uint32_t opcodeId);
 
+    private:
+        /**
+         * Add Bayer GainMap opcode(s) for the given metadata parameters.
+         * CFA layout must match the layout of the shading map passed into the
+         * lensShadingMap parameter.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        status_t addBayerGainMapsForMetadata(uint32_t lsmWidth,
+                                                uint32_t lsmHeight,
+                                                uint32_t activeAreaWidth,
+                                                uint32_t activeAreaHeight,
+                                                CfaLayout cfa,
+                                                const float* lensShadingMap);
+
+        /**
+         * Add Bayer GainMap opcode(s) for the given metadata parameters.
+         * CFA layout must match the layout of the shading map passed into the
+         * lensShadingMap parameter.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        status_t addMonochromeGainMapsForMetadata(uint32_t lsmWidth,
+                                                uint32_t lsmHeight,
+                                                uint32_t activeAreaWidth,
+                                                uint32_t activeAreaHeight,
+                                                const float* lensShadingMap);
 };
 
 } /*namespace img_utils*/
diff --git a/media/img_utils/src/DngUtils.cpp b/media/img_utils/src/DngUtils.cpp
index 9ac7e2a..9304f53 100644
--- a/media/img_utils/src/DngUtils.cpp
+++ b/media/img_utils/src/DngUtils.cpp
@@ -60,34 +60,36 @@
                                                    uint32_t activeAreaRight,
                                                    CfaLayout cfa,
                                                    const float* lensShadingMap) {
+    status_t err = OK;
     uint32_t activeAreaWidth = activeAreaRight - activeAreaLeft;
     uint32_t activeAreaHeight = activeAreaBottom - activeAreaTop;
-    double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
-    double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
 
-    std::vector<float> redMapVector(lsmWidth * lsmHeight);
-    float *redMap = redMapVector.data();
-
-    std::vector<float> greenEvenMapVector(lsmWidth * lsmHeight);
-    float *greenEvenMap = greenEvenMapVector.data();
-
-    std::vector<float> greenOddMapVector(lsmWidth * lsmHeight);
-    float *greenOddMap = greenOddMapVector.data();
-
-    std::vector<float> blueMapVector(lsmWidth * lsmHeight);
-    float *blueMap = blueMapVector.data();
-
-    size_t lsmMapSize = lsmWidth * lsmHeight * 4;
-
-    // Split lens shading map channels into separate arrays
-    size_t j = 0;
-    for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
-        redMap[j] = lensShadingMap[i + LSM_R_IND];
-        greenEvenMap[j] = lensShadingMap[i + LSM_GE_IND];
-        greenOddMap[j] = lensShadingMap[i + LSM_GO_IND];
-        blueMap[j] = lensShadingMap[i + LSM_B_IND];
+    switch (cfa) {
+        case CFA_RGGB:
+        case CFA_GRBG:
+        case CFA_GBRG:
+        case CFA_BGGR:
+            err = addBayerGainMapsForMetadata(lsmWidth, lsmHeight, activeAreaWidth,
+                    activeAreaHeight, cfa, lensShadingMap);
+            break;
+        case CFA_NONE:
+            err = addMonochromeGainMapsForMetadata(lsmWidth, lsmHeight, activeAreaWidth,
+                    activeAreaHeight, lensShadingMap);
+            break;
+        default:
+            ALOGE("%s: Unknown CFA layout %d", __FUNCTION__, cfa);
+            err = BAD_VALUE;
+            break;
     }
+    return err;
+}
 
+status_t OpcodeListBuilder::addBayerGainMapsForMetadata(uint32_t lsmWidth,
+                                                   uint32_t lsmHeight,
+                                                   uint32_t activeAreaWidth,
+                                                   uint32_t activeAreaHeight,
+                                                   CfaLayout cfa,
+                                                   const float* lensShadingMap) {
     uint32_t redTop = 0;
     uint32_t redLeft = 0;
     uint32_t greenEvenTop = 0;
@@ -143,6 +145,32 @@
             return BAD_VALUE;
     }
 
+    std::vector<float> redMapVector(lsmWidth * lsmHeight);
+    float *redMap = redMapVector.data();
+
+    std::vector<float> greenEvenMapVector(lsmWidth * lsmHeight);
+    float *greenEvenMap = greenEvenMapVector.data();
+
+    std::vector<float> greenOddMapVector(lsmWidth * lsmHeight);
+    float *greenOddMap = greenOddMapVector.data();
+
+    std::vector<float> blueMapVector(lsmWidth * lsmHeight);
+    float *blueMap = blueMapVector.data();
+
+    double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
+    double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
+
+    size_t lsmMapSize = lsmWidth * lsmHeight * 4;
+
+    // Split lens shading map channels into separate arrays
+    size_t j = 0;
+    for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
+        redMap[j] = lensShadingMap[i + LSM_R_IND];
+        greenEvenMap[j] = lensShadingMap[i + LSM_GE_IND];
+        greenOddMap[j] = lensShadingMap[i + LSM_GO_IND];
+        blueMap[j] = lensShadingMap[i + LSM_B_IND];
+    }
+
     status_t err = addGainMap(/*top*/redTop,
                               /*left*/redLeft,
                               /*bottom*/activeAreaHeight - 1,
@@ -216,6 +244,46 @@
     return err;
 }
 
+status_t OpcodeListBuilder::addMonochromeGainMapsForMetadata(uint32_t lsmWidth,
+                                                   uint32_t lsmHeight,
+                                                   uint32_t activeAreaWidth,
+                                                   uint32_t activeAreaHeight,
+                                                   const float* lensShadingMap) {
+    std::vector<float> mapVector(lsmWidth * lsmHeight);
+    float *map = mapVector.data();
+
+    double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
+    double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
+
+    size_t lsmMapSize = lsmWidth * lsmHeight * 4;
+
+    // Split lens shading map channels into separate arrays
+    size_t j = 0;
+    for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
+        map[j] = lensShadingMap[i];
+    }
+
+    status_t err = addGainMap(/*top*/0,
+                              /*left*/0,
+                              /*bottom*/activeAreaHeight - 1,
+                              /*right*/activeAreaWidth - 1,
+                              /*plane*/0,
+                              /*planes*/1,
+                              /*rowPitch*/1,
+                              /*colPitch*/1,
+                              /*mapPointsV*/lsmHeight,
+                              /*mapPointsH*/lsmWidth,
+                              /*mapSpacingV*/spacingV,
+                              /*mapSpacingH*/spacingH,
+                              /*mapOriginV*/0,
+                              /*mapOriginH*/0,
+                              /*mapPlanes*/1,
+                              /*mapGains*/map);
+    if (err != OK) return err;
+
+    return err;
+}
+
 status_t OpcodeListBuilder::addGainMap(uint32_t top,
                                        uint32_t left,
                                        uint32_t bottom,
diff --git a/media/libaaudio/Android.bp b/media/libaaudio/Android.bp
index f00f7a8..4857008 100644
--- a/media/libaaudio/Android.bp
+++ b/media/libaaudio/Android.bp
@@ -32,6 +32,7 @@
 cc_library_headers {
     name: "libaaudio_headers",
     export_include_dirs: ["include"],
+    version_script: "libaaudio.map.txt",
 }
 
 subdirs = ["*"]
diff --git a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
index 64deb60..9711b86 100644
--- a/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
+++ b/media/libaaudio/examples/loopback/src/LoopbackAnalyzer.h
@@ -34,12 +34,123 @@
 
 // Tag for machine readable results as property = value pairs
 #define LOOPBACK_RESULT_TAG      "RESULT: "
-#define LOOPBACK_SAMPLE_RATE     48000
 
-#define MILLIS_PER_SECOND        1000
+constexpr int32_t kDefaultSampleRate = 48000;
+constexpr int32_t kMillisPerSecond   = 1000;
+constexpr int32_t kMinLatencyMillis  = 4;    // arbitrary and very low
+constexpr int32_t kMaxLatencyMillis  = 400;  // arbitrary and generous
+constexpr double  kMaxEchoGain       = 10.0; // based on experiments, otherwise too noisy
+constexpr double  kMinimumConfidence = 0.5;
 
-#define MAX_ZEROTH_PARTIAL_BINS  40
-constexpr double MAX_ECHO_GAIN = 10.0; // based on experiments, otherwise autocorrelation too noisy
+static void printAudioScope(float sample) {
+    const int maxStars = 80; // arbitrary, fits on one line
+    char c = '*';
+    if (sample < -1.0) {
+        sample = -1.0;
+        c = '$';
+    } else if (sample > 1.0) {
+        sample = 1.0;
+        c = '$';
+    }
+    int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars);
+    for (int i = 0; i < numSpaces; i++) {
+        putchar(' ');
+    }
+    printf("%c\n", c);
+}
+
+/*
+
+FIR filter designed with
+http://t-filter.appspot.com
+
+sampling frequency: 48000 Hz
+
+* 0 Hz - 8000 Hz
+  gain = 1.2
+  desired ripple = 5 dB
+  actual ripple = 5.595266169703693 dB
+
+* 12000 Hz - 20000 Hz
+  gain = 0
+  desired attenuation = -40 dB
+  actual attenuation = -37.58691566571914 dB
+
+*/
+
+#define FILTER_TAP_NUM 11
+
+static const float sFilterTaps8000[FILTER_TAP_NUM] = {
+        -0.05944219353343189f,
+        -0.07303434839503208f,
+        -0.037690487672689066f,
+        0.1870480506596512f,
+        0.3910337357836833f,
+        0.5333672385425637f,
+        0.3910337357836833f,
+        0.1870480506596512f,
+        -0.037690487672689066f,
+        -0.07303434839503208f,
+        -0.05944219353343189f
+};
+
+class LowPassFilter {
+public:
+
+    /*
+     * Filter one input sample.
+     * @return filtered output
+     */
+    float filter(float input) {
+        float output = 0.0f;
+        mX[mCursor] = input;
+        // Index backwards over x.
+        int xIndex = mCursor + FILTER_TAP_NUM;
+        // Write twice so we avoid having to wrap in the middle of the convolution.
+        mX[xIndex] = input;
+        for (int i = 0; i < FILTER_TAP_NUM; i++) {
+            output += sFilterTaps8000[i] * mX[xIndex--];
+        }
+        if (++mCursor >= FILTER_TAP_NUM) {
+            mCursor = 0;
+        }
+        return output;
+    }
+
+    /**
+     * @return true if PASSED
+     */
+    bool test() {
+        // Measure the impulse of the filter at different phases so we exercise
+        // all the wraparound cases in the FIR.
+        for (int offset = 0; offset < (FILTER_TAP_NUM * 2); offset++ ) {
+            // printf("LowPassFilter: cursor = %d\n", mCursor);
+            // Offset by one each time.
+            if (filter(0.0f) != 0.0f) {
+                printf("ERROR: filter should return 0.0 before impulse response\n");
+                return false;
+            }
+            for (int i = 0; i < FILTER_TAP_NUM; i++) {
+                float output = filter((i == 0) ? 1.0f : 0.0f); // impulse
+                if (output != sFilterTaps8000[i]) {
+                    printf("ERROR: filter should return impulse response\n");
+                    return false;
+                }
+            }
+            for (int i = 0; i < FILTER_TAP_NUM; i++) {
+                if (filter(0.0f) != 0.0f) {
+                    printf("ERROR: filter should return 0.0 after impulse response\n");
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+private:
+    float   mX[FILTER_TAP_NUM * 2]{}; // twice as big as needed to avoid wrapping
+    int32_t mCursor = 0;
+};
 
 // A narrow impulse seems to have better immunity against over estimating the
 // latency due to detecting subharmonics by the auto-correlator.
@@ -78,6 +189,12 @@
     int64_t mSeed = 99887766;
 };
 
+
+typedef struct LatencyReport_s {
+    double latencyInFrames;
+    double confidence;
+} LatencyReport;
+
 static double calculateCorrelation(const float *a,
                                    const float *b,
                                    int windowSize)
@@ -101,127 +218,75 @@
     return correlation;
 }
 
-static int calculateCorrelations(const float *haystack, int haystackSize,
-                                 const float *needle, int needleSize,
-                                 float *results, int resultSize)
-{
-    int maxCorrelations = haystackSize - needleSize;
-    int numCorrelations = std::min(maxCorrelations, resultSize);
-
-    for (int ic = 0; ic < numCorrelations; ic++) {
-        double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
-        results[ic] = correlation;
-    }
-
-    return numCorrelations;
-}
-
-/*==========================================================================================*/
-/**
- * Scan until we get a correlation of a single scan that goes over the tolerance level,
- * peaks then drops back down.
- */
-static double findFirstMatch(const float *haystack, int haystackSize,
-                             const float *needle, int needleSize, double threshold  )
-{
-    int ic;
-    // How many correlations can we calculate?
-    int numCorrelations = haystackSize - needleSize;
-    double maxCorrelation = 0.0;
-    int peakIndex = -1;
-    double location = -1.0;
-    const double backThresholdScaler = 0.5;
-
-    for (ic = 0; ic < numCorrelations; ic++) {
-        double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
-
-        if( (correlation > maxCorrelation) ) {
-            maxCorrelation = correlation;
-            peakIndex = ic;
-        }
-
-        //printf("PaQa_FindFirstMatch: ic = %4d, correlation = %8f, maxSum = %8f\n",
-        //    ic, correlation, maxSum );
-        // Are we past what we were looking for?
-        if((maxCorrelation > threshold) && (correlation < backThresholdScaler * maxCorrelation)) {
-            location = peakIndex;
-            break;
-        }
-    }
-
-    return location;
-}
-
-typedef struct LatencyReport_s {
-    double latencyInFrames;
-    double confidence;
-} LatencyReport;
-
-// Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
-// Using first echo instead of the original impulse for a better match.
-static int measureLatencyFromEchos(const float *haystack, int haystackSize,
-                            const float *needle, int needleSize,
-                            LatencyReport *report) {
-    const double threshold = 0.1;
-    // printf("%s: haystackSize = %d, needleSize = %d\n", __func__, haystackSize, needleSize);
-
-    // Find first peak
-    int first = (int) (findFirstMatch(haystack,
-                                      haystackSize,
-                                      needle,
-                                      needleSize,
-                                      threshold) + 0.5);
-
-    // Use first echo as the needle for the other echos because
-    // it will be more similar.
-    needle = &haystack[first];
-    int again = (int) (findFirstMatch(haystack,
-                                      haystackSize,
-                                      needle,
-                                      needleSize,
-                                      threshold) + 0.5);
-    first = again;
-
+static int measureLatencyFromEchos(const float *data,
+                                   int32_t numFloats,
+                                   int32_t sampleRate,
+                                   LatencyReport *report) {
     // Allocate results array
-    int remaining = haystackSize - first;
-    const int maxReasonableLatencyFrames = 48000 * 2; // arbitrary but generous value
-    int numCorrelations = std::min(remaining, maxReasonableLatencyFrames);
-    float *correlations = new float[numCorrelations];
-    float *harmonicSums = new float[numCorrelations](); // set to zero
+    const int minReasonableLatencyFrames = sampleRate * kMinLatencyMillis / kMillisPerSecond;
+    const int maxReasonableLatencyFrames = sampleRate * kMaxLatencyMillis / kMillisPerSecond;
+    int32_t maxCorrelationSize = maxReasonableLatencyFrames * 3;
+    int numCorrelations = std::min(numFloats, maxCorrelationSize);
+    float *correlations = new float[numCorrelations]{};
+    float *harmonicSums = new float[numCorrelations]{};
 
-    // Generate correlation for every position.
-    numCorrelations = calculateCorrelations(&haystack[first], remaining,
-                                            needle, needleSize,
-                                            correlations, numCorrelations);
+    // Perform sliding auto-correlation.
+    // Skip first frames to avoid huge peak at zero offset.
+    for (int i = minReasonableLatencyFrames; i < numCorrelations; i++) {
+        int32_t remaining = numFloats - i;
+        float correlation = (float) calculateCorrelation(&data[i], data, remaining);
+        correlations[i] = correlation;
+        // printf("correlation[%d] = %f\n", ic, correlation);
+    }
 
-    // Add higher harmonics mapped onto lower harmonics.
-    // This reinforces the "fundamental" echo.
+    // Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
+    // Add higher harmonics mapped onto lower harmonics. This reinforces the "fundamental" echo.
     const int numEchoes = 8;
     for (int partial = 1; partial < numEchoes; partial++) {
-        for (int i = 0; i < numCorrelations; i++) {
+        for (int i = minReasonableLatencyFrames; i < numCorrelations; i++) {
             harmonicSums[i / partial] += correlations[i] / partial;
         }
     }
 
     // Find highest peak in correlation array.
     float maxCorrelation = 0.0;
-    float sumOfPeaks = 0.0;
     int peakIndex = 0;
-    const int skip = MAX_ZEROTH_PARTIAL_BINS; // skip low bins
-    for (int i = skip; i < numCorrelations; i++) {
+    for (int i = 0; i < numCorrelations; i++) {
         if (harmonicSums[i] > maxCorrelation) {
             maxCorrelation = harmonicSums[i];
-            sumOfPeaks += maxCorrelation;
             peakIndex = i;
             // printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
         }
     }
-
     report->latencyInFrames = peakIndex;
-    if (sumOfPeaks < 0.0001) {
+/*
+    {
+        int32_t topPeak = peakIndex * 7 / 2;
+        for (int i = 0; i < topPeak; i++) {
+            float sample = harmonicSums[i];
+            printf("%4d: %7.5f ", i, sample);
+            printAudioScope(sample);
+        }
+    }
+*/
+
+    // Calculate confidence.
+    if (maxCorrelation < 0.001) {
         report->confidence = 0.0;
     } else {
-        report->confidence = maxCorrelation / sumOfPeaks;
+        // Compare peak to average value around peak.
+        int32_t numSamples = std::min(numCorrelations, peakIndex * 2);
+        if (numSamples <= 0) {
+            report->confidence = 0.0;
+        } else {
+            double sum = 0.0;
+            for (int i = 0; i < numSamples; i++) {
+                sum += harmonicSums[i];
+            }
+            const double average = sum / numSamples;
+            const double ratio = average / maxCorrelation; // will be < 1.0
+            report->confidence = 1.0 - sqrt(ratio);
+        }
     }
 
     delete[] correlations;
@@ -317,7 +382,9 @@
         }
 
         assert(info.channels == 1);
+        assert(info.format == SF_FORMAT_FLOAT);
 
+        setSampleRate(info.samplerate);
         allocate(info.frames);
         mFrameCounter = sf_readf_float(sndFile, mData, info.frames);
 
@@ -325,11 +392,49 @@
         return mFrameCounter;
     }
 
+    /**
+     * Square the samples so they are all positive and so the peaks are emphasized.
+     */
+    void square() {
+        for (int i = 0; i < mFrameCounter; i++) {
+            const float sample = mData[i];
+            mData[i] = sample * sample;
+        }
+    }
+
+    /**
+     * Low pass filter the recording using a simple FIR filter.
+     * Note that the lowpass filter cutoff tracks the sample rate.
+     * That is OK because the impulse width is a fixed number of samples.
+     */
+    void lowPassFilter() {
+        for (int i = 0; i < mFrameCounter; i++) {
+            mData[i] = mLowPassFilter.filter(mData[i]);
+        }
+    }
+
+    /**
+     * Remove DC offset using a one-pole one-zero IIR filter.
+     */
+    void dcBlocker() {
+        const float R = 0.996; // narrow notch at zero Hz
+        float x1 = 0.0;
+        float y1 = 0.0;
+        for (int i = 0; i < mFrameCounter; i++) {
+            const float x = mData[i];
+            const float y = x - x1 + (R * y1);
+            mData[i] = y;
+            y1 = y;
+            x1 = x;
+        }
+    }
+
 private:
-    float  *mData = nullptr;
-    int32_t mFrameCounter = 0;
-    int32_t mMaxFrames = 0;
-    int32_t mSampleRate = 48000; // common default
+    float        *mData = nullptr;
+    int32_t       mFrameCounter = 0;
+    int32_t       mMaxFrames = 0;
+    int32_t       mSampleRate = kDefaultSampleRate; // common default
+    LowPassFilter mLowPassFilter;
 };
 
 // ====================================================================================
@@ -349,8 +454,12 @@
 
     virtual void printStatus() {};
 
-    virtual int getResult() {
-        return -1;
+    int32_t getResult() {
+        return mResult;
+    }
+
+    void setResult(int32_t result) {
+        mResult = result;
     }
 
     virtual bool isDone() {
@@ -379,7 +488,7 @@
     static float measurePeakAmplitude(float *inputData, int inputChannelCount, int numFrames) {
         float peak = 0.0f;
         for (int i = 0; i < numFrames; i++) {
-            float pos = fabs(*inputData);
+            const float pos = fabs(*inputData);
             if (pos > peak) {
                 peak = pos;
             }
@@ -390,7 +499,8 @@
 
 
 private:
-    int32_t mSampleRate = LOOPBACK_SAMPLE_RATE;
+    int32_t mSampleRate = kDefaultSampleRate;
+    int32_t mResult = 0;
 };
 
 class PeakDetector {
@@ -409,24 +519,6 @@
     float  mPrevious = 0.0f;
 };
 
-
-static void printAudioScope(float sample) {
-    const int maxStars = 80; // arbitrary, fits on one line
-    char c = '*';
-    if (sample < -1.0) {
-        sample = -1.0;
-        c = '$';
-    } else if (sample > 1.0) {
-        sample = 1.0;
-        c = '$';
-    }
-    int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars);
-    for (int i = 0; i < numSpaces; i++) {
-        putchar(' ');
-    }
-    printf("%c\n", c);
-}
-
 // ====================================================================================
 /**
  * Measure latency given a loopback stream data.
@@ -447,17 +539,13 @@
     }
 
     void reset() override {
-        mDownCounter = 200;
+        mDownCounter = getSampleRate() / 2;
         mLoopCounter = 0;
         mMeasuredLoopGain = 0.0f;
         mEchoGain = 1.0f;
         mState = STATE_INITIAL_SILENCE;
     }
 
-    virtual int getResult() {
-        return mState == STATE_DONE ? 0 : -1;
-    }
-
     virtual bool isDone() {
         return mState == STATE_DONE || mState == STATE_FAILED;
     }
@@ -470,34 +558,57 @@
         return mEchoGain;
     }
 
-    void report() override {
+    bool testLowPassFilter() {
+        LowPassFilter filter;
+        return filter.test();
+    }
 
+    void report() override {
         printf("EchoAnalyzer ---------------\n");
+        if (getResult() != 0) {
+            printf(LOOPBACK_RESULT_TAG "result          = %d\n", getResult());
+            return;
+        }
+
+        // printf("LowPassFilter test %s\n", testLowPassFilter() ? "PASSED" : "FAILED");
+
         printf(LOOPBACK_RESULT_TAG "measured.gain          = %8f\n", mMeasuredLoopGain);
         printf(LOOPBACK_RESULT_TAG "echo.gain              = %8f\n", mEchoGain);
         printf(LOOPBACK_RESULT_TAG "test.state             = %8d\n", mState);
         printf(LOOPBACK_RESULT_TAG "test.state.name        = %8s\n", convertStateToText(mState));
+
         if (mState == STATE_WAITING_FOR_SILENCE) {
             printf("WARNING - Stuck waiting for silence. Input may be too noisy!\n");
-        }
-        if (mMeasuredLoopGain >= 0.9999) {
+            setResult(ERROR_NOISY);
+        } else if (mMeasuredLoopGain >= 0.9999) {
             printf("   ERROR - clipping, turn down volume slightly\n");
+            setResult(ERROR_CLIPPING);
+        } else if (mState != STATE_DONE && mState != STATE_GATHERING_ECHOS) {
+            printf("WARNING - Bad state. Check volume on device.\n");
+            setResult(ERROR_INVALID_STATE);
         } else {
-            const float *needle = s_Impulse;
-            int needleSize = (int) (sizeof(s_Impulse) / sizeof(float));
-            float *haystack = mAudioRecording.getData();
-            int haystackSize = mAudioRecording.size();
-            measureLatencyFromEchos(haystack, haystackSize, needle, needleSize, &mLatencyReport);
-            if (mLatencyReport.confidence < 0.01) {
-                printf("   ERROR - confidence too low = %f\n", mLatencyReport.confidence);
-            } else {
-                double latencyMillis = 1000.0 * mLatencyReport.latencyInFrames / getSampleRate();
-                printf(LOOPBACK_RESULT_TAG "latency.frames         = %8.2f\n",
-                       mLatencyReport.latencyInFrames);
-                printf(LOOPBACK_RESULT_TAG "latency.msec           = %8.2f\n",
-                       latencyMillis);
-                printf(LOOPBACK_RESULT_TAG "latency.confidence     = %8.6f\n",
-                       mLatencyReport.confidence);
+            // Cleanup the signal to improve the auto-correlation.
+            mAudioRecording.dcBlocker();
+            mAudioRecording.square();
+            mAudioRecording.lowPassFilter();
+
+            printf("Please wait several seconds for auto-correlation to complete.\n");
+            measureLatencyFromEchos(mAudioRecording.getData(),
+                                    mAudioRecording.size(),
+                                    getSampleRate(),
+                                    &mLatencyReport);
+
+            double latencyMillis = kMillisPerSecond * (double) mLatencyReport.latencyInFrames
+                                   / getSampleRate();
+            printf(LOOPBACK_RESULT_TAG "latency.frames         = %8.2f\n",
+                   mLatencyReport.latencyInFrames);
+            printf(LOOPBACK_RESULT_TAG "latency.msec           = %8.2f\n",
+                   latencyMillis);
+            printf(LOOPBACK_RESULT_TAG "latency.confidence     = %8.6f\n",
+                   mLatencyReport.confidence);
+            if (mLatencyReport.confidence < kMinimumConfidence) {
+                printf("   ERROR - confidence too low!\n");
+                setResult(ERROR_CONFIDENCE);
             }
         }
     }
@@ -523,6 +634,11 @@
         sendImpulses(outputData, outputChannelCount, kImpulseSizeInFrames);
     }
 
+    // @return number of frames for a typical block of processing
+    int32_t getBlockFrames() {
+        return getSampleRate() / 8;
+    }
+
     void process(float *inputData, int inputChannelCount,
                  float *outputData, int outputChannelCount,
                  int numFrames) override {
@@ -540,10 +656,11 @@
                 for (int i = 0; i < numSamples; i++) {
                     outputData[i] = 0;
                 }
-                if (mDownCounter-- <= 0) {
+                mDownCounter -= numFrames;
+                if (mDownCounter <= 0) {
                     nextState = STATE_MEASURING_GAIN;
                     //printf("%5d: switch to STATE_MEASURING_GAIN\n", mLoopCounter);
-                    mDownCounter = 8;
+                    mDownCounter = getBlockFrames() * 2;
                 }
                 break;
 
@@ -552,15 +669,16 @@
                 peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
                 // If we get several in a row then go to next state.
                 if (peak > mPulseThreshold) {
-                    if (mDownCounter-- <= 0) {
+                    mDownCounter -= numFrames;
+                    if (mDownCounter <= 0) {
                         //printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n",
                         //       mLoopCounter, peak);
-                        mDownCounter = 8;
+                        mDownCounter = getBlockFrames();
                         mMeasuredLoopGain = peak;  // assumes original pulse amplitude is one
                         mSilenceThreshold = peak * 0.1; // scale silence to measured pulse
                         // Calculate gain that will give us a nice decaying echo.
                         mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
-                        if (mEchoGain > MAX_ECHO_GAIN) {
+                        if (mEchoGain > kMaxEchoGain) {
                             printf("ERROR - loop gain too low. Increase the volume.\n");
                             nextState = STATE_FAILED;
                         } else {
@@ -568,7 +686,7 @@
                         }
                     }
                 } else if (numFrames > kImpulseSizeInFrames){ // ignore short callbacks
-                    mDownCounter = 8;
+                    mDownCounter = getBlockFrames();
                 }
                 break;
 
@@ -581,13 +699,14 @@
                 peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
                 // If we get several in a row then go to next state.
                 if (peak < mSilenceThreshold) {
-                    if (mDownCounter-- <= 0) {
+                    mDownCounter -= numFrames;
+                    if (mDownCounter <= 0) {
                         nextState = STATE_SENDING_PULSE;
                         //printf("%5d: switch to STATE_SENDING_PULSE\n", mLoopCounter);
-                        mDownCounter = 8;
+                        mDownCounter = getBlockFrames();
                     }
                 } else {
-                    mDownCounter = 8;
+                    mDownCounter = getBlockFrames();
                 }
                 break;
 
@@ -620,11 +739,11 @@
                 }
                 if (numWritten  < numFrames) {
                     nextState = STATE_DONE;
-                    //printf("%5d: switch to STATE_DONE\n", mLoopCounter);
                 }
                 break;
 
             case STATE_DONE:
+            case STATE_FAILED:
             default:
                 break;
         }
@@ -638,11 +757,22 @@
     }
 
     int load(const char *fileName) override {
-        return mAudioRecording.load(fileName);
+        int result = mAudioRecording.load(fileName);
+        setSampleRate(mAudioRecording.getSampleRate());
+        mState = STATE_DONE;
+        return result;
     }
 
 private:
 
+    enum error_code {
+        ERROR_OK = 0,
+        ERROR_NOISY = -99,
+        ERROR_CLIPPING,
+        ERROR_CONFIDENCE,
+        ERROR_INVALID_STATE
+    };
+
     enum echo_state {
         STATE_INITIAL_SILENCE,
         STATE_MEASURING_GAIN,
@@ -708,10 +838,6 @@
 class SineAnalyzer : public LoopbackProcessor {
 public:
 
-    virtual int getResult() {
-        return mState == STATE_LOCKED ? 0 : -1;
-    }
-
     void report() override {
         printf("SineAnalyzer ------------------\n");
         printf(LOOPBACK_RESULT_TAG "peak.amplitude     = %8f\n", mPeakAmplitude);
@@ -724,10 +850,9 @@
         float signalToNoiseDB = 10.0 * log(signalToNoise);
         printf(LOOPBACK_RESULT_TAG "signal.to.noise.db = %8.2f\n", signalToNoiseDB);
         if (signalToNoiseDB < MIN_SNRATIO_DB) {
-            printf("WARNING - signal to noise ratio is too low! < %d dB\n", MIN_SNRATIO_DB);
+            printf("ERROR - signal to noise ratio is too low! < %d dB. Adjust volume.\n", MIN_SNRATIO_DB);
+            setResult(ERROR_NOISY);
         }
-        printf(LOOPBACK_RESULT_TAG "phase.offset       = %8.5f\n", mPhaseOffset);
-        printf(LOOPBACK_RESULT_TAG "ref.phase          = %8.5f\n", mPhase);
         printf(LOOPBACK_RESULT_TAG "frames.accumulated = %8d\n", mFramesAccumulated);
         printf(LOOPBACK_RESULT_TAG "sine.period        = %8d\n", mSinePeriod);
         printf(LOOPBACK_RESULT_TAG "test.state         = %8d\n", mState);
@@ -736,10 +861,15 @@
         bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
         if (!gotLock) {
             printf("ERROR - failed to lock on reference sine tone\n");
+            setResult(ERROR_NO_LOCK);
         } else {
             // Only print if meaningful.
             printf(LOOPBACK_RESULT_TAG "glitch.count       = %8d\n", mGlitchCount);
             printf(LOOPBACK_RESULT_TAG "max.glitch         = %8f\n", mMaxGlitchDelta);
+            if (mGlitchCount > 0) {
+                printf("ERROR - number of glitches > 0\n");
+                setResult(ERROR_GLITCHES);
+            }
         }
     }
 
@@ -913,6 +1043,13 @@
 
 private:
 
+    enum error_code {
+        OK,
+        ERROR_NO_LOCK = -80,
+        ERROR_GLITCHES,
+        ERROR_NOISY
+    };
+
     enum sine_state_t {
         STATE_IDLE,
         STATE_MEASURE_NOISE,
@@ -963,8 +1100,6 @@
     sine_state_t  mState = STATE_IDLE;
 };
 
-
-#undef LOOPBACK_SAMPLE_RATE
 #undef LOOPBACK_RESULT_TAG
 
 #endif /* AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H */
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 124efd8..2a02b20 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -35,11 +35,14 @@
 #include "AAudioExampleUtils.h"
 #include "LoopbackAnalyzer.h"
 
+// V0.4.00 = rectify and low-pass filter the echos, use auto-correlation on entire echo
+#define APP_VERSION             "0.4.00"
+
 // Tag for machine readable results as property = value pairs
 #define RESULT_TAG              "RESULT: "
 #define FILENAME_ALL            "/data/loopback_all.wav"
 #define FILENAME_ECHOS          "/data/loopback_echos.wav"
-#define APP_VERSION             "0.3.00"
+#define FILENAME_PROCESSED      "/data/loopback_processed.wav"
 
 constexpr int kLogPeriodMillis       = 1000;
 constexpr int kNumInputChannels      = 1;
@@ -173,7 +176,8 @@
         int64_t inputFramesWritten = AAudioStream_getFramesWritten(myData->inputStream);
         int64_t inputFramesRead = AAudioStream_getFramesRead(myData->inputStream);
         int64_t framesAvailable = inputFramesWritten - inputFramesRead;
-        actualFramesRead = readFormattedData(myData, numFrames);
+
+        actualFramesRead = readFormattedData(myData, numFrames); // READ
         if (actualFramesRead < 0) {
             result = AAUDIO_CALLBACK_RESULT_STOP;
         } else {
@@ -193,6 +197,7 @@
                 }
                 myData->insufficientReadCount++;
                 myData->insufficientReadFrames += numFrames - actualFramesRead; // deficit
+                // printf("Error insufficientReadCount = %d\n",(int)myData->insufficientReadCount);
             }
 
             int32_t numSamples = actualFramesRead * myData->actualInputChannelCount;
@@ -355,6 +360,9 @@
 
     printf("%s - Audio loopback using AAudio V" APP_VERSION "\n", argv[0]);
 
+    // Use LOW_LATENCY as the default to match input default.
+    argParser.setPerformanceMode(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+
     for (int i = 1; i < argc; i++) {
         const char *arg = argv[i];
         if (argParser.parseArg(arg)) {
@@ -403,7 +411,7 @@
     }
 
     int32_t requestedDuration = argParser.getDurationSeconds();
-    int32_t requestedDurationMillis = requestedDuration * MILLIS_PER_SECOND;
+    int32_t requestedDurationMillis = requestedDuration * kMillisPerSecond;
     int32_t timeMillis = 0;
     int32_t recordingDuration = std::min(60 * 5, requestedDuration);
 
@@ -420,9 +428,11 @@
 
             loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer;
             int read = loopbackData.loopbackProcessor->load(FILENAME_ECHOS);
-            printf("main() read %d mono samples from %s on Android device\n", read, FILENAME_ECHOS);
+            printf("main() read %d mono samples from %s on Android device, rate = %d\n",
+                   read, FILENAME_ECHOS,
+                   loopbackData.loopbackProcessor->getSampleRate());
             loopbackData.loopbackProcessor->report();
-            return 0;
+            goto report_result;
         }
             break;
         default:
@@ -580,45 +590,6 @@
     printf("input error = %d = %s\n",
            loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError));
 
-    if (loopbackData.inputError == AAUDIO_OK) {
-        if (testMode == TEST_SINE_MAGNITUDE) {
-            printAudioGraph(loopbackData.audioRecording, 200);
-            // Print again so we don't have to scroll past waveform.
-            printf("OUTPUT Stream ----------------------------------------\n");
-            argParser.compareWithStream(outputStream);
-            printf("INPUT  Stream ----------------------------------------\n");
-            argParser.compareWithStream(inputStream);
-        }
-
-        loopbackData.loopbackProcessor->report();
-    }
-
-    {
-        int32_t framesRead = AAudioStream_getFramesRead(inputStream);
-        int32_t framesWritten = AAudioStream_getFramesWritten(inputStream);
-        printf("Callback Results ---------------------------------------- INPUT\n");
-        printf("  input overruns   = %8d\n", AAudioStream_getXRunCount(inputStream));
-        printf("  framesWritten    = %8d\n", framesWritten);
-        printf("  framesRead       = %8d\n", framesRead);
-        printf("  myFramesRead     = %8d\n", (int) loopbackData.framesReadTotal);
-        printf("  written - read   = %8d\n", (int) (framesWritten - framesRead));
-        printf("  insufficient #   = %8d\n", (int) loopbackData.insufficientReadCount);
-        if (loopbackData.insufficientReadCount > 0) {
-            printf("  insuffic. frames = %8d\n", (int) loopbackData.insufficientReadFrames);
-        }
-    }
-    {
-        int32_t framesRead = AAudioStream_getFramesRead(outputStream);
-        int32_t framesWritten = AAudioStream_getFramesWritten(outputStream);
-        printf("Callback Results ---------------------------------------- OUTPUT\n");
-        printf("  output underruns = %8d\n", AAudioStream_getXRunCount(outputStream));
-        printf("  myFramesWritten  = %8d\n", (int) loopbackData.framesWrittenTotal);
-        printf("  framesWritten    = %8d\n", framesWritten);
-        printf("  framesRead       = %8d\n", framesRead);
-        printf("  min numFrames    = %8d\n", (int) loopbackData.minNumFrames);
-        printf("  max numFrames    = %8d\n", (int) loopbackData.maxNumFrames);
-    }
-
     written = loopbackData.loopbackProcessor->save(FILENAME_ECHOS);
     if (written > 0) {
         printf("main() wrote %8d mono samples to \"%s\" on Android device\n",
@@ -631,10 +602,46 @@
                written, FILENAME_ALL);
     }
 
-    if (loopbackData.loopbackProcessor->getResult() < 0) {
-        printf("ERROR: LOOPBACK PROCESSING FAILED. Maybe because the volume was too low.\n");
-        result = loopbackData.loopbackProcessor->getResult();
+    if (loopbackData.inputError == AAUDIO_OK) {
+        if (testMode == TEST_SINE_MAGNITUDE) {
+            printAudioGraph(loopbackData.audioRecording, 200);
+        }
+
+        loopbackData.loopbackProcessor->report();
     }
+
+    {
+        int32_t framesRead = AAudioStream_getFramesRead(inputStream);
+        int32_t framesWritten = AAudioStream_getFramesWritten(inputStream);
+        const int64_t framesAvailable = framesWritten - framesRead;
+        printf("Callback Results ---------------------------------------- INPUT\n");
+        printf("  input overruns   = %8d\n", AAudioStream_getXRunCount(inputStream));
+        printf("  framesWritten    = %8d\n", framesWritten);
+        printf("  framesRead       = %8d\n", framesRead);
+        printf("  myFramesRead     = %8d\n", (int) loopbackData.framesReadTotal);
+        printf("  written - read   = %8d\n", (int) framesAvailable);
+        printf("  insufficient #   = %8d\n", (int) loopbackData.insufficientReadCount);
+        if (loopbackData.insufficientReadCount > 0) {
+            printf("  insuffic. frames = %8d\n", (int) loopbackData.insufficientReadFrames);
+        }
+        int32_t actualInputCapacity = AAudioStream_getBufferCapacityInFrames(inputStream);
+        if (framesAvailable > 2 * actualInputCapacity) {
+            printf("  WARNING: written - read > 2*capacity !\n");
+        }
+    }
+
+    {
+        int32_t framesRead = AAudioStream_getFramesRead(outputStream);
+        int32_t framesWritten = AAudioStream_getFramesWritten(outputStream);
+        printf("Callback Results ---------------------------------------- OUTPUT\n");
+        printf("  output underruns = %8d\n", AAudioStream_getXRunCount(outputStream));
+        printf("  myFramesWritten  = %8d\n", (int) loopbackData.framesWrittenTotal);
+        printf("  framesWritten    = %8d\n", framesWritten);
+        printf("  framesRead       = %8d\n", framesRead);
+        printf("  min numFrames    = %8d\n", (int) loopbackData.minNumFrames);
+        printf("  max numFrames    = %8d\n", (int) loopbackData.maxNumFrames);
+    }
+
     if (loopbackData.insufficientReadCount > 3) {
         printf("ERROR: LOOPBACK PROCESSING FAILED. insufficientReadCount too high\n");
         result = AAUDIO_ERROR_UNAVAILABLE;
@@ -646,13 +653,23 @@
     delete[] loopbackData.inputFloatData;
     delete[] loopbackData.inputShortData;
 
+report_result:
+    written = loopbackData.loopbackProcessor->save(FILENAME_PROCESSED);
+    if (written > 0) {
+        printf("main() wrote %8d processed samples to \"%s\" on Android device\n",
+               written, FILENAME_PROCESSED);
+    }
+
+    if (loopbackData.loopbackProcessor->getResult() < 0) {
+        result = loopbackData.loopbackProcessor->getResult();
+    }
     printf(RESULT_TAG "result = %d \n", result); // machine readable
     printf("result is %s\n", AAudio_convertResultToText(result)); // human readable
     if (result != AAUDIO_OK) {
-        printf("FAILURE\n");
+        printf("TEST FAILED\n");
         return EXIT_FAILURE;
     } else {
-        printf("SUCCESS\n");
+        printf("TEST PASSED\n");
         return EXIT_SUCCESS;
     }
 }
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
index ece9e6a..a5dc55f 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -130,10 +130,12 @@
     }
 
     int32_t getBufferCapacity() const {
+        printf("%s() returns %d\n", __func__, mBufferCapacity);
         return mBufferCapacity;
     }
 
     void setBufferCapacity(int32_t frames) {
+        printf("%s(%d)\n", __func__, frames);
         mBufferCapacity = frames;
     }
 
@@ -197,7 +199,7 @@
      * @param builder
      */
     void applyParameters(AAudioStreamBuilder *builder) const {
-        AAudioStreamBuilder_setBufferCapacityInFrames(builder, mBufferCapacity);
+        AAudioStreamBuilder_setBufferCapacityInFrames(builder, getBufferCapacity());
         AAudioStreamBuilder_setChannelCount(builder, mChannelCount);
         AAudioStreamBuilder_setDeviceId(builder, mDeviceId);
         AAudioStreamBuilder_setFormat(builder, mFormat);
@@ -281,7 +283,7 @@
                     if (strlen(arg) > 2) {
                         policy = atoi(&arg[2]);
                     }
-                    if (!AAudio_setMMapPolicy(policy)) {
+                    if (AAudio_setMMapPolicy(policy) != AAUDIO_OK) {
                         printf("ERROR: invalid MMAP policy mode %i\n", policy);
                     }
                 } break;
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 8b35a85..96fccae 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -810,6 +810,13 @@
     if (t != 0) {
         if (!isOffloaded_l()) {
             t->pause();
+        } else if (mTransfer == TRANSFER_SYNC_NOTIF_CALLBACK) {
+            const sp<AudioTrackThread> t = mAudioTrackThread;
+            if (t != 0) {
+                // causes wake up of the playback thread, that will callback the client for
+                // EVENT_STREAM_END in processAudioBuffer()
+                t->wake();
+            }
         }
     } else {
         setpriority(PRIO_PROCESS, 0, mPreviousPriority);
diff --git a/media/libaudioprocessing/AudioResamplerSinc.cpp b/media/libaudioprocessing/AudioResamplerSinc.cpp
index 320b8cf..5a03a0d 100644
--- a/media/libaudioprocessing/AudioResamplerSinc.cpp
+++ b/media/libaudioprocessing/AudioResamplerSinc.cpp
@@ -19,6 +19,7 @@
 
 #define __STDC_CONSTANT_MACROS
 #include <malloc.h>
+#include <pthread.h>
 #include <string.h>
 #include <stdlib.h>
 #include <dlfcn.h>
diff --git a/media/libeffects/Android.bp b/media/libeffects/Android.bp
deleted file mode 100644
index 0dd3f17..0000000
--- a/media/libeffects/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["factory", "config"]
diff --git a/media/libeffects/downmix/Android.bp b/media/libeffects/downmix/Android.bp
new file mode 100644
index 0000000..227f2a1
--- /dev/null
+++ b/media/libeffects/downmix/Android.bp
@@ -0,0 +1,27 @@
+// Multichannel downmix effect library
+cc_library_shared {
+    name: "libdownmix",
+
+    vendor: true,
+    srcs: ["EffectDownmix.c"],
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+
+    relative_install_path: "soundfx",
+
+    cflags: [
+        //"-DBUILD_FLOAT",
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Werror",
+    ],
+
+    header_libs: [
+        "libaudioeffects",
+        "libhardware_headers",
+    ],
+    static_libs: ["libaudioutils" ],
+}
diff --git a/media/libeffects/downmix/Android.mk b/media/libeffects/downmix/Android.mk
deleted file mode 100644
index a5fbf14..0000000
--- a/media/libeffects/downmix/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Multichannel downmix effect library
-include $(CLEAR_VARS)
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_SRC_FILES:= \
-	EffectDownmix.c
-
-LOCAL_SHARED_LIBRARIES := \
-	libcutils liblog
-
-LOCAL_MODULE:= libdownmix
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_RELATIVE_PATH := soundfx
-
-LOCAL_C_INCLUDES := \
-	$(call include-path-for, audio-effects) \
-	$(call include-path-for, audio-utils)
-
-#-DBUILD_FLOAT
-LOCAL_CFLAGS += -fvisibility=hidden
-LOCAL_CFLAGS += -Wall -Werror
-
-LOCAL_HEADER_LIBRARIES += libhardware_headers
-include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/dynamicsproc/Android.bp b/media/libeffects/dynamicsproc/Android.bp
new file mode 100644
index 0000000..eafc483
--- /dev/null
+++ b/media/libeffects/dynamicsproc/Android.bp
@@ -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.
+
+// DynamicsProcessing library
+cc_library_shared {
+    name: "libdynproc",
+
+    vendor: true,
+
+    srcs: [
+        "EffectDynamicsProcessing.cpp",
+        "dsp/DPBase.cpp",
+        "dsp/DPFrequency.cpp",
+    ],
+
+    cflags: [
+        "-O2",
+        "-fvisibility=hidden",
+
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+
+    relative_install_path: "soundfx",
+
+    header_libs: [
+        "libaudioeffects",
+        "libeigen",
+    ],
+}
diff --git a/media/libeffects/dynamicsproc/Android.mk b/media/libeffects/dynamicsproc/Android.mk
deleted file mode 100644
index 7be0c49..0000000
--- a/media/libeffects/dynamicsproc/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# DynamicsProcessing library
-include $(CLEAR_VARS)
-
-LOCAL_VENDOR_MODULE := true
-
-EIGEN_PATH := external/eigen
-LOCAL_C_INCLUDES += $(EIGEN_PATH)
-
-LOCAL_SRC_FILES:= \
-    EffectDynamicsProcessing.cpp \
-    dsp/DPBase.cpp \
-    dsp/DPFrequency.cpp
-
-LOCAL_CFLAGS+= -O2 -fvisibility=hidden
-LOCAL_CFLAGS += -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-    libcutils \
-    liblog \
-
-LOCAL_MODULE_RELATIVE_PATH := soundfx
-LOCAL_MODULE:= libdynproc
-
-LOCAL_HEADER_LIBRARIES := \
-    libaudioeffects
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/loudness/Android.bp b/media/libeffects/loudness/Android.bp
new file mode 100644
index 0000000..5a13af6
--- /dev/null
+++ b/media/libeffects/loudness/Android.bp
@@ -0,0 +1,27 @@
+// LoudnessEnhancer library
+cc_library_shared {
+    name: "libldnhncr",
+
+    vendor: true,
+    srcs: [
+        "EffectLoudnessEnhancer.cpp",
+        "dsp/core/dynamic_range_compression.cpp",
+    ],
+
+    cflags: [
+        "-O2",
+        "-fvisibility=hidden",
+
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+
+    relative_install_path: "soundfx",
+
+    header_libs: ["libaudioeffects"],
+}
diff --git a/media/libeffects/loudness/Android.mk b/media/libeffects/loudness/Android.mk
deleted file mode 100644
index 712cbd5..0000000
--- a/media/libeffects/loudness/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# LoudnessEnhancer library
-include $(CLEAR_VARS)
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_SRC_FILES:= \
-    EffectLoudnessEnhancer.cpp \
-    dsp/core/dynamic_range_compression.cpp
-
-LOCAL_CFLAGS+= -O2 -fvisibility=hidden
-LOCAL_CFLAGS += -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := \
-    libcutils \
-    liblog \
-
-LOCAL_MODULE_RELATIVE_PATH := soundfx
-LOCAL_MODULE:= libldnhncr
-
-LOCAL_HEADER_LIBRARIES := \
-    libaudioeffects
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/lvm/lib/Android.bp b/media/libeffects/lvm/lib/Android.bp
new file mode 100644
index 0000000..5c57c43
--- /dev/null
+++ b/media/libeffects/lvm/lib/Android.bp
@@ -0,0 +1,211 @@
+// Music bundle
+cc_library_static {
+    name: "libmusicbundle",
+
+    arch: {
+        arm: {
+            instruction_set: "arm",
+        },
+    },
+
+    vendor: true,
+    srcs: [
+        "StereoWidening/src/LVCS_BypassMix.c",
+        "StereoWidening/src/LVCS_Control.c",
+        "StereoWidening/src/LVCS_Equaliser.c",
+        "StereoWidening/src/LVCS_Init.c",
+        "StereoWidening/src/LVCS_Process.c",
+        "StereoWidening/src/LVCS_ReverbGenerator.c",
+        "StereoWidening/src/LVCS_StereoEnhancer.c",
+        "StereoWidening/src/LVCS_Tables.c",
+        "Bass/src/LVDBE_Control.c",
+        "Bass/src/LVDBE_Init.c",
+        "Bass/src/LVDBE_Process.c",
+        "Bass/src/LVDBE_Tables.c",
+        "Bundle/src/LVM_API_Specials.c",
+        "Bundle/src/LVM_Buffers.c",
+        "Bundle/src/LVM_Init.c",
+        "Bundle/src/LVM_Process.c",
+        "Bundle/src/LVM_Tables.c",
+        "Bundle/src/LVM_Control.c",
+        "SpectrumAnalyzer/src/LVPSA_Control.c",
+        "SpectrumAnalyzer/src/LVPSA_Init.c",
+        "SpectrumAnalyzer/src/LVPSA_Memory.c",
+        "SpectrumAnalyzer/src/LVPSA_Process.c",
+        "SpectrumAnalyzer/src/LVPSA_QPD_Init.c",
+        "SpectrumAnalyzer/src/LVPSA_QPD_Process.c",
+        "SpectrumAnalyzer/src/LVPSA_Tables.c",
+        "Eq/src/LVEQNB_CalcCoef.c",
+        "Eq/src/LVEQNB_Control.c",
+        "Eq/src/LVEQNB_Init.c",
+        "Eq/src/LVEQNB_Process.c",
+        "Eq/src/LVEQNB_Tables.c",
+        "Common/src/InstAlloc.c",
+        "Common/src/DC_2I_D16_TRC_WRA_01.c",
+        "Common/src/DC_2I_D16_TRC_WRA_01_Init.c",
+        "Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.c",
+        "Common/src/FO_2I_D16F32Css_LShx_TRC_WRA_01_Init.c",
+        "Common/src/FO_1I_D16F16C15_TRC_WRA_01.c",
+        "Common/src/FO_1I_D16F16Css_TRC_WRA_01_Init.c",
+        "Common/src/BP_1I_D16F32C30_TRC_WRA_01.c",
+        "Common/src/BP_1I_D16F16C14_TRC_WRA_01.c",
+        "Common/src/BP_1I_D32F32C30_TRC_WRA_02.c",
+        "Common/src/BP_1I_D16F16Css_TRC_WRA_01_Init.c",
+        "Common/src/BP_1I_D16F32Cll_TRC_WRA_01_Init.c",
+        "Common/src/BP_1I_D32F32Cll_TRC_WRA_02_Init.c",
+        "Common/src/BQ_2I_D32F32Cll_TRC_WRA_01_Init.c",
+        "Common/src/BQ_2I_D32F32C30_TRC_WRA_01.c",
+        "Common/src/BQ_2I_D16F32C15_TRC_WRA_01.c",
+        "Common/src/BQ_2I_D16F32C14_TRC_WRA_01.c",
+        "Common/src/BQ_2I_D16F32C13_TRC_WRA_01.c",
+        "Common/src/BQ_2I_D16F32Css_TRC_WRA_01_init.c",
+        "Common/src/BQ_2I_D16F16C15_TRC_WRA_01.c",
+        "Common/src/BQ_2I_D16F16C14_TRC_WRA_01.c",
+        "Common/src/BQ_2I_D16F16Css_TRC_WRA_01_Init.c",
+        "Common/src/BQ_1I_D16F16C15_TRC_WRA_01.c",
+        "Common/src/BQ_1I_D16F16Css_TRC_WRA_01_Init.c",
+        "Common/src/BQ_1I_D16F32C14_TRC_WRA_01.c",
+        "Common/src/BQ_1I_D16F32Css_TRC_WRA_01_init.c",
+        "Common/src/PK_2I_D32F32C30G11_TRC_WRA_01.c",
+        "Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.c",
+        "Common/src/PK_2I_D32F32CssGss_TRC_WRA_01_Init.c",
+        "Common/src/PK_2I_D32F32CllGss_TRC_WRA_01_Init.c",
+        "Common/src/Int16LShiftToInt32_16x32.c",
+        "Common/src/From2iToMono_16.c",
+        "Common/src/Copy_16.c",
+        "Common/src/MonoTo2I_16.c",
+        "Common/src/MonoTo2I_32.c",
+        "Common/src/LoadConst_16.c",
+        "Common/src/LoadConst_32.c",
+        "Common/src/dB_to_Lin32.c",
+        "Common/src/Shift_Sat_v16xv16.c",
+        "Common/src/Shift_Sat_v32xv32.c",
+        "Common/src/Abs_32.c",
+        "Common/src/Int32RShiftToInt16_Sat_32x16.c",
+        "Common/src/From2iToMono_32.c",
+        "Common/src/mult3s_16x16.c",
+        "Common/src/Mult3s_32x16.c",
+        "Common/src/NonLinComp_D16.c",
+        "Common/src/DelayMix_16x16.c",
+        "Common/src/MSTo2i_Sat_16x16.c",
+        "Common/src/From2iToMS_16x16.c",
+        "Common/src/Mac3s_Sat_16x16.c",
+        "Common/src/Mac3s_Sat_32x16.c",
+        "Common/src/Add2_Sat_16x16.c",
+        "Common/src/Add2_Sat_32x32.c",
+        "Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.c",
+        "Common/src/LVC_MixSoft_1St_D16C31_SAT.c",
+        "Common/src/LVC_Mixer_VarSlope_SetTimeConstant.c",
+        "Common/src/LVC_Mixer_SetTimeConstant.c",
+        "Common/src/LVC_Mixer_SetTarget.c",
+        "Common/src/LVC_Mixer_GetTarget.c",
+        "Common/src/LVC_Mixer_Init.c",
+        "Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.c",
+        "Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.c",
+        "Common/src/LVC_Core_MixInSoft_D16C31_SAT.c",
+        "Common/src/LVC_Mixer_GetCurrent.c",
+        "Common/src/LVC_MixSoft_2St_D16C31_SAT.c",
+        "Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.c",
+        "Common/src/LVC_Core_MixHard_2St_D16C31_SAT.c",
+        "Common/src/LVC_MixInSoft_D16C31_SAT.c",
+        "Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.c",
+        "Common/src/LVM_Timer.c",
+        "Common/src/LVM_Timer_Init.c",
+    ],
+
+    local_include_dirs: [
+        "Eq/lib",
+        "Eq/src",
+        "Bass/lib",
+        "Bass/src",
+        "Common/src",
+        "Bundle/src",
+        "SpectrumAnalyzer/lib",
+        "SpectrumAnalyzer/src",
+        "StereoWidening/src",
+        "StereoWidening/lib",
+    ],
+    export_include_dirs: [
+        "Common/lib",
+        "Bundle/lib",
+    ],
+
+    cflags: [
+        "-fvisibility=hidden",
+        "-DBUILD_FLOAT",
+        "-DHIGHER_FS",
+
+        "-Wall",
+        "-Werror",
+    ],
+
+}
+
+// Reverb library
+cc_library_static {
+    name: "libreverb",
+
+    arch: {
+        arm: {
+            instruction_set: "arm",
+        },
+    },
+
+    vendor: true,
+    srcs: [
+        "Reverb/src/LVREV_ApplyNewSettings.c",
+        "Reverb/src/LVREV_ClearAudioBuffers.c",
+        "Reverb/src/LVREV_GetControlParameters.c",
+        "Reverb/src/LVREV_GetInstanceHandle.c",
+        "Reverb/src/LVREV_GetMemoryTable.c",
+        "Reverb/src/LVREV_Process.c",
+        "Reverb/src/LVREV_SetControlParameters.c",
+        "Reverb/src/LVREV_Tables.c",
+        "Common/src/Abs_32.c",
+        "Common/src/InstAlloc.c",
+        "Common/src/LoadConst_16.c",
+        "Common/src/LoadConst_32.c",
+        "Common/src/From2iToMono_32.c",
+        "Common/src/Mult3s_32x16.c",
+        "Common/src/FO_1I_D32F32C31_TRC_WRA_01.c",
+        "Common/src/FO_1I_D32F32Cll_TRC_WRA_01_Init.c",
+        "Common/src/DelayAllPass_Sat_32x16To32.c",
+        "Common/src/Copy_16.c",
+        "Common/src/Mac3s_Sat_32x16.c",
+        "Common/src/DelayWrite_32.c",
+        "Common/src/Shift_Sat_v32xv32.c",
+        "Common/src/Add2_Sat_32x32.c",
+        "Common/src/JoinTo2i_32x32.c",
+        "Common/src/MonoTo2I_32.c",
+        "Common/src/LVM_FO_HPF.c",
+        "Common/src/LVM_FO_LPF.c",
+        "Common/src/LVM_Polynomial.c",
+        "Common/src/LVM_Power10.c",
+        "Common/src/LVM_GetOmega.c",
+        "Common/src/MixSoft_2St_D32C31_SAT.c",
+        "Common/src/MixSoft_1St_D32C31_WRA.c",
+        "Common/src/MixInSoft_D32C31_SAT.c",
+        "Common/src/LVM_Mixer_TimeConstant.c",
+        "Common/src/Core_MixHard_2St_D32C31_SAT.c",
+        "Common/src/Core_MixSoft_1St_D32C31_WRA.c",
+        "Common/src/Core_MixInSoft_D32C31_SAT.c",
+    ],
+
+    local_include_dirs: [
+        "Reverb/src",
+        "Common/src",
+    ],
+    export_include_dirs: [
+        "Reverb/lib",
+        "Common/lib",
+    ],
+
+    cflags: [
+        "-fvisibility=hidden",
+        "-DBUILD_FLOAT",
+        "-DHIGHER_FS",
+
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/media/libeffects/lvm/lib/Android.mk b/media/libeffects/lvm/lib/Android.mk
deleted file mode 100644
index 941eb3e..0000000
--- a/media/libeffects/lvm/lib/Android.mk
+++ /dev/null
@@ -1,191 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Music bundle
-
-include $(CLEAR_VARS)
-
-LOCAL_ARM_MODE := arm
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_SRC_FILES:= \
-    StereoWidening/src/LVCS_BypassMix.c \
-    StereoWidening/src/LVCS_Control.c \
-    StereoWidening/src/LVCS_Equaliser.c \
-    StereoWidening/src/LVCS_Init.c \
-    StereoWidening/src/LVCS_Process.c \
-    StereoWidening/src/LVCS_ReverbGenerator.c \
-    StereoWidening/src/LVCS_StereoEnhancer.c \
-    StereoWidening/src/LVCS_Tables.c \
-    Bass/src/LVDBE_Control.c \
-    Bass/src/LVDBE_Init.c \
-    Bass/src/LVDBE_Process.c \
-    Bass/src/LVDBE_Tables.c \
-    Bundle/src/LVM_API_Specials.c \
-    Bundle/src/LVM_Buffers.c \
-    Bundle/src/LVM_Init.c \
-    Bundle/src/LVM_Process.c \
-    Bundle/src/LVM_Tables.c \
-    Bundle/src/LVM_Control.c \
-    SpectrumAnalyzer/src/LVPSA_Control.c \
-    SpectrumAnalyzer/src/LVPSA_Init.c \
-    SpectrumAnalyzer/src/LVPSA_Memory.c \
-    SpectrumAnalyzer/src/LVPSA_Process.c \
-    SpectrumAnalyzer/src/LVPSA_QPD_Init.c \
-    SpectrumAnalyzer/src/LVPSA_QPD_Process.c \
-    SpectrumAnalyzer/src/LVPSA_Tables.c \
-    Eq/src/LVEQNB_CalcCoef.c \
-    Eq/src/LVEQNB_Control.c \
-    Eq/src/LVEQNB_Init.c \
-    Eq/src/LVEQNB_Process.c \
-    Eq/src/LVEQNB_Tables.c \
-    Common/src/InstAlloc.c \
-    Common/src/DC_2I_D16_TRC_WRA_01.c \
-    Common/src/DC_2I_D16_TRC_WRA_01_Init.c \
-    Common/src/FO_2I_D16F32C15_LShx_TRC_WRA_01.c \
-    Common/src/FO_2I_D16F32Css_LShx_TRC_WRA_01_Init.c \
-    Common/src/FO_1I_D16F16C15_TRC_WRA_01.c \
-    Common/src/FO_1I_D16F16Css_TRC_WRA_01_Init.c \
-    Common/src/BP_1I_D16F32C30_TRC_WRA_01.c \
-    Common/src/BP_1I_D16F16C14_TRC_WRA_01.c \
-    Common/src/BP_1I_D32F32C30_TRC_WRA_02.c \
-    Common/src/BP_1I_D16F16Css_TRC_WRA_01_Init.c \
-    Common/src/BP_1I_D16F32Cll_TRC_WRA_01_Init.c \
-    Common/src/BP_1I_D32F32Cll_TRC_WRA_02_Init.c \
-    Common/src/BQ_2I_D32F32Cll_TRC_WRA_01_Init.c \
-    Common/src/BQ_2I_D32F32C30_TRC_WRA_01.c \
-    Common/src/BQ_2I_D16F32C15_TRC_WRA_01.c \
-    Common/src/BQ_2I_D16F32C14_TRC_WRA_01.c \
-    Common/src/BQ_2I_D16F32C13_TRC_WRA_01.c \
-    Common/src/BQ_2I_D16F32Css_TRC_WRA_01_init.c \
-    Common/src/BQ_2I_D16F16C15_TRC_WRA_01.c \
-    Common/src/BQ_2I_D16F16C14_TRC_WRA_01.c \
-    Common/src/BQ_2I_D16F16Css_TRC_WRA_01_Init.c \
-    Common/src/BQ_1I_D16F16C15_TRC_WRA_01.c \
-    Common/src/BQ_1I_D16F16Css_TRC_WRA_01_Init.c \
-    Common/src/BQ_1I_D16F32C14_TRC_WRA_01.c \
-    Common/src/BQ_1I_D16F32Css_TRC_WRA_01_init.c \
-    Common/src/PK_2I_D32F32C30G11_TRC_WRA_01.c \
-    Common/src/PK_2I_D32F32C14G11_TRC_WRA_01.c \
-    Common/src/PK_2I_D32F32CssGss_TRC_WRA_01_Init.c \
-    Common/src/PK_2I_D32F32CllGss_TRC_WRA_01_Init.c \
-    Common/src/Int16LShiftToInt32_16x32.c \
-    Common/src/From2iToMono_16.c \
-    Common/src/Copy_16.c \
-    Common/src/MonoTo2I_16.c \
-    Common/src/MonoTo2I_32.c \
-    Common/src/LoadConst_16.c \
-    Common/src/LoadConst_32.c \
-    Common/src/dB_to_Lin32.c \
-    Common/src/Shift_Sat_v16xv16.c \
-    Common/src/Shift_Sat_v32xv32.c \
-    Common/src/Abs_32.c \
-    Common/src/Int32RShiftToInt16_Sat_32x16.c \
-    Common/src/From2iToMono_32.c \
-    Common/src/mult3s_16x16.c \
-    Common/src/Mult3s_32x16.c \
-    Common/src/NonLinComp_D16.c \
-    Common/src/DelayMix_16x16.c \
-    Common/src/MSTo2i_Sat_16x16.c \
-    Common/src/From2iToMS_16x16.c \
-    Common/src/Mac3s_Sat_16x16.c \
-    Common/src/Mac3s_Sat_32x16.c \
-    Common/src/Add2_Sat_16x16.c \
-    Common/src/Add2_Sat_32x32.c \
-    Common/src/LVC_MixSoft_1St_2i_D16C31_SAT.c \
-    Common/src/LVC_MixSoft_1St_D16C31_SAT.c \
-    Common/src/LVC_Mixer_VarSlope_SetTimeConstant.c \
-    Common/src/LVC_Mixer_SetTimeConstant.c \
-    Common/src/LVC_Mixer_SetTarget.c \
-    Common/src/LVC_Mixer_GetTarget.c \
-    Common/src/LVC_Mixer_Init.c \
-    Common/src/LVC_Core_MixHard_1St_2i_D16C31_SAT.c \
-    Common/src/LVC_Core_MixSoft_1St_2i_D16C31_WRA.c \
-    Common/src/LVC_Core_MixInSoft_D16C31_SAT.c \
-    Common/src/LVC_Mixer_GetCurrent.c \
-    Common/src/LVC_MixSoft_2St_D16C31_SAT.c \
-    Common/src/LVC_Core_MixSoft_1St_D16C31_WRA.c \
-    Common/src/LVC_Core_MixHard_2St_D16C31_SAT.c \
-    Common/src/LVC_MixInSoft_D16C31_SAT.c \
-    Common/src/AGC_MIX_VOL_2St1Mon_D32_WRA.c \
-    Common/src/LVM_Timer.c \
-    Common/src/LVM_Timer_Init.c
-
-LOCAL_MODULE:= libmusicbundle
-
-LOCAL_C_INCLUDES += \
-    $(LOCAL_PATH)/Eq/lib \
-    $(LOCAL_PATH)/Eq/src \
-    $(LOCAL_PATH)/Bass/lib \
-    $(LOCAL_PATH)/Bass/src \
-    $(LOCAL_PATH)/Common/lib \
-    $(LOCAL_PATH)/Common/src \
-    $(LOCAL_PATH)/Bundle/lib \
-    $(LOCAL_PATH)/Bundle/src \
-    $(LOCAL_PATH)/SpectrumAnalyzer/lib \
-    $(LOCAL_PATH)/SpectrumAnalyzer/src \
-    $(LOCAL_PATH)/StereoWidening/src \
-    $(LOCAL_PATH)/StereoWidening/lib
-
-LOCAL_CFLAGS += -fvisibility=hidden -DBUILD_FLOAT -DHIGHER_FS
-LOCAL_CFLAGS += -Wall -Werror
-
-include $(BUILD_STATIC_LIBRARY)
-
-
-
-# Reverb library
-include $(CLEAR_VARS)
-
-LOCAL_ARM_MODE := arm
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_SRC_FILES:= \
-    Reverb/src/LVREV_ApplyNewSettings.c \
-    Reverb/src/LVREV_ClearAudioBuffers.c \
-    Reverb/src/LVREV_GetControlParameters.c \
-    Reverb/src/LVREV_GetInstanceHandle.c \
-    Reverb/src/LVREV_GetMemoryTable.c \
-    Reverb/src/LVREV_Process.c \
-    Reverb/src/LVREV_SetControlParameters.c \
-    Reverb/src/LVREV_Tables.c \
-    Common/src/Abs_32.c \
-    Common/src/InstAlloc.c \
-    Common/src/LoadConst_16.c \
-    Common/src/LoadConst_32.c \
-    Common/src/From2iToMono_32.c \
-    Common/src/Mult3s_32x16.c \
-    Common/src/FO_1I_D32F32C31_TRC_WRA_01.c \
-    Common/src/FO_1I_D32F32Cll_TRC_WRA_01_Init.c \
-    Common/src/DelayAllPass_Sat_32x16To32.c \
-    Common/src/Copy_16.c \
-    Common/src/Mac3s_Sat_32x16.c \
-    Common/src/DelayWrite_32.c \
-    Common/src/Shift_Sat_v32xv32.c \
-    Common/src/Add2_Sat_32x32.c \
-    Common/src/JoinTo2i_32x32.c \
-    Common/src/MonoTo2I_32.c \
-    Common/src/LVM_FO_HPF.c \
-    Common/src/LVM_FO_LPF.c \
-    Common/src/LVM_Polynomial.c \
-    Common/src/LVM_Power10.c \
-    Common/src/LVM_GetOmega.c \
-    Common/src/MixSoft_2St_D32C31_SAT.c \
-    Common/src/MixSoft_1St_D32C31_WRA.c \
-    Common/src/MixInSoft_D32C31_SAT.c \
-    Common/src/LVM_Mixer_TimeConstant.c \
-    Common/src/Core_MixHard_2St_D32C31_SAT.c \
-    Common/src/Core_MixSoft_1St_D32C31_WRA.c \
-    Common/src/Core_MixInSoft_D32C31_SAT.c
-
-LOCAL_MODULE:= libreverb
-
-LOCAL_C_INCLUDES += \
-    $(LOCAL_PATH)/Reverb/lib \
-    $(LOCAL_PATH)/Reverb/src \
-    $(LOCAL_PATH)/Common/lib \
-    $(LOCAL_PATH)/Common/src
-
-LOCAL_CFLAGS += -fvisibility=hidden -DBUILD_FLOAT -DHIGHER_FS
-LOCAL_CFLAGS += -Wall -Werror
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libeffects/lvm/wrapper/Android.bp b/media/libeffects/lvm/wrapper/Android.bp
new file mode 100644
index 0000000..10fd970
--- /dev/null
+++ b/media/libeffects/lvm/wrapper/Android.bp
@@ -0,0 +1,88 @@
+// The wrapper -DBUILD_FLOAT needs to match
+// the lvm library -DBUILD_FLOAT.
+
+// music bundle wrapper
+cc_library_shared {
+    name: "libbundlewrapper",
+
+    arch: {
+        arm: {
+            instruction_set: "arm",
+        },
+    },
+
+    vendor: true,
+    srcs: ["Bundle/EffectBundle.cpp"],
+
+    cflags: [
+        "-fvisibility=hidden",
+        "-DBUILD_FLOAT",
+        "-DHIGHER_FS",
+
+        "-Wall",
+        "-Werror",
+    ],
+
+    relative_install_path: "soundfx",
+
+    static_libs: ["libmusicbundle"],
+
+    shared_libs: [
+        "libaudioutils",
+        "libcutils",
+        "libdl",
+        "liblog",
+    ],
+
+    local_include_dirs: ["Bundle"],
+
+    header_libs: [
+        "libhardware_headers",
+        "libaudioeffects",
+    ],
+}
+
+// reverb wrapper
+cc_library_shared {
+    name: "libreverbwrapper",
+
+    arch: {
+        arm: {
+            instruction_set: "arm",
+        },
+    },
+
+    vendor: true,
+    srcs: ["Reverb/EffectReverb.cpp"],
+
+    cflags: [
+        "-fvisibility=hidden",
+        "-DBUILD_FLOAT",
+        "-DHIGHER_FS",
+
+        "-Wall",
+        "-Werror",
+    ],
+
+    relative_install_path: "soundfx",
+
+    static_libs: ["libreverb"],
+
+    shared_libs: [
+        "libaudioutils",
+        "libcutils",
+        "libdl",
+        "liblog",
+    ],
+
+    local_include_dirs: ["Reverb"],
+
+    header_libs: [
+        "libhardware_headers",
+        "libaudioeffects",
+    ],
+
+    sanitize: {
+        integer_overflow: true,
+    },
+}
diff --git a/media/libeffects/lvm/wrapper/Android.mk b/media/libeffects/lvm/wrapper/Android.mk
deleted file mode 100644
index 341dbc2..0000000
--- a/media/libeffects/lvm/wrapper/Android.mk
+++ /dev/null
@@ -1,77 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# The wrapper -DBUILD_FLOAT needs to match
-# the lvm library -DBUILD_FLOAT.
-
-# music bundle wrapper
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_ARM_MODE := arm
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_SRC_FILES:= \
-	Bundle/EffectBundle.cpp
-
-LOCAL_CFLAGS += -fvisibility=hidden -DBUILD_FLOAT -DHIGHER_FS
-LOCAL_CFLAGS += -Wall -Werror
-
-LOCAL_MODULE:= libbundlewrapper
-
-LOCAL_MODULE_RELATIVE_PATH := soundfx
-
-LOCAL_STATIC_LIBRARIES += libmusicbundle
-
-LOCAL_SHARED_LIBRARIES := \
-     libaudioutils \
-     libcutils \
-     libdl \
-     liblog \
-
-LOCAL_C_INCLUDES += \
-	$(LOCAL_PATH)/Bundle \
-	$(LOCAL_PATH)/../lib/Common/lib/ \
-	$(LOCAL_PATH)/../lib/Bundle/lib/ \
-	$(call include-path-for, audio-effects) \
-	$(call include-path-for, audio-utils) \
-
-LOCAL_HEADER_LIBRARIES += libhardware_headers
-include $(BUILD_SHARED_LIBRARY)
-
-
-# reverb wrapper
-include $(CLEAR_VARS)
-
-LOCAL_ARM_MODE := arm
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_SRC_FILES:= \
-    Reverb/EffectReverb.cpp
-
-LOCAL_CFLAGS += -fvisibility=hidden -DBUILD_FLOAT -DHIGHER_FS
-LOCAL_CFLAGS += -Wall -Werror
-
-LOCAL_MODULE:= libreverbwrapper
-
-LOCAL_MODULE_RELATIVE_PATH := soundfx
-
-LOCAL_STATIC_LIBRARIES += libreverb
-
-LOCAL_SHARED_LIBRARIES := \
-     libaudioutils \
-     libcutils \
-     libdl \
-     liblog \
-
-LOCAL_C_INCLUDES += \
-    $(LOCAL_PATH)/Reverb \
-    $(LOCAL_PATH)/../lib/Common/lib/ \
-    $(LOCAL_PATH)/../lib/Reverb/lib/ \
-    $(call include-path-for, audio-effects) \
-    $(call include-path-for, audio-utils) \
-
-LOCAL_HEADER_LIBRARIES += libhardware_headers
-
-LOCAL_SANITIZE := integer_overflow
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/preprocessing/Android.bp b/media/libeffects/preprocessing/Android.bp
new file mode 100644
index 0000000..c87635f
--- /dev/null
+++ b/media/libeffects/preprocessing/Android.bp
@@ -0,0 +1,35 @@
+// audio preprocessing wrapper
+cc_library_shared {
+    name: "libaudiopreprocessing",
+
+    vendor: true,
+
+    relative_install_path: "soundfx",
+
+    srcs: ["PreProcessing.cpp"],
+
+    include_dirs: [
+        "external/webrtc",
+        "external/webrtc/webrtc/modules/include",
+        "external/webrtc/webrtc/modules/audio_processing/include",
+    ],
+
+    shared_libs: [
+        "libwebrtc_audio_preprocessing",
+        "libspeexresampler",
+        "libutils",
+        "liblog",
+    ],
+
+    cflags: [
+        "-DWEBRTC_POSIX",
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Werror",
+    ],
+
+    header_libs: [
+        "libaudioeffects",
+        "libhardware_headers",
+    ],
+}
diff --git a/media/libeffects/preprocessing/Android.mk b/media/libeffects/preprocessing/Android.mk
deleted file mode 100644
index 358da8b..0000000
--- a/media/libeffects/preprocessing/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# audio preprocessing wrapper
-include $(CLEAR_VARS)
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE:= libaudiopreprocessing
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_RELATIVE_PATH := soundfx
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_SRC_FILES:= \
-    PreProcessing.cpp
-
-LOCAL_C_INCLUDES += \
-    external/webrtc \
-    external/webrtc/webrtc/modules/include \
-    external/webrtc/webrtc/modules/audio_processing/include \
-    $(call include-path-for, audio-effects)
-
-LOCAL_SHARED_LIBRARIES := \
-    libwebrtc_audio_preprocessing \
-    libspeexresampler \
-    libutils \
-    liblog
-
-LOCAL_SHARED_LIBRARIES += libdl
-
-LOCAL_CFLAGS += \
-    -DWEBRTC_POSIX
-
-LOCAL_CFLAGS += -fvisibility=hidden
-LOCAL_CFLAGS += -Wall -Werror
-
-LOCAL_HEADER_LIBRARIES += libhardware_headers
-include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/proxy/Android.bp b/media/libeffects/proxy/Android.bp
new file mode 100644
index 0000000..c6abb9e
--- /dev/null
+++ b/media/libeffects/proxy/Android.bp
@@ -0,0 +1,38 @@
+// Copyright 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libeffectproxy",
+    relative_install_path: "soundfx",
+
+    vendor: true,
+    srcs: ["EffectProxy.cpp"],
+
+    cflags: [
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Werror",
+    ],
+
+    include_dirs: ["frameworks/av/media/libeffects/factory"],
+
+    header_libs: ["libaudioeffects"],
+    shared_libs: [
+        "liblog",
+        "libcutils",
+        "libutils",
+        "libdl",
+        "libeffects",
+    ],
+}
diff --git a/media/libeffects/proxy/Android.mk b/media/libeffects/proxy/Android.mk
deleted file mode 100644
index c4de30d..0000000
--- a/media/libeffects/proxy/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_MODULE:= libeffectproxy
-LOCAL_MODULE_RELATIVE_PATH := soundfx
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_VENDOR_MODULE := true
-LOCAL_SRC_FILES := \
-        EffectProxy.cpp
-
-LOCAL_CFLAGS+= -fvisibility=hidden
-LOCAL_CFLAGS += -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libdl libeffects
-
-LOCAL_C_INCLUDES := \
-        system/media/audio_effects/include \
-        frameworks/av/media/libeffects/factory
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index f267f76..3302982 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -23,6 +23,8 @@
 #include <android/hardware/drm/1.0/IDrmPluginListener.h>
 #include <android/hardware/drm/1.1/IDrmFactory.h>
 #include <android/hardware/drm/1.1/IDrmPlugin.h>
+#include <android/hardware/drm/1.2/IDrmFactory.h>
+#include <android/hardware/drm/1.2/IDrmPlugin.h>
 
 #include <media/MediaAnalyticsItem.h>
 #include <mediadrm/DrmMetrics.h>
@@ -36,6 +38,7 @@
 using drm::V1_0::IDrmPlugin;
 using drm::V1_0::IDrmPluginListener;
 using drm::V1_0::KeyStatus;
+using drm::V1_2::OfflineLicenseState;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
@@ -113,6 +116,11 @@
     virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
             DrmPlugin::SecurityLevel *level) const;
 
+    virtual status_t getOfflineLicenseKeySetIds(List<Vector<uint8_t>> &keySetIds) const;
+    virtual status_t removeOfflineLicense(Vector<uint8_t> const &keySetId);
+    virtual status_t getOfflineLicenseState(Vector<uint8_t> const &keySetId,
+            DrmPlugin::OfflineLicenseState *licenseState) const;
+
     virtual status_t getPropertyString(String8 const &name, String8 &value ) const;
     virtual status_t getPropertyByteArray(String8 const &name,
                                           Vector<uint8_t> &value ) const;
@@ -182,6 +190,7 @@
     const Vector<sp<IDrmFactory>> mFactories;
     sp<IDrmPlugin> mPlugin;
     sp<drm::V1_1::IDrmPlugin> mPluginV1_1;
+    sp<drm::V1_2::IDrmPlugin> mPluginV1_2;
     String8 mAppPackageName;
 
     // Mutable to allow modification within GetPropertyByteArray.
diff --git a/media/libmedia/include/media/IDrm.h b/media/libmedia/include/media/IDrm.h
index 8e9eb3a..49166c6 100644
--- a/media/libmedia/include/media/IDrm.h
+++ b/media/libmedia/include/media/IDrm.h
@@ -75,8 +75,8 @@
                                               Vector<uint8_t> &certificate,
                                               Vector<uint8_t> &wrappedKey) = 0;
 
-    virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops) = 0;
-    virtual status_t getSecureStopIds(List<Vector<uint8_t> > &secureStopIds) = 0;
+    virtual status_t getSecureStops(List<Vector<uint8_t>> &secureStops) = 0;
+    virtual status_t getSecureStopIds(List<Vector<uint8_t>> &secureStopIds) = 0;
     virtual status_t getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop) = 0;
 
     virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) = 0;
@@ -91,6 +91,11 @@
     virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
             DrmPlugin::SecurityLevel *level) const = 0;
 
+    virtual status_t getOfflineLicenseKeySetIds(List<Vector<uint8_t>> &keySetIds) const = 0;
+    virtual status_t removeOfflineLicense(Vector<uint8_t> const &keySetId) = 0;
+    virtual status_t getOfflineLicenseState(Vector<uint8_t> const &keySetId,
+            DrmPlugin::OfflineLicenseState *licenseState) const = 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;
diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
index eea7cfc..72efcdf 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
@@ -179,6 +179,7 @@
     kKeyPssh              = 'pssh',  // raw data
     kKeyCASystemID        = 'caid',  // int32_t
     kKeyCASessionID       = 'seid',  // raw data
+    kKeyCAPrivateData     = 'cadc',  // raw data
 
     kKeyEncryptedByteBlock = 'cblk',  // uint8_t
     kKeySkipByteBlock     = 'sblk',  // uint8_t
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 135c9b6..d3de01e 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -478,7 +478,7 @@
 // caller responsible for the returned string
 bool MediaAnalyticsItem::getCString(MediaAnalyticsItem::Attr name, char **value) {
     Prop *prop = findProp(name);
-    if (prop == NULL || prop->mType != kTypeDouble) {
+    if (prop == NULL || prop->mType != kTypeCString) {
         return false;
     }
     if (value != NULL) {
diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp
index 5c0c6ea..3d6879e 100644
--- a/media/libmediaplayer2/JAudioTrack.cpp
+++ b/media/libmediaplayer2/JAudioTrack.cpp
@@ -34,8 +34,8 @@
         callback_t cbf,                               // Offload
         void* user,                                   // Offload
         size_t frameCount,                            // bufferSizeInBytes
-        audio_session_t sessionId,                    // AudioTrack
-        const audio_attributes_t* pAttributes,        // AudioAttributes
+        int32_t sessionId,                    // AudioTrack
+        const jobject attributes,                     // AudioAttributes
         float maxRequiredSpeed) {                     // bufferSizeInBytes
 
     JNIEnv *env = JavaVMHelper::getJNIEnv();
@@ -65,13 +65,17 @@
     jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
     jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
 
-    jobject jAudioAttributesObj = JAudioAttributes::createAudioAttributesObj(env, pAttributes);
-    mAudioAttributesObj = reinterpret_cast<jobject>(env->NewGlobalRef(jAudioAttributesObj));
-    env->DeleteLocalRef(jAudioAttributesObj);
+    if (attributes != NULL) {
+        mAudioAttributesObj = new JObjectHolder(attributes);
+    } else {
+        mAudioAttributesObj = new JObjectHolder(
+                JAudioAttributes::createAudioAttributesObj(env, NULL));
+    }
 
     jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes",
             "(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;");
-    jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioAttributes, mAudioAttributesObj);
+    jBuilderObj = env->CallObjectMethod(jBuilderObj,
+            jSetAudioAttributes, mAudioAttributesObj->getJObject());
 
     jmethodID jSetAudioFormat = env->GetMethodID(jBuilderCls, "setAudioFormat",
             "(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;");
@@ -96,6 +100,7 @@
         jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetSessionId, sessionId);
     }
 
+    mFlags = AUDIO_OUTPUT_FLAG_NONE;
     if (cbf != NULL) {
         jmethodID jSetOffloadedPlayback = env->GetMethodID(jBuilderCls, "setOffloadedPlayback",
                 "(Z)Landroid/media/AudioTrack$Builder;");
@@ -125,7 +130,6 @@
     JNIEnv *env = JavaVMHelper::getJNIEnv();
     env->DeleteGlobalRef(mAudioTrackCls);
     env->DeleteGlobalRef(mAudioTrackObj);
-    env->DeleteGlobalRef(mAudioAttributesObj);
 }
 
 size_t JAudioTrack::frameCount() {
@@ -486,44 +490,38 @@
     return NO_ERROR;
 }
 
-audio_port_handle_t JAudioTrack::getRoutedDeviceId() {
+jobject JAudioTrack::getRoutedDevice() {
     JNIEnv *env = JavaVMHelper::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("android/media/AudioDeviceInfo");
-    jmethodID jGetId = env->GetMethodID(jAudioDeviceInfoCls, "getId", "()I");
-    jint routedDeviceId = env->CallIntMethod(jAudioDeviceInfoObj, jGetId);
-    return routedDeviceId;
+    return env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice);
 }
 
-audio_session_t JAudioTrack::getAudioSessionId() {
+int32_t JAudioTrack::getAudioSessionId() {
     JNIEnv *env = JavaVMHelper::getJNIEnv();
     jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I");
     jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId);
-    return (audio_session_t) sessionId;
+    return sessionId;
 }
 
-status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
+status_t JAudioTrack::setPreferredDevice(jobject device) {
     JNIEnv *env = JavaVMHelper::getJNIEnv();
-    jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl");
-    jmethodID jSetAudioOutputDeviceById = env->GetStaticMethodID(
-            jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z");
-    jboolean result = env->CallStaticBooleanMethod(
-            jMP2ImplCls, jSetAudioOutputDeviceById, mAudioTrackObj, deviceId);
+    jmethodID jSetPreferredDeviceId = env->GetMethodID(mAudioTrackCls, "setPreferredDevice",
+            "(Landroid/media/AudioDeviceInfo;)Z");
+    jboolean result = env->CallBooleanMethod(mAudioTrackObj, jSetPreferredDeviceId, device);
     return result == true ? NO_ERROR : BAD_VALUE;
 }
 
 audio_stream_type_t JAudioTrack::getAudioStreamType() {
+    if (mAudioAttributesObj == NULL) {
+        return AUDIO_STREAM_DEFAULT;
+    }
     JNIEnv *env = JavaVMHelper::getJNIEnv();
     jclass jAudioAttributesCls = env->FindClass("android/media/AudioAttributes");
     jmethodID jGetVolumeControlStream = env->GetMethodID(jAudioAttributesCls,
-                                        "getVolumeControlStream", "()I");
-    int javaAudioStreamType = env->CallIntMethod(mAudioAttributesObj, jGetVolumeControlStream);
+            "getVolumeControlStream", "()I");
+    int javaAudioStreamType = env->CallIntMethod(
+            mAudioAttributesObj->getJObject(), jGetVolumeControlStream);
     return (audio_stream_type_t)javaAudioStreamType;
 }
 
diff --git a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
index 9218fce..98a3e75 100644
--- a/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
+++ b/media/libmediaplayer2/MediaPlayer2AudioOutput.cpp
@@ -59,8 +59,8 @@
     return NO_ERROR;
 }
 
-MediaPlayer2AudioOutput::MediaPlayer2AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
-        const audio_attributes_t* attr, std::vector<jobject>& routingDelegatesBackup)
+MediaPlayer2AudioOutput::MediaPlayer2AudioOutput(int32_t sessionId, uid_t uid, int pid,
+        const jobject attributes)
     : mCallback(nullptr),
       mCallbackCookie(nullptr),
       mCallbackData(nullptr),
@@ -74,26 +74,15 @@
       mPid(pid),
       mSendLevel(0.0),
       mAuxEffectId(0),
-      mFlags(AUDIO_OUTPUT_FLAG_NONE),
-      mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
-      mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE) {
+      mFlags(AUDIO_OUTPUT_FLAG_NONE) {
     ALOGV("MediaPlayer2AudioOutput(%d)", sessionId);
-    if (attr != nullptr) {
-        mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
-        if (mAttributes != nullptr) {
-            memcpy(mAttributes, attr, sizeof(audio_attributes_t));
-        }
-    } else {
-        mAttributes = nullptr;
+
+    if (attributes != nullptr) {
+        mAttributes = new JObjectHolder(attributes);
     }
 
     setMinBufferCount();
     mRoutingDelegates.clear();
-    for (auto routingDelegate : routingDelegatesBackup) {
-        mRoutingDelegates.push_back(std::pair<jobject, jobject>(
-                JAudioTrack::getListener(routingDelegate), routingDelegate));
-    }
-    routingDelegatesBackup.clear();
 }
 
 MediaPlayer2AudioOutput::~MediaPlayer2AudioOutput() {
@@ -101,7 +90,6 @@
         JAudioTrack::removeGlobalRef(routingDelegate.second);
     }
     close();
-    free(mAttributes);
     delete mCallbackData;
 }
 
@@ -253,17 +241,9 @@
     return status;
 }
 
-void MediaPlayer2AudioOutput::setAudioAttributes(const audio_attributes_t * attributes) {
+void MediaPlayer2AudioOutput::setAudioAttributes(const jobject attributes) {
     Mutex::Autolock lock(mLock);
-    if (attributes == nullptr) {
-        free(mAttributes);
-        mAttributes = nullptr;
-    } else {
-        if (mAttributes == nullptr) {
-            mAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
-        }
-        memcpy(mAttributes, attributes, sizeof(audio_attributes_t));
-    }
+    mAttributes = (attributes == nullptr) ? nullptr : new JObjectHolder(attributes);
 }
 
 audio_stream_type_t MediaPlayer2AudioOutput::getAudioStreamType() const {
@@ -327,7 +307,7 @@
                  newcbd,
                  frameCount,
                  mSessionId,
-                 mAttributes,
+                 mAttributes != nullptr ? mAttributes->getJObject() : nullptr,
                  1.0f);  // default value for maxRequiredSpeed
     } else {
         // TODO: Due to buffer memory concerns, we use a max target playback speed
@@ -346,7 +326,7 @@
                  nullptr,
                  frameCount,
                  mSessionId,
-                 mAttributes,
+                 mAttributes != nullptr ? mAttributes->getJObject() : nullptr,
                  targetSpeed);
     }
 
@@ -388,7 +368,9 @@
             res = mJAudioTrack->attachAuxEffect(mAuxEffectId);
         }
     }
-    mJAudioTrack->setOutputDevice(mSelectedDeviceId);
+    if (mPreferredDevice != nullptr) {
+        mJAudioTrack->setPreferredDevice(mPreferredDevice->getJObject());
+    }
 
     mJAudioTrack->registerRoutingDelegates(mRoutingDelegates);
 
@@ -518,24 +500,26 @@
     return NO_ERROR;
 }
 
-status_t MediaPlayer2AudioOutput::setOutputDevice(audio_port_handle_t deviceId) {
-    ALOGV("setOutputDevice(%d)", deviceId);
+status_t MediaPlayer2AudioOutput::setPreferredDevice(jobject device) {
+    ALOGV("setPreferredDevice");
     Mutex::Autolock lock(mLock);
-    mSelectedDeviceId = deviceId;
+    status_t ret = NO_ERROR;
     if (mJAudioTrack != nullptr) {
-        return mJAudioTrack->setOutputDevice(deviceId);
+        ret = mJAudioTrack->setPreferredDevice(device);
     }
-    return NO_ERROR;
+    if (ret == NO_ERROR) {
+        mPreferredDevice = new JObjectHolder(device);
+    }
+    return ret;
 }
 
-status_t MediaPlayer2AudioOutput::getRoutedDeviceId(audio_port_handle_t* deviceId) {
-    ALOGV("getRoutedDeviceId");
+jobject MediaPlayer2AudioOutput::getRoutedDevice() {
+    ALOGV("getRoutedDevice");
     Mutex::Autolock lock(mLock);
     if (mJAudioTrack != nullptr) {
-        mRoutedDeviceId = mJAudioTrack->getRoutedDeviceId();
+        return mJAudioTrack->getRoutedDevice();
     }
-    *deviceId = mRoutedDeviceId;
-    return NO_ERROR;
+    return nullptr;
 }
 
 status_t MediaPlayer2AudioOutput::addAudioDeviceCallback(jobject jRoutingDelegate) {
@@ -567,15 +551,6 @@
     return NO_ERROR;
 }
 
-void MediaPlayer2AudioOutput::copyAudioDeviceCallback(
-        std::vector<jobject>& routingDelegateTarget) {
-    ALOGV("copyAudioDeviceCallback");
-    for (std::vector<std::pair<jobject, jobject>>::iterator it = mRoutingDelegates.begin();
-            it != mRoutingDelegates.end(); it++) {
-        routingDelegateTarget.push_back(it->second);
-    }
-}
-
 // static
 void MediaPlayer2AudioOutput::CallbackWrapper(
         int event, void *cookie, void *info) {
@@ -648,11 +623,16 @@
     data->unlock();
 }
 
-audio_session_t MediaPlayer2AudioOutput::getSessionId() const {
+int32_t MediaPlayer2AudioOutput::getSessionId() const {
     Mutex::Autolock lock(mLock);
     return mSessionId;
 }
 
+void MediaPlayer2AudioOutput::setSessionId(const int32_t sessionId) {
+    Mutex::Autolock lock(mLock);
+    mSessionId = sessionId;
+}
+
 uint32_t MediaPlayer2AudioOutput::getSampleRate() const {
     Mutex::Autolock lock(mLock);
     if (mJAudioTrack == 0) {
diff --git a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
index 629968f..8ea70ef 100644
--- a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
+++ b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
@@ -25,7 +25,7 @@
 #include <media/VolumeShaper.h>
 #include <system/audio.h>
 #include <utils/Errors.h>
-
+#include <mediaplayer2/JObjectHolder.h>
 #include <media/AudioTimestamp.h>   // It has dependency on audio.h/Errors.h, but doesn't
                                     // include them in it. Therefore it is included here at last.
 
@@ -114,8 +114,8 @@
                 callback_t cbf,
                 void* user,
                 size_t frameCount = 0,
-                audio_session_t sessionId  = AUDIO_SESSION_ALLOCATE,
-                const audio_attributes_t* pAttributes = NULL,
+                int32_t sessionId  = AUDIO_SESSION_ALLOCATE,
+                const jobject pAttributes = NULL,
                 float maxRequiredSpeed = 1.0f);
 
     /*
@@ -335,25 +335,24 @@
      */
     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.
+    /* Returns the AudioDeviceInfo used by the output to which this AudioTrack is
+     * attached.
      */
-    audio_port_handle_t getRoutedDeviceId();
+    jobject getRoutedDevice();
 
     /* Returns the ID of the audio session this AudioTrack belongs to. */
-    audio_session_t getAudioSessionId();
+    int32_t getAudioSessionId();
 
-    /* Selects the audio device to use for output of this AudioTrack. A value of
-     * AUDIO_PORT_HANDLE_NONE indicates default routing.
+    /* Sets the preferred audio device to use for output of this AudioTrack.
      *
      * Parameters:
-     *  The device ID of the selected device (as returned by the AudioDevicesManager API).
+     * Device: an AudioDeviceInfo object.
      *
      * Returned value:
      *  - NO_ERROR: successful operation
-     *  - BAD_VALUE: failed to find the valid output device with given device Id.
+     *  - BAD_VALUE: failed to set the device
      */
-    status_t setOutputDevice(audio_port_handle_t deviceId);
+    status_t setPreferredDevice(jobject device);
 
     // TODO: Add AUDIO_OUTPUT_FLAG_DIRECT when it is possible to check.
     // TODO: Add AUDIO_FLAG_HW_AV_SYNC when it is possible to check.
@@ -447,7 +446,7 @@
 
     jclass mAudioTrackCls;
     jobject mAudioTrackObj;
-    jobject mAudioAttributesObj;
+    sp<JObjectHolder> mAudioAttributesObj;
 
     /* Creates a Java VolumeShaper.Configuration object from VolumeShaper::Configuration */
     jobject createVolumeShaperConfigurationObj(
diff --git a/media/libmediaplayer2/include/mediaplayer2/JObjectHolder.h b/media/libmediaplayer2/include/mediaplayer2/JObjectHolder.h
new file mode 100644
index 0000000..93d8b40
--- /dev/null
+++ b/media/libmediaplayer2/include/mediaplayer2/JObjectHolder.h
@@ -0,0 +1,47 @@
+/*
+ * 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 JOBJECT_HOLDER_H_
+
+#define JOBJECT_HOLDER_H_
+
+#include "jni.h"
+#include <mediaplayer2/JavaVMHelper.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+// Helper class for managing global reference of jobject.
+struct JObjectHolder : public RefBase {
+    JObjectHolder(jobject obj) {
+        JNIEnv *env = JavaVMHelper::getJNIEnv();
+        mJObject = reinterpret_cast<jobject>(env->NewGlobalRef(obj));
+    }
+
+    virtual ~JObjectHolder() {
+        JNIEnv *env = JavaVMHelper::getJNIEnv();
+        env->DeleteGlobalRef(mJObject);
+    }
+
+    jobject getJObject() { return mJObject; }
+
+private:
+    jobject mJObject;
+};
+
+}  //" android
+
+#endif  // JOBJECT_HOLDER_H_
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
index 9fca641..bda4f61 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2AudioOutput.h
@@ -20,12 +20,15 @@
 
 #include <mediaplayer2/MediaPlayer2Interface.h>
 #include <mediaplayer2/JAudioTrack.h>
+#include <mediaplayer2/JObjectHolder.h>
 
 #include <vector>
 #include <utility>
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
+#include "jni.h"
+
 namespace android {
 
 class AudioTrack;
@@ -35,11 +38,10 @@
     class CallbackData;
 
 public:
-    MediaPlayer2AudioOutput(audio_session_t sessionId,
+    MediaPlayer2AudioOutput(int32_t sessionId,
                             uid_t uid,
                             int pid,
-                            const audio_attributes_t * attr,
-                            std::vector<jobject>& routingDelegatesBackup);
+                            const jobject attributes);
     virtual ~MediaPlayer2AudioOutput();
 
     virtual bool ready() const {
@@ -55,7 +57,8 @@
     virtual status_t getTimestamp(AudioTimestamp &ts) const;
     virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const;
     virtual status_t getFramesWritten(uint32_t *frameswritten) const;
-    virtual audio_session_t getSessionId() const;
+    virtual int32_t getSessionId() const;
+    virtual void setSessionId(const int32_t id);
     virtual uint32_t getSampleRate() const;
     virtual int64_t getBufferDurationInUs() const;
 
@@ -73,7 +76,7 @@
     virtual void flush();
     virtual void pause();
     virtual void close();
-    void setAudioAttributes(const audio_attributes_t * attributes);
+    void setAudioAttributes(const jobject attributes);
     virtual audio_stream_type_t getAudioStreamType() const;
 
     void setVolume(float volume);
@@ -92,11 +95,10 @@
         //return mNextOutput == NULL;
     }
     // AudioRouting
-    virtual status_t setOutputDevice(audio_port_handle_t deviceId);
-    virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
+    virtual status_t setPreferredDevice(jobject device);
+    virtual jobject getRoutedDevice();
     virtual status_t addAudioDeviceCallback(jobject routingDelegate);
     virtual status_t removeAudioDeviceCallback(jobject listener);
-    virtual void copyAudioDeviceCallback(std::vector<jobject>& routingDelegateTarget);
 
 private:
     static void setMinBufferCount();
@@ -109,20 +111,19 @@
     AudioCallback           mCallback;
     void *                  mCallbackCookie;
     CallbackData *          mCallbackData;
-    audio_attributes_t *    mAttributes;
+    sp<JObjectHolder>       mAttributes;
     float                   mVolume;
     AudioPlaybackRate       mPlaybackRate;
     uint32_t                mSampleRateHz; // sample rate of the content, as set in open()
     float                   mMsecsPerFrame;
     size_t                  mFrameSize;
-    audio_session_t         mSessionId;
+    int32_t                 mSessionId;
     uid_t                   mUid;
     int                     mPid;
     float                   mSendLevel;
     int                     mAuxEffectId;
     audio_output_flags_t    mFlags;
-    audio_port_handle_t     mSelectedDeviceId;
-    audio_port_handle_t     mRoutedDeviceId;
+    sp<JObjectHolder>       mPreferredDevice;
     mutable Mutex           mLock;
     std::vector<std::pair<jobject, jobject>> mRoutingDelegates; // <listener, routingDelegate>
 
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index 9a35ed9..4b19e38 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -34,6 +34,7 @@
 #include <media/stagefright/foundation/AHandler.h>
 #include <mediaplayer2/MediaPlayer2Types.h>
 
+#include "jni.h"
 #include "mediaplayer2.pb.h"
 
 using android::media::MediaPlayer2Proto::PlayerMessage;
@@ -93,7 +94,7 @@
         virtual status_t getTimestamp(AudioTimestamp &ts) const = 0;
         virtual int64_t getPlayedOutDurationUs(int64_t nowUs) const = 0;
         virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
-        virtual audio_session_t getSessionId() const = 0;
+        virtual int32_t getSessionId() const = 0;
         virtual audio_stream_type_t getAudioStreamType() const = 0;
         virtual uint32_t getSampleRate() const = 0;
         virtual int64_t getBufferDurationInUs() const = 0;
@@ -142,8 +143,8 @@
         }
 
         // AudioRouting
-        virtual status_t    setOutputDevice(audio_port_handle_t deviceId);
-        virtual status_t    getRoutedDeviceId(audio_port_handle_t* deviceId);
+        virtual status_t    setPreferredDevice(jobject device);
+        virtual jobject     getRoutedDevice();
         virtual status_t    addAudioDeviceCallback(jobject routingDelegate);
         virtual status_t    removeAudioDeviceCallback(jobject listener);
     };
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index a1908e6..4bc1a4a 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -24,8 +24,8 @@
 #include <media/mediaplayer_common.h>
 #include <mediaplayer2/MediaPlayer2Interface.h>
 #include <mediaplayer2/MediaPlayer2Types.h>
+#include <mediaplayer2/JObjectHolder.h>
 
-#include <vector>
 #include <jni.h>
 #include <utils/Errors.h>
 #include <utils/Mutex.h>
@@ -34,6 +34,8 @@
 #include <utils/Vector.h>
 #include <system/audio-base.h>
 
+#include "jni.h"
+
 namespace android {
 
 struct ANativeWindowWrapper;
@@ -53,7 +55,7 @@
 public:
     ~MediaPlayer2();
 
-    static sp<MediaPlayer2> Create();
+    static sp<MediaPlayer2> Create(int32_t sessionId);
     static status_t DumpAll(int fd, const Vector<String16>& args);
 
             void            disconnect();
@@ -94,39 +96,40 @@
             void            notify(int64_t srcId, int msg, int ext1, int ext2,
                                    const PlayerMessage *obj = NULL);
             status_t        invoke(const PlayerMessage &request, PlayerMessage *reply);
-            status_t        setAudioSessionId(audio_session_t sessionId);
-            audio_session_t getAudioSessionId();
+            status_t        setAudioSessionId(int32_t sessionId);
+            int32_t         getAudioSessionId();
             status_t        setAuxEffectSendLevel(float level);
             status_t        attachAuxEffect(int effectId);
-            status_t        setParameter(int key, const Parcel& request);
+            status_t        setAudioAttributes(const jobject attributes);
+            jobject         getAudioAttributes();
             status_t        getParameter(int key, Parcel* reply);
 
             // Modular DRM
             status_t        prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
             status_t        releaseDrm();
             // AudioRouting
-            status_t        setOutputDevice(audio_port_handle_t deviceId);
-            audio_port_handle_t getRoutedDeviceId();
+            status_t        setPreferredDevice(jobject device);
+            jobject         getRoutedDevice();
             status_t        addAudioDeviceCallback(jobject routingDelegate);
             status_t        removeAudioDeviceCallback(jobject listener);
 
             status_t        dump(int fd, const Vector<String16>& args);
 
 private:
-    MediaPlayer2();
+    MediaPlayer2(int32_t sessionId);
     bool init();
 
     // Disconnect from the currently connected ANativeWindow.
     void disconnectNativeWindow_l();
 
-    status_t setAudioAttributes_l(const Parcel &request);
+    status_t setAudioAttributes_l(const jobject attributes);
 
     void clear_l();
     status_t seekTo_l(int64_t msec, MediaPlayer2SeekMode mode);
     status_t prepareAsync_l();
     status_t getDuration_l(int64_t *msec);
     status_t reset_l();
-    status_t checkStateForKeySet_l(int key);
+    status_t checkState_l();
 
     pid_t                       mPid;
     uid_t                       mUid;
@@ -143,15 +146,13 @@
     int64_t                     mSeekPosition;
     MediaPlayer2SeekMode        mSeekMode;
     audio_stream_type_t         mStreamType;
-    Parcel*                     mAudioAttributesParcel;
     bool                        mLoop;
     float                       mVolume;
     int                         mVideoWidth;
     int                         mVideoHeight;
-    audio_session_t             mAudioSessionId;
-    audio_attributes_t *        mAudioAttributes;
+    int32_t                     mAudioSessionId;
+    sp<JObjectHolder>           mAudioAttributes;
     float                       mSendLevel;
-    std::vector<jobject>        mRoutingDelegates;
     sp<ANativeWindowWrapper>    mConnectedWindow;
 };
 
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index 4960694..921a5b7 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -53,57 +53,6 @@
 const int kDumpLockRetries = 50;
 const int kDumpLockSleepUs = 20000;
 
-// marshalling tag indicating flattened utf16 tags
-// keep in sync with frameworks/base/media/java/android/media/AudioAttributes.java
-const int32_t kAudioAttributesMarshallTagFlattenTags = 1;
-
-// Audio attributes format in a parcel:
-//
-//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |                       usage                                   |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |                       content_type                            |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |                       source                                  |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |                       flags                                   |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |                       kAudioAttributesMarshallTagFlattenTags  | // ignore tags if not found
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-// |                       flattened tags in UTF16                 |
-// |                         ...                                   |
-// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-//
-// @param p Parcel that contains audio attributes.
-// @param[out] attributes On exit points to an initialized audio_attributes_t structure
-// @param[out] status On exit contains the status code to be returned.
-void unmarshallAudioAttributes(const Parcel& parcel, audio_attributes_t *attributes) {
-    attributes->usage = (audio_usage_t) parcel.readInt32();
-    attributes->content_type = (audio_content_type_t) parcel.readInt32();
-    attributes->source = (audio_source_t) parcel.readInt32();
-    attributes->flags = (audio_flags_mask_t) parcel.readInt32();
-    const bool hasFlattenedTag = (parcel.readInt32() == kAudioAttributesMarshallTagFlattenTags);
-    if (hasFlattenedTag) {
-        // the tags are UTF16, convert to UTF8
-        String16 tags = parcel.readString16();
-        ssize_t realTagSize = utf16_to_utf8_length(tags.string(), tags.size());
-        if (realTagSize <= 0) {
-            strcpy(attributes->tags, "");
-        } else {
-            // copy the flattened string into the attributes as the destination for the conversion:
-            // copying array size -1, array for tags was calloc'd, no need to NULL-terminate it
-            size_t tagSize = realTagSize > AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 ?
-                    AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1 : realTagSize;
-            utf16_to_utf8(tags.string(), tagSize, attributes->tags,
-                    sizeof(attributes->tags) / sizeof(attributes->tags[0]));
-        }
-    } else {
-        ALOGE("unmarshallAudioAttributes() received unflattened tags, ignoring tag values");
-        strcpy(attributes->tags, "");
-    }
-}
-
 class proxyListener : public MediaPlayer2InterfaceListener {
 public:
     proxyListener(const wp<MediaPlayer2> &player)
@@ -269,8 +218,8 @@
 }  // anonymous namespace
 
 //static
-sp<MediaPlayer2> MediaPlayer2::Create() {
-    sp<MediaPlayer2> player = new MediaPlayer2();
+sp<MediaPlayer2> MediaPlayer2::Create(int32_t sessionId) {
+    sp<MediaPlayer2> player = new MediaPlayer2(sessionId);
 
     if (!player->init()) {
         return NULL;
@@ -287,13 +236,13 @@
     return dumpPlayers(fd, args);
 }
 
-MediaPlayer2::MediaPlayer2() {
+MediaPlayer2::MediaPlayer2(int32_t sessionId) {
     ALOGV("constructor");
     mSrcId = 0;
     mLockThreadId = 0;
     mListener = NULL;
     mStreamType = AUDIO_STREAM_MUSIC;
-    mAudioAttributesParcel = NULL;
+    mAudioAttributes = NULL;
     mCurrentPosition = -1;
     mCurrentSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC;
     mSeekPosition = -1;
@@ -302,29 +251,19 @@
     mLoop = false;
     mVolume = 1.0;
     mVideoWidth = mVideoHeight = 0;
-    mAudioSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
-    AudioSystem::acquireAudioSessionId(mAudioSessionId, -1);
     mSendLevel = 0;
 
     // TODO: get pid and uid from JAVA
     mPid = IPCThreadState::self()->getCallingPid();
     mUid = IPCThreadState::self()->getCallingUid();
 
-    mAudioAttributes = NULL;
+    mAudioOutput = new MediaPlayer2AudioOutput(sessionId, mUid, mPid, NULL /*attributes*/);
 }
 
 MediaPlayer2::~MediaPlayer2() {
     ALOGV("destructor");
-    if (mAudioAttributesParcel != NULL) {
-        delete mAudioAttributesParcel;
-        mAudioAttributesParcel = NULL;
-    }
-    AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
     disconnect();
     removePlayer(this);
-    if (mAudioAttributes != NULL) {
-        free(mAudioAttributes);
-    }
 }
 
 bool MediaPlayer2::init() {
@@ -412,13 +351,7 @@
 
         clear_l();
 
-        if (mAudioOutput != NULL) {
-            mAudioOutput->copyAudioDeviceCallback(mRoutingDelegates);
-        }
-
         player->setListener(new proxyListener(this));
-        mAudioOutput = new MediaPlayer2AudioOutput(mAudioSessionId, mUid,
-                mPid, mAudioAttributes, mRoutingDelegates);
         player->setAudioSink(mAudioOutput);
 
         err = player->setDataSource(dsd);
@@ -568,22 +501,9 @@
     return mPlayer->setBufferingSettings(buffering);
 }
 
-status_t MediaPlayer2::setAudioAttributes_l(const Parcel &parcel) {
-    if (mAudioAttributes != NULL) {
-        free(mAudioAttributes);
-    }
-    mAudioAttributes = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
-    if (mAudioAttributes == NULL) {
-        return NO_MEMORY;
-    }
-    unmarshallAudioAttributes(parcel, mAudioAttributes);
-
-    ALOGV("setAudioAttributes_l() usage=%d content=%d flags=0x%x tags=%s",
-            mAudioAttributes->usage, mAudioAttributes->content_type, mAudioAttributes->flags,
-            mAudioAttributes->tags);
-
-    if (mAudioOutput != 0) {
-        mAudioOutput->setAudioAttributes(mAudioAttributes);
+status_t MediaPlayer2::setAudioAttributes_l(const jobject attributes) {
+    if (mAudioOutput != NULL) {
+        mAudioOutput->setAudioAttributes(attributes);
     }
     return NO_ERROR;
 }
@@ -592,8 +512,8 @@
     ALOGV("prepareAsync");
     Mutex::Autolock _l(mLock);
     if ((mPlayer != 0) && (mCurrentState & MEDIA_PLAYER2_INITIALIZED)) {
-        if (mAudioAttributesParcel != NULL) {
-            status_t err = setAudioAttributes_l(*mAudioAttributesParcel);
+        if (mAudioAttributes != NULL) {
+            status_t err = setAudioAttributes_l(mAudioAttributes->getJObject());
             if (err != OK) {
                 return err;
             }
@@ -968,7 +888,7 @@
     return OK;
 }
 
-status_t MediaPlayer2::setAudioSessionId(audio_session_t sessionId) {
+status_t MediaPlayer2::setAudioSessionId(int32_t sessionId) {
     ALOGV("MediaPlayer2::setAudioSessionId(%d)", sessionId);
     Mutex::Autolock _l(mLock);
     if (!(mCurrentState & MEDIA_PLAYER2_IDLE)) {
@@ -978,17 +898,18 @@
     if (sessionId < 0) {
         return BAD_VALUE;
     }
-    if (sessionId != mAudioSessionId) {
-        AudioSystem::acquireAudioSessionId(sessionId, -1);
-        AudioSystem::releaseAudioSessionId(mAudioSessionId, -1);
-        mAudioSessionId = sessionId;
+    if (mAudioOutput != NULL && sessionId != mAudioOutput->getSessionId()) {
+        mAudioOutput->setSessionId(sessionId);
     }
     return NO_ERROR;
 }
 
-audio_session_t MediaPlayer2::getAudioSessionId() {
+int32_t MediaPlayer2::getAudioSessionId() {
     Mutex::Autolock _l(mLock);
-    return mAudioSessionId;
+    if (mAudioOutput != NULL) {
+        return mAudioOutput->getSessionId();
+    }
+    return 0;
 }
 
 status_t MediaPlayer2::setAuxEffectSendLevel(float level) {
@@ -1015,67 +936,37 @@
 }
 
 // always call with lock held
-status_t MediaPlayer2::checkStateForKeySet_l(int key) {
-    switch(key) {
-    case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
-        if (mCurrentState & ( MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED |
-                MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE) ) {
-            // Can't change the audio attributes after prepare
-            ALOGE("trying to set audio attributes called in state %d", mCurrentState);
-            return INVALID_OPERATION;
-        }
-        break;
-    default:
-        // parameter doesn't require player state check
-        break;
+status_t MediaPlayer2::checkState_l() {
+    if (mCurrentState & ( MEDIA_PLAYER2_PREPARED | MEDIA_PLAYER2_STARTED |
+            MEDIA_PLAYER2_PAUSED | MEDIA_PLAYER2_PLAYBACK_COMPLETE) ) {
+        // Can't change the audio attributes after prepare
+        ALOGE("trying to set audio attributes called in state %d", mCurrentState);
+        return INVALID_OPERATION;
     }
     return OK;
 }
 
-status_t MediaPlayer2::setParameter(int key, const Parcel& request) {
-    ALOGV("MediaPlayer2::setParameter(%d)", key);
+status_t MediaPlayer2::setAudioAttributes(const jobject attributes) {
+    ALOGV("MediaPlayer2::setAudioAttributes");
     status_t status = INVALID_OPERATION;
     Mutex::Autolock _l(mLock);
-    if (checkStateForKeySet_l(key) != OK) {
+    if (checkState_l() != OK) {
         return status;
     }
-    switch (key) {
-    case MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES:
-        // save the marshalled audio attributes
-        if (mAudioAttributesParcel != NULL) {
-            delete mAudioAttributesParcel;
-        }
-        mAudioAttributesParcel = new Parcel();
-        mAudioAttributesParcel->appendFrom(&request, 0, request.dataSize());
-        status = setAudioAttributes_l(request);
-        if (status != OK) {
-            return status;
-        }
-        break;
-    default:
-        ALOGV_IF(mPlayer == NULL, "setParameter: no active player");
-        break;
-    }
-
-    if (mPlayer != NULL) {
-        status = mPlayer->setParameter(key, request);
-    }
+    mAudioAttributes = new JObjectHolder(attributes);
+    status = setAudioAttributes_l(attributes);
     return status;
 }
 
+jobject MediaPlayer2::getAudioAttributes() {
+    ALOGV("MediaPlayer2::getAudioAttributes)");
+    Mutex::Autolock _l(mLock);
+    return mAudioAttributes != NULL ? mAudioAttributes->getJObject() : NULL;
+}
+
 status_t MediaPlayer2::getParameter(int key, Parcel *reply) {
     ALOGV("MediaPlayer2::getParameter(%d)", key);
     Mutex::Autolock _l(mLock);
-    if (key == MEDIA2_KEY_PARAMETER_AUDIO_ATTRIBUTES) {
-        if (reply == NULL) {
-            return BAD_VALUE;
-        }
-        if (mAudioAttributesParcel != NULL) {
-            reply->appendFrom(mAudioAttributesParcel, 0, mAudioAttributesParcel->dataSize());
-        }
-        return OK;
-    }
-
     if (mPlayer == NULL) {
         ALOGV("getParameter: no active player");
         return INVALID_OPERATION;
@@ -1264,34 +1155,28 @@
     return status;
 }
 
-status_t MediaPlayer2::setOutputDevice(audio_port_handle_t deviceId) {
+status_t MediaPlayer2::setPreferredDevice(jobject device) {
     Mutex::Autolock _l(mLock);
     if (mAudioOutput == NULL) {
-        ALOGV("setOutputDevice: audio sink not init");
+        ALOGV("setPreferredDevice: audio sink not init");
         return NO_INIT;
     }
-    return mAudioOutput->setOutputDevice(deviceId);
+    return mAudioOutput->setPreferredDevice(device);
 }
 
-audio_port_handle_t MediaPlayer2::getRoutedDeviceId() {
+jobject MediaPlayer2::getRoutedDevice() {
     Mutex::Autolock _l(mLock);
     if (mAudioOutput == NULL) {
-        ALOGV("getRoutedDeviceId: audio sink not init");
-        return AUDIO_PORT_HANDLE_NONE;
+        ALOGV("getRoutedDevice: audio sink not init");
+        return nullptr;
     }
-    audio_port_handle_t deviceId;
-    status_t status = mAudioOutput->getRoutedDeviceId(&deviceId);
-    if (status != NO_ERROR) {
-        return AUDIO_PORT_HANDLE_NONE;
-    }
-    return deviceId;
+    return mAudioOutput->getRoutedDevice();
 }
 
 status_t MediaPlayer2::addAudioDeviceCallback(jobject routingDelegate) {
     Mutex::Autolock _l(mLock);
     if (mAudioOutput == NULL) {
         ALOGV("addAudioDeviceCallback: player not init");
-        mRoutingDelegates.push_back(routingDelegate);
         return NO_INIT;
     }
     return mAudioOutput->addAudioDeviceCallback(routingDelegate);
diff --git a/media/libmediaplayer2/nuplayer2/Android.bp b/media/libmediaplayer2/nuplayer2/Android.bp
index c3c94b6..93c218e 100644
--- a/media/libmediaplayer2/nuplayer2/Android.bp
+++ b/media/libmediaplayer2/nuplayer2/Android.bp
@@ -1,6 +1,7 @@
 cc_library_static {
 
     srcs: [
+        "JMediaPlayer2Utils.cpp",
         "JWakeLock.cpp",
         "GenericSource2.cpp",
         "HTTPLiveSource2.cpp",
@@ -29,6 +30,7 @@
         "frameworks/av/media/libstagefright/rtsp",
         "frameworks/av/media/libstagefright/timedtext",
         "frameworks/av/media/ndk",
+        "frameworks/base/core/jni",
     ],
 
     cflags: [
@@ -57,6 +59,7 @@
     static_libs: [
         "libmedia_helper",
         "libmediaplayer2-protos",
+        "libmedia2_jni_core",
     ],
 
     name: "libstagefright_nuplayer2",
diff --git a/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp
new file mode 100644
index 0000000..bbd22bc
--- /dev/null
+++ b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "JMediaPlayer2Utils"
+
+#include "JMediaPlayer2Utils.h"
+#include <mediaplayer2/JavaVMHelper.h>
+
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/Utils.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+
+#include "log/log.h"
+
+namespace android {
+
+static const int64_t kOffloadMinDurationSec = 60;
+
+// static
+bool JMediaPlayer2Utils::isOffloadedAudioPlaybackSupported(
+        const sp<MetaData>& meta, bool hasVideo, bool isStreaming, audio_stream_type_t streamType)
+{
+    if (hasVideo || streamType != AUDIO_STREAM_MUSIC) {
+        return false;
+    }
+
+    audio_offload_info_t info = AUDIO_INFO_INITIALIZER;
+    if (OK != getAudioOffloadInfo(meta, hasVideo, isStreaming, streamType, &info)) {
+        return false;
+    }
+
+    if (info.duration_us < kOffloadMinDurationSec * 1000000) {
+        return false;
+    }
+
+    int32_t audioFormat = audioFormatFromNative(info.format);
+    int32_t channelMask = outChannelMaskFromNative(info.channel_mask);
+    if (audioFormat == ENCODING_INVALID || channelMask == CHANNEL_INVALID) {
+        return false;
+    }
+
+    JNIEnv* env = JavaVMHelper::getJNIEnv();
+    jclass jMP2UtilsCls = env->FindClass("android/media/MediaPlayer2Utils");
+    jmethodID jSetAudioOutputDeviceById = env->GetStaticMethodID(
+            jMP2UtilsCls, "isOffloadedAudioPlaybackSupported", "(III)Z");
+    jboolean result = env->CallStaticBooleanMethod(
+            jMP2UtilsCls, jSetAudioOutputDeviceById, audioFormat, info.sample_rate, channelMask);
+    return result;
+}
+
+}  // namespace android
diff --git a/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.h b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.h
new file mode 100644
index 0000000..fcbd43c
--- /dev/null
+++ b/media/libmediaplayer2/nuplayer2/JMediaPlayer2Utils.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _J_MEDIAPLAYER2_UTILS2_H_
+#define _J_MEDIAPLAYER2_UTILS2_H_
+
+#include <media/stagefright/MetaData.h>
+
+#include "jni.h"
+#include "android_media_AudioFormat.h"
+
+namespace android {
+
+struct JMediaPlayer2Utils {
+    static bool isOffloadedAudioPlaybackSupported(
+            const sp<MetaData>& meta, bool hasVideo, bool isStreaming,
+            audio_stream_type_t streamType);
+};
+
+}  // namespace android
+
+#endif  // _J_MEDIAPLAYER2_UTILS2_H_
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index c37460b..5ea600f 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -24,6 +24,7 @@
 #include "NuPlayer2.h"
 
 #include "HTTPLiveSource2.h"
+#include "JMediaPlayer2Utils.h"
 #include "NuPlayer2CCDecoder.h"
 #include "NuPlayer2Decoder.h"
 #include "NuPlayer2DecoderBase.h"
@@ -1699,7 +1700,8 @@
     }
 
     mOffloadAudio =
-        canOffloadStream(audioMeta, hasVideo, mCurrentSourceInfo.mSource->isStreaming(), streamType)
+        JMediaPlayer2Utils::isOffloadedAudioPlaybackSupported(
+                audioMeta, hasVideo, mCurrentSourceInfo.mSource->isStreaming(), streamType)
                 && (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f);
 
     // Modular DRM: Disabling audio offload if the source is protected
@@ -1992,7 +1994,7 @@
     sp<AMessage> videoFormat = mCurrentSourceInfo.mSource->getFormat(false /* audio */);
     audio_stream_type_t streamType = mAudioSink->getAudioStreamType();
     const bool hasVideo = (videoFormat != NULL);
-    bool canOffload = canOffloadStream(
+    bool canOffload = JMediaPlayer2Utils::isOffloadedAudioPlaybackSupported(
             audioMeta, hasVideo, mCurrentSourceInfo.mSource->isStreaming(), streamType)
                     && (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f);
 
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
index 7db78c1..d800412 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
@@ -224,7 +224,8 @@
         status_t err = mAudioSink->getPlaybackRate(rate);
         if (err == OK) {
             if (!isAudioPlaybackRateEqual(*rate, mPlaybackSettings)) {
-                ALOGW("correcting mismatch in internal/external playback rate");
+                ALOGW("correcting mismatch in internal/external playback rate, %f vs %f",
+                      rate->mSpeed, mPlaybackSettings.mSpeed);
             }
             // get playback settings used by audiosink, as it may be
             // slightly off due to audiosink not taking small changes.
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 30c0b1c..e3ae02e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1996,7 +1996,7 @@
         mTotalPausedDurationUs += resumeStartTimeUs - mPauseStartTimeUs - 30000;
     }
     double timeOffset = -mTotalPausedDurationUs;
-    if (mCaptureFpsEnable) {
+    if (mCaptureFpsEnable && (mVideoSource == VIDEO_SOURCE_CAMERA)) {
         timeOffset *= mCaptureFps / mFrameRate;
     }
     sp<MetaData> meta = new MetaData;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 8cd6eda..f3b69d6 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1547,6 +1547,16 @@
             notifyBufferingUpdate(100);
         }
 
+        if (mPreparing) {
+            notifyPreparedAndCleanup(finalStatus);
+            mPreparing = false;
+        } else if (mSentPauseOnBuffering) {
+            sendCacheStats();
+            mSentPauseOnBuffering = false;
+            sp<AMessage> notify = dupNotify();
+            notify->setInt32("what", kWhatResumeOnBufferingEnd);
+            notify->post();
+        }
         return;
     }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 57a0198..b258332 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -26,6 +26,8 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/AUtils.h>
 #include <media/stagefright/MediaClock.h>
+#include <media/stagefright/MediaCodecConstants.h>
+#include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/Utils.h>
@@ -87,6 +89,20 @@
 // static
 const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll;
 
+static audio_format_t constexpr audioFormatFromEncoding(int32_t pcmEncoding) {
+    switch (pcmEncoding) {
+    case kAudioEncodingPcmFloat:
+        return AUDIO_FORMAT_PCM_FLOAT;
+    case kAudioEncodingPcm16bit:
+        return AUDIO_FORMAT_PCM_16_BIT;
+    case kAudioEncodingPcm8bit:
+        return AUDIO_FORMAT_PCM_8_BIT; // TODO: do we want to support this?
+    default:
+        ALOGE("%s: Invalid encoding: %d", __func__, pcmEncoding);
+        return AUDIO_FORMAT_INVALID;
+    }
+}
+
 NuPlayer::Renderer::Renderer(
         const sp<MediaPlayerBase::AudioSink> &sink,
         const sp<MediaClock> &mediaClock,
@@ -1877,8 +1893,13 @@
     int32_t sampleRate;
     CHECK(format->findInt32("sample-rate", &sampleRate));
 
+    // read pcm encoding from MediaCodec output format, if available
+    int32_t pcmEncoding;
+    audio_format_t audioFormat =
+            format->findInt32(KEY_PCM_ENCODING, &pcmEncoding) ?
+                    audioFormatFromEncoding(pcmEncoding) : AUDIO_FORMAT_PCM_16_BIT;
+
     if (offloadingAudio()) {
-        audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT;
         AString mime;
         CHECK(format->findString("mime", &mime));
         status_t err = mapMimeToAudioFormat(audioFormat, mime.c_str());
@@ -1980,7 +2001,7 @@
         const PcmInfo info = {
                 (audio_channel_mask_t)channelMask,
                 (audio_output_flags_t)pcmFlags,
-                AUDIO_FORMAT_PCM_16_BIT, // TODO: change to audioFormat
+                audioFormat,
                 numChannels,
                 sampleRate
         };
@@ -2019,7 +2040,7 @@
                     sampleRate,
                     numChannels,
                     (audio_channel_mask_t)channelMask,
-                    AUDIO_FORMAT_PCM_16_BIT,
+                    audioFormat,
                     0 /* bufferCount - unused */,
                     mUseAudioCallback ? &NuPlayer::Renderer::AudioSinkCallback : NULL,
                     mUseAudioCallback ? this : NULL,
@@ -2077,4 +2098,3 @@
 }
 
 }  // namespace android
-
diff --git a/media/libmediaplayerservice/tests/Android.bp b/media/libmediaplayerservice/tests/Android.bp
index e86b68a..4749a8b 100644
--- a/media/libmediaplayerservice/tests/Android.bp
+++ b/media/libmediaplayerservice/tests/Android.bp
@@ -11,6 +11,7 @@
         "libutils",
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
     ],
 
     compile_multilib: "32",
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 3956520..dd29474 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -569,7 +569,7 @@
 }
 
 
-std::vector<std::pair<const char *, uint32_t>> tagMappings {
+static std::vector<std::pair<const char *, uint32_t>> stringMappings {
     {
         { "album", kKeyAlbum },
         { "albumartist", kKeyAlbumArtist },
@@ -581,47 +581,108 @@
         { "date", kKeyDate },
         { "discnum", kKeyDiscNumber },
         { "genre", kKeyGenre },
+        { "location", kKeyLocation },
         { "lyricist", kKeyWriter },
         { "title", kKeyTitle },
         { "year", kKeyYear },
     }
 };
 
-void convertMessageToMetaDataTags(const sp<AMessage> &msg, sp<MetaData> &meta) {
-    for (auto elem : tagMappings) {
+static std::vector<std::pair<const char *, uint32_t>> int64Mappings {
+    {
+        { "exif-offset", kKeyExifOffset },
+        { "exif-size", kKeyExifSize },
+    }
+};
+
+static std::vector<std::pair<const char *, uint32_t>> int32Mappings {
+    {
+        { "loop", kKeyAutoLoop },
+        { "time-scale", kKeyTimeScale },
+        { "crypto-mode", kKeyCryptoMode },
+        { "crypto-default-iv-size", kKeyCryptoDefaultIVSize },
+        { "crypto-encrypted-byte-block", kKeyEncryptedByteBlock },
+        { "crypto-skip-byte-block", kKeySkipByteBlock },
+        { "max-bitrate", kKeyMaxBitRate },
+        { "pcm-big-endian", kKeyPcmBigEndian },
+        { "temporal-layer-count", kKeyTemporalLayerCount },
+        { "thumbnail-width", kKeyThumbnailWidth },
+        { "thumbnail-height", kKeyThumbnailHeight },
+    }
+};
+
+static std::vector<std::pair<const char *, uint32_t>> bufferMappings {
+    {
+        { "albumart", kKeyAlbumArt },
+        { "pssh", kKeyPssh },
+        { "crypto-iv", kKeyCryptoIV },
+        { "crypto-key", kKeyCryptoKey },
+        { "icc-profile", kKeyIccProfile },
+        { "text-format-data", kKeyTextFormatData },
+    }
+};
+
+void convertMessageToMetaDataFromMappings(const sp<AMessage> &msg, sp<MetaData> &meta) {
+    for (auto elem : stringMappings) {
         AString value;
         if (msg->findString(elem.first, &value)) {
             meta->setCString(elem.second, value.c_str());
         }
     }
-    sp<ABuffer> buf;
-    if (msg->findBuffer("albumart", &buf)) {
-        meta->setData(kKeyAlbumArt, MetaDataBase::Type::TYPE_NONE, buf->data(), buf->size());
+
+    for (auto elem : int64Mappings) {
+        int64_t value;
+        if (msg->findInt64(elem.first, &value)) {
+            meta->setInt64(elem.second, value);
+        }
     }
 
-    int32_t loop;
-    if (msg->findInt32("loop", &loop)) {
-        meta->setInt32(kKeyAutoLoop, loop);
+    for (auto elem : int32Mappings) {
+        int32_t value;
+        if (msg->findInt32(elem.first, &value)) {
+            meta->setInt32(elem.second, value);
+        }
+    }
+
+    for (auto elem : bufferMappings) {
+        sp<ABuffer> value;
+        if (msg->findBuffer(elem.first, &value)) {
+            meta->setData(elem.second,
+                    MetaDataBase::Type::TYPE_NONE, value->data(), value->size());
+        }
     }
 }
 
-void convertMetaDataToMessageTags(const MetaDataBase *meta, sp<AMessage> format) {
-    for (auto elem : tagMappings) {
+void convertMetaDataToMessageFromMappings(const MetaDataBase *meta, sp<AMessage> format) {
+    for (auto elem : stringMappings) {
         const char *value;
         if (meta->findCString(elem.second, &value)) {
             format->setString(elem.first, value, strlen(value));
         }
     }
-    uint32_t type;
-    const void* data;
-    size_t size;
-    if (meta->findData(kKeyAlbumArt, &type, &data, &size)) {
-        sp<ABuffer> buf = ABuffer::CreateAsCopy(data, size);
-        format->setBuffer("albumart", buf);
+
+    for (auto elem : int64Mappings) {
+        int64_t value;
+        if (meta->findInt64(elem.second, &value)) {
+            format->setInt64(elem.first, value);
+        }
     }
-    int32_t loop;
-    if (meta->findInt32(kKeyAutoLoop, &loop)) {
-        format->setInt32("loop", loop);
+
+    for (auto elem : int32Mappings) {
+        int32_t value;
+        if (meta->findInt32(elem.second, &value)) {
+            format->setInt32(elem.first, value);
+        }
+    }
+
+    for (auto elem : bufferMappings) {
+        uint32_t type;
+        const void* data;
+        size_t size;
+        if (meta->findData(elem.second, &type, &data, &size)) {
+            sp<ABuffer> buf = ABuffer::CreateAsCopy(data, size);
+            format->setBuffer(elem.first, buf);
+        }
     }
 }
 
@@ -648,7 +709,7 @@
     sp<AMessage> msg = new AMessage;
     msg->setString("mime", mime);
 
-    convertMetaDataToMessageTags(meta, msg);
+    convertMetaDataToMessageFromMappings(meta, msg);
 
     uint32_t type;
     const void *data;
@@ -663,6 +724,16 @@
         memcpy(buffer->data(), data, size);
     }
 
+    if (meta->findData(kKeyCAPrivateData, &type, &data, &size)) {
+        sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
+        if (buffer.get() == NULL || buffer->base() == NULL) {
+            return NO_MEMORY;
+        }
+
+        msg->setBuffer("ca-private-data", buffer);
+        memcpy(buffer->data(), data, size);
+    }
+
     int32_t systemId;
     if (meta->findInt32(kKeyCASystemID, &systemId)) {
         msg->setInt32("ca-system-id", systemId);
@@ -1117,7 +1188,7 @@
                 msg->setInt32("max-bitrate", (int32_t)maxBitrate);
             }
         }
-    } else if (meta->findData(kTypeD263, &type, &data, &size)) {
+    } else if (meta->findData(kKeyD263, &type, &data, &size)) {
         const uint8_t *ptr = (const uint8_t *)data;
         parseH263ProfileLevelFromD263(ptr, size, msg);
     } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
@@ -1217,13 +1288,6 @@
         msg->setBuffer("csd-0", buffer);
     }
 
-    // TODO expose "crypto-key"/kKeyCryptoKey through public api
-    if (meta->findData(kKeyCryptoKey, &type, &data, &size)) {
-        sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
-        msg->setBuffer("crypto-key", buffer);
-        memcpy(buffer->data(), data, size);
-    }
-
     *format = msg;
 
     return OK;
@@ -1422,7 +1486,20 @@
         ALOGW("did not find mime type");
     }
 
-    convertMessageToMetaDataTags(msg, meta);
+    convertMessageToMetaDataFromMappings(msg, meta);
+
+    int32_t systemId;
+    if (msg->findInt32("ca-system-id", &systemId)) {
+        meta->setInt32(kKeyCASystemID, systemId);
+
+        sp<ABuffer> caSessionId, caPvtData;
+        if (msg->findBuffer("ca-session-id", &caSessionId)) {
+            meta->setData(kKeyCASessionID, 0, caSessionId->data(), caSessionId->size());
+        }
+        if (msg->findBuffer("ca-private-data", &caPvtData)) {
+            meta->setData(kKeyCAPrivateData, 0, caPvtData->data(), caPvtData->size());
+        }
+    }
 
     int64_t durationUs;
     if (msg->findInt64("durationUs", &durationUs)) {
@@ -1455,7 +1532,7 @@
             meta->setInt32(kKeyWidth, width);
             meta->setInt32(kKeyHeight, height);
         } else {
-            ALOGW("did not find width and/or height");
+            ALOGV("did not find width and/or height");
         }
 
         int32_t sarWidth, sarHeight;
@@ -1611,7 +1688,7 @@
             // The written ESDS is actually for an audio stream, but it's enough
             // for transporting the CSD to muxers.
             reassembleESDS(csd0, esds.data());
-            meta->setData(kKeyESDS, kKeyESDS, esds.data(), esds.size());
+            meta->setData(kKeyESDS, kTypeESDS, esds.data(), esds.size());
         } else if (mime == MEDIA_MIMETYPE_VIDEO_HEVC ||
                    mime == MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC) {
             std::vector<uint8_t> hvcc(csd0size + 1024);
@@ -1640,11 +1717,12 @@
     } else if ((mime == MEDIA_MIMETYPE_VIDEO_HEVC || mime == MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)
             && msg->findBuffer("csd-hevc", &csd0)) {
         meta->setData(kKeyHVCC, kTypeHVCC, csd0->data(), csd0->size());
-    }
-
-    int32_t timeScale;
-    if (msg->findInt32("time-scale", &timeScale)) {
-        meta->setInt32(kKeyTimeScale, timeScale);
+    } else if (msg->findBuffer("esds", &csd0)) {
+        meta->setData(kKeyESDS, kTypeESDS, csd0->data(), csd0->size());
+    } else if (msg->findBuffer("mpeg2-stream-header", &csd0)) {
+        meta->setData(kKeyStreamHeader, 'mdat', csd0->data(), csd0->size());
+    } else if (msg->findBuffer("d263", &csd0)) {
+        meta->setData(kKeyD263, kTypeD263, csd0->data(), csd0->size());
     }
 
     // XXX TODO add whatever other keys there are
@@ -1775,43 +1853,43 @@
     return;
 }
 
-bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo,
-                      bool isStreaming, audio_stream_type_t streamType)
+status_t getAudioOffloadInfo(const sp<MetaData>& meta, bool hasVideo,
+        bool isStreaming, audio_stream_type_t streamType, audio_offload_info_t *info)
 {
     const char *mime;
     if (meta == NULL) {
-        return false;
+        return BAD_VALUE;
     }
     CHECK(meta->findCString(kKeyMIMEType, &mime));
 
-    audio_offload_info_t info = AUDIO_INFO_INITIALIZER;
+    (*info) = AUDIO_INFO_INITIALIZER;
 
-    info.format = AUDIO_FORMAT_INVALID;
-    if (mapMimeToAudioFormat(info.format, mime) != OK) {
+    info->format = AUDIO_FORMAT_INVALID;
+    if (mapMimeToAudioFormat(info->format, mime) != OK) {
         ALOGE(" Couldn't map mime type \"%s\" to a valid AudioSystem::audio_format !", mime);
-        return false;
+        return BAD_VALUE;
     } else {
-        ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info.format);
+        ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info->format);
     }
 
-    if (AUDIO_FORMAT_INVALID == info.format) {
+    if (AUDIO_FORMAT_INVALID == info->format) {
         // can't offload if we don't know what the source format is
         ALOGE("mime type \"%s\" not a known audio format", mime);
-        return false;
+        return BAD_VALUE;
     }
 
     // Redefine aac format according to its profile
     // Offloading depends on audio DSP capabilities.
     int32_t aacaot = -1;
     if (meta->findInt32(kKeyAACAOT, &aacaot)) {
-        mapAACProfileToAudioFormat(info.format,(OMX_AUDIO_AACPROFILETYPE) aacaot);
+        mapAACProfileToAudioFormat(info->format,(OMX_AUDIO_AACPROFILETYPE) aacaot);
     }
 
     int32_t srate = -1;
     if (!meta->findInt32(kKeySampleRate, &srate)) {
         ALOGV("track of type '%s' does not publish sample rate", mime);
     }
-    info.sample_rate = srate;
+    info->sample_rate = srate;
 
     int32_t cmask = 0;
     if (!meta->findInt32(kKeyChannelMask, &cmask) || cmask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
@@ -1825,25 +1903,34 @@
             cmask = audio_channel_out_mask_from_count(channelCount);
         }
     }
-    info.channel_mask = cmask;
+    info->channel_mask = cmask;
 
     int64_t duration = 0;
     if (!meta->findInt64(kKeyDuration, &duration)) {
         ALOGV("track of type '%s' does not publish duration", mime);
     }
-    info.duration_us = duration;
+    info->duration_us = duration;
 
     int32_t brate = -1;
     if (!meta->findInt32(kKeyBitRate, &brate)) {
         ALOGV("track of type '%s' does not publish bitrate", mime);
     }
-    info.bit_rate = brate;
+    info->bit_rate = brate;
 
 
-    info.stream_type = streamType;
-    info.has_video = hasVideo;
-    info.is_streaming = isStreaming;
+    info->stream_type = streamType;
+    info->has_video = hasVideo;
+    info->is_streaming = isStreaming;
+    return OK;
+}
 
+bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo,
+                      bool isStreaming, audio_stream_type_t streamType)
+{
+    audio_offload_info_t info = AUDIO_INFO_INITIALIZER;
+    if (OK != getAudioOffloadInfo(meta, hasVideo, isStreaming, streamType, &info)) {
+        return false;
+    }
     // Check if offload is possible for given format, stream type, sample rate,
     // bit rate, duration, video and streaming
     return AudioSystem::isOffloadSupported(info);
diff --git a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder_basic_op_cequivalent.h b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder_basic_op_cequivalent.h
index 3c7590c..7a86ec2 100644
--- a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder_basic_op_cequivalent.h
+++ b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder_basic_op_cequivalent.h
@@ -467,7 +467,12 @@
     __inline  int32 fxp_mac_16by16(int16 var1,  int16 var2, int32 L_add)
     {
 
-        L_add += (int32)var1 * var2;
+        int32 l_orig = L_add;
+        if (__builtin_add_overflow( (int32)var1 * var2, l_orig, &L_add)) {
+            // needs saturation
+            if (l_orig > 0) L_add = MAX_32;
+            else            L_add = MIN_32;
+        }
 
         return L_add;
     }
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index 3ef4c0e..a462ae7 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -327,6 +327,7 @@
 constexpr char KEY_BITRATE_MODE[] = "bitrate-mode";
 constexpr char KEY_CA_SESSION_ID[] = "ca-session-id";
 constexpr char KEY_CA_SYSTEM_ID[] = "ca-system-id";
+constexpr char KEY_CA_PRIVATE_DATA[] = "ca-private-data";
 constexpr char KEY_CAPTURE_RATE[] = "capture-rate";
 constexpr char KEY_CHANNEL_COUNT[] = "channel-count";
 constexpr char KEY_CHANNEL_MASK[] = "channel-mask";
diff --git a/media/libstagefright/include/media/stagefright/Utils.h b/media/libstagefright/include/media/stagefright/Utils.h
index 46a419d..e8e0a11 100644
--- a/media/libstagefright/include/media/stagefright/Utils.h
+++ b/media/libstagefright/include/media/stagefright/Utils.h
@@ -52,6 +52,10 @@
 // Send information from MetaData to the HAL via AudioSink
 status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink, const sp<MetaData>& meta);
 
+// Return |audio_offload_info_t| filled from given metadata
+status_t getAudioOffloadInfo(const sp<MetaData>& meta, bool hasVideo,
+        bool isStreaming, audio_stream_type_t streamType, audio_offload_info_t *info);
+
 // Check whether the stream defined by meta can be offloaded to hardware
 bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo,
                       bool isStreaming, audio_stream_type_t streamType);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 3debe34..aa4a4db 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -117,12 +117,6 @@
     void signalNewSampleAesKey(const sp<AMessage> &keyItem);
 
 private:
-    struct StreamInfo {
-        unsigned mType;
-        unsigned mTypeExt;
-        unsigned mPID;
-        int32_t mCASystemId;
-    };
 
     ATSParser *mParser;
     unsigned mProgramNumber;
@@ -143,12 +137,7 @@
 };
 
 struct ATSParser::Stream : public RefBase {
-    Stream(Program *program,
-           unsigned elementaryPID,
-           unsigned streamType,
-           unsigned streamTypeExt,
-           unsigned PCR_PID,
-           int32_t CA_system_ID);
+    Stream(Program *program, unsigned PCR_PID, const StreamInfo &info);
 
     unsigned type() const { return mStreamType; }
     unsigned typeExt() const { return mStreamTypeExt; }
@@ -575,8 +564,12 @@
                 mProgramNumber, info.mPID, streamCA)) {
             return ERROR_MALFORMED;
         }
-        info.mCASystemId = hasProgramCA ? programCA.mSystemID :
-                           hasStreamCA ? streamCA.mSystemID  : -1;
+        if (hasProgramCA) {
+            info.mCADescriptor = programCA;
+        } else if (hasStreamCA) {
+            info.mCADescriptor = streamCA;
+        }
+
         infos.push(info);
     }
 
@@ -635,14 +628,13 @@
         ssize_t index = mStreams.indexOfKey(info.mPID);
 
         if (index < 0) {
-            sp<Stream> stream = new Stream(
-                    this, info.mPID, info.mType, info.mTypeExt, PCR_PID, info.mCASystemId);
+            sp<Stream> stream = new Stream(this, PCR_PID, info);
 
             if (mSampleAesKeyItem != NULL) {
                 stream->signalNewSampleAesKey(mSampleAesKeyItem);
             }
 
-            isAddingScrambledStream |= info.mCASystemId >= 0;
+            isAddingScrambledStream |= info.mCADescriptor.mSystemID >= 0;
             mStreams.add(info.mPID, stream);
         }
     }
@@ -751,31 +743,25 @@
 static const size_t kInitialStreamBufferSize = 192 * 1024;
 
 ATSParser::Stream::Stream(
-        Program *program,
-        unsigned elementaryPID,
-        unsigned streamType,
-        unsigned streamTypeExt,
-        unsigned PCR_PID,
-        int32_t CA_system_ID)
+        Program *program, unsigned PCR_PID, const StreamInfo &info)
     : mProgram(program),
-      mElementaryPID(elementaryPID),
-      mStreamType(streamType),
-      mStreamTypeExt(streamTypeExt),
+      mElementaryPID(info.mPID),
+      mStreamType(info.mType),
+      mStreamTypeExt(info.mTypeExt),
       mPCR_PID(PCR_PID),
       mExpectedContinuityCounter(-1),
       mPayloadStarted(false),
       mEOSReached(false),
       mPrevPTS(0),
       mQueue(NULL),
-      mScrambled(CA_system_ID >= 0) {
-
+      mScrambled(info.mCADescriptor.mSystemID >= 0) {
     mSampleEncrypted =
             mStreamType == STREAMTYPE_H264_ENCRYPTED ||
             mStreamType == STREAMTYPE_AAC_ENCRYPTED  ||
             mStreamType == STREAMTYPE_AC3_ENCRYPTED;
 
     ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d, SampleEncrypted: %d",
-            elementaryPID, streamType, mScrambled, mSampleEncrypted);
+            info.mPID, info.mType, mScrambled, mSampleEncrypted);
 
     uint32_t flags = 0;
     if (((isVideo() || isAudio()) && mScrambled)) {
@@ -835,7 +821,7 @@
 
         default:
             ALOGE("stream PID 0x%02x has invalid stream type 0x%02x",
-                    elementaryPID, streamType);
+                    info.mPID, info.mType);
             return;
     }
 
@@ -855,7 +841,13 @@
                     isAudio() ? MEDIA_MIMETYPE_AUDIO_SCRAMBLED
                               : MEDIA_MIMETYPE_VIDEO_SCRAMBLED);
             // for MediaExtractor.CasInfo
-            meta->setInt32(kKeyCASystemID, CA_system_ID);
+            const CADescriptor &descriptor = info.mCADescriptor;
+            meta->setInt32(kKeyCASystemID, descriptor.mSystemID);
+
+            meta->setData(kKeyCAPrivateData, 0,
+                    descriptor.mPrivateData.data(),
+                    descriptor.mPrivateData.size());
+
             mSource = new AnotherPacketSource(meta);
         }
     }
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index a31dc46..9ece21e 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -185,11 +185,19 @@
     struct PSISection;
     struct CasManager;
     struct CADescriptor {
-        int32_t mSystemID;
+        CADescriptor() : mPID(0), mSystemID(-1) {}
         unsigned mPID;
+        int32_t mSystemID;
         std::vector<uint8_t> mPrivateData;
     };
 
+    struct StreamInfo {
+        unsigned mType;
+        unsigned mTypeExt;
+        unsigned mPID;
+        CADescriptor mCADescriptor;
+    };
+
     sp<CasManager> mCasManager;
 
     uint32_t mFlags;
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 6537ad1..b282ed8 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -288,13 +288,19 @@
 EXPORT const char* AMEDIAFORMAT_KEY_COMPILATION = "compilation";
 EXPORT const char* AMEDIAFORMAT_KEY_COMPLEXITY = "complexity";
 EXPORT const char* AMEDIAFORMAT_KEY_COMPOSER = "composer";
+EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE = "crypto-default-iv-size";
+EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK = "crypto-encrypted-byte-block";
+EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_IV = "crypto-iv";
 EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_KEY = "crypto-key";
+EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_MODE = "crypto-mode";
+EXPORT const char* AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK = "crypto-skip-byte-block";
 EXPORT const char* AMEDIAFORMAT_KEY_CSD = "csd";
 EXPORT const char* AMEDIAFORMAT_KEY_CSD_0 = "csd-0";
 EXPORT const char* AMEDIAFORMAT_KEY_CSD_1 = "csd-1";
 EXPORT const char* AMEDIAFORMAT_KEY_CSD_2 = "csd-2";
 EXPORT const char* AMEDIAFORMAT_KEY_CSD_AVC = "csd-avc";
 EXPORT const char* AMEDIAFORMAT_KEY_CSD_HEVC = "csd-hevc";
+EXPORT const char* AMEDIAFORMAT_KEY_D263 = "d263";
 EXPORT const char* AMEDIAFORMAT_KEY_DATE = "date";
 EXPORT const char* AMEDIAFORMAT_KEY_DISCNUMBER = "discnum";
 EXPORT const char* AMEDIAFORMAT_KEY_DISPLAY_CROP = "crop";
@@ -303,13 +309,18 @@
 EXPORT const char* AMEDIAFORMAT_KEY_DURATION = "durationUs";
 EXPORT const char* AMEDIAFORMAT_KEY_ENCODER_DELAY = "encoder-delay";
 EXPORT const char* AMEDIAFORMAT_KEY_ENCODER_PADDING = "encoder-padding";
+EXPORT const char* AMEDIAFORMAT_KEY_ESDS = "esds";
+EXPORT const char* AMEDIAFORMAT_KEY_EXIF_OFFSET = "exif-offset";
+EXPORT const char* AMEDIAFORMAT_KEY_EXIF_SIZE = "exif-size";
 EXPORT const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
+EXPORT const char* AMEDIAFORMAT_KEY_FRAME_COUNT = "frame-count";
 EXPORT const char* AMEDIAFORMAT_KEY_FRAME_RATE = "frame-rate";
 EXPORT const char* AMEDIAFORMAT_KEY_GENRE = "genre";
 EXPORT const char* AMEDIAFORMAT_KEY_GRID_COLUMNS = "grid-cols";
 EXPORT const char* AMEDIAFORMAT_KEY_GRID_ROWS = "grid-rows";
 EXPORT const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO = "hdr-static-info";
 EXPORT const char* AMEDIAFORMAT_KEY_HEIGHT = "height";
+EXPORT const char* AMEDIAFORMAT_KEY_ICC_PROFILE = "icc-profile";
 EXPORT const char* AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
 EXPORT const char* AMEDIAFORMAT_KEY_IS_ADTS = "is-adts";
 EXPORT const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT = "is-autoselect";
@@ -319,17 +330,22 @@
 EXPORT const char* AMEDIAFORMAT_KEY_LANGUAGE = "language";
 EXPORT const char* AMEDIAFORMAT_KEY_LATENCY = "latency";
 EXPORT const char* AMEDIAFORMAT_KEY_LEVEL = "level";
+EXPORT const char* AMEDIAFORMAT_KEY_LOCATION = "location";
 EXPORT const char* AMEDIAFORMAT_KEY_LOOP = "loop";
 EXPORT const char* AMEDIAFORMAT_KEY_LYRICIST = "lyricist";
+EXPORT const char* AMEDIAFORMAT_KEY_MAX_BIT_RATE = "max-bitrate";
 EXPORT const char* AMEDIAFORMAT_KEY_MAX_HEIGHT = "max-height";
 EXPORT const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size";
 EXPORT const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width";
 EXPORT const char* AMEDIAFORMAT_KEY_MIME = "mime";
 EXPORT const char* AMEDIAFORMAT_KEY_MPEG_USER_DATA = "mpeg-user-data";
+EXPORT const char* AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER = "mpeg2-stream-header";
 EXPORT const char* AMEDIAFORMAT_KEY_OPERATING_RATE = "operating-rate";
 EXPORT const char* AMEDIAFORMAT_KEY_PCM_ENCODING = "pcm-encoding";
 EXPORT const char* AMEDIAFORMAT_KEY_PRIORITY = "priority";
 EXPORT const char* AMEDIAFORMAT_KEY_PROFILE = "profile";
+EXPORT const char* AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN = "pcm-big-endian";
+EXPORT const char* AMEDIAFORMAT_KEY_PSSH = "pssh";
 EXPORT const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
 EXPORT const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
 EXPORT const char* AMEDIAFORMAT_KEY_ROTATION = "rotation-degrees";
@@ -339,9 +355,13 @@
 EXPORT const char* AMEDIAFORMAT_KEY_SEI = "sei";
 EXPORT const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT = "slice-height";
 EXPORT const char* AMEDIAFORMAT_KEY_STRIDE = "stride";
+EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT = "temporal-layer-count";
 EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID = "temporal-layer-id";
 EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYERING = "ts-schema";
+EXPORT const char* AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA = "text-format-data";
+EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT = "thumbnail-height";
 EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_TIME = "thumbnail-time";
+EXPORT const char* AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH = "thumbnail-width";
 EXPORT const char* AMEDIAFORMAT_KEY_TILE_HEIGHT = "tile-height";
 EXPORT const char* AMEDIAFORMAT_KEY_TILE_WIDTH = "tile-width";
 EXPORT const char* AMEDIAFORMAT_KEY_TIME_US = "timeUs";
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 3a3268c..89cfd5e 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -188,20 +188,40 @@
 extern const char* AMEDIAFORMAT_KEY_COLOR_TRANSFER __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_COMPILATION __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_COMPOSER __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_CRYPTO_DEFAULT_IV_SIZE __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_BYTE_BLOCK __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_CRYPTO_IV __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_CRYPTO_KEY __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_CRYPTO_MODE __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_CRYPTO_SKIP_BYTE_BLOCK __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_CSD_AVC __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_CSD_HEVC __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_D263 __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_DATE __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_DISCNUMBER __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_ENCODER_DELAY __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_ENCODER_PADDING __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_ESDS __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_EXIF_OFFSET __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_EXIF_SIZE __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_FRAME_COUNT __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_GENRE __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_ICC_PROFILE __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_LOCATION __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_LOOP __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_LYRICIST __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_MAX_BIT_RATE __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_MPEG2_STREAM_HEADER __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_PSSH __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_SAR_HEIGHT __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_SAR_WIDTH __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_TEXT_FORMAT_DATA __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_TIME __INTRODUCED_IN(29);
+extern const char* AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_TITLE __INTRODUCED_IN(29);
 extern const char* AMEDIAFORMAT_KEY_YEAR __INTRODUCED_IN(29);
 
diff --git a/packages/MediaComponents/apex/Android.bp b/packages/MediaComponents/apex/Android.bp
new file mode 100644
index 0000000..363a416
--- /dev/null
+++ b/packages/MediaComponents/apex/Android.bp
@@ -0,0 +1,39 @@
+filegroup {
+    name: "media_aidl",
+    srcs: [
+        "java/android/media/**/*.aidl",
+        "java/android/service/**/*.aidl",
+    ],
+    exclude_srcs: [
+        // Exclude these aidls to avoid errors such as
+        // "Refusing to generate code with unstructured parcelables."
+        "java/android/media/MediaDescription.aidl",
+        "java/android/media/MediaMetadata.aidl",
+        "java/android/media/Rating.aidl",
+        "java/android/media/browse/MediaBrowser.aidl",
+        "java/android/media/session/MediaSession.aidl",
+        "java/android/media/session/ParcelableVolumeInfo.aidl",
+        "java/android/media/session/PlaybackState.aidl",
+    ],
+}
+
+java_library {
+    name: "media",
+    installable: true,
+    sdk_version: "current",
+    srcs: [
+        "java/android/media/**/*.java",
+        "java/android/service/**/*.java",
+        ":media_aidl",
+        ":framework-media-annotation-srcs",
+    ],
+    aidl: {
+        local_include_dirs: ["java"],
+        include_dirs: [
+            "frameworks/base/core/java",
+            // for android.graphics.Bitmap
+            // from IMediaBrowserServiceCallback
+            "frameworks/base/graphics/java",
+            ],
+    },
+}
diff --git a/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl b/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl
new file mode 100644
index 0000000..e4a4a42
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/IRemoteVolumeController.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.session.ISessionController;
+
+/**
+ * AIDL for the MediaSessionService to report interesting events on remote playback
+ * to a volume control dialog. See also IVolumeController for the AudioService half.
+ * TODO add in better support for multiple remote sessions.
+ * @hide
+ */
+oneway interface IRemoteVolumeController {
+    void remoteVolumeChanged(ISessionController session, int flags);
+    // sets the default session to use with the slider, replaces remoteSliderVisibility
+    // on IVolumeController
+    void updateRemoteController(ISessionController session);
+}
diff --git a/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl b/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl
new file mode 100644
index 0000000..c83a19e
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/ISessionTokensListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.os.Bundle;
+
+/**
+ * Listens for changes to the list of session tokens.
+ * @hide
+ */
+oneway interface ISessionTokensListener {
+    void onSessionTokensChanged(in List<Bundle> tokens);
+}
diff --git a/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl b/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl
new file mode 100644
index 0000000..6f934f7
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/MediaDescription.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable MediaDescription;
diff --git a/packages/MediaComponents/apex/java/android/media/MediaDescription.java b/packages/MediaComponents/apex/java/android/media/MediaDescription.java
new file mode 100644
index 0000000..31079e5
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/MediaDescription.java
@@ -0,0 +1,383 @@
+package android.media;
+
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.media.browse.MediaBrowser;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * A simple set of metadata for a media item suitable for display. This can be
+ * created using the Builder or retrieved from existing metadata using
+ * {@link MediaMetadata#getDescription()}.
+ */
+public class MediaDescription implements Parcelable {
+    /**
+     * A unique persistent id for the content or null.
+     */
+    private final String mMediaId;
+    /**
+     * A primary title suitable for display or null.
+     */
+    private final CharSequence mTitle;
+    /**
+     * A subtitle suitable for display or null.
+     */
+    private final CharSequence mSubtitle;
+    /**
+     * A description suitable for display or null.
+     */
+    private final CharSequence mDescription;
+    /**
+     * A bitmap icon suitable for display or null.
+     */
+    private final Bitmap mIcon;
+    /**
+     * A Uri for an icon suitable for display or null.
+     */
+    private final Uri mIconUri;
+    /**
+     * Extras for opaque use by apps/system.
+     */
+    private final Bundle mExtras;
+    /**
+     * A Uri to identify this content.
+     */
+    private final Uri mMediaUri;
+
+    /**
+     * Used as a long extra field to indicate the bluetooth folder type of the media item as
+     * specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5. This is valid only for
+     * {@link MediaBrowser.MediaItem} with {@link MediaBrowser.MediaItem#FLAG_BROWSABLE}. The value
+     * should be one of the following:
+     * <ul>
+     * <li>{@link #BT_FOLDER_TYPE_MIXED}</li>
+     * <li>{@link #BT_FOLDER_TYPE_TITLES}</li>
+     * <li>{@link #BT_FOLDER_TYPE_ALBUMS}</li>
+     * <li>{@link #BT_FOLDER_TYPE_ARTISTS}</li>
+     * <li>{@link #BT_FOLDER_TYPE_GENRES}</li>
+     * <li>{@link #BT_FOLDER_TYPE_PLAYLISTS}</li>
+     * <li>{@link #BT_FOLDER_TYPE_YEARS}</li>
+     * </ul>
+     *
+     * @see #getExtras()
+     */
+    public static final String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
+
+    /**
+     * The type of folder that is unknown or contains media elements of mixed types as specified in
+     * the section 6.10.2.2 of the Bluetooth AVRCP 1.5.
+     */
+    public static final long BT_FOLDER_TYPE_MIXED = 0;
+
+    /**
+     * The type of folder that contains media elements only as specified in the section 6.10.2.2 of
+     * the Bluetooth AVRCP 1.5.
+     */
+    public static final long BT_FOLDER_TYPE_TITLES = 1;
+
+    /**
+     * The type of folder that contains folders categorized by album as specified in the section
+     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
+     */
+    public static final long BT_FOLDER_TYPE_ALBUMS = 2;
+
+    /**
+     * The type of folder that contains folders categorized by artist as specified in the section
+     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
+     */
+    public static final long BT_FOLDER_TYPE_ARTISTS = 3;
+
+    /**
+     * The type of folder that contains folders categorized by genre as specified in the section
+     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
+     */
+    public static final long BT_FOLDER_TYPE_GENRES = 4;
+
+    /**
+     * The type of folder that contains folders categorized by playlist as specified in the section
+     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
+     */
+    public static final long BT_FOLDER_TYPE_PLAYLISTS = 5;
+
+    /**
+     * The type of folder that contains folders categorized by year as specified in the section
+     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
+     */
+    public static final long BT_FOLDER_TYPE_YEARS = 6;
+
+    private MediaDescription(String mediaId, CharSequence title, CharSequence subtitle,
+            CharSequence description, Bitmap icon, Uri iconUri, Bundle extras, Uri mediaUri) {
+        mMediaId = mediaId;
+        mTitle = title;
+        mSubtitle = subtitle;
+        mDescription = description;
+        mIcon = icon;
+        mIconUri = iconUri;
+        mExtras = extras;
+        mMediaUri = mediaUri;
+    }
+
+    private MediaDescription(Parcel in) {
+        mMediaId = in.readString();
+        mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mIcon = in.readParcelable(null);
+        mIconUri = in.readParcelable(null);
+        mExtras = in.readBundle();
+        mMediaUri = in.readParcelable(null);
+    }
+
+    /**
+     * Returns the media id or null. See
+     * {@link MediaMetadata#METADATA_KEY_MEDIA_ID}.
+     */
+    public @Nullable String getMediaId() {
+        return mMediaId;
+    }
+
+    /**
+     * Returns a title suitable for display or null.
+     *
+     * @return A title or null.
+     */
+    public @Nullable CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Returns a subtitle suitable for display or null.
+     *
+     * @return A subtitle or null.
+     */
+    public @Nullable CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * Returns a description suitable for display or null.
+     *
+     * @return A description or null.
+     */
+    public @Nullable CharSequence getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * Returns a bitmap icon suitable for display or null.
+     *
+     * @return An icon or null.
+     */
+    public @Nullable Bitmap getIconBitmap() {
+        return mIcon;
+    }
+
+    /**
+     * Returns a Uri for an icon suitable for display or null.
+     *
+     * @return An icon uri or null.
+     */
+    public @Nullable Uri getIconUri() {
+        return mIconUri;
+    }
+
+    /**
+     * Returns any extras that were added to the description.
+     *
+     * @return A bundle of extras or null.
+     */
+    public @Nullable Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Returns a Uri representing this content or null.
+     *
+     * @return A media Uri or null.
+     */
+    public @Nullable Uri getMediaUri() {
+        return mMediaUri;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mMediaId);
+        TextUtils.writeToParcel(mTitle, dest, 0);
+        TextUtils.writeToParcel(mSubtitle, dest, 0);
+        TextUtils.writeToParcel(mDescription, dest, 0);
+        dest.writeParcelable(mIcon, flags);
+        dest.writeParcelable(mIconUri, flags);
+        dest.writeBundle(mExtras);
+        dest.writeParcelable(mMediaUri, flags);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) {
+            return false;
+        }
+
+        if (!(o instanceof MediaDescription)){
+            return false;
+        }
+
+        final MediaDescription d = (MediaDescription) o;
+
+        if (!String.valueOf(mTitle).equals(String.valueOf(d.mTitle))) {
+            return false;
+        }
+
+        if (!String.valueOf(mSubtitle).equals(String.valueOf(d.mSubtitle))) {
+            return false;
+        }
+
+        if (!String.valueOf(mDescription).equals(String.valueOf(d.mDescription))) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return mTitle + ", " + mSubtitle + ", " + mDescription;
+    }
+
+    public static final Parcelable.Creator<MediaDescription> CREATOR =
+            new Parcelable.Creator<MediaDescription>() {
+                @Override
+                public MediaDescription createFromParcel(Parcel in) {
+                    return new MediaDescription(in);
+                }
+
+                @Override
+                public MediaDescription[] newArray(int size) {
+                    return new MediaDescription[size];
+                }
+            };
+
+    /**
+     * Builder for {@link MediaDescription} objects.
+     */
+    public static class Builder {
+        private String mMediaId;
+        private CharSequence mTitle;
+        private CharSequence mSubtitle;
+        private CharSequence mDescription;
+        private Bitmap mIcon;
+        private Uri mIconUri;
+        private Bundle mExtras;
+        private Uri mMediaUri;
+
+        /**
+         * Creates an initially empty builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Sets the media id.
+         *
+         * @param mediaId The unique id for the item or null.
+         * @return this
+         */
+        public Builder setMediaId(@Nullable String mediaId) {
+            mMediaId = mediaId;
+            return this;
+        }
+
+        /**
+         * Sets the title.
+         *
+         * @param title A title suitable for display to the user or null.
+         * @return this
+         */
+        public Builder setTitle(@Nullable CharSequence title) {
+            mTitle = title;
+            return this;
+        }
+
+        /**
+         * Sets the subtitle.
+         *
+         * @param subtitle A subtitle suitable for display to the user or null.
+         * @return this
+         */
+        public Builder setSubtitle(@Nullable CharSequence subtitle) {
+            mSubtitle = subtitle;
+            return this;
+        }
+
+        /**
+         * Sets the description.
+         *
+         * @param description A description suitable for display to the user or
+         *            null.
+         * @return this
+         */
+        public Builder setDescription(@Nullable CharSequence description) {
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Sets the icon.
+         *
+         * @param icon A {@link Bitmap} icon suitable for display to the user or
+         *            null.
+         * @return this
+         */
+        public Builder setIconBitmap(@Nullable Bitmap icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Sets the icon uri.
+         *
+         * @param iconUri A {@link Uri} for an icon suitable for display to the
+         *            user or null.
+         * @return this
+         */
+        public Builder setIconUri(@Nullable Uri iconUri) {
+            mIconUri = iconUri;
+            return this;
+        }
+
+        /**
+         * Sets a bundle of extras.
+         *
+         * @param extras The extras to include with this description or null.
+         * @return this
+         */
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Sets the media uri.
+         *
+         * @param mediaUri The content's {@link Uri} for the item or null.
+         * @return this
+         */
+        public Builder setMediaUri(@Nullable Uri mediaUri) {
+            mMediaUri = mediaUri;
+            return this;
+        }
+
+        public MediaDescription build() {
+            return new MediaDescription(mMediaId, mTitle, mSubtitle, mDescription, mIcon, mIconUri,
+                    mExtras, mMediaUri);
+        }
+    }
+}
diff --git a/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl b/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl
new file mode 100644
index 0000000..66ee483
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/MediaMetadata.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable MediaMetadata;
diff --git a/packages/MediaComponents/apex/java/android/media/MediaMetadata.java b/packages/MediaComponents/apex/java/android/media/MediaMetadata.java
new file mode 100644
index 0000000..33e6916
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/MediaMetadata.java
@@ -0,0 +1,943 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+import java.util.Objects;
+
+/**
+ * Contains metadata about an item, such as the title, artist, etc.
+ */
+public final class MediaMetadata implements Parcelable {
+    private static final String TAG = "MediaMetadata";
+
+    /**
+     * @hide
+     */
+    @StringDef(prefix = { "METADATA_KEY_" }, value = {
+            METADATA_KEY_TITLE,
+            METADATA_KEY_ARTIST,
+            METADATA_KEY_ALBUM,
+            METADATA_KEY_AUTHOR,
+            METADATA_KEY_WRITER,
+            METADATA_KEY_COMPOSER,
+            METADATA_KEY_COMPILATION,
+            METADATA_KEY_DATE,
+            METADATA_KEY_GENRE,
+            METADATA_KEY_ALBUM_ARTIST,
+            METADATA_KEY_ART_URI,
+            METADATA_KEY_ALBUM_ART_URI,
+            METADATA_KEY_DISPLAY_TITLE,
+            METADATA_KEY_DISPLAY_SUBTITLE,
+            METADATA_KEY_DISPLAY_DESCRIPTION,
+            METADATA_KEY_DISPLAY_ICON_URI,
+            METADATA_KEY_MEDIA_ID,
+            METADATA_KEY_MEDIA_URI,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TextKey {}
+
+    /**
+     * @hide
+     */
+    @StringDef(prefix = { "METADATA_KEY_" }, value = {
+            METADATA_KEY_DURATION,
+            METADATA_KEY_YEAR,
+            METADATA_KEY_TRACK_NUMBER,
+            METADATA_KEY_NUM_TRACKS,
+            METADATA_KEY_DISC_NUMBER,
+            METADATA_KEY_BT_FOLDER_TYPE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LongKey {}
+
+    /**
+     * @hide
+     */
+    @StringDef(prefix = { "METADATA_KEY_" }, value = {
+            METADATA_KEY_ART,
+            METADATA_KEY_ALBUM_ART,
+            METADATA_KEY_DISPLAY_ICON,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BitmapKey {}
+
+    /**
+     * @hide
+     */
+    @StringDef(prefix = { "METADATA_KEY_" }, value = {
+            METADATA_KEY_USER_RATING,
+            METADATA_KEY_RATING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RatingKey {}
+
+    /**
+     * The title of the media.
+     */
+    public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+
+    /**
+     * The artist of the media.
+     */
+    public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+
+    /**
+     * The duration of the media in ms. A negative duration indicates that the
+     * duration is unknown (or infinite).
+     */
+    public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+
+    /**
+     * The album title for the media.
+     */
+    public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+
+    /**
+     * The author of the media.
+     */
+    public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+
+    /**
+     * The writer of the media.
+     */
+    public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+
+    /**
+     * The composer of the media.
+     */
+    public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+
+    /**
+     * The compilation status of the media.
+     */
+    public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+
+    /**
+     * The date the media was created or published. The format is unspecified
+     * but RFC 3339 is recommended.
+     */
+    public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
+
+    /**
+     * The year the media was created or published as a long.
+     */
+    public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+
+    /**
+     * The genre of the media.
+     */
+    public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+
+    /**
+     * The track number for the media.
+     */
+    public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+
+    /**
+     * The number of tracks in the media's original source.
+     */
+    public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+
+    /**
+     * The disc number for the media's original source.
+     */
+    public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+
+    /**
+     * The artist for the album of the media's original source.
+     */
+    public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+
+    /**
+     * The artwork for the media as a {@link Bitmap}.
+     * <p>
+     * The artwork should be relatively small and may be scaled down by the
+     * system if it is too large. For higher resolution artwork
+     * {@link #METADATA_KEY_ART_URI} should be used instead.
+     */
+    public static final String METADATA_KEY_ART = "android.media.metadata.ART";
+
+    /**
+     * The artwork for the media as a Uri formatted String. The artwork can be
+     * loaded using a combination of {@link ContentResolver#openInputStream} and
+     * {@link BitmapFactory#decodeStream}.
+     * <p>
+     * For the best results, Uris should use the content:// style and support
+     * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through
+     * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
+     */
+    public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+
+    /**
+     * The artwork for the album of the media's original source as a
+     * {@link Bitmap}.
+     * <p>
+     * The artwork should be relatively small and may be scaled down by the
+     * system if it is too large. For higher resolution artwork
+     * {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
+     */
+    public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+
+    /**
+     * The artwork for the album of the media's original source as a Uri
+     * formatted String. The artwork can be loaded using a combination of
+     * {@link ContentResolver#openInputStream} and
+     * {@link BitmapFactory#decodeStream}.
+     * <p>
+     * For the best results, Uris should use the content:// style and support
+     * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through
+     * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
+     */
+    public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+
+    /**
+     * The user's rating for the media.
+     *
+     * @see Rating
+     */
+    public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+
+    /**
+     * The overall rating for the media.
+     *
+     * @see Rating
+     */
+    public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
+
+    /**
+     * A title that is suitable for display to the user. This will generally be
+     * the same as {@link #METADATA_KEY_TITLE} but may differ for some formats.
+     * When displaying media described by this metadata this should be preferred
+     * if present.
+     */
+    public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+
+    /**
+     * A subtitle that is suitable for display to the user. When displaying a
+     * second line for media described by this metadata this should be preferred
+     * to other fields if present.
+     */
+    public static final String METADATA_KEY_DISPLAY_SUBTITLE
+            = "android.media.metadata.DISPLAY_SUBTITLE";
+
+    /**
+     * A description that is suitable for display to the user. When displaying
+     * more information for media described by this metadata this should be
+     * preferred to other fields if present.
+     */
+    public static final String METADATA_KEY_DISPLAY_DESCRIPTION
+            = "android.media.metadata.DISPLAY_DESCRIPTION";
+
+    /**
+     * An icon or thumbnail that is suitable for display to the user. When
+     * displaying an icon for media described by this metadata this should be
+     * preferred to other fields if present. This must be a {@link Bitmap}.
+     * <p>
+     * The icon should be relatively small and may be scaled down by the system
+     * if it is too large. For higher resolution artwork
+     * {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
+     */
+    public static final String METADATA_KEY_DISPLAY_ICON
+            = "android.media.metadata.DISPLAY_ICON";
+
+    /**
+     * A Uri formatted String for an icon or thumbnail that is suitable for
+     * display to the user. When displaying more information for media described
+     * by this metadata the display description should be preferred to other
+     * fields when present. The icon can be loaded using a combination of
+     * {@link ContentResolver#openInputStream} and
+     * {@link BitmapFactory#decodeStream}.
+     * <p>
+     * For the best results, Uris should use the content:// style and support
+     * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through
+     * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
+     */
+    public static final String METADATA_KEY_DISPLAY_ICON_URI
+            = "android.media.metadata.DISPLAY_ICON_URI";
+
+    /**
+     * A String key for identifying the content. This value is specific to the
+     * service providing the content. If used, this should be a persistent
+     * unique key for the underlying content. It may be used with
+     * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
+     * to initiate playback when provided by a {@link MediaBrowser} connected to
+     * the same app.
+     */
+    public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+
+    /**
+     * A Uri formatted String representing the content. This value is specific to the
+     * service providing the content. It may be used with
+     * {@link MediaController.TransportControls#playFromUri(Uri, Bundle)}
+     * to initiate playback when provided by a {@link MediaBrowser} connected to
+     * the same app.
+     */
+    public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+
+    /**
+     * The bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth
+     * AVRCP 1.5. It should be one of the following:
+     * <ul>
+     * <li>{@link MediaDescription#BT_FOLDER_TYPE_MIXED}</li>
+     * <li>{@link MediaDescription#BT_FOLDER_TYPE_TITLES}</li>
+     * <li>{@link MediaDescription#BT_FOLDER_TYPE_ALBUMS}</li>
+     * <li>{@link MediaDescription#BT_FOLDER_TYPE_ARTISTS}</li>
+     * <li>{@link MediaDescription#BT_FOLDER_TYPE_GENRES}</li>
+     * <li>{@link MediaDescription#BT_FOLDER_TYPE_PLAYLISTS}</li>
+     * <li>{@link MediaDescription#BT_FOLDER_TYPE_YEARS}</li>
+     * </ul>
+     */
+    public static final String METADATA_KEY_BT_FOLDER_TYPE
+            = "android.media.metadata.BT_FOLDER_TYPE";
+
+    private static final @TextKey String[] PREFERRED_DESCRIPTION_ORDER = {
+            METADATA_KEY_TITLE,
+            METADATA_KEY_ARTIST,
+            METADATA_KEY_ALBUM,
+            METADATA_KEY_ALBUM_ARTIST,
+            METADATA_KEY_WRITER,
+            METADATA_KEY_AUTHOR,
+            METADATA_KEY_COMPOSER
+    };
+
+    private static final @BitmapKey String[] PREFERRED_BITMAP_ORDER = {
+            METADATA_KEY_DISPLAY_ICON,
+            METADATA_KEY_ART,
+            METADATA_KEY_ALBUM_ART
+    };
+
+    private static final @TextKey String[] PREFERRED_URI_ORDER = {
+            METADATA_KEY_DISPLAY_ICON_URI,
+            METADATA_KEY_ART_URI,
+            METADATA_KEY_ALBUM_ART_URI
+    };
+
+    private static final int METADATA_TYPE_INVALID = -1;
+    private static final int METADATA_TYPE_LONG = 0;
+    private static final int METADATA_TYPE_TEXT = 1;
+    private static final int METADATA_TYPE_BITMAP = 2;
+    private static final int METADATA_TYPE_RATING = 3;
+    private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
+
+    static {
+        METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
+        METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT);
+    }
+
+    private static final SparseArray<String> EDITOR_KEY_MAPPING;
+
+    static {
+        EDITOR_KEY_MAPPING = new SparseArray<String>();
+        EDITOR_KEY_MAPPING.put(MediaMetadataEditor.BITMAP_KEY_ARTWORK, METADATA_KEY_ART);
+        EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_OTHERS, METADATA_KEY_RATING);
+        EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_USER, METADATA_KEY_USER_RATING);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_KEY_ALBUM);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
+                METADATA_KEY_ALBUM_ARTIST);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_KEY_ARTIST);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_KEY_AUTHOR);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
+                METADATA_KEY_TRACK_NUMBER);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_KEY_COMPOSER);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPILATION,
+                METADATA_KEY_COMPILATION);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_KEY_DATE);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
+                METADATA_KEY_DISC_NUMBER);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_KEY_DURATION);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_KEY_GENRE);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS,
+                METADATA_KEY_NUM_TRACKS);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_KEY_TITLE);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_KEY_WRITER);
+        EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_KEY_YEAR);
+    }
+
+    private final Bundle mBundle;
+    private MediaDescription mDescription;
+
+    private MediaMetadata(Bundle bundle) {
+        mBundle = new Bundle(bundle);
+    }
+
+    private MediaMetadata(Parcel in) {
+        //TODO(b/119789387): Resolve hidden API usage: Bundle#setDefusable
+        //mBundle = Bundle.setDefusable(in.readBundle(), true);
+        mBundle = new Bundle();  //TODO:remove this.
+    }
+
+    /**
+     * Returns true if the given key is contained in the metadata
+     *
+     * @param key a String key
+     * @return true if the key exists in this metadata, false otherwise
+     */
+    public boolean containsKey(String key) {
+        return mBundle.containsKey(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if no mapping of
+     * the desired type exists for the given key or a null value is explicitly
+     * associated with the key.
+     *
+     * @param key The key the value is stored under
+     * @return a CharSequence value, or null
+     */
+    public CharSequence getText(@TextKey String key) {
+        return mBundle.getCharSequence(key);
+    }
+
+    /**
+     * Returns the text value associated with the given key as a String, or null
+     * if no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key. This is equivalent to
+     * calling {@link #getText getText().toString()} if the value is not null.
+     *
+     * @param key The key the value is stored under
+     * @return a String value, or null
+     */
+    public String getString(@TextKey String key) {
+        CharSequence text = getText(key);
+        if (text != null) {
+            return text.toString();
+        }
+        return null;
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0L if no long exists
+     * for the given key.
+     *
+     * @param key The key the value is stored under
+     * @return a long value
+     */
+    public long getLong(@LongKey String key) {
+        return mBundle.getLong(key, 0);
+    }
+
+    /**
+     * Returns a {@link Rating} for the given key or null if no rating exists
+     * for the given key.
+     *
+     * @param key The key the value is stored under
+     * @return A {@link Rating} or null
+     */
+    public Rating getRating(@RatingKey String key) {
+        Rating rating = null;
+        try {
+            rating = mBundle.getParcelable(key);
+        } catch (Exception e) {
+            // ignore, value was not a bitmap
+            Log.w(TAG, "Failed to retrieve a key as Rating.", e);
+        }
+        return rating;
+    }
+
+    /**
+     * Returns a {@link Bitmap} for the given key or null if no bitmap exists
+     * for the given key.
+     *
+     * @param key The key the value is stored under
+     * @return A {@link Bitmap} or null
+     */
+    public Bitmap getBitmap(@BitmapKey String key) {
+        Bitmap bmp = null;
+        try {
+            bmp = mBundle.getParcelable(key);
+        } catch (Exception e) {
+            // ignore, value was not a bitmap
+            Log.w(TAG, "Failed to retrieve a key as Bitmap.", e);
+        }
+        return bmp;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBundle(mBundle);
+    }
+
+    /**
+     * Returns the number of fields in this metadata.
+     *
+     * @return The number of fields in the metadata.
+     */
+    public int size() {
+        return mBundle.size();
+    }
+
+    /**
+     * Returns a Set containing the Strings used as keys in this metadata.
+     *
+     * @return a Set of String keys
+     */
+    public Set<String> keySet() {
+        return mBundle.keySet();
+    }
+
+    /**
+     * Returns a simple description of this metadata for display purposes.
+     *
+     * @return A simple description of this metadata.
+     */
+    public @NonNull MediaDescription getDescription() {
+        if (mDescription != null) {
+            return mDescription;
+        }
+
+        String mediaId = getString(METADATA_KEY_MEDIA_ID);
+
+        CharSequence[] text = new CharSequence[3];
+        Bitmap icon = null;
+        Uri iconUri = null;
+
+        // First handle the case where display data is set already
+        CharSequence displayText = getText(METADATA_KEY_DISPLAY_TITLE);
+        if (!TextUtils.isEmpty(displayText)) {
+            // If they have a display title use only display data, otherwise use
+            // our best bets
+            text[0] = displayText;
+            text[1] = getText(METADATA_KEY_DISPLAY_SUBTITLE);
+            text[2] = getText(METADATA_KEY_DISPLAY_DESCRIPTION);
+        } else {
+            // Use whatever fields we can
+            int textIndex = 0;
+            int keyIndex = 0;
+            while (textIndex < text.length && keyIndex < PREFERRED_DESCRIPTION_ORDER.length) {
+                CharSequence next = getText(PREFERRED_DESCRIPTION_ORDER[keyIndex++]);
+                if (!TextUtils.isEmpty(next)) {
+                    // Fill in the next empty bit of text
+                    text[textIndex++] = next;
+                }
+            }
+        }
+
+        // Get the best art bitmap we can find
+        for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) {
+            Bitmap next = getBitmap(PREFERRED_BITMAP_ORDER[i]);
+            if (next != null) {
+                icon = next;
+                break;
+            }
+        }
+
+        // Get the best Uri we can find
+        for (int i = 0; i < PREFERRED_URI_ORDER.length; i++) {
+            String next = getString(PREFERRED_URI_ORDER[i]);
+            if (!TextUtils.isEmpty(next)) {
+                iconUri = Uri.parse(next);
+                break;
+            }
+        }
+
+        Uri mediaUri = null;
+        String mediaUriStr = getString(METADATA_KEY_MEDIA_URI);
+        if (!TextUtils.isEmpty(mediaUriStr)) {
+            mediaUri = Uri.parse(mediaUriStr);
+        }
+
+        MediaDescription.Builder bob = new MediaDescription.Builder();
+        bob.setMediaId(mediaId);
+        bob.setTitle(text[0]);
+        bob.setSubtitle(text[1]);
+        bob.setDescription(text[2]);
+        bob.setIconBitmap(icon);
+        bob.setIconUri(iconUri);
+        bob.setMediaUri(mediaUri);
+        if (mBundle.containsKey(METADATA_KEY_BT_FOLDER_TYPE)) {
+            Bundle bundle = new Bundle();
+            bundle.putLong(MediaDescription.EXTRA_BT_FOLDER_TYPE,
+                    getLong(METADATA_KEY_BT_FOLDER_TYPE));
+            bob.setExtras(bundle);
+        }
+        mDescription = bob.build();
+
+        return mDescription;
+    }
+
+    /**
+     * Helper for getting the String key used by {@link MediaMetadata} from the
+     * integer key that {@link MediaMetadataEditor} uses.
+     *
+     * @param editorKey The key used by the editor
+     * @return The key used by this class or null if no mapping exists
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static String getKeyFromMetadataEditorKey(int editorKey) {
+        return EDITOR_KEY_MAPPING.get(editorKey, null);
+    }
+
+    public static final Parcelable.Creator<MediaMetadata> CREATOR =
+            new Parcelable.Creator<MediaMetadata>() {
+                @Override
+                public MediaMetadata createFromParcel(Parcel in) {
+                    return new MediaMetadata(in);
+                }
+
+                @Override
+                public MediaMetadata[] newArray(int size) {
+                    return new MediaMetadata[size];
+                }
+            };
+
+    /**
+     * Compares the contents of this object to another MediaMetadata object. It
+     * does not compare Bitmaps and Ratings as the media player can choose to
+     * forgo these fields depending on how you retrieve the MediaMetadata.
+     *
+     * @param o The Metadata object to compare this object against
+     * @return Whether or not the two objects have matching fields (excluding
+     * Bitmaps and Ratings)
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof MediaMetadata)) {
+            return false;
+        }
+
+        final MediaMetadata m = (MediaMetadata) o;
+
+        for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) {
+            String key = METADATA_KEYS_TYPE.keyAt(i);
+            switch (METADATA_KEYS_TYPE.valueAt(i)) {
+                case METADATA_TYPE_TEXT:
+                    if (!Objects.equals(getString(key), m.getString(key))) {
+                        return false;
+                    }
+                    break;
+                case METADATA_TYPE_LONG:
+                    if (getLong(key) != m.getLong(key)) {
+                        return false;
+                    }
+                    break;
+                default:
+                    // Ignore ratings and bitmaps when comparing
+                    break;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = 17;
+
+        for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) {
+            String key = METADATA_KEYS_TYPE.keyAt(i);
+            switch (METADATA_KEYS_TYPE.valueAt(i)) {
+                case METADATA_TYPE_TEXT:
+                    hashCode = 31 * hashCode + Objects.hash(getString(key));
+                    break;
+                case METADATA_TYPE_LONG:
+                    hashCode = 31 * hashCode + Long.hashCode(getLong(key));
+                    break;
+                default:
+                    // Ignore ratings and bitmaps when comparing
+                    break;
+            }
+        }
+
+        return hashCode;
+    }
+
+    /**
+     * Use to build MediaMetadata objects. The system defined metadata keys must
+     * use the appropriate data type.
+     */
+    public static final class Builder {
+        private final Bundle mBundle;
+
+        /**
+         * Create an empty Builder. Any field that should be included in the
+         * {@link MediaMetadata} must be added.
+         */
+        public Builder() {
+            mBundle = new Bundle();
+        }
+
+        /**
+         * Create a Builder using a {@link MediaMetadata} instance to set the
+         * initial values. All fields in the source metadata will be included in
+         * the new metadata. Fields can be overwritten by adding the same key.
+         *
+         * @param source
+         */
+        public Builder(MediaMetadata source) {
+            mBundle = new Bundle(source.mBundle);
+        }
+
+        /**
+         * Create a Builder using a {@link MediaMetadata} instance to set
+         * initial values, but replace bitmaps with a scaled down copy if they
+         * are larger than maxBitmapSize.
+         *
+         * @param source The original metadata to copy.
+         * @param maxBitmapSize The maximum height/width for bitmaps contained
+         *            in the metadata.
+         * @hide
+         */
+        public Builder(MediaMetadata source, int maxBitmapSize) {
+            this(source);
+            for (String key : mBundle.keySet()) {
+                Object value = mBundle.get(key);
+                if (value != null && value instanceof Bitmap) {
+                    Bitmap bmp = (Bitmap) value;
+                    if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
+                        putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
+                    }
+                }
+            }
+        }
+
+        /**
+         * Put a CharSequence value into the metadata. Custom keys may be used,
+         * but if the METADATA_KEYs defined in this class are used they may only
+         * be one of the following:
+         * <ul>
+         * <li>{@link #METADATA_KEY_TITLE}</li>
+         * <li>{@link #METADATA_KEY_ARTIST}</li>
+         * <li>{@link #METADATA_KEY_ALBUM}</li>
+         * <li>{@link #METADATA_KEY_AUTHOR}</li>
+         * <li>{@link #METADATA_KEY_WRITER}</li>
+         * <li>{@link #METADATA_KEY_COMPOSER}</li>
+         * <li>{@link #METADATA_KEY_DATE}</li>
+         * <li>{@link #METADATA_KEY_GENRE}</li>
+         * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
+         * <li>{@link #METADATA_KEY_ART_URI}</li>
+         * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
+         * </ul>
+         *
+         * @param key The key for referencing this value
+         * @param value The CharSequence value to store
+         * @return The Builder to allow chaining
+         */
+        public Builder putText(@TextKey String key, CharSequence value) {
+            if (METADATA_KEYS_TYPE.containsKey(key)) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
+                    throw new IllegalArgumentException("The " + key
+                            + " key cannot be used to put a CharSequence");
+                }
+            }
+            mBundle.putCharSequence(key, value);
+            return this;
+        }
+
+        /**
+         * Put a String value into the metadata. Custom keys may be used, but if
+         * the METADATA_KEYs defined in this class are used they may only be one
+         * of the following:
+         * <ul>
+         * <li>{@link #METADATA_KEY_TITLE}</li>
+         * <li>{@link #METADATA_KEY_ARTIST}</li>
+         * <li>{@link #METADATA_KEY_ALBUM}</li>
+         * <li>{@link #METADATA_KEY_AUTHOR}</li>
+         * <li>{@link #METADATA_KEY_WRITER}</li>
+         * <li>{@link #METADATA_KEY_COMPOSER}</li>
+         * <li>{@link #METADATA_KEY_DATE}</li>
+         * <li>{@link #METADATA_KEY_GENRE}</li>
+         * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
+         * <li>{@link #METADATA_KEY_ART_URI}</li>
+         * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
+         * </ul>
+         * <p>
+         * Uris for artwork should use the content:// style and support
+         * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork
+         * through {@link ContentResolver#openTypedAssetFileDescriptor(Uri,
+         * String, Bundle)}.
+         *
+         * @param key The key for referencing this value
+         * @param value The String value to store
+         * @return The Builder to allow chaining
+         */
+        public Builder putString(@TextKey String key, String value) {
+            if (METADATA_KEYS_TYPE.containsKey(key)) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
+                    throw new IllegalArgumentException("The " + key
+                            + " key cannot be used to put a String");
+                }
+            }
+            mBundle.putCharSequence(key, value);
+            return this;
+        }
+
+        /**
+         * Put a long value into the metadata. Custom keys may be used, but if
+         * the METADATA_KEYs defined in this class are used they may only be one
+         * of the following:
+         * <ul>
+         * <li>{@link #METADATA_KEY_DURATION}</li>
+         * <li>{@link #METADATA_KEY_TRACK_NUMBER}</li>
+         * <li>{@link #METADATA_KEY_NUM_TRACKS}</li>
+         * <li>{@link #METADATA_KEY_DISC_NUMBER}</li>
+         * <li>{@link #METADATA_KEY_YEAR}</li>
+         * </ul>
+         *
+         * @param key The key for referencing this value
+         * @param value The long value to store
+         * @return The Builder to allow chaining
+         */
+        public Builder putLong(@LongKey String key, long value) {
+            if (METADATA_KEYS_TYPE.containsKey(key)) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) {
+                    throw new IllegalArgumentException("The " + key
+                            + " key cannot be used to put a long");
+                }
+            }
+            mBundle.putLong(key, value);
+            return this;
+        }
+
+        /**
+         * Put a {@link Rating} into the metadata. Custom keys may be used, but
+         * if the METADATA_KEYs defined in this class are used they may only be
+         * one of the following:
+         * <ul>
+         * <li>{@link #METADATA_KEY_RATING}</li>
+         * <li>{@link #METADATA_KEY_USER_RATING}</li>
+         * </ul>
+         *
+         * @param key The key for referencing this value
+         * @param value The Rating value to store
+         * @return The Builder to allow chaining
+         */
+        public Builder putRating(@RatingKey String key, Rating value) {
+            if (METADATA_KEYS_TYPE.containsKey(key)) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) {
+                    throw new IllegalArgumentException("The " + key
+                            + " key cannot be used to put a Rating");
+                }
+            }
+            mBundle.putParcelable(key, value);
+            return this;
+        }
+
+        /**
+         * Put a {@link Bitmap} into the metadata. Custom keys may be used, but
+         * if the METADATA_KEYs defined in this class are used they may only be
+         * one of the following:
+         * <ul>
+         * <li>{@link #METADATA_KEY_ART}</li>
+         * <li>{@link #METADATA_KEY_ALBUM_ART}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_ICON}</li>
+         * </ul>
+         * <p>
+         * Large bitmaps may be scaled down by the system when
+         * {@link android.media.session.MediaSession#setMetadata} is called.
+         * To pass full resolution images {@link Uri Uris} should be used with
+         * {@link #putString}.
+         *
+         * @param key The key for referencing this value
+         * @param value The Bitmap to store
+         * @return The Builder to allow chaining
+         */
+        public Builder putBitmap(@BitmapKey String key, Bitmap value) {
+            if (METADATA_KEYS_TYPE.containsKey(key)) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
+                    throw new IllegalArgumentException("The " + key
+                            + " key cannot be used to put a Bitmap");
+                }
+            }
+            mBundle.putParcelable(key, value);
+            return this;
+        }
+
+        /**
+         * Creates a {@link MediaMetadata} instance with the specified fields.
+         *
+         * @return The new MediaMetadata instance
+         */
+        public MediaMetadata build() {
+            return new MediaMetadata(mBundle);
+        }
+
+        private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
+            float maxSizeF = maxSize;
+            float widthScale = maxSizeF / bmp.getWidth();
+            float heightScale = maxSizeF / bmp.getHeight();
+            float scale = Math.min(widthScale, heightScale);
+            int height = (int) (bmp.getHeight() * scale);
+            int width = (int) (bmp.getWidth() * scale);
+            return Bitmap.createScaledBitmap(bmp, width, height, true);
+        }
+    }
+}
diff --git a/packages/MediaComponents/apex/java/android/media/Rating.aidl b/packages/MediaComponents/apex/java/android/media/Rating.aidl
new file mode 100644
index 0000000..1dc336a
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/Rating.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+parcelable Rating;
diff --git a/packages/MediaComponents/apex/java/android/media/Rating.java b/packages/MediaComponents/apex/java/android/media/Rating.java
new file mode 100644
index 0000000..04d5364
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/Rating.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A class to encapsulate rating information used as content metadata.
+ * A rating is defined by its rating style (see {@link #RATING_HEART},
+ * {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
+ * {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may
+ * be defined as "unrated"), both of which are defined when the rating instance is constructed
+ * through one of the factory methods.
+ */
+public final class Rating implements Parcelable {
+    private final static String TAG = "Rating";
+
+    /**
+     * @hide
+     */
+    @IntDef({RATING_NONE, RATING_HEART, RATING_THUMB_UP_DOWN, RATING_3_STARS, RATING_4_STARS,
+            RATING_5_STARS, RATING_PERCENTAGE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Style {}
+
+    /**
+     * @hide
+     */
+    @IntDef({RATING_3_STARS, RATING_4_STARS, RATING_5_STARS})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StarStyle {}
+
+    /**
+     * Indicates a rating style is not supported. A Rating will never have this
+     * type, but can be used by other classes to indicate they do not support
+     * Rating.
+     */
+    public final static int RATING_NONE = 0;
+
+    /**
+     * A rating style with a single degree of rating, "heart" vs "no heart". Can be used to
+     * indicate the content referred to is a favorite (or not).
+     */
+    public final static int RATING_HEART = 1;
+
+    /**
+     * A rating style for "thumb up" vs "thumb down".
+     */
+    public final static int RATING_THUMB_UP_DOWN = 2;
+
+    /**
+     * A rating style with 0 to 3 stars.
+     */
+    public final static int RATING_3_STARS = 3;
+
+    /**
+     * A rating style with 0 to 4 stars.
+     */
+    public final static int RATING_4_STARS = 4;
+
+    /**
+     * A rating style with 0 to 5 stars.
+     */
+    public final static int RATING_5_STARS = 5;
+
+    /**
+     * A rating style expressed as a percentage.
+     */
+    public final static int RATING_PERCENTAGE = 6;
+
+    private final static float RATING_NOT_RATED = -1.0f;
+
+    private final int mRatingStyle;
+
+    private final float mRatingValue;
+
+    private Rating(@Style int ratingStyle, float rating) {
+        mRatingStyle = ratingStyle;
+        mRatingValue = rating;
+    }
+
+    @Override
+    public String toString() {
+        return "Rating:style=" + mRatingStyle + " rating="
+                + (mRatingValue < 0.0f ? "unrated" : String.valueOf(mRatingValue));
+    }
+
+    @Override
+    public int describeContents() {
+        return mRatingStyle;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRatingStyle);
+        dest.writeFloat(mRatingValue);
+    }
+
+    public static final Parcelable.Creator<Rating> CREATOR
+            = new Parcelable.Creator<Rating>() {
+        /**
+         * Rebuilds a Rating previously stored with writeToParcel().
+         * @param p    Parcel object to read the Rating from
+         * @return a new Rating created from the data in the parcel
+         */
+        @Override
+        public Rating createFromParcel(Parcel p) {
+            return new Rating(p.readInt(), p.readFloat());
+        }
+
+        @Override
+        public Rating[] newArray(int size) {
+            return new Rating[size];
+        }
+    };
+
+    /**
+     * Return a Rating instance with no rating.
+     * Create and return a new Rating instance with no rating known for the given
+     * rating style.
+     * @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
+     *    {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
+     *    or {@link #RATING_PERCENTAGE}.
+     * @return null if an invalid rating style is passed, a new Rating instance otherwise.
+     */
+    public static Rating newUnratedRating(@Style int ratingStyle) {
+        switch(ratingStyle) {
+            case RATING_HEART:
+            case RATING_THUMB_UP_DOWN:
+            case RATING_3_STARS:
+            case RATING_4_STARS:
+            case RATING_5_STARS:
+            case RATING_PERCENTAGE:
+                return new Rating(ratingStyle, RATING_NOT_RATED);
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Return a Rating instance with a heart-based rating.
+     * Create and return a new Rating instance with a rating style of {@link #RATING_HEART},
+     * and a heart-based rating.
+     * @param hasHeart true for a "heart selected" rating, false for "heart unselected".
+     * @return a new Rating instance.
+     */
+    public static Rating newHeartRating(boolean hasHeart) {
+        return new Rating(RATING_HEART, hasHeart ? 1.0f : 0.0f);
+    }
+
+    /**
+     * Return a Rating instance with a thumb-based rating.
+     * Create and return a new Rating instance with a {@link #RATING_THUMB_UP_DOWN}
+     * rating style, and a "thumb up" or "thumb down" rating.
+     * @param thumbIsUp true for a "thumb up" rating, false for "thumb down".
+     * @return a new Rating instance.
+     */
+    public static Rating newThumbRating(boolean thumbIsUp) {
+        return new Rating(RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f);
+    }
+
+    /**
+     * Return a Rating instance with a star-based rating.
+     * Create and return a new Rating instance with one of the star-base rating styles
+     * and the given integer or fractional number of stars. Non integer values can for instance
+     * be used to represent an average rating value, which might not be an integer number of stars.
+     * @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
+     *     {@link #RATING_5_STARS}.
+     * @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to
+     *     the rating style.
+     * @return null if the rating style is invalid, or the rating is out of range,
+     *     a new Rating instance otherwise.
+     */
+    public static Rating newStarRating(@StarStyle int starRatingStyle, float starRating) {
+        float maxRating = -1.0f;
+        switch(starRatingStyle) {
+            case RATING_3_STARS:
+                maxRating = 3.0f;
+                break;
+            case RATING_4_STARS:
+                maxRating = 4.0f;
+                break;
+            case RATING_5_STARS:
+                maxRating = 5.0f;
+                break;
+            default:
+                Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating");
+                        return null;
+        }
+        if ((starRating < 0.0f) || (starRating > maxRating)) {
+            Log.e(TAG, "Trying to set out of range star-based rating");
+            return null;
+        }
+        return new Rating(starRatingStyle, starRating);
+    }
+
+    /**
+     * Return a Rating instance with a percentage-based rating.
+     * Create and return a new Rating instance with a {@link #RATING_PERCENTAGE}
+     * rating style, and a rating of the given percentage.
+     * @param percent the value of the rating
+     * @return null if the rating is out of range, a new Rating instance otherwise.
+     */
+    public static Rating newPercentageRating(float percent) {
+        if ((percent < 0.0f) || (percent > 100.0f)) {
+            Log.e(TAG, "Invalid percentage-based rating value");
+            return null;
+        } else {
+            return new Rating(RATING_PERCENTAGE, percent);
+        }
+    }
+
+    /**
+     * Return whether there is a rating value available.
+     * @return true if the instance was not created with {@link #newUnratedRating(int)}.
+     */
+    public boolean isRated() {
+        return mRatingValue >= 0.0f;
+    }
+
+    /**
+     * Return the rating style.
+     * @return one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN},
+     *    {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
+     *    or {@link #RATING_PERCENTAGE}.
+     */
+    @Style
+    public int getRatingStyle() {
+        return mRatingStyle;
+    }
+
+    /**
+     * Return whether the rating is "heart selected".
+     * @return true if the rating is "heart selected", false if the rating is "heart unselected",
+     *    if the rating style is not {@link #RATING_HEART} or if it is unrated.
+     */
+    public boolean hasHeart() {
+        if (mRatingStyle != RATING_HEART) {
+            return false;
+        } else {
+            return (mRatingValue == 1.0f);
+        }
+    }
+
+    /**
+     * Return whether the rating is "thumb up".
+     * @return true if the rating is "thumb up", false if the rating is "thumb down",
+     *    if the rating style is not {@link #RATING_THUMB_UP_DOWN} or if it is unrated.
+     */
+    public boolean isThumbUp() {
+        if (mRatingStyle != RATING_THUMB_UP_DOWN) {
+            return false;
+        } else {
+            return (mRatingValue == 1.0f);
+        }
+    }
+
+    /**
+     * Return the star-based rating value.
+     * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
+     *    not star-based, or if it is unrated.
+     */
+    public float getStarRating() {
+        switch (mRatingStyle) {
+            case RATING_3_STARS:
+            case RATING_4_STARS:
+            case RATING_5_STARS:
+                if (isRated()) {
+                    return mRatingValue;
+                }
+            default:
+                return -1.0f;
+        }
+    }
+
+    /**
+     * Return the percentage-based rating value.
+     * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is
+     *    not percentage-based, or if it is unrated.
+     */
+    public float getPercentRating() {
+        if ((mRatingStyle != RATING_PERCENTAGE) || !isRated()) {
+            return -1.0f;
+        } else {
+            return mRatingValue;
+        }
+    }
+}
diff --git a/packages/MediaComponents/apex/java/android/media/VolumeProvider.java b/packages/MediaComponents/apex/java/android/media/VolumeProvider.java
new file mode 100644
index 0000000..1c017c5
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/VolumeProvider.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+import android.annotation.IntDef;
+import android.media.session.MediaSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Handles requests to adjust or set the volume on a session. This is also used
+ * to push volume updates back to the session. The provider must call
+ * {@link #setCurrentVolume(int)} each time the volume being provided changes.
+ * <p>
+ * You can set a volume provider on a session by calling
+ * {@link MediaSession#setPlaybackToRemote}.
+ */
+public abstract class VolumeProvider {
+
+    /**
+     * @hide
+     */
+    @IntDef({VOLUME_CONTROL_FIXED, VOLUME_CONTROL_RELATIVE, VOLUME_CONTROL_ABSOLUTE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ControlType {}
+
+    /**
+     * The volume is fixed and can not be modified. Requests to change volume
+     * should be ignored.
+     */
+    public static final int VOLUME_CONTROL_FIXED = 0;
+
+    /**
+     * The volume control uses relative adjustment via
+     * {@link #onAdjustVolume(int)}. Attempts to set the volume to a specific
+     * value should be ignored.
+     */
+    public static final int VOLUME_CONTROL_RELATIVE = 1;
+
+    /**
+     * The volume control uses an absolute value. It may be adjusted using
+     * {@link #onAdjustVolume(int)} or set directly using
+     * {@link #onSetVolumeTo(int)}.
+     */
+    public static final int VOLUME_CONTROL_ABSOLUTE = 2;
+
+    private final int mControlType;
+    private final int mMaxVolume;
+    private int mCurrentVolume;
+    private Callback mCallback;
+
+    /**
+     * Create a new volume provider for handling volume events. You must specify
+     * the type of volume control, the maximum volume that can be used, and the
+     * current volume on the output.
+     *
+     * @param volumeControl The method for controlling volume that is used by
+     *            this provider.
+     * @param maxVolume The maximum allowed volume.
+     * @param currentVolume The current volume on the output.
+     */
+    public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume) {
+        mControlType = volumeControl;
+        mMaxVolume = maxVolume;
+        mCurrentVolume = currentVolume;
+    }
+
+    /**
+     * Get the volume control type that this volume provider uses.
+     *
+     * @return The volume control type for this volume provider
+     */
+    @ControlType
+    public final int getVolumeControl() {
+        return mControlType;
+    }
+
+    /**
+     * Get the maximum volume this provider allows.
+     *
+     * @return The max allowed volume.
+     */
+    public final int getMaxVolume() {
+        return mMaxVolume;
+    }
+
+    /**
+     * Gets the current volume. This will be the last value set by
+     * {@link #setCurrentVolume(int)}.
+     *
+     * @return The current volume.
+     */
+    public final int getCurrentVolume() {
+        return mCurrentVolume;
+    }
+
+    /**
+     * Notify the system that the current volume has been changed. This must be
+     * called every time the volume changes to ensure it is displayed properly.
+     *
+     * @param currentVolume The current volume on the output.
+     */
+    public final void setCurrentVolume(int currentVolume) {
+        mCurrentVolume = currentVolume;
+        if (mCallback != null) {
+            mCallback.onVolumeChanged(this);
+        }
+    }
+
+    /**
+     * Override to handle requests to set the volume of the current output.
+     * After the volume has been modified {@link #setCurrentVolume} must be
+     * called to notify the system.
+     *
+     * @param volume The volume to set the output to.
+     */
+    public void onSetVolumeTo(int volume) {
+    }
+
+    /**
+     * Override to handle requests to adjust the volume of the current output.
+     * Direction will be one of {@link AudioManager#ADJUST_LOWER},
+     * {@link AudioManager#ADJUST_RAISE}, {@link AudioManager#ADJUST_SAME}.
+     * After the volume has been modified {@link #setCurrentVolume} must be
+     * called to notify the system.
+     *
+     * @param direction The direction to change the volume in.
+     */
+    public void onAdjustVolume(int direction) {
+    }
+
+    /**
+     * Sets a callback to receive volume changes.
+     * @hide
+     */
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    /**
+     * Listens for changes to the volume.
+     * @hide
+     */
+    public static abstract class Callback {
+        public abstract void onVolumeChanged(VolumeProvider volumeProvider);
+    }
+}
diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl
new file mode 100644
index 0000000..782e094
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media.browse;
+
+parcelable MediaBrowser.MediaItem;
\ No newline at end of file
diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java
new file mode 100644
index 0000000..4e091ad
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowser.java
@@ -0,0 +1,1177 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.browse;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+//import android.content.pm.ParceledListSlice;
+import android.media.MediaDescription;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.service.media.IMediaBrowserService;
+import android.service.media.IMediaBrowserServiceCallbacks;
+import android.service.media.MediaBrowserService;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+
+/**
+ * Browses media content offered by a link MediaBrowserService.
+ * <p>
+ * This object is not thread-safe. All calls should happen on the thread on which the browser
+ * was constructed.
+ * </p>
+ * <h3>Standard Extra Data</h3>
+ *
+ * <p>These are the current standard fields that can be used as extra data via
+ * {@link #subscribe(String, Bundle, SubscriptionCallback)},
+ * {@link #unsubscribe(String, SubscriptionCallback)}, and
+ * {@link SubscriptionCallback#onChildrenLoaded(String, List, Bundle)}.
+ *
+ * <ul>
+ *     <li> {@link #EXTRA_PAGE}
+ *     <li> {@link #EXTRA_PAGE_SIZE}
+ * </ul>
+ */
+public final class MediaBrowser {
+    private static final String TAG = "MediaBrowser";
+    private static final boolean DBG = false;
+
+    /**
+     * Used as an int extra field to denote the page number to subscribe.
+     * The value of {@code EXTRA_PAGE} should be greater than or equal to 0.
+     *
+     * @see #EXTRA_PAGE_SIZE
+     */
+    public static final String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+
+    /**
+     * Used as an int extra field to denote the number of media items in a page.
+     * The value of {@code EXTRA_PAGE_SIZE} should be greater than or equal to 1.
+     *
+     * @see #EXTRA_PAGE
+     */
+    public static final String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
+
+    private static final int CONNECT_STATE_DISCONNECTING = 0;
+    private static final int CONNECT_STATE_DISCONNECTED = 1;
+    private static final int CONNECT_STATE_CONNECTING = 2;
+    private static final int CONNECT_STATE_CONNECTED = 3;
+    private static final int CONNECT_STATE_SUSPENDED = 4;
+
+    private final Context mContext;
+    private final ComponentName mServiceComponent;
+    private final ConnectionCallback mCallback;
+    private final Bundle mRootHints;
+    private final Handler mHandler = new Handler();
+    private final ArrayMap<String, Subscription> mSubscriptions = new ArrayMap<>();
+
+    private volatile int mState = CONNECT_STATE_DISCONNECTED;
+    private volatile String mRootId;
+    private volatile MediaSession.Token mMediaSessionToken;
+    private volatile Bundle mExtras;
+
+    private MediaServiceConnection mServiceConnection;
+    private IMediaBrowserService mServiceBinder;
+    private IMediaBrowserServiceCallbacks mServiceCallbacks;
+
+    /**
+     * Creates a media browser for the specified media browser service.
+     *
+     * @param context The context.
+     * @param serviceComponent The component name of the media browser service.
+     * @param callback The connection callback.
+     * @param rootHints An optional bundle of service-specific arguments to send
+     * to the media browser service when connecting and retrieving the root id
+     * for browsing, or null if none. The contents of this bundle may affect
+     * the information returned when browsing.
+     * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_RECENT
+     * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
+     * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
+     */
+    public MediaBrowser(Context context, ComponentName serviceComponent,
+            ConnectionCallback callback, Bundle rootHints) {
+        if (context == null) {
+            throw new IllegalArgumentException("context must not be null");
+        }
+        if (serviceComponent == null) {
+            throw new IllegalArgumentException("service component must not be null");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("connection callback must not be null");
+        }
+        mContext = context;
+        mServiceComponent = serviceComponent;
+        mCallback = callback;
+        mRootHints = rootHints == null ? null : new Bundle(rootHints);
+    }
+
+    /**
+     * Connects to the media browser service.
+     * <p>
+     * The connection callback specified in the constructor will be invoked
+     * when the connection completes or fails.
+     * </p>
+     */
+    public void connect() {
+        if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) {
+            throw new IllegalStateException("connect() called while neither disconnecting nor "
+                    + "disconnected (state=" + getStateLabel(mState) + ")");
+        }
+
+        mState = CONNECT_STATE_CONNECTING;
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mState == CONNECT_STATE_DISCONNECTING) {
+                    return;
+                }
+                mState = CONNECT_STATE_CONNECTING;
+                // TODO: remove this extra check.
+                if (DBG) {
+                    if (mServiceConnection != null) {
+                        throw new RuntimeException("mServiceConnection should be null. Instead it"
+                                + " is " + mServiceConnection);
+                    }
+                }
+                if (mServiceBinder != null) {
+                    throw new RuntimeException("mServiceBinder should be null. Instead it is "
+                            + mServiceBinder);
+                }
+                if (mServiceCallbacks != null) {
+                    throw new RuntimeException("mServiceCallbacks should be null. Instead it is "
+                            + mServiceCallbacks);
+                }
+
+                final Intent intent = new Intent(MediaBrowserService.SERVICE_INTERFACE);
+                intent.setComponent(mServiceComponent);
+
+                mServiceConnection = new MediaServiceConnection();
+
+                boolean bound = false;
+                try {
+                    bound = mContext.bindService(intent, mServiceConnection,
+                            Context.BIND_AUTO_CREATE);
+                } catch (Exception ex) {
+                    Log.e(TAG, "Failed binding to service " + mServiceComponent);
+                }
+
+                if (!bound) {
+                    // Tell them that it didn't work.
+                    forceCloseConnection();
+                    mCallback.onConnectionFailed();
+                }
+
+                if (DBG) {
+                    Log.d(TAG, "connect...");
+                    dump();
+                }
+            }
+        });
+    }
+
+    /**
+     * Disconnects from the media browser service.
+     * After this, no more callbacks will be received.
+     */
+    public void disconnect() {
+        // It's ok to call this any state, because allowing this lets apps not have
+        // to check isConnected() unnecessarily. They won't appreciate the extra
+        // assertions for this. We do everything we can here to go back to a sane state.
+        mState = CONNECT_STATE_DISCONNECTING;
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                // connect() could be called before this. Then we will disconnect and reconnect.
+                if (mServiceCallbacks != null) {
+                    try {
+                        mServiceBinder.disconnect(mServiceCallbacks);
+                    } catch (RemoteException ex) {
+                        // We are disconnecting anyway. Log, just for posterity but it's not
+                        // a big problem.
+                        Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
+                    }
+                }
+                int state = mState;
+                forceCloseConnection();
+                // If the state was not CONNECT_STATE_DISCONNECTING, keep the state so that
+                // the operation came after disconnect() can be handled properly.
+                if (state != CONNECT_STATE_DISCONNECTING) {
+                    mState = state;
+                }
+                if (DBG) {
+                    Log.d(TAG, "disconnect...");
+                    dump();
+                }
+            }
+        });
+    }
+
+    /**
+     * Null out the variables and unbind from the service. This doesn't include
+     * calling disconnect on the service, because we only try to do that in the
+     * clean shutdown cases.
+     * <p>
+     * Everywhere that calls this EXCEPT for disconnect() should follow it with
+     * a call to mCallback.onConnectionFailed(). Disconnect doesn't do that callback
+     * for a clean shutdown, but everywhere else is a dirty shutdown and should
+     * notify the app.
+     * <p>
+     * Also, mState should be updated properly. Mostly it should be CONNECT_STATE_DIACONNECTED
+     * except for disconnect().
+     */
+    private void forceCloseConnection() {
+        if (mServiceConnection != null) {
+            try {
+                mContext.unbindService(mServiceConnection);
+            } catch (IllegalArgumentException e) {
+                if (DBG) {
+                    Log.d(TAG, "unbindService failed", e);
+                }
+            }
+        }
+        mState = CONNECT_STATE_DISCONNECTED;
+        mServiceConnection = null;
+        mServiceBinder = null;
+        mServiceCallbacks = null;
+        mRootId = null;
+        mMediaSessionToken = null;
+    }
+
+    /**
+     * Returns whether the browser is connected to the service.
+     */
+    public boolean isConnected() {
+        return mState == CONNECT_STATE_CONNECTED;
+    }
+
+    /**
+     * Gets the service component that the media browser is connected to.
+     */
+    public @NonNull ComponentName getServiceComponent() {
+        if (!isConnected()) {
+            throw new IllegalStateException("getServiceComponent() called while not connected" +
+                    " (state=" + mState + ")");
+        }
+        return mServiceComponent;
+    }
+
+    /**
+     * Gets the root id.
+     * <p>
+     * Note that the root id may become invalid or change when the
+     * browser is disconnected.
+     * </p>
+     *
+     * @throws IllegalStateException if not connected.
+     */
+    public @NonNull String getRoot() {
+        if (!isConnected()) {
+            throw new IllegalStateException("getRoot() called while not connected (state="
+                    + getStateLabel(mState) + ")");
+        }
+        return mRootId;
+    }
+
+    /**
+     * Gets any extras for the media service.
+     *
+     * @throws IllegalStateException if not connected.
+     */
+    public @Nullable Bundle getExtras() {
+        if (!isConnected()) {
+            throw new IllegalStateException("getExtras() called while not connected (state="
+                    + getStateLabel(mState) + ")");
+        }
+        return mExtras;
+    }
+
+    /**
+     * Gets the media session token associated with the media browser.
+     * <p>
+     * Note that the session token may become invalid or change when the
+     * browser is disconnected.
+     * </p>
+     *
+     * @return The session token for the browser, never null.
+     *
+     * @throws IllegalStateException if not connected.
+     */
+     public @NonNull MediaSession.Token getSessionToken() {
+        if (!isConnected()) {
+            throw new IllegalStateException("getSessionToken() called while not connected (state="
+                    + mState + ")");
+        }
+        return mMediaSessionToken;
+    }
+
+    /**
+     * Queries for information about the media items that are contained within
+     * the specified id and subscribes to receive updates when they change.
+     * <p>
+     * The list of subscriptions is maintained even when not connected and is
+     * restored after the reconnection. It is ok to subscribe while not connected
+     * but the results will not be returned until the connection completes.
+     * </p>
+     * <p>
+     * If the id is already subscribed with a different callback then the new
+     * callback will replace the previous one and the child data will be
+     * reloaded.
+     * </p>
+     *
+     * @param parentId The id of the parent media item whose list of children
+     *            will be subscribed.
+     * @param callback The callback to receive the list of children.
+     */
+    public void subscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) {
+        subscribeInternal(parentId, null, callback);
+    }
+
+    /**
+     * Queries with service-specific arguments for information about the media items
+     * that are contained within the specified id and subscribes to receive updates
+     * when they change.
+     * <p>
+     * The list of subscriptions is maintained even when not connected and is
+     * restored after the reconnection. It is ok to subscribe while not connected
+     * but the results will not be returned until the connection completes.
+     * </p>
+     * <p>
+     * If the id is already subscribed with a different callback then the new
+     * callback will replace the previous one and the child data will be
+     * reloaded.
+     * </p>
+     *
+     * @param parentId The id of the parent media item whose list of children
+     *            will be subscribed.
+     * @param options The bundle of service-specific arguments to send to the media
+     *            browser service. The contents of this bundle may affect the
+     *            information returned when browsing.
+     * @param callback The callback to receive the list of children.
+     */
+    public void subscribe(@NonNull String parentId, @NonNull Bundle options,
+            @NonNull SubscriptionCallback callback) {
+        if (options == null) {
+            throw new IllegalArgumentException("options cannot be null");
+        }
+        subscribeInternal(parentId, new Bundle(options), callback);
+    }
+
+    /**
+     * Unsubscribes for changes to the children of the specified media id.
+     * <p>
+     * The query callback will no longer be invoked for results associated with
+     * this id once this method returns.
+     * </p>
+     *
+     * @param parentId The id of the parent media item whose list of children
+     *            will be unsubscribed.
+     */
+    public void unsubscribe(@NonNull String parentId) {
+        unsubscribeInternal(parentId, null);
+    }
+
+    /**
+     * Unsubscribes for changes to the children of the specified media id through a callback.
+     * <p>
+     * The query callback will no longer be invoked for results associated with
+     * this id once this method returns.
+     * </p>
+     *
+     * @param parentId The id of the parent media item whose list of children
+     *            will be unsubscribed.
+     * @param callback A callback sent to the media browser service to subscribe.
+     */
+    public void unsubscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        unsubscribeInternal(parentId, callback);
+    }
+
+    /**
+     * Retrieves a specific {@link MediaItem} from the connected service. Not
+     * all services may support this, so falling back to subscribing to the
+     * parent's id should be used when unavailable.
+     *
+     * @param mediaId The id of the item to retrieve.
+     * @param cb The callback to receive the result on.
+     */
+    public void getItem(final @NonNull String mediaId, @NonNull final ItemCallback cb) {
+        if (TextUtils.isEmpty(mediaId)) {
+            throw new IllegalArgumentException("mediaId cannot be empty.");
+        }
+        if (cb == null) {
+            throw new IllegalArgumentException("cb cannot be null.");
+        }
+        if (mState != CONNECT_STATE_CONNECTED) {
+            Log.i(TAG, "Not connected, unable to retrieve the MediaItem.");
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    cb.onError(mediaId);
+                }
+            });
+            return;
+        }
+        ResultReceiver receiver = new ResultReceiver(mHandler) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (!isConnected()) {
+                    return;
+                }
+                if (resultCode != 0 || resultData == null
+                        || !resultData.containsKey(MediaBrowserService.KEY_MEDIA_ITEM)) {
+                    cb.onError(mediaId);
+                    return;
+                }
+                Parcelable item = resultData.getParcelable(MediaBrowserService.KEY_MEDIA_ITEM);
+                if (item != null && !(item instanceof MediaItem)) {
+                    cb.onError(mediaId);
+                    return;
+                }
+                cb.onItemLoaded((MediaItem)item);
+            }
+        };
+        try {
+            mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks);
+        } catch (RemoteException e) {
+            Log.i(TAG, "Remote error getting media item.");
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    cb.onError(mediaId);
+                }
+            });
+        }
+    }
+
+    private void subscribeInternal(String parentId, Bundle options, SubscriptionCallback callback) {
+        // Check arguments.
+        if (TextUtils.isEmpty(parentId)) {
+            throw new IllegalArgumentException("parentId cannot be empty.");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        // Update or create the subscription.
+        Subscription sub = mSubscriptions.get(parentId);
+        if (sub == null) {
+            sub = new Subscription();
+            mSubscriptions.put(parentId, sub);
+        }
+        sub.putCallback(mContext, options, callback);
+
+        // If we are connected, tell the service that we are watching. If we aren't connected,
+        // the service will be told when we connect.
+        if (isConnected()) {
+            try {
+                if (options == null) {
+                    mServiceBinder.addSubscriptionDeprecated(parentId, mServiceCallbacks);
+                }
+                mServiceBinder.addSubscription(parentId, callback.mToken, options,
+                        mServiceCallbacks);
+            } catch (RemoteException ex) {
+                // Process is crashing. We will disconnect, and upon reconnect we will
+                // automatically reregister. So nothing to do here.
+                Log.d(TAG, "addSubscription failed with RemoteException parentId=" + parentId);
+            }
+        }
+    }
+
+    private void unsubscribeInternal(String parentId, SubscriptionCallback callback) {
+        // Check arguments.
+        if (TextUtils.isEmpty(parentId)) {
+            throw new IllegalArgumentException("parentId cannot be empty.");
+        }
+
+        Subscription sub = mSubscriptions.get(parentId);
+        if (sub == null) {
+            return;
+        }
+        // Tell the service if necessary.
+        try {
+            if (callback == null) {
+                if (isConnected()) {
+                    mServiceBinder.removeSubscriptionDeprecated(parentId, mServiceCallbacks);
+                    mServiceBinder.removeSubscription(parentId, null, mServiceCallbacks);
+                }
+            } else {
+                final List<SubscriptionCallback> callbacks = sub.getCallbacks();
+                final List<Bundle> optionsList = sub.getOptionsList();
+                for (int i = callbacks.size() - 1; i >= 0; --i) {
+                    if (callbacks.get(i) == callback) {
+                        if (isConnected()) {
+                            mServiceBinder.removeSubscription(
+                                    parentId, callback.mToken, mServiceCallbacks);
+                        }
+                        callbacks.remove(i);
+                        optionsList.remove(i);
+                    }
+                }
+            }
+        } catch (RemoteException ex) {
+            // Process is crashing. We will disconnect, and upon reconnect we will
+            // automatically reregister. So nothing to do here.
+            Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId);
+        }
+
+        if (sub.isEmpty() || callback == null) {
+            mSubscriptions.remove(parentId);
+        }
+    }
+
+    /**
+     * For debugging.
+     */
+    private static String getStateLabel(int state) {
+        switch (state) {
+            case CONNECT_STATE_DISCONNECTING:
+                return "CONNECT_STATE_DISCONNECTING";
+            case CONNECT_STATE_DISCONNECTED:
+                return "CONNECT_STATE_DISCONNECTED";
+            case CONNECT_STATE_CONNECTING:
+                return "CONNECT_STATE_CONNECTING";
+            case CONNECT_STATE_CONNECTED:
+                return "CONNECT_STATE_CONNECTED";
+            case CONNECT_STATE_SUSPENDED:
+                return "CONNECT_STATE_SUSPENDED";
+            default:
+                return "UNKNOWN/" + state;
+        }
+    }
+
+    private final void onServiceConnected(final IMediaBrowserServiceCallbacks callback,
+            final String root, final MediaSession.Token session, final Bundle extra) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                // Check to make sure there hasn't been a disconnect or a different
+                // ServiceConnection.
+                if (!isCurrent(callback, "onConnect")) {
+                    return;
+                }
+                // Don't allow them to call us twice.
+                if (mState != CONNECT_STATE_CONNECTING) {
+                    Log.w(TAG, "onConnect from service while mState="
+                            + getStateLabel(mState) + "... ignoring");
+                    return;
+                }
+                mRootId = root;
+                mMediaSessionToken = session;
+                mExtras = extra;
+                mState = CONNECT_STATE_CONNECTED;
+
+                if (DBG) {
+                    Log.d(TAG, "ServiceCallbacks.onConnect...");
+                    dump();
+                }
+                mCallback.onConnected();
+
+                // we may receive some subscriptions before we are connected, so re-subscribe
+                // everything now
+                for (Entry<String, Subscription> subscriptionEntry : mSubscriptions.entrySet()) {
+                    String id = subscriptionEntry.getKey();
+                    Subscription sub = subscriptionEntry.getValue();
+                    List<SubscriptionCallback> callbackList = sub.getCallbacks();
+                    List<Bundle> optionsList = sub.getOptionsList();
+                    for (int i = 0; i < callbackList.size(); ++i) {
+                        try {
+                            mServiceBinder.addSubscription(id, callbackList.get(i).mToken,
+                                    optionsList.get(i), mServiceCallbacks);
+                        } catch (RemoteException ex) {
+                            // Process is crashing. We will disconnect, and upon reconnect we will
+                            // automatically reregister. So nothing to do here.
+                            Log.d(TAG, "addSubscription failed with RemoteException parentId="
+                                    + id);
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    private final void onConnectionFailed(final IMediaBrowserServiceCallbacks callback) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                Log.e(TAG, "onConnectFailed for " + mServiceComponent);
+
+                // Check to make sure there hasn't been a disconnect or a different
+                // ServiceConnection.
+                if (!isCurrent(callback, "onConnectFailed")) {
+                    return;
+                }
+                // Don't allow them to call us twice.
+                if (mState != CONNECT_STATE_CONNECTING) {
+                    Log.w(TAG, "onConnect from service while mState="
+                            + getStateLabel(mState) + "... ignoring");
+                    return;
+                }
+
+                // Clean up
+                forceCloseConnection();
+
+                // Tell the app.
+                mCallback.onConnectionFailed();
+            }
+        });
+    }
+
+    //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice.
+    /*
+    private final void onLoadChildren(final IMediaBrowserServiceCallbacks callback,
+            final String parentId, final ParceledListSlice list, final Bundle options) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                // Check that there hasn't been a disconnect or a different
+                // ServiceConnection.
+                if (!isCurrent(callback, "onLoadChildren")) {
+                    return;
+                }
+
+                if (DBG) {
+                    Log.d(TAG, "onLoadChildren for " + mServiceComponent + " id=" + parentId);
+                }
+
+                // Check that the subscription is still subscribed.
+                final Subscription subscription = mSubscriptions.get(parentId);
+                if (subscription != null) {
+                    // Tell the app.
+                    SubscriptionCallback subscriptionCallback =
+                            subscription.getCallback(mContext, options);
+                    if (subscriptionCallback != null) {
+                        List<MediaItem> data = list == null ? null : list.getList();
+                        if (options == null) {
+                            if (data == null) {
+                                subscriptionCallback.onError(parentId);
+                            } else {
+                                subscriptionCallback.onChildrenLoaded(parentId, data);
+                            }
+                        } else {
+                            if (data == null) {
+                                subscriptionCallback.onError(parentId, options);
+                            } else {
+                                subscriptionCallback.onChildrenLoaded(parentId, data, options);
+                            }
+                        }
+                        return;
+                    }
+                }
+                if (DBG) {
+                    Log.d(TAG, "onLoadChildren for id that isn't subscribed id=" + parentId);
+                }
+            }
+        });
+    }
+    */
+
+    /**
+     * Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not.
+     */
+    private boolean isCurrent(IMediaBrowserServiceCallbacks callback, String funcName) {
+        if (mServiceCallbacks != callback || mState == CONNECT_STATE_DISCONNECTING
+                || mState == CONNECT_STATE_DISCONNECTED) {
+            if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) {
+                Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection="
+                        + mServiceCallbacks + " this=" + this);
+            }
+            return false;
+        }
+        return true;
+    }
+
+    private ServiceCallbacks getNewServiceCallbacks() {
+        return new ServiceCallbacks(this);
+    }
+
+    /**
+     * Log internal state.
+     * @hide
+     */
+    void dump() {
+        Log.d(TAG, "MediaBrowser...");
+        Log.d(TAG, "  mServiceComponent=" + mServiceComponent);
+        Log.d(TAG, "  mCallback=" + mCallback);
+        Log.d(TAG, "  mRootHints=" + mRootHints);
+        Log.d(TAG, "  mState=" + getStateLabel(mState));
+        Log.d(TAG, "  mServiceConnection=" + mServiceConnection);
+        Log.d(TAG, "  mServiceBinder=" + mServiceBinder);
+        Log.d(TAG, "  mServiceCallbacks=" + mServiceCallbacks);
+        Log.d(TAG, "  mRootId=" + mRootId);
+        Log.d(TAG, "  mMediaSessionToken=" + mMediaSessionToken);
+    }
+
+    /**
+     * A class with information on a single media item for use in browsing/searching media.
+     * MediaItems are application dependent so we cannot guarantee that they contain the
+     * right values.
+     */
+    public static class MediaItem implements Parcelable {
+        private final int mFlags;
+        private final MediaDescription mDescription;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
+        public @interface Flags { }
+
+        /**
+         * Flag: Indicates that the item has children of its own.
+         */
+        public static final int FLAG_BROWSABLE = 1 << 0;
+
+        /**
+         * Flag: Indicates that the item is playable.
+         * <p>
+         * The id of this item may be passed to
+         * {@link MediaController.TransportControls#playFromMediaId(String, Bundle)}
+         * to start playing it.
+         * </p>
+         */
+        public static final int FLAG_PLAYABLE = 1 << 1;
+
+        /**
+         * Create a new MediaItem for use in browsing media.
+         * @param description The description of the media, which must include a
+         *            media id.
+         * @param flags The flags for this item.
+         */
+        public MediaItem(@NonNull MediaDescription description, @Flags int flags) {
+            if (description == null) {
+                throw new IllegalArgumentException("description cannot be null");
+            }
+            if (TextUtils.isEmpty(description.getMediaId())) {
+                throw new IllegalArgumentException("description must have a non-empty media id");
+            }
+            mFlags = flags;
+            mDescription = description;
+        }
+
+        /**
+         * Private constructor.
+         */
+        private MediaItem(Parcel in) {
+            mFlags = in.readInt();
+            mDescription = MediaDescription.CREATOR.createFromParcel(in);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeInt(mFlags);
+            mDescription.writeToParcel(out, flags);
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("MediaItem{");
+            sb.append("mFlags=").append(mFlags);
+            sb.append(", mDescription=").append(mDescription);
+            sb.append('}');
+            return sb.toString();
+        }
+
+        public static final Parcelable.Creator<MediaItem> CREATOR =
+                new Parcelable.Creator<MediaItem>() {
+                    @Override
+                    public MediaItem createFromParcel(Parcel in) {
+                        return new MediaItem(in);
+                    }
+
+                    @Override
+                    public MediaItem[] newArray(int size) {
+                        return new MediaItem[size];
+                    }
+                };
+
+        /**
+         * Gets the flags of the item.
+         */
+        public @Flags int getFlags() {
+            return mFlags;
+        }
+
+        /**
+         * Returns whether this item is browsable.
+         * @see #FLAG_BROWSABLE
+         */
+        public boolean isBrowsable() {
+            return (mFlags & FLAG_BROWSABLE) != 0;
+        }
+
+        /**
+         * Returns whether this item is playable.
+         * @see #FLAG_PLAYABLE
+         */
+        public boolean isPlayable() {
+            return (mFlags & FLAG_PLAYABLE) != 0;
+        }
+
+        /**
+         * Returns the description of the media.
+         */
+        public @NonNull MediaDescription getDescription() {
+            return mDescription;
+        }
+
+        /**
+         * Returns the media id in the {@link MediaDescription} for this item.
+         * @see android.media.MediaMetadata#METADATA_KEY_MEDIA_ID
+         */
+        public @Nullable String getMediaId() {
+            return mDescription.getMediaId();
+        }
+    }
+
+    /**
+     * Callbacks for connection related events.
+     */
+    public static class ConnectionCallback {
+        /**
+         * Invoked after {@link MediaBrowser#connect()} when the request has successfully completed.
+         */
+        public void onConnected() {
+        }
+
+        /**
+         * Invoked when the client is disconnected from the media browser.
+         */
+        public void onConnectionSuspended() {
+        }
+
+        /**
+         * Invoked when the connection to the media browser failed.
+         */
+        public void onConnectionFailed() {
+        }
+    }
+
+    /**
+     * Callbacks for subscription related events.
+     */
+    public static abstract class SubscriptionCallback {
+        Binder mToken;
+
+        public SubscriptionCallback() {
+            mToken = new Binder();
+        }
+
+        /**
+         * Called when the list of children is loaded or updated.
+         *
+         * @param parentId The media id of the parent media item.
+         * @param children The children which were loaded.
+         */
+        public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children) {
+        }
+
+        /**
+         * Called when the list of children is loaded or updated.
+         *
+         * @param parentId The media id of the parent media item.
+         * @param children The children which were loaded.
+         * @param options The bundle of service-specific arguments sent to the media
+         *            browser service. The contents of this bundle may affect the
+         *            information returned when browsing.
+         */
+        public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children,
+                @NonNull Bundle options) {
+        }
+
+        /**
+         * Called when the id doesn't exist or other errors in subscribing.
+         * <p>
+         * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe}
+         * called, because some errors may heal themselves.
+         * </p>
+         *
+         * @param parentId The media id of the parent media item whose children could
+         *            not be loaded.
+         */
+        public void onError(@NonNull String parentId) {
+        }
+
+        /**
+         * Called when the id doesn't exist or other errors in subscribing.
+         * <p>
+         * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe}
+         * called, because some errors may heal themselves.
+         * </p>
+         *
+         * @param parentId The media id of the parent media item whose children could
+         *            not be loaded.
+         * @param options The bundle of service-specific arguments sent to the media
+         *            browser service.
+         */
+        public void onError(@NonNull String parentId, @NonNull Bundle options) {
+        }
+    }
+
+    /**
+     * Callback for receiving the result of {@link #getItem}.
+     */
+    public static abstract class ItemCallback {
+        /**
+         * Called when the item has been returned by the connected service.
+         *
+         * @param item The item that was returned or null if it doesn't exist.
+         */
+        public void onItemLoaded(MediaItem item) {
+        }
+
+        /**
+         * Called there was an error retrieving it or the connected service doesn't support
+         * {@link #getItem}.
+         *
+         * @param mediaId The media id of the media item which could not be loaded.
+         */
+        public void onError(@NonNull String mediaId) {
+        }
+    }
+
+    /**
+     * ServiceConnection to the other app.
+     */
+    private class MediaServiceConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(final ComponentName name, final IBinder binder) {
+            postOrRun(new Runnable() {
+                @Override
+                public void run() {
+                    if (DBG) {
+                        Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name
+                                + " binder=" + binder);
+                        dump();
+                    }
+
+                    // Make sure we are still the current connection, and that they haven't called
+                    // disconnect().
+                    if (!isCurrent("onServiceConnected")) {
+                        return;
+                    }
+
+                    // Save their binder
+                    mServiceBinder = IMediaBrowserService.Stub.asInterface(binder);
+
+                    // We make a new mServiceCallbacks each time we connect so that we can drop
+                    // responses from previous connections.
+                    mServiceCallbacks = getNewServiceCallbacks();
+                    mState = CONNECT_STATE_CONNECTING;
+
+                    // Call connect, which is async. When we get a response from that we will
+                    // say that we're connected.
+                    try {
+                        if (DBG) {
+                            Log.d(TAG, "ServiceCallbacks.onConnect...");
+                            dump();
+                        }
+                        mServiceBinder.connect(mContext.getPackageName(), mRootHints,
+                                mServiceCallbacks);
+                    } catch (RemoteException ex) {
+                        // Connect failed, which isn't good. But the auto-reconnect on the service
+                        // will take over and we will come back. We will also get the
+                        // onServiceDisconnected, which has all the cleanup code. So let that do
+                        // it.
+                        Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
+                        if (DBG) {
+                            Log.d(TAG, "ServiceCallbacks.onConnect...");
+                            dump();
+                        }
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void onServiceDisconnected(final ComponentName name) {
+            postOrRun(new Runnable() {
+                @Override
+                public void run() {
+                    if (DBG) {
+                        Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name
+                                + " this=" + this + " mServiceConnection=" + mServiceConnection);
+                        dump();
+                    }
+
+                    // Make sure we are still the current connection, and that they haven't called
+                    // disconnect().
+                    if (!isCurrent("onServiceDisconnected")) {
+                        return;
+                    }
+
+                    // Clear out what we set in onServiceConnected
+                    mServiceBinder = null;
+                    mServiceCallbacks = null;
+
+                    // And tell the app that it's suspended.
+                    mState = CONNECT_STATE_SUSPENDED;
+                    mCallback.onConnectionSuspended();
+                }
+            });
+        }
+
+        private void postOrRun(Runnable r) {
+            if (Thread.currentThread() == mHandler.getLooper().getThread()) {
+                r.run();
+            } else {
+                mHandler.post(r);
+            }
+        }
+
+        /**
+         * Return true if this is the current ServiceConnection. Also logs if it's not.
+         */
+        private boolean isCurrent(String funcName) {
+            if (mServiceConnection != this || mState == CONNECT_STATE_DISCONNECTING
+                    || mState == CONNECT_STATE_DISCONNECTED) {
+                if (mState != CONNECT_STATE_DISCONNECTING && mState != CONNECT_STATE_DISCONNECTED) {
+                    // Check mState, because otherwise this log is noisy.
+                    Log.i(TAG, funcName + " for " + mServiceComponent + " with mServiceConnection="
+                            + mServiceConnection + " this=" + this);
+                }
+                return false;
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Callbacks from the service.
+     */
+    private static class ServiceCallbacks extends IMediaBrowserServiceCallbacks.Stub {
+        private WeakReference<MediaBrowser> mMediaBrowser;
+
+        public ServiceCallbacks(MediaBrowser mediaBrowser) {
+            mMediaBrowser = new WeakReference<MediaBrowser>(mediaBrowser);
+        }
+
+        /**
+         * The other side has acknowledged our connection. The parameters to this function
+         * are the initial data as requested.
+         */
+        @Override
+        public void onConnect(String root, MediaSession.Token session,
+                final Bundle extras) {
+            MediaBrowser mediaBrowser = mMediaBrowser.get();
+            if (mediaBrowser != null) {
+                mediaBrowser.onServiceConnected(this, root, session, extras);
+            }
+        }
+
+        /**
+         * The other side does not like us. Tell the app via onConnectionFailed.
+         */
+        @Override
+        public void onConnectFailed() {
+            MediaBrowser mediaBrowser = mMediaBrowser.get();
+            if (mediaBrowser != null) {
+                mediaBrowser.onConnectionFailed(this);
+            }
+        }
+
+        //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice.
+        /*
+        @Override
+        public void onLoadChildren(String parentId, ParceledListSlice list) {
+            onLoadChildrenWithOptions(parentId, list, null);
+        }
+
+        @Override
+        public void onLoadChildrenWithOptions(String parentId, ParceledListSlice list,
+                final Bundle options) {
+            MediaBrowser mediaBrowser = mMediaBrowser.get();
+            if (mediaBrowser != null) {
+                mediaBrowser.onLoadChildren(this, parentId, list, options);
+            }
+        }
+        */
+    }
+
+    private static class Subscription {
+        private final List<SubscriptionCallback> mCallbacks;
+        private final List<Bundle> mOptionsList;
+
+        public Subscription() {
+            mCallbacks = new ArrayList<>();
+            mOptionsList = new ArrayList<>();
+        }
+
+        public boolean isEmpty() {
+            return mCallbacks.isEmpty();
+        }
+
+        public List<Bundle> getOptionsList() {
+            return mOptionsList;
+        }
+
+        public List<SubscriptionCallback> getCallbacks() {
+            return mCallbacks;
+        }
+
+        public SubscriptionCallback getCallback(Context context, Bundle options) {
+            if (options != null) {
+                options.setClassLoader(context.getClassLoader());
+            }
+            for (int i = 0; i < mOptionsList.size(); ++i) {
+                if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
+                    return mCallbacks.get(i);
+                }
+            }
+            return null;
+        }
+
+        public void putCallback(Context context, Bundle options, SubscriptionCallback callback) {
+            if (options != null) {
+                options.setClassLoader(context.getClassLoader());
+            }
+            for (int i = 0; i < mOptionsList.size(); ++i) {
+                if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
+                    mCallbacks.set(i, callback);
+                    return;
+                }
+            }
+            mCallbacks.add(callback);
+            mOptionsList.add(options);
+        }
+    }
+}
diff --git a/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java
new file mode 100644
index 0000000..2943e60
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/browse/MediaBrowserUtils.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.browse;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+public class MediaBrowserUtils {
+    public static boolean areSameOptions(Bundle options1, Bundle options2) {
+        if (options1 == options2) {
+            return true;
+        } else if (options1 == null) {
+            return options2.getInt(MediaBrowser.EXTRA_PAGE, -1) == -1
+                    && options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) == -1;
+        } else if (options2 == null) {
+            return options1.getInt(MediaBrowser.EXTRA_PAGE, -1) == -1
+                    && options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1) == -1;
+        } else {
+            return options1.getInt(MediaBrowser.EXTRA_PAGE, -1)
+                    == options2.getInt(MediaBrowser.EXTRA_PAGE, -1)
+                    && options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1)
+                    == options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
+        }
+    }
+
+    public static boolean hasDuplicatedItems(Bundle options1, Bundle options2) {
+        int page1 = options1 == null ? -1 : options1.getInt(MediaBrowser.EXTRA_PAGE, -1);
+        int page2 = options2 == null ? -1 : options2.getInt(MediaBrowser.EXTRA_PAGE, -1);
+        int pageSize1 = options1 == null ? -1 : options1.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
+        int pageSize2 = options2 == null ? -1 : options2.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
+
+        int startIndex1, startIndex2, endIndex1, endIndex2;
+        if (page1 == -1 || pageSize1 == -1) {
+            startIndex1 = 0;
+            endIndex1 = Integer.MAX_VALUE;
+        } else {
+            startIndex1 = pageSize1 * page1;
+            endIndex1 = startIndex1 + pageSize1 - 1;
+        }
+
+        if (page2 == -1 || pageSize2 == -1) {
+            startIndex2 = 0;
+            endIndex2 = Integer.MAX_VALUE;
+        } else {
+            startIndex2 = pageSize2 * page2;
+            endIndex2 = startIndex2 + pageSize2 - 1;
+        }
+
+        if (startIndex1 <= startIndex2 && startIndex2 <= endIndex1) {
+            return true;
+        } else if (startIndex1 <= endIndex2 && endIndex2 <= endIndex1) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl
new file mode 100644
index 0000000..4b9e4bd
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/IActiveSessionsListener.aidl
@@ -0,0 +1,26 @@
+/* Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.media.session.MediaSession;
+
+/**
+ * Listens for changes to the list of active sessions.
+ * @hide
+ */
+oneway interface IActiveSessionsListener {
+    void onActiveSessionsChanged(in List<MediaSession.Token> sessions);
+}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl
new file mode 100644
index 0000000..322bffa
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/ICallback.aidl
@@ -0,0 +1,35 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.media.session.MediaSession;
+import android.view.KeyEvent;
+
+/**
+ * @hide
+ */
+oneway interface ICallback {
+    void onMediaKeyEventDispatchedToMediaSession(in KeyEvent event,
+            in MediaSession.Token sessionToken);
+    void onMediaKeyEventDispatchedToMediaButtonReceiver(in KeyEvent event,
+            in ComponentName mediaButtonReceiver);
+
+    void onAddressedPlayerChangedToMediaSession(in MediaSession.Token sessionToken);
+    void onAddressedPlayerChangedToMediaButtonReceiver(in ComponentName mediaButtonReceiver);
+}
+
diff --git a/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl
new file mode 100644
index 0000000..aa98ea3
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/IOnMediaKeyListener.aidl
@@ -0,0 +1,28 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.os.ResultReceiver;
+import android.view.KeyEvent;
+
+/**
+ * Listener to handle media key.
+ * @hide
+ */
+oneway interface IOnMediaKeyListener {
+    void onMediaKey(in KeyEvent event, in ResultReceiver result);
+}
+
diff --git a/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl b/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl
new file mode 100644
index 0000000..07b8347
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/IOnVolumeKeyLongPressListener.aidl
@@ -0,0 +1,27 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.view.KeyEvent;
+
+/**
+ * Listener to handle volume key long-press.
+ * @hide
+ */
+oneway interface IOnVolumeKeyLongPressListener {
+    void onVolumeKeyLongPress(in KeyEvent event);
+}
+
diff --git a/packages/MediaComponents/apex/java/android/media/session/ISession.aidl b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl
new file mode 100644
index 0000000..cbd93cb
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/ISession.aidl
@@ -0,0 +1,55 @@
+/* Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.app.PendingIntent;
+import android.content.pm.ParceledListSlice;
+//import android.media.AudioAttributes;
+import android.media.MediaMetadata;
+import android.media.session.ISessionController;
+import android.media.session.PlaybackState;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+
+/**
+ * Interface to a MediaSession in the system.
+ * @hide
+ */
+interface ISession {
+    void sendEvent(String event, in Bundle data);
+    ISessionController getController();
+    void setFlags(int flags);
+    void setActive(boolean active);
+    void setMediaButtonReceiver(in PendingIntent mbr);
+    void setLaunchPendingIntent(in PendingIntent pi);
+    void destroy();
+
+    // These commands are for the TransportPerformer
+    void setMetadata(in MediaMetadata metadata);
+    void setPlaybackState(in PlaybackState state);
+    //TODO(b/119750807): Resolve hidden API usage ParceledListSlice.
+    //void setQueue(in ParceledListSlice queue);
+    void setQueueTitle(CharSequence title);
+    void setExtras(in Bundle extras);
+    void setRatingType(int type);
+
+    // These commands relate to volume handling
+    //TODO(b/119751592): Decide if AudioAttributes should be updated.
+    //void setPlaybackToLocal(in AudioAttributes attributes);
+    void setPlaybackToRemote(int control, int max);
+    void setCurrentVolume(int currentVolume);
+}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl
new file mode 100644
index 0000000..626338d
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/ISessionCallback.aidl
@@ -0,0 +1,71 @@
+/* Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.content.Intent;
+import android.media.Rating;
+import android.media.session.ISessionControllerCallback;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+
+/**
+ * @hide
+ */
+oneway interface ISessionCallback {
+    void onCommand(String packageName, int pid, int uid, ISessionControllerCallback caller,
+            String command, in Bundle args, in ResultReceiver cb);
+    void onMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent,
+            int sequenceNumber, in ResultReceiver cb);
+    void onMediaButtonFromController(String packageName, int pid, int uid,
+            ISessionControllerCallback caller, in Intent mediaButtonIntent);
+
+    // These callbacks are for the TransportPerformer
+    void onPrepare(String packageName, int pid, int uid, ISessionControllerCallback caller);
+    void onPrepareFromMediaId(String packageName, int pid, int uid,
+            ISessionControllerCallback caller, String mediaId, in Bundle extras);
+    void onPrepareFromSearch(String packageName, int pid, int uid,
+            ISessionControllerCallback caller, String query, in Bundle extras);
+    void onPrepareFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller,
+            in Uri uri, in Bundle extras);
+    void onPlay(String packageName, int pid, int uid, ISessionControllerCallback caller);
+    void onPlayFromMediaId(String packageName, int pid, int uid, ISessionControllerCallback caller,
+            String mediaId, in Bundle extras);
+    void onPlayFromSearch(String packageName, int pid, int uid, ISessionControllerCallback caller,
+            String query, in Bundle extras);
+    void onPlayFromUri(String packageName, int pid, int uid, ISessionControllerCallback caller,
+            in Uri uri, in Bundle extras);
+    void onSkipToTrack(String packageName, int pid, int uid, ISessionControllerCallback caller,
+            long id);
+    void onPause(String packageName, int pid, int uid, ISessionControllerCallback caller);
+    void onStop(String packageName, int pid, int uid, ISessionControllerCallback caller);
+    void onNext(String packageName, int pid, int uid, ISessionControllerCallback caller);
+    void onPrevious(String packageName, int pid, int uid, ISessionControllerCallback caller);
+    void onFastForward(String packageName, int pid, int uid, ISessionControllerCallback caller);
+    void onRewind(String packageName, int pid, int uid, ISessionControllerCallback caller);
+    void onSeekTo(String packageName, int pid, int uid, ISessionControllerCallback caller,
+            long pos);
+    void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller,
+            in Rating rating);
+    void onCustomAction(String packageName, int pid, int uid, ISessionControllerCallback caller,
+            String action, in Bundle args);
+
+    // These callbacks are for volume handling
+    void onAdjustVolume(String packageName, int pid, int uid, ISessionControllerCallback caller,
+            int direction);
+    void onSetVolumeTo(String packageName, int pid, int uid,
+            ISessionControllerCallback caller, int value);
+}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl
new file mode 100644
index 0000000..031a388
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/ISessionController.aidl
@@ -0,0 +1,89 @@
+/* Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+//import android.content.pm.ParceledListSlice;
+import android.media.MediaMetadata;
+import android.media.Rating;
+import android.media.session.ISessionControllerCallback;
+import android.media.session.MediaSession;
+import android.media.session.ParcelableVolumeInfo;
+import android.media.session.PlaybackState;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.view.KeyEvent;
+
+import java.util.List;
+
+/**
+ * Interface to MediaSessionRecord in the system.
+ * @hide
+ */
+interface ISessionController {
+    void sendCommand(String packageName, ISessionControllerCallback caller,
+            String command, in Bundle args, in ResultReceiver cb);
+    boolean sendMediaButton(String packageName, ISessionControllerCallback caller,
+            boolean asSystemService, in KeyEvent mediaButton);
+    void registerCallbackListener(String packageName, ISessionControllerCallback cb);
+    void unregisterCallbackListener(ISessionControllerCallback cb);
+    boolean isTransportControlEnabled();
+    String getPackageName();
+    String getTag();
+    PendingIntent getLaunchPendingIntent();
+    long getFlags();
+    ParcelableVolumeInfo getVolumeAttributes();
+    void adjustVolume(String packageName, ISessionControllerCallback caller,
+            boolean asSystemService, int direction, int flags);
+    void setVolumeTo(String packageName, ISessionControllerCallback caller,
+            int value, int flags);
+
+    // These commands are for the TransportControls
+    void prepare(String packageName, ISessionControllerCallback caller);
+    void prepareFromMediaId(String packageName, ISessionControllerCallback caller,
+            String mediaId, in Bundle extras);
+    void prepareFromSearch(String packageName, ISessionControllerCallback caller,
+            String string, in Bundle extras);
+    void prepareFromUri(String packageName, ISessionControllerCallback caller,
+            in Uri uri, in Bundle extras);
+    void play(String packageName, ISessionControllerCallback caller);
+    void playFromMediaId(String packageName, ISessionControllerCallback caller,
+            String mediaId, in Bundle extras);
+    void playFromSearch(String packageName, ISessionControllerCallback caller,
+            String string, in Bundle extras);
+    void playFromUri(String packageName, ISessionControllerCallback caller,
+            in Uri uri, in Bundle extras);
+    void skipToQueueItem(String packageName, ISessionControllerCallback caller, long id);
+    void pause(String packageName, ISessionControllerCallback caller);
+    void stop(String packageName, ISessionControllerCallback caller);
+    void next(String packageName, ISessionControllerCallback caller);
+    void previous(String packageName, ISessionControllerCallback caller);
+    void fastForward(String packageName, ISessionControllerCallback caller);
+    void rewind(String packageName, ISessionControllerCallback caller);
+    void seekTo(String packageName, ISessionControllerCallback caller, long pos);
+    void rate(String packageName, ISessionControllerCallback caller, in Rating rating);
+    void sendCustomAction(String packageName, ISessionControllerCallback caller,
+            String action, in Bundle args);
+    MediaMetadata getMetadata();
+    PlaybackState getPlaybackState();
+    //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice.
+    //ParceledListSlice getQueue();
+    CharSequence getQueueTitle();
+    Bundle getExtras();
+    int getRatingType();
+}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl
new file mode 100644
index 0000000..173504b
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/ISessionControllerCallback.aidl
@@ -0,0 +1,40 @@
+/* Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+//import android.content.pm.ParceledListSlice;
+import android.media.MediaMetadata;
+import android.media.session.ParcelableVolumeInfo;
+import android.media.session.PlaybackState;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface ISessionControllerCallback {
+    void onEvent(String event, in Bundle extras);
+    void onSessionDestroyed();
+
+    // These callbacks are for the TransportController
+    void onPlaybackStateChanged(in PlaybackState state);
+    void onMetadataChanged(in MediaMetadata metadata);
+    //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice.
+    //void onQueueChanged(in ParceledListSlice queue);
+    void onQueueTitleChanged(CharSequence title);
+    void onExtrasChanged(in Bundle extras);
+    void onVolumeInfoChanged(in ParcelableVolumeInfo info);
+}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl b/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl
new file mode 100644
index 0000000..3578c16
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/ISessionManager.aidl
@@ -0,0 +1,66 @@
+/* Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.content.ComponentName;
+import android.media.IRemoteVolumeController;
+import android.media.ISessionTokensListener;
+import android.media.session.IActiveSessionsListener;
+import android.media.session.ICallback;
+import android.media.session.IOnMediaKeyListener;
+import android.media.session.IOnVolumeKeyLongPressListener;
+import android.media.session.ISession;
+import android.media.session.ISessionCallback;
+import android.os.Bundle;
+import android.view.KeyEvent;
+
+/**
+ * Interface to the MediaSessionManagerService
+ * @hide
+ */
+interface ISessionManager {
+    ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
+    List<IBinder> getSessions(in ComponentName compName, int userId);
+    void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
+            boolean needWakeLock);
+    void dispatchVolumeKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
+            int stream, boolean musicOnly);
+    void dispatchAdjustVolume(String packageName, int suggestedStream, int delta, int flags);
+    void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
+            int userId);
+    void removeSessionsListener(in IActiveSessionsListener listener);
+
+    // This is for the system volume UI only
+    void setRemoteVolumeController(in IRemoteVolumeController rvc);
+
+    // For PhoneWindowManager to precheck media keys
+    boolean isGlobalPriorityActive();
+
+    void setCallback(in ICallback callback);
+    void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
+    void setOnMediaKeyListener(in IOnMediaKeyListener listener);
+
+    // MediaSession2
+    boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
+    boolean createSession2(in Bundle sessionToken);
+    void destroySession2(in Bundle sessionToken);
+    List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly,
+            String packageName);
+
+    void addSessionTokensListener(in ISessionTokensListener listener, int userId,
+            String packageName);
+    void removeSessionTokensListener(in ISessionTokensListener listener, String packageName);
+}
diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaController.java b/packages/MediaComponents/apex/java/android/media/session/MediaController.java
new file mode 100644
index 0000000..1f29185
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/MediaController.java
@@ -0,0 +1,1200 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
+import android.app.PendingIntent;
+import android.content.Context;
+//import android.content.pm.ParceledListSlice;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.MediaMetadata;
+import android.media.Rating;
+import android.media.VolumeProvider;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Allows an app to interact with an ongoing media session. Media buttons and
+ * other commands can be sent to the session. A callback may be registered to
+ * receive updates from the session, such as metadata and play state changes.
+ * <p>
+ * A MediaController can be created through {@link MediaSessionManager} if you
+ * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or are an
+ * enabled notification listener or by getting a {@link MediaSession.Token}
+ * directly from the session owner.
+ * <p>
+ * MediaController objects are thread-safe.
+ */
+public final class MediaController {
+    private static final String TAG = "MediaController";
+
+    private static final int MSG_EVENT = 1;
+    private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
+    private static final int MSG_UPDATE_METADATA = 3;
+    private static final int MSG_UPDATE_VOLUME = 4;
+    private static final int MSG_UPDATE_QUEUE = 5;
+    private static final int MSG_UPDATE_QUEUE_TITLE = 6;
+    private static final int MSG_UPDATE_EXTRAS = 7;
+    private static final int MSG_DESTROYED = 8;
+
+    private final ISessionController mSessionBinder;
+
+    private final MediaSession.Token mToken;
+    private final Context mContext;
+    private final CallbackStub mCbStub = new CallbackStub(this);
+    private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
+    private final Object mLock = new Object();
+
+    private boolean mCbRegistered = false;
+    private String mPackageName;
+    private String mTag;
+
+    private final TransportControls mTransportControls;
+
+    /**
+     * Call for creating a MediaController directly from a binder. Should only
+     * be used by framework code.
+     *
+     * @hide
+     */
+    public MediaController(Context context, ISessionController sessionBinder) {
+        if (sessionBinder == null) {
+            throw new IllegalArgumentException("Session token cannot be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("Context cannot be null");
+        }
+        mSessionBinder = sessionBinder;
+        mTransportControls = new TransportControls();
+        mToken = new MediaSession.Token(sessionBinder);
+        mContext = context;
+    }
+
+    /**
+     * Create a new MediaController from a session's token.
+     *
+     * @param context The caller's context.
+     * @param token The token for the session.
+     */
+    public MediaController(@NonNull Context context, @NonNull MediaSession.Token token) {
+        this(context, token.getBinder());
+    }
+
+    /**
+     * Get a {@link TransportControls} instance to send transport actions to
+     * the associated session.
+     *
+     * @return A transport controls instance.
+     */
+    public @NonNull TransportControls getTransportControls() {
+        return mTransportControls;
+    }
+
+    /**
+     * Send the specified media button event to the session. Only media keys can
+     * be sent by this method, other keys will be ignored.
+     *
+     * @param keyEvent The media button event to dispatch.
+     * @return true if the event was sent to the session, false otherwise.
+     */
+    public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) {
+        return dispatchMediaButtonEventInternal(false, keyEvent);
+    }
+
+    /**
+     * Dispatches the media button event as system service to the session.
+     * <p>
+     * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the
+     * foreground activity didn't consume the key from the hardware devices.
+     *
+     * @param keyEvent media key event
+     * @return {@code true} if the event was sent to the session, {@code false} otherwise
+     * @hide
+     */
+    public boolean dispatchMediaButtonEventAsSystemService(@NonNull KeyEvent keyEvent) {
+        return dispatchMediaButtonEventInternal(true, keyEvent);
+    }
+
+    private boolean dispatchMediaButtonEventInternal(boolean asSystemService,
+            @NonNull KeyEvent keyEvent) {
+        if (keyEvent == null) {
+            throw new IllegalArgumentException("KeyEvent may not be null");
+        }
+        //TODO(b/119789707): Resolve hidden API usage: KeyEvent#isMediaKey
+        /*
+        if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+            return false;
+        }
+        */
+        try {
+            //TODO(b/119748678): Resolve mContext.getOpPackageName() through this file.
+            // Temporarilly it's replaced with "mContext.getOpPackageName()" for compiling.
+            return mSessionBinder.sendMediaButton("mContext.getOpPackageName()", mCbStub,
+                    asSystemService, keyEvent);
+        } catch (RemoteException e) {
+            // System is dead. =(
+        }
+        return false;
+    }
+
+    /**
+     * Dispatches the volume button event as system service to the session.
+     * <p>
+     * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the
+     * foreground activity didn't consume the key from the hardware devices.
+     *
+     * @param keyEvent volume key event
+     * @hide
+     */
+    public void dispatchVolumeButtonEventAsSystemService(@NonNull KeyEvent keyEvent) {
+        switch (keyEvent.getAction()) {
+            case KeyEvent.ACTION_DOWN: {
+                int direction = 0;
+                switch (keyEvent.getKeyCode()) {
+                    case KeyEvent.KEYCODE_VOLUME_UP:
+                        direction = AudioManager.ADJUST_RAISE;
+                        break;
+                    case KeyEvent.KEYCODE_VOLUME_DOWN:
+                        direction = AudioManager.ADJUST_LOWER;
+                        break;
+                    case KeyEvent.KEYCODE_VOLUME_MUTE:
+                        direction = AudioManager.ADJUST_TOGGLE_MUTE;
+                        break;
+                }
+                try {
+                    mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, true,
+                            direction, AudioManager.FLAG_SHOW_UI);
+                } catch (RemoteException e) {
+                    Log.wtf(TAG, "Error calling adjustVolumeBy", e);
+                }
+            }
+
+            case KeyEvent.ACTION_UP: {
+                //TODO(b/119790339): Resolve hidden API usage. AudioManager.FLAG_FROM_KEY
+                final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
+                        //| AudioManager.FLAG_FROM_KEY;
+                try {
+                    mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, true, 0,
+                            flags);
+                } catch (RemoteException e) {
+                    Log.wtf(TAG, "Error calling adjustVolumeBy", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the current playback state for this session.
+     *
+     * @return The current PlaybackState or null
+     */
+    public @Nullable PlaybackState getPlaybackState() {
+        try {
+            return mSessionBinder.getPlaybackState();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getPlaybackState.", e);
+            return null;
+        }
+    }
+
+    /**
+     * Get the current metadata for this session.
+     *
+     * @return The current MediaMetadata or null.
+     */
+    public @Nullable MediaMetadata getMetadata() {
+        try {
+            return mSessionBinder.getMetadata();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getMetadata.", e);
+            return null;
+        }
+    }
+
+    /**
+     * Get the current play queue for this session if one is set. If you only
+     * care about the current item {@link #getMetadata()} should be used.
+     *
+     * @return The current play queue or null.
+     */
+    public @Nullable List<MediaSession.QueueItem> getQueue() {
+        //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice.
+        /*
+        try {
+            ParceledListSlice queue = mSessionBinder.getQueue();
+            if (queue != null) {
+                return queue.getList();
+            }
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getQueue.", e);
+        }
+        */
+        return null;
+    }
+
+    /**
+     * Get the queue title for this session.
+     */
+    public @Nullable CharSequence getQueueTitle() {
+        try {
+            return mSessionBinder.getQueueTitle();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getQueueTitle", e);
+        }
+        return null;
+    }
+
+    /**
+     * Get the extras for this session.
+     */
+    public @Nullable Bundle getExtras() {
+        try {
+            return mSessionBinder.getExtras();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getExtras", e);
+        }
+        return null;
+    }
+
+    /**
+     * Get the rating type supported by the session. One of:
+     * <ul>
+     * <li>{@link Rating#RATING_NONE}</li>
+     * <li>{@link Rating#RATING_HEART}</li>
+     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
+     * <li>{@link Rating#RATING_3_STARS}</li>
+     * <li>{@link Rating#RATING_4_STARS}</li>
+     * <li>{@link Rating#RATING_5_STARS}</li>
+     * <li>{@link Rating#RATING_PERCENTAGE}</li>
+     * </ul>
+     *
+     * @return The supported rating type
+     */
+    public int getRatingType() {
+        try {
+            return mSessionBinder.getRatingType();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getRatingType.", e);
+            return Rating.RATING_NONE;
+        }
+    }
+
+    /**
+     * Get the flags for this session. Flags are defined in {@link MediaSession}.
+     *
+     * @return The current set of flags for the session.
+     */
+    public @MediaSession.SessionFlags long getFlags() {
+        try {
+            return mSessionBinder.getFlags();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getFlags.", e);
+        }
+        return 0;
+    }
+
+    /**
+     * Get the current playback info for this session.
+     *
+     * @return The current playback info or null.
+     */
+    public @Nullable PlaybackInfo getPlaybackInfo() {
+        try {
+            ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
+            return new PlaybackInfo(result.volumeType, result.audioAttrs, result.controlType,
+                    result.maxVolume, result.currentVolume);
+
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getAudioInfo.", e);
+        }
+        return null;
+    }
+
+    /**
+     * Get an intent for launching UI associated with this session if one
+     * exists.
+     *
+     * @return A {@link PendingIntent} to launch UI or null.
+     */
+    public @Nullable PendingIntent getSessionActivity() {
+        try {
+            return mSessionBinder.getLaunchPendingIntent();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getPendingIntent.", e);
+        }
+        return null;
+    }
+
+    /**
+     * Get the token for the session this is connected to.
+     *
+     * @return The token for the connected session.
+     */
+    public @NonNull MediaSession.Token getSessionToken() {
+        return mToken;
+    }
+
+    /**
+     * Set the volume of the output this session is playing on. The command will
+     * be ignored if it does not support
+     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
+     * {@link AudioManager} may be used to affect the handling.
+     *
+     * @see #getPlaybackInfo()
+     * @param value The value to set it to, between 0 and the reported max.
+     * @param flags Flags from {@link AudioManager} to include with the volume
+     *            request.
+     */
+    public void setVolumeTo(int value, int flags) {
+        try {
+            mSessionBinder.setVolumeTo("mContext.getOpPackageName()", mCbStub, value, flags);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling setVolumeTo.", e);
+        }
+    }
+
+    /**
+     * Adjust the volume of the output this session is playing on. The direction
+     * must be one of {@link AudioManager#ADJUST_LOWER},
+     * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
+     * The command will be ignored if the session does not support
+     * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
+     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
+     * {@link AudioManager} may be used to affect the handling.
+     *
+     * @see #getPlaybackInfo()
+     * @param direction The direction to adjust the volume in.
+     * @param flags Any flags to pass with the command.
+     */
+    public void adjustVolume(int direction, int flags) {
+        try {
+            mSessionBinder.adjustVolume("mContext.getOpPackageName()", mCbStub, false, direction,
+                    flags);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
+        }
+    }
+
+    /**
+     * Registers a callback to receive updates from the Session. Updates will be
+     * posted on the caller's thread.
+     *
+     * @param callback The callback object, must not be null.
+     */
+    public void registerCallback(@NonNull Callback callback) {
+        registerCallback(callback, null);
+    }
+
+    /**
+     * Registers a callback to receive updates from the session. Updates will be
+     * posted on the specified handler's thread.
+     *
+     * @param callback The callback object, must not be null.
+     * @param handler The handler to post updates on. If null the callers thread
+     *            will be used.
+     */
+    public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+        if (handler == null) {
+            handler = new Handler();
+        }
+        synchronized (mLock) {
+            addCallbackLocked(callback, handler);
+        }
+    }
+
+    /**
+     * Unregisters the specified callback. If an update has already been posted
+     * you may still receive it after calling this method.
+     *
+     * @param callback The callback to remove.
+     */
+    public void unregisterCallback(@NonNull Callback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+        synchronized (mLock) {
+            removeCallbackLocked(callback);
+        }
+    }
+
+    /**
+     * Sends a generic command to the session. It is up to the session creator
+     * to decide what commands and parameters they will support. As such,
+     * commands should only be sent to sessions that the controller owns.
+     *
+     * @param command The command to send
+     * @param args Any parameters to include with the command
+     * @param cb The callback to receive the result on
+     */
+    public void sendCommand(@NonNull String command, @Nullable Bundle args,
+            @Nullable ResultReceiver cb) {
+        if (TextUtils.isEmpty(command)) {
+            throw new IllegalArgumentException("command cannot be null or empty");
+        }
+        try {
+            mSessionBinder.sendCommand("mContext.getOpPackageName()", mCbStub, command, args, cb);
+        } catch (RemoteException e) {
+            Log.d(TAG, "Dead object in sendCommand.", e);
+        }
+    }
+
+    /**
+     * Get the session owner's package name.
+     *
+     * @return The package name of of the session owner.
+     */
+    public String getPackageName() {
+        if (mPackageName == null) {
+            try {
+                mPackageName = mSessionBinder.getPackageName();
+            } catch (RemoteException e) {
+                Log.d(TAG, "Dead object in getPackageName.", e);
+            }
+        }
+        return mPackageName;
+    }
+
+    /**
+     * Get the session's tag for debugging purposes.
+     *
+     * @return The session's tag.
+     * @hide
+     */
+    public String getTag() {
+        if (mTag == null) {
+            try {
+                mTag = mSessionBinder.getTag();
+            } catch (RemoteException e) {
+                Log.d(TAG, "Dead object in getTag.", e);
+            }
+        }
+        return mTag;
+    }
+
+    /*
+     * @hide
+     */
+    ISessionController getSessionBinder() {
+        return mSessionBinder;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean controlsSameSession(MediaController other) {
+        if (other == null) return false;
+        return mSessionBinder.asBinder() == other.getSessionBinder().asBinder();
+    }
+
+    private void addCallbackLocked(Callback cb, Handler handler) {
+        if (getHandlerForCallbackLocked(cb) != null) {
+            Log.w(TAG, "Callback is already added, ignoring");
+            return;
+        }
+        MessageHandler holder = new MessageHandler(handler.getLooper(), cb);
+        mCallbacks.add(holder);
+        holder.mRegistered = true;
+
+        if (!mCbRegistered) {
+            try {
+                mSessionBinder.registerCallbackListener("mContext.getOpPackageName()", mCbStub);
+                mCbRegistered = true;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in registerCallback", e);
+            }
+        }
+    }
+
+    private boolean removeCallbackLocked(Callback cb) {
+        boolean success = false;
+        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+            MessageHandler handler = mCallbacks.get(i);
+            if (cb == handler.mCallback) {
+                mCallbacks.remove(i);
+                success = true;
+                handler.mRegistered = false;
+            }
+        }
+        if (mCbRegistered && mCallbacks.size() == 0) {
+            try {
+                mSessionBinder.unregisterCallbackListener(mCbStub);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in removeCallbackLocked");
+            }
+            mCbRegistered = false;
+        }
+        return success;
+    }
+
+    private MessageHandler getHandlerForCallbackLocked(Callback cb) {
+        if (cb == null) {
+            throw new IllegalArgumentException("Callback cannot be null");
+        }
+        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+            MessageHandler handler = mCallbacks.get(i);
+            if (cb == handler.mCallback) {
+                return handler;
+            }
+        }
+        return null;
+    }
+
+    private final void postMessage(int what, Object obj, Bundle data) {
+        synchronized (mLock) {
+            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+                mCallbacks.get(i).post(what, obj, data);
+            }
+        }
+    }
+
+    /**
+     * Callback for receiving updates from the session. A Callback can be
+     * registered using {@link #registerCallback}.
+     */
+    public static abstract class Callback {
+        /**
+         * Override to handle the session being destroyed. The session is no
+         * longer valid after this call and calls to it will be ignored.
+         */
+        public void onSessionDestroyed() {
+        }
+
+        /**
+         * Override to handle custom events sent by the session owner without a
+         * specified interface. Controllers should only handle these for
+         * sessions they own.
+         *
+         * @param event The event from the session.
+         * @param extras Optional parameters for the event, may be null.
+         */
+        public void onSessionEvent(@NonNull String event, @Nullable Bundle extras) {
+        }
+
+        /**
+         * Override to handle changes in playback state.
+         *
+         * @param state The new playback state of the session
+         */
+        public void onPlaybackStateChanged(@Nullable PlaybackState state) {
+        }
+
+        /**
+         * Override to handle changes to the current metadata.
+         *
+         * @param metadata The current metadata for the session or null if none.
+         * @see MediaMetadata
+         */
+        public void onMetadataChanged(@Nullable MediaMetadata metadata) {
+        }
+
+        /**
+         * Override to handle changes to items in the queue.
+         *
+         * @param queue A list of items in the current play queue. It should
+         *            include the currently playing item as well as previous and
+         *            upcoming items if applicable.
+         * @see MediaSession.QueueItem
+         */
+        public void onQueueChanged(@Nullable List<MediaSession.QueueItem> queue) {
+        }
+
+        /**
+         * Override to handle changes to the queue title.
+         *
+         * @param title The title that should be displayed along with the play queue such as
+         *              "Now Playing". May be null if there is no such title.
+         */
+        public void onQueueTitleChanged(@Nullable CharSequence title) {
+        }
+
+        /**
+         * Override to handle changes to the {@link MediaSession} extras.
+         *
+         * @param extras The extras that can include other information associated with the
+         *               {@link MediaSession}.
+         */
+        public void onExtrasChanged(@Nullable Bundle extras) {
+        }
+
+        /**
+         * Override to handle changes to the audio info.
+         *
+         * @param info The current audio info for this session.
+         */
+        public void onAudioInfoChanged(PlaybackInfo info) {
+        }
+    }
+
+    /**
+     * Interface for controlling media playback on a session. This allows an app
+     * to send media transport commands to the session.
+     */
+    public final class TransportControls {
+        private static final String TAG = "TransportController";
+
+        private TransportControls() {
+        }
+
+        /**
+         * Request that the player prepare its playback. In other words, other sessions can continue
+         * to play during the preparation of this session. This method can be used to speed up the
+         * start of the playback. Once the preparation is done, the session will change its playback
+         * state to {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to
+         * start playback.
+         */
+        public void prepare() {
+            try {
+                mSessionBinder.prepare("mContext.getOpPackageName()", mCbStub);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling prepare.", e);
+            }
+        }
+
+        /**
+         * Request that the player prepare playback for a specific media id. In other words, other
+         * sessions can continue to play during the preparation of this session. This method can be
+         * used to speed up the start of the playback. Once the preparation is done, the session
+         * will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
+         * {@link #play} can be called to start playback. If the preparation is not needed,
+         * {@link #playFromMediaId} can be directly called without this method.
+         *
+         * @param mediaId The id of the requested media.
+         * @param extras Optional extras that can include extra information about the media item
+         *               to be prepared.
+         */
+        public void prepareFromMediaId(String mediaId, Bundle extras) {
+            if (TextUtils.isEmpty(mediaId)) {
+                throw new IllegalArgumentException(
+                        "You must specify a non-empty String for prepareFromMediaId.");
+            }
+            try {
+                mSessionBinder.prepareFromMediaId("mContext.getOpPackageName()", mCbStub, mediaId,
+                        extras);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
+            }
+        }
+
+        /**
+         * Request that the player prepare playback for a specific search query. An empty or null
+         * query should be treated as a request to prepare any music. In other words, other sessions
+         * can continue to play during the preparation of this session. This method can be used to
+         * speed up the start of the playback. Once the preparation is done, the session will
+         * change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
+         * {@link #play} can be called to start playback. If the preparation is not needed,
+         * {@link #playFromSearch} can be directly called without this method.
+         *
+         * @param query The search query.
+         * @param extras Optional extras that can include extra information
+         *               about the query.
+         */
+        public void prepareFromSearch(String query, Bundle extras) {
+            if (query == null) {
+                // This is to remain compatible with
+                // INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
+                query = "";
+            }
+            try {
+                mSessionBinder.prepareFromSearch("mContext.getOpPackageName()", mCbStub, query,
+                        extras);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
+            }
+        }
+
+        /**
+         * Request that the player prepare playback for a specific {@link Uri}. In other words,
+         * other sessions can continue to play during the preparation of this session. This method
+         * can be used to speed up the start of the playback. Once the preparation is done, the
+         * session will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards,
+         * {@link #play} can be called to start playback. If the preparation is not needed,
+         * {@link #playFromUri} can be directly called without this method.
+         *
+         * @param uri The URI of the requested media.
+         * @param extras Optional extras that can include extra information about the media item
+         *               to be prepared.
+         */
+        public void prepareFromUri(Uri uri, Bundle extras) {
+            if (uri == null || Uri.EMPTY.equals(uri)) {
+                throw new IllegalArgumentException(
+                        "You must specify a non-empty Uri for prepareFromUri.");
+            }
+            try {
+                mSessionBinder.prepareFromUri("mContext.getOpPackageName()", mCbStub, uri, extras);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
+            }
+        }
+
+        /**
+         * Request that the player start its playback at its current position.
+         */
+        public void play() {
+            try {
+                mSessionBinder.play("mContext.getOpPackageName()", mCbStub);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling play.", e);
+            }
+        }
+
+        /**
+         * Request that the player start playback for a specific media id.
+         *
+         * @param mediaId The id of the requested media.
+         * @param extras Optional extras that can include extra information about the media item
+         *               to be played.
+         */
+        public void playFromMediaId(String mediaId, Bundle extras) {
+            if (TextUtils.isEmpty(mediaId)) {
+                throw new IllegalArgumentException(
+                        "You must specify a non-empty String for playFromMediaId.");
+            }
+            try {
+                mSessionBinder.playFromMediaId("mContext.getOpPackageName()", mCbStub, mediaId,
+                        extras);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
+            }
+        }
+
+        /**
+         * Request that the player start playback for a specific search query.
+         * An empty or null query should be treated as a request to play any
+         * music.
+         *
+         * @param query The search query.
+         * @param extras Optional extras that can include extra information
+         *               about the query.
+         */
+        public void playFromSearch(String query, Bundle extras) {
+            if (query == null) {
+                // This is to remain compatible with
+                // INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
+                query = "";
+            }
+            try {
+                mSessionBinder.playFromSearch("mContext.getOpPackageName()", mCbStub, query, extras);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling play(" + query + ").", e);
+            }
+        }
+
+        /**
+         * Request that the player start playback for a specific {@link Uri}.
+         *
+         * @param uri The URI of the requested media.
+         * @param extras Optional extras that can include extra information about the media item
+         *               to be played.
+         */
+        public void playFromUri(Uri uri, Bundle extras) {
+            if (uri == null || Uri.EMPTY.equals(uri)) {
+                throw new IllegalArgumentException(
+                        "You must specify a non-empty Uri for playFromUri.");
+            }
+            try {
+                mSessionBinder.playFromUri("mContext.getOpPackageName()", mCbStub, uri, extras);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling play(" + uri + ").", e);
+            }
+        }
+
+        /**
+         * Play an item with a specific id in the play queue. If you specify an
+         * id that is not in the play queue, the behavior is undefined.
+         */
+        public void skipToQueueItem(long id) {
+            try {
+                mSessionBinder.skipToQueueItem("mContext.getOpPackageName()", mCbStub, id);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
+            }
+        }
+
+        /**
+         * Request that the player pause its playback and stay at its current
+         * position.
+         */
+        public void pause() {
+            try {
+                mSessionBinder.pause("mContext.getOpPackageName()", mCbStub);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling pause.", e);
+            }
+        }
+
+        /**
+         * Request that the player stop its playback; it may clear its state in
+         * whatever way is appropriate.
+         */
+        public void stop() {
+            try {
+                mSessionBinder.stop("mContext.getOpPackageName()", mCbStub);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling stop.", e);
+            }
+        }
+
+        /**
+         * Move to a new location in the media stream.
+         *
+         * @param pos Position to move to, in milliseconds.
+         */
+        public void seekTo(long pos) {
+            try {
+                mSessionBinder.seekTo("mContext.getOpPackageName()", mCbStub, pos);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling seekTo.", e);
+            }
+        }
+
+        /**
+         * Start fast forwarding. If playback is already fast forwarding this
+         * may increase the rate.
+         */
+        public void fastForward() {
+            try {
+                mSessionBinder.fastForward("mContext.getOpPackageName()", mCbStub);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling fastForward.", e);
+            }
+        }
+
+        /**
+         * Skip to the next item.
+         */
+        public void skipToNext() {
+            try {
+                mSessionBinder.next("mContext.getOpPackageName()", mCbStub);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling next.", e);
+            }
+        }
+
+        /**
+         * Start rewinding. If playback is already rewinding this may increase
+         * the rate.
+         */
+        public void rewind() {
+            try {
+                mSessionBinder.rewind("mContext.getOpPackageName()", mCbStub);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling rewind.", e);
+            }
+        }
+
+        /**
+         * Skip to the previous item.
+         */
+        public void skipToPrevious() {
+            try {
+                mSessionBinder.previous("mContext.getOpPackageName()", mCbStub);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling previous.", e);
+            }
+        }
+
+        /**
+         * Rate the current content. This will cause the rating to be set for
+         * the current user. The Rating type must match the type returned by
+         * {@link #getRatingType()}.
+         *
+         * @param rating The rating to set for the current content
+         */
+        public void setRating(Rating rating) {
+            try {
+                mSessionBinder.rate("mContext.getOpPackageName()", mCbStub, rating);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling rate.", e);
+            }
+        }
+
+        /**
+         * Send a custom action back for the {@link MediaSession} to perform.
+         *
+         * @param customAction The action to perform.
+         * @param args Optional arguments to supply to the {@link MediaSession} for this
+         *             custom action.
+         */
+        public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
+                @Nullable Bundle args) {
+            if (customAction == null) {
+                throw new IllegalArgumentException("CustomAction cannot be null.");
+            }
+            sendCustomAction(customAction.getAction(), args);
+        }
+
+        /**
+         * Send the id and args from a custom action back for the {@link MediaSession} to perform.
+         *
+         * @see #sendCustomAction(PlaybackState.CustomAction action, Bundle args)
+         * @param action The action identifier of the {@link PlaybackState.CustomAction} as
+         *               specified by the {@link MediaSession}.
+         * @param args Optional arguments to supply to the {@link MediaSession} for this
+         *             custom action.
+         */
+        public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
+            if (TextUtils.isEmpty(action)) {
+                throw new IllegalArgumentException("CustomAction cannot be null.");
+            }
+            try {
+                mSessionBinder.sendCustomAction("mContext.getOpPackageName()", mCbStub, action, args);
+            } catch (RemoteException e) {
+                Log.d(TAG, "Dead object in sendCustomAction.", e);
+            }
+        }
+    }
+
+    /**
+     * Holds information about the current playback and how audio is handled for
+     * this session.
+     */
+    public static final class PlaybackInfo {
+        /**
+         * The session uses remote playback.
+         */
+        public static final int PLAYBACK_TYPE_REMOTE = 2;
+        /**
+         * The session uses local playback.
+         */
+        public static final int PLAYBACK_TYPE_LOCAL = 1;
+
+        private final int mVolumeType;
+        private final int mVolumeControl;
+        private final int mMaxVolume;
+        private final int mCurrentVolume;
+        private final AudioAttributes mAudioAttrs;
+
+        /**
+         * @hide
+         */
+        public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) {
+            mVolumeType = type;
+            mAudioAttrs = attrs;
+            mVolumeControl = control;
+            mMaxVolume = max;
+            mCurrentVolume = current;
+        }
+
+        /**
+         * Get the type of playback which affects volume handling. One of:
+         * <ul>
+         * <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
+         * <li>{@link #PLAYBACK_TYPE_REMOTE}</li>
+         * </ul>
+         *
+         * @return The type of playback this session is using.
+         */
+        public int getPlaybackType() {
+            return mVolumeType;
+        }
+
+        /**
+         * Get the audio attributes for this session. The attributes will affect
+         * volume handling for the session. When the volume type is
+         * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
+         * remote volume handler.
+         *
+         * @return The attributes for this session.
+         */
+        public AudioAttributes getAudioAttributes() {
+            return mAudioAttrs;
+        }
+
+        /**
+         * Get the type of volume control that can be used. One of:
+         * <ul>
+         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
+         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
+         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
+         * </ul>
+         *
+         * @return The type of volume control that may be used with this
+         *         session.
+         */
+        public int getVolumeControl() {
+            return mVolumeControl;
+        }
+
+        /**
+         * Get the maximum volume that may be set for this session.
+         *
+         * @return The maximum allowed volume where this session is playing.
+         */
+        public int getMaxVolume() {
+            return mMaxVolume;
+        }
+
+        /**
+         * Get the current volume for this session.
+         *
+         * @return The current volume where this session is playing.
+         */
+        public int getCurrentVolume() {
+            return mCurrentVolume;
+        }
+    }
+
+    private final static class CallbackStub extends ISessionControllerCallback.Stub {
+        private final WeakReference<MediaController> mController;
+
+        public CallbackStub(MediaController controller) {
+            mController = new WeakReference<MediaController>(controller);
+        }
+
+        @Override
+        public void onSessionDestroyed() {
+            MediaController controller = mController.get();
+            if (controller != null) {
+                controller.postMessage(MSG_DESTROYED, null, null);
+            }
+        }
+
+        @Override
+        public void onEvent(String event, Bundle extras) {
+            MediaController controller = mController.get();
+            if (controller != null) {
+                controller.postMessage(MSG_EVENT, event, extras);
+            }
+        }
+
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            MediaController controller = mController.get();
+            if (controller != null) {
+                controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
+            }
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            MediaController controller = mController.get();
+            if (controller != null) {
+                controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
+            }
+        }
+
+        //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice.
+        /*
+        @Override
+        public void onQueueChanged(ParceledListSlice parceledQueue) {
+            List<MediaSession.QueueItem> queue = parceledQueue == null ? null : parceledQueue
+                    .getList();
+            MediaController controller = mController.get();
+            if (controller != null) {
+                controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
+            }
+        }
+        */
+
+        @Override
+        public void onQueueTitleChanged(CharSequence title) {
+            MediaController controller = mController.get();
+            if (controller != null) {
+                controller.postMessage(MSG_UPDATE_QUEUE_TITLE, title, null);
+            }
+        }
+
+        @Override
+        public void onExtrasChanged(Bundle extras) {
+            MediaController controller = mController.get();
+            if (controller != null) {
+                controller.postMessage(MSG_UPDATE_EXTRAS, extras, null);
+            }
+        }
+
+        @Override
+        public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
+            MediaController controller = mController.get();
+            if (controller != null) {
+                PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs,
+                        pvi.controlType, pvi.maxVolume, pvi.currentVolume);
+                controller.postMessage(MSG_UPDATE_VOLUME, info, null);
+            }
+        }
+
+    }
+
+    private final static class MessageHandler extends Handler {
+        private final MediaController.Callback mCallback;
+        private boolean mRegistered = false;
+
+        public MessageHandler(Looper looper, MediaController.Callback cb) {
+            //TODO:(b/119539849) Uncomment below line and resolve the error.
+            // super(looper, null, true);
+            mCallback = cb;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (!mRegistered) {
+                return;
+            }
+            switch (msg.what) {
+                case MSG_EVENT:
+                    mCallback.onSessionEvent((String) msg.obj, msg.getData());
+                    break;
+                case MSG_UPDATE_PLAYBACK_STATE:
+                    mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
+                    break;
+                case MSG_UPDATE_METADATA:
+                    mCallback.onMetadataChanged((MediaMetadata) msg.obj);
+                    break;
+                case MSG_UPDATE_QUEUE:
+                    mCallback.onQueueChanged((List<MediaSession.QueueItem>) msg.obj);
+                    break;
+                case MSG_UPDATE_QUEUE_TITLE:
+                    mCallback.onQueueTitleChanged((CharSequence) msg.obj);
+                    break;
+                case MSG_UPDATE_EXTRAS:
+                    mCallback.onExtrasChanged((Bundle) msg.obj);
+                    break;
+                case MSG_UPDATE_VOLUME:
+                    mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj);
+                    break;
+                case MSG_DESTROYED:
+                    mCallback.onSessionDestroyed();
+                    break;
+            }
+        }
+
+        public void post(int what, Object obj, Bundle data) {
+            Message msg = obtainMessage(what, obj);
+            msg.setData(data);
+            msg.sendToTarget();
+        }
+    }
+
+}
diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl b/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl
new file mode 100644
index 0000000..f657cef
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.aidl
@@ -0,0 +1,19 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media.session;
+
+parcelable MediaSession.Token;
+parcelable MediaSession.QueueItem;
\ No newline at end of file
diff --git a/packages/MediaComponents/apex/java/android/media/session/MediaSession.java b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java
new file mode 100644
index 0000000..b3ebbc8
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/MediaSession.java
@@ -0,0 +1,1571 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+//import android.content.pm.ParceledListSlice;
+import android.media.AudioAttributes;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.Rating;
+import android.media.VolumeProvider;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.UserHandle;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
+import android.service.media.MediaBrowserService;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Allows interaction with media controllers, volume keys, media buttons, and
+ * transport controls.
+ * <p>
+ * A MediaSession should be created when an app wants to publish media playback
+ * information or handle media keys. In general an app only needs one session
+ * for all playback, though multiple sessions can be created to provide finer
+ * grain controls of media.
+ * <p>
+ * Once a session is created the owner of the session may pass its
+ * {@link #getSessionToken() session token} to other processes to allow them to
+ * create a {@link MediaController} to interact with the session.
+ * <p>
+ * To receive commands, media keys, and other events a {@link Callback} must be
+ * set with {@link #setCallback(Callback)} and {@link #setActive(boolean)
+ * setActive(true)} must be called.
+ * <p>
+ * When an app is finished performing playback it must call {@link #release()}
+ * to clean up the session and notify any controllers.
+ * <p>
+ * MediaSession objects are thread safe.
+ */
+public final class MediaSession {
+    private static final String TAG = "MediaSession";
+
+    /**
+     * Set this flag on the session to indicate that it can handle media button
+     * events.
+     * @deprecated This flag is no longer used. All media sessions are expected to handle media
+     * button events now.
+     */
+    @Deprecated
+    public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0;
+
+    /**
+     * Set this flag on the session to indicate that it handles transport
+     * control commands through its {@link Callback}.
+     * @deprecated This flag is no longer used. All media sessions are expected to handle transport
+     * controls now.
+     */
+    @Deprecated
+    public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
+
+    /**
+     * System only flag for a session that needs to have priority over all other
+     * sessions. This flag ensures this session will receive media button events
+     * regardless of the current ordering in the system.
+     *
+     * @hide
+     */
+    public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16;
+
+    /**
+     * @hide
+     */
+    public static final int INVALID_UID = -1;
+
+    /**
+     * @hide
+     */
+    public static final int INVALID_PID = -1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {
+            FLAG_HANDLES_MEDIA_BUTTONS,
+            FLAG_HANDLES_TRANSPORT_CONTROLS,
+            FLAG_EXCLUSIVE_GLOBAL_PRIORITY })
+    public @interface SessionFlags { }
+
+    private final Object mLock = new Object();
+    private final int mMaxBitmapSize;
+
+    private final MediaSession.Token mSessionToken;
+    private final MediaController mController;
+    private final ISession mBinder;
+    private final CallbackStub mCbStub;
+
+    // Do not change the name of mCallback. Support lib accesses this by using reflection.
+    @UnsupportedAppUsage
+    private CallbackMessageHandler mCallback;
+    private VolumeProvider mVolumeProvider;
+    private PlaybackState mPlaybackState;
+
+    private boolean mActive = false;
+
+    /**
+     * Creates a new session. The session will automatically be registered with
+     * the system but will not be published until {@link #setActive(boolean)
+     * setActive(true)} is called. You must call {@link #release()} when
+     * finished with the session.
+     *
+     * @param context The context to use to create the session.
+     * @param tag A short name for debugging purposes.
+     */
+    public MediaSession(@NonNull Context context, @NonNull String tag) {
+        //TODO(b/119749861): Resolve hidden API usage, UserHandle.myUserId
+        //this(context, tag, UserHandle.myUserId());
+        this(context, tag, 0);  //TODO: remove this.
+    }
+
+    /**
+     * Creates a new session as the specified user. To create a session as a
+     * user other than your own you must hold the
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+     * permission.
+     *
+     * @param context The context to use to create the session.
+     * @param tag A short name for debugging purposes.
+     * @param userId The user id to create the session as.
+     * @hide
+     */
+    public MediaSession(@NonNull Context context, @NonNull String tag, int userId) {
+        if (context == null) {
+            throw new IllegalArgumentException("context cannot be null.");
+        }
+        if (TextUtils.isEmpty(tag)) {
+            throw new IllegalArgumentException("tag cannot be null or empty");
+        }
+        //TODO(b/119749798): Resolve hidden API usage. com.android.internal.R
+        //mMaxBitmapSize = context.getResources().getDimensionPixelSize(
+                //com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize);
+        mMaxBitmapSize = 1024;  //TODO: remove this.
+        mCbStub = new CallbackStub(this);
+        MediaSessionManager manager = (MediaSessionManager) context
+                .getSystemService(Context.MEDIA_SESSION_SERVICE);
+        try {
+            //TODO(b/119749862): Resolve hidden API usage. MediaSessioManager#createSession
+            //mBinder = manager.createSession(mCbStub, tag, userId);
+            mBinder = null;  //TODO: remove this.
+            mSessionToken = new Token(mBinder.getController());
+            mController = new MediaController(context, mSessionToken);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Remote error creating session.", e);
+        }
+    }
+
+    /**
+     * Set the callback to receive updates for the MediaSession. This includes
+     * media button events and transport controls. The caller's thread will be
+     * used to post updates.
+     * <p>
+     * Set the callback to null to stop receiving updates.
+     *
+     * @param callback The callback object
+     */
+    public void setCallback(@Nullable Callback callback) {
+        setCallback(callback, null);
+    }
+
+    /**
+     * Set the callback to receive updates for the MediaSession. This includes
+     * media button events and transport controls.
+     * <p>
+     * Set the callback to null to stop receiving updates.
+     *
+     * @param callback The callback to receive updates on.
+     * @param handler The handler that events should be posted on.
+     */
+    public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
+        synchronized (mLock) {
+            if (mCallback != null) {
+                // We're updating the callback, clear the session from the old one.
+                mCallback.mCallback.mSession = null;
+                mCallback.removeCallbacksAndMessages(null);
+            }
+            if (callback == null) {
+                mCallback = null;
+                return;
+            }
+            if (handler == null) {
+                handler = new Handler();
+            }
+            callback.mSession = this;
+            CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),
+                    callback);
+            mCallback = msgHandler;
+        }
+    }
+
+    /**
+     * Set an intent for launching UI for this Session. This can be used as a
+     * quick link to an ongoing media screen. The intent should be for an
+     * activity that may be started using {@link Activity#startActivity(Intent)}.
+     *
+     * @param pi The intent to launch to show UI for this Session.
+     */
+    public void setSessionActivity(@Nullable PendingIntent pi) {
+        try {
+            mBinder.setLaunchPendingIntent(pi);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setLaunchPendingIntent.", e);
+        }
+    }
+
+    /**
+     * Set a pending intent for your media button receiver to allow restarting
+     * playback after the session has been stopped. If your app is started in
+     * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via
+     * the pending intent.
+     *
+     * @param mbr The {@link PendingIntent} to send the media button event to.
+     */
+    public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
+        try {
+            mBinder.setMediaButtonReceiver(mbr);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
+        }
+    }
+
+    /**
+     * Set any flags for the session.
+     *
+     * @param flags The flags to set for this session.
+     */
+    public void setFlags(@SessionFlags int flags) {
+        try {
+            mBinder.setFlags(flags);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setFlags.", e);
+        }
+    }
+
+    /**
+     * Set the attributes for this session's audio. This will affect the
+     * system's volume handling for this session. If
+     * {@link #setPlaybackToRemote} was previously called it will stop receiving
+     * volume commands and the system will begin sending volume changes to the
+     * appropriate stream.
+     * <p>
+     * By default sessions use attributes for media.
+     *
+     * @param attributes The {@link AudioAttributes} for this session's audio.
+     */
+    public void setPlaybackToLocal(AudioAttributes attributes) {
+        if (attributes == null) {
+            throw new IllegalArgumentException("Attributes cannot be null for local playback.");
+        }
+        //TODO(b/119751592): Decide if AudioAttributes should be updated.
+        /*
+        try {
+            mBinder.setPlaybackToLocal(attributes);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
+        }
+        */
+    }
+
+    /**
+     * Configure this session to use remote volume handling. This must be called
+     * to receive volume button events, otherwise the system will adjust the
+     * appropriate stream volume for this session. If
+     * {@link #setPlaybackToLocal} was previously called the system will stop
+     * handling volume changes for this session and pass them to the volume
+     * provider instead.
+     *
+     * @param volumeProvider The provider that will handle volume changes. May
+     *            not be null.
+     */
+    public void setPlaybackToRemote(@NonNull VolumeProvider volumeProvider) {
+        if (volumeProvider == null) {
+            throw new IllegalArgumentException("volumeProvider may not be null!");
+        }
+        synchronized (mLock) {
+            mVolumeProvider = volumeProvider;
+        }
+        volumeProvider.setCallback(new VolumeProvider.Callback() {
+            @Override
+            public void onVolumeChanged(VolumeProvider volumeProvider) {
+                notifyRemoteVolumeChanged(volumeProvider);
+            }
+        });
+
+        try {
+            mBinder.setPlaybackToRemote(volumeProvider.getVolumeControl(),
+                    volumeProvider.getMaxVolume());
+            mBinder.setCurrentVolume(volumeProvider.getCurrentVolume());
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
+        }
+    }
+
+    /**
+     * Set if this session is currently active and ready to receive commands. If
+     * set to false your session's controller may not be discoverable. You must
+     * set the session to active before it can start receiving media button
+     * events or transport commands.
+     *
+     * @param active Whether this session is active or not.
+     */
+    public void setActive(boolean active) {
+        if (mActive == active) {
+            return;
+        }
+        try {
+            mBinder.setActive(active);
+            mActive = active;
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setActive.", e);
+        }
+    }
+
+    /**
+     * Get the current active state of this session.
+     *
+     * @return True if the session is active, false otherwise.
+     */
+    public boolean isActive() {
+        return mActive;
+    }
+
+    /**
+     * Send a proprietary event to all MediaControllers listening to this
+     * Session. It's up to the Controller/Session owner to determine the meaning
+     * of any events.
+     *
+     * @param event The name of the event to send
+     * @param extras Any extras included with the event
+     */
+    public void sendSessionEvent(@NonNull String event, @Nullable Bundle extras) {
+        if (TextUtils.isEmpty(event)) {
+            throw new IllegalArgumentException("event cannot be null or empty");
+        }
+        try {
+            mBinder.sendEvent(event, extras);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error sending event", e);
+        }
+    }
+
+    /**
+     * This must be called when an app has finished performing playback. If
+     * playback is expected to start again shortly the session can be left open,
+     * but it must be released if your activity or service is being destroyed.
+     */
+    public void release() {
+        try {
+            mBinder.destroy();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error releasing session: ", e);
+        }
+    }
+
+    /**
+     * Retrieve a token object that can be used by apps to create a
+     * {@link MediaController} for interacting with this session. The owner of
+     * the session is responsible for deciding how to distribute these tokens.
+     *
+     * @return A token that can be used to create a MediaController for this
+     *         session
+     */
+    public @NonNull Token getSessionToken() {
+        return mSessionToken;
+    }
+
+    /**
+     * Get a controller for this session. This is a convenience method to avoid
+     * having to cache your own controller in process.
+     *
+     * @return A controller for this session.
+     */
+    public @NonNull MediaController getController() {
+        return mController;
+    }
+
+    /**
+     * Update the current playback state.
+     *
+     * @param state The current state of playback
+     */
+    public void setPlaybackState(@Nullable PlaybackState state) {
+        mPlaybackState = state;
+        try {
+            mBinder.setPlaybackState(state);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Dead object in setPlaybackState.", e);
+        }
+    }
+
+    /**
+     * Update the current metadata. New metadata can be created using
+     * {@link android.media.MediaMetadata.Builder}. This operation may take time proportional to
+     * the size of the bitmap to replace large bitmaps with a scaled down copy.
+     *
+     * @param metadata The new metadata
+     * @see android.media.MediaMetadata.Builder#putBitmap
+     */
+    public void setMetadata(@Nullable MediaMetadata metadata) {
+        if (metadata != null) {
+            metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build();
+        }
+        try {
+            mBinder.setMetadata(metadata);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Dead object in setPlaybackState.", e);
+        }
+    }
+
+    /**
+     * Update the list of items in the play queue. It is an ordered list and
+     * should contain the current item, and previous or upcoming items if they
+     * exist. Specify null if there is no current play queue.
+     * <p>
+     * The queue should be of reasonable size. If the play queue is unbounded
+     * within your app, it is better to send a reasonable amount in a sliding
+     * window instead.
+     *
+     * @param queue A list of items in the play queue.
+     */
+    public void setQueue(@Nullable List<QueueItem> queue) {
+        //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice.
+        /*
+        try {
+            mBinder.setQueue(queue == null ? null : new ParceledListSlice<QueueItem>(queue));
+        } catch (RemoteException e) {
+            Log.wtf("Dead object in setQueue.", e);
+        }
+        */
+    }
+
+    /**
+     * Set the title of the play queue. The UI should display this title along
+     * with the play queue itself.
+     * e.g. "Play Queue", "Now Playing", or an album name.
+     *
+     * @param title The title of the play queue.
+     */
+    public void setQueueTitle(@Nullable CharSequence title) {
+        try {
+            mBinder.setQueueTitle(title);
+        } catch (RemoteException e) {
+            Log.wtf("Dead object in setQueueTitle.", e);
+        }
+    }
+
+    /**
+     * Set the style of rating used by this session. Apps trying to set the
+     * rating should use this style. Must be one of the following:
+     * <ul>
+     * <li>{@link Rating#RATING_NONE}</li>
+     * <li>{@link Rating#RATING_3_STARS}</li>
+     * <li>{@link Rating#RATING_4_STARS}</li>
+     * <li>{@link Rating#RATING_5_STARS}</li>
+     * <li>{@link Rating#RATING_HEART}</li>
+     * <li>{@link Rating#RATING_PERCENTAGE}</li>
+     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
+     * </ul>
+     */
+    public void setRatingType(@Rating.Style int type) {
+        try {
+            mBinder.setRatingType(type);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in setRatingType.", e);
+        }
+    }
+
+    /**
+     * Set some extras that can be associated with the {@link MediaSession}. No assumptions should
+     * be made as to how a {@link MediaController} will handle these extras.
+     * Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts.
+     *
+     * @param extras The extras associated with the {@link MediaSession}.
+     */
+    public void setExtras(@Nullable Bundle extras) {
+        try {
+            mBinder.setExtras(extras);
+        } catch (RemoteException e) {
+            Log.wtf("Dead object in setExtras.", e);
+        }
+    }
+
+    /**
+     * Gets the controller information who sent the current request.
+     * <p>
+     * Note: This is only valid while in a request callback, such as {@link Callback#onPlay}.
+     *
+     * @throws IllegalStateException If this method is called outside of {@link Callback} methods.
+     * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
+     */
+    public final @NonNull RemoteUserInfo getCurrentControllerInfo() {
+        if (mCallback == null || mCallback.mCurrentControllerInfo == null) {
+            throw new IllegalStateException(
+                    "This should be called inside of MediaSession.Callback methods");
+        }
+        return mCallback.mCurrentControllerInfo;
+    }
+
+    /**
+     * Notify the system that the remote volume changed.
+     *
+     * @param provider The provider that is handling volume changes.
+     * @hide
+     */
+    public void notifyRemoteVolumeChanged(VolumeProvider provider) {
+        synchronized (mLock) {
+            if (provider == null || provider != mVolumeProvider) {
+                Log.w(TAG, "Received update from stale volume provider");
+                return;
+            }
+        }
+        try {
+            mBinder.setCurrentVolume(provider.getCurrentVolume());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in notifyVolumeChanged", e);
+        }
+    }
+
+    /**
+     * Returns the name of the package that sent the last media button, transport control, or
+     * command from controllers and the system. This is only valid while in a request callback, such
+     * as {@link Callback#onPlay}.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public String getCallingPackage() {
+        if (mCallback != null && mCallback.mCurrentControllerInfo != null) {
+            return mCallback.mCurrentControllerInfo.getPackageName();
+        }
+        return null;
+    }
+
+    private void dispatchPrepare(RemoteUserInfo caller) {
+        postToCallback(caller, CallbackMessageHandler.MSG_PREPARE, null, null);
+    }
+
+    private void dispatchPrepareFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
+        postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras);
+    }
+
+    private void dispatchPrepareFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
+        postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_SEARCH, query, extras);
+    }
+
+    private void dispatchPrepareFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
+        postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_URI, uri, extras);
+    }
+
+    private void dispatchPlay(RemoteUserInfo caller) {
+        postToCallback(caller, CallbackMessageHandler.MSG_PLAY, null, null);
+    }
+
+    private void dispatchPlayFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
+        postToCallback(caller, CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras);
+    }
+
+    private void dispatchPlayFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
+        postToCallback(caller, CallbackMessageHandler.MSG_PLAY_SEARCH, query, extras);
+    }
+
+    private void dispatchPlayFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
+        postToCallback(caller, CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
+    }
+
+    private void dispatchSkipToItem(RemoteUserInfo caller, long id) {
+        postToCallback(caller, CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, null);
+    }
+
+    private void dispatchPause(RemoteUserInfo caller) {
+        postToCallback(caller, CallbackMessageHandler.MSG_PAUSE, null, null);
+    }
+
+    private void dispatchStop(RemoteUserInfo caller) {
+        postToCallback(caller, CallbackMessageHandler.MSG_STOP, null, null);
+    }
+
+    private void dispatchNext(RemoteUserInfo caller) {
+        postToCallback(caller, CallbackMessageHandler.MSG_NEXT, null, null);
+    }
+
+    private void dispatchPrevious(RemoteUserInfo caller) {
+        postToCallback(caller, CallbackMessageHandler.MSG_PREVIOUS, null, null);
+    }
+
+    private void dispatchFastForward(RemoteUserInfo caller) {
+        postToCallback(caller, CallbackMessageHandler.MSG_FAST_FORWARD, null, null);
+    }
+
+    private void dispatchRewind(RemoteUserInfo caller) {
+        postToCallback(caller, CallbackMessageHandler.MSG_REWIND, null, null);
+    }
+
+    private void dispatchSeekTo(RemoteUserInfo caller, long pos) {
+        postToCallback(caller, CallbackMessageHandler.MSG_SEEK_TO, pos, null);
+    }
+
+    private void dispatchRate(RemoteUserInfo caller, Rating rating) {
+        postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null);
+    }
+
+    private void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) {
+        postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
+    }
+
+    private void dispatchMediaButton(RemoteUserInfo caller, Intent mediaButtonIntent) {
+        postToCallback(caller, CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, null);
+    }
+
+    private void dispatchMediaButtonDelayed(RemoteUserInfo info, Intent mediaButtonIntent,
+            long delay) {
+        postToCallbackDelayed(info, CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT,
+                mediaButtonIntent, null, delay);
+    }
+
+    private void dispatchAdjustVolume(RemoteUserInfo caller, int direction) {
+        postToCallback(caller, CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, null);
+    }
+
+    private void dispatchSetVolumeTo(RemoteUserInfo caller, int volume) {
+        postToCallback(caller, CallbackMessageHandler.MSG_SET_VOLUME, volume, null);
+    }
+
+    private void dispatchCommand(RemoteUserInfo caller, String command, Bundle args,
+            ResultReceiver resultCb) {
+        Command cmd = new Command(command, args, resultCb);
+        postToCallback(caller, CallbackMessageHandler.MSG_COMMAND, cmd, null);
+    }
+
+    private void postToCallback(RemoteUserInfo caller, int what, Object obj, Bundle data) {
+        postToCallbackDelayed(caller, what, obj, data, 0);
+    }
+
+    private void postToCallbackDelayed(RemoteUserInfo caller, int what, Object obj, Bundle data,
+            long delay) {
+        synchronized (mLock) {
+            if (mCallback != null) {
+                mCallback.post(caller, what, obj, data, delay);
+            }
+        }
+    }
+
+    /**
+     * Return true if this is considered an active playback state.
+     *
+     * @hide
+     */
+    public static boolean isActiveState(int state) {
+        switch (state) {
+            case PlaybackState.STATE_FAST_FORWARDING:
+            case PlaybackState.STATE_REWINDING:
+            case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
+            case PlaybackState.STATE_SKIPPING_TO_NEXT:
+            case PlaybackState.STATE_BUFFERING:
+            case PlaybackState.STATE_CONNECTING:
+            case PlaybackState.STATE_PLAYING:
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Represents an ongoing session. This may be passed to apps by the session
+     * owner to allow them to create a {@link MediaController} to communicate with
+     * the session.
+     */
+    public static final class Token implements Parcelable {
+
+        private ISessionController mBinder;
+
+        /**
+         * @hide
+         */
+        public Token(ISessionController binder) {
+            mBinder = binder;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeStrongBinder(mBinder.asBinder());
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((mBinder == null) ? 0 : mBinder.asBinder().hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            Token other = (Token) obj;
+            if (mBinder == null) {
+                if (other.mBinder != null)
+                    return false;
+            } else if (!mBinder.asBinder().equals(other.mBinder.asBinder()))
+                return false;
+            return true;
+        }
+
+        ISessionController getBinder() {
+            return mBinder;
+        }
+
+        public static final Parcelable.Creator<Token> CREATOR
+                = new Parcelable.Creator<Token>() {
+            @Override
+            public Token createFromParcel(Parcel in) {
+                return new Token(ISessionController.Stub.asInterface(in.readStrongBinder()));
+            }
+
+            @Override
+            public Token[] newArray(int size) {
+                return new Token[size];
+            }
+        };
+    }
+
+    /**
+     * Receives media buttons, transport controls, and commands from controllers
+     * and the system. A callback may be set using {@link #setCallback}.
+     */
+    public abstract static class Callback {
+
+        private MediaSession mSession;
+        private CallbackMessageHandler mHandler;
+        private boolean mMediaPlayPauseKeyPending;
+
+        public Callback() {
+        }
+
+        /**
+         * Called when a controller has sent a command to this session.
+         * The owner of the session may handle custom commands but is not
+         * required to.
+         *
+         * @param command The command name.
+         * @param args Optional parameters for the command, may be null.
+         * @param cb A result receiver to which a result may be sent by the command, may be null.
+         */
+        public void onCommand(@NonNull String command, @Nullable Bundle args,
+                @Nullable ResultReceiver cb) {
+        }
+
+        /**
+         * Called when a media button is pressed and this session has the
+         * highest priority or a controller sends a media button event to the
+         * session. The default behavior will call the relevant method if the
+         * action for it was set.
+         * <p>
+         * The intent will be of type {@link Intent#ACTION_MEDIA_BUTTON} with a
+         * KeyEvent in {@link Intent#EXTRA_KEY_EVENT}
+         *
+         * @param mediaButtonIntent an intent containing the KeyEvent as an
+         *            extra
+         * @return True if the event was handled, false otherwise.
+         */
+        public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) {
+            if (mSession != null && mHandler != null
+                    && Intent.ACTION_MEDIA_BUTTON.equals(mediaButtonIntent.getAction())) {
+                KeyEvent ke = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+                if (ke != null && ke.getAction() == KeyEvent.ACTION_DOWN) {
+                    PlaybackState state = mSession.mPlaybackState;
+                    long validActions = state == null ? 0 : state.getActions();
+                    switch (ke.getKeyCode()) {
+                        case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                        case KeyEvent.KEYCODE_HEADSETHOOK:
+                            if (ke.getRepeatCount() > 0) {
+                                // Consider long-press as a single tap.
+                                handleMediaPlayPauseKeySingleTapIfPending();
+                            } else if (mMediaPlayPauseKeyPending) {
+                                // Consider double tap as the next.
+                                mHandler.removeMessages(CallbackMessageHandler
+                                        .MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
+                                mMediaPlayPauseKeyPending = false;
+                                if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
+                                    onSkipToNext();
+                                }
+                            } else {
+                                mMediaPlayPauseKeyPending = true;
+                                mSession.dispatchMediaButtonDelayed(
+                                        mSession.getCurrentControllerInfo(),
+                                        mediaButtonIntent, ViewConfiguration.getDoubleTapTimeout());
+                            }
+                            return true;
+                        default:
+                            // If another key is pressed within double tap timeout, consider the
+                            // pending play/pause as a single tap to handle media keys in order.
+                            handleMediaPlayPauseKeySingleTapIfPending();
+                            break;
+                    }
+
+                    switch (ke.getKeyCode()) {
+                        case KeyEvent.KEYCODE_MEDIA_PLAY:
+                            if ((validActions & PlaybackState.ACTION_PLAY) != 0) {
+                                onPlay();
+                                return true;
+                            }
+                            break;
+                        case KeyEvent.KEYCODE_MEDIA_PAUSE:
+                            if ((validActions & PlaybackState.ACTION_PAUSE) != 0) {
+                                onPause();
+                                return true;
+                            }
+                            break;
+                        case KeyEvent.KEYCODE_MEDIA_NEXT:
+                            if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
+                                onSkipToNext();
+                                return true;
+                            }
+                            break;
+                        case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+                            if ((validActions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) {
+                                onSkipToPrevious();
+                                return true;
+                            }
+                            break;
+                        case KeyEvent.KEYCODE_MEDIA_STOP:
+                            if ((validActions & PlaybackState.ACTION_STOP) != 0) {
+                                onStop();
+                                return true;
+                            }
+                            break;
+                        case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+                            if ((validActions & PlaybackState.ACTION_FAST_FORWARD) != 0) {
+                                onFastForward();
+                                return true;
+                            }
+                            break;
+                        case KeyEvent.KEYCODE_MEDIA_REWIND:
+                            if ((validActions & PlaybackState.ACTION_REWIND) != 0) {
+                                onRewind();
+                                return true;
+                            }
+                            break;
+                    }
+                }
+            }
+            return false;
+        }
+
+        private void handleMediaPlayPauseKeySingleTapIfPending() {
+            if (!mMediaPlayPauseKeyPending) {
+                return;
+            }
+            mMediaPlayPauseKeyPending = false;
+            mHandler.removeMessages(CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
+            PlaybackState state = mSession.mPlaybackState;
+            long validActions = state == null ? 0 : state.getActions();
+            boolean isPlaying = state != null
+                    && state.getState() == PlaybackState.STATE_PLAYING;
+            boolean canPlay = (validActions & (PlaybackState.ACTION_PLAY_PAUSE
+                        | PlaybackState.ACTION_PLAY)) != 0;
+            boolean canPause = (validActions & (PlaybackState.ACTION_PLAY_PAUSE
+                        | PlaybackState.ACTION_PAUSE)) != 0;
+            if (isPlaying && canPause) {
+                onPause();
+            } else if (!isPlaying && canPlay) {
+                onPlay();
+            }
+        }
+
+        /**
+         * Override to handle requests to prepare playback. During the preparation, a session should
+         * not hold audio focus in order to allow other sessions play seamlessly. The state of
+         * playback should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is
+         * done.
+         */
+        public void onPrepare() {
+        }
+
+        /**
+         * Override to handle requests to prepare for playing a specific mediaId that was provided
+         * by your app's {@link MediaBrowserService}. During the preparation, a session should not
+         * hold audio focus in order to allow other sessions play seamlessly. The state of playback
+         * should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+         * The playback of the prepared content should start in the implementation of
+         * {@link #onPlay}. Override {@link #onPlayFromMediaId} to handle requests for starting
+         * playback without preparation.
+         */
+        public void onPrepareFromMediaId(String mediaId, Bundle extras) {
+        }
+
+        /**
+         * Override to handle requests to prepare playback from a search query. An empty query
+         * indicates that the app may prepare any music. The implementation should attempt to make a
+         * smart choice about what to play. During the preparation, a session should not hold audio
+         * focus in order to allow other sessions play seamlessly. The state of playback should be
+         * updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done. The playback
+         * of the prepared content should start in the implementation of {@link #onPlay}. Override
+         * {@link #onPlayFromSearch} to handle requests for starting playback without preparation.
+         */
+        public void onPrepareFromSearch(String query, Bundle extras) {
+        }
+
+        /**
+         * Override to handle requests to prepare a specific media item represented by a URI.
+         * During the preparation, a session should not hold audio focus in order to allow
+         * other sessions play seamlessly. The state of playback should be updated to
+         * {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+         * The playback of the prepared content should start in the implementation of
+         * {@link #onPlay}. Override {@link #onPlayFromUri} to handle requests
+         * for starting playback without preparation.
+         */
+        public void onPrepareFromUri(Uri uri, Bundle extras) {
+        }
+
+        /**
+         * Override to handle requests to begin playback.
+         */
+        public void onPlay() {
+        }
+
+        /**
+         * Override to handle requests to begin playback from a search query. An
+         * empty query indicates that the app may play any music. The
+         * implementation should attempt to make a smart choice about what to
+         * play.
+         */
+        public void onPlayFromSearch(String query, Bundle extras) {
+        }
+
+        /**
+         * Override to handle requests to play a specific mediaId that was
+         * provided by your app's {@link MediaBrowserService}.
+         */
+        public void onPlayFromMediaId(String mediaId, Bundle extras) {
+        }
+
+        /**
+         * Override to handle requests to play a specific media item represented by a URI.
+         */
+        public void onPlayFromUri(Uri uri, Bundle extras) {
+        }
+
+        /**
+         * Override to handle requests to play an item with a given id from the
+         * play queue.
+         */
+        public void onSkipToQueueItem(long id) {
+        }
+
+        /**
+         * Override to handle requests to pause playback.
+         */
+        public void onPause() {
+        }
+
+        /**
+         * Override to handle requests to skip to the next media item.
+         */
+        public void onSkipToNext() {
+        }
+
+        /**
+         * Override to handle requests to skip to the previous media item.
+         */
+        public void onSkipToPrevious() {
+        }
+
+        /**
+         * Override to handle requests to fast forward.
+         */
+        public void onFastForward() {
+        }
+
+        /**
+         * Override to handle requests to rewind.
+         */
+        public void onRewind() {
+        }
+
+        /**
+         * Override to handle requests to stop playback.
+         */
+        public void onStop() {
+        }
+
+        /**
+         * Override to handle requests to seek to a specific position in ms.
+         *
+         * @param pos New position to move to, in milliseconds.
+         */
+        public void onSeekTo(long pos) {
+        }
+
+        /**
+         * Override to handle the item being rated.
+         *
+         * @param rating
+         */
+        public void onSetRating(@NonNull Rating rating) {
+        }
+
+        /**
+         * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
+         * performed.
+         *
+         * @param action The action that was originally sent in the
+         *               {@link PlaybackState.CustomAction}.
+         * @param extras Optional extras specified by the {@link MediaController}.
+         */
+        public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static class CallbackStub extends ISessionCallback.Stub {
+        private WeakReference<MediaSession> mMediaSession;
+
+        public CallbackStub(MediaSession session) {
+            mMediaSession = new WeakReference<>(session);
+        }
+
+        private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid,
+                ISessionControllerCallback caller) {
+            //TODO(b/119752205): Resolve hidden API usage. 4-param constructor of RemoteUserInfo
+            /*
+            return new RemoteUserInfo(packageName, pid, uid,
+                    caller != null ? caller.asBinder() : null);
+            */
+            return new RemoteUserInfo(packageName, pid, uid);
+        }
+
+        @Override
+        public void onCommand(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, String command, Bundle args, ResultReceiver cb) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchCommand(createRemoteUserInfo(packageName, pid, uid, caller),
+                        command, args, cb);
+            }
+        }
+
+        @Override
+        public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent,
+                int sequenceNumber, ResultReceiver cb) {
+            MediaSession session = mMediaSession.get();
+            try {
+                if (session != null) {
+                    session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, null),
+                            mediaButtonIntent);
+                }
+            } finally {
+                if (cb != null) {
+                    cb.send(sequenceNumber, null);
+                }
+            }
+        }
+
+        @Override
+        public void onMediaButtonFromController(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, Intent mediaButtonIntent) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, caller),
+                        mediaButtonIntent);
+            }
+        }
+
+        @Override
+        public void onPrepare(String packageName, int pid, int uid,
+                ISessionControllerCallback caller) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid, caller));
+            }
+        }
+
+        @Override
+        public void onPrepareFromMediaId(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, String mediaId,
+                Bundle extras) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchPrepareFromMediaId(
+                        createRemoteUserInfo(packageName, pid, uid, caller), mediaId, extras);
+            }
+        }
+
+        @Override
+        public void onPrepareFromSearch(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, String query,
+                Bundle extras) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchPrepareFromSearch(
+                        createRemoteUserInfo(packageName, pid, uid, caller), query, extras);
+            }
+        }
+
+        @Override
+        public void onPrepareFromUri(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, Uri uri, Bundle extras) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchPrepareFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
+                        uri, extras);
+            }
+        }
+
+        @Override
+        public void onPlay(String packageName, int pid, int uid,
+                ISessionControllerCallback caller) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchPlay(createRemoteUserInfo(packageName, pid, uid, caller));
+            }
+        }
+
+        @Override
+        public void onPlayFromMediaId(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, String mediaId,
+                Bundle extras) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchPlayFromMediaId(createRemoteUserInfo(packageName, pid, uid, caller),
+                        mediaId, extras);
+            }
+        }
+
+        @Override
+        public void onPlayFromSearch(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, String query,
+                Bundle extras) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchPlayFromSearch(createRemoteUserInfo(packageName, pid, uid, caller),
+                        query, extras);
+            }
+        }
+
+        @Override
+        public void onPlayFromUri(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, Uri uri, Bundle extras) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchPlayFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
+                        uri, extras);
+            }
+        }
+
+        @Override
+        public void onSkipToTrack(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, long id) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchSkipToItem(createRemoteUserInfo(packageName, pid, uid, caller), id);
+            }
+        }
+
+        @Override
+        public void onPause(String packageName, int pid, int uid,
+                ISessionControllerCallback caller) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchPause(createRemoteUserInfo(packageName, pid, uid, caller));
+            }
+        }
+
+        @Override
+        public void onStop(String packageName, int pid, int uid,
+                ISessionControllerCallback caller) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchStop(createRemoteUserInfo(packageName, pid, uid, caller));
+            }
+        }
+
+        @Override
+        public void onNext(String packageName, int pid, int uid,
+                ISessionControllerCallback caller) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchNext(createRemoteUserInfo(packageName, pid, uid, caller));
+            }
+        }
+
+        @Override
+        public void onPrevious(String packageName, int pid, int uid,
+                ISessionControllerCallback caller) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid, caller));
+            }
+        }
+
+        @Override
+        public void onFastForward(String packageName, int pid, int uid,
+                ISessionControllerCallback caller) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchFastForward(createRemoteUserInfo(packageName, pid, uid, caller));
+            }
+        }
+
+        @Override
+        public void onRewind(String packageName, int pid, int uid,
+                ISessionControllerCallback caller) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchRewind(createRemoteUserInfo(packageName, pid, uid, caller));
+            }
+        }
+
+        @Override
+        public void onSeekTo(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, long pos) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchSeekTo(createRemoteUserInfo(packageName, pid, uid, caller), pos);
+            }
+        }
+
+        @Override
+        public void onRate(String packageName, int pid, int uid, ISessionControllerCallback caller,
+                Rating rating) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchRate(createRemoteUserInfo(packageName, pid, uid, caller), rating);
+            }
+        }
+
+        @Override
+        public void onCustomAction(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, String action, Bundle args) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchCustomAction(createRemoteUserInfo(packageName, pid, uid, caller),
+                        action, args);
+            }
+        }
+
+        @Override
+        public void onAdjustVolume(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, int direction) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchAdjustVolume(createRemoteUserInfo(packageName, pid, uid, caller),
+                        direction);
+            }
+        }
+
+        @Override
+        public void onSetVolumeTo(String packageName, int pid, int uid,
+                ISessionControllerCallback caller, int value) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchSetVolumeTo(createRemoteUserInfo(packageName, pid, uid, caller),
+                        value);
+            }
+        }
+    }
+
+    /**
+     * A single item that is part of the play queue. It contains a description
+     * of the item and its id in the queue.
+     */
+    public static final class QueueItem implements Parcelable {
+        /**
+         * This id is reserved. No items can be explicitly assigned this id.
+         */
+        public static final int UNKNOWN_ID = -1;
+
+        private final MediaDescription mDescription;
+        @UnsupportedAppUsage
+        private final long mId;
+
+        /**
+         * Create a new {@link MediaSession.QueueItem}.
+         *
+         * @param description The {@link MediaDescription} for this item.
+         * @param id An identifier for this item. It must be unique within the
+         *            play queue and cannot be {@link #UNKNOWN_ID}.
+         */
+        public QueueItem(MediaDescription description, long id) {
+            if (description == null) {
+                throw new IllegalArgumentException("Description cannot be null.");
+            }
+            if (id == UNKNOWN_ID) {
+                throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID");
+            }
+            mDescription = description;
+            mId = id;
+        }
+
+        private QueueItem(Parcel in) {
+            mDescription = MediaDescription.CREATOR.createFromParcel(in);
+            mId = in.readLong();
+        }
+
+        /**
+         * Get the description for this item.
+         */
+        public MediaDescription getDescription() {
+            return mDescription;
+        }
+
+        /**
+         * Get the queue id for this item.
+         */
+        public long getQueueId() {
+            return mId;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            mDescription.writeToParcel(dest, flags);
+            dest.writeLong(mId);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<MediaSession.QueueItem> CREATOR =
+                new Creator<MediaSession.QueueItem>() {
+
+            @Override
+            public MediaSession.QueueItem createFromParcel(Parcel p) {
+                return new MediaSession.QueueItem(p);
+            }
+
+            @Override
+            public MediaSession.QueueItem[] newArray(int size) {
+                return new MediaSession.QueueItem[size];
+            }
+        };
+
+        @Override
+        public String toString() {
+            return "MediaSession.QueueItem {" +
+                    "Description=" + mDescription +
+                    ", Id=" + mId + " }";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == null) {
+                return false;
+            }
+
+            if (!(o instanceof QueueItem)) {
+                return false;
+            }
+
+            final QueueItem item = (QueueItem) o;
+            if (mId != item.mId) {
+                return false;
+            }
+
+            if (!Objects.equals(mDescription, item.mDescription)) {
+                return false;
+            }
+
+            return true;
+        }
+    }
+
+    private static final class Command {
+        public final String command;
+        public final Bundle extras;
+        public final ResultReceiver stub;
+
+        public Command(String command, Bundle extras, ResultReceiver stub) {
+            this.command = command;
+            this.extras = extras;
+            this.stub = stub;
+        }
+    }
+
+    private class CallbackMessageHandler extends Handler {
+        private static final int MSG_COMMAND = 1;
+        private static final int MSG_MEDIA_BUTTON = 2;
+        private static final int MSG_PREPARE = 3;
+        private static final int MSG_PREPARE_MEDIA_ID = 4;
+        private static final int MSG_PREPARE_SEARCH = 5;
+        private static final int MSG_PREPARE_URI = 6;
+        private static final int MSG_PLAY = 7;
+        private static final int MSG_PLAY_MEDIA_ID = 8;
+        private static final int MSG_PLAY_SEARCH = 9;
+        private static final int MSG_PLAY_URI = 10;
+        private static final int MSG_SKIP_TO_ITEM = 11;
+        private static final int MSG_PAUSE = 12;
+        private static final int MSG_STOP = 13;
+        private static final int MSG_NEXT = 14;
+        private static final int MSG_PREVIOUS = 15;
+        private static final int MSG_FAST_FORWARD = 16;
+        private static final int MSG_REWIND = 17;
+        private static final int MSG_SEEK_TO = 18;
+        private static final int MSG_RATE = 19;
+        private static final int MSG_CUSTOM_ACTION = 20;
+        private static final int MSG_ADJUST_VOLUME = 21;
+        private static final int MSG_SET_VOLUME = 22;
+        private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23;
+
+        private MediaSession.Callback mCallback;
+        private RemoteUserInfo mCurrentControllerInfo;
+
+        public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
+            //TODO:(b/119539849) Uncomment below line and resolve the error.
+            //super(looper, null, true);
+            mCallback = callback;
+            mCallback.mHandler = this;
+        }
+
+        public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) {
+            Pair<RemoteUserInfo, Object> objWithCaller = Pair.create(caller, obj);
+            Message msg = obtainMessage(what, objWithCaller);
+            msg.setData(data);
+            if (delayMs > 0) {
+                sendMessageDelayed(msg, delayMs);
+            } else {
+                sendMessage(msg);
+            }
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            mCurrentControllerInfo = ((Pair<RemoteUserInfo, Object>) msg.obj).first;
+
+            VolumeProvider vp;
+            Object obj = ((Pair<RemoteUserInfo, Object>) msg.obj).second;
+
+            switch (msg.what) {
+                case MSG_COMMAND:
+                    Command cmd = (Command) obj;
+                    mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
+                    break;
+                case MSG_MEDIA_BUTTON:
+                    mCallback.onMediaButtonEvent((Intent) obj);
+                    break;
+                case MSG_PREPARE:
+                    mCallback.onPrepare();
+                    break;
+                case MSG_PREPARE_MEDIA_ID:
+                    mCallback.onPrepareFromMediaId((String) obj, msg.getData());
+                    break;
+                case MSG_PREPARE_SEARCH:
+                    mCallback.onPrepareFromSearch((String) obj, msg.getData());
+                    break;
+                case MSG_PREPARE_URI:
+                    mCallback.onPrepareFromUri((Uri) obj, msg.getData());
+                    break;
+                case MSG_PLAY:
+                    mCallback.onPlay();
+                    break;
+                case MSG_PLAY_MEDIA_ID:
+                    mCallback.onPlayFromMediaId((String) obj, msg.getData());
+                    break;
+                case MSG_PLAY_SEARCH:
+                    mCallback.onPlayFromSearch((String) obj, msg.getData());
+                    break;
+                case MSG_PLAY_URI:
+                    mCallback.onPlayFromUri((Uri) obj, msg.getData());
+                    break;
+                case MSG_SKIP_TO_ITEM:
+                    mCallback.onSkipToQueueItem((Long) obj);
+                    break;
+                case MSG_PAUSE:
+                    mCallback.onPause();
+                    break;
+                case MSG_STOP:
+                    mCallback.onStop();
+                    break;
+                case MSG_NEXT:
+                    mCallback.onSkipToNext();
+                    break;
+                case MSG_PREVIOUS:
+                    mCallback.onSkipToPrevious();
+                    break;
+                case MSG_FAST_FORWARD:
+                    mCallback.onFastForward();
+                    break;
+                case MSG_REWIND:
+                    mCallback.onRewind();
+                    break;
+                case MSG_SEEK_TO:
+                    mCallback.onSeekTo((Long) obj);
+                    break;
+                case MSG_RATE:
+                    mCallback.onSetRating((Rating) obj);
+                    break;
+                case MSG_CUSTOM_ACTION:
+                    mCallback.onCustomAction((String) obj, msg.getData());
+                    break;
+                case MSG_ADJUST_VOLUME:
+                    synchronized (mLock) {
+                        vp = mVolumeProvider;
+                    }
+                    if (vp != null) {
+                        vp.onAdjustVolume((int) obj);
+                    }
+                    break;
+                case MSG_SET_VOLUME:
+                    synchronized (mLock) {
+                        vp = mVolumeProvider;
+                    }
+                    if (vp != null) {
+                        vp.onSetVolumeTo((int) obj);
+                    }
+                    break;
+                case MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT:
+                    mCallback.handleMediaPlayPauseKeySingleTapIfPending();
+                    break;
+            }
+            mCurrentControllerInfo = null;
+        }
+    }
+}
diff --git a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl
new file mode 100644
index 0000000..c4250f0
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media.session;
+
+parcelable ParcelableVolumeInfo;
diff --git a/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java
new file mode 100644
index 0000000..f59c975
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/ParcelableVolumeInfo.java
@@ -0,0 +1,80 @@
+/* Copyright 2014, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+package android.media.session;
+
+import android.media.AudioAttributes;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Convenience class for passing information about the audio configuration of a
+ * session. The public implementation is {@link MediaController.PlaybackInfo}.
+ *
+ * @hide
+ */
+public class ParcelableVolumeInfo implements Parcelable {
+    public int volumeType;
+    public AudioAttributes audioAttrs;
+    public int controlType;
+    public int maxVolume;
+    public int currentVolume;
+
+    public ParcelableVolumeInfo(int volumeType, AudioAttributes audioAttrs, int controlType,
+            int maxVolume,
+            int currentVolume) {
+        this.volumeType = volumeType;
+        this.audioAttrs = audioAttrs;
+        this.controlType = controlType;
+        this.maxVolume = maxVolume;
+        this.currentVolume = currentVolume;
+    }
+
+    public ParcelableVolumeInfo(Parcel from) {
+        volumeType = from.readInt();
+        controlType = from.readInt();
+        maxVolume = from.readInt();
+        currentVolume = from.readInt();
+        audioAttrs = AudioAttributes.CREATOR.createFromParcel(from);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(volumeType);
+        dest.writeInt(controlType);
+        dest.writeInt(maxVolume);
+        dest.writeInt(currentVolume);
+        audioAttrs.writeToParcel(dest, flags);
+    }
+
+
+    public static final Parcelable.Creator<ParcelableVolumeInfo> CREATOR
+            = new Parcelable.Creator<ParcelableVolumeInfo>() {
+        @Override
+        public ParcelableVolumeInfo createFromParcel(Parcel in) {
+            return new ParcelableVolumeInfo(in);
+        }
+
+        @Override
+        public ParcelableVolumeInfo[] newArray(int size) {
+            return new ParcelableVolumeInfo[size];
+        }
+    };
+}
diff --git a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl
new file mode 100644
index 0000000..0876ebd
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media.session;
+
+parcelable PlaybackState;
diff --git a/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java
new file mode 100644
index 0000000..ed4f9af
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/media/session/PlaybackState.java
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.session;
+
+import android.annotation.DrawableRes;
+import android.annotation.IntDef;
+import android.annotation.LongDef;
+import android.annotation.Nullable;
+import android.media.RemoteControlClient;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import java.util.ArrayList;
+import java.util.List;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Playback state for a {@link MediaSession}. This includes a state like
+ * {@link PlaybackState#STATE_PLAYING}, the current playback position,
+ * and the current control capabilities.
+ */
+public final class PlaybackState implements Parcelable {
+    private static final String TAG = "PlaybackState";
+
+    /**
+     * @hide
+     */
+    @LongDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND,
+            ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING,
+            ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
+            ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE,
+            ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Actions {}
+
+    /**
+     * Indicates this session supports the stop command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_STOP = 1 << 0;
+
+    /**
+     * Indicates this session supports the pause command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_PAUSE = 1 << 1;
+
+    /**
+     * Indicates this session supports the play command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_PLAY = 1 << 2;
+
+    /**
+     * Indicates this session supports the rewind command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_REWIND = 1 << 3;
+
+    /**
+     * Indicates this session supports the previous command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4;
+
+    /**
+     * Indicates this session supports the next command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_SKIP_TO_NEXT = 1 << 5;
+
+    /**
+     * Indicates this session supports the fast forward command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_FAST_FORWARD = 1 << 6;
+
+    /**
+     * Indicates this session supports the set rating command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_SET_RATING = 1 << 7;
+
+    /**
+     * Indicates this session supports the seek to command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_SEEK_TO = 1 << 8;
+
+    /**
+     * Indicates this session supports the play/pause toggle command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_PLAY_PAUSE = 1 << 9;
+
+    /**
+     * Indicates this session supports the play from media id command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10;
+
+    /**
+     * Indicates this session supports the play from search command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11;
+
+    /**
+     * Indicates this session supports the skip to queue item command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12;
+
+    /**
+     * Indicates this session supports the play from URI command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_PLAY_FROM_URI = 1 << 13;
+
+    /**
+     * Indicates this session supports the prepare command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_PREPARE = 1 << 14;
+
+    /**
+     * Indicates this session supports the prepare from media id command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_PREPARE_FROM_MEDIA_ID = 1 << 15;
+
+    /**
+     * Indicates this session supports the prepare from search command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_PREPARE_FROM_SEARCH = 1 << 16;
+
+    /**
+     * Indicates this session supports the prepare from URI command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_PREPARE_FROM_URI = 1 << 17;
+
+    /**
+     * @hide
+     */
+    @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING,
+            STATE_REWINDING, STATE_BUFFERING, STATE_ERROR, STATE_CONNECTING,
+            STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {}
+
+    /**
+     * This is the default playback state and indicates that no media has been
+     * added yet, or the performer has been reset and has no content to play.
+     *
+     * @see Builder#setState(int, long, float)
+     * @see Builder#setState(int, long, float, long)
+     */
+    public final static int STATE_NONE = 0;
+
+    /**
+     * State indicating this item is currently stopped.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_STOPPED = 1;
+
+    /**
+     * State indicating this item is currently paused.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_PAUSED = 2;
+
+    /**
+     * State indicating this item is currently playing.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_PLAYING = 3;
+
+    /**
+     * State indicating this item is currently fast forwarding.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_FAST_FORWARDING = 4;
+
+    /**
+     * State indicating this item is currently rewinding.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_REWINDING = 5;
+
+    /**
+     * State indicating this item is currently buffering and will begin playing
+     * when enough data has buffered.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_BUFFERING = 6;
+
+    /**
+     * State indicating this item is currently in an error state. The error
+     * message should also be set when entering this state.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_ERROR = 7;
+
+    /**
+     * State indicating the class doing playback is currently connecting to a
+     * new destination.  Depending on the implementation you may return to the previous
+     * state when the connection finishes or enter {@link #STATE_NONE}.
+     * If the connection failed {@link #STATE_ERROR} should be used.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_CONNECTING = 8;
+
+    /**
+     * State indicating the player is currently skipping to the previous item.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_SKIPPING_TO_PREVIOUS = 9;
+
+    /**
+     * State indicating the player is currently skipping to the next item.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_SKIPPING_TO_NEXT = 10;
+
+    /**
+     * State indicating the player is currently skipping to a specific item in
+     * the queue.
+     *
+     * @see Builder#setState
+     */
+    public final static int STATE_SKIPPING_TO_QUEUE_ITEM = 11;
+
+    /**
+     * Use this value for the position to indicate the position is not known.
+     */
+    public final static long PLAYBACK_POSITION_UNKNOWN = -1;
+
+    private final int mState;
+    private final long mPosition;
+    private final long mBufferedPosition;
+    private final float mSpeed;
+    private final long mActions;
+    private List<PlaybackState.CustomAction> mCustomActions;
+    private final CharSequence mErrorMessage;
+    private final long mUpdateTime;
+    private final long mActiveItemId;
+    private final Bundle mExtras;
+
+    private PlaybackState(int state, long position, long updateTime, float speed,
+            long bufferedPosition, long transportControls,
+            List<PlaybackState.CustomAction> customActions, long activeItemId,
+            CharSequence error, Bundle extras) {
+        mState = state;
+        mPosition = position;
+        mSpeed = speed;
+        mUpdateTime = updateTime;
+        mBufferedPosition = bufferedPosition;
+        mActions = transportControls;
+        mCustomActions = new ArrayList<>(customActions);
+        mActiveItemId = activeItemId;
+        mErrorMessage = error;
+        mExtras = extras;
+    }
+
+    private PlaybackState(Parcel in) {
+        mState = in.readInt();
+        mPosition = in.readLong();
+        mSpeed = in.readFloat();
+        mUpdateTime = in.readLong();
+        mBufferedPosition = in.readLong();
+        mActions = in.readLong();
+        mCustomActions = in.createTypedArrayList(CustomAction.CREATOR);
+        mActiveItemId = in.readLong();
+        mErrorMessage = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mExtras = in.readBundle();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder bob = new StringBuilder("PlaybackState {");
+        bob.append("state=").append(mState);
+        bob.append(", position=").append(mPosition);
+        bob.append(", buffered position=").append(mBufferedPosition);
+        bob.append(", speed=").append(mSpeed);
+        bob.append(", updated=").append(mUpdateTime);
+        bob.append(", actions=").append(mActions);
+        bob.append(", custom actions=").append(mCustomActions);
+        bob.append(", active item id=").append(mActiveItemId);
+        bob.append(", error=").append(mErrorMessage);
+        bob.append("}");
+        return bob.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mState);
+        dest.writeLong(mPosition);
+        dest.writeFloat(mSpeed);
+        dest.writeLong(mUpdateTime);
+        dest.writeLong(mBufferedPosition);
+        dest.writeLong(mActions);
+        dest.writeTypedList(mCustomActions);
+        dest.writeLong(mActiveItemId);
+        TextUtils.writeToParcel(mErrorMessage, dest, 0);
+        dest.writeBundle(mExtras);
+    }
+
+    /**
+     * Get the current state of playback. One of the following:
+     * <ul>
+     * <li> {@link PlaybackState#STATE_NONE}</li>
+     * <li> {@link PlaybackState#STATE_STOPPED}</li>
+     * <li> {@link PlaybackState#STATE_PLAYING}</li>
+     * <li> {@link PlaybackState#STATE_PAUSED}</li>
+     * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
+     * <li> {@link PlaybackState#STATE_REWINDING}</li>
+     * <li> {@link PlaybackState#STATE_BUFFERING}</li>
+     * <li> {@link PlaybackState#STATE_ERROR}</li>
+     * <li> {@link PlaybackState#STATE_CONNECTING}</li>
+     * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
+     * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
+     * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
+     * </ul>
+     */
+    @State
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * Get the current playback position in ms.
+     */
+    public long getPosition() {
+        return mPosition;
+    }
+
+    /**
+     * Get the current buffered position in ms. This is the farthest playback
+     * point that can be reached from the current position using only buffered
+     * content.
+     */
+    public long getBufferedPosition() {
+        return mBufferedPosition;
+    }
+
+    /**
+     * Get the current playback speed as a multiple of normal playback. This
+     * should be negative when rewinding. A value of 1 means normal playback and
+     * 0 means paused.
+     *
+     * @return The current speed of playback.
+     */
+    public float getPlaybackSpeed() {
+        return mSpeed;
+    }
+
+    /**
+     * Get the current actions available on this session. This should use a
+     * bitmask of the available actions.
+     * <ul>
+     * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
+     * <li> {@link PlaybackState#ACTION_REWIND}</li>
+     * <li> {@link PlaybackState#ACTION_PLAY}</li>
+     * <li> {@link PlaybackState#ACTION_PAUSE}</li>
+     * <li> {@link PlaybackState#ACTION_STOP}</li>
+     * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
+     * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
+     * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
+     * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
+     * <li> {@link PlaybackState#ACTION_PLAY_PAUSE}</li>
+     * <li> {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}</li>
+     * <li> {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}</li>
+     * <li> {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}</li>
+     * <li> {@link PlaybackState#ACTION_PLAY_FROM_URI}</li>
+     * <li> {@link PlaybackState#ACTION_PREPARE}</li>
+     * <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li>
+     * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
+     * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
+     * </ul>
+     */
+    @Actions
+    public long getActions() {
+        return mActions;
+    }
+
+    /**
+     * Get the list of custom actions.
+     */
+    public List<PlaybackState.CustomAction> getCustomActions() {
+        return mCustomActions;
+    }
+
+    /**
+     * Get a user readable error message. This should be set when the state is
+     * {@link PlaybackState#STATE_ERROR}.
+     */
+    public CharSequence getErrorMessage() {
+        return mErrorMessage;
+    }
+
+    /**
+     * Get the elapsed real time at which position was last updated. If the
+     * position has never been set this will return 0;
+     *
+     * @return The last time the position was updated.
+     */
+    public long getLastPositionUpdateTime() {
+        return mUpdateTime;
+    }
+
+    /**
+     * Get the id of the currently active item in the queue. If there is no
+     * queue or a queue is not supported by the session this will be
+     * {@link MediaSession.QueueItem#UNKNOWN_ID}.
+     *
+     * @return The id of the currently active item in the queue or
+     *         {@link MediaSession.QueueItem#UNKNOWN_ID}.
+     */
+    public long getActiveQueueItemId() {
+        return mActiveItemId;
+    }
+
+    /**
+     * Get any custom extras that were set on this playback state.
+     *
+     * @return The extras for this state or null.
+     */
+    public @Nullable Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Get the {@link PlaybackState} state for the given
+     * {@link RemoteControlClient} state.
+     *
+     * @param rccState The state used by {@link RemoteControlClient}.
+     * @return The equivalent state used by {@link PlaybackState}.
+     * @hide
+     */
+    public static int getStateFromRccState(int rccState) {
+        switch (rccState) {
+            case RemoteControlClient.PLAYSTATE_BUFFERING:
+                return STATE_BUFFERING;
+            case RemoteControlClient.PLAYSTATE_ERROR:
+                return STATE_ERROR;
+            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+                return STATE_FAST_FORWARDING;
+            //RemoteControlClient.PLAYSTATE_NONE is hidden
+            case 0:  //RemoteControlClient.PLAYSTATE_NONE:
+                return STATE_NONE;
+            case RemoteControlClient.PLAYSTATE_PAUSED:
+                return STATE_PAUSED;
+            case RemoteControlClient.PLAYSTATE_PLAYING:
+                return STATE_PLAYING;
+            case RemoteControlClient.PLAYSTATE_REWINDING:
+                return STATE_REWINDING;
+            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+                return STATE_SKIPPING_TO_PREVIOUS;
+            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+                return STATE_SKIPPING_TO_NEXT;
+            case RemoteControlClient.PLAYSTATE_STOPPED:
+                return STATE_STOPPED;
+            default:
+                return -1;
+        }
+    }
+
+    /**
+     * Get the {@link RemoteControlClient} state for the given
+     * {@link PlaybackState} state.
+     *
+     * @param state The state used by {@link PlaybackState}.
+     * @return The equivalent state used by {@link RemoteControlClient}.
+     * @hide
+     */
+    public static int getRccStateFromState(int state) {
+        switch (state) {
+            case STATE_BUFFERING:
+                return RemoteControlClient.PLAYSTATE_BUFFERING;
+            case STATE_ERROR:
+                return RemoteControlClient.PLAYSTATE_ERROR;
+            case STATE_FAST_FORWARDING:
+                return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
+            case STATE_NONE:
+                //RemoteControlClient.PLAYSTATE_NONE is hidden
+                return 0;  //RemoteControlClient.PLAYSTATE_NONE;
+            case STATE_PAUSED:
+                return RemoteControlClient.PLAYSTATE_PAUSED;
+            case STATE_PLAYING:
+                return RemoteControlClient.PLAYSTATE_PLAYING;
+            case STATE_REWINDING:
+                return RemoteControlClient.PLAYSTATE_REWINDING;
+            case STATE_SKIPPING_TO_PREVIOUS:
+                return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
+            case STATE_SKIPPING_TO_NEXT:
+                return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
+            case STATE_STOPPED:
+                return RemoteControlClient.PLAYSTATE_STOPPED;
+            default:
+                return -1;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static long getActionsFromRccControlFlags(int rccFlags) {
+        long actions = 0;
+        long flag = 1;
+        while (flag <= rccFlags) {
+            if ((flag & rccFlags) != 0) {
+                actions |= getActionForRccFlag((int) flag);
+            }
+            flag = flag << 1;
+        }
+        return actions;
+    }
+
+    /**
+     * @hide
+     */
+    public static int getRccControlFlagsFromActions(long actions) {
+        int rccFlags = 0;
+        long action = 1;
+        while (action <= actions && action < Integer.MAX_VALUE) {
+            if ((action & actions) != 0) {
+                rccFlags |= getRccFlagForAction(action);
+            }
+            action = action << 1;
+        }
+        return rccFlags;
+    }
+
+    private static long getActionForRccFlag(int flag) {
+        switch (flag) {
+            case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS:
+                return ACTION_SKIP_TO_PREVIOUS;
+            case RemoteControlClient.FLAG_KEY_MEDIA_REWIND:
+                return ACTION_REWIND;
+            case RemoteControlClient.FLAG_KEY_MEDIA_PLAY:
+                return ACTION_PLAY;
+            case RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE:
+                return ACTION_PLAY_PAUSE;
+            case RemoteControlClient.FLAG_KEY_MEDIA_PAUSE:
+                return ACTION_PAUSE;
+            case RemoteControlClient.FLAG_KEY_MEDIA_STOP:
+                return ACTION_STOP;
+            case RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD:
+                return ACTION_FAST_FORWARD;
+            case RemoteControlClient.FLAG_KEY_MEDIA_NEXT:
+                return ACTION_SKIP_TO_NEXT;
+            case RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE:
+                return ACTION_SEEK_TO;
+            case RemoteControlClient.FLAG_KEY_MEDIA_RATING:
+                return ACTION_SET_RATING;
+        }
+        return 0;
+    }
+
+    private static int getRccFlagForAction(long action) {
+        // We only care about the lower set of actions that can map to rcc
+        // flags.
+        int testAction = action < Integer.MAX_VALUE ? (int) action : 0;
+        switch (testAction) {
+            case (int) ACTION_SKIP_TO_PREVIOUS:
+                return RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
+            case (int) ACTION_REWIND:
+                return RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
+            case (int) ACTION_PLAY:
+                return RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
+            case (int) ACTION_PLAY_PAUSE:
+                return RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
+            case (int) ACTION_PAUSE:
+                return RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
+            case (int) ACTION_STOP:
+                return RemoteControlClient.FLAG_KEY_MEDIA_STOP;
+            case (int) ACTION_FAST_FORWARD:
+                return RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
+            case (int) ACTION_SKIP_TO_NEXT:
+                return RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
+            case (int) ACTION_SEEK_TO:
+                return RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;
+            case (int) ACTION_SET_RATING:
+                return RemoteControlClient.FLAG_KEY_MEDIA_RATING;
+        }
+        return 0;
+    }
+
+    public static final Parcelable.Creator<PlaybackState> CREATOR =
+            new Parcelable.Creator<PlaybackState>() {
+        @Override
+        public PlaybackState createFromParcel(Parcel in) {
+            return new PlaybackState(in);
+        }
+
+        @Override
+        public PlaybackState[] newArray(int size) {
+            return new PlaybackState[size];
+        }
+    };
+
+    /**
+     * {@link PlaybackState.CustomAction CustomActions} can be used to extend the capabilities of
+     * the standard transport controls by exposing app specific actions to
+     * {@link MediaController MediaControllers}.
+     */
+    public static final class CustomAction implements Parcelable {
+        private final String mAction;
+        private final CharSequence mName;
+        private final int mIcon;
+        private final Bundle mExtras;
+
+        /**
+         * Use {@link PlaybackState.CustomAction.Builder#build()}.
+         */
+        private CustomAction(String action, CharSequence name, int icon, Bundle extras) {
+            mAction = action;
+            mName = name;
+            mIcon = icon;
+            mExtras = extras;
+        }
+
+        private CustomAction(Parcel in) {
+            mAction = in.readString();
+            mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            mIcon = in.readInt();
+            mExtras = in.readBundle();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(mAction);
+            TextUtils.writeToParcel(mName, dest, flags);
+            dest.writeInt(mIcon);
+            dest.writeBundle(mExtras);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Parcelable.Creator<PlaybackState.CustomAction> CREATOR
+                = new Parcelable.Creator<PlaybackState.CustomAction>() {
+
+            @Override
+            public PlaybackState.CustomAction createFromParcel(Parcel p) {
+                return new PlaybackState.CustomAction(p);
+            }
+
+            @Override
+            public PlaybackState.CustomAction[] newArray(int size) {
+                return new PlaybackState.CustomAction[size];
+            }
+        };
+
+        /**
+         * Returns the action of the {@link CustomAction}.
+         *
+         * @return The action of the {@link CustomAction}.
+         */
+        public String getAction() {
+            return mAction;
+        }
+
+        /**
+         * Returns the display name of this action. e.g. "Favorite"
+         *
+         * @return The display name of this {@link CustomAction}.
+         */
+        public CharSequence getName() {
+            return mName;
+        }
+
+        /**
+         * Returns the resource id of the icon in the {@link MediaSession MediaSession's} package.
+         *
+         * @return The resource id of the icon in the {@link MediaSession MediaSession's} package.
+         */
+        public int getIcon() {
+            return mIcon;
+        }
+
+        /**
+         * Returns extras which provide additional application-specific information about the
+         * action, or null if none. These arguments are meant to be consumed by a
+         * {@link MediaController} if it knows how to handle them.
+         *
+         * @return Optional arguments for the {@link CustomAction}.
+         */
+        public Bundle getExtras() {
+            return mExtras;
+        }
+
+        @Override
+        public String toString() {
+            return "Action:" +
+                    "mName='" + mName +
+                    ", mIcon=" + mIcon +
+                    ", mExtras=" + mExtras;
+        }
+
+        /**
+         * Builder for {@link CustomAction} objects.
+         */
+        public static final class Builder {
+            private final String mAction;
+            private final CharSequence mName;
+            private final int mIcon;
+            private Bundle mExtras;
+
+            /**
+             * Creates a {@link CustomAction} builder with the id, name, and icon set.
+             *
+             * @param action The action of the {@link CustomAction}.
+             * @param name The display name of the {@link CustomAction}. This name will be displayed
+             *             along side the action if the UI supports it.
+             * @param icon The icon resource id of the {@link CustomAction}. This resource id
+             *             must be in the same package as the {@link MediaSession}. It will be
+             *             displayed with the custom action if the UI supports it.
+             */
+            public Builder(String action, CharSequence name, @DrawableRes int icon) {
+                if (TextUtils.isEmpty(action)) {
+                    throw new IllegalArgumentException(
+                            "You must specify an action to build a CustomAction.");
+                }
+                if (TextUtils.isEmpty(name)) {
+                    throw new IllegalArgumentException(
+                            "You must specify a name to build a CustomAction.");
+                }
+                if (icon == 0) {
+                    throw new IllegalArgumentException(
+                            "You must specify an icon resource id to build a CustomAction.");
+                }
+                mAction = action;
+                mName = name;
+                mIcon = icon;
+            }
+
+            /**
+             * Set optional extras for the {@link CustomAction}. These extras are meant to be
+             * consumed by a {@link MediaController} if it knows how to handle them.
+             * Keys should be fully qualified (e.g. "com.example.MY_ARG") to avoid collisions.
+             *
+             * @param extras Optional extras for the {@link CustomAction}.
+             * @return this.
+             */
+            public Builder setExtras(Bundle extras) {
+                mExtras = extras;
+                return this;
+            }
+
+            /**
+             * Build and return the {@link CustomAction} instance with the specified values.
+             *
+             * @return A new {@link CustomAction} instance.
+             */
+            public CustomAction build() {
+                return new CustomAction(mAction, mName, mIcon, mExtras);
+            }
+        }
+    }
+
+    /**
+     * Builder for {@link PlaybackState} objects.
+     */
+    public static final class Builder {
+        private final List<PlaybackState.CustomAction> mCustomActions = new ArrayList<>();
+
+        private int mState;
+        private long mPosition;
+        private long mBufferedPosition;
+        private float mSpeed;
+        private long mActions;
+        private CharSequence mErrorMessage;
+        private long mUpdateTime;
+        private long mActiveItemId = MediaSession.QueueItem.UNKNOWN_ID;
+        private Bundle mExtras;
+
+        /**
+         * Creates an initially empty state builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Creates a builder with the same initial values as those in the from
+         * state.
+         *
+         * @param from The state to use for initializing the builder.
+         */
+        public Builder(PlaybackState from) {
+            if (from == null) {
+                return;
+            }
+            mState = from.mState;
+            mPosition = from.mPosition;
+            mBufferedPosition = from.mBufferedPosition;
+            mSpeed = from.mSpeed;
+            mActions = from.mActions;
+            if (from.mCustomActions != null) {
+                mCustomActions.addAll(from.mCustomActions);
+            }
+            mErrorMessage = from.mErrorMessage;
+            mUpdateTime = from.mUpdateTime;
+            mActiveItemId = from.mActiveItemId;
+            mExtras = from.mExtras;
+        }
+
+        /**
+         * Set the current state of playback.
+         * <p>
+         * The position must be in ms and indicates the current playback
+         * position within the item. If the position is unknown use
+         * {@link #PLAYBACK_POSITION_UNKNOWN}. When not using an unknown
+         * position the time at which the position was updated must be provided.
+         * It is okay to use {@link SystemClock#elapsedRealtime()} if the
+         * current position was just retrieved.
+         * <p>
+         * The speed is a multiple of normal playback and should be 0 when
+         * paused and negative when rewinding. Normal playback speed is 1.0.
+         * <p>
+         * The state must be one of the following:
+         * <ul>
+         * <li> {@link PlaybackState#STATE_NONE}</li>
+         * <li> {@link PlaybackState#STATE_STOPPED}</li>
+         * <li> {@link PlaybackState#STATE_PLAYING}</li>
+         * <li> {@link PlaybackState#STATE_PAUSED}</li>
+         * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
+         * <li> {@link PlaybackState#STATE_REWINDING}</li>
+         * <li> {@link PlaybackState#STATE_BUFFERING}</li>
+         * <li> {@link PlaybackState#STATE_ERROR}</li>
+         * <li> {@link PlaybackState#STATE_CONNECTING}</li>
+         * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
+         * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
+         * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
+         * </ul>
+         *
+         * @param state The current state of playback.
+         * @param position The position in the current item in ms.
+         * @param playbackSpeed The current speed of playback as a multiple of
+         *            normal playback.
+         * @param updateTime The time in the {@link SystemClock#elapsedRealtime}
+         *            timebase that the position was updated at.
+         * @return this
+         */
+        public Builder setState(@State int state, long position, float playbackSpeed,
+                long updateTime) {
+            mState = state;
+            mPosition = position;
+            mUpdateTime = updateTime;
+            mSpeed = playbackSpeed;
+            return this;
+        }
+
+        /**
+         * Set the current state of playback.
+         * <p>
+         * The position must be in ms and indicates the current playback
+         * position within the item. If the position is unknown use
+         * {@link #PLAYBACK_POSITION_UNKNOWN}. The update time will be set to
+         * the current {@link SystemClock#elapsedRealtime()}.
+         * <p>
+         * The speed is a multiple of normal playback and should be 0 when
+         * paused and negative when rewinding. Normal playback speed is 1.0.
+         * <p>
+         * The state must be one of the following:
+         * <ul>
+         * <li> {@link PlaybackState#STATE_NONE}</li>
+         * <li> {@link PlaybackState#STATE_STOPPED}</li>
+         * <li> {@link PlaybackState#STATE_PLAYING}</li>
+         * <li> {@link PlaybackState#STATE_PAUSED}</li>
+         * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
+         * <li> {@link PlaybackState#STATE_REWINDING}</li>
+         * <li> {@link PlaybackState#STATE_BUFFERING}</li>
+         * <li> {@link PlaybackState#STATE_ERROR}</li>
+         * <li> {@link PlaybackState#STATE_CONNECTING}</li>
+         * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li>
+         * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li>
+         * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
+         * </ul>
+         *
+         * @param state The current state of playback.
+         * @param position The position in the current item in ms.
+         * @param playbackSpeed The current speed of playback as a multiple of
+         *            normal playback.
+         * @return this
+         */
+        public Builder setState(@State int state, long position, float playbackSpeed) {
+            return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime());
+        }
+
+        /**
+         * Set the current actions available on this session. This should use a
+         * bitmask of possible actions.
+         * <ul>
+         * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
+         * <li> {@link PlaybackState#ACTION_REWIND}</li>
+         * <li> {@link PlaybackState#ACTION_PLAY}</li>
+         * <li> {@link PlaybackState#ACTION_PAUSE}</li>
+         * <li> {@link PlaybackState#ACTION_STOP}</li>
+         * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
+         * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
+         * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
+         * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
+         * <li> {@link PlaybackState#ACTION_PLAY_PAUSE}</li>
+         * <li> {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}</li>
+         * <li> {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}</li>
+         * <li> {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}</li>
+         * <li> {@link PlaybackState#ACTION_PLAY_FROM_URI}</li>
+         * <li> {@link PlaybackState#ACTION_PREPARE}</li>
+         * <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li>
+         * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
+         * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
+         * </ul>
+         *
+         * @param actions The set of actions allowed.
+         * @return this
+         */
+        public Builder setActions(@Actions long actions) {
+            mActions = actions;
+            return this;
+        }
+
+        /**
+         * Add a custom action to the playback state. Actions can be used to
+         * expose additional functionality to {@link MediaController
+         * MediaControllers} beyond what is offered by the standard transport
+         * controls.
+         * <p>
+         * e.g. start a radio station based on the current item or skip ahead by
+         * 30 seconds.
+         *
+         * @param action An identifier for this action. It can be sent back to
+         *            the {@link MediaSession} through
+         *            {@link MediaController.TransportControls#sendCustomAction(String, Bundle)}.
+         * @param name The display name for the action. If text is shown with
+         *            the action or used for accessibility, this is what should
+         *            be used.
+         * @param icon The resource action of the icon that should be displayed
+         *            for the action. The resource should be in the package of
+         *            the {@link MediaSession}.
+         * @return this
+         */
+        public Builder addCustomAction(String action, String name, int icon) {
+            return addCustomAction(new PlaybackState.CustomAction(action, name, icon, null));
+        }
+
+        /**
+         * Add a custom action to the playback state. Actions can be used to expose additional
+         * functionality to {@link MediaController MediaControllers} beyond what is offered by the
+         * standard transport controls.
+         * <p>
+         * An example of an action would be to start a radio station based on the current item
+         * or to skip ahead by 30 seconds.
+         *
+         * @param customAction The custom action to add to the {@link PlaybackState}.
+         * @return this
+         */
+        public Builder addCustomAction(PlaybackState.CustomAction customAction) {
+            if (customAction == null) {
+                throw new IllegalArgumentException(
+                        "You may not add a null CustomAction to PlaybackState.");
+            }
+            mCustomActions.add(customAction);
+            return this;
+        }
+
+        /**
+         * Set the current buffered position in ms. This is the farthest
+         * playback point that can be reached from the current position using
+         * only buffered content.
+         *
+         * @param bufferedPosition The position in ms that playback is buffered
+         *            to.
+         * @return this
+         */
+        public Builder setBufferedPosition(long bufferedPosition) {
+            mBufferedPosition = bufferedPosition;
+            return this;
+        }
+
+        /**
+         * Set the active item in the play queue by specifying its id. The
+         * default value is {@link MediaSession.QueueItem#UNKNOWN_ID}
+         *
+         * @param id The id of the active item.
+         * @return this
+         */
+        public Builder setActiveQueueItemId(long id) {
+            mActiveItemId = id;
+            return this;
+        }
+
+        /**
+         * Set a user readable error message. This should be set when the state
+         * is {@link PlaybackState#STATE_ERROR}.
+         *
+         * @param error The error message for display to the user.
+         * @return this
+         */
+        public Builder setErrorMessage(CharSequence error) {
+            mErrorMessage = error;
+            return this;
+        }
+
+        /**
+         * Set any custom extras to be included with the playback state.
+         *
+         * @param extras The extras to include.
+         * @return this
+         */
+        public Builder setExtras(Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Build and return the {@link PlaybackState} instance with these
+         * values.
+         *
+         * @return A new state instance.
+         */
+        public PlaybackState build() {
+            return new PlaybackState(mState, mPosition, mUpdateTime, mSpeed, mBufferedPosition,
+                    mActions, mCustomActions, mActiveItemId, mErrorMessage, mExtras);
+        }
+    }
+}
diff --git a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl
new file mode 100644
index 0000000..84f41f6
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserService.aidl
@@ -0,0 +1,27 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+
+package android.service.media;
+
+import android.content.res.Configuration;
+import android.service.media.IMediaBrowserServiceCallbacks;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+
+/**
+ * Media API allows clients to browse through hierarchy of a user’s media collection,
+ * playback a specific media entry and interact with the now playing queue.
+ * @hide
+ */
+oneway interface IMediaBrowserService {
+    void connect(String pkg, in Bundle rootHints, IMediaBrowserServiceCallbacks callbacks);
+    void disconnect(IMediaBrowserServiceCallbacks callbacks);
+
+    void addSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
+    void removeSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
+
+    void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks);
+    void addSubscription(String uri, in IBinder token, in Bundle options,
+            IMediaBrowserServiceCallbacks callbacks);
+    void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks);
+}
diff --git a/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
new file mode 100644
index 0000000..bcc2826
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -0,0 +1,28 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+
+package android.service.media;
+
+//import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+
+/**
+ * Media API allows clients to browse through hierarchy of a user’s media collection,
+ * playback a specific media entry and interact with the now playing queue.
+ * @hide
+ */
+oneway interface IMediaBrowserServiceCallbacks {
+    /**
+     * Invoked when the connected has been established.
+     * @param root The root media id for browsing.
+     * @param session The {@link MediaSession.Token media session token} that can be used to control
+     *         the playback of the media app.
+     * @param extra Extras returned by the media service.
+     */
+    void onConnect(String root, in MediaSession.Token session, in Bundle extras);
+    void onConnectFailed();
+    //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice.
+    //void onLoadChildren(String mediaId, in ParceledListSlice list);
+    //void onLoadChildrenWithOptions(String mediaId, in ParceledListSlice list, in Bundle options);
+}
diff --git a/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java
new file mode 100644
index 0000000..2f2fbaf
--- /dev/null
+++ b/packages/MediaComponents/apex/java/android/service/media/MediaBrowserService.java
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.UnsupportedAppUsage;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+//import android.content.pm.ParceledListSlice;
+import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowserUtils;
+import android.media.session.MediaSession;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.service.media.IMediaBrowserService;
+import android.service.media.IMediaBrowserServiceCallbacks;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Base class for media browser services.
+ * <p>
+ * Media browser services enable applications to browse media content provided by an application
+ * and ask the application to start playing it. They may also be used to control content that
+ * is already playing by way of a {@link MediaSession}.
+ * </p>
+ *
+ * To extend this class, you must declare the service in your manifest file with
+ * an intent filter with the {@link #SERVICE_INTERFACE} action.
+ *
+ * For example:
+ * </p><pre>
+ * &lt;service android:name=".MyMediaBrowserService"
+ *          android:label="&#64;string/service_name" >
+ *     &lt;intent-filter>
+ *         &lt;action android:name="android.media.browse.MediaBrowserService" />
+ *     &lt;/intent-filter>
+ * &lt;/service>
+ * </pre>
+ *
+ */
+public abstract class MediaBrowserService extends Service {
+    private static final String TAG = "MediaBrowserService";
+    private static final boolean DBG = false;
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
+
+    /**
+     * A key for passing the MediaItem to the ResultReceiver in getItem.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final String KEY_MEDIA_ITEM = "media_item";
+
+    private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 1 << 0;
+    private static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 1 << 1;
+
+    private static final int RESULT_ERROR = -1;
+    private static final int RESULT_OK = 0;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED,
+            RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED })
+    private @interface ResultFlags { }
+
+    private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
+    private ConnectionRecord mCurConnection;
+    private final Handler mHandler = new Handler();
+    private ServiceBinder mBinder;
+    MediaSession.Token mSession;
+
+    /**
+     * All the info about a connection.
+     */
+    private class ConnectionRecord implements IBinder.DeathRecipient {
+        String pkg;
+        int uid;
+        int pid;
+        Bundle rootHints;
+        IMediaBrowserServiceCallbacks callbacks;
+        BrowserRoot root;
+        HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
+
+        @Override
+        public void binderDied() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mConnections.remove(callbacks.asBinder());
+                }
+            });
+        }
+    }
+
+    /**
+     * Completion handler for asynchronous callback methods in {@link MediaBrowserService}.
+     * <p>
+     * Each of the methods that takes one of these to send the result must call
+     * {@link #sendResult} to respond to the caller with the given results. If those
+     * functions return without calling {@link #sendResult}, they must instead call
+     * {@link #detach} before returning, and then may call {@link #sendResult} when
+     * they are done. If more than one of those methods is called, an exception will
+     * be thrown.
+     *
+     * @see #onLoadChildren
+     * @see #onLoadItem
+     */
+    public class Result<T> {
+        private Object mDebug;
+        private boolean mDetachCalled;
+        private boolean mSendResultCalled;
+        @UnsupportedAppUsage
+        private int mFlags;
+
+        Result(Object debug) {
+            mDebug = debug;
+        }
+
+        /**
+         * Send the result back to the caller.
+         */
+        public void sendResult(T result) {
+            if (mSendResultCalled) {
+                throw new IllegalStateException("sendResult() called twice for: " + mDebug);
+            }
+            mSendResultCalled = true;
+            onResultSent(result, mFlags);
+        }
+
+        /**
+         * Detach this message from the current thread and allow the {@link #sendResult}
+         * call to happen later.
+         */
+        public void detach() {
+            if (mDetachCalled) {
+                throw new IllegalStateException("detach() called when detach() had already"
+                        + " been called for: " + mDebug);
+            }
+            if (mSendResultCalled) {
+                throw new IllegalStateException("detach() called when sendResult() had already"
+                        + " been called for: " + mDebug);
+            }
+            mDetachCalled = true;
+        }
+
+        boolean isDone() {
+            return mDetachCalled || mSendResultCalled;
+        }
+
+        void setFlags(@ResultFlags int flags) {
+            mFlags = flags;
+        }
+
+        /**
+         * Called when the result is sent, after assertions about not being called twice
+         * have happened.
+         */
+        void onResultSent(T result, @ResultFlags int flags) {
+        }
+    }
+
+    private class ServiceBinder extends IMediaBrowserService.Stub {
+        @Override
+        public void connect(final String pkg, final Bundle rootHints,
+                final IMediaBrowserServiceCallbacks callbacks) {
+
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            if (!isValidPackage(pkg, uid)) {
+                throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
+                        + " package=" + pkg);
+            }
+
+            mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        final IBinder b = callbacks.asBinder();
+
+                        // Clear out the old subscriptions. We are getting new ones.
+                        mConnections.remove(b);
+
+                        final ConnectionRecord connection = new ConnectionRecord();
+                        connection.pkg = pkg;
+                        connection.pid = pid;
+                        connection.uid = uid;
+                        connection.rootHints = rootHints;
+                        connection.callbacks = callbacks;
+
+                        mCurConnection = connection;
+                        connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
+                        mCurConnection = null;
+
+                        // If they didn't return something, don't allow this client.
+                        if (connection.root == null) {
+                            Log.i(TAG, "No root for client " + pkg + " from service "
+                                    + getClass().getName());
+                            try {
+                                callbacks.onConnectFailed();
+                            } catch (RemoteException ex) {
+                                Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
+                                        + "pkg=" + pkg);
+                            }
+                        } else {
+                            try {
+                                mConnections.put(b, connection);
+                                b.linkToDeath(connection, 0);
+                                if (mSession != null) {
+                                    callbacks.onConnect(connection.root.getRootId(),
+                                            mSession, connection.root.getExtras());
+                                }
+                            } catch (RemoteException ex) {
+                                Log.w(TAG, "Calling onConnect() failed. Dropping client. "
+                                        + "pkg=" + pkg);
+                                mConnections.remove(b);
+                            }
+                        }
+                    }
+                });
+        }
+
+        @Override
+        public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
+            mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        final IBinder b = callbacks.asBinder();
+
+                        // Clear out the old subscriptions. We are getting new ones.
+                        final ConnectionRecord old = mConnections.remove(b);
+                        if (old != null) {
+                            // TODO
+                            old.callbacks.asBinder().unlinkToDeath(old, 0);
+                        }
+                    }
+                });
+        }
+
+        @Override
+        public void addSubscriptionDeprecated(String id, IMediaBrowserServiceCallbacks callbacks) {
+            // do-nothing
+        }
+
+        @Override
+        public void addSubscription(final String id, final IBinder token, final Bundle options,
+                final IMediaBrowserServiceCallbacks callbacks) {
+            mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        final IBinder b = callbacks.asBinder();
+
+                        // Get the record for the connection
+                        final ConnectionRecord connection = mConnections.get(b);
+                        if (connection == null) {
+                            Log.w(TAG, "addSubscription for callback that isn't registered id="
+                                + id);
+                            return;
+                        }
+
+                        MediaBrowserService.this.addSubscription(id, connection, token, options);
+                    }
+                });
+        }
+
+        @Override
+        public void removeSubscriptionDeprecated(String id, IMediaBrowserServiceCallbacks callbacks) {
+            // do-nothing
+        }
+
+        @Override
+        public void removeSubscription(final String id, final IBinder token,
+                final IMediaBrowserServiceCallbacks callbacks) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    final IBinder b = callbacks.asBinder();
+
+                    ConnectionRecord connection = mConnections.get(b);
+                    if (connection == null) {
+                        Log.w(TAG, "removeSubscription for callback that isn't registered id="
+                                + id);
+                        return;
+                    }
+                    if (!MediaBrowserService.this.removeSubscription(id, connection, token)) {
+                        Log.w(TAG, "removeSubscription called for " + id
+                                + " which is not subscribed");
+                    }
+                }
+            });
+        }
+
+        @Override
+        public void getMediaItem(final String mediaId, final ResultReceiver receiver,
+                final IMediaBrowserServiceCallbacks callbacks) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    final IBinder b = callbacks.asBinder();
+                    ConnectionRecord connection = mConnections.get(b);
+                    if (connection == null) {
+                        Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
+                        return;
+                    }
+                    performLoadItem(mediaId, connection, receiver);
+                }
+            });
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mBinder = new ServiceBinder();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mBinder;
+        }
+        return null;
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+    }
+
+    /**
+     * Called to get the root information for browsing by a particular client.
+     * <p>
+     * The implementation should verify that the client package has permission
+     * to access browse media information before returning the root id; it
+     * should return null if the client is not allowed to access this
+     * information.
+     * </p>
+     *
+     * @param clientPackageName The package name of the application which is
+     *            requesting access to browse media.
+     * @param clientUid The uid of the application which is requesting access to
+     *            browse media.
+     * @param rootHints An optional bundle of service-specific arguments to send
+     *            to the media browser service when connecting and retrieving the
+     *            root id for browsing, or null if none. The contents of this
+     *            bundle may affect the information returned when browsing.
+     * @return The {@link BrowserRoot} for accessing this app's content or null.
+     * @see BrowserRoot#EXTRA_RECENT
+     * @see BrowserRoot#EXTRA_OFFLINE
+     * @see BrowserRoot#EXTRA_SUGGESTED
+     */
+    public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
+            int clientUid, @Nullable Bundle rootHints);
+
+    /**
+     * Called to get information about the children of a media item.
+     * <p>
+     * Implementations must call {@link Result#sendResult result.sendResult}
+     * with the list of children. If loading the children will be an expensive
+     * operation that should be performed on another thread,
+     * {@link Result#detach result.detach} may be called before returning from
+     * this function, and then {@link Result#sendResult result.sendResult}
+     * called when the loading is complete.
+     * </p><p>
+     * In case the media item does not have any children, call {@link Result#sendResult}
+     * with an empty list. When the given {@code parentId} is invalid, implementations must
+     * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
+     * {@link MediaBrowser.SubscriptionCallback#onError}.
+     * </p>
+     *
+     * @param parentId The id of the parent media item whose children are to be
+     *            queried.
+     * @param result The Result to send the list of children to.
+     */
+    public abstract void onLoadChildren(@NonNull String parentId,
+            @NonNull Result<List<MediaBrowser.MediaItem>> result);
+
+    /**
+     * Called to get information about the children of a media item.
+     * <p>
+     * Implementations must call {@link Result#sendResult result.sendResult}
+     * with the list of children. If loading the children will be an expensive
+     * operation that should be performed on another thread,
+     * {@link Result#detach result.detach} may be called before returning from
+     * this function, and then {@link Result#sendResult result.sendResult}
+     * called when the loading is complete.
+     * </p><p>
+     * In case the media item does not have any children, call {@link Result#sendResult}
+     * with an empty list. When the given {@code parentId} is invalid, implementations must
+     * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
+     * {@link MediaBrowser.SubscriptionCallback#onError}.
+     * </p>
+     *
+     * @param parentId The id of the parent media item whose children are to be
+     *            queried.
+     * @param result The Result to send the list of children to.
+     * @param options The bundle of service-specific arguments sent from the media
+     *            browser. The information returned through the result should be
+     *            affected by the contents of this bundle.
+     */
+    public void onLoadChildren(@NonNull String parentId,
+            @NonNull Result<List<MediaBrowser.MediaItem>> result, @NonNull Bundle options) {
+        // To support backward compatibility, when the implementation of MediaBrowserService doesn't
+        // override onLoadChildren() with options, onLoadChildren() without options will be used
+        // instead, and the options will be applied in the implementation of result.onResultSent().
+        result.setFlags(RESULT_FLAG_OPTION_NOT_HANDLED);
+        onLoadChildren(parentId, result);
+    }
+
+    /**
+     * Called to get information about a specific media item.
+     * <p>
+     * Implementations must call {@link Result#sendResult result.sendResult}. If
+     * loading the item will be an expensive operation {@link Result#detach
+     * result.detach} may be called before returning from this function, and
+     * then {@link Result#sendResult result.sendResult} called when the item has
+     * been loaded.
+     * </p><p>
+     * When the given {@code itemId} is invalid, implementations must call
+     * {@link Result#sendResult result.sendResult} with {@code null}.
+     * </p><p>
+     * The default implementation will invoke {@link MediaBrowser.ItemCallback#onError}.
+     * </p>
+     *
+     * @param itemId The id for the specific
+     *            {@link android.media.browse.MediaBrowser.MediaItem}.
+     * @param result The Result to send the item to.
+     */
+    public void onLoadItem(String itemId, Result<MediaBrowser.MediaItem> result) {
+        result.setFlags(RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED);
+        result.sendResult(null);
+    }
+
+    /**
+     * Call to set the media session.
+     * <p>
+     * This should be called as soon as possible during the service's startup.
+     * It may only be called once.
+     *
+     * @param token The token for the service's {@link MediaSession}.
+     */
+    public void setSessionToken(final MediaSession.Token token) {
+        if (token == null) {
+            throw new IllegalArgumentException("Session token may not be null.");
+        }
+        if (mSession != null) {
+            throw new IllegalStateException("The session token has already been set.");
+        }
+        mSession = token;
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                Iterator<ConnectionRecord> iter = mConnections.values().iterator();
+                while (iter.hasNext()){
+                    ConnectionRecord connection = iter.next();
+                    try {
+                        connection.callbacks.onConnect(connection.root.getRootId(), token,
+                                connection.root.getExtras());
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
+                        iter.remove();
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Gets the session token, or null if it has not yet been created
+     * or if it has been destroyed.
+     */
+    public @Nullable MediaSession.Token getSessionToken() {
+        return mSession;
+    }
+
+    /**
+     * Gets the root hints sent from the currently connected {@link MediaBrowser}.
+     * The root hints are service-specific arguments included in an optional bundle sent to the
+     * media browser service when connecting and retrieving the root id for browsing, or null if
+     * none. The contents of this bundle may affect the information returned when browsing.
+     *
+     * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or
+     *             {@link #onLoadChildren} or {@link #onLoadItem}.
+     * @see MediaBrowserService.BrowserRoot#EXTRA_RECENT
+     * @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
+     * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
+     */
+    public final Bundle getBrowserRootHints() {
+        if (mCurConnection == null) {
+            throw new IllegalStateException("This should be called inside of onGetRoot or"
+                    + " onLoadChildren or onLoadItem methods");
+        }
+        return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
+    }
+
+    /**
+     * Gets the browser information who sent the current request.
+     *
+     * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or
+     *             {@link #onLoadChildren} or {@link #onLoadItem}.
+     * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
+     */
+    public final RemoteUserInfo getCurrentBrowserInfo() {
+        if (mCurConnection == null) {
+            throw new IllegalStateException("This should be called inside of onGetRoot or"
+                    + " onLoadChildren or onLoadItem methods");
+        }
+        //TODO(b/119752205): Resolve hidden API usage. 4-param constructor of RemoteUserInfo
+        /*
+        return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid,
+                mCurConnection.callbacks.asBinder());
+        */
+        return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid);
+    }
+
+    /**
+     * Notifies all connected media browsers that the children of
+     * the specified parent id have changed in some way.
+     * This will cause browsers to fetch subscribed content again.
+     *
+     * @param parentId The id of the parent media item whose
+     * children changed.
+     */
+    public void notifyChildrenChanged(@NonNull String parentId) {
+        notifyChildrenChangedInternal(parentId, null);
+    }
+
+    /**
+     * Notifies all connected media browsers that the children of
+     * the specified parent id have changed in some way.
+     * This will cause browsers to fetch subscribed content again.
+     *
+     * @param parentId The id of the parent media item whose
+     *            children changed.
+     * @param options The bundle of service-specific arguments to send
+     *            to the media browser. The contents of this bundle may
+     *            contain the information about the change.
+     */
+    public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) {
+        if (options == null) {
+            throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged");
+        }
+        notifyChildrenChangedInternal(parentId, options);
+    }
+
+    private void notifyChildrenChangedInternal(final String parentId, final Bundle options) {
+        if (parentId == null) {
+            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
+        }
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                for (IBinder binder : mConnections.keySet()) {
+                    ConnectionRecord connection = mConnections.get(binder);
+                    List<Pair<IBinder, Bundle>> callbackList =
+                            connection.subscriptions.get(parentId);
+                    if (callbackList != null) {
+                        for (Pair<IBinder, Bundle> callback : callbackList) {
+                            if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) {
+                                performLoadChildren(parentId, connection, callback.second);
+                            }
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Return whether the given package is one of the ones that is owned by the uid.
+     */
+    private boolean isValidPackage(String pkg, int uid) {
+        if (pkg == null) {
+            return false;
+        }
+        final PackageManager pm = getPackageManager();
+        final String[] packages = pm.getPackagesForUid(uid);
+        final int N = packages.length;
+        for (int i=0; i<N; i++) {
+            if (packages[i].equals(pkg)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Save the subscription and if it is a new subscription send the results.
+     */
+    private void addSubscription(String id, ConnectionRecord connection, IBinder token,
+            Bundle options) {
+        // Save the subscription
+        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
+        if (callbackList == null) {
+            callbackList = new ArrayList<>();
+        }
+        for (Pair<IBinder, Bundle> callback : callbackList) {
+            if (token == callback.first
+                    && MediaBrowserUtils.areSameOptions(options, callback.second)) {
+                return;
+            }
+        }
+        callbackList.add(new Pair<>(token, options));
+        connection.subscriptions.put(id, callbackList);
+        // send the results
+        performLoadChildren(id, connection, options);
+    }
+
+    /**
+     * Remove the subscription.
+     */
+    private boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) {
+        if (token == null) {
+            return connection.subscriptions.remove(id) != null;
+        }
+        boolean removed = false;
+        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
+        if (callbackList != null) {
+            Iterator<Pair<IBinder, Bundle>> iter = callbackList.iterator();
+            while (iter.hasNext()){
+                if (token == iter.next().first) {
+                    removed = true;
+                    iter.remove();
+                }
+            }
+            if (callbackList.size() == 0) {
+                connection.subscriptions.remove(id);
+            }
+        }
+        return removed;
+    }
+
+    /**
+     * Call onLoadChildren and then send the results back to the connection.
+     * <p>
+     * Callers must make sure that this connection is still connected.
+     */
+    private void performLoadChildren(final String parentId, final ConnectionRecord connection,
+            final Bundle options) {
+        final Result<List<MediaBrowser.MediaItem>> result
+                = new Result<List<MediaBrowser.MediaItem>>(parentId) {
+            @Override
+            void onResultSent(List<MediaBrowser.MediaItem> list, @ResultFlags int flag) {
+                if (mConnections.get(connection.callbacks.asBinder()) != connection) {
+                    if (DBG) {
+                        Log.d(TAG, "Not sending onLoadChildren result for connection that has"
+                                + " been disconnected. pkg=" + connection.pkg + " id=" + parentId);
+                    }
+                    return;
+                }
+
+                List<MediaBrowser.MediaItem> filteredList =
+                        (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
+                        ? applyOptions(list, options) : list;
+                //TODO:(b/119750807) Resolve hidden API usage ParceledListSlice.
+                /*
+                final ParceledListSlice<MediaBrowser.MediaItem> pls =
+                        filteredList == null ? null : new ParceledListSlice<>(filteredList);
+                try {
+                    connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options);
+                } catch (RemoteException ex) {
+                    // The other side is in the process of crashing.
+                    Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
+                            + " package=" + connection.pkg);
+                }
+                */
+            }
+        };
+
+        mCurConnection = connection;
+        if (options == null) {
+            onLoadChildren(parentId, result);
+        } else {
+            onLoadChildren(parentId, result, options);
+        }
+        mCurConnection = null;
+
+        if (!result.isDone()) {
+            throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
+                    + " before returning for package=" + connection.pkg + " id=" + parentId);
+        }
+    }
+
+    private List<MediaBrowser.MediaItem> applyOptions(List<MediaBrowser.MediaItem> list,
+            final Bundle options) {
+        if (list == null) {
+            return null;
+        }
+        int page = options.getInt(MediaBrowser.EXTRA_PAGE, -1);
+        int pageSize = options.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
+        if (page == -1 && pageSize == -1) {
+            return list;
+        }
+        int fromIndex = pageSize * page;
+        int toIndex = fromIndex + pageSize;
+        if (page < 0 || pageSize < 1 || fromIndex >= list.size()) {
+            return Collections.EMPTY_LIST;
+        }
+        if (toIndex > list.size()) {
+            toIndex = list.size();
+        }
+        return list.subList(fromIndex, toIndex);
+    }
+
+    private void performLoadItem(String itemId, final ConnectionRecord connection,
+            final ResultReceiver receiver) {
+        final Result<MediaBrowser.MediaItem> result =
+                new Result<MediaBrowser.MediaItem>(itemId) {
+            @Override
+            void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) {
+                if (mConnections.get(connection.callbacks.asBinder()) != connection) {
+                    if (DBG) {
+                        Log.d(TAG, "Not sending onLoadItem result for connection that has"
+                                + " been disconnected. pkg=" + connection.pkg + " id=" + itemId);
+                    }
+                    return;
+                }
+                if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) {
+                    receiver.send(RESULT_ERROR, null);
+                    return;
+                }
+                Bundle bundle = new Bundle();
+                bundle.putParcelable(KEY_MEDIA_ITEM, item);
+                receiver.send(RESULT_OK, bundle);
+            }
+        };
+
+        mCurConnection = connection;
+        onLoadItem(itemId, result);
+        mCurConnection = null;
+
+        if (!result.isDone()) {
+            throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
+                    + " before returning for id=" + itemId);
+        }
+    }
+
+    /**
+     * Contains information that the browser service needs to send to the client
+     * when first connected.
+     */
+    public static final class BrowserRoot {
+        /**
+         * The lookup key for a boolean that indicates whether the browser service should return a
+         * browser root for recently played media items.
+         *
+         * <p>When creating a media browser for a given media browser service, this key can be
+         * supplied as a root hint for retrieving media items that are recently played.
+         * If the media browser service can provide such media items, the implementation must return
+         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
+         *
+         * <p>The root hint may contain multiple keys.
+         *
+         * @see #EXTRA_OFFLINE
+         * @see #EXTRA_SUGGESTED
+         */
+        public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
+
+        /**
+         * The lookup key for a boolean that indicates whether the browser service should return a
+         * browser root for offline media items.
+         *
+         * <p>When creating a media browser for a given media browser service, this key can be
+         * supplied as a root hint for retrieving media items that are can be played without an
+         * internet connection.
+         * If the media browser service can provide such media items, the implementation must return
+         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
+         *
+         * <p>The root hint may contain multiple keys.
+         *
+         * @see #EXTRA_RECENT
+         * @see #EXTRA_SUGGESTED
+         */
+        public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+
+        /**
+         * The lookup key for a boolean that indicates whether the browser service should return a
+         * browser root for suggested media items.
+         *
+         * <p>When creating a media browser for a given media browser service, this key can be
+         * supplied as a root hint for retrieving the media items suggested by the media browser
+         * service. The list of media items passed in {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)}
+         * is considered ordered by relevance, first being the top suggestion.
+         * If the media browser service can provide such media items, the implementation must return
+         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
+         *
+         * <p>The root hint may contain multiple keys.
+         *
+         * @see #EXTRA_RECENT
+         * @see #EXTRA_OFFLINE
+         */
+        public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
+
+        final private String mRootId;
+        final private Bundle mExtras;
+
+        /**
+         * Constructs a browser root.
+         * @param rootId The root id for browsing.
+         * @param extras Any extras about the browser service.
+         */
+        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
+            if (rootId == null) {
+                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
+                        "Use null for BrowserRoot instead.");
+            }
+            mRootId = rootId;
+            mExtras = extras;
+        }
+
+        /**
+         * Gets the root id for browsing.
+         */
+        public String getRootId() {
+            return mRootId;
+        }
+
+        /**
+         * Gets any extras about the browser service.
+         */
+        public Bundle getExtras() {
+            return mExtras;
+        }
+    }
+}
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_audiotrack_dark.png b/packages/MediaComponents/res/drawable-hdpi/ic_audiotrack_dark.png
deleted file mode 100644
index 17fd51f..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_audiotrack_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_audiotrack_light.png b/packages/MediaComponents/res/drawable-hdpi/ic_audiotrack_light.png
deleted file mode 100644
index d7c8252..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_audiotrack_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_dialog_close_dark.png b/packages/MediaComponents/res/drawable-hdpi/ic_dialog_close_dark.png
deleted file mode 100644
index 928ddea..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_dialog_close_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_dialog_close_light.png b/packages/MediaComponents/res/drawable-hdpi/ic_dialog_close_light.png
deleted file mode 100644
index 1a9cd75..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_dialog_close_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_media_pause_dark.png b/packages/MediaComponents/res/drawable-hdpi/ic_media_pause_dark.png
deleted file mode 100644
index 7192ad4..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_media_pause_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_media_pause_light.png b/packages/MediaComponents/res/drawable-hdpi/ic_media_pause_light.png
deleted file mode 100644
index bb707ea..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_media_pause_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_media_play_dark.png b/packages/MediaComponents/res/drawable-hdpi/ic_media_play_dark.png
deleted file mode 100644
index 0c32d00..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_media_play_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_media_play_light.png b/packages/MediaComponents/res/drawable-hdpi/ic_media_play_light.png
deleted file mode 100644
index 5345ee3..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_media_play_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_media_stop_dark.png b/packages/MediaComponents/res/drawable-hdpi/ic_media_stop_dark.png
deleted file mode 100644
index 801d341..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_media_stop_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_media_stop_light.png b/packages/MediaComponents/res/drawable-hdpi/ic_media_stop_light.png
deleted file mode 100644
index 9d6b65d..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_media_stop_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_media_subtitle_disabled.png b/packages/MediaComponents/res/drawable-hdpi/ic_media_subtitle_disabled.png
deleted file mode 100644
index 0354f61..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_media_subtitle_disabled.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_media_subtitle_enabled.png b/packages/MediaComponents/res/drawable-hdpi/ic_media_subtitle_enabled.png
deleted file mode 100644
index 5f8febe..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_media_subtitle_enabled.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disabled_dark.png b/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disabled_dark.png
deleted file mode 100644
index 8ad305d..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disabled_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disabled_light.png b/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disabled_light.png
deleted file mode 100644
index 887fde4..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disabled_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disconnected_dark.png b/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disconnected_dark.png
deleted file mode 100644
index 5739df7..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disconnected_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disconnected_light.png b/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disconnected_light.png
deleted file mode 100644
index 58c344a..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_disconnected_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_grey.png b/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_grey.png
deleted file mode 100644
index 1a03420..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_mr_button_grey.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_dark.png b/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_dark.png
deleted file mode 100755
index 723e455..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_group_dark.png b/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_group_dark.png
deleted file mode 100755
index 40c25a3..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_group_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_group_light.png b/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_group_light.png
deleted file mode 100755
index afdb9c1..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_group_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_light.png b/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_light.png
deleted file mode 100755
index 846c109..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_speaker_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_tv_dark.png b/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_tv_dark.png
deleted file mode 100755
index 33bf484..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_tv_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_tv_light.png b/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_tv_light.png
deleted file mode 100755
index c911b5c..0000000
--- a/packages/MediaComponents/res/drawable-hdpi/ic_vol_type_tv_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_audiotrack_dark.png b/packages/MediaComponents/res/drawable-mdpi/ic_audiotrack_dark.png
deleted file mode 100644
index e94ed50..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_audiotrack_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_audiotrack_light.png b/packages/MediaComponents/res/drawable-mdpi/ic_audiotrack_light.png
deleted file mode 100644
index 2cf7e0c..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_audiotrack_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_dialog_close_dark.png b/packages/MediaComponents/res/drawable-mdpi/ic_dialog_close_dark.png
deleted file mode 100644
index 66558a8..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_dialog_close_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_dialog_close_light.png b/packages/MediaComponents/res/drawable-mdpi/ic_dialog_close_light.png
deleted file mode 100644
index 40a1a84..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_dialog_close_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_media_pause_dark.png b/packages/MediaComponents/res/drawable-mdpi/ic_media_pause_dark.png
deleted file mode 100644
index f49aed7..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_media_pause_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_media_pause_light.png b/packages/MediaComponents/res/drawable-mdpi/ic_media_pause_light.png
deleted file mode 100644
index 74068ea..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_media_pause_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_media_play_dark.png b/packages/MediaComponents/res/drawable-mdpi/ic_media_play_dark.png
deleted file mode 100644
index 9cc777c..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_media_play_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_media_play_light.png b/packages/MediaComponents/res/drawable-mdpi/ic_media_play_light.png
deleted file mode 100644
index f208795..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_media_play_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_media_stop_dark.png b/packages/MediaComponents/res/drawable-mdpi/ic_media_stop_dark.png
deleted file mode 100644
index 3ad2c9c..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_media_stop_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_media_stop_light.png b/packages/MediaComponents/res/drawable-mdpi/ic_media_stop_light.png
deleted file mode 100644
index b002ab7..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_media_stop_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_media_subtitle_disabled.png b/packages/MediaComponents/res/drawable-mdpi/ic_media_subtitle_disabled.png
deleted file mode 100644
index 0354f61..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_media_subtitle_disabled.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_media_subtitle_enabled.png b/packages/MediaComponents/res/drawable-mdpi/ic_media_subtitle_enabled.png
deleted file mode 100644
index 5f8febe..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_media_subtitle_enabled.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disabled_dark.png b/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disabled_dark.png
deleted file mode 100644
index 4446ea4..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disabled_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disabled_light.png b/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disabled_light.png
deleted file mode 100644
index 4d790c6..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disabled_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disconnected_dark.png b/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disconnected_dark.png
deleted file mode 100644
index c401dc0..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disconnected_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disconnected_light.png b/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disconnected_light.png
deleted file mode 100644
index e24d586..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_disconnected_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_grey.png b/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_grey.png
deleted file mode 100644
index ccbb772..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_mr_button_grey.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_dark.png b/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_dark.png
deleted file mode 100755
index 7cc9845..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_group_dark.png b/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_group_dark.png
deleted file mode 100755
index 22617e1..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_group_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_group_light.png b/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_group_light.png
deleted file mode 100755
index cefef3c..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_group_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_light.png b/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_light.png
deleted file mode 100755
index 9a0047c..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_speaker_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_tv_dark.png b/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_tv_dark.png
deleted file mode 100755
index ca5d6a2..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_tv_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_tv_light.png b/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_tv_light.png
deleted file mode 100755
index 8134310..0000000
--- a/packages/MediaComponents/res/drawable-mdpi/ic_vol_type_tv_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_audiotrack_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_audiotrack_dark.png
deleted file mode 100644
index b5c899f..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_audiotrack_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_audiotrack_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_audiotrack_light.png
deleted file mode 100644
index 4778e00..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_audiotrack_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_dialog_close_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_dialog_close_dark.png
deleted file mode 100644
index f992fc5..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_dialog_close_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_dialog_close_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_dialog_close_light.png
deleted file mode 100644
index d3884e6..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_dialog_close_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_media_pause_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_media_pause_dark.png
deleted file mode 100644
index 660ac65..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_media_pause_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_media_pause_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_media_pause_light.png
deleted file mode 100644
index 792104f..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_media_pause_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_media_play_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_media_play_dark.png
deleted file mode 100644
index be5c062..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_media_play_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_media_play_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_media_play_light.png
deleted file mode 100644
index d12d495..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_media_play_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_media_stop_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_media_stop_dark.png
deleted file mode 100644
index 5239336..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_media_stop_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_media_stop_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_media_stop_light.png
deleted file mode 100644
index 5bc5a6c..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_media_stop_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_00_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_00_dark.png
deleted file mode 100644
index f6dd214..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_00_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_00_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_00_light.png
deleted file mode 100644
index 6b7bdcd..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_00_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_01_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_01_dark.png
deleted file mode 100644
index c7fe576..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_01_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_01_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_01_light.png
deleted file mode 100644
index 0a5d6aa..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_01_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_02_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_02_dark.png
deleted file mode 100644
index 0aadfa3..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_02_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_02_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_02_light.png
deleted file mode 100644
index 125fe0b..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_02_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_03_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_03_dark.png
deleted file mode 100644
index 05c48a7..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_03_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_03_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_03_light.png
deleted file mode 100644
index 741e911..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_03_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_04_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_04_dark.png
deleted file mode 100644
index ae4218a..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_04_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_04_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_04_light.png
deleted file mode 100644
index 8b30fab..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_04_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_05_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_05_dark.png
deleted file mode 100644
index d7aa903..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_05_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_05_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_05_light.png
deleted file mode 100644
index f7e2f29..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_05_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_06_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_06_dark.png
deleted file mode 100644
index e7871e2..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_06_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_06_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_06_light.png
deleted file mode 100644
index 8c57f63..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_06_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_07_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_07_dark.png
deleted file mode 100644
index 0041b01..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_07_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_07_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_07_light.png
deleted file mode 100644
index 6dbb694..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_07_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_08_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_08_dark.png
deleted file mode 100644
index 08e1013..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_08_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_08_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_08_light.png
deleted file mode 100644
index 5c352c3..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_08_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_09_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_09_dark.png
deleted file mode 100644
index 70532e9..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_09_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_09_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_09_light.png
deleted file mode 100644
index 9c6ba30..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_09_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_10_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_10_dark.png
deleted file mode 100644
index 9ba3b5f..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_10_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_10_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_10_light.png
deleted file mode 100644
index bd4bb22..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_10_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_11_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_11_dark.png
deleted file mode 100644
index 2156127..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_11_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_11_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_11_light.png
deleted file mode 100644
index b417a9f..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_11_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_12_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_12_dark.png
deleted file mode 100644
index 9bf633e..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_12_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_12_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_12_light.png
deleted file mode 100644
index ba51811..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_12_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_13_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_13_dark.png
deleted file mode 100644
index 756a53c..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_13_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_13_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_13_light.png
deleted file mode 100644
index 4705dca..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_13_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_14_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_14_dark.png
deleted file mode 100644
index 50e4ea3..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_14_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_14_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_14_light.png
deleted file mode 100644
index bc6724f..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_14_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_15_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_15_dark.png
deleted file mode 100644
index 9e3b410..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_15_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_15_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_15_light.png
deleted file mode 100644
index 2f18abd..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_15_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_16_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_16_dark.png
deleted file mode 100644
index de81133..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_16_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_16_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_16_light.png
deleted file mode 100644
index b80b191..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_16_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_17_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_17_dark.png
deleted file mode 100644
index 48aba3d..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_17_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_17_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_17_light.png
deleted file mode 100644
index ca34d5b..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_17_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_18_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_18_dark.png
deleted file mode 100644
index e9957b3..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_18_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_18_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_18_light.png
deleted file mode 100644
index a5d384f..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_18_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_19_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_19_dark.png
deleted file mode 100644
index ddc6297..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_19_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_19_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_19_light.png
deleted file mode 100644
index 28ab684..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_19_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_20_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_20_dark.png
deleted file mode 100644
index 51e7f75..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_20_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_20_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_20_light.png
deleted file mode 100644
index 4aa3ca3..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_20_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_21_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_21_dark.png
deleted file mode 100644
index 9caecde..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_21_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_21_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_21_light.png
deleted file mode 100644
index 1b8d0b6..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_21_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_22_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_22_dark.png
deleted file mode 100644
index 400be3c..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_22_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_22_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_22_light.png
deleted file mode 100644
index c14f1bf..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_22_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_23_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_23_dark.png
deleted file mode 100644
index 4e18b46..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_23_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_23_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_23_light.png
deleted file mode 100644
index c4c2c00..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_23_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_24_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_24_dark.png
deleted file mode 100644
index 98fae44..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_24_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_24_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_24_light.png
deleted file mode 100644
index d64c289..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_24_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_25_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_25_dark.png
deleted file mode 100644
index 91f9327..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_25_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_25_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_25_light.png
deleted file mode 100644
index f5e1f69..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_25_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_26_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_26_dark.png
deleted file mode 100644
index 3e6fafd..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_26_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_26_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_26_light.png
deleted file mode 100644
index ae2bd87..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_26_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_27_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_27_dark.png
deleted file mode 100644
index f73a1f8..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_27_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_27_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_27_light.png
deleted file mode 100644
index 78c1069..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_27_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_28_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_28_dark.png
deleted file mode 100644
index 562b803..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_28_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_28_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_28_light.png
deleted file mode 100644
index ddfba02..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_28_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_29_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_29_dark.png
deleted file mode 100644
index 257f2d2..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_29_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_29_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_29_light.png
deleted file mode 100644
index 38f5478..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_29_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_30_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_30_dark.png
deleted file mode 100644
index f995af0..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_30_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_30_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_30_light.png
deleted file mode 100644
index c50b7f0..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connected_30_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_00_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_00_dark.png
deleted file mode 100644
index f6dd214..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_00_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_00_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_00_light.png
deleted file mode 100644
index 6b7bdcd..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_00_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_01_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_01_dark.png
deleted file mode 100644
index c7fe576..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_01_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_01_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_01_light.png
deleted file mode 100644
index 0a5d6aa..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_01_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_02_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_02_dark.png
deleted file mode 100644
index 0aadfa3..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_02_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_02_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_02_light.png
deleted file mode 100644
index 125fe0b..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_02_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_03_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_03_dark.png
deleted file mode 100644
index 05c48a7..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_03_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_03_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_03_light.png
deleted file mode 100644
index 741e911..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_03_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_04_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_04_dark.png
deleted file mode 100644
index ae4218a..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_04_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_04_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_04_light.png
deleted file mode 100644
index 8b30fab..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_04_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_05_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_05_dark.png
deleted file mode 100644
index d7aa903..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_05_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_05_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_05_light.png
deleted file mode 100644
index f7e2f29..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_05_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_06_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_06_dark.png
deleted file mode 100644
index e7871e2..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_06_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_06_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_06_light.png
deleted file mode 100644
index 8c57f63..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_06_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_07_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_07_dark.png
deleted file mode 100644
index 0041b01..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_07_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_07_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_07_light.png
deleted file mode 100644
index 6dbb694..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_07_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_08_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_08_dark.png
deleted file mode 100644
index 08e1013..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_08_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_08_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_08_light.png
deleted file mode 100644
index 5c352c3..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_08_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_09_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_09_dark.png
deleted file mode 100644
index 70532e9..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_09_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_09_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_09_light.png
deleted file mode 100644
index 9c6ba30..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_09_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_10_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_10_dark.png
deleted file mode 100644
index 9ba3b5f..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_10_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_10_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_10_light.png
deleted file mode 100644
index bd4bb22..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_10_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_11_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_11_dark.png
deleted file mode 100644
index f3570f4..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_11_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_11_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_11_light.png
deleted file mode 100644
index 65a403e..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_11_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_12_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_12_dark.png
deleted file mode 100644
index f644bfd..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_12_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_12_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_12_light.png
deleted file mode 100644
index c7d6048..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_12_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_13_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_13_dark.png
deleted file mode 100644
index 6e0d558..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_13_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_13_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_13_light.png
deleted file mode 100644
index f3bc48d..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_13_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_14_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_14_dark.png
deleted file mode 100644
index 14d8f8e..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_14_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_14_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_14_light.png
deleted file mode 100644
index 98b90e5..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_14_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_15_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_15_dark.png
deleted file mode 100644
index 83234a7..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_15_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_15_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_15_light.png
deleted file mode 100644
index 47d452f..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_15_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_16_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_16_dark.png
deleted file mode 100644
index b81cf5a..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_16_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_16_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_16_light.png
deleted file mode 100644
index 20d08b4..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_16_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_17_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_17_dark.png
deleted file mode 100644
index 6feb3f1..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_17_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_17_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_17_light.png
deleted file mode 100644
index e6ae8b3..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_17_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_18_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_18_dark.png
deleted file mode 100644
index 0b0fc08..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_18_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_18_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_18_light.png
deleted file mode 100644
index c2a16ac..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_18_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_19_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_19_dark.png
deleted file mode 100644
index a3598cc..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_19_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_19_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_19_light.png
deleted file mode 100644
index 846d16d..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_19_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_20_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_20_dark.png
deleted file mode 100644
index 2070455..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_20_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_20_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_20_light.png
deleted file mode 100644
index ae6db13..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_20_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_21_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_21_dark.png
deleted file mode 100644
index 7f3828a..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_21_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_21_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_21_light.png
deleted file mode 100644
index aaccc73..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_21_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_22_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_22_dark.png
deleted file mode 100644
index 5c8ced9..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_22_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_22_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_22_light.png
deleted file mode 100644
index ad01b9e..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_22_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_23_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_23_dark.png
deleted file mode 100644
index ce31dd3..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_23_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_23_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_23_light.png
deleted file mode 100644
index 9ef78e4..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_23_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_24_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_24_dark.png
deleted file mode 100644
index a7c2cdb..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_24_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_24_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_24_light.png
deleted file mode 100644
index e7c5bea..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_24_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_25_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_25_dark.png
deleted file mode 100644
index ecad0d4..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_25_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_25_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_25_light.png
deleted file mode 100644
index 5fa5923..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_25_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_26_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_26_dark.png
deleted file mode 100644
index f687e25..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_26_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_26_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_26_light.png
deleted file mode 100644
index 9c06db8..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_26_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_27_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_27_dark.png
deleted file mode 100644
index 90225ba..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_27_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_27_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_27_light.png
deleted file mode 100644
index 19697de..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_27_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_28_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_28_dark.png
deleted file mode 100644
index d37ec21..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_28_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_28_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_28_light.png
deleted file mode 100644
index 21840bf..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_28_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_29_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_29_dark.png
deleted file mode 100644
index 5445e3a..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_29_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_29_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_29_light.png
deleted file mode 100644
index 2337c65..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_29_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_30_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_30_dark.png
deleted file mode 100644
index f6dd214..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_30_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_30_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_30_light.png
deleted file mode 100644
index 6b7bdcd..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_connecting_30_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disabled_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disabled_dark.png
deleted file mode 100644
index c4dc132..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disabled_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disabled_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disabled_light.png
deleted file mode 100644
index b14617c..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disabled_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disconnected_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disconnected_dark.png
deleted file mode 100644
index bb30773..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disconnected_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disconnected_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disconnected_light.png
deleted file mode 100644
index a05d7d7..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_disconnected_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_grey.png b/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_grey.png
deleted file mode 100644
index 2238d58..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_mr_button_grey.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_dark.png
deleted file mode 100755
index e40349d..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_group_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_group_dark.png
deleted file mode 100755
index f67c463..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_group_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_group_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_group_light.png
deleted file mode 100755
index 7fcebf5..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_group_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_light.png
deleted file mode 100755
index ea32a7a..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_speaker_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_tv_dark.png b/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_tv_dark.png
deleted file mode 100755
index d62ca37..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_tv_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_tv_light.png b/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_tv_light.png
deleted file mode 100755
index 3131256..0000000
--- a/packages/MediaComponents/res/drawable-xhdpi/ic_vol_type_tv_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_audiotrack_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_audiotrack_dark.png
deleted file mode 100644
index f131e1b..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_audiotrack_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_audiotrack_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_audiotrack_light.png
deleted file mode 100644
index e5946a2..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_audiotrack_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_dialog_close_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_dialog_close_dark.png
deleted file mode 100644
index b85e87f..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_dialog_close_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_dialog_close_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_dialog_close_light.png
deleted file mode 100644
index 51b4401..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_dialog_close_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_pause_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_media_pause_dark.png
deleted file mode 100644
index 3ea7e03..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_pause_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_pause_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_media_pause_light.png
deleted file mode 100644
index dc63538..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_pause_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_play_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_media_play_dark.png
deleted file mode 100644
index 2745c3a..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_play_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_play_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_media_play_light.png
deleted file mode 100644
index eda3ba5..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_play_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_stop_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_media_stop_dark.png
deleted file mode 100644
index 035ca18..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_stop_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_stop_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_media_stop_light.png
deleted file mode 100644
index eac183d..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_media_stop_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_00_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_00_dark.png
deleted file mode 100644
index 0db679e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_00_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_00_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_00_light.png
deleted file mode 100644
index 51c6051..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_00_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_01_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_01_dark.png
deleted file mode 100644
index c083914..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_01_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_01_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_01_light.png
deleted file mode 100644
index c3c3caf..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_01_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_02_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_02_dark.png
deleted file mode 100644
index fc444cf..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_02_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_02_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_02_light.png
deleted file mode 100644
index abd6377..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_02_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_03_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_03_dark.png
deleted file mode 100644
index 6dbd1da..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_03_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_03_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_03_light.png
deleted file mode 100644
index d2e7108..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_03_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_04_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_04_dark.png
deleted file mode 100644
index d9f596b..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_04_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_04_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_04_light.png
deleted file mode 100644
index 4f32e1a..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_04_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_05_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_05_dark.png
deleted file mode 100644
index c568e04..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_05_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_05_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_05_light.png
deleted file mode 100644
index ed20dd9..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_05_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_06_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_06_dark.png
deleted file mode 100644
index bbe39e7..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_06_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_06_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_06_light.png
deleted file mode 100644
index 1edc15f..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_06_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_07_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_07_dark.png
deleted file mode 100644
index 78aebaf..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_07_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_07_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_07_light.png
deleted file mode 100644
index b5a6a4f..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_07_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_08_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_08_dark.png
deleted file mode 100644
index 44b91ce..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_08_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_08_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_08_light.png
deleted file mode 100644
index 85f66f9..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_08_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_09_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_09_dark.png
deleted file mode 100644
index 51ea34b..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_09_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_09_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_09_light.png
deleted file mode 100644
index 952de04..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_09_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_10_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_10_dark.png
deleted file mode 100644
index 8b1aa21..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_10_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_10_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_10_light.png
deleted file mode 100644
index 534bcc0..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_10_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_11_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_11_dark.png
deleted file mode 100644
index f666b35..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_11_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_11_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_11_light.png
deleted file mode 100644
index 145a8fb..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_11_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_12_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_12_dark.png
deleted file mode 100644
index edeb132..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_12_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_12_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_12_light.png
deleted file mode 100644
index 9da2b60..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_12_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_13_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_13_dark.png
deleted file mode 100644
index ab80aa9..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_13_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_13_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_13_light.png
deleted file mode 100644
index 115efe4..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_13_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_14_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_14_dark.png
deleted file mode 100644
index 8c0cc31..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_14_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_14_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_14_light.png
deleted file mode 100644
index e6ae6fc..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_14_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_15_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_15_dark.png
deleted file mode 100644
index b8816c9..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_15_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_15_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_15_light.png
deleted file mode 100644
index bd42931..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_15_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_16_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_16_dark.png
deleted file mode 100644
index 10d5b7f..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_16_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_16_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_16_light.png
deleted file mode 100644
index 303a0fe..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_16_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_17_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_17_dark.png
deleted file mode 100644
index 3c2a655..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_17_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_17_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_17_light.png
deleted file mode 100644
index 90debc2..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_17_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_18_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_18_dark.png
deleted file mode 100644
index d3e78a7..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_18_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_18_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_18_light.png
deleted file mode 100644
index 3a3f991..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_18_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_19_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_19_dark.png
deleted file mode 100644
index 63fad9e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_19_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_19_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_19_light.png
deleted file mode 100644
index d6dd8d4..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_19_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_20_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_20_dark.png
deleted file mode 100644
index 890fd5f..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_20_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_20_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_20_light.png
deleted file mode 100644
index 6b0b5c1..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_20_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_21_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_21_dark.png
deleted file mode 100644
index 9ce1ef1..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_21_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_21_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_21_light.png
deleted file mode 100644
index 81710d4..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_21_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_22_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_22_dark.png
deleted file mode 100644
index 861c080..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_22_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_22_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_22_light.png
deleted file mode 100644
index 1c4aa21..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_22_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_23_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_23_dark.png
deleted file mode 100644
index 59a6b30..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_23_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_23_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_23_light.png
deleted file mode 100644
index c6e8fe0..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_23_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_24_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_24_dark.png
deleted file mode 100644
index 57b840e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_24_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_24_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_24_light.png
deleted file mode 100644
index bf24050..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_24_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_25_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_25_dark.png
deleted file mode 100644
index 01c18c1..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_25_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_25_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_25_light.png
deleted file mode 100644
index be9753e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_25_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_26_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_26_dark.png
deleted file mode 100644
index 3f291b1..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_26_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_26_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_26_light.png
deleted file mode 100644
index dc1c619..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_26_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_27_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_27_dark.png
deleted file mode 100644
index 6504a70..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_27_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_27_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_27_light.png
deleted file mode 100644
index a7e0a60..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_27_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_28_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_28_dark.png
deleted file mode 100644
index 57b1f3e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_28_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_28_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_28_light.png
deleted file mode 100644
index 5c551ec..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_28_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_29_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_29_dark.png
deleted file mode 100644
index 238667e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_29_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_29_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_29_light.png
deleted file mode 100644
index ffb8183..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_29_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_30_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_30_dark.png
deleted file mode 100644
index 4893f18..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_30_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_30_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_30_light.png
deleted file mode 100644
index ac5e156..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connected_30_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_00_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_00_dark.png
deleted file mode 100644
index 0db679e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_00_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_00_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_00_light.png
deleted file mode 100644
index 51c6051..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_00_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_01_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_01_dark.png
deleted file mode 100644
index c083914..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_01_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_01_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_01_light.png
deleted file mode 100644
index c3c3caf..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_01_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_02_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_02_dark.png
deleted file mode 100644
index fc444cf..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_02_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_02_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_02_light.png
deleted file mode 100644
index abd6377..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_02_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_03_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_03_dark.png
deleted file mode 100644
index 6dbd1da..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_03_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_03_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_03_light.png
deleted file mode 100644
index d2e7108..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_03_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_04_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_04_dark.png
deleted file mode 100644
index d9f596b..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_04_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_04_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_04_light.png
deleted file mode 100644
index 4f32e1a..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_04_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_05_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_05_dark.png
deleted file mode 100644
index c568e04..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_05_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_05_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_05_light.png
deleted file mode 100644
index ed20dd9..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_05_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_06_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_06_dark.png
deleted file mode 100644
index bbe39e7..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_06_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_06_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_06_light.png
deleted file mode 100644
index 1edc15f..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_06_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_07_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_07_dark.png
deleted file mode 100644
index 78aebaf..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_07_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_07_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_07_light.png
deleted file mode 100644
index b5a6a4f..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_07_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_08_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_08_dark.png
deleted file mode 100644
index 44b91ce..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_08_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_08_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_08_light.png
deleted file mode 100644
index 85f66f9..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_08_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_09_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_09_dark.png
deleted file mode 100644
index 51ea34b..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_09_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_09_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_09_light.png
deleted file mode 100644
index 952de04..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_09_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_10_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_10_dark.png
deleted file mode 100644
index 8b1aa21..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_10_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_10_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_10_light.png
deleted file mode 100644
index 534bcc0..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_10_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_11_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_11_dark.png
deleted file mode 100644
index 1fffa01..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_11_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_11_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_11_light.png
deleted file mode 100644
index 0ff7e57..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_11_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_12_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_12_dark.png
deleted file mode 100644
index 06ac4dc..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_12_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_12_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_12_light.png
deleted file mode 100644
index 42a86f5..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_12_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_13_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_13_dark.png
deleted file mode 100644
index 0301090..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_13_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_13_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_13_light.png
deleted file mode 100644
index 4396f0e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_13_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_14_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_14_dark.png
deleted file mode 100644
index e19001b..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_14_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_14_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_14_light.png
deleted file mode 100644
index 2271581..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_14_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_15_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_15_dark.png
deleted file mode 100644
index 5e96208..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_15_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_15_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_15_light.png
deleted file mode 100644
index 0f69500..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_15_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_16_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_16_dark.png
deleted file mode 100644
index 07e1bd6..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_16_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_16_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_16_light.png
deleted file mode 100644
index cde8f19..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_16_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_17_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_17_dark.png
deleted file mode 100644
index b632e95..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_17_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_17_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_17_light.png
deleted file mode 100644
index 11d5d2e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_17_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_18_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_18_dark.png
deleted file mode 100644
index 660d527..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_18_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_18_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_18_light.png
deleted file mode 100644
index 2761ae1..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_18_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_19_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_19_dark.png
deleted file mode 100644
index 0aa3f84..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_19_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_19_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_19_light.png
deleted file mode 100644
index 27d166f..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_19_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_20_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_20_dark.png
deleted file mode 100644
index ebe527e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_20_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_20_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_20_light.png
deleted file mode 100644
index aeb2a8e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_20_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_21_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_21_dark.png
deleted file mode 100644
index 7337af5..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_21_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_21_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_21_light.png
deleted file mode 100644
index f3f31ef..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_21_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_22_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_22_dark.png
deleted file mode 100644
index 20d9f57..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_22_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_22_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_22_light.png
deleted file mode 100644
index bf8eb77..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_22_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_23_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_23_dark.png
deleted file mode 100644
index 56a0e14..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_23_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_23_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_23_light.png
deleted file mode 100644
index 67425e1..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_23_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_24_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_24_dark.png
deleted file mode 100644
index 7c76e19..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_24_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_24_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_24_light.png
deleted file mode 100644
index e02f1ed..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_24_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_25_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_25_dark.png
deleted file mode 100644
index f5fdcdd..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_25_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_25_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_25_light.png
deleted file mode 100644
index 8ce9b819..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_25_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_26_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_26_dark.png
deleted file mode 100644
index a29e443..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_26_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_26_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_26_light.png
deleted file mode 100644
index 349ca89..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_26_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_27_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_27_dark.png
deleted file mode 100644
index 0fc75d5..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_27_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_27_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_27_light.png
deleted file mode 100644
index 5cbd27c..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_27_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_28_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_28_dark.png
deleted file mode 100644
index 0ebb0ac..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_28_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_28_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_28_light.png
deleted file mode 100644
index 5b514aa..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_28_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_29_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_29_dark.png
deleted file mode 100644
index 8e7fe5c..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_29_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_29_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_29_light.png
deleted file mode 100644
index efb2c10..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_29_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_30_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_30_dark.png
deleted file mode 100644
index 0db679e..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_30_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_30_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_30_light.png
deleted file mode 100644
index 51c6051..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_connecting_30_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disabled_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disabled_dark.png
deleted file mode 100644
index fdb2121..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disabled_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disabled_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disabled_light.png
deleted file mode 100644
index 9ce7e3a..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disabled_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disconnected_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disconnected_dark.png
deleted file mode 100644
index e8601ce..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disconnected_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disconnected_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disconnected_light.png
deleted file mode 100644
index 34928d7..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_disconnected_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_grey.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_grey.png
deleted file mode 100644
index 792fd77..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_mr_button_grey.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_dark.png
deleted file mode 100755
index f171a8c..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_group_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_group_dark.png
deleted file mode 100755
index c8cb6ca..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_group_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_group_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_group_light.png
deleted file mode 100755
index 9c8863d..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_group_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_light.png
deleted file mode 100755
index 9335038..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_speaker_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_tv_dark.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_tv_dark.png
deleted file mode 100755
index a6a4858..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_tv_dark.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_tv_light.png b/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_tv_light.png
deleted file mode 100755
index 4ca6787..0000000
--- a/packages/MediaComponents/res/drawable-xxhdpi/ic_vol_type_tv_light.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_00.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_00.png
deleted file mode 100644
index b2305d2..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_00.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_01.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_01.png
deleted file mode 100644
index 59395d4..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_01.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_02.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_02.png
deleted file mode 100644
index 70a7282..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_02.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_03.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_03.png
deleted file mode 100644
index b3f0f53..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_03.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_04.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_04.png
deleted file mode 100644
index 66a80d9..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_04.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_05.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_05.png
deleted file mode 100644
index 8ec3939..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_05.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_06.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_06.png
deleted file mode 100644
index 0f02536..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_06.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_07.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_07.png
deleted file mode 100644
index ba228f4..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_07.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_08.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_08.png
deleted file mode 100644
index 304277e..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_08.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_09.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_09.png
deleted file mode 100644
index f865bfb..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_09.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_10.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_10.png
deleted file mode 100644
index 17c5d6b..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_10.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_11.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_11.png
deleted file mode 100644
index a2f4ad5..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_11.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_12.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_12.png
deleted file mode 100644
index c230648..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_12.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_13.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_13.png
deleted file mode 100644
index b99324e..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_13.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_14.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_14.png
deleted file mode 100644
index c8618f0..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_14.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_15.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_15.png
deleted file mode 100644
index 4a0d770..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_collapse_15.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_00.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_00.png
deleted file mode 100644
index 4a0d770..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_00.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_01.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_01.png
deleted file mode 100644
index 4db4e50..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_01.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_02.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_02.png
deleted file mode 100644
index 82b5f03..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_02.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_03.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_03.png
deleted file mode 100644
index b05c758..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_03.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_04.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_04.png
deleted file mode 100644
index fa5c7fa..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_04.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_05.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_05.png
deleted file mode 100644
index 2c287e4..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_05.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_06.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_06.png
deleted file mode 100644
index eb7d0cf..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_06.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_07.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_07.png
deleted file mode 100644
index 95fa72b..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_07.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_08.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_08.png
deleted file mode 100644
index 5650eea..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_08.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_09.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_09.png
deleted file mode 100644
index 6f44355..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_09.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_10.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_10.png
deleted file mode 100644
index 4e877c3..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_10.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_11.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_11.png
deleted file mode 100644
index 7927f0a..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_11.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_12.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_12.png
deleted file mode 100644
index 71b19bb..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_12.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_13.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_13.png
deleted file mode 100644
index bf5921e..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_13.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_14.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_14.png
deleted file mode 100644
index 14b76b1..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_14.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_15.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_15.png
deleted file mode 100644
index b2305d2..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_group_expand_15.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable-xxxhdpi/ic_mr_button_grey.png b/packages/MediaComponents/res/drawable-xxxhdpi/ic_mr_button_grey.png
deleted file mode 100644
index 04a9525..0000000
--- a/packages/MediaComponents/res/drawable-xxxhdpi/ic_mr_button_grey.png
+++ /dev/null
Binary files differ
diff --git a/packages/MediaComponents/res/drawable/custom_progress.xml b/packages/MediaComponents/res/drawable/custom_progress.xml
deleted file mode 100644
index 9731a6e..0000000
--- a/packages/MediaComponents/res/drawable/custom_progress.xml
+++ /dev/null
@@ -1,36 +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.
--->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-    <item android:id="@android:id/background">
-        <shape android:shape="rectangle" >
-            <solid android:color="#26000000" />
-        </shape>
-    </item>
-    <item android:id="@android:id/secondaryProgress">
-        <clip>
-            <shape android:shape="rectangle" >
-                <solid android:color="#5Cffffff" />
-            </shape>
-        </clip>
-    </item>
-    <item android:id="@android:id/progress">
-        <clip>
-            <shape android:shape="rectangle" >
-                <solid android:color="#ffffff" />
-            </shape>
-        </clip>
-    </item>
-</layer-list>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/custom_progress_thumb.xml b/packages/MediaComponents/res/drawable/custom_progress_thumb.xml
deleted file mode 100644
index 1a35970..0000000
--- a/packages/MediaComponents/res/drawable/custom_progress_thumb.xml
+++ /dev/null
@@ -1,22 +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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval" >
-    <solid android:color="#ffffff" />
-    <size
-        android:height="@dimen/mcv2_custom_progress_thumb_size"
-        android:width="@dimen/mcv2_custom_progress_thumb_size" />
-</shape>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_arrow_back.xml b/packages/MediaComponents/res/drawable/ic_arrow_back.xml
deleted file mode 100644
index 2b5f71e..0000000
--- a/packages/MediaComponents/res/drawable/ic_arrow_back.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="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_aspect_ratio.xml b/packages/MediaComponents/res/drawable/ic_aspect_ratio.xml
deleted file mode 100644
index c6228e6..0000000
--- a/packages/MediaComponents/res/drawable/ic_aspect_ratio.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="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_audiotrack.xml b/packages/MediaComponents/res/drawable/ic_audiotrack.xml
deleted file mode 100644
index 27c12b5..0000000
--- a/packages/MediaComponents/res/drawable/ic_audiotrack.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:fillColor="#FFFFFF"
-        android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_check.xml b/packages/MediaComponents/res/drawable/ic_check.xml
deleted file mode 100644
index 32f720b..0000000
--- a/packages/MediaComponents/res/drawable/ic_check.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:pathData="M0 0h24v24H0z" />
-    <path
-        android:fillColor="#FFFFFF"
-        android:pathData="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" />
-</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_chevron_left.xml b/packages/MediaComponents/res/drawable/ic_chevron_left.xml
deleted file mode 100644
index 8336d17..0000000
--- a/packages/MediaComponents/res/drawable/ic_chevron_left.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="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_chevron_right.xml b/packages/MediaComponents/res/drawable/ic_chevron_right.xml
deleted file mode 100644
index fb2ce09..0000000
--- a/packages/MediaComponents/res/drawable/ic_chevron_right.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="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_default_album_image.xml b/packages/MediaComponents/res/drawable/ic_default_album_image.xml
deleted file mode 100644
index 1cee643..0000000
--- a/packages/MediaComponents/res/drawable/ic_default_album_image.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="512dp"
-    android:height="512dp"
-    android:viewportWidth="512"
-    android:viewportHeight="512">
-
-    <path
-        android:fillColor="#616161"
-        android:pathData="M 0 0 H 512 V 512 H 0 V 0 Z" />
-    <path
-        android:fillColor="#525252"
-        android:pathData="M256,151v123.14c-6.88-4.02-14.82-6.48-23.33-6.48 c-25.78,0-46.67,20.88-46.67,46.67c0,25.78,20.88,46.67,46.67,46.67s46.67-20.88,46.67-46.67V197.67H326V151H256z" />
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_forward_30.xml b/packages/MediaComponents/res/drawable/ic_forward_30.xml
deleted file mode 100644
index 7efdf16..0000000
--- a/packages/MediaComponents/res/drawable/ic_forward_30.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="40dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <group>
-        <clip-path android:pathData="M24,24H0V0h24v24z M 0,0" />
-    <path
-        android:pathData="M9.6 13.5h.4c.2 0 .4,-.1.5,-.2s.2,-.2.2,-.4v-.2s-.1,-.1,-.1,-.2,-.1,-.1,-.2,-.1h-.5s-.1.1,-.2.1,-.1.1,-.1.2v.2h-1c0,-.2 0,-.3.1,-.5s.2,-.3.3,-.4.3,-.2.4,-.2.4,-.1.5,-.1c.2 0 .4 0 .6.1s.3.1.5.2.2.2.3.4.1.3.1.5v.3s-.1.2,-.1.3,-.1.2,-.2.2,-.2.1,-.3.2c.2.1.4.2.5.4s.2.4.2.6c0 .2 0 .4,-.1.5s-.2.3,-.3.4,-.3.2,-.5.2,-.4.1,-.6.1c-.2 0,-.4 0,-.5,-.1s-.3,-.1,-.5,-.2,-.2,-.2,-.3,-.4,-.1,-.4,-.1,-.6h.8v.2s.1.1.1.2.1.1.2.1h.5s.1,-.1.2,-.1.1,-.1.1,-.2v-.5s-.1,-.1,-.1,-.2,-.1,-.1,-.2,-.1h-.6v-.7zm5.7.7c0 .3 0 .6,-.1.8l-.3.6s-.3.3,-.5.3,-.4.1,-.6.1,-.4 0,-.6,-.1,-.3,-.2,-.5,-.3,-.2,-.3,-.3,-.6,-.1,-.5,-.1,-.8v-.7c0,-.3 0,-.6.1,-.8l.3,-.6s.3,-.3.5,-.3.4,-.1.6,-.1.4 0 .6.1.3.2.5.3.2.3.3.6.1.5.1.8v.7zm-.9,-.8v-.5s-.1,-.2,-.1,-.3,-.1,-.1,-.2,-.2,-.2,-.1,-.3,-.1,-.2 0,-.3.1l-.2.2s-.1.2,-.1.3v2s.1.2.1.3.1.1.2.2.2.1.3.1.2 0 .3,-.1l.2,-.2s.1,-.2.1,-.3v-1.5zM4 13c0 4.4 3.6 8 8 8s8,-3.6 8,-8h-2c0 3.3,-2.7 6,-6 6s-6,-2.7,-6,-6 2.7,-6 6,-6v4l5,-5,-5,-5v4c-4.4 0,-8 3.6,-8 8z"
-        android:fillColor="#FFFFFF"/>
-    </group>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_fullscreen.xml b/packages/MediaComponents/res/drawable/ic_fullscreen.xml
deleted file mode 100644
index 4b4f6bc..0000000
--- a/packages/MediaComponents/res/drawable/ic_fullscreen.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="M7,14L5,14v5h5v-2L7,17v-3zM5,10h2L7,7h3L10,5L5,5v5zM17,17h-3v2h5v-5h-2v3zM14,5v2h3v3h2L19,5h-5z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_fullscreen_exit.xml b/packages/MediaComponents/res/drawable/ic_fullscreen_exit.xml
deleted file mode 100644
index bc204e2..0000000
--- a/packages/MediaComponents/res/drawable/ic_fullscreen_exit.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="M5,16h3v3h2v-5L5,14v2zM8,8L5,8v2h5L10,5L8,5v3zM14,19h2v-3h3v-2h-5v5zM16,8L16,5h-2v5h5L19,8h-3z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_help.xml b/packages/MediaComponents/res/drawable/ic_help.xml
deleted file mode 100644
index 4d1d75d..0000000
--- a/packages/MediaComponents/res/drawable/ic_help.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:pathData="M0 0h24v24H0z" />
-    <path
-        android:fillColor="#FFFFFF"
-        android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1
-17h-2v-2h2v2zm2.07-7.75l-.9 .92 C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1 .45 -2.1
-1.17-2.83l1.24-1.26c.37-.36 .59 -.86 .59 -1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21
-1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z" />
-</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_high_quality.xml b/packages/MediaComponents/res/drawable/ic_high_quality.xml
deleted file mode 100644
index f76e22f..0000000
--- a/packages/MediaComponents/res/drawable/ic_high_quality.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="34dp"
-    android:height="34dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:pathData="M0 0h24v24H0z" />
-    <path
-        android:fillColor="#FFFFFF"
-        android:pathData="M19 4H5c-1.11 0-2 0.9-2 2v12c0 1.1 0.89 2 2 2h14c1.1 0 2-0.9 2-2V6c0-1.1-0.9-2-2-2zm-8 11H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7-1c0 0.55-0.45 1-1 1h-0.75v1.5h-1.5V15H14c-0.55 0-1-0.45-1-1v-4c0-0.55 0.45 -1 1-1h3c0.55 0 1 0.45 1 1v4zm-3.5-0.5h2v-3h-2v3z" />
-</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_launch.xml b/packages/MediaComponents/res/drawable/ic_launch.xml
deleted file mode 100644
index f7af6aa..0000000
--- a/packages/MediaComponents/res/drawable/ic_launch.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:fillColor="#FFFFFF"
-        android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_mute.xml b/packages/MediaComponents/res/drawable/ic_mute.xml
deleted file mode 100644
index 560aaec..0000000
--- a/packages/MediaComponents/res/drawable/ic_mute.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="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_pause_circle_filled.xml b/packages/MediaComponents/res/drawable/ic_pause_circle_filled.xml
deleted file mode 100644
index 73be228..0000000
--- a/packages/MediaComponents/res/drawable/ic_pause_circle_filled.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="40dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,16L9,16L9,8h2v8zM15,16h-2L13,8h2v8z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_play_circle_filled.xml b/packages/MediaComponents/res/drawable/ic_play_circle_filled.xml
deleted file mode 100644
index 9d39def..0000000
--- a/packages/MediaComponents/res/drawable/ic_play_circle_filled.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="40dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,16.5v-9l6,4.5 -6,4.5z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_replay_circle_filled.xml b/packages/MediaComponents/res/drawable/ic_replay_circle_filled.xml
deleted file mode 100644
index a56d5d9..0000000
--- a/packages/MediaComponents/res/drawable/ic_replay_circle_filled.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="40dp"
-    android:height="40dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:pathData="M0,0h24v24H0V0z" />
-    <path
-        android:fillColor="#FFFFFF"
-        android:fillType="evenOdd"
-        android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10c5.52,0,10-4.48,10-10
-C22,6.48,17.52,2,12,2z
-M18,12c0,3.31-2.69,6-6,6c-3.31,0-6-2.69-6-6h2c0,2.21,1.79,4,4,4s4-1.79,4-4s-1.79-4-4-4v3L8,7l4-4v3
-C15.31,6,18,8.69,18,12z" />
-</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_rewind_10.xml b/packages/MediaComponents/res/drawable/ic_rewind_10.xml
deleted file mode 100644
index ae586b4..0000000
--- a/packages/MediaComponents/res/drawable/ic_rewind_10.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="40dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <group>
-        <clip-path android:pathData="M0,0h24v24H0V0z M 0,0" />
-    <path
-        android:pathData="M12 5V1L7 6l5 5V7c3.3 0 6 2.7 6 6s-2.7 6,-6 6,-6,-2.7,-6,-6H4c0 4.4 3.6 8 8 8s8,-3.6 8,-8,-3.6,-8,-8,-8zm-1.1 11H10v-3.3L9 13v-.7l1.8,-.6h.1V16zm4.3,-1.8c0 .3 0 .6,-.1.8l-.3.6s-.3.3,-.5.3,-.4.1,-.6.1,-.4 0,-.6,-.1,-.3,-.2,-.5,-.3,-.2,-.3,-.3,-.6,-.1,-.5,-.1,-.8v-.7c0,-.3 0,-.6.1,-.8l.3,-.6s.3,-.3.5,-.3.4,-.1.6,-.1.4 0 .6.1c.2.1.3.2.5.3s.2.3.3.6.1.5.1.8v.7zm-.9,-.8v-.5s-.1,-.2,-.1,-.3,-.1,-.1,-.2,-.2,-.2,-.1,-.3,-.1,-.2 0,-.3.1l-.2.2s-.1.2,-.1.3v2s.1.2.1.3.1.1.2.2.2.1.3.1.2 0 .3,-.1l.2,-.2s.1,-.2.1,-.3v-1.5z"
-        android:fillColor="#FFFFFF"/>
-    </group>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_sd.xml b/packages/MediaComponents/res/drawable/ic_sd.xml
deleted file mode 100644
index decb6d2..0000000
--- a/packages/MediaComponents/res/drawable/ic_sd.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:pathData="M0,0h24v24H0V0z" />
-    <path
-        android:fillColor="#FFFFFF"
-        android:pathData="M19,3H5C3.89,3,3,3.9,3,5v14c0,1.1,0.89,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z
-M13,9h4c0.55,0,1,0.45,1,1v4 c0,0.55-0.45,1-1,1h-4V9z
-M9.5,13.5v-1H7c-0.55,0-1-0.45-1-1V10c0-0.55,0.45-1,1-1h3c0.55,0,1,0.45,1,1v1H9.5v-0.5h-2v1H10
-c0.55,0,1,0.45,1,1V14c0,0.55-0.45,1-1,1H7c-0.55,0-1-0.45-1-1v-1h1.5v0.5H9.5z
-M14.5,13.5h2v-3h-2V13.5z" />
-</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_settings.xml b/packages/MediaComponents/res/drawable/ic_settings.xml
deleted file mode 100644
index a59ecc1..0000000
--- a/packages/MediaComponents/res/drawable/ic_settings.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="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_skip_next.xml b/packages/MediaComponents/res/drawable/ic_skip_next.xml
deleted file mode 100644
index b1f2812..0000000
--- a/packages/MediaComponents/res/drawable/ic_skip_next.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="40dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_skip_previous.xml b/packages/MediaComponents/res/drawable/ic_skip_previous.xml
deleted file mode 100644
index 81da314..0000000
--- a/packages/MediaComponents/res/drawable/ic_skip_previous.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="40dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M6,6h2v12L6,18zM9.5,12l8.5,6L18,6z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/MediaComponents/res/drawable/ic_subtitle_off.xml b/packages/MediaComponents/res/drawable/ic_subtitle_off.xml
deleted file mode 100644
index c0a727a..0000000
--- a/packages/MediaComponents/res/drawable/ic_subtitle_off.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:pathData="M0,0h24v24H0V0z" />
-    <path
-        android:fillColor="#FFFFFF"
-        android:pathData="M19.5,5.5v13h-15v-13H19.5z M19,4H5C3.89,4,3,4.9,3,6v12c0,1.1,0.89,2,2,2h14c1.1,0,2-0.9,2-2V6C21,4.9,20.1,4,19,4L19,4z" />
-    <path
-        android:fillColor="#FFFFFF"
-        android:pathData="M11,11H9.5v-0.5h-2v3h2V13H11v1c0,0.55-0.45,1-1,1H7c-0.55,0-1-0.45-1-1v-4c0-0.55,0.45-1,1-1h3c0.55,0,1,0.45,1,1V11z" />
-    <path
-        android:fillColor="#FFFFFF"
-        android:pathData="M18,11h-1.5v-0.5h-2v3h2V13H18v1c0,0.55-0.45,1-1,1h-3c-0.55,0-1-0.45-1-1v-4c0-0.55,0.45-1,1-1h3c0.55,0,1,0.45,1,1V11z" />
-</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_subtitle_on.xml b/packages/MediaComponents/res/drawable/ic_subtitle_on.xml
deleted file mode 100644
index 7c91c06..0000000
--- a/packages/MediaComponents/res/drawable/ic_subtitle_on.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:pathData="M0 0h24v24H0z" />
-    <path
-        android:fillColor="#FFFFFF"
-        android:pathData="M19 4H5c-1.11 0-2 0.9-2 2v12c0 1.1 0.89 2 2 2h14c1.1 0 2-0.9 2-2V6c0-1.1-0.9-2-2-2zm-8 7H9.5v-0.5h-2v3h2V13H11v1c0 0.55-0.45 1-1 1H7c-0.55 0-1-0.45-1-1v-4c0-0.55 0.45 -1 1-1h3c0.55 0 1 0.45 1 1v1zm7 0h-1.5v-0.5h-2v3h2V13H18v1c0 0.55-0.45 1-1 1h-3c-0.55 0-1-0.45-1-1v-4c0-0.55 0.45 -1 1-1h3c0.55 0 1 0.45 1 1v1z" />
-</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/ic_unmute.xml b/packages/MediaComponents/res/drawable/ic_unmute.xml
deleted file mode 100644
index 9dfb2b9..0000000
--- a/packages/MediaComponents/res/drawable/ic_unmute.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="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/drawable/mr_button_connected_dark.xml b/packages/MediaComponents/res/drawable/mr_button_connected_dark.xml
deleted file mode 100644
index 110ff13..0000000
--- a/packages/MediaComponents/res/drawable/mr_button_connected_dark.xml
+++ /dev/null
@@ -1,51 +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.
--->
-
-<animation-list
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="true">
-    <item android:drawable="@drawable/ic_mr_button_connected_00_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_01_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_02_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_03_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_04_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_05_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_06_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_07_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_08_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_09_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_10_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_11_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_12_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_13_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_14_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_15_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_16_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_17_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_18_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_19_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_20_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_21_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_22_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_23_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_24_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_25_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_26_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_27_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_28_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_29_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_30_dark" android:duration="42" />
-</animation-list>
diff --git a/packages/MediaComponents/res/drawable/mr_button_connected_light.xml b/packages/MediaComponents/res/drawable/mr_button_connected_light.xml
deleted file mode 100644
index bcfc7fe..0000000
--- a/packages/MediaComponents/res/drawable/mr_button_connected_light.xml
+++ /dev/null
@@ -1,51 +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.
--->
-
-<animation-list
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="true">
-    <item android:drawable="@drawable/ic_mr_button_connected_00_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_01_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_02_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_03_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_04_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_05_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_06_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_07_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_08_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_09_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_10_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_11_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_12_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_13_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_14_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_15_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_16_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_17_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_18_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_19_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_20_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_21_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_22_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_23_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_24_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_25_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_26_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_27_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_28_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_29_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connected_30_light" android:duration="42" />
-</animation-list>
diff --git a/packages/MediaComponents/res/drawable/mr_button_connecting_dark.xml b/packages/MediaComponents/res/drawable/mr_button_connecting_dark.xml
deleted file mode 100644
index 55af7b3..0000000
--- a/packages/MediaComponents/res/drawable/mr_button_connecting_dark.xml
+++ /dev/null
@@ -1,51 +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.
--->
-
-<animation-list
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="false">
-    <item android:drawable="@drawable/ic_mr_button_connecting_00_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_01_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_02_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_03_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_04_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_05_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_06_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_07_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_08_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_09_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_10_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_11_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_12_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_13_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_14_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_15_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_16_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_17_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_18_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_19_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_20_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_21_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_22_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_23_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_24_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_25_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_26_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_27_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_28_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_29_dark" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_30_dark" android:duration="42" />
-</animation-list>
diff --git a/packages/MediaComponents/res/drawable/mr_button_connecting_light.xml b/packages/MediaComponents/res/drawable/mr_button_connecting_light.xml
deleted file mode 100644
index 93b4170..0000000
--- a/packages/MediaComponents/res/drawable/mr_button_connecting_light.xml
+++ /dev/null
@@ -1,51 +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.
--->
-
-<animation-list
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="false">
-    <item android:drawable="@drawable/ic_mr_button_connecting_00_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_01_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_02_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_03_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_04_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_05_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_06_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_07_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_08_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_09_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_10_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_11_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_12_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_13_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_14_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_15_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_16_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_17_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_18_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_19_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_20_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_21_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_22_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_23_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_24_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_25_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_26_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_27_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_28_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_29_light" android:duration="42" />
-    <item android:drawable="@drawable/ic_mr_button_connecting_30_light" android:duration="42" />
-</animation-list>
diff --git a/packages/MediaComponents/res/drawable/mr_button_dark.xml b/packages/MediaComponents/res/drawable/mr_button_dark.xml
deleted file mode 100644
index 8f1dfaa..0000000
--- a/packages/MediaComponents/res/drawable/mr_button_dark.xml
+++ /dev/null
@@ -1,25 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_checked="true" android:state_enabled="true"
-            android:drawable="@drawable/mr_button_connected_dark" />
-    <item android:state_checkable="true" android:state_enabled="true"
-            android:drawable="@drawable/mr_button_connecting_dark" />
-    <item android:state_enabled="true"
-            android:drawable="@drawable/ic_mr_button_disconnected_dark" />
-    <item android:drawable="@drawable/ic_mr_button_disabled_dark" />
-</selector>
diff --git a/packages/MediaComponents/res/drawable/mr_button_light.xml b/packages/MediaComponents/res/drawable/mr_button_light.xml
deleted file mode 100644
index 1d3d84e..0000000
--- a/packages/MediaComponents/res/drawable/mr_button_light.xml
+++ /dev/null
@@ -1,25 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_checked="true" android:state_enabled="true"
-            android:drawable="@drawable/mr_button_connected_light" />
-    <item android:state_checkable="true" android:state_enabled="true"
-            android:drawable="@drawable/mr_button_connecting_light" />
-    <item android:state_enabled="true"
-            android:drawable="@drawable/ic_mr_button_disconnected_light" />
-    <item android:drawable="@drawable/ic_mr_button_disabled_light" />
-</selector>
diff --git a/packages/MediaComponents/res/drawable/mr_dialog_close_dark.xml b/packages/MediaComponents/res/drawable/mr_dialog_close_dark.xml
deleted file mode 100644
index 288c8c7..0000000
--- a/packages/MediaComponents/res/drawable/mr_dialog_close_dark.xml
+++ /dev/null
@@ -1,19 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/ic_dialog_close_dark" />
-</selector>
diff --git a/packages/MediaComponents/res/drawable/mr_dialog_close_light.xml b/packages/MediaComponents/res/drawable/mr_dialog_close_light.xml
deleted file mode 100644
index cd50e0f..0000000
--- a/packages/MediaComponents/res/drawable/mr_dialog_close_light.xml
+++ /dev/null
@@ -1,24 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <bitmap
-            android:src="@drawable/ic_dialog_close_light"
-            android:alpha="0.87" />
-    </item>
-
-</selector>
diff --git a/packages/MediaComponents/res/drawable/mr_dialog_material_background_dark.xml b/packages/MediaComponents/res/drawable/mr_dialog_material_background_dark.xml
deleted file mode 100644
index ebc7eca..0000000
--- a/packages/MediaComponents/res/drawable/mr_dialog_material_background_dark.xml
+++ /dev/null
@@ -1,23 +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.
--->
-
-<!-- This is the copy of @drawable/abc_dialog_material_background_dark except for inset
-     which includes unnecessary padding. -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shape="rectangle">
-    <corners android:radius="2dp" />
-    <solid android:color="@color/background_floating_material_dark" />
-</shape>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/mr_dialog_material_background_light.xml b/packages/MediaComponents/res/drawable/mr_dialog_material_background_light.xml
deleted file mode 100644
index c1b235a..0000000
--- a/packages/MediaComponents/res/drawable/mr_dialog_material_background_light.xml
+++ /dev/null
@@ -1,23 +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.
--->
-
-<!-- This is the copy of @drawable/abc_dialog_material_background_light except for inset
-     which includes unnecessary padding. -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shape="rectangle">
-    <corners android:radius="2dp" />
-    <solid android:color="@color/background_floating_material_light" />
-</shape>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/drawable/mr_group_collapse.xml b/packages/MediaComponents/res/drawable/mr_group_collapse.xml
deleted file mode 100644
index 8f72bc8..0000000
--- a/packages/MediaComponents/res/drawable/mr_group_collapse.xml
+++ /dev/null
@@ -1,35 +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.
--->
-
-<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="true">
-    <item android:drawable="@drawable/ic_group_collapse_00" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_01" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_02" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_03" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_04" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_05" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_06" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_07" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_08" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_09" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_10" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_11" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_12" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_13" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_14" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_collapse_15" android:duration="13" />
-</animation-list>
diff --git a/packages/MediaComponents/res/drawable/mr_group_expand.xml b/packages/MediaComponents/res/drawable/mr_group_expand.xml
deleted file mode 100644
index 6b3fdb6..0000000
--- a/packages/MediaComponents/res/drawable/mr_group_expand.xml
+++ /dev/null
@@ -1,35 +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.
--->
-
-<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="true">
-    <item android:drawable="@drawable/ic_group_expand_00" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_01" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_02" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_03" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_04" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_05" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_06" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_07" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_08" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_09" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_10" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_11" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_12" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_13" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_14" android:duration="13" />
-    <item android:drawable="@drawable/ic_group_expand_15" android:duration="13" />
-</animation-list>
diff --git a/packages/MediaComponents/res/drawable/mr_media_pause_dark.xml b/packages/MediaComponents/res/drawable/mr_media_pause_dark.xml
deleted file mode 100644
index 86218a7..0000000
--- a/packages/MediaComponents/res/drawable/mr_media_pause_dark.xml
+++ /dev/null
@@ -1,19 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/ic_media_pause_dark" />
-</selector>
diff --git a/packages/MediaComponents/res/drawable/mr_media_pause_light.xml b/packages/MediaComponents/res/drawable/mr_media_pause_light.xml
deleted file mode 100644
index 2dd1f02..0000000
--- a/packages/MediaComponents/res/drawable/mr_media_pause_light.xml
+++ /dev/null
@@ -1,23 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <bitmap
-            android:src="@drawable/ic_media_pause_light"
-            android:alpha="0.87" />
-    </item>
-</selector>
diff --git a/packages/MediaComponents/res/drawable/mr_media_play_dark.xml b/packages/MediaComponents/res/drawable/mr_media_play_dark.xml
deleted file mode 100644
index 9d45a33..0000000
--- a/packages/MediaComponents/res/drawable/mr_media_play_dark.xml
+++ /dev/null
@@ -1,19 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/ic_media_play_dark" />
-</selector>
diff --git a/packages/MediaComponents/res/drawable/mr_media_play_light.xml b/packages/MediaComponents/res/drawable/mr_media_play_light.xml
deleted file mode 100644
index f1fb7a6..0000000
--- a/packages/MediaComponents/res/drawable/mr_media_play_light.xml
+++ /dev/null
@@ -1,23 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <bitmap
-            android:src="@drawable/ic_media_play_light"
-            android:alpha="0.87" />
-    </item>
-</selector>
diff --git a/packages/MediaComponents/res/drawable/mr_media_stop_dark.xml b/packages/MediaComponents/res/drawable/mr_media_stop_dark.xml
deleted file mode 100644
index 3e108a9..0000000
--- a/packages/MediaComponents/res/drawable/mr_media_stop_dark.xml
+++ /dev/null
@@ -1,19 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/ic_media_stop_dark" />
-</selector>
diff --git a/packages/MediaComponents/res/drawable/mr_media_stop_light.xml b/packages/MediaComponents/res/drawable/mr_media_stop_light.xml
deleted file mode 100644
index b2c6ce8..0000000
--- a/packages/MediaComponents/res/drawable/mr_media_stop_light.xml
+++ /dev/null
@@ -1,23 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <bitmap
-            android:src="@drawable/ic_media_stop_light"
-            android:alpha="0.87" />
-    </item>
-</selector>
diff --git a/packages/MediaComponents/res/drawable/mr_vol_type_audiotrack_dark.xml b/packages/MediaComponents/res/drawable/mr_vol_type_audiotrack_dark.xml
deleted file mode 100644
index 44f4fd6..0000000
--- a/packages/MediaComponents/res/drawable/mr_vol_type_audiotrack_dark.xml
+++ /dev/null
@@ -1,19 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/ic_audiotrack_dark" />
-</selector>
diff --git a/packages/MediaComponents/res/drawable/mr_vol_type_audiotrack_light.xml b/packages/MediaComponents/res/drawable/mr_vol_type_audiotrack_light.xml
deleted file mode 100644
index 5c9dbc0..0000000
--- a/packages/MediaComponents/res/drawable/mr_vol_type_audiotrack_light.xml
+++ /dev/null
@@ -1,23 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <bitmap
-            android:src="@drawable/ic_audiotrack_light"
-            android:alpha="0.87" />
-    </item>
-</selector>
diff --git a/packages/MediaComponents/res/interpolator/mr_fast_out_slow_in.xml b/packages/MediaComponents/res/interpolator/mr_fast_out_slow_in.xml
deleted file mode 100644
index 6b6a171..0000000
--- a/packages/MediaComponents/res/interpolator/mr_fast_out_slow_in.xml
+++ /dev/null
@@ -1,22 +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
-  -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:controlX1="0.4"
-    android:controlY1="0"
-    android:controlX2="0.2"
-    android:controlY2="1"/>
diff --git a/packages/MediaComponents/res/interpolator/mr_linear_out_slow_in.xml b/packages/MediaComponents/res/interpolator/mr_linear_out_slow_in.xml
deleted file mode 100644
index 20bf298..0000000
--- a/packages/MediaComponents/res/interpolator/mr_linear_out_slow_in.xml
+++ /dev/null
@@ -1,22 +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
-  -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:controlX1="0"
-    android:controlY1="0"
-    android:controlX2="0.2"
-    android:controlY2="1"/>
diff --git a/packages/MediaComponents/res/layout/embedded_music.xml b/packages/MediaComponents/res/layout/embedded_music.xml
deleted file mode 100644
index 3e4d365..0000000
--- a/packages/MediaComponents/res/layout/embedded_music.xml
+++ /dev/null
@@ -1,39 +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="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="0.25"/>
-
-    <ImageView
-        android:id="@+id/album"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="0.5"
-        android:scaleType="fitCenter"
-        android:src="@drawable/ic_default_album_image" />
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="0.25"/>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/embedded_settings_list_item.xml b/packages/MediaComponents/res/layout/embedded_settings_list_item.xml
deleted file mode 100644
index 1156dca..0000000
--- a/packages/MediaComponents/res/layout/embedded_settings_list_item.xml
+++ /dev/null
@@ -1,63 +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="wrap_content"
-    android:layout_height="@dimen/mcv2_embedded_settings_height"
-    android:orientation="horizontal"
-    android:background="@color/black_opacity_70">
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/mcv2_embedded_settings_height"
-        android:gravity="center"
-        android:orientation="horizontal">
-
-        <ImageView
-            android:id="@+id/icon"
-            android:layout_width="@dimen/mcv2_embedded_settings_icon_size"
-            android:layout_height="@dimen/mcv2_embedded_settings_icon_size"
-            android:layout_margin="8dp"
-            android:gravity="center" />
-    </LinearLayout>
-
-    <RelativeLayout
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/mcv2_embedded_settings_height"
-        android:gravity="center"
-        android:orientation="vertical">
-
-        <TextView
-            android:id="@+id/main_text"
-            android:layout_width="wrap_content"
-            android:layout_height="@dimen/mcv2_embedded_settings_text_height"
-            android:gravity="center"
-            android:paddingLeft="2dp"
-            android:textColor="@color/white"
-            android:textSize="@dimen/mcv2_embedded_settings_main_text_size"/>
-
-        <TextView
-            android:id="@+id/sub_text"
-            android:layout_width="wrap_content"
-            android:layout_height="@dimen/mcv2_embedded_settings_text_height"
-            android:layout_below="@id/main_text"
-            android:gravity="center"
-            android:paddingLeft="2dp"
-            android:textColor="@color/white_opacity_70"
-            android:textSize="@dimen/mcv2_embedded_settings_sub_text_size"/>
-    </RelativeLayout>
-</LinearLayout>
-
diff --git a/packages/MediaComponents/res/layout/embedded_sub_settings_list_item.xml b/packages/MediaComponents/res/layout/embedded_sub_settings_list_item.xml
deleted file mode 100644
index 5947a72..0000000
--- a/packages/MediaComponents/res/layout/embedded_sub_settings_list_item.xml
+++ /dev/null
@@ -1,53 +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="wrap_content"
-    android:layout_height="@dimen/mcv2_embedded_settings_height"
-    android:orientation="horizontal"
-    android:background="@color/black_opacity_70">
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/mcv2_embedded_settings_height"
-        android:gravity="center"
-        android:orientation="horizontal">
-
-        <ImageView
-            android:id="@+id/check"
-            android:layout_width="@dimen/mcv2_embedded_settings_icon_size"
-            android:layout_height="@dimen/mcv2_embedded_settings_icon_size"
-            android:layout_margin="8dp"
-            android:gravity="center"
-            android:src="@drawable/ic_check"/>
-    </LinearLayout>
-
-    <RelativeLayout
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/mcv2_embedded_settings_height"
-        android:gravity="center"
-        android:orientation="vertical">
-
-        <TextView
-            android:id="@+id/text"
-            android:layout_width="wrap_content"
-            android:layout_height="@dimen/mcv2_embedded_settings_text_height"
-            android:gravity="center"
-            android:paddingLeft="2dp"
-            android:textColor="@color/white"
-            android:textSize="@dimen/mcv2_embedded_settings_main_text_size"/>
-    </RelativeLayout>
-</LinearLayout>
diff --git a/packages/MediaComponents/res/layout/embedded_transport_controls.xml b/packages/MediaComponents/res/layout/embedded_transport_controls.xml
deleted file mode 100644
index a3a5957..0000000
--- a/packages/MediaComponents/res/layout/embedded_transport_controls.xml
+++ /dev/null
@@ -1,31 +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="match_parent"
-    android:layout_height="match_parent"
-    android:gravity="center"
-    android:orientation="horizontal"
-    android:paddingLeft="@dimen/mcv2_transport_controls_padding"
-    android:paddingRight="@dimen/mcv2_transport_controls_padding"
-    android:visibility="visible">
-
-    <ImageButton android:id="@+id/prev" style="@style/EmbeddedTransportControlsButton.Previous" />
-    <ImageButton android:id="@+id/rew" style="@style/EmbeddedTransportControlsButton.Rew" />
-    <ImageButton android:id="@+id/pause" style="@style/EmbeddedTransportControlsButton.Pause" />
-    <ImageButton android:id="@+id/ffwd" style="@style/EmbeddedTransportControlsButton.Ffwd" />
-    <ImageButton android:id="@+id/next" style="@style/EmbeddedTransportControlsButton.Next" />
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/full_landscape_music.xml b/packages/MediaComponents/res/layout/full_landscape_music.xml
deleted file mode 100644
index 8ce7058..0000000
--- a/packages/MediaComponents/res/layout/full_landscape_music.xml
+++ /dev/null
@@ -1,54 +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="match_parent"
-    android:layout_height="match_parent"
-    android:background="#B300FF00"
-    android:orientation="horizontal">
-
-    <LinearLayout
-        android:id="@+id/music_image"
-        style="@style/FullMusicLandscape.Image">
-
-        <ImageView
-            android:id="@+id/album"
-            android:layout_width="@dimen/mcv2_full_album_image_landscape_size"
-            android:layout_height="@dimen/mcv2_full_album_image_landscape_size"
-            android:src="@drawable/ic_default_album_image"/>
-    </LinearLayout>
-
-    <LinearLayout
-        android:id="@+id/music_text"
-        style="@style/FullMusicLandscape.Text">
-
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/mcv2_music_title_unknown_text"
-            android:textSize="20sp"
-            android:textStyle="bold"
-            android:textColor="#FFFFFF" />
-        <TextView
-            android:id="@+id/artist"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/mcv2_music_artist_unknown_text"
-            android:textSize="16sp"
-            android:textColor="#BBBBBB" />
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/full_portrait_music.xml b/packages/MediaComponents/res/layout/full_portrait_music.xml
deleted file mode 100644
index 75f1bb3..0000000
--- a/packages/MediaComponents/res/layout/full_portrait_music.xml
+++ /dev/null
@@ -1,53 +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="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <LinearLayout
-        android:id="@+id/music_image"
-        style="@style/FullMusicPortrait.Image">
-
-        <ImageView
-            android:id="@+id/album"
-            android:layout_width="@dimen/mcv2_full_album_image_portrait_size"
-            android:layout_height="@dimen/mcv2_full_album_image_portrait_size"
-            android:src="@drawable/ic_default_album_image"/>
-    </LinearLayout>
-
-    <LinearLayout
-        android:id="@+id/music_text"
-        style="@style/FullMusicPortrait.Text">
-
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="@dimen/mcv2_full_album_image_portrait_size"
-            android:layout_height="wrap_content"
-            android:text="@string/mcv2_music_title_unknown_text"
-            android:textSize="20sp"
-            android:textStyle="bold"
-            android:textColor="#FFFFFF" />
-        <TextView
-            android:id="@+id/artist"
-            android:layout_width="@dimen/mcv2_full_album_image_portrait_size"
-            android:layout_height="wrap_content"
-            android:text="@string/mcv2_music_artist_unknown_text"
-            android:textSize="16sp"
-            android:textColor="#BBBBBB" />
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/full_settings_list_item.xml b/packages/MediaComponents/res/layout/full_settings_list_item.xml
deleted file mode 100644
index f92ea5e..0000000
--- a/packages/MediaComponents/res/layout/full_settings_list_item.xml
+++ /dev/null
@@ -1,62 +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="wrap_content"
-    android:layout_height="@dimen/mcv2_full_settings_height"
-    android:orientation="horizontal"
-    android:background="@color/black_opacity_70">
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/mcv2_full_settings_height"
-        android:gravity="center"
-        android:orientation="horizontal">
-
-        <ImageView
-            android:id="@+id/icon"
-            android:layout_width="@dimen/mcv2_full_settings_icon_size"
-            android:layout_height="@dimen/mcv2_full_settings_icon_size"
-            android:layout_margin="8dp"
-            android:gravity="center"/>
-    </LinearLayout>
-
-    <RelativeLayout
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/mcv2_full_settings_height"
-        android:gravity="center"
-        android:orientation="vertical">
-
-        <TextView
-            android:id="@+id/main_text"
-            android:layout_width="wrap_content"
-            android:layout_height="@dimen/mcv2_full_settings_text_height"
-            android:paddingLeft="2dp"
-            android:gravity="center"
-            android:textColor="@color/white"
-            android:textSize="@dimen/mcv2_full_settings_main_text_size"/>
-
-        <TextView
-            android:id="@+id/sub_text"
-            android:layout_width="wrap_content"
-            android:layout_height="@dimen/mcv2_full_settings_text_height"
-            android:layout_below="@id/main_text"
-            android:gravity="center"
-            android:paddingLeft="2dp"
-            android:textColor="@color/white_opacity_70"
-            android:textSize="@dimen/mcv2_full_settings_sub_text_size"/>
-    </RelativeLayout>
-</LinearLayout>
diff --git a/packages/MediaComponents/res/layout/full_sub_settings_list_item.xml b/packages/MediaComponents/res/layout/full_sub_settings_list_item.xml
deleted file mode 100644
index 49128d0..0000000
--- a/packages/MediaComponents/res/layout/full_sub_settings_list_item.xml
+++ /dev/null
@@ -1,53 +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="wrap_content"
-    android:layout_height="@dimen/mcv2_full_settings_height"
-    android:orientation="horizontal"
-    android:background="@color/black_opacity_70">
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/mcv2_full_settings_height"
-        android:gravity="center"
-        android:orientation="horizontal">
-
-        <ImageView
-            android:id="@+id/check"
-            android:layout_width="@dimen/mcv2_full_settings_icon_size"
-            android:layout_height="@dimen/mcv2_full_settings_icon_size"
-            android:layout_margin="8dp"
-            android:gravity="center"
-            android:src="@drawable/ic_check"/>
-    </LinearLayout>
-
-    <RelativeLayout
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/mcv2_full_settings_height"
-        android:gravity="center"
-        android:orientation="vertical">
-
-        <TextView
-            android:id="@+id/text"
-            android:layout_width="wrap_content"
-            android:layout_height="@dimen/mcv2_full_settings_text_height"
-            android:gravity="center"
-            android:paddingLeft="2dp"
-            android:textColor="@color/white"
-            android:textSize="@dimen/mcv2_full_settings_main_text_size"/>
-    </RelativeLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/full_transport_controls.xml b/packages/MediaComponents/res/layout/full_transport_controls.xml
deleted file mode 100644
index 0914785..0000000
--- a/packages/MediaComponents/res/layout/full_transport_controls.xml
+++ /dev/null
@@ -1,31 +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="match_parent"
-    android:layout_height="match_parent"
-    android:gravity="center"
-    android:orientation="horizontal"
-    android:paddingLeft="@dimen/mcv2_transport_controls_padding"
-    android:paddingRight="@dimen/mcv2_transport_controls_padding"
-    android:visibility="visible">
-
-    <ImageButton android:id="@+id/prev" style="@style/FullTransportControlsButton.Previous" />
-    <ImageButton android:id="@+id/rew" style="@style/FullTransportControlsButton.Rew" />
-    <ImageButton android:id="@+id/pause" style="@style/FullTransportControlsButton.Pause" />
-    <ImageButton android:id="@+id/ffwd" style="@style/FullTransportControlsButton.Ffwd" />
-    <ImageButton android:id="@+id/next" style="@style/FullTransportControlsButton.Next" />
-</LinearLayout>
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
deleted file mode 100644
index 4658f04..0000000
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ /dev/null
@@ -1,265 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:layoutDirection="ltr">
-
-    <RelativeLayout
-        android:id="@+id/title_bar"
-        android:background="@layout/title_bar_gradient"
-        style="@style/TitleBar">
-
-        <LinearLayout
-            android:id="@+id/title_bar_left"
-            android:gravity="center"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentLeft="true"
-            android:layout_centerVertical="true"
-            android:orientation="horizontal">
-
-            <ImageButton
-                android:id="@+id/back"
-                android:clickable="true"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_alignParentStart="true"
-                android:layout_centerVertical="true"
-                android:paddingLeft="5dip"
-                android:visibility="visible"
-                style="@style/TitleBarButton.Back"/>
-
-            <TextView
-                android:id="@+id/title_text"
-                android:ellipsize="end"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_toRightOf="@id/back"
-                android:layout_centerVertical="true"
-                android:maxLines="1"
-                android:paddingLeft="5dip"
-                android:paddingRight="5dip"
-                android:textSize="15sp"
-                android:textColor="#FFFFFFFF"/>
-        </LinearLayout>
-
-        <LinearLayout
-            android:id="@+id/title_bar_right"
-            android:gravity="center"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentRight="true"
-            android:layout_centerVertical="true"
-            android:orientation="horizontal">
-
-            <LinearLayout
-                android:id="@+id/ad_external_link"
-                android:clickable="true"
-                android:gravity="center"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_alignParentRight="true"
-                android:layout_centerVertical="true"
-                android:paddingLeft="5dip"
-                android:paddingRight="10dip"
-                android:orientation="horizontal"
-                android:visibility="gone">
-
-                <TextView
-                    android:id="@+id/ad_text"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_centerVertical="true"
-                    android:paddingRight="5dip"
-                    android:text="@string/MediaControlView2_ad_text"
-                    android:textSize="10sp"
-                    android:textColor="#FFFFFFFF" />
-
-                <ImageButton
-                    android:id="@+id/ad_launch"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_centerVertical="true"
-                    style="@style/TitleBarButton.Launch" />
-            </LinearLayout>
-
-            <view class="com.android.support.mediarouter.app.MediaRouteButton"
-                android:id="@+id/cast"
-                android:layout_centerVertical="true"
-                android:visibility="gone"
-                android:contentDescription="@string/mr_button_content_description"
-                style="@style/TitleBarButton" />
-        </LinearLayout>
-
-    </RelativeLayout>
-
-    <LinearLayout
-        android:id="@+id/center_view"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:gravity="center"
-        android:orientation="vertical">
-    </LinearLayout>
-
-    <LinearLayout
-        android:id="@+id/minimal_extra_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="right">
-
-        <ImageButton
-            android:id="@+id/fullscreen"
-            android:gravity="right"
-            style="@style/BottomBarButton.FullScreen" />
-    </LinearLayout>
-
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/mcv2_custom_progress_thumb_size">
-
-        <SeekBar
-            android:id="@+id/progress"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/mcv2_custom_progress_thumb_size"
-            android:contentDescription="@string/mcv2_seek_bar_desc"
-            android:padding="0dp"
-            android:maxHeight="@dimen/mcv2_custom_progress_max_size"
-            android:minHeight="@dimen/mcv2_custom_progress_max_size"
-            android:elevation="10dp"/>
-
-        <View
-            android:id="@+id/progress_buffer"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/mcv2_buffer_view_height"
-            android:layout_alignParentBottom="true"
-            android:background="@color/bottom_bar_background"
-            android:elevation="0dp"/>
-    </RelativeLayout>
-
-    <RelativeLayout
-        android:id="@+id/bottom_bar"
-        android:layout_width="match_parent"
-        android:layout_height="44dp"
-        android:orientation="horizontal"
-        android:background="@color/bottom_bar_background">
-
-        <LinearLayout
-            android:id="@+id/bottom_bar_left"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_alignParentStart="true"
-            android:layout_centerVertical="true">
-
-            <TextView
-                android:id="@+id/ad_skip_time"
-                android:gravity="center"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:layout_marginLeft="4dp"
-                android:textSize="12sp"
-                android:textColor="#FFFFFF"
-                android:visibility="gone" />
-        </LinearLayout>
-
-        <LinearLayout
-            android:id="@+id/time"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_toRightOf="@id/bottom_bar_left"
-            android:paddingLeft="10dp"
-            android:paddingRight="10dp"
-            android:gravity="center" >
-
-            <TextView
-                android:id="@+id/time_current"
-                style="@style/TimeText.Current"/>
-            <TextView
-                android:id="@+id/time_interpunct"
-                style="@style/TimeText.Interpunct"/>
-            <TextView
-                android:id="@+id/time_end"
-                style="@style/TimeText.End"/>
-        </LinearLayout>
-
-        <LinearLayout
-            android:id="@+id/bottom_bar_right"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentEnd="true"
-            android:gravity="right">
-
-            <LinearLayout
-                android:id="@+id/basic_controls"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="center_vertical"
-                android:orientation="horizontal" >
-
-                <TextView
-                    android:id="@+id/ad_remaining"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:gravity="center"
-                    android:textSize="12sp"
-                    android:textColor="#FFFFFF"
-                    android:visibility="gone" />
-
-                <ImageButton
-                    android:id="@+id/mute"
-                    style="@style/BottomBarButton.Mute" />
-                <ImageButton
-                    android:id="@+id/subtitle"
-                    android:scaleType="fitCenter"
-                    android:visibility="gone"
-                    style="@style/BottomBarButton.CC" />
-                <ImageButton
-                    android:id="@+id/fullscreen"
-                    style="@style/BottomBarButton.FullScreen"/>
-                <ImageButton
-                    android:id="@+id/overflow_right"
-                    style="@style/BottomBarButton.OverflowRight"/>
-            </LinearLayout>
-
-            <LinearLayout
-                android:id="@+id/extra_controls"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:visibility="gone"
-                android:orientation="horizontal"
-                android:gravity="center_vertical">
-
-                <LinearLayout
-                    android:id="@+id/custom_buttons"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content" />
-
-                <ImageButton
-                    android:id="@+id/video_quality"
-                    style="@style/BottomBarButton.VideoQuality" />
-                <ImageButton
-                    android:id="@+id/settings"
-                    style="@style/BottomBarButton.Settings" />
-                <ImageButton
-                    android:id="@+id/overflow_left"
-                    style="@style/BottomBarButton.OverflowLeft"/>
-            </LinearLayout>
-        </LinearLayout>
-    </RelativeLayout>
-</LinearLayout>
diff --git a/packages/MediaComponents/res/layout/minimal_transport_controls.xml b/packages/MediaComponents/res/layout/minimal_transport_controls.xml
deleted file mode 100644
index 800c80b..0000000
--- a/packages/MediaComponents/res/layout/minimal_transport_controls.xml
+++ /dev/null
@@ -1,24 +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="match_parent"
-    android:layout_height="match_parent"
-    android:gravity="center"
-    android:orientation="horizontal">
-
-    <ImageButton android:id="@+id/pause" style="@style/MinimalTransportControlsButton" />
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/mr_chooser_dialog.xml b/packages/MediaComponents/res/layout/mr_chooser_dialog.xml
deleted file mode 100644
index ee89e16..0000000
--- a/packages/MediaComponents/res/layout/mr_chooser_dialog.xml
+++ /dev/null
@@ -1,55 +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:orientation="vertical">
-    <TextView android:id="@+id/mr_chooser_title"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:paddingLeft="24dp"
-              android:paddingRight="24dp"
-              android:paddingTop="24dp"
-              android:text="@string/mr_chooser_title"
-              android:singleLine="true"
-              android:ellipsize="end"
-              android:textAppearance="@style/TextAppearance.MediaRouter.Title" />
-    <ListView android:id="@+id/mr_chooser_list"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:divider="@android:color/transparent"
-              android:dividerHeight="0dp" />
-    <LinearLayout android:id="@android:id/empty"
-              android:layout_width="fill_parent"
-              android:layout_height="240dp"
-              android:orientation="vertical"
-              android:paddingTop="90dp"
-              android:paddingLeft="16dp"
-              android:paddingRight="16dp"
-              android:visibility="gone">
-        <TextView android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:layout_gravity="center"
-                  android:text="@string/mr_chooser_searching"
-                  android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText" />
-        <ProgressBar android:layout_width="150dp"
-                     android:layout_height="wrap_content"
-                     android:layout_gravity="center"
-                     android:indeterminate="true"
-                     style="?android:attr/progressBarStyleHorizontal" />
-    </LinearLayout>
-</LinearLayout>
diff --git a/packages/MediaComponents/res/layout/mr_chooser_list_item.xml b/packages/MediaComponents/res/layout/mr_chooser_list_item.xml
deleted file mode 100644
index 958879b..0000000
--- a/packages/MediaComponents/res/layout/mr_chooser_list_item.xml
+++ /dev/null
@@ -1,51 +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="56dp"
-              android:paddingLeft="24dp"
-              android:paddingRight="24dp"
-              android:orientation="horizontal"
-              android:gravity="center_vertical" >
-
-    <ImageView android:id="@+id/mr_chooser_route_icon"
-               android:layout_width="24dp"
-               android:layout_height="24dp"
-               android:layout_marginRight="24dp" />
-
-    <LinearLayout android:layout_width="fill_parent"
-                  android:layout_height="wrap_content"
-                  android:layout_marginBottom="1dp"
-                  android:orientation="vertical" >
-
-        <TextView android:id="@+id/mr_chooser_route_name"
-                  android:layout_width="fill_parent"
-                  android:layout_height="32dp"
-                  android:singleLine="true"
-                  android:ellipsize="marquee"
-                  android:textAppearance="@style/TextAppearance.MediaRouter.PrimaryText" />
-
-        <TextView android:id="@+id/mr_chooser_route_desc"
-                  android:layout_width="fill_parent"
-                  android:layout_height="24dp"
-                  android:singleLine="true"
-                  android:ellipsize="marquee"
-                  android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText" />
-    </LinearLayout>
-
-</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
deleted file mode 100644
index f6f7be5..0000000
--- a/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml
+++ /dev/null
@@ -1,206 +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.
--->
-
-<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">
-    <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="?android: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" >
-            <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" />
-            <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="?android:attr/selectableItemBackgroundBorderless" />
-        </LinearLayout>
-        <FrameLayout android:id="@+id/mr_custom_control"
-            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">
-            <ImageView android:id="@+id/mr_art"
-                android:layout_width="fill_parent"
-                android:layout_height="wrap_content"
-                android:adjustViewBounds="true"
-                android:scaleType="fitXY"
-                android:background="?android: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">
-                <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">
-                    <RelativeLayout
-                        android:id="@+id/mr_playback_control"
-                        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="?android: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="?android: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>
-        <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">
-            <androidx.appcompat.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="?android:attr/buttonBarNeutralButtonStyle"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"/>
-                <androidx.legacy.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="?android:attr/buttonBarPositiveButtonStyle"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"/>
-            </androidx.appcompat.widget.ButtonBarLayout>
-        </ScrollView>
-    </LinearLayout>
-</FrameLayout>
diff --git a/packages/MediaComponents/res/layout/mr_controller_volume_item.xml b/packages/MediaComponents/res/layout/mr_controller_volume_item.xml
deleted file mode 100644
index 12d85ae..0000000
--- a/packages/MediaComponents/res/layout/mr_controller_volume_item.xml
+++ /dev/null
@@ -1,52 +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">
-    <LinearLayout android:id="@+id/volume_item_container"
-                  android:layout_width="fill_parent"
-                  android:layout_height="@dimen/mr_controller_volume_group_list_item_height"
-                  android:paddingLeft="24dp"
-                  android:paddingRight="60dp"
-                  android:paddingBottom="8dp"
-                  android:orientation="vertical" >
-        <TextView android:id="@+id/mr_name"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText"
-                  android:singleLine="true" />
-        <LinearLayout android:layout_width="fill_parent"
-                      android:layout_height="wrap_content"
-                      android:orientation="horizontal"
-                      android:gravity="center_vertical">
-            <ImageView android:id="@+id/mr_volume_item_icon"
-                       android:layout_width="@dimen/mr_controller_volume_group_list_item_icon_size"
-                       android:layout_height="@dimen/mr_controller_volume_group_list_item_icon_size"
-                       android:layout_marginTop="8dp"
-                       android:layout_marginBottom="8dp"
-                       android:scaleType="fitCenter"
-                       android:src="?attr/mediaRouteAudioTrackDrawable" />
-            <androidx.mediarouter.app.MediaRouteVolumeSlider
-                android:id="@+id/mr_volume_slider"
-                android:layout_width="fill_parent"
-                android:layout_height="40dp"
-                android:minHeight="40dp"
-                android:maxHeight="40dp"
-                android:contentDescription="@string/mr_controller_volume_slider" />
-        </LinearLayout>
-    </LinearLayout>
-</LinearLayout>
diff --git a/packages/MediaComponents/res/layout/settings_list.xml b/packages/MediaComponents/res/layout/settings_list.xml
deleted file mode 100644
index ea30538..0000000
--- a/packages/MediaComponents/res/layout/settings_list.xml
+++ /dev/null
@@ -1,22 +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.
--->
-
-<ListView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/mcv2_embedded_settings_width"
-    android:layout_height="@dimen/mcv2_embedded_settings_height"
-    android:divider="@null"
-    android:dividerHeight="0dp">
-</ListView>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/title_bar_gradient.xml b/packages/MediaComponents/res/layout/title_bar_gradient.xml
deleted file mode 100644
index ab1fc6e..0000000
--- a/packages/MediaComponents/res/layout/title_bar_gradient.xml
+++ /dev/null
@@ -1,23 +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.
--->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <gradient
-        android:startColor="@color/title_bar_gradient_start"
-        android:endColor="@color/title_bar_gradient_end"
-        android:angle="-270" />
-</shape>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/values-af/strings.xml b/packages/MediaComponents/res/values-af/strings.xml
deleted file mode 100644
index 47230ad..0000000
--- a/packages/MediaComponents/res/values-af/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Stelsel"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Toestelle"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-knoppie"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Uitsaai-knoppie. Ontkoppel"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Uitsaai-knoppie. Koppel tans"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Uitsaai-knoppie. Gekoppel"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Saai uit na"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Vind tans toestelle"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ontkoppel"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Hou op uitsaai"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Maak toe"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Speel"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Laat wag"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Stop"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Vou uit"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Vou in"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumkunswerk"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volumeglyer"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Geen media is gekies nie"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Geen inligting beskikbaar nie"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Saai tans skerm uit"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-am/strings.xml b/packages/MediaComponents/res/values-am/strings.xml
deleted file mode 100644
index 39a1903..0000000
--- a/packages/MediaComponents/res/values-am/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"ስርዓት"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"መሣሪያዎች"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"የCast አዝራር"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast አዝራር። ግንኙነት ተቋርጧል"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast አዝራር በማገናኘት ላይ"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast አዝራር። ተገናኝቷል"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Cast አድርግ ወደ"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"መሣሪያዎችን በማግኘት ላይ"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ግንኙነት አቋርጥ"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Cast ማድረግ አቁም"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"ዝጋ"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"አጫውት"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"ለአፍታ አቁም"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"አቁም"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"አስፋ"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ሰብስብ"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"የአልበም ስነ-ጥበብ"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ተንሸራታች የድምፅ መቆጣጠሪያ"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ምንም ማህደረመረጃ አልተመረጠም"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ምንም መረጃ አይገኝም"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ማያ ገጽን በመውሰድ ላይ"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ar/strings.xml b/packages/MediaComponents/res/values-ar/strings.xml
deleted file mode 100644
index f8fb97d..0000000
--- a/packages/MediaComponents/res/values-ar/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"النظام"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"الأجهزة"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"زر الإرسال"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"زر الإرسال. تم قطع الاتصال"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"زر الإرسال. جارٍ الاتصال"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"زر الإرسال. تم الاتصال"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"إرسال إلى"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"جارٍ البحث عن أجهزة"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"قطع الاتصال"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"إيقاف الإرسال"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"إغلاق"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"تشغيل"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"إيقاف مؤقت"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"إيقاف"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"توسيع"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"تصغير"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"صورة الألبوم"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"شريط تمرير مستوى الصوت"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"لم يتم اختيار أي وسائط"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"لا تتوفر أي معلومات"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"جارٍ إرسال الشاشة"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-az/strings.xml b/packages/MediaComponents/res/values-az/strings.xml
deleted file mode 100644
index a3c60ab..0000000
--- a/packages/MediaComponents/res/values-az/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Cihazlar"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Yayım düyməsi"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Yayım düyməsi. Bağlantı kəsildi"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Yayım düyməsi. Qoşulur"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Yayım düyməsi. Qoşuldu"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Bura yayımlayın"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Cihazlar axtarılır"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Bağlantını kəsin"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Yayımı dayandırın"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Qapadın"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Oynadın"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Durdurun"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Dayandırın"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Genişləndirin"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Yığcamlaşdırın"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albom incəsənəti"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Səs hərmi diyircəyi"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Heç bir media seçilməyib"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Əlçatan məlumat yoxdur"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Ekran yayımlanır"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-b+sr+Latn/strings.xml b/packages/MediaComponents/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index e25bd6e..0000000
--- a/packages/MediaComponents/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Uređaji"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Dugme Prebaci"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Dugme Prebaci. Veza je prekinuta"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Dugme Prebaci. Povezuje se"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Dugme Prebaci. Povezan je"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Prebacuj na"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Pronalaženje uređaja"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini vezu"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Zaustavi prebacivanje"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Zatvori"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Pusti"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pauziraj"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Zaustavi"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Proširi"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skupi"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Omot albuma"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Klizač za jačinu zvuka"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nema izabranih medija"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nisu dostupne nikakve informacije"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Prebacuje se ekran"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-be/strings.xml b/packages/MediaComponents/res/values-be/strings.xml
deleted file mode 100644
index ac391c1..0000000
--- a/packages/MediaComponents/res/values-be/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Сістэма"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Прылады"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Кнопка трансляцыі"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Кнопка трансляцыі. Адключана"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Кнопка трансляцыі. Ідзе падключэнне"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Кнопка трансляцыі. Падключана"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Трансліраваць на"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Пошук прылад"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Адлучыць"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Спыніць трансляцыю"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Закрыць"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Прайграць"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Прыпыніць"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Спыніць"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Разгарнуць"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Згарнуць"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Вокладка альбома"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Паўзунок гучнасці"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Медыяфайл не выбраны"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Інфармацыя адсутнічае"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Экран трансляцыі"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-bg/strings.xml b/packages/MediaComponents/res/values-bg/strings.xml
deleted file mode 100644
index 76712d4..0000000
--- a/packages/MediaComponents/res/values-bg/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Система"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Устройства"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Бутон за предаване"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Бутон за предаване. Връзката е прекратена"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Бутон за предаване. Свързва се"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Бутон за предаване. Установена е връзка"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Предаване към"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Търсят се устройства"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Прекратяване на връзката"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Спиране на предаването"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Затваряне"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Пускане"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Поставяне на пауза"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Спиране"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Разгъване"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Свиване"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Обложка на албума"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Плъзгач за силата на звука"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Няма избрана мултимедия"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Няма налична информация"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Екранът се предава"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-bn/strings.xml b/packages/MediaComponents/res/values-bn/strings.xml
deleted file mode 100644
index 1bf5932..0000000
--- a/packages/MediaComponents/res/values-bn/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"সিস্টেম"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ডিভাইসগুলি"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"কাস্ট করার বোতাম"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"কাস্ট করার বোতাম৷ সংযোগ বিচ্ছিন্ন হয়েছে"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"কাস্ট করার বোতাম৷ সংযোগ করা হচ্ছে"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"কাস্ট করার বোতাম৷ সংযুক্ত হয়েছে"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"এতে কাস্ট করুন"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"ডিভাইসগুলিকে খোঁজা হচ্ছে"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"সংযোগ বিচ্ছিন্ন করুন"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"কাস্ট করা বন্ধ করুন"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"বন্ধ করুন"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"চালান"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"বিরাম দিন"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"থামান"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"বড় করুন"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"সঙ্কুচিত করুন"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"অ্যালবাম শৈলি"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ভলিউম স্লাইডার"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"কোনো মিডিয়া নির্বাচন করা হয়নি"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"কোনো তথ্য উপলব্ধ নেই"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"স্ক্রীন কাস্ট করা হচ্ছে"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-bs/strings.xml b/packages/MediaComponents/res/values-bs/strings.xml
deleted file mode 100644
index 711c742..0000000
--- a/packages/MediaComponents/res/values-bs/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Uređaji"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Dugme za emitiranje"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Dugme za emitiranje. Veza je prekinuta"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Dugme za emitiranje. Povezivanje"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Dugme za emitiranje. Povezano"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Emitiranje na"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Traženje uređaja"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini vezu"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Zaustavi prebacivanje"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Zatvori"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Reproduciraj"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pauziraj"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Zaustavi"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Proširi"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skupi"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Omot albuma"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Klizač za jačinu zvuka"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nijedan medij nije odabran"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nema dostupnih informacija"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Prebacuje se ekran"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ca/strings.xml b/packages/MediaComponents/res/values-ca/strings.xml
deleted file mode 100644
index bf85acf..0000000
--- a/packages/MediaComponents/res/values-ca/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositius"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Botó d\'emetre"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botó Emet. Desconnectat."</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botó Emet. S\'està connectant."</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botó Emet. Connectat."</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Emet a"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"S\'estan cercant dispositius"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconnecta"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Atura l\'emissió"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Tanca"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Reprodueix"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Posa en pausa"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Atura"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Desplega"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Replega"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Imatge de l\'àlbum"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Control lliscant de volum"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No s\'ha seleccionat cap fitxer multimèdia"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"No hi ha informació disponible"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Emissió de pantalla"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-cs/strings.xml b/packages/MediaComponents/res/values-cs/strings.xml
deleted file mode 100644
index 09a8920..0000000
--- a/packages/MediaComponents/res/values-cs/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Systém"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Zařízení"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Tlačítko odesílání"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Tlačítko odesílání. Odpojeno"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Tlačítko odesílání. Připojování"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Tlačítko odesílání. Připojeno"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Odesílat do"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Hledání zařízení"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Odpojit"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Zastavit odesílání"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Zavřít"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Přehrát"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pozastavit"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Zastavit"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Rozbalit"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Sbalit"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Obal alba"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Posuvník hlasitosti"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nebyla vybrána žádná média"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nejsou k dispozici žádné informace"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Odesílání obsahu obrazovky"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-da/strings.xml b/packages/MediaComponents/res/values-da/strings.xml
deleted file mode 100644
index 8e7a790..0000000
--- a/packages/MediaComponents/res/values-da/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Enheder"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-knap"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast-knap. Forbindelsen er afbrudt"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast-knap. Opretter forbindelse"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast-knap. Tilsluttet"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Cast til"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Finder enheder"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Afbryd"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Stop med at caste"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Luk"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Afspil"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Sæt på pause"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Stop"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Udvid"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skjul"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumgrafik"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Lydstyrkeskyder"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ingen medier er markeret"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Der er ingen tilgængelige oplysninger"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Skærmen castes"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-de/strings.xml b/packages/MediaComponents/res/values-de/strings.xml
deleted file mode 100644
index 26bf57c..0000000
--- a/packages/MediaComponents/res/values-de/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Geräte"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-Symbol"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Streaming-Schaltfläche. Nicht verbunden"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Streaming-Schaltfläche. Verbindung wird hergestellt"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Streaming-Schaltfläche. Verbunden"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Streamen auf"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Geräte werden gesucht."</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Verbindung trennen"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Streaming beenden"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Schließen"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Wiedergeben"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pausieren"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Beenden"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Maximieren"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Minimieren"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumcover"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Schieberegler für die Lautstärke"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Keine Medien ausgewählt"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Keine Informationen verfügbar"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Bildschirm wird gestreamt."</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-el/strings.xml b/packages/MediaComponents/res/values-el/strings.xml
deleted file mode 100644
index d82f69b..0000000
--- a/packages/MediaComponents/res/values-el/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Σύστημα"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Συσκευές"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Κουμπί Cast"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Κουμπί μετάδοσης. Αποσυνδέθηκε"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Κουμπί μετάδοση. Σύνδεση σε εξέλιξη"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Κουμπί μετάδοσης. Συνδέθηκε"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Μετάδοση σε"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Εύρεση συσκευών"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Αποσύνδεση"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Διακοπή μετάδοσης"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Κλείσιμο"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Αναπαραγωγή"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Παύση"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Διακοπή"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Ανάπτυξη"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Σύμπτυξη"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Εξώφυλλο άλμπουμ"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Ρυθμιστικό έντασης ήχου"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Δεν έχουν επιλεγεί μέσα"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Δεν υπάρχουν διαθέσιμες πληροφορίες"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Μετάδοση οθόνης"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-en-rAU/strings.xml b/packages/MediaComponents/res/values-en-rAU/strings.xml
deleted file mode 100644
index dd3f219..0000000
--- a/packages/MediaComponents/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Devices"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast button"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast button. Disconnected"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast button. Connecting"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast button. Connected"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Cast to"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Finding devices"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Disconnect"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Stop casting"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Close"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Play"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pause"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Stop"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expand"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Collapse"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Album art"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volume slider"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No media selected"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"No info available"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Casting screen"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-en-rCA/strings.xml b/packages/MediaComponents/res/values-en-rCA/strings.xml
deleted file mode 100644
index dd3f219..0000000
--- a/packages/MediaComponents/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Devices"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast button"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast button. Disconnected"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast button. Connecting"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast button. Connected"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Cast to"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Finding devices"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Disconnect"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Stop casting"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Close"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Play"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pause"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Stop"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expand"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Collapse"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Album art"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volume slider"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No media selected"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"No info available"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Casting screen"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-en-rGB/strings.xml b/packages/MediaComponents/res/values-en-rGB/strings.xml
deleted file mode 100644
index dd3f219..0000000
--- a/packages/MediaComponents/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Devices"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast button"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast button. Disconnected"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast button. Connecting"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast button. Connected"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Cast to"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Finding devices"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Disconnect"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Stop casting"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Close"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Play"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pause"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Stop"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expand"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Collapse"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Album art"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volume slider"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No media selected"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"No info available"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Casting screen"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-en-rIN/strings.xml b/packages/MediaComponents/res/values-en-rIN/strings.xml
deleted file mode 100644
index dd3f219..0000000
--- a/packages/MediaComponents/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Devices"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast button"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast button. Disconnected"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast button. Connecting"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast button. Connected"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Cast to"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Finding devices"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Disconnect"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Stop casting"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Close"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Play"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pause"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Stop"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expand"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Collapse"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Album art"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volume slider"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No media selected"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"No info available"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Casting screen"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-en-rXC/strings.xml b/packages/MediaComponents/res/values-en-rXC/strings.xml
deleted file mode 100644
index a87007e..0000000
--- a/packages/MediaComponents/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‏‎‎‎‏‎System‎‏‎‎‏‎"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‏‏‏‎Devices‎‏‎‎‏‎"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‏‎Cast button‎‏‎‎‏‎"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‎Cast button. Disconnected‎‏‎‎‏‎"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎Cast button. Connecting‎‏‎‎‏‎"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‏‏‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‎‏‎‏‎Cast button. Connected‎‏‎‎‏‎"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎‎‎‏‏‎‏‏‎‏‏‎‎Cast to‎‏‎‎‏‎"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎Finding devices‎‏‎‎‏‎"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‏‎‎‎Disconnect‎‏‎‎‏‎"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‏‏‎‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎Stop casting‎‏‎‎‏‎"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎Close‎‏‎‎‏‎"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‎Play‎‏‎‎‏‎"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎Pause‎‏‎‎‏‎"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎Stop‎‏‎‎‏‎"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‏‎Expand‎‏‎‎‏‎"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‎‎Collapse‎‏‎‎‏‎"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎Album art‎‏‎‎‏‎"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‏‎‏‎‏‎‏‏‎‏‎Volume slider‎‏‎‎‏‎"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎No media selected‎‏‎‎‏‎"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‎‏‏‎‎‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‏‎‎‎No info available‎‏‎‎‏‎"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‎‎‎‎‎‏‏‎Casting screen‎‏‎‎‏‎"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-es-rUS/strings.xml b/packages/MediaComponents/res/values-es-rUS/strings.xml
deleted file mode 100644
index 441ead1..0000000
--- a/packages/MediaComponents/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Botón para transmitir"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botón para transmitir (desconectado)"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botón para transmitir (conectando)"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botón para transmitir (conectado)"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Transmitir a"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Buscando dispositivos"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Detener la transmisión"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Cerrar"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Reproducir"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pausar"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Detener"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Mostrar"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Ocultar"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Imagen del álbum"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Control deslizante del volumen"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No se seleccionó ningún contenido multimedia"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Sin información disponible"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Transmitiendo pantalla"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-es/strings.xml b/packages/MediaComponents/res/values-es/strings.xml
deleted file mode 100644
index ff43008..0000000
--- a/packages/MediaComponents/res/values-es/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Botón de enviar"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botón de enviar. Desconectado"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botón de enviar. Conectando"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botón de enviar. Conectado"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Enviar a"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Buscando dispositivos"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Detener envío de contenido"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Cerrar"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Reproducir"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pausa"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Detener"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Mostrar"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Ocultar"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Portada del álbum"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Control deslizante de volumen"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No se ha seleccionado ningún medio"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"No hay información disponible"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Enviando pantalla"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-et/strings.xml b/packages/MediaComponents/res/values-et/strings.xml
deleted file mode 100644
index 453235b..0000000
--- a/packages/MediaComponents/res/values-et/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Süsteem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Seadmed"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Ülekandenupp"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Ülekandenupp. Ühendus on katkestatud"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Ülekandenupp. Ühendamine"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Ülekandenupp. Ühendatud"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Ülekandmine seadmesse"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Seadmete otsimine"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Katkesta ühendus"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Peata ülekandmine"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Sulgemine"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Esitamine"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Peatamine"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Peata"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Laiendamine"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Ahendamine"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumi kujundus"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Helitugevuse liugur"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Meediat pole valitud"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Teave puudub"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Ekraanikuva ülekandmine"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-eu/strings.xml b/packages/MediaComponents/res/values-eu/strings.xml
deleted file mode 100644
index dba19e4..0000000
--- a/packages/MediaComponents/res/values-eu/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Gailuak"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Igorri botoia"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Igortzeko botoia. Deskonektatuta"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Igortzeko botoia. Konektatzen"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Igortzeko botoia. Konektatuta"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Igorri hona:"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Gailuak bilatzen"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Deskonektatu"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Utzi igortzeari"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Itxi"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Erreproduzitu"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pausatu"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Gelditu"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Zabaldu"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Tolestu"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumaren azala"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Bolumenaren graduatzailea"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ez da hautatu multimedia-edukirik"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Ez dago informaziorik"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Pantaila igortzen"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-fa/strings.xml b/packages/MediaComponents/res/values-fa/strings.xml
deleted file mode 100644
index 4c6c779..0000000
--- a/packages/MediaComponents/res/values-fa/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"سیستم"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"دستگاه‌ها"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"دکمه ارسال محتوا"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"دکمه فرستادن. ارتباط قطع شد"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"دکمه فرستادن. درحال مرتبط‌سازی"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"دکمه فرستادن. مرتبط شد"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"ارسال محتوا به"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"پیدا کردن دستگاه‌ها"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"قطع ارتباط"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"توقف ارسال محتوا"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"بستن"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"پخش"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"مکث"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"توقف"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"بزرگ کردن"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"کوچک کردن"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"عکس روی جلد آلبوم"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"لغزنده میزان صدا"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"رسانه انتخاب نشده است"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"اطلاعات در دسترس نیست"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"درحال فرستادن صفحه"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-fi/strings.xml b/packages/MediaComponents/res/values-fi/strings.xml
deleted file mode 100644
index d683435..0000000
--- a/packages/MediaComponents/res/values-fi/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Järjestelmä"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Laitteet"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-painike"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast-painike. Yhteys katkaistu"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast-painike. Yhdistetään"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast-painike. Yhdistetty"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Suoratoiston kohde"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Etsitään laitteita"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Katkaise yhteys"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Lopeta suoratoisto"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Sulje"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Toista"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Keskeytä"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Pysäytä"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Laajenna"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Tiivistä"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumin kansikuva"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Äänenvoimakkuuden liukusäädin"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ei valittua mediaa."</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Tietoja ei ole saatavilla"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Suoratoistetaan näyttöä"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-fr-rCA/strings.xml b/packages/MediaComponents/res/values-fr-rCA/strings.xml
deleted file mode 100644
index c4f984b..0000000
--- a/packages/MediaComponents/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Système"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Appareils"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Bouton Diffuser"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Bouton Diffuser. Déconnecté"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Bouton Diffuser. Connexion en cours…"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Bouton Diffuser. Connecté"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Diffuser sur"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Recherche d\'appareils"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Se déconnecter"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Arrêter la diffusion"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Fermer"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Lire"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Interrompre"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Arrêter"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Développer"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Réduire"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Image de l\'album"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Curseur de réglage du volume"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Aucun média sélectionné"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Aucune information disponible"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Diffusion de l\'écran en cours"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-fr/strings.xml b/packages/MediaComponents/res/values-fr/strings.xml
deleted file mode 100644
index 12c312f..0000000
--- a/packages/MediaComponents/res/values-fr/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Système"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Appareils"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Icône Cast"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Icône Cast. Déconnecté"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Icône Cast. Connexion…"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Icône Cast. Connecté"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Caster sur"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Recherche d\'appareils…"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Déconnecter"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Arrêter la diffusion"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Fermer"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Lecture"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pause"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Arrêter"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Développer"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Réduire"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Image de l\'album"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Curseur de volume"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Aucun média sélectionné"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Aucune information disponible"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Diffusion de l\'écran en cours…"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-gl/strings.xml b/packages/MediaComponents/res/values-gl/strings.xml
deleted file mode 100644
index 1b2c354..0000000
--- a/packages/MediaComponents/res/values-gl/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Botón de emitir"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botón de emitir. Desconectado"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botón de emitir. Conectando"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botón de emitir. Conectado"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Emitir a"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Buscando dispositivos"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Deter emisión"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Pechar"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Reproduce"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pausa"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Deter"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Ampliar"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Contraer"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Portada do álbum"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Control desprazable do volume"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Non se seleccionaron recursos"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Non hai información dispoñible"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Emisión de pantalla"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-gu/strings.xml b/packages/MediaComponents/res/values-gu/strings.xml
deleted file mode 100644
index 2cd5f3f..0000000
--- a/packages/MediaComponents/res/values-gu/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"સિસ્ટમ"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ઉપકરણો"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"કાસ્ટ કરો બટન"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"કાસ્ટ કરો બટન. ડિસ્કનેક્ટ કર્યું"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"કાસ્ટ કરો બટન. કનેક્ટ થઈ રહ્યું છે"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"કાસ્ટ કરો બટન. કનેક્ટ થયું"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"આના પર કાસ્ટ કરો"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"ઉપકરણો શોધી રહ્યાં છીએ"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ડિસ્કનેક્ટ કરો"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"કાસ્ટ કરવાનું રોકો"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"બંધ કરો"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ચલાવો"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"થોભાવો"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"રોકો"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"વિસ્તૃત કરો"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"સંકુચિત કરો"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"આલ્બમ કલા"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"વૉલ્યુમ સ્લાઇડર"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"કોઈ મીડિયા પસંદ કરેલ નથી"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"કોઈ માહિતી ઉપલબ્ધ નથી"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"સ્ક્રીનને કાસ્ટ કરી રહ્યાં છે"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-hi/strings.xml b/packages/MediaComponents/res/values-hi/strings.xml
deleted file mode 100644
index 9552a59..0000000
--- a/packages/MediaComponents/res/values-hi/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"सिस्टम"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"डिवाइस"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"कास्ट करें बटन"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"कास्ट करें बटन. डिसकनेक्ट है"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"कास्ट करें बटन. कनेक्ट हो रहा है"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"कास्ट करें बटन. कनेक्ट है"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"इस पर कास्‍ट करें"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"डिवाइस ढूंढ रहा है"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"डिसकनेक्ट करें"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"कास्ट करना बंद करें"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"बंद करें"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"चलाएं"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"रोकें"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"बंद करें"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"विस्तार करें"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"छोटा करें"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"एल्बम आर्ट"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"वॉल्यूम स्लाइडर"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"कोई मीडिया चयनित नहीं है"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"कोई जानकारी मौजूद नहीं है"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"स्क्रीन कास्ट हो रही है"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-hr/strings.xml b/packages/MediaComponents/res/values-hr/strings.xml
deleted file mode 100644
index 3c43ee7..0000000
--- a/packages/MediaComponents/res/values-hr/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sustav"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Uređaji"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Gumb za emitiranje"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Gumb za emitiranje. Veza prekinuta"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Gumb za emitiranje. Povezivanje"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Gumb za emitiranje. Povezan"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Emitiranje na"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Traženje uređaja"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini vezu"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Zaustavi emitiranje"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Zatvaranje"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Reprodukcija"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pauziranje"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Zaustavi"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Proširivanje"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Sažimanje"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Naslovnica albuma"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Klizač za glasnoću"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nije odabran nijedan medij"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Informacije nisu dostupne"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Emitiranje zaslona"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-hu/strings.xml b/packages/MediaComponents/res/values-hu/strings.xml
deleted file mode 100644
index a36bdfe..0000000
--- a/packages/MediaComponents/res/values-hu/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Rendszer"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Eszközök"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Átküldés gomb"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Átküldés gomb. Kapcsolat bontva"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Átküldés gomb. Csatlakozás"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Átküldés gomb. Csatlakoztatva"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Átküldés ide"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Eszközök keresése"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Leválasztás"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Átküldés leállítása"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Bezárás"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Lejátszás"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Szüneteltetés"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Leállítás"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Kibontás"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Összecsukás"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Lemezborító"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Hangerőszabályzó"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nincs média kiválasztva"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nincs információ"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Képernyőtartalom átküldése"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-hy/strings.xml b/packages/MediaComponents/res/values-hy/strings.xml
deleted file mode 100644
index 8ec82b7..0000000
--- a/packages/MediaComponents/res/values-hy/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Համակարգ"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Սարքեր"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Հեռարձակման կոճակ"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Հեռարձակման կոճակ: Սարքն անջատված է"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Հեռարձակման կոճակ: Սարքը կապակցվում է"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Հեռարձակման կոճակ: Սարքը կապակցված է"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Ընտրեք սարքը"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Սարքերի որոնում"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Անջատել"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Դադարեցնել հեռարձակումը"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Փակել"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Նվագարկել"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Դադար"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Դադարեցնել"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Ընդարձակել"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Կոծկել"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Ալբոմի շապիկ"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Ձայնի ուժգնության կարգավորիչ"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Մեդիա ֆայլեր չեն ընտրվել"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Տեղեկությունները հասանելի չեն"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Էկրանը հեռարձակվում է"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-in/strings.xml b/packages/MediaComponents/res/values-in/strings.xml
deleted file mode 100644
index 6b2752e..0000000
--- a/packages/MediaComponents/res/values-in/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Perangkat"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Tombol Cast"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Tombol Cast. Terputus"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Tombol Cast. Menghubungkan"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Tombol Cast. Terhubung"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Transmisikan ke"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Mencari perangkat"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Putuskan sambungan"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Hentikan cast"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Tutup"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Putar"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Jeda"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Berhenti"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Luaskan"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Ciutkan"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Sampul album"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Bilah geser volume"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Tidak ada media yang dipilih"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Tidak ada info yang tersedia"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Transmisi layar"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-is/strings.xml b/packages/MediaComponents/res/values-is/strings.xml
deleted file mode 100644
index 6a35ea6..0000000
--- a/packages/MediaComponents/res/values-is/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Kerfi"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Tæki"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Útsendingarhnappur"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Útsendingarhnappur. Aftengt"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Útsendingarhnappur. Tengist"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Útsendingarhnappur. Tengt"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Senda út í"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Leitað að tækjum"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Aftengjast"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Stöðva útsendingu"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Loka"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Spila"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Hlé"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Stöðva"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Stækka"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Minnka"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Plötuumslag"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Hljóðstyrkssleði"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Enginn miðill valinn"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Engar upplýsingar í boði"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Skjár sendur út"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-it/strings.xml b/packages/MediaComponents/res/values-it/strings.xml
deleted file mode 100644
index 716e3ac..0000000
--- a/packages/MediaComponents/res/values-it/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivi"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Pulsante Trasmetti"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Pulsante Trasmetti. Disconnesso"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Pulsante Trasmetti. Connessione in corso"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Pulsante Trasmetti. Connesso"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Trasmetti a"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Ricerca di dispositivi in corso"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Scollega"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Interrompi trasmissione"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Chiudi"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Riproduci"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pausa"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Interrompi"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Espandi"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Comprimi"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Copertina"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Dispositivo di scorrimento del volume"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nessun contenuto multimediale selezionato"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nessuna informazione disponibile"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Trasmissione dello schermo in corso"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-iw/strings.xml b/packages/MediaComponents/res/values-iw/strings.xml
deleted file mode 100644
index 252b0ce..0000000
--- a/packages/MediaComponents/res/values-iw/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"מערכת"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"מכשירים"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"‏לחצן הפעלת Cast"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"‏לחצן הפעלת Cast. מנותק"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"‏לחצן הפעלת Cast. מתחבר"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"‏לחצן הפעלת Cast. מחובר"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"העברה אל"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"מחפש מכשירים"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"נתק"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"הפסק את ההעברה"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"סגור"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"הפעל"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"השהה"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"הפסק"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"הרחב"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"כווץ"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"עטיפת אלבום"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"מחוון עוצמה"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"לא נבחרה מדיה"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"אין מידע זמין"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"העברת מסך מתבצעת"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ja/strings.xml b/packages/MediaComponents/res/values-ja/strings.xml
deleted file mode 100644
index a149727..0000000
--- a/packages/MediaComponents/res/values-ja/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"システム"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"端末"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"キャストアイコン"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"キャスト アイコン。接続解除済み"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"キャスト アイコン。接続中"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"キャスト アイコン。接続済み"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"キャストするデバイス"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"端末を検索しています"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"接続を解除"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"キャストを停止"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"閉じる"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"再生"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"一時停止"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"停止"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"展開"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"折りたたむ"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"アルバムアート"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"音量スライダー"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"メディアが選択されていません"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"情報がありません"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"画面をキャストしています"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ka/strings.xml b/packages/MediaComponents/res/values-ka/strings.xml
deleted file mode 100644
index 3da081a..0000000
--- a/packages/MediaComponents/res/values-ka/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"სისტემა"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"მოწყობილობები"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ტრანსლირების ღილაკი"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ტრანსლირების ღილაკი. გათიშული"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ტრანსლირების ღილაკი. მიმდინარეობს დაკავშირება"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ტრანსლირების ღილაკი. დაკავშირებული"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"ტრანსლირება:"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"მოწყობილობების მოძიება..."</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"კავშირის გაწყვეტა"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ტრანსლირების შეწყვეტა"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"დახურვა"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"დაკვრა"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"პაუზა"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"შეწყვეტა"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"გაშლა"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ჩაკეცვა"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ალბომის გარეკანი"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ხმის სლაიდერი"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"მედია არჩეული არ არის"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ინფორმაცია არ არის ხელმისაწვდომი"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"მიმდინარეობს ეკრანის გადაცემა"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-kk/strings.xml b/packages/MediaComponents/res/values-kk/strings.xml
deleted file mode 100644
index 94dcbb3..0000000
--- a/packages/MediaComponents/res/values-kk/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Жүйе"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Құрылғылар"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Трансляциялау түймесі"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"\"Трансляциялау\" түймесі. Ажыратулы"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"\"Трансляциялау\" түймесі. Қосылуда"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"\"Трансляциялау\" түймесі. Қосылды"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Келесіге трансляциялау"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Құрылғыларды табу"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ажырату"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Трансляциялауды тоқтату"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Жабу"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Ойнату"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Кідірту"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Тоқтату"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Жаю"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Жию"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Альбом шебері"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Дыбыс деңгейінің жүгірткісі"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ешбір тасушы таңдалмаған"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Қол жетімді ақпарат жоқ"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Экранды трансляциялау"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-km/strings.xml b/packages/MediaComponents/res/values-km/strings.xml
deleted file mode 100644
index e44780e..0000000
--- a/packages/MediaComponents/res/values-km/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"ប្រព័ន្ធ"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ឧបករណ៍"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ប៊ូតុងខាស"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ខាសប៊ូតុង៖ បានកាត់ផ្តាច់"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ខាសប៊ូតុង៖ កំពុងភ្ជាប់"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ខាសប៊ូតុង៖ បានភ្ជាប់ហើយ"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"បញ្ជូនទៅ"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"កំពុងស្វែងរកឧបករណ៍"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ផ្ដាច់"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ឈប់ភ្ជាប់"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"បិទ"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ចាក់"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"ផ្អាក"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"ឈប់"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ពង្រីក"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"បង្រួម"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ស្នាដៃសិល្បៈអាល់ប៊ុម"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"របារកម្រិតសំឡេង"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"គ្មានការជ្រើសមេឌៀទេ"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"មិនមានព័ត៌មានទេ"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"កំពុងខាសអេក្រង់"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-kn/strings.xml b/packages/MediaComponents/res/values-kn/strings.xml
deleted file mode 100644
index 4237fdd..0000000
--- a/packages/MediaComponents/res/values-kn/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"ಸಿಸ್ಟಂ"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ಸಾಧನಗಳು"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ಬಿತ್ತರಿಸು ಬಟನ್‌"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ಬಿತ್ತರಿಸು ಬಟನ್‌. ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ಬಿತ್ತರಿಸು ಬಟನ್‌. ಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ಬಿತ್ತರಿಸು ಬಟನ್‌. ಸಂಪರ್ಕಿತಗೊಂಡಿದೆ"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"ಇದಕ್ಕೆ ಬಿತ್ತರಿಸಿ"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"ಸಾಧನಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸು"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ಬಿತ್ತರಿಸುವಿಕೆ ನಿಲ್ಲಿಸಿ"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"ಮುಚ್ಚು"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ಪ್ಲೇ ಮಾಡಿ"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"ವಿರಾಮ"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"ನಿಲ್ಲಿಸಿ"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ವಿಸ್ತರಿಸು"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ಸಂಕುಚಿಸು"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ಆಲ್ಬಮ್ ಕಲೆ"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ವಾಲ್ಯೂಮ್ ಸ್ಲೈಡರ್"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ಯಾವುದೇ ಮಾಧ್ಯಮ ಆಯ್ಕೆಮಾಡಲಾಗಿಲ್ಲ"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ಯಾವುದೇ ಮಾಹಿತಿ ಲಭ್ಯವಿಲ್ಲ"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ಪರದೆಯನ್ನು ಬಿತ್ತರಿಸಲಾಗುತ್ತಿದೆ"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ko/strings.xml b/packages/MediaComponents/res/values-ko/strings.xml
deleted file mode 100644
index be893a9..0000000
--- a/packages/MediaComponents/res/values-ko/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"시스템"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"기기"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"전송 버튼"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"전송 버튼. 연결 해제됨"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"전송 버튼. 연결 중"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"전송 버튼. 연결됨"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"전송할 기기"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"기기를 찾는 중"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"연결 해제"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"전송 중지"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"닫기"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"재생"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"일시중지"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"중지"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"펼치기"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"접기"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"앨범아트"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"볼륨 슬라이더"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"선택한 미디어 없음"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"정보가 없습니다."</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"화면 전송 중"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ky/strings.xml b/packages/MediaComponents/res/values-ky/strings.xml
deleted file mode 100644
index 57813af..0000000
--- a/packages/MediaComponents/res/values-ky/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Тутум"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Түзмөктөр"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Тышкы экранга чыгаруу баскычы"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Тышкы экранга чыгаруу баскычы. Түзмөк ажырап турат."</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Тышкы экранга чыгаруу баскычы. Түзмөк туташууда"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Тышкы экранга чыгаруу баскычы. Түзмөк туташып турат"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Төмөнкүгө чыгаруу"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Түзмөктөр изделүүдө"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ажыратуу"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Тышк экранга чыгарну токтотуу"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Жабуу"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Ойнотуу"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Тындыруу"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Токтотуу"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Жайып көрсөтүү"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Жыйыштыруу"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Альбом мукабасы"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Үндү катуулатуучу сыдырма"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Бир да медиа файл тандалган жок"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Эч маалымат жок"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Тышкы экранга чыгарылууда"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-land/dimens.xml b/packages/MediaComponents/res/values-land/dimens.xml
deleted file mode 100644
index 29f1e1d..0000000
--- a/packages/MediaComponents/res/values-land/dimens.xml
+++ /dev/null
@@ -1,26 +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.
--->
-
-<resources>
-    <!-- MediaRouteController's volume group list -->
-    <eat-comment />
-    <!-- Maximum height of volume group list. -->
-    <dimen name="mr_controller_volume_group_list_max_height">132dp</dimen>
-    <!-- Height of volume group item. -->
-    <dimen name="mr_controller_volume_group_list_item_height">61dp</dimen>
-    <!-- Size of an item's icon. -->
-    <dimen name="mr_controller_volume_group_list_item_icon_size">18dp</dimen>
-</resources>
diff --git a/packages/MediaComponents/res/values-lo/strings.xml b/packages/MediaComponents/res/values-lo/strings.xml
deleted file mode 100644
index 91737db..0000000
--- a/packages/MediaComponents/res/values-lo/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"ລະບົບ"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ອຸປະກອນ"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ປຸ່ມ​ຄາ​ສ​ທ໌"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ປຸ່ມສົ່ງສັນຍານ. ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ປຸ່ມສົ່ງສັນຍານ. ກຳລັງເຊື່ອມຕໍ່"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ປຸ່ມສົ່ງສັນຍານ. ເຊື່ອມຕໍ່ແລ້ວ"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"ສົ່ງສັນຍານຫາ"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"ກຳລັງ​ຊອກ​ຫາ​ອຸ​ປະ​ກອນ"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ຕັດການເຊື່ອມຕໍ່"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ຢຸດການສົ່ງສັນຍານ"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"ປິດ"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ຫຼິ້ນ"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"ຢຸດຊົ່ວຄາວ"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"ຢຸດ"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ຂະຫຍາຍ"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ຫຍໍ້ລົງ"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ໜ້າປົກອະລະບໍ້າ"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ຕົວປັບລະດັບສຽງ"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ບໍ່​ໄດ້​ເລືອກ​ມີ​ເດຍ​ໃດ​ໄວ້"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ບໍ່​ມີ​ຂໍ້​ມູນ"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ການສົ່ງພາບໜ້າຈໍ"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-lt/strings.xml b/packages/MediaComponents/res/values-lt/strings.xml
deleted file mode 100644
index ff036d1..0000000
--- a/packages/MediaComponents/res/values-lt/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Įrenginiai"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Perdavimo mygtukas"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Perdavimo mygtukas. Atsijungta"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Perdavimo mygtukas. Prisijungiama"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Perdavimo mygtukas. Prisijungta"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Perduoti į"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Randami įrenginiai"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Atjungti"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Sustabdyti perdavimą"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Uždaryti"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Leisti"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pristabdyti"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Sustabdyti"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Išskleisti"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Sutraukti"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumo viršelis"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Garsumo šliaužiklis"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nepasirinkta jokia medija"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Informacija nepasiekiama"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Perduodamas ekranas"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-lv/strings.xml b/packages/MediaComponents/res/values-lv/strings.xml
deleted file mode 100644
index 454063e..0000000
--- a/packages/MediaComponents/res/values-lv/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistēma"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Ierīces"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Apraides poga"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Apraides poga. Savienojums pārtraukts"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Apraides poga. Notiek savienojuma izveide"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Apraides poga. Savienojums izveidots"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Apraidīšana uz ierīci"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Notiek ierīču meklēšana"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Atvienot"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Apturēt apraidi"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Aizvērt"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Atskaņot"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Apturēt"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Apturēt"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Izvērst"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Sakļaut"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albuma vāciņš"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Skaļuma slīdnis"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nav atlasīti multivides faili"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nav pieejama informācija"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Notiek ekrāna apraide"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-mk/strings.xml b/packages/MediaComponents/res/values-mk/strings.xml
deleted file mode 100644
index 12dee36..0000000
--- a/packages/MediaComponents/res/values-mk/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Систем"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Уреди"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Копчето за Cast"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Копче за Cast. Исклучено"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Копче за Cast. Се поврзува"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Копче за Cast. Поврзано"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Емитувај на"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Се бараат уреди"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Исклучи"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Сопри го емитувањето"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Затвори"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Репродуцирај"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Паузирај"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Сопри"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Прошири"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Собери"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Корица на албум"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Лизгач за јачина на звук"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Не се избрани медиуми"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Нема достапни информации"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Екранот се емитува"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ml/strings.xml b/packages/MediaComponents/res/values-ml/strings.xml
deleted file mode 100644
index 2d914b9..0000000
--- a/packages/MediaComponents/res/values-ml/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"സിസ്റ്റം"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ഉപകരണങ്ങൾ"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ടാപ്പുചെയ്യുക"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"കാസ്റ്റ് ബട്ടൺ. വിച്ഛേദിച്ചു"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"കാസ്റ്റ് ബട്ടൺ. കണക്‌റ്റുചെയ്യുന്നു"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"കാസ്റ്റ് ബട്ടൺ. കണക്റ്റുചെയ്തു"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"ഇതിലേക്ക് കാസ്റ്റുചെയ്യുക"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"ഉപകരണങ്ങൾ കണ്ടെത്തുന്നു"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"വിച്ഛേദിക്കുക"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"കാസ്റ്റുചെയ്യൽ നിർത്തുക"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"അവസാനിപ്പിക്കുക"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"പ്ലേ ചെയ്യുക"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"തൽക്കാലം നിർത്തൂ"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"നിര്‍ത്തുക"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"വികസിപ്പിക്കുക"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ചുരുക്കുക"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ആൽബം ആർട്ട്"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"വോളിയം സ്ലൈഡർ"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"മീഡിയയൊന്നും തിരഞ്ഞെടുത്തിട്ടില്ല"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"വിവരങ്ങളൊന്നും ലഭ്യമല്ല"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"സ്‌ക്രീൻ കാസ്റ്റുചെയ്യുന്നു"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-mn/strings.xml b/packages/MediaComponents/res/values-mn/strings.xml
deleted file mode 100644
index ef87c92..0000000
--- a/packages/MediaComponents/res/values-mn/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Систем"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Төхөөрөмжүүд"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Дамжуулах товчлуур"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Дамжуулах товчлуур. Салсан"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Дамжуулах товчлуур. Холбож байна"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Дамжуулах товчлуур. Холбогдсон"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Дамжуулах"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Төхөөрөмж хайж байна"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Салгах"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Дамжуулахыг зогсоох"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Хаах"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Тоглуулах"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Түр зогсоох"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Зогсоох"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Дэлгэх"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Хураах"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Цомгийн зураг"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Дууны түвшин тааруулагч"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ямар ч медиа сонгоогүй"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Мэдээлэл байхгүй байна"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Дэлгэцийг дамжуулж байна"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-mr/strings.xml b/packages/MediaComponents/res/values-mr/strings.xml
deleted file mode 100644
index 2ffbebb..0000000
--- a/packages/MediaComponents/res/values-mr/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"सिस्टम"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"डिव्हाइसेस"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"कास्ट बटण"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"कास्ट बटण. डिस्कनेक्ट केले"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"कास्ट बटण. कनेक्ट करत आहे"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"कास्ट बटण. कनेक्ट केले"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"यावर कास्ट करा"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"डिव्हाइसेस शोधत आहे"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"डिस्‍कनेक्‍ट करा"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"कास्ट करणे थांबवा"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"बंद करा"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"प्ले करा"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"विराम"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"थांबा"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"विस्तृत करा"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"संकुचित करा"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"अल्बम कला"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"व्हॉल्यूम स्लायडर"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"मीडिया निवडला नाही"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"कोणतीही माहिती उपलब्ध नाही"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"स्क्रीन कास्‍ट करत आहे"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ms/strings.xml b/packages/MediaComponents/res/values-ms/strings.xml
deleted file mode 100644
index 085e480..0000000
--- a/packages/MediaComponents/res/values-ms/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Peranti"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Butang Hantar"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Butang hantar. Sambungan diputuskan"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Butang hantar. Menyambung"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Butang hantar. Disambungkan"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Hantar ke"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Mencari peranti"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Putuskan sambungan"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Berhenti menghantar"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Tutup"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Main"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Jeda"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Berhenti"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Kembangkan"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Runtuhkan"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Seni album"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Peluncur kelantangan"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Tiada media dipilih"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Maklumat tidak tersedia"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Menghantar skrin"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-my/strings.xml b/packages/MediaComponents/res/values-my/strings.xml
deleted file mode 100644
index 083d805..0000000
--- a/packages/MediaComponents/res/values-my/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"စနစ်"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"စက်ပစ္စည်းများ"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ကာစ်တ်လုပ်ရန် ခလုတ်"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ကာစ်ခလုတ်။ ချိတ်ဆက်မထားပါ"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ကာစ်ခလုတ်။ ချိတ်ဆက်နေသည်"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ကာစ်ခလုတ်။ ချိတ်ဆက်ထားသည်"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"ကာစ်လုပ်ရန် စက်"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"စက်ပစ္စည်းများ ရှာဖွေခြင်း"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ဆက်သွယ်မှု ဖြတ်ရန်"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ကာစ်လုပ်ခြင်း ရပ်ရန်"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"ပိတ်ရန်"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ဖွင့်ရန်"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"ခဏရပ်ရန်"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"ရပ်ရန်"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ဖြန့်ချရန်၃"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ခေါက်သိမ်းရန်..."</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"အယ်လ်ဘမ်ပုံ"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"အသံအတိုးအကျယ်ချိန်သည့် ဆလိုက်ဒါ"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"မည်သည့်မီဒီမှ မရွေးချယ်ထားပါ"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"အချက်အလက် မရရှိနိုင်ပါ"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"တည်းဖြတ်ရေး မျက်နှာပြင်"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-nb/strings.xml b/packages/MediaComponents/res/values-nb/strings.xml
deleted file mode 100644
index 4f764c9..0000000
--- a/packages/MediaComponents/res/values-nb/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Enheter"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-ikonet"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast-knappen. Frakoblet"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast-knappen. Kobler til"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast-knappen. Tilkoblet"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Cast til"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Søker etter enheter"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Koble fra"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Stopp castingen"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Lukk"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Spill av"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Sett på pause"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Stopp"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Utvid"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skjul"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumgrafikk"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Glidebryter for volum"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Du har ikke valgt noen medier"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Ingen informasjon er tilgjengelig"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Caster skjermen"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ne/strings.xml b/packages/MediaComponents/res/values-ne/strings.xml
deleted file mode 100644
index d6c2e1a..0000000
--- a/packages/MediaComponents/res/values-ne/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"प्रणाली"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"उपकरणहरू"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast बटन"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast बटन। जडान विच्छेद भयो"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast बटन। जडान हुँदै"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast बटन। जडान भयो"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"यसमा Cast गर्नुहोस्"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"यन्त्रहरू पत्ता लगाउँदै"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"विच्छेद गर्नुहोस्"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"casting रोक्नुहोस्"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"बन्द गर्नुहोस्"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"बजाउनुहोस्"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"रोक्नुहोस्"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"रोक्नुहोस्"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"विस्तार गर्नुहोस्"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"संक्षिप्त पार्नुहोस्"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"एल्बम आर्ट"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"भोल्युमको स्लाइडर"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"कुनै मिडिया चयन भएको छैन"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"जानकारी उपलब्ध छैन"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"स्क्रिन cast गर्दै"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-nl/strings.xml b/packages/MediaComponents/res/values-nl/strings.xml
deleted file mode 100644
index 05df62d..0000000
--- a/packages/MediaComponents/res/values-nl/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Systeem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Apparaten"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-icoon"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast-icoon. Verbinding verbroken"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast-icoon. Verbinding maken"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast-icoon. Verbonden"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Casten naar"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Apparaten zoeken"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Loskoppelen"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Casten stoppen"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Sluiten"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Afspelen"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Onderbreken"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Stoppen"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Uitvouwen"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Samenvouwen"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumhoes"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volumeschuifregelaar"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Geen media geselecteerd"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Geen informatie beschikbaar"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Scherm casten"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-pa/strings.xml b/packages/MediaComponents/res/values-pa/strings.xml
deleted file mode 100644
index 1b5df71..0000000
--- a/packages/MediaComponents/res/values-pa/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"ਸਿਸਟਮ"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ਡਿਵਾਈਸਾਂ"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ਕਾਸਟ ਬਟਨ"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ਕਾਸਟ ਬਟਨ। ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ਕਾਸਟ ਬਟਨ। ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ਕਾਸਟ ਬਟਨ। ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"ਏਥੇ ਕਾਸਟ ਕਰੋ"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"ਡੀਵਾਈਸਾਂ ਨੂੰ ਲੱਭਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰੋ"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"ਬੰਦ ਕਰੋ"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ਪਲੇ ਕਰੋ"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"ਰੋਕੋ"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"ਬੰਦ ਕਰੋ"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ਵਿਸਤਾਰ ਕਰੋ"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ਬੰਦ ਕਰੋ"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ਐਲਬਮ ਆਰਟ"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ਵੌਲਯੂਮ ਸਲਾਈਡਰ"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ਕੋਈ ਵੀ ਮੀਡੀਆ ਨਹੀਂ ਚੁਣਿਆ ਗਿਆ"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ਕੋਈ ਜਾਣਕਾਰੀ ਉਪਲਬਧ ਨਹੀਂ"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ਸਕ੍ਰੀਨ ਜੋੜ ਰਿਹਾ ਹੈ"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-pl/strings.xml b/packages/MediaComponents/res/values-pl/strings.xml
deleted file mode 100644
index c792a6d..0000000
--- a/packages/MediaComponents/res/values-pl/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Urządzenia"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Przycisk Cast"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Przycisk Prześlij ekran. Rozłączono"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Przycisk Prześlij ekran. Łączę"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Przycisk Prześlij ekran. Połączono"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Przesyłaj na"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Znajdowanie urządzeń"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Odłącz"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Zatrzymaj przesyłanie"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Zamknij"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Odtwórz"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Wstrzymaj"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Zatrzymaj"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Rozwiń"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Zwiń"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Okładka albumu"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Suwak głośności"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nie wybrano multimediów"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Brak informacji"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Przesyłam ekran"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-pt-rBR/strings.xml b/packages/MediaComponents/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 43c619d..0000000
--- a/packages/MediaComponents/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Botão Transmitir"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botão \"Transmitir\". Desconectado"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botão \"Transmitir\". Conectando"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botão \"Transmitir\". Conectado"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Transmitir para"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Localizando dispositivos"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Interromper transmissão"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Fechar"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Reproduzir"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pausar"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Parar"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expandir"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Recolher"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Arte do álbum"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Controle deslizante de volume"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nenhuma mídia selecionada"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nenhuma informação disponível"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Transmitindo a tela"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-pt-rPT/strings.xml b/packages/MediaComponents/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 3f0a61d..0000000
--- a/packages/MediaComponents/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Botão Transmitir"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botão Transmitir. Desligado"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botão Transmitir. A ligar..."</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botão Transmitir. Ligado"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Transmitir para"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"A localizar dispositivos"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desassociar"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Parar a transmissão"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Fechar"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Reproduzir"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Interromper"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Parar"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expandir"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Reduzir"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Imagem do álbum"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Controlo de deslize do volume"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nenhum suporte multimédia selecionado"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nenhuma informação disponível"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"A transmitir o ecrã"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-pt/strings.xml b/packages/MediaComponents/res/values-pt/strings.xml
deleted file mode 100644
index 43c619d..0000000
--- a/packages/MediaComponents/res/values-pt/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Botão Transmitir"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botão \"Transmitir\". Desconectado"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botão \"Transmitir\". Conectando"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botão \"Transmitir\". Conectado"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Transmitir para"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Localizando dispositivos"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Interromper transmissão"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Fechar"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Reproduzir"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pausar"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Parar"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expandir"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Recolher"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Arte do álbum"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Controle deslizante de volume"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nenhuma mídia selecionada"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nenhuma informação disponível"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Transmitindo a tela"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ro/strings.xml b/packages/MediaComponents/res/values-ro/strings.xml
deleted file mode 100644
index 6ebb2f6..0000000
--- a/packages/MediaComponents/res/values-ro/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispozitive"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Butonul de proiecție"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Butonul de proiecție. Deconectat"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Butonul de proiecție. Se conectează"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Butonul de proiecție. Conectat"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Proiectați pe"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Se caută dispozitive"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Deconectați-vă"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Nu mai proiectați"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Închideți"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Redați"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Întrerupeți"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Opriți"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Extindeți"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Restrângeți"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Grafica albumului"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Glisor pentru volum"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Niciun fișier media selectat"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nu sunt disponibile informații"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Se proiectează ecranul"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ru/strings.xml b/packages/MediaComponents/res/values-ru/strings.xml
deleted file mode 100644
index 7c462d2..0000000
--- a/packages/MediaComponents/res/values-ru/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Система"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Устройства"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Кнопка трансляции"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Кнопка трансляции. Устройство отключено."</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Кнопка трансляции. Устройство подключается."</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Кнопка трансляции. Устройство подключено."</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Выберите устройство"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Поиск устройств…"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Отключить"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Прекратить трансляцию"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Закрыть"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Воспроизвести"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Приостановить"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Остановить"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Развернуть"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Свернуть"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Обложка"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Регулятор громкости"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Медиафайл не выбран"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Данных нет"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Подключение к удаленному монитору"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-si/strings.xml b/packages/MediaComponents/res/values-si/strings.xml
deleted file mode 100644
index a55ce50..0000000
--- a/packages/MediaComponents/res/values-si/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"පද්ධතිය"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"උපාංග"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"විකාශ බොත්තම"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"විකාශ බොත්තම. විසන්ධි කරන ලදී"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"විකාශ බොත්තම සම්බන්ධ කරමින්"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"විකාශ බොත්තම සම්බන්ධ කරන ලදී"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"විකාශය"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"උපාංග සෙවීම"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"විසන්ධි කරන්න"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"විකාශ කිරීම නතර කරන්න"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"වසන්න"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ධාවනය කරන්න"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"විරාම ගන්වන්න"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"නතර කරන්න"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"දිග හරින්න"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"හකුළන්න"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ඇල්බම කලාව"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"හඬ පරිමා ස්ලයිඩරය"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"මාධ්‍යය තෝරා නැත"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ලබා ගත හැකි තොරතුරු නොමැත"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"විකාශ තිරය"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-sk/strings.xml b/packages/MediaComponents/res/values-sk/strings.xml
deleted file mode 100644
index a58aa11..0000000
--- a/packages/MediaComponents/res/values-sk/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Systém"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Zariadenia"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Tlačidlo prenosu"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Tlačidlo prenosu. Odpojené"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Tlačidlo prenosu. Pripája sa"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Tlačidlo prenosu. Pripojené"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Prenos do"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Hľadajú sa zariadenia"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Odpojiť"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Zastaviť prenášanie"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Zavrieť"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Prehrať"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pozastaviť"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Zastaviť"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Rozbaliť"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Zbaliť"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Obrázok albumu"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Posúvač hlasitosti"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nie sú vybrané žiadne médiá"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nie sú k dispozícii žiadne informácie"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Prenáša sa obrazovka"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-sl/strings.xml b/packages/MediaComponents/res/values-sl/strings.xml
deleted file mode 100644
index 8ca4ce4..0000000
--- a/packages/MediaComponents/res/values-sl/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Naprave"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Gumb za predvajanje"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Gumb za predvajanje. Povezava je prekinjena."</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Gumb za predvajanje. Vzpostavljanje povezave."</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Gumb za predvajanje. Povezava je vzpostavljena."</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Predvajanje prek:"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Iskanje naprav"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini povezavo"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Ustavi predvajanje"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Zapri"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Predvajanje"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Zaustavi"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Ustavi"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Razširi"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Strni"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Naslovnica albuma"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Drsnik za glasnost"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ni izbrane predstavnosti"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Podatki niso na voljo"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Predvajanje zaslona"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-sq/strings.xml b/packages/MediaComponents/res/values-sq/strings.xml
deleted file mode 100644
index 816e110..0000000
--- a/packages/MediaComponents/res/values-sq/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistemi"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Pajisjet"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Butoni i transmetimit"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Butoni i transmetimit. Je i shkëputur"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Butoni i transmetimit. Po lidhet"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Butoni i transmetimit. Je i lidhur"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Transmeto te"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Po kërkon pajisje"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Shkëpute"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Ndalo transmetimin"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Mbyll"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Luaj"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pauzë"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Ndalo"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Zgjeroje"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Palose"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Kopertina e albumit"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Rrëshqitësi i volumit"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nuk u zgjodh asnjë media"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nuk jepet asnjë informacion"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Po transmeton ekranin"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-sr/strings.xml b/packages/MediaComponents/res/values-sr/strings.xml
deleted file mode 100644
index caabad5..0000000
--- a/packages/MediaComponents/res/values-sr/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Систем"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Уређаји"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Дугме Пребаци"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Дугме Пребаци. Веза је прекинута"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Дугме Пребаци. Повезује се"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Дугме Пребаци. Повезан је"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Пребацуј на"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Проналажење уређаја"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Прекини везу"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Заустави пребацивање"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Затвори"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Пусти"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Паузирај"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Заустави"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Прошири"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Скупи"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Омот албума"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Клизач за јачину звука"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Нема изабраних медија"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Нису доступне никакве информације"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Пребацује се екран"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-sv/strings.xml b/packages/MediaComponents/res/values-sv/strings.xml
deleted file mode 100644
index ca7d3e0..0000000
--- a/packages/MediaComponents/res/values-sv/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Enheter"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-knappen"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast-knappen. Frånkopplad"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast-knappen. Ansluter"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast-knappen. Ansluten"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Casta till"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Letar efter enheter"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Koppla från"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Sluta casta"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Stäng"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Spela upp"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Pausa"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Avbryt"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Utöka"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Komprimera"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Skivomslag"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volymreglage"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Inga media har valts"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Det finns ingen information"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Skärmen castas"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-sw/strings.xml b/packages/MediaComponents/res/values-sw/strings.xml
deleted file mode 100644
index 9562cb1..0000000
--- a/packages/MediaComponents/res/values-sw/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Mfumo"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Vifaa"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Kitufe cha kutuma"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Kitufe cha kutuma. Kimeondolewa"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Kitufe cha kutuma. Kinaunganisha"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Kitufe cha kutuma. Kimeunganishwa"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Tuma kwenye"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Inatafuta vifaa"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ondoa"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Acha kutuma"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Funga"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Cheza"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Sitisha"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Simamisha"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Panua"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Kunja"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Sanaa ya albamu"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Kitelezi cha sauti"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Hakuna maudhui yaliyochaguliwa"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Hakuna maelezo yaliyopatikana"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Inatuma skrini"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-sw600dp/dimens.xml b/packages/MediaComponents/res/values-sw600dp/dimens.xml
deleted file mode 100644
index 4042348..0000000
--- a/packages/MediaComponents/res/values-sw600dp/dimens.xml
+++ /dev/null
@@ -1,24 +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.
--->
-
-<resources>
-    <!-- The platform's desired fixed width for a dialog along the major axis
-         (the screen is in landscape). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="mr_dialog_fixed_width_major">60%</item>
-    <!-- The platform's desired fixed width for a dialog along the minor axis
-         (the screen is in portrait). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="mr_dialog_fixed_width_minor">90%</item>
-</resources>
diff --git a/packages/MediaComponents/res/values-sw720dp/dimens.xml b/packages/MediaComponents/res/values-sw720dp/dimens.xml
deleted file mode 100644
index 634ab8d..0000000
--- a/packages/MediaComponents/res/values-sw720dp/dimens.xml
+++ /dev/null
@@ -1,24 +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.
--->
-
-<resources>
-    <!-- The platform's desired fixed width for a dialog along the major axis
-         (the screen is in landscape). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="mr_dialog_fixed_width_major">50%</item>
-    <!-- The platform's desired fixed width for a dialog along the minor axis
-         (the screen is in portrait). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="mr_dialog_fixed_width_minor">70%</item>
-</resources>
diff --git a/packages/MediaComponents/res/values-ta/strings.xml b/packages/MediaComponents/res/values-ta/strings.xml
deleted file mode 100644
index e1978f3..0000000
--- a/packages/MediaComponents/res/values-ta/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"சிஸ்டம்"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"சாதனங்கள்"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"திரையிடு பட்டன்"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"அனுப்புதல் பொத்தான். துண்டிக்கப்பட்டது"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"அனுப்புதல் பொத்தான். இணைக்கிறது"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"அனுப்புதல் பொத்தான். இணைக்கப்பட்டது"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"இதற்கு அனுப்பு"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"சாதனங்களைத் தேடுகிறது"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"தொடர்பைத் துண்டி"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"அனுப்புவதை நிறுத்து"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"மூடும்"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"இயக்கும்"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"இடைநிறுத்தும்"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"நிறுத்துவதற்கான பொத்தான்"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"விரிவாக்கு"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"சுருக்கு"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ஆல்பம் ஆர்ட்"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ஒலியளவு ஸ்லைடர்"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"மீடியா எதுவும் தேர்ந்தெடுக்கப்படவில்லை"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"தகவல் எதுவுமில்லை"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"திரையை அனுப்புகிறீர்கள்"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-te/strings.xml b/packages/MediaComponents/res/values-te/strings.xml
deleted file mode 100644
index 7d312e3..0000000
--- a/packages/MediaComponents/res/values-te/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"సిస్టమ్"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"పరికరాలు"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ప్రసారం చేయి బటన్"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ప్రసార బటన్. డిస్‌కనెక్ట్ చేయబడింది"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ప్రసార బటన్. కనెక్ట్ చేస్తోంది"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ప్రసార బటన్. కనెక్ట్ చేయబడింది"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"దీనికి ప్రసారం చేయండి"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"పరికరాలను కనుగొంటోంది"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"డిస్‌కనెక్ట్ చేయి"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"ప్రసారాన్ని ఆపివేయి"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"మూసివేస్తుంది"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"ప్లే చేస్తుంది"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"పాజ్ చేస్తుంది"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"ఆపివేయి"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"విస్తరింపజేస్తుంది"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"కుదిస్తుంది"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ఆల్బమ్ ఆర్ట్"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"వాల్యూమ్ స్లయిడర్"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"మీడియా ఏదీ ఎంచుకోబడలేదు"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"సమాచారం అందుబాటులో లేదు"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"స్క్రీన్‌ను ప్రసారం చేస్తోంది"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-th/strings.xml b/packages/MediaComponents/res/values-th/strings.xml
deleted file mode 100644
index cfa8ae5..0000000
--- a/packages/MediaComponents/res/values-th/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"ระบบ"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"อุปกรณ์"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"ปุ่ม \"แคสต์\""</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ปุ่ม \"แคสต์\" ยกเลิกการเชื่อมต่อ"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ปุ่ม \"แคสต์\" กำลังเชื่อมต่อ"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ปุ่ม \"แคสต์\" เชื่อมต่อแล้ว"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"แคสต์ไปยัง"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"กำลังค้นหาอุปกรณ์"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"ยกเลิกการเชื่อมต่อ"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"หยุดแคสต์"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"ปิด"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"เล่น"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"หยุดชั่วคราว"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"หยุด"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"ขยาย"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ยุบ"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"ปกอัลบั้ม"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"แถบเลื่อนปรับระดับเสียง"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ไม่ได้เลือกสื่อไว้"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ไม่มีข้อมูล"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"กำลังแคสต์หน้าจอ"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-tl/strings.xml b/packages/MediaComponents/res/values-tl/strings.xml
deleted file mode 100644
index a8be3d0..0000000
--- a/packages/MediaComponents/res/values-tl/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Mga Device"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Button na I-cast"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Button na I-cast. Nadiskonekta"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Button na I-cast. Kumokonekta"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Button na I-cast. Nakakonekta"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"I-cast sa"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Naghahanap ng mga device"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Idiskonekta"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Ihinto ang pag-cast"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Isara"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"I-play"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"I-pause"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Ihinto"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Palawakin"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"I-collapse"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Album art"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Slider ng volume"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Walang piniling media"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Walang available na impormasyon"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Kina-cast ang screen"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-tr/strings.xml b/packages/MediaComponents/res/values-tr/strings.xml
deleted file mode 100644
index 05f6392..0000000
--- a/packages/MediaComponents/res/values-tr/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Cihazlar"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Yayınla düğmesi"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Yayınla düğmesi. Bağlantı kesildi"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Yayınla düğmesi. Bağlanıyor"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Yayınla düğmesi. Bağlandı"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Şuraya yayınla:"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Cihazlar bulunuyor"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Bağlantıyı kes"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Yayını durdur"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Kapat"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Oynat"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Duraklat"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Durdur"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Genişlet"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Daralt"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albüm kapağı"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Ses düzeyi kaydırma çubuğu"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Medya seçilmedi"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Bilgi yok"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Ekran yayınlanıyor"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-uk/strings.xml b/packages/MediaComponents/res/values-uk/strings.xml
deleted file mode 100644
index 33d365e..0000000
--- a/packages/MediaComponents/res/values-uk/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Система"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Пристрої"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Кнопка трансляції"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Кнопка трансляції. Від’єднано"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Кнопка трансляції. Під’єднання"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Кнопка трансляції. Під’єднано"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Транслювати на"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Пошук пристроїв"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Відключити"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Припинити трансляцію"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Закрити"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Відтворити"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Призупинити"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Припинити"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Розгорнути"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Згорнути"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Обкладинка альбому"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Повзунок гучності"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Медіа-файл не вибрано"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Немає даних"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Трансляція екрана"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-ur/strings.xml b/packages/MediaComponents/res/values-ur/strings.xml
deleted file mode 100644
index 632c598..0000000
--- a/packages/MediaComponents/res/values-ur/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"سسٹم"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"آلات"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"کاسٹ کرنے کا بٹن"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"کاسٹ کرنے کا بٹن۔ غیر منسلک ہے"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"کاسٹ کرنے کا بٹن۔ منسلک ہو رہا ہے"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"کاسٹ کرنے کا بٹن۔ منسلک ہے"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"اس میں کاسٹ کریں"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"آلات تلاش ہو رہے ہیں"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"غیر منسلک کریں"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"کاسٹ کرنا بند کریں"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"بند کریں"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"چلائیں"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"موقوف کریں"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"روکیں"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"پھیلائیں"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"سکیڑیں"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"البم آرٹ"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"والیوم سلائیڈر"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"کوئی میڈیا منتخب نہیں ہے"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"کوئی معلومات دستیاب نہیں"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"سکرین کاسٹ ہو رہی ہے"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-uz/strings.xml b/packages/MediaComponents/res/values-uz/strings.xml
deleted file mode 100644
index 10a0817..0000000
--- a/packages/MediaComponents/res/values-uz/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Tizim"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Qurilmalar"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Translatsiya tugmasi"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Translatsiya tugmasi. Uzildi"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Translatsiya tugmasi. Ulanmoqda"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Translatsiya tugmasi. Ulandi"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Quyidagiga translatsiya qilish:"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Qurilmalarni topish"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ulanishni uzish"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Translatsiyani to‘xtatish"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Yopish"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Boshlash"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"To‘xtatib turish"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"To‘xtatish"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Yoyish"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Yig‘ish"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Albom muqovasi"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Tovush balandligi slayderi"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Multimedia tanlamagan"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Hech qanday ma’lumot yo‘q"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Ekranni translatsiya qilish"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-vi/strings.xml b/packages/MediaComponents/res/values-vi/strings.xml
deleted file mode 100644
index 7098cca..0000000
--- a/packages/MediaComponents/res/values-vi/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Hệ thống"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Thiết bị"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Nút truyền"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Nút truyền. Đã ngắt kết nối"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Nút truyền. Đang kết nối"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Nút truyền. Đã kết nối"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Truyền tới"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Đang tìm thiết bị"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ngắt kết nối"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Dừng truyền"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Đóng"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Phát"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Tạm dừng"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Dừng"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Mở rộng"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Thu gọn"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Ảnh bìa album"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Thanh trượt âm lượng"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Không có phương tiện nào được chọn"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Không có thông tin nào"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Đang truyền màn hình"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-zh-rCN/strings.xml b/packages/MediaComponents/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 1e22d01..0000000
--- a/packages/MediaComponents/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"系统"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"设备"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"投射按钮"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"投射按钮。已断开连接"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"投射按钮。正在连接"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"投射按钮。已连接"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"投射到"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"正在查找设备"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"断开连接"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"停止投射"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"关闭"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"播放"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"暂停"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"停止"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"展开"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"收起"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"专辑封面"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"音量滑块"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"未选择任何媒体"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"没有任何相关信息"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"正在投射屏幕"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-zh-rHK/strings.xml b/packages/MediaComponents/res/values-zh-rHK/strings.xml
deleted file mode 100644
index 156e5c2..0000000
--- a/packages/MediaComponents/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"系統"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"裝置"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"投放按鈕"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"投放按鈕。已解除連接"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"投放按鈕。正在連接"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"投放按鈕。已連接"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"投放至"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"正在尋找裝置"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"中斷連線"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"停止投放"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"關閉"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"播放"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"暫停"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"停止"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"展開"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"收合"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"專輯封面"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"音量滑桿"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"尚未選擇媒體"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"沒有詳細資料"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"正在投放螢幕"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-zh-rTW/strings.xml b/packages/MediaComponents/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 6cafde1..0000000
--- a/packages/MediaComponents/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"系統"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"裝置"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"投放按鈕"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"投放按鈕;已中斷連線"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"投放按鈕;連線中"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"投放按鈕;已連線"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"投放到"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"正在尋找裝置"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"中斷連線"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"停止投放"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"關閉"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"播放"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"暫停"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"停止"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"展開"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"收合"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"專輯封面"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"音量滑桿"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"未選取任何媒體"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"沒有可用的資訊"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"正在投放螢幕"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values-zu/strings.xml b/packages/MediaComponents/res/values-zu/strings.xml
deleted file mode 100644
index e107c43..0000000
--- a/packages/MediaComponents/res/values-zu/strings.xml
+++ /dev/null
@@ -1,40 +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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="mr_system_route_name" msgid="5441529851481176817">"Isistimu"</string>
-    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Amadivayisi"</string>
-    <string name="mr_button_content_description" msgid="3698378085901466129">"Inkinobho ye-Cast"</string>
-    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Inkinobho yokusakaza. Kunqanyuliwe"</string>
-    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Inkinobho yokusakaza. Kuyaxhunywa"</string>
-    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Inkinobho yokusakaza. Kuxhunyiwe"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Sakaza ku-"</string>
-    <string name="mr_chooser_searching" msgid="6349900579507521956">"Ithola amadivayisi"</string>
-    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Nqamula"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Misa ukusakaza"</string>
-    <string name="mr_controller_close_description" msgid="7333862312480583260">"Vala"</string>
-    <string name="mr_controller_play" msgid="683634565969987458">"Dlala"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"Misa isikhashana"</string>
-    <string name="mr_controller_stop" msgid="735874641921425123">"Misa"</string>
-    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Nweba"</string>
-    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Goqa"</string>
-    <string name="mr_controller_album_art" msgid="6422801843540543585">"Ubuciko be-albhamu"</string>
-    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Isilayida sevolumu"</string>
-    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ayikho imidiya ekhethiwe"</string>
-    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Alukho ulwazi olutholakalayo"</string>
-    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Isikrini sokusakaza"</string>
-</resources>
diff --git a/packages/MediaComponents/res/values/arrays.xml b/packages/MediaComponents/res/values/arrays.xml
deleted file mode 100644
index 1187320..0000000
--- a/packages/MediaComponents/res/values/arrays.xml
+++ /dev/null
@@ -1,27 +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.
--->
-
-<resources>
-    <integer-array name="speed_multiplied_by_100">
-        <item>25</item>
-        <item>50</item>
-        <item>75</item>
-        <item>100</item>
-        <item>125</item>
-        <item>150</item>
-        <item>200</item>
-    </integer-array>
-</resources>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/values/attrs.xml b/packages/MediaComponents/res/values/attrs.xml
deleted file mode 100644
index e37285b..0000000
--- a/packages/MediaComponents/res/values/attrs.xml
+++ /dev/null
@@ -1,44 +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.
--->
-
-<resources>
-    <declare-styleable name="MediaRouteButton">
-        <!-- This drawable is a state list where the "checked" state
-             indicates active media routing.  Checkable indicates connecting
-             and non-checked / non-checkable indicates
-             that media is playing to the local device only. -->
-        <attr name="externalRouteEnabledDrawable" format="reference" />
-        <!-- Tint to apply to the media route button -->
-        <attr name="mediaRouteButtonTint" format="color" />
-
-        <attr name="android:minWidth" />
-        <attr name="android:minHeight" />
-    </declare-styleable>
-
-    <attr name="mediaRouteButtonStyle" format="reference" />
-    <attr name="mediaRouteCloseDrawable" format="reference" />
-    <attr name="mediaRoutePlayDrawable" format="reference" />
-    <attr name="mediaRoutePauseDrawable" format="reference" />
-    <attr name="mediaRouteStopDrawable" format="reference" />
-    <attr name="mediaRouteAudioTrackDrawable" format="reference" />
-    <attr name="mediaRouteDefaultIconDrawable" format="reference" />
-    <attr name="mediaRouteTvIconDrawable" format="reference" />
-    <attr name="mediaRouteSpeakerIconDrawable" format="reference" />
-    <attr name="mediaRouteSpeakerGroupIconDrawable" format="reference" />
-    <attr name="mediaRouteControlPanelThemeOverlay" format="reference" />
-
-    <attr name="mediaRouteTheme" format="reference" />
-</resources>
diff --git a/packages/MediaComponents/res/values/colors.xml b/packages/MediaComponents/res/values/colors.xml
deleted file mode 100644
index e7bc299..0000000
--- a/packages/MediaComponents/res/values/colors.xml
+++ /dev/null
@@ -1,25 +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.
--->
-
-<resources>
-    <color name="gray">#808080</color>
-    <color name="white">#ffffff</color>
-    <color name="white_opacity_70">#B3ffffff</color>
-    <color name="black_opacity_70">#B3000000</color>
-    <color name="title_bar_gradient_start">#50000000</color>
-    <color name="title_bar_gradient_end">#00000000</color>
-    <color name="bottom_bar_background">#40202020</color>
-</resources>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/values/dimens.xml b/packages/MediaComponents/res/values/dimens.xml
deleted file mode 100644
index 2d7b022..0000000
--- a/packages/MediaComponents/res/values/dimens.xml
+++ /dev/null
@@ -1,72 +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.
--->
-
-<resources>
-    <!-- Dialog size -->
-    <eat-comment />
-    <!-- The platform's desired fixed width for a dialog along the major axis
-         (the screen is in landscape). This may be either a fraction or a dimension.-->
-    <dimen name="mr_dialog_fixed_width_major">320dp</dimen>
-    <!-- The platform's desired fixed width for a dialog along the minor axis
-         (the screen is in portrait). This may be either a fraction or a dimension.-->
-    <dimen name="mr_dialog_fixed_width_minor">320dp</dimen>
-
-    <!-- MediaRouteController's volume group list -->
-    <eat-comment />
-    <!-- Maximum height of volume group list. -->
-    <dimen name="mr_controller_volume_group_list_max_height">288dp</dimen>
-    <!-- Height of volume group item. -->
-    <dimen name="mr_controller_volume_group_list_item_height">68dp</dimen>
-    <!-- Size of an item's icon. -->
-    <dimen name="mr_controller_volume_group_list_item_icon_size">24dp</dimen>
-
-    <dimen name="mr_controller_volume_group_list_padding_top">16dp</dimen>
-    <!-- Group list expand/collapse animation duration. -->
-    <integer name="mr_controller_volume_group_list_animation_duration_ms">400</integer>
-    <!-- Group list fade in animation duration. -->
-    <integer name="mr_controller_volume_group_list_fade_in_duration_ms">400</integer>
-    <!-- Group list fade out animation duration. -->
-    <integer name="mr_controller_volume_group_list_fade_out_duration_ms">200</integer>
-
-    <dimen name="mcv2_embedded_settings_width">150dp</dimen>
-    <dimen name="mcv2_embedded_settings_height">36dp</dimen>
-    <dimen name="mcv2_embedded_settings_icon_size">20dp</dimen>
-    <dimen name="mcv2_embedded_settings_text_height">18dp</dimen>
-    <dimen name="mcv2_embedded_settings_main_text_size">12sp</dimen>
-    <dimen name="mcv2_embedded_settings_sub_text_size">10sp</dimen>
-    <dimen name="mcv2_full_settings_width">225dp</dimen>
-    <dimen name="mcv2_full_settings_height">54dp</dimen>
-    <dimen name="mcv2_full_settings_icon_size">30dp</dimen>
-    <dimen name="mcv2_full_settings_text_height">27dp</dimen>
-    <dimen name="mcv2_full_settings_main_text_size">16sp</dimen>
-    <dimen name="mcv2_full_settings_sub_text_size">13sp</dimen>
-    <dimen name="mcv2_settings_offset">8dp</dimen>
-
-    <dimen name="mcv2_transport_controls_padding">4dp</dimen>
-    <dimen name="mcv2_pause_icon_size">36dp</dimen>
-    <dimen name="mcv2_full_icon_size">28dp</dimen>
-    <dimen name="mcv2_embedded_icon_size">24dp</dimen>
-    <dimen name="mcv2_minimal_icon_size">24dp</dimen>
-    <dimen name="mcv2_icon_margin">10dp</dimen>
-
-    <dimen name="mcv2_full_album_image_portrait_size">232dp</dimen>
-    <dimen name="mcv2_full_album_image_landscape_size">176dp</dimen>
-
-    <dimen name="mcv2_custom_progress_max_size">2dp</dimen>
-    <dimen name="mcv2_custom_progress_thumb_size">12dp</dimen>
-    <dimen name="mcv2_buffer_view_height">5dp</dimen>
-    <!-- TODO: adjust bottom bar view -->
-</resources>
diff --git a/packages/MediaComponents/res/values/strings.xml b/packages/MediaComponents/res/values/strings.xml
deleted file mode 100644
index 2597a3b..0000000
--- a/packages/MediaComponents/res/values/strings.xml
+++ /dev/null
@@ -1,157 +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.
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- Name for the default system route prior to Jellybean. [CHAR LIMIT=30] -->
-    <string name="mr_system_route_name">System</string>
-
-    <!-- Name for the user route category created when publishing routes to the system in Jellybean and above. [CHAR LIMIT=30] -->
-    <string name="mr_user_route_category_name">Devices</string>
-
-    <!-- String to be shown as a tooltip of MediaRouteButton
-        Cast is the standard android verb for sending content to a remote device. [CHAR LIMIT=50] -->
-    <string name="mr_button_content_description">Cast button</string>
-
-    <!-- Content description of a MediaRouteButton for accessibility support when no remote device is connected.
-        Cast is the standard android verb for sending content to a remote device. [CHAR LIMIT=NONE] -->
-    <string name="mr_cast_button_disconnected">Cast button. Disconnected</string>
-
-    <!-- Content description of a MediaRouteButton for accessibility support while connecting to a remote device.
-        Cast is the standard android verb for sending content to a remote device. [CHAR LIMIT=NONE] -->
-    <string name="mr_cast_button_connecting">Cast button. Connecting</string>
-
-    <!-- Content description of a MediaRouteButton for accessibility support when a remote device is connected.
-        Cast is the standard android verb for sending content to a remote device. [CHAR LIMIT=NONE] -->
-    <string name="mr_cast_button_connected">Cast button. Connected</string>
-
-    <!-- Title of the media route chooser dialog. [CHAR LIMIT=30] -->
-    <string name="mr_chooser_title">Cast to</string>
-
-    <!-- Placeholder text to show when no devices have been found. [CHAR LIMIT=50] -->
-    <string name="mr_chooser_searching">Finding devices</string>
-
-    <!-- Button to disconnect from a media route.  [CHAR LIMIT=30] -->
-    <string name="mr_controller_disconnect">Disconnect</string>
-
-    <!-- Button to stop playback and disconnect from a media route. [CHAR LIMIT=30] -->
-    <string name="mr_controller_stop_casting">Stop casting</string>
-
-    <!-- Content description for accessibility (not shown on the screen): dialog close button. [CHAR LIMIT=NONE] -->
-    <string name="mr_controller_close_description">Close</string>
-
-    <!-- Content description for accessibility (not shown on the screen): media play button. [CHAR LIMIT=NONE] -->
-    <string name="mr_controller_play">Play</string>
-
-    <!-- Content description for accessibility (not shown on the screen): media pause button. [CHAR LIMIT=NONE] -->
-    <string name="mr_controller_pause">Pause</string>
-
-    <!-- Content description for accessibility (not shown on the screen): media stop button. [CHAR LIMIT=NONE] -->
-    <string name="mr_controller_stop">Stop</string>
-
-    <!-- Content description for accessibility (not shown on the screen): group expand button. Pressing button shows group members of a selected route group. [CHAR LIMIT=NONE] -->
-    <string name="mr_controller_expand_group">Expand</string>
-
-    <!-- Content description for accessibility (not shown on the screen): group collapse button. Pressing button hides group members of a selected route group. [CHAR LIMIT=NONE] -->
-    <string name="mr_controller_collapse_group">Collapse</string>
-
-    <!-- Content description for accessibility (not shown on the screen): album art button. Clicking on the album art takes user to a predefined activity per media app. [CHAR LIMIT=50] -->
-    <string name="mr_controller_album_art">Album art</string>
-
-    <!-- Content description for accessibility (not shown on the screen): volume slider. [CHAR LIMIT=NONE] -->
-    <string name="mr_controller_volume_slider">Volume slider</string>
-
-    <!-- Placeholder text to show when no media have been selected for playback. [CHAR LIMIT=50] -->
-    <string name="mr_controller_no_media_selected">No media selected</string>
-
-    <!-- Placeholder text to show when no title/description have been found for a given song/video. [CHAR LIMIT=50] -->
-    <string name="mr_controller_no_info_available">No info available</string>
-
-    <!-- Placeholder text indicating that the user is currently casting screen. [CHAR LIMIT=50] -->
-    <string name="mr_controller_casting_screen">Casting screen</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>
-    <!-- Text for error alert when a video cannot be played. It can be used by any app. -->
-    <string name="VideoView2_error_text_unknown">Can\'t play this video.</string>
-    <!-- Button to close error alert when a video cannot be played. -->
-    <string name="VideoView2_error_button">OK</string>
-
-    <!-- Text for displaying ad skip wait time. -->
-    <string name="MediaControlView2_ad_skip_wait_time">
-        You can skip Ad in <xliff:g id="wait_time" example="5">%1$d</xliff:g>s
-    </string>
-    <!-- Text for displaying ad total remaining time. -->
-    <string name="MediaControlView2_ad_remaining_time">
-        Ad · <xliff:g id="remaining_time" example="1:15">%1$s</xliff:g> remaining
-    </string>
-    <!-- Placeholder text indicating that the user can press the button to go to an external website. -->
-    <string name="MediaControlView2_ad_text">Visit Advertiser</string>
-    <string name="MediaControlView2_subtitle_text">Closed caption</string>
-    <string name="MediaControlView2_subtitle_off_text">Off</string>
-    <string name="MediaControlView2_audio_track_text">Audio track</string>
-    <string name="MediaControlView2_audio_track_none_text">None</string>
-    <string name="MediaControlView2_video_quality_text">Video quality</string>
-    <string name="MediaControlView2_video_quality_auto_text">Auto</string>
-    <string name="MediaControlView2_help_text">Help &amp; feedback</string>
-    <string name="MediaControlView2_playback_speed_text">Playback speed</string>
-    <string-array name="MediaControlView2_playback_speeds">
-        <item>0.25x</item>
-        <item>0.5x</item>
-        <item>0.75x</item>
-        <item>Normal</item>
-        <item>1.25x</item>
-        <item>1.5x</item>
-        <item>2x</item>
-    </string-array>
-    <!-- Placeholder text for displaying time. Used to calculate which size layout to use. -->
-    <string name="MediaControlView2_time_placeholder">00:00:00</string>
-
-    <!-- Text for displaying subtitle track number. -->
-    <string name="MediaControlView2_subtitle_track_number_text">
-        Track <xliff:g id="track_number" example="1">%1$s</xliff:g>
-    </string>
-    <!-- Text for displaying audio track number. -->
-    <string name="MediaControlView2_audio_track_number_text">
-        Track <xliff:g id="audio_number" example="1">%1$s</xliff:g>
-    </string>
-    <!-- Text for displaying unknown song title. -->
-    <string name="mcv2_music_title_unknown_text">Song title unknown</string>
-    <!-- Text for displaying unknown artist name. -->
-    <string name="mcv2_music_artist_unknown_text">Artist unknown</string>
-
-    <!--Content Descriptions -->
-    <string name="mcv2_back_button_desc">Back</string>
-    <string name="mcv2_overflow_left_button_desc">See more buttons</string>
-    <string name="mcv2_overflow_right_button_desc">Back to previous button list</string>
-    <string name="mcv2_seek_bar_desc">Playback progress</string>
-    <string name="mcv2_settings_button_desc">Settings</string>
-    <string name="mcv2_video_quality_button_desc">Video Quality Selection</string>
-    <string name="mcv2_cc_is_on">Subtitle is on. Click to hide it.</string>
-    <string name="mcv2_cc_is_off">Subtitle is off. Click to show it.</string>
-    <string name="mcv2_replay_button_desc">Replay</string>
-    <string name="mcv2_play_button_desc">Play</string>
-    <string name="mcv2_pause_button_desc">Pause</string>
-    <string name="mcv2_previous_button_desc">Previous media</string>
-    <string name="mcv2_next_button_desc">Next media</string>
-    <string name="mcv2_rewind_button_desc">Rewind by 10 seconds</string>
-    <string name="mcv2_ffwd_button_desc">Go forward by 30 seconds</string>
-    <string name="mcv2_launch_button_desc">Launch Link</string>
-    <string name="mcv2_muted_button_desc">Muted. Click to unmute</string>
-    <string name="mcv2_unmuted_button_desc">Click to Mute</string>
-    <string name="mcv2_full_screen_button_desc">Full screen</string>
-</resources>
diff --git a/packages/MediaComponents/res/values/style.xml b/packages/MediaComponents/res/values/style.xml
deleted file mode 100644
index 5b9a8ee..0000000
--- a/packages/MediaComponents/res/values/style.xml
+++ /dev/null
@@ -1,221 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <style name="FullTransportControlsButton">
-        <item name="android:background">@null</item>
-        <item name="android:layout_margin">@dimen/mcv2_icon_margin</item>
-        <item name="android:scaleType">fitXY</item>
-    </style>
-
-    <style name="FullTransportControlsButton.Previous">
-        <item name="android:src">@drawable/ic_skip_previous</item>
-        <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
-        <item name="android:contentDescription">@string/mcv2_previous_button_desc</item>
-    </style>
-
-    <style name="FullTransportControlsButton.Next">
-        <item name="android:src">@drawable/ic_skip_next</item>
-        <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
-        <item name="android:contentDescription">@string/mcv2_next_button_desc</item>
-    </style>
-
-    <style name="FullTransportControlsButton.Pause">
-        <item name="android:src">@drawable/ic_pause_circle_filled</item>
-        <item name="android:layout_width">@dimen/mcv2_pause_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_pause_icon_size</item>
-        <item name="android:contentDescription">@string/mcv2_pause_button_desc</item>
-    </style>
-
-    <style name="FullTransportControlsButton.Ffwd">
-        <item name="android:src">@drawable/ic_forward_30</item>
-        <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
-        <item name="android:contentDescription">@string/mcv2_ffwd_button_desc</item>
-    </style>
-
-    <style name="FullTransportControlsButton.Rew">
-        <item name="android:src">@drawable/ic_rewind_10</item>
-        <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
-        <item name="android:contentDescription">@string/mcv2_rewind_button_desc</item>
-    </style>
-
-    <style name="EmbeddedTransportControlsButton">
-        <item name="android:background">@null</item>
-        <item name="android:layout_margin">@dimen/mcv2_icon_margin</item>
-        <item name="android:scaleType">fitXY</item>
-    </style>
-
-    <style name="EmbeddedTransportControlsButton.Previous">
-        <item name="android:src">@drawable/ic_skip_previous</item>
-        <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
-        <item name="android:contentDescription">@string/mcv2_previous_button_desc</item>
-    </style>
-
-    <style name="EmbeddedTransportControlsButton.Next">
-        <item name="android:src">@drawable/ic_skip_next</item>
-        <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
-        <item name="android:contentDescription">@string/mcv2_next_button_desc</item>
-    </style>
-
-    <style name="EmbeddedTransportControlsButton.Pause">
-        <item name="android:src">@drawable/ic_pause_circle_filled</item>
-        <item name="android:layout_width">@dimen/mcv2_pause_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_pause_icon_size</item>
-        <item name="android:contentDescription">@string/mcv2_pause_button_desc</item>
-    </style>
-
-    <style name="EmbeddedTransportControlsButton.Ffwd">
-        <item name="android:src">@drawable/ic_forward_30</item>
-        <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
-        <item name="android:contentDescription">@string/mcv2_ffwd_button_desc</item>
-    </style>
-
-    <style name="EmbeddedTransportControlsButton.Rew">
-        <item name="android:src">@drawable/ic_rewind_10</item>
-        <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
-        <item name="android:contentDescription">@string/mcv2_rewind_button_desc</item>
-    </style>
-
-    <style name="MinimalTransportControlsButton">
-        <item name="android:background">@null</item>
-        <item name="android:layout_width">@dimen/mcv2_pause_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_pause_icon_size</item>
-        <item name="android:layout_margin">@dimen/mcv2_icon_margin</item>
-        <item name="android:scaleType">fitXY</item>
-        <item name="android:src">@drawable/ic_pause_circle_filled</item>
-        <item name="android:contentDescription">@string/mcv2_pause_button_desc</item>
-    </style>
-
-    <style name="TitleBar">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">46dp</item>
-        <item name="android:paddingStart">5dp</item>
-        <item name="android:paddingEnd">5dp</item>
-    </style>
-
-    <style name="TitleBarButton">
-        <item name="android:background">@null</item>
-        <item name="android:layout_width">36dp</item>
-        <item name="android:layout_height">36dp</item>
-    </style>
-
-    <style name="TitleBarButton.Back">
-        <item name="android:src">@drawable/ic_arrow_back</item>
-        <item name="android:contentDescription">@string/mcv2_back_button_desc</item>
-    </style>
-
-    <style name="TitleBarButton.Launch">
-        <item name="android:src">@drawable/ic_launch</item>
-        <item name="android:contentDescription">@string/mcv2_launch_button_desc</item>
-    </style>
-
-    <style name="TimeText">
-        <item name="android:layout_width">wrap_content</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:paddingStart">4dp</item>
-        <item name="android:paddingEnd">4dp</item>
-        <item name="android:textStyle">bold</item>
-        <item name="android:textSize">14sp</item>
-        <item name="android:gravity">center</item>
-    </style>
-
-    <style name="TimeText.Current">
-        <item name="android:textColor">@color/white</item>
-        <item name="android:text">@string/MediaControlView2_time_placeholder</item>
-    </style>
-
-    <style name="TimeText.Interpunct">
-        <item name="android:textColor">@color/white_opacity_70</item>
-        <item name="android:text">·</item>
-    </style>
-
-    <style name="TimeText.End">
-        <item name="android:textColor">@color/white_opacity_70</item>
-        <item name="android:text">@string/MediaControlView2_time_placeholder</item>
-    </style>
-
-    <style name="BottomBarButton">
-        <item name="android:background">@null</item>
-        <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
-        <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
-        <item name="android:layout_margin">@dimen/mcv2_icon_margin</item>
-        <item name="android:gravity">center_horizontal</item>
-        <item name="android:scaleType">fitXY</item>
-    </style>
-
-    <style name="BottomBarButton.CC">
-        <item name="android:src">@drawable/ic_subtitle_off</item>
-        <item name="android:contentDescription">@string/mcv2_cc_is_off</item>
-    </style>
-
-    <style name="BottomBarButton.FullScreen">
-        <item name="android:src">@drawable/ic_fullscreen</item>
-        <item name="android:contentDescription">@string/mcv2_full_screen_button_desc</item>
-    </style>
-
-    <style name="BottomBarButton.OverflowRight">
-        <item name="android:src">@drawable/ic_chevron_right</item>
-        <item name="android:contentDescription">@string/mcv2_overflow_right_button_desc</item>
-    </style>
-
-    <style name="BottomBarButton.OverflowLeft">
-        <item name="android:src">@drawable/ic_chevron_left</item>
-        <item name="android:contentDescription">@string/mcv2_overflow_left_button_desc</item>
-    </style>
-
-    <style name="BottomBarButton.Settings">
-        <item name="android:src">@drawable/ic_settings</item>
-        <item name="android:contentDescription">@string/mcv2_settings_button_desc</item>
-    </style>
-
-    <style name="BottomBarButton.Mute">
-        <item name="android:src">@drawable/ic_unmute</item>
-        <item name="android:contentDescription">@string/mcv2_unmuted_button_desc</item>
-    </style>
-
-    <style name="BottomBarButton.VideoQuality">
-      <item name="android:src">@drawable/ic_high_quality</item>
-      <item name="android:contentDescription">@string/mcv2_video_quality_button_desc</item>
-    </style>
-
-    <style name="FullMusicPortrait">
-        <item name="android:layout_height">0dp</item>
-    </style>
-
-    <style name="FullMusicPortrait.Image">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_weight">0.6</item>
-        <item name="android:gravity">center</item>
-    </style>
-
-    <style name="FullMusicPortrait.Text">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_weight">0.4</item>
-        <item name="android:gravity">top|center</item>
-        <item name="android:orientation">vertical</item>
-    </style>
-
-    <style name="FullMusicLandscape">
-        <item name="android:layout_width">0dp</item>
-    </style>
-
-    <style name="FullMusicLandscape.Image">
-        <item name="android:layout_height">match_parent</item>
-        <item name="android:layout_weight">0.35</item>
-        <item name="android:gravity">center|right</item>
-    </style>
-
-    <style name="FullMusicLandscape.Text">
-        <item name="android:layout_height">match_parent</item>
-        <item name="android:layout_weight">0.65</item>
-        <item name="android:layout_marginLeft">24dp</item>
-        <item name="android:gravity">center|left</item>
-        <item name="android:orientation">vertical</item>
-    </style>
-</resources>
diff --git a/packages/MediaComponents/res/values/styles.xml b/packages/MediaComponents/res/values/styles.xml
deleted file mode 100644
index bde6900..0000000
--- a/packages/MediaComponents/res/values/styles.xml
+++ /dev/null
@@ -1,33 +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.
--->
-
-<resources>
-    <style name="Widget.MediaRouter.MediaRouteButton"
-            parent="Widget.AppCompat.ActionButton">
-        <item name="externalRouteEnabledDrawable">@drawable/mr_button_dark</item>
-    </style>
-
-    <style name="Widget.MediaRouter.Light.MediaRouteButton"
-            parent="Widget.AppCompat.Light.ActionButton">
-        <item name="externalRouteEnabledDrawable">@drawable/mr_button_light</item>
-    </style>
-
-    <style name="TextAppearance.MediaRouter.Title" parent="TextAppearance.AppCompat.Title" />
-
-    <style name="TextAppearance.MediaRouter.PrimaryText" parent="TextAppearance.AppCompat.Subhead" />
-
-    <style name="TextAppearance.MediaRouter.SecondaryText" parent="TextAppearance.AppCompat.Body1" />
-</resources>
diff --git a/packages/MediaComponents/res/values/symbols.xml b/packages/MediaComponents/res/values/symbols.xml
deleted file mode 100644
index ee0e8c6..0000000
--- a/packages/MediaComponents/res/values/symbols.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* 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.
-** 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>
-    <!--java-symbol type="id" name="cc" />
-    <java-symbol type="id" name="ffwd" />
-    <java-symbol type="id" name="mediacontroller_progress" />
-    <java-symbol type="id" name="next" />
-    <java-symbol type="id" name="pause" />
-    <java-symbol type="id" name="prev" />
-    <java-symbol type="id" name="rew" />
-    <java-symbol type="id" name="time" />
-    <java-symbol type="id" name="time_current" /-->
-</resources>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/values/themes.xml b/packages/MediaComponents/res/values/themes.xml
deleted file mode 100644
index d9a754b..0000000
--- a/packages/MediaComponents/res/values/themes.xml
+++ /dev/null
@@ -1,75 +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.
--->
-
-<resources>
-
-    <style name="Theme.MediaRouter" parent="android:Theme.Material">
-        <item name="android:windowNoTitle">true</item>
-        <item name="mediaRouteButtonStyle">@style/Widget.MediaRouter.MediaRouteButton</item>
-
-        <item name="mediaRouteCloseDrawable">@drawable/mr_dialog_close_dark</item>
-        <item name="mediaRoutePlayDrawable">@drawable/mr_media_play_dark</item>
-        <item name="mediaRoutePauseDrawable">@drawable/mr_media_pause_dark</item>
-        <item name="mediaRouteStopDrawable">@drawable/mr_media_stop_dark</item>
-        <item name="mediaRouteAudioTrackDrawable">@drawable/mr_vol_type_audiotrack_dark</item>
-        <item name="mediaRouteDefaultIconDrawable">@drawable/ic_mr_button_disconnected_dark</item>
-        <item name="mediaRouteTvIconDrawable">@drawable/ic_vol_type_tv_dark</item>
-        <item name="mediaRouteSpeakerIconDrawable">@drawable/ic_vol_type_speaker_dark</item>
-        <item name="mediaRouteSpeakerGroupIconDrawable">@drawable/ic_vol_type_speaker_group_dark</item>
-
-        <item name="mediaRouteControlPanelThemeOverlay">@null</item>
-    </style>
-
-    <style name="Theme.MediaRouter.LightControlPanel">
-        <item name="mediaRouteControlPanelThemeOverlay">@style/ThemeOverlay.MediaRouter.Light</item>
-    </style>
-
-    <style name="Theme.MediaRouter.Light" parent="android:Theme.Material.Light">
-        <item name="android:windowNoTitle">true</item>
-        <item name="mediaRouteButtonStyle">@style/Widget.MediaRouter.Light.MediaRouteButton</item>
-
-        <item name="mediaRouteCloseDrawable">@drawable/mr_dialog_close_light</item>
-        <item name="mediaRoutePlayDrawable">@drawable/mr_media_play_light</item>
-        <item name="mediaRoutePauseDrawable">@drawable/mr_media_pause_light</item>
-        <item name="mediaRouteStopDrawable">@drawable/mr_media_stop_light</item>
-        <item name="mediaRouteAudioTrackDrawable">@drawable/mr_vol_type_audiotrack_light</item>
-        <item name="mediaRouteDefaultIconDrawable">@drawable/ic_mr_button_grey</item>
-        <item name="mediaRouteTvIconDrawable">@drawable/ic_vol_type_tv_light</item>
-        <item name="mediaRouteSpeakerIconDrawable">@drawable/ic_vol_type_speaker_light</item>
-        <item name="mediaRouteSpeakerGroupIconDrawable">@drawable/ic_vol_type_speaker_group_light</item>
-
-        <item name="mediaRouteControlPanelThemeOverlay">@null</item>
-    </style>
-
-    <style name="Theme.MediaRouter.Light.DarkControlPanel">
-        <item name="mediaRouteControlPanelThemeOverlay">@style/ThemeOverlay.MediaRouter.Dark</item>
-    </style>
-
-    <style name="ThemeOverlay.MediaRouter.Dark" parent="android:Theme.Material">
-        <item name="mediaRoutePlayDrawable">@drawable/mr_media_play_dark</item>
-        <item name="mediaRoutePauseDrawable">@drawable/mr_media_pause_dark</item>
-        <item name="mediaRouteStopDrawable">@drawable/mr_media_stop_dark</item>
-        <item name="mediaRouteAudioTrackDrawable">@drawable/mr_vol_type_audiotrack_dark</item>
-
-    </style>
-    <style name="ThemeOverlay.MediaRouter.Light" parent="android:Theme.Material.Light">
-        <item name="mediaRoutePlayDrawable">@drawable/mr_media_play_light</item>
-        <item name="mediaRoutePauseDrawable">@drawable/mr_media_pause_light</item>
-        <item name="mediaRouteStopDrawable">@drawable/mr_media_stop_light</item>
-        <item name="mediaRouteAudioTrackDrawable">@drawable/mr_vol_type_audiotrack_light</item>
-    </style>
-
-</resources>
diff --git a/packages/MediaComponents/src/com/android/media/IMediaController2.aidl b/packages/MediaComponents/src/com/android/media/IMediaController2.aidl
index 0488b70..cc5acf9 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaController2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaController2.aidl
@@ -52,16 +52,4 @@
     void onAllowedCommandsChanged(in Bundle commands);
 
     void onCustomCommand(in Bundle command, in Bundle args, in ResultReceiver receiver);
-
-    //////////////////////////////////////////////////////////////////////////////////////////////
-    // Browser sepcific
-    //////////////////////////////////////////////////////////////////////////////////////////////
-    void onGetLibraryRootDone(in Bundle rootHints, String rootMediaId, in Bundle rootExtra);
-    void onGetItemDone(String mediaId, in Bundle result);
-    void onChildrenChanged(String rootMediaId, int itemCount, in Bundle extras);
-    void onGetChildrenDone(String parentId, int page, int pageSize, in List<Bundle> result,
-            in Bundle extras);
-    void onSearchResultChanged(String query, int itemCount, in Bundle extras);
-    void onGetSearchResultDone(String query, int page, int pageSize, in List<Bundle> result,
-            in Bundle extras);
 }
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
index 664467d..5761455 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -69,17 +69,4 @@
     void skipToNextItem(IMediaController2 caller);
     void setRepeatMode(IMediaController2 caller, int repeatMode);
     void setShuffleMode(IMediaController2 caller, int shuffleMode);
-
-    //////////////////////////////////////////////////////////////////////////////////////////////
-    // library service specific
-    //////////////////////////////////////////////////////////////////////////////////////////////
-    void getLibraryRoot(IMediaController2 caller, in Bundle rootHints);
-    void getItem(IMediaController2 caller, String mediaId);
-    void getChildren(IMediaController2 caller, String parentId, int page, int pageSize,
-            in Bundle extras);
-    void search(IMediaController2 caller, String query, in Bundle extras);
-    void getSearchResult(IMediaController2 caller, String query, int page, int pageSize,
-            in Bundle extras);
-    void subscribe(IMediaController2 caller, String parentId, in Bundle extras);
-    void unsubscribe(IMediaController2 caller, String parentId);
 }
diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
deleted file mode 100644
index 0327beb..0000000
--- a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * 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.MediaBrowser2;
-import android.media.MediaBrowser2.BrowserCallback;
-import android.media.MediaItem2;
-import android.media.SessionToken2;
-import android.media.update.MediaBrowser2Provider;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.text.TextUtils;
-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(Context context, MediaBrowser2 instance, SessionToken2 token,
-            Executor executor, BrowserCallback callback) {
-        super(context, instance, token, executor, callback);
-        mInstance = instance;
-        mCallback = callback;
-    }
-
-    @Override MediaBrowser2 getInstance() {
-        return (MediaBrowser2) super.getInstance();
-    }
-
-    @Override
-    public void getLibraryRoot_impl(Bundle rootHints) {
-        final IMediaSession2 binder = getSessionBinder();
-        if (binder != null) {
-            try {
-                binder.getLibraryRoot(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 extras) {
-        if (parentId == null) {
-            throw new IllegalArgumentException("parentId shouldn't be null");
-        }
-
-        final IMediaSession2 binder = getSessionBinder();
-        if (binder != null) {
-            try {
-                binder.subscribe(getControllerStub(), parentId, extras);
-            } 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 unsubscribe_impl(String parentId) {
-        if (parentId == null) {
-            throw new IllegalArgumentException("parentId shouldn't be null");
-        }
-
-        final IMediaSession2 binder = getSessionBinder();
-        if (binder != null) {
-            try {
-                binder.unsubscribe(getControllerStub(), parentId);
-            } 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 getItem_impl(String mediaId) {
-        if (mediaId == null) {
-            throw new IllegalArgumentException("mediaId shouldn't be null");
-        }
-
-        final IMediaSession2 binder = getSessionBinder();
-        if (binder != null) {
-            try {
-                binder.getItem(getControllerStub(), mediaId);
-            } 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 getChildren_impl(String parentId, int page, int pageSize, Bundle extras) {
-        if (parentId == null) {
-            throw new IllegalArgumentException("parentId shouldn't be null");
-        }
-        if (page < 1 || pageSize < 1) {
-            throw new IllegalArgumentException("Neither page nor pageSize should be less than 1");
-        }
-
-        final IMediaSession2 binder = getSessionBinder();
-        if (binder != null) {
-            try {
-                binder.getChildren(getControllerStub(), parentId, page, pageSize, extras);
-            } 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 search_impl(String query, Bundle extras) {
-        if (TextUtils.isEmpty(query)) {
-            throw new IllegalArgumentException("query shouldn't be empty");
-        }
-        final IMediaSession2 binder = getSessionBinder();
-        if (binder != null) {
-            try {
-                binder.search(getControllerStub(), query, extras);
-            } 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 getSearchResult_impl(String query, int page, int pageSize, Bundle extras) {
-        if (TextUtils.isEmpty(query)) {
-            throw new IllegalArgumentException("query shouldn't be empty");
-        }
-        if (page < 1 || pageSize < 1) {
-            throw new IllegalArgumentException("Neither page nor pageSize should be less than 1");
-        }
-        final IMediaSession2 binder = getSessionBinder();
-        if (binder != null) {
-            try {
-                binder.getSearchResult(getControllerStub(), query, page, pageSize, extras);
-            } 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());
-        }
-    }
-
-    public void onGetLibraryRootDone(
-            final Bundle rootHints, final String rootMediaId, final Bundle rootExtra) {
-        getCallbackExecutor().execute(() -> {
-            mCallback.onGetLibraryRootDone(getInstance(), rootHints, rootMediaId, rootExtra);
-        });
-    }
-
-    public void onGetItemDone(String mediaId, MediaItem2 item) {
-        getCallbackExecutor().execute(() -> {
-            mCallback.onGetItemDone(getInstance(), mediaId, item);
-        });
-    }
-
-    public void onGetChildrenDone(String parentId, int page, int pageSize, List<MediaItem2> result,
-            Bundle extras) {
-        getCallbackExecutor().execute(() -> {
-            mCallback.onGetChildrenDone(getInstance(), parentId, page, pageSize, result, extras);
-        });
-    }
-
-    public void onSearchResultChanged(String query, int itemCount, Bundle extras) {
-        getCallbackExecutor().execute(() -> {
-            mCallback.onSearchResultChanged(getInstance(), query, itemCount, extras);
-        });
-    }
-
-    public void onGetSearchResultDone(String query, int page, int pageSize, List<MediaItem2> result,
-            Bundle extras) {
-        getCallbackExecutor().execute(() -> {
-            mCallback.onGetSearchResultDone(getInstance(), query, page, pageSize, result, extras);
-        });
-    }
-
-    public void onChildrenChanged(final String parentId, int itemCount, final Bundle extras) {
-        getCallbackExecutor().execute(() -> {
-            mCallback.onChildrenChanged(getInstance(), parentId, itemCount, extras);
-        });
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 2883087..1c4cf82 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -45,7 +45,6 @@
 import android.media.MediaPlaylistAgent.RepeatMode;
 import android.media.MediaPlaylistAgent.ShuffleMode;
 import android.media.MediaSession2.CommandButton;
-import android.media.MediaSessionService2;
 import android.media.Rating2;
 import android.media.SessionCommand2;
 import android.media.SessionCommandGroup2;
@@ -154,64 +153,6 @@
             // Session
             mServiceConnection = null;
             connectToSession(SessionToken2Impl.from(mToken).getSessionBinder());
-        } else {
-            // Session service
-            if (Process.myUid() == Process.SYSTEM_UID) {
-                // It's system server (MediaSessionService) that wants to monitor session.
-                // Don't bind if able..
-                IMediaSession2 binder = SessionToken2Impl.from(mToken).getSessionBinder();
-                if (binder != null) {
-                    // Use binder in the session token instead of bind by its own.
-                    // Otherwise server will holds the binding to the service *forever* and service
-                    // will never stop.
-                    mServiceConnection = null;
-                    connectToSession(SessionToken2Impl.from(mToken).getSessionBinder());
-                    return;
-                } else if (DEBUG) {
-                    // Should happen only when system server wants to dispatch media key events to
-                    // a dead service.
-                    Log.d(TAG, "System server binds to a session service. Should unbind"
-                            + " immediately after the use.");
-                }
-            }
-            mServiceConnection = new SessionServiceConnection();
-            connectToService();
-        }
-    }
-
-    private void connectToService() {
-        // Service. Needs to get fresh binder whenever connection is needed.
-        SessionToken2Impl impl = SessionToken2Impl.from(mToken);
-        final Intent intent = new Intent(MediaSessionService2.SERVICE_INTERFACE);
-        intent.setClassName(mToken.getPackageName(), impl.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.
-        boolean result;
-        if (Process.myUid() == Process.SYSTEM_UID) {
-            // Use bindServiceAsUser() for binding from system service to avoid following warning.
-            // ContextImpl: Calling a method in the system process without a qualified user
-            result = mContext.bindServiceAsUser(intent, mServiceConnection, Context.BIND_AUTO_CREATE,
-                    UserHandle.getUserHandleForUid(mToken.getUid()));
-        } else {
-            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");
         }
     }
 
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java b/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
index ece4a00..5b71e65 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Stub.java
@@ -54,14 +54,6 @@
         return controller;
     }
 
-    private MediaBrowser2Impl getBrowser() throws IllegalStateException {
-        final MediaController2Impl controller = getController();
-        if (controller instanceof MediaBrowser2Impl) {
-            return (MediaBrowser2Impl) controller;
-        }
-        return null;
-    }
-
     public void destroy() {
         mController.clear();
     }
@@ -327,144 +319,4 @@
         }
         controller.onCustomCommand(command, args, receiver);
     }
-
-    ////////////////////////////////////////////////////////////////////////////////////////////
-    // MediaBrowser specific
-    ////////////////////////////////////////////////////////////////////////////////////////////
-    @Override
-    public void onGetLibraryRootDone(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.onGetLibraryRootDone(rootHints, rootMediaId, rootExtra);
-    }
-
-
-    @Override
-    public void onGetItemDone(String mediaId, Bundle itemBundle) throws RuntimeException {
-        if (mediaId == null) {
-            Log.w(TAG, "onGetItemDone(): Ignoring null mediaId");
-            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;
-        }
-        browser.onGetItemDone(mediaId, MediaItem2.fromBundle(itemBundle));
-    }
-
-    @Override
-    public void onGetChildrenDone(String parentId, int page, int pageSize,
-            List<Bundle> itemBundleList, Bundle extras) throws RuntimeException {
-        if (parentId == null) {
-            Log.w(TAG, "onGetChildrenDone(): Ignoring null parentId");
-            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<MediaItem2> result = null;
-        if (itemBundleList != null) {
-            result = new ArrayList<>();
-            for (Bundle bundle : itemBundleList) {
-                result.add(MediaItem2.fromBundle(bundle));
-            }
-        }
-        browser.onGetChildrenDone(parentId, page, pageSize, result, extras);
-    }
-
-    @Override
-    public void onSearchResultChanged(String query, int itemCount, Bundle extras)
-            throws RuntimeException {
-        if (TextUtils.isEmpty(query)) {
-            Log.w(TAG, "onSearchResultChanged(): Ignoring empty query");
-            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;
-        }
-        browser.onSearchResultChanged(query, itemCount, extras);
-    }
-
-    @Override
-    public void onGetSearchResultDone(String query, int page, int pageSize,
-            List<Bundle> itemBundleList, Bundle extras) throws RuntimeException {
-        if (TextUtils.isEmpty(query)) {
-            Log.w(TAG, "onGetSearchResultDone(): Ignoring empty query");
-            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<MediaItem2> result = null;
-        if (itemBundleList != null) {
-            result = new ArrayList<>();
-            for (Bundle bundle : itemBundleList) {
-                result.add(MediaItem2.fromBundle(bundle));
-            }
-        }
-        browser.onGetSearchResultDone(query, page, pageSize, result, extras);
-    }
-
-    @Override
-    public void onChildrenChanged(String parentId, int itemCount, Bundle extras) {
-        if (parentId == null) {
-            Log.w(TAG, "onChildrenChanged(): Ignoring null parentId");
-            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;
-        }
-        browser.onChildrenChanged(parentId, itemCount, extras);
-    }
 }
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
deleted file mode 100644
index cf34cd4..0000000
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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.media.MediaLibraryService2;
-import android.media.MediaLibraryService2.LibraryRoot;
-import android.media.MediaLibraryService2.MediaLibrarySession;
-import android.media.MediaLibraryService2.MediaLibrarySession.Builder;
-import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
-import android.media.MediaPlayerBase;
-import android.media.MediaPlaylistAgent;
-import android.media.MediaSession2;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSessionService2;
-import android.media.SessionToken2;
-import android.media.VolumeProvider2;
-import android.media.update.MediaLibraryService2Provider;
-import android.os.Bundle;
-import android.text.TextUtils;
-
-import com.android.media.MediaSession2Impl.BuilderBaseImpl;
-
-import java.util.concurrent.Executor;
-
-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
-    int getSessionType() {
-        return SessionToken2.TYPE_LIBRARY_SERVICE;
-    }
-
-    public static class MediaLibrarySessionImpl extends MediaSession2Impl
-            implements MediaLibrarySessionProvider {
-        public MediaLibrarySessionImpl(Context context,
-                MediaPlayerBase player, String id, MediaPlaylistAgent playlistAgent,
-                VolumeProvider2 volumeProvider,
-                PendingIntent sessionActivity, Executor callbackExecutor,
-                MediaLibrarySessionCallback callback) {
-            super(context, player, id, playlistAgent, volumeProvider, sessionActivity,
-                    callbackExecutor, callback);
-            // Don't put any extra initialization here. Here's the reason.
-            // System service will recognize this session inside of the super constructor and would
-            // connect to this session assuming that initialization is finished. However, if any
-            // initialization logic is here, calls from the server would fail.
-            // see: MediaSession2Stub#connect()
-        }
-
-        @Override
-        MediaLibrarySession createInstance() {
-            return new MediaLibrarySession(this);
-        }
-
-        @Override
-        MediaLibrarySession getInstance() {
-            return (MediaLibrarySession) super.getInstance();
-        }
-
-        @Override
-        MediaLibrarySessionCallback getCallback() {
-            return (MediaLibrarySessionCallback) super.getCallback();
-        }
-
-        @Override
-        public void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
-                int itemCount, Bundle extras) {
-            if (controller == null) {
-                throw new IllegalArgumentException("controller shouldn't be null");
-            }
-            if (parentId == null) {
-                throw new IllegalArgumentException("parentId shouldn't be null");
-            }
-            getSessionStub().notifyChildrenChangedNotLocked(controller, parentId, itemCount,
-                    extras);
-        }
-
-        @Override
-        public void notifyChildrenChanged_impl(String parentId, int itemCount, Bundle extras) {
-            if (parentId == null) {
-                throw new IllegalArgumentException("parentId shouldn't be null");
-            }
-            getSessionStub().notifyChildrenChangedNotLocked(parentId, itemCount, extras);
-        }
-
-        @Override
-        public void notifySearchResultChanged_impl(ControllerInfo controller, String query,
-                int itemCount, Bundle extras) {
-            ensureCallingThread();
-            if (controller == null) {
-                throw new IllegalArgumentException("controller shouldn't be null");
-            }
-            if (TextUtils.isEmpty(query)) {
-                throw new IllegalArgumentException("query shouldn't be empty");
-            }
-            getSessionStub().notifySearchResultChanged(controller, query, itemCount, extras);
-        }
-    }
-
-    public static class BuilderImpl
-            extends BuilderBaseImpl<MediaLibrarySession, MediaLibrarySessionCallback> {
-        public BuilderImpl(MediaLibraryService2 service, Builder instance,
-                Executor callbackExecutor, MediaLibrarySessionCallback callback) {
-            super(service);
-            setSessionCallback_impl(callbackExecutor, callback);
-        }
-
-        @Override
-        public MediaLibrarySession build_impl() {
-            return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mPlaylistAgent,
-                    mVolumeProvider, mSessionActivity, mCallbackExecutor, mCallback).getInstance();
-        }
-    }
-
-    public static final class LibraryRootImpl implements LibraryRootProvider {
-        private final LibraryRoot mInstance;
-        private final String mRootId;
-        private final Bundle mExtras;
-
-        public LibraryRootImpl(LibraryRoot instance, String rootId, Bundle extras) {
-            if (rootId == null) {
-                throw new IllegalArgumentException("rootId shouldn't be null.");
-            }
-            mInstance = instance;
-            mRootId = rootId;
-            mExtras = extras;
-        }
-
-        @Override
-        public String getRootId_impl() {
-            return mRootId;
-        }
-
-        @Override
-        public Bundle getExtras_impl() {
-            return mExtras;
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 72ecf54..cd9edb8 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -35,7 +35,6 @@
 import android.media.MediaController2;
 import android.media.MediaController2.PlaybackInfo;
 import android.media.MediaItem2;
-import android.media.MediaLibraryService2;
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerBase;
 import android.media.MediaPlayerBase.PlayerEventCallback;
@@ -48,7 +47,6 @@
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.OnDataSourceMissingHelper;
 import android.media.MediaSession2.SessionCallback;
-import android.media.MediaSessionService2;
 import android.media.SessionCommand2;
 import android.media.SessionCommandGroup2;
 import android.media.SessionToken2;
@@ -153,21 +151,8 @@
         mPlaylistEventCallback = new MyPlaylistEventCallback(this);
 
         // Infer type from the id and package name.
-        String libraryService = getServiceName(context, MediaLibraryService2.SERVICE_INTERFACE, id);
-        String sessionService = getServiceName(context, MediaSessionService2.SERVICE_INTERFACE, id);
-        if (sessionService != null && libraryService != null) {
-            throw new IllegalArgumentException("Ambiguous session type. Multiple"
-                    + " session services define the same id=" + id);
-        } else if (libraryService != null) {
-            mSessionToken = new SessionToken2Impl(Process.myUid(), TYPE_LIBRARY_SERVICE,
-                    mContext.getPackageName(), libraryService, id, mSessionStub).getInstance();
-        } else if (sessionService != null) {
-            mSessionToken = new SessionToken2Impl(Process.myUid(), TYPE_SESSION_SERVICE,
-                    mContext.getPackageName(), sessionService, id, mSessionStub).getInstance();
-        } else {
-            mSessionToken = new SessionToken2Impl(Process.myUid(), TYPE_SESSION,
-                    mContext.getPackageName(), null, id, mSessionStub).getInstance();
-        }
+        mSessionToken = new SessionToken2Impl(Process.myUid(), TYPE_SESSION,
+                mContext.getPackageName(), null, id, mSessionStub).getInstance();
 
         updatePlayer(player, playlistAgent, volumeProvider);
 
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 11ccd9f..53a5986 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.media.MediaController2;
 import android.media.MediaItem2;
-import android.media.MediaLibraryService2.LibraryRoot;
 import android.media.MediaMetadata2;
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.ControllerInfo;
@@ -43,7 +42,6 @@
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 
-import com.android.media.MediaLibraryService2Impl.MediaLibrarySessionImpl;
 import com.android.media.MediaSession2Impl.CommandButtonImpl;
 import com.android.media.MediaSession2Impl.CommandGroupImpl;
 import com.android.media.MediaSession2Impl.ControllerInfoImpl;
@@ -123,14 +121,6 @@
         return session;
     }
 
-    private MediaLibrarySessionImpl getLibrarySession() throws IllegalStateException {
-        final MediaSession2Impl session = getSession();
-        if (!(session instanceof MediaLibrarySessionImpl)) {
-            throw new RuntimeException("Session isn't a library session");
-        }
-        return (MediaLibrarySessionImpl) session;
-    }
-
     // Get controller if the command from caller to session is able to be handled.
     private ControllerInfo getControllerIfAble(IMediaController2 caller) {
         synchronized (mLock) {
@@ -255,24 +245,6 @@
         });
     }
 
-    private void onBrowserCommand(@NonNull IMediaController2 caller,
-            @NonNull LibrarySessionRunnable runnable) {
-        final MediaLibrarySessionImpl session = getLibrarySession();
-        // TODO(jaewan): Consider command code
-        final ControllerInfo controller = getControllerIfAble(caller);
-        if (session == null || controller == null) {
-            return;
-        }
-        session.getCallbackExecutor().execute(() -> {
-            // TODO(jaewan): Consider command code
-            if (getControllerIfAble(caller) == null) {
-                return;
-            }
-            runnable.run(session, controller);
-        });
-    }
-
-
     private void notifyAll(int commandCode, @NonNull NotifyRunnable runnable) {
         List<ControllerInfo> controllers = getControllers();
         for (int i = 0; i < controllers.size(); i++) {
@@ -751,161 +723,6 @@
     }
 
     //////////////////////////////////////////////////////////////////////////////////////////////
-    // AIDL methods for LibrarySession overrides
-    //////////////////////////////////////////////////////////////////////////////////////////////
-
-    @Override
-    public void getLibraryRoot(final IMediaController2 caller, final Bundle rootHints)
-            throws RuntimeException {
-        onBrowserCommand(caller, (session, controller) -> {
-            final LibraryRoot root = session.getCallback().onGetLibraryRoot(session.getInstance(),
-                    controller, rootHints);
-            notify(controller, (unused, iController) -> {
-                iController.onGetLibraryRootDone(rootHints,
-                        root == null ? null : root.getRootId(),
-                        root == null ? null : root.getExtras());
-            });
-        });
-    }
-
-    @Override
-    public void getItem(final IMediaController2 caller, final String mediaId)
-            throws RuntimeException {
-        onBrowserCommand(caller, (session, controller) -> {
-            if (mediaId == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "mediaId shouldn't be null");
-                }
-                return;
-            }
-            final MediaItem2 result = session.getCallback().onGetItem(session.getInstance(),
-                    controller, mediaId);
-            notify(controller, (unused, iController) -> {
-                iController.onGetItemDone(mediaId, result == null ? null : result.toBundle());
-            });
-        });
-    }
-
-    @Override
-    public void getChildren(final IMediaController2 caller, final String parentId,
-            final int page, final int pageSize, final Bundle extras) throws RuntimeException {
-        onBrowserCommand(caller, (session, controller) -> {
-            if (parentId == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "parentId shouldn't be null");
-                }
-                return;
-            }
-            if (page < 1 || pageSize < 1) {
-                if (DEBUG) {
-                    Log.d(TAG, "Neither page nor pageSize should be less than 1");
-                }
-                return;
-            }
-            List<MediaItem2> result = session.getCallback().onGetChildren(session.getInstance(),
-                    controller, parentId, page, pageSize, extras);
-            if (result != null && result.size() > pageSize) {
-                throw new IllegalArgumentException("onGetChildren() shouldn't return media items "
-                        + "more than pageSize. result.size()=" + result.size() + " pageSize="
-                        + pageSize);
-            }
-            final List<Bundle> bundleList;
-            if (result != null) {
-                bundleList = new ArrayList<>();
-                for (MediaItem2 item : result) {
-                    bundleList.add(item == null ? null : item.toBundle());
-                }
-            } else {
-                bundleList = null;
-            }
-            notify(controller, (unused, iController) -> {
-                iController.onGetChildrenDone(parentId, page, pageSize, bundleList, extras);
-            });
-        });
-    }
-
-    @Override
-    public void search(IMediaController2 caller, String query, Bundle extras) {
-        onBrowserCommand(caller, (session, controller) -> {
-            if (TextUtils.isEmpty(query)) {
-                Log.w(TAG, "search(): Ignoring empty query from " + controller);
-                return;
-            }
-            session.getCallback().onSearch(session.getInstance(), controller, query, extras);
-        });
-    }
-
-    @Override
-    public void getSearchResult(final IMediaController2 caller, final String query,
-            final int page, final int pageSize, final Bundle extras) {
-        onBrowserCommand(caller, (session, controller) -> {
-            if (TextUtils.isEmpty(query)) {
-                Log.w(TAG, "getSearchResult(): Ignoring empty query from " + controller);
-                return;
-            }
-            if (page < 1 || pageSize < 1) {
-                Log.w(TAG, "getSearchResult(): Ignoring negative page / pageSize."
-                        + " page=" + page + " pageSize=" + pageSize + " from " + controller);
-                return;
-            }
-            List<MediaItem2> result = session.getCallback().onGetSearchResult(session.getInstance(),
-                    controller, query, page, pageSize, extras);
-            if (result != null && result.size() > pageSize) {
-                throw new IllegalArgumentException("onGetSearchResult() shouldn't return media "
-                        + "items more than pageSize. result.size()=" + result.size() + " pageSize="
-                        + pageSize);
-            }
-            final List<Bundle> bundleList;
-            if (result != null) {
-                bundleList = new ArrayList<>();
-                for (MediaItem2 item : result) {
-                    bundleList.add(item == null ? null : item.toBundle());
-                }
-            } else {
-                bundleList = null;
-            }
-            notify(controller, (unused, iController) -> {
-                iController.onGetSearchResultDone(query, page, pageSize, bundleList, extras);
-            });
-        });
-    }
-
-    @Override
-    public void subscribe(final IMediaController2 caller, final String parentId,
-            final Bundle option) {
-        onBrowserCommand(caller, (session, controller) -> {
-            if (parentId == null) {
-                Log.w(TAG, "subscribe(): Ignoring null parentId from " + controller);
-                return;
-            }
-            session.getCallback().onSubscribe(session.getInstance(),
-                    controller, parentId, option);
-            synchronized (mLock) {
-                Set<String> subscription = mSubscriptions.get(controller);
-                if (subscription == null) {
-                    subscription = new HashSet<>();
-                    mSubscriptions.put(controller, subscription);
-                }
-                subscription.add(parentId);
-            }
-        });
-    }
-
-    @Override
-    public void unsubscribe(final IMediaController2 caller, final String parentId) {
-        onBrowserCommand(caller, (session, controller) -> {
-            if (parentId == null) {
-                Log.w(TAG, "unsubscribe(): Ignoring null parentId from " + controller);
-                return;
-            }
-            session.getCallback().onUnsubscribe(session.getInstance(), controller, parentId);
-            synchronized (mLock) {
-                mSubscriptions.remove(controller);
-            }
-        });
-    }
-
-    //////////////////////////////////////////////////////////////////////////////////////////////
     // APIs for MediaSession2Impl
     //////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -1055,44 +872,6 @@
     }
 
     //////////////////////////////////////////////////////////////////////////////////////////////
-    // APIs for MediaLibrarySessionImpl
-    //////////////////////////////////////////////////////////////////////////////////////////////
-
-    public void notifySearchResultChanged(ControllerInfo controller, String query, int itemCount,
-            Bundle extras) {
-        notify(controller, (unused, iController) -> {
-            iController.onSearchResultChanged(query, itemCount, extras);
-        });
-    }
-
-    public void notifyChildrenChangedNotLocked(ControllerInfo controller, String parentId,
-            int itemCount, Bundle extras) {
-        notify(controller, (unused, iController) -> {
-            if (isSubscribed(controller, parentId)) {
-                iController.onChildrenChanged(parentId, itemCount, extras);
-            }
-        });
-    }
-
-    public void notifyChildrenChangedNotLocked(String parentId, int itemCount, Bundle extras) {
-        notifyAll((controller, iController) -> {
-            if (isSubscribed(controller, parentId)) {
-                iController.onChildrenChanged(parentId, itemCount, extras);
-            }
-        });
-    }
-
-    private boolean isSubscribed(ControllerInfo controller, String parentId) {
-        synchronized (mLock) {
-            Set<String> subscriptions = mSubscriptions.get(controller);
-            if (subscriptions == null || !subscriptions.contains(parentId)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    //////////////////////////////////////////////////////////////////////////////////////////////
     // Misc
     //////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -1102,11 +881,6 @@
     }
 
     @FunctionalInterface
-    private interface LibrarySessionRunnable {
-        void run(final MediaLibrarySessionImpl session, final ControllerInfo controller);
-    }
-
-    @FunctionalInterface
     private interface NotifyRunnable {
         void run(final ControllerInfo controller,
                 final IMediaController2 iController) throws RemoteException;
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
deleted file mode 100644
index d975839..0000000
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * 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.Notification;
-import android.app.NotificationManager;
-import android.content.Intent;
-import android.media.MediaPlayerBase;
-import android.media.MediaPlayerBase.PlayerEventCallback;
-import android.media.MediaSession2;
-import android.media.MediaSessionService2;
-import android.media.MediaSessionService2.MediaNotification;
-import android.media.SessionToken2;
-import android.media.SessionToken2.TokenType;
-import android.media.update.MediaSessionService2Provider;
-import android.os.IBinder;
-import android.util.Log;
-
-import androidx.annotation.GuardedBy;
-
-// 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. (b/74094611)
-
-    private final MediaSessionService2 mInstance;
-    private final PlayerEventCallback mCallback = new SessionServiceEventCallback();
-
-    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() {
-        // Provide default notification UI later.
-        return null;
-    }
-
-    @Override
-    public void onCreate_impl() {
-        mNotificationManager = (NotificationManager) mInstance.getSystemService(
-                NOTIFICATION_SERVICE);
-        mStartSelfIntent = new Intent(mInstance, mInstance.getClass());
-
-        SessionToken2 token = new SessionToken2(mInstance, mInstance.getPackageName(),
-                mInstance.getClass().getName());
-        if (token.getType() != getSessionType()) {
-            throw new RuntimeException("Expected session service, but was " + token.getType());
-        }
-        mSession = mInstance.onCreateSession(token.getId());
-        if (mSession == null || !token.getId().equals(mSession.getToken().getId())) {
-            throw new RuntimeException("Expected session with id " + token.getId()
-                    + ", but got " + mSession);
-        }
-        // TODO(jaewan): Uncomment here.
-        // mSession.registerPlayerEventCallback(mCallback, mSession.getExecutor());
-    }
-
-    @TokenType int getSessionType() {
-        return SessionToken2.TYPE_SESSION_SERVICE;
-    }
-
-    public IBinder onBind_impl(Intent intent) {
-        if (MediaSessionService2.SERVICE_INTERFACE.equals(intent.getAction())) {
-            return ((MediaSession2Impl) mSession.getProvider()).getSessionStub().asBinder();
-        }
-        return null;
-    }
-
-    private void updateNotification(int playerState) {
-        MediaNotification mediaNotification = mInstance.onUpdateNotification();
-        if (mediaNotification == null) {
-            return;
-        }
-        switch(playerState) {
-            case MediaPlayerBase.PLAYER_STATE_PLAYING:
-                if (!mIsRunningForeground) {
-                    mIsRunningForeground = true;
-                    mInstance.startForegroundService(mStartSelfIntent);
-                    mInstance.startForeground(mediaNotification.getNotificationId(),
-                            mediaNotification.getNotification());
-                    return;
-                }
-                break;
-            case MediaPlayerBase.PLAYER_STATE_IDLE:
-            case MediaPlayerBase.PLAYER_STATE_ERROR:
-                if (mIsRunningForeground) {
-                    mIsRunningForeground = false;
-                    mInstance.stopForeground(true);
-                    return;
-                }
-                break;
-        }
-        mNotificationManager.notify(mediaNotification.getNotificationId(),
-                mediaNotification.getNotification());
-    }
-
-    private class SessionServiceEventCallback extends PlayerEventCallback {
-        @Override
-        public void onPlayerStateChanged(MediaPlayerBase player, int state) {
-            // TODO: Implement this
-            return;
-        }
-    }
-
-    public static class MediaNotificationImpl implements MediaNotificationProvider {
-        private int mNotificationId;
-        private Notification mNotification;
-
-        public MediaNotificationImpl(MediaNotification instance, int notificationId,
-                Notification notification) {
-            if (notification == null) {
-                throw new IllegalArgumentException("notification shouldn't be null");
-            }
-            mNotificationId = notificationId;
-            mNotification = notification;
-        }
-
-        @Override
-        public int getNotificationId_impl() {
-            return mNotificationId;
-        }
-
-        @Override
-        public Notification getNotification_impl() {
-            return mNotification;
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/media/RoutePlayer.java b/packages/MediaComponents/src/com/android/media/RoutePlayer.java
deleted file mode 100644
index ebff0e2..0000000
--- a/packages/MediaComponents/src/com/android/media/RoutePlayer.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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.DataSourceDesc;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.support.mediarouter.media.MediaItemStatus;
-import com.android.support.mediarouter.media.MediaRouter;
-import com.android.support.mediarouter.media.MediaSessionStatus;
-import com.android.support.mediarouter.media.RemotePlaybackClient;
-import com.android.support.mediarouter.media.RemotePlaybackClient.ItemActionCallback;
-import com.android.support.mediarouter.media.RemotePlaybackClient.SessionActionCallback;
-import com.android.support.mediarouter.media.RemotePlaybackClient.StatusCallback;
-
-@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
-public class RoutePlayer extends MediaSession.Callback {
-    public static final long PLAYBACK_ACTIONS = PlaybackState.ACTION_PAUSE
-            | PlaybackState.ACTION_PLAY | PlaybackState.ACTION_SEEK_TO
-            | PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_REWIND;
-
-    private RemotePlaybackClient mClient;
-    private String mSessionId;
-    private String mItemId;
-    private PlayerEventCallback mCallback;
-
-    private StatusCallback mStatusCallback = new StatusCallback() {
-        @Override
-        public void onItemStatusChanged(Bundle data,
-                String sessionId, MediaSessionStatus sessionStatus,
-                String itemId, MediaItemStatus itemStatus) {
-            updateSessionStatus(sessionId, sessionStatus);
-            updateItemStatus(itemId, itemStatus);
-        }
-    };
-
-    public RoutePlayer(Context context, MediaRouter.RouteInfo route) {
-        mClient = new RemotePlaybackClient(context, route);
-        mClient.setStatusCallback(mStatusCallback);
-        mClient.startSession(null, new SessionActionCallback() {
-            @Override
-            public void onResult(Bundle data,
-                    String sessionId, MediaSessionStatus sessionStatus) {
-                updateSessionStatus(sessionId, sessionStatus);
-            }
-        });
-    }
-
-    @Override
-    public void onPlay() {
-        mClient.resume(null, new SessionActionCallback() {
-            @Override
-            public void onResult(Bundle data,
-                    String sessionId, MediaSessionStatus sessionStatus) {
-                updateSessionStatus(sessionId, sessionStatus);
-            }
-        });
-    }
-
-    @Override
-    public void onPause() {
-        mClient.pause(null, new SessionActionCallback() {
-            @Override
-            public void onResult(Bundle data,
-                    String sessionId, MediaSessionStatus sessionStatus) {
-                updateSessionStatus(sessionId, sessionStatus);
-            }
-        });
-    }
-
-    @Override
-    public void onSeekTo(long pos) {
-        mClient.seek(mItemId, pos, null, new ItemActionCallback() {
-            @Override
-            public void onResult(Bundle data,
-                    String sessionId, MediaSessionStatus sessionStatus,
-                    String itemId, MediaItemStatus itemStatus) {
-                updateSessionStatus(sessionId, sessionStatus);
-                updateItemStatus(itemId, itemStatus);
-            }
-        });
-    }
-
-    @Override
-    public void onStop() {
-        mClient.stop(null, new SessionActionCallback() {
-            @Override
-            public void onResult(Bundle data,
-                    String sessionId, MediaSessionStatus sessionStatus) {
-                updateSessionStatus(sessionId, sessionStatus);
-            }
-        });
-    }
-
-    public void setPlayerEventCallback(PlayerEventCallback callback) {
-        mCallback = callback;
-    }
-
-    public void openVideo(DataSourceDesc dsd) {
-        mClient.play(dsd.getUri(), "video/mp4", null, 0, null, new ItemActionCallback() {
-            @Override
-            public void onResult(Bundle data,
-                    String sessionId, MediaSessionStatus sessionStatus,
-                    String itemId, MediaItemStatus itemStatus) {
-                updateSessionStatus(sessionId, sessionStatus);
-                updateItemStatus(itemId, itemStatus);
-                playInternal(dsd.getUri());
-            }
-        });
-    }
-
-    public void release() {
-        if (mClient != null) {
-            mClient.release();
-            mClient = null;
-        }
-        if (mCallback != null) {
-            mCallback = null;
-        }
-    }
-
-    private void playInternal(Uri uri) {
-        mClient.play(uri, "video/mp4", null, 0, null, new ItemActionCallback() {
-            @Override
-            public void onResult(Bundle data,
-                    String sessionId, MediaSessionStatus sessionStatus,
-                    String itemId, MediaItemStatus itemStatus) {
-                updateSessionStatus(sessionId, sessionStatus);
-                updateItemStatus(itemId, itemStatus);
-            }
-        });
-    }
-
-    private void updateSessionStatus(String sessionId, MediaSessionStatus sessionStatus) {
-        mSessionId = sessionId;
-    }
-
-    private void updateItemStatus(String itemId, MediaItemStatus itemStatus) {
-        mItemId = itemId;
-        if (itemStatus == null || mCallback == null) return;
-        mCallback.onPlayerStateChanged(itemStatus);
-    }
-
-    public static abstract class PlayerEventCallback {
-        public void onPlayerStateChanged(MediaItemStatus itemStatus) { }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
index f792712..396ebbb 100644
--- a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
@@ -25,8 +25,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
-import android.media.MediaLibraryService2;
-import android.media.MediaSessionService2;
 import android.media.SessionToken2;
 import android.media.SessionToken2.TokenType;
 import android.media.update.SessionToken2Provider;
@@ -75,24 +73,8 @@
             }
         }
         mUid = uid;
-
-        // Infer id and type from package name and service name
-        // TODO(jaewan): Handle multi-user.
-        String id = getSessionIdFromService(manager, MediaLibraryService2.SERVICE_INTERFACE,
-                packageName, serviceName);
-        if (id != null) {
-            mId = id;
-            mType = TYPE_LIBRARY_SERVICE;
-        } else {
-            // retry with session service
-            mId = getSessionIdFromService(manager, MediaSessionService2.SERVICE_INTERFACE,
-                    packageName, serviceName);
-            mType = TYPE_SESSION_SERVICE;
-        }
-        if (mId == null) {
-            throw new IllegalArgumentException("service " + serviceName + " doesn't implement"
-                    + " session service nor library service. Use service's full name.");
-        }
+        mId = null;
+        mType = -1;
         mPackageName = packageName;
         mServiceName = serviceName;
         mSessionBinder = null;
@@ -110,38 +92,13 @@
         mInstance = new SessionToken2(this);
     }
 
-    private static String getSessionIdFromService(PackageManager manager, String serviceInterface,
-            String packageName, String serviceName) {
-        Intent serviceIntent = new Intent(serviceInterface);
-        serviceIntent.setPackage(packageName);
-        // Use queryIntentServices to find services with MediaLibraryService2.SERVICE_INTERFACE.
-        // We cannot use resolveService with intent specified class name, because resolveService
-        // ignores actions if Intent.setClassName() is specified.
-        List<ResolveInfo> list = manager.queryIntentServices(
-                serviceIntent, PackageManager.GET_META_DATA);
-        if (list != null) {
-            for (int i = 0; i < list.size(); i++) {
-                ResolveInfo resolveInfo = list.get(i);
-                if (resolveInfo == null || resolveInfo.serviceInfo == null) {
-                    continue;
-                }
-                if (TextUtils.equals(resolveInfo.serviceInfo.name, serviceName)) {
-                    return getSessionId(resolveInfo);
-                }
-            }
-        }
-        return null;
-    }
-
     public static String getSessionId(ResolveInfo resolveInfo) {
         if (resolveInfo == null || resolveInfo.serviceInfo == null) {
             return null;
         } else if (resolveInfo.serviceInfo.metaData == null) {
             return "";
-        } else {
-            return resolveInfo.serviceInfo.metaData.getString(
-                    MediaSessionService2.SERVICE_META_DATA, "");
         }
+        return null;
     }
 
     public SessionToken2 getInstance() {
diff --git a/packages/MediaComponents/src/com/android/media/subtitle/ClosedCaptionRenderer.java b/packages/MediaComponents/src/com/android/media/subtitle/ClosedCaptionRenderer.java
deleted file mode 100644
index ff7eec9..0000000
--- a/packages/MediaComponents/src/com/android/media/subtitle/ClosedCaptionRenderer.java
+++ /dev/null
@@ -1,1501 +0,0 @@
-/*
- * 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.subtitle;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.media.MediaFormat;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.TextPaint;
-import android.text.style.CharacterStyle;
-import android.text.style.StyleSpan;
-import android.text.style.UnderlineSpan;
-import android.text.style.UpdateAppearance;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.CaptioningManager;
-import android.view.accessibility.CaptioningManager.CaptionStyle;
-import android.view.accessibility.CaptioningManager.CaptioningChangeListener;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Vector;
-
-// Note: This is forked from android.media.ClosedCaptionRenderer since P
-public class ClosedCaptionRenderer extends SubtitleController.Renderer {
-    private final Context mContext;
-    private Cea608CCWidget mCCWidget;
-
-    public ClosedCaptionRenderer(Context context) {
-        mContext = context;
-    }
-
-    @Override
-    public boolean supports(MediaFormat format) {
-        if (format.containsKey(MediaFormat.KEY_MIME)) {
-            String mimeType = format.getString(MediaFormat.KEY_MIME);
-            return MediaFormat.MIMETYPE_TEXT_CEA_608.equals(mimeType);
-        }
-        return false;
-    }
-
-    @Override
-    public SubtitleTrack createTrack(MediaFormat format) {
-        String mimeType = format.getString(MediaFormat.KEY_MIME);
-        if (MediaFormat.MIMETYPE_TEXT_CEA_608.equals(mimeType)) {
-            if (mCCWidget == null) {
-                mCCWidget = new Cea608CCWidget(mContext);
-            }
-            return new Cea608CaptionTrack(mCCWidget, format);
-        }
-        throw new RuntimeException("No matching format: " + format.toString());
-    }
-}
-
-class Cea608CaptionTrack extends SubtitleTrack {
-    private final Cea608CCParser mCCParser;
-    private final Cea608CCWidget mRenderingWidget;
-
-    Cea608CaptionTrack(Cea608CCWidget renderingWidget, MediaFormat format) {
-        super(format);
-
-        mRenderingWidget = renderingWidget;
-        mCCParser = new Cea608CCParser(mRenderingWidget);
-    }
-
-    @Override
-    public void onData(byte[] data, boolean eos, long runID) {
-        mCCParser.parse(data);
-    }
-
-    @Override
-    public RenderingWidget getRenderingWidget() {
-        return mRenderingWidget;
-    }
-
-    @Override
-    public void updateView(Vector<Cue> activeCues) {
-        // Overriding with NO-OP, CC rendering by-passes this
-    }
-}
-
-/**
- * Abstract widget class to render a closed caption track.
- */
-abstract class ClosedCaptionWidget extends ViewGroup implements SubtitleTrack.RenderingWidget {
-
-    interface ClosedCaptionLayout {
-        void setCaptionStyle(CaptionStyle captionStyle);
-        void setFontScale(float scale);
-    }
-
-    private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT;
-
-    /** Captioning manager, used to obtain and track caption properties. */
-    private final CaptioningManager mManager;
-
-    /** Current caption style. */
-    protected CaptionStyle mCaptionStyle;
-
-    /** Callback for rendering changes. */
-    protected OnChangedListener mListener;
-
-    /** Concrete layout of CC. */
-    protected ClosedCaptionLayout mClosedCaptionLayout;
-
-    /** Whether a caption style change listener is registered. */
-    private boolean mHasChangeListener;
-
-    public ClosedCaptionWidget(Context context) {
-        this(context, null);
-    }
-
-    public ClosedCaptionWidget(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyle) {
-        this(context, attrs, defStyle, 0);
-    }
-
-    public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        // Cannot render text over video when layer type is hardware.
-        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
-
-        mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
-        mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(mManager.getUserStyle());
-
-        mClosedCaptionLayout = createCaptionLayout(context);
-        mClosedCaptionLayout.setCaptionStyle(mCaptionStyle);
-        mClosedCaptionLayout.setFontScale(mManager.getFontScale());
-        addView((ViewGroup) mClosedCaptionLayout, LayoutParams.MATCH_PARENT,
-                LayoutParams.MATCH_PARENT);
-
-        requestLayout();
-    }
-
-    public abstract ClosedCaptionLayout createCaptionLayout(Context context);
-
-    @Override
-    public void setOnChangedListener(OnChangedListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    public void setSize(int width, int height) {
-        final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
-        final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
-
-        measure(widthSpec, heightSpec);
-        layout(0, 0, width, height);
-    }
-
-    @Override
-    public void setVisible(boolean visible) {
-        if (visible) {
-            setVisibility(View.VISIBLE);
-        } else {
-            setVisibility(View.GONE);
-        }
-
-        manageChangeListener();
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        manageChangeListener();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-
-        manageChangeListener();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        ((ViewGroup) mClosedCaptionLayout).measure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        ((ViewGroup) mClosedCaptionLayout).layout(l, t, r, b);
-    }
-
-    /**
-     * Manages whether this renderer is listening for caption style changes.
-     */
-    private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() {
-        @Override
-        public void onUserStyleChanged(CaptionStyle userStyle) {
-            mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(userStyle);
-            mClosedCaptionLayout.setCaptionStyle(mCaptionStyle);
-        }
-
-        @Override
-        public void onFontScaleChanged(float fontScale) {
-            mClosedCaptionLayout.setFontScale(fontScale);
-        }
-    };
-
-    private void manageChangeListener() {
-        final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE;
-        if (mHasChangeListener != needsListener) {
-            mHasChangeListener = needsListener;
-
-            if (needsListener) {
-                mManager.addCaptioningChangeListener(mCaptioningListener);
-            } else {
-                mManager.removeCaptioningChangeListener(mCaptioningListener);
-            }
-        }
-    }
-}
-
-/**
- * CCParser processes CEA-608 closed caption data.
- *
- * It calls back into OnDisplayChangedListener upon
- * display change with styled text for rendering.
- *
- */
-class Cea608CCParser {
-    public static final int MAX_ROWS = 15;
-    public static final int MAX_COLS = 32;
-
-    private static final String TAG = "Cea608CCParser";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private static final int INVALID = -1;
-
-    // EIA-CEA-608: Table 70 - Control Codes
-    private static final int RCL = 0x20;
-    private static final int BS  = 0x21;
-    private static final int AOF = 0x22;
-    private static final int AON = 0x23;
-    private static final int DER = 0x24;
-    private static final int RU2 = 0x25;
-    private static final int RU3 = 0x26;
-    private static final int RU4 = 0x27;
-    private static final int FON = 0x28;
-    private static final int RDC = 0x29;
-    private static final int TR  = 0x2a;
-    private static final int RTD = 0x2b;
-    private static final int EDM = 0x2c;
-    private static final int CR  = 0x2d;
-    private static final int ENM = 0x2e;
-    private static final int EOC = 0x2f;
-
-    // Transparent Space
-    private static final char TS = '\u00A0';
-
-    // Captioning Modes
-    private static final int MODE_UNKNOWN = 0;
-    private static final int MODE_PAINT_ON = 1;
-    private static final int MODE_ROLL_UP = 2;
-    private static final int MODE_POP_ON = 3;
-    private static final int MODE_TEXT = 4;
-
-    private final DisplayListener mListener;
-
-    private int mMode = MODE_PAINT_ON;
-    private int mRollUpSize = 4;
-    private int mPrevCtrlCode = INVALID;
-
-    private CCMemory mDisplay = new CCMemory();
-    private CCMemory mNonDisplay = new CCMemory();
-    private CCMemory mTextMem = new CCMemory();
-
-    Cea608CCParser(DisplayListener listener) {
-        mListener = listener;
-    }
-
-    public void parse(byte[] data) {
-        CCData[] ccData = CCData.fromByteArray(data);
-
-        for (int i = 0; i < ccData.length; i++) {
-            if (DEBUG) {
-                Log.d(TAG, ccData[i].toString());
-            }
-
-            if (handleCtrlCode(ccData[i])
-                    || handleTabOffsets(ccData[i])
-                    || handlePACCode(ccData[i])
-                    || handleMidRowCode(ccData[i])) {
-                continue;
-            }
-
-            handleDisplayableChars(ccData[i]);
-        }
-    }
-
-    interface DisplayListener {
-        void onDisplayChanged(SpannableStringBuilder[] styledTexts);
-        CaptionStyle getCaptionStyle();
-    }
-
-    private CCMemory getMemory() {
-        // get the CC memory to operate on for current mode
-        switch (mMode) {
-        case MODE_POP_ON:
-            return mNonDisplay;
-        case MODE_TEXT:
-            // TODO(chz): support only caption mode for now,
-            // in text mode, dump everything to text mem.
-            return mTextMem;
-        case MODE_PAINT_ON:
-        case MODE_ROLL_UP:
-            return mDisplay;
-        default:
-            Log.w(TAG, "unrecoginized mode: " + mMode);
-        }
-        return mDisplay;
-    }
-
-    private boolean handleDisplayableChars(CCData ccData) {
-        if (!ccData.isDisplayableChar()) {
-            return false;
-        }
-
-        // Extended char includes 1 automatic backspace
-        if (ccData.isExtendedChar()) {
-            getMemory().bs();
-        }
-
-        getMemory().writeText(ccData.getDisplayText());
-
-        if (mMode == MODE_PAINT_ON || mMode == MODE_ROLL_UP) {
-            updateDisplay();
-        }
-
-        return true;
-    }
-
-    private boolean handleMidRowCode(CCData ccData) {
-        StyleCode m = ccData.getMidRow();
-        if (m != null) {
-            getMemory().writeMidRowCode(m);
-            return true;
-        }
-        return false;
-    }
-
-    private boolean handlePACCode(CCData ccData) {
-        PAC pac = ccData.getPAC();
-
-        if (pac != null) {
-            if (mMode == MODE_ROLL_UP) {
-                getMemory().moveBaselineTo(pac.getRow(), mRollUpSize);
-            }
-            getMemory().writePAC(pac);
-            return true;
-        }
-
-        return false;
-    }
-
-    private boolean handleTabOffsets(CCData ccData) {
-        int tabs = ccData.getTabOffset();
-
-        if (tabs > 0) {
-            getMemory().tab(tabs);
-            return true;
-        }
-
-        return false;
-    }
-
-    private boolean handleCtrlCode(CCData ccData) {
-        int ctrlCode = ccData.getCtrlCode();
-
-        if (mPrevCtrlCode != INVALID && mPrevCtrlCode == ctrlCode) {
-            // discard double ctrl codes (but if there's a 3rd one, we still take that)
-            mPrevCtrlCode = INVALID;
-            return true;
-        }
-
-        switch(ctrlCode) {
-        case RCL:
-            // select pop-on style
-            mMode = MODE_POP_ON;
-            break;
-        case BS:
-            getMemory().bs();
-            break;
-        case DER:
-            getMemory().der();
-            break;
-        case RU2:
-        case RU3:
-        case RU4:
-            mRollUpSize = (ctrlCode - 0x23);
-            // erase memory if currently in other style
-            if (mMode != MODE_ROLL_UP) {
-                mDisplay.erase();
-                mNonDisplay.erase();
-            }
-            // select roll-up style
-            mMode = MODE_ROLL_UP;
-            break;
-        case FON:
-            Log.i(TAG, "Flash On");
-            break;
-        case RDC:
-            // select paint-on style
-            mMode = MODE_PAINT_ON;
-            break;
-        case TR:
-            mMode = MODE_TEXT;
-            mTextMem.erase();
-            break;
-        case RTD:
-            mMode = MODE_TEXT;
-            break;
-        case EDM:
-            // erase display memory
-            mDisplay.erase();
-            updateDisplay();
-            break;
-        case CR:
-            if (mMode == MODE_ROLL_UP) {
-                getMemory().rollUp(mRollUpSize);
-            } else {
-                getMemory().cr();
-            }
-            if (mMode == MODE_ROLL_UP) {
-                updateDisplay();
-            }
-            break;
-        case ENM:
-            // erase non-display memory
-            mNonDisplay.erase();
-            break;
-        case EOC:
-            // swap display/non-display memory
-            swapMemory();
-            // switch to pop-on style
-            mMode = MODE_POP_ON;
-            updateDisplay();
-            break;
-        case INVALID:
-        default:
-            mPrevCtrlCode = INVALID;
-            return false;
-        }
-
-        mPrevCtrlCode = ctrlCode;
-
-        // handled
-        return true;
-    }
-
-    private void updateDisplay() {
-        if (mListener != null) {
-            CaptionStyle captionStyle = mListener.getCaptionStyle();
-            mListener.onDisplayChanged(mDisplay.getStyledText(captionStyle));
-        }
-    }
-
-    private void swapMemory() {
-        CCMemory temp = mDisplay;
-        mDisplay = mNonDisplay;
-        mNonDisplay = temp;
-    }
-
-    private static class StyleCode {
-        static final int COLOR_WHITE = 0;
-        static final int COLOR_GREEN = 1;
-        static final int COLOR_BLUE = 2;
-        static final int COLOR_CYAN = 3;
-        static final int COLOR_RED = 4;
-        static final int COLOR_YELLOW = 5;
-        static final int COLOR_MAGENTA = 6;
-        static final int COLOR_INVALID = 7;
-
-        static final int STYLE_ITALICS   = 0x00000001;
-        static final int STYLE_UNDERLINE = 0x00000002;
-
-        static final String[] mColorMap = {
-            "WHITE", "GREEN", "BLUE", "CYAN", "RED", "YELLOW", "MAGENTA", "INVALID"
-        };
-
-        final int mStyle;
-        final int mColor;
-
-        static StyleCode fromByte(byte data2) {
-            int style = 0;
-            int color = (data2 >> 1) & 0x7;
-
-            if ((data2 & 0x1) != 0) {
-                style |= STYLE_UNDERLINE;
-            }
-
-            if (color == COLOR_INVALID) {
-                // WHITE ITALICS
-                color = COLOR_WHITE;
-                style |= STYLE_ITALICS;
-            }
-
-            return new StyleCode(style, color);
-        }
-
-        StyleCode(int style, int color) {
-            mStyle = style;
-            mColor = color;
-        }
-
-        boolean isItalics() {
-            return (mStyle & STYLE_ITALICS) != 0;
-        }
-
-        boolean isUnderline() {
-            return (mStyle & STYLE_UNDERLINE) != 0;
-        }
-
-        int getColor() {
-            return mColor;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder str = new StringBuilder();
-            str.append("{");
-            str.append(mColorMap[mColor]);
-            if ((mStyle & STYLE_ITALICS) != 0) {
-                str.append(", ITALICS");
-            }
-            if ((mStyle & STYLE_UNDERLINE) != 0) {
-                str.append(", UNDERLINE");
-            }
-            str.append("}");
-
-            return str.toString();
-        }
-    }
-
-    private static class PAC extends StyleCode {
-        final int mRow;
-        final int mCol;
-
-        static PAC fromBytes(byte data1, byte data2) {
-            int[] rowTable = {11, 1, 3, 12, 14, 5, 7, 9};
-            int row = rowTable[data1 & 0x07] + ((data2 & 0x20) >> 5);
-            int style = 0;
-            if ((data2 & 1) != 0) {
-                style |= STYLE_UNDERLINE;
-            }
-            if ((data2 & 0x10) != 0) {
-                // indent code
-                int indent = (data2 >> 1) & 0x7;
-                return new PAC(row, indent * 4, style, COLOR_WHITE);
-            } else {
-                // style code
-                int color = (data2 >> 1) & 0x7;
-
-                if (color == COLOR_INVALID) {
-                    // WHITE ITALICS
-                    color = COLOR_WHITE;
-                    style |= STYLE_ITALICS;
-                }
-                return new PAC(row, -1, style, color);
-            }
-        }
-
-        PAC(int row, int col, int style, int color) {
-            super(style, color);
-            mRow = row;
-            mCol = col;
-        }
-
-        boolean isIndentPAC() {
-            return (mCol >= 0);
-        }
-
-        int getRow() {
-            return mRow;
-        }
-
-        int getCol() {
-            return mCol;
-        }
-
-        @Override
-        public String toString() {
-            return String.format("{%d, %d}, %s",
-                    mRow, mCol, super.toString());
-        }
-    }
-
-    /**
-     * Mutable version of BackgroundSpan to facilitate text rendering with edge styles.
-     */
-    public static class MutableBackgroundColorSpan extends CharacterStyle
-            implements UpdateAppearance {
-        private int mColor;
-
-        public MutableBackgroundColorSpan(int color) {
-            mColor = color;
-        }
-
-        public void setBackgroundColor(int color) {
-            mColor = color;
-        }
-
-        public int getBackgroundColor() {
-            return mColor;
-        }
-
-        @Override
-        public void updateDrawState(TextPaint ds) {
-            ds.bgColor = mColor;
-        }
-    }
-
-    /* CCLineBuilder keeps track of displayable chars, as well as
-     * MidRow styles and PACs, for a single line of CC memory.
-     *
-     * It generates styled text via getStyledText() method.
-     */
-    private static class CCLineBuilder {
-        private final StringBuilder mDisplayChars;
-        private final StyleCode[] mMidRowStyles;
-        private final StyleCode[] mPACStyles;
-
-        CCLineBuilder(String str) {
-            mDisplayChars = new StringBuilder(str);
-            mMidRowStyles = new StyleCode[mDisplayChars.length()];
-            mPACStyles = new StyleCode[mDisplayChars.length()];
-        }
-
-        void setCharAt(int index, char ch) {
-            mDisplayChars.setCharAt(index, ch);
-            mMidRowStyles[index] = null;
-        }
-
-        void setMidRowAt(int index, StyleCode m) {
-            mDisplayChars.setCharAt(index, ' ');
-            mMidRowStyles[index] = m;
-        }
-
-        void setPACAt(int index, PAC pac) {
-            mPACStyles[index] = pac;
-        }
-
-        char charAt(int index) {
-            return mDisplayChars.charAt(index);
-        }
-
-        int length() {
-            return mDisplayChars.length();
-        }
-
-        void applyStyleSpan(
-                SpannableStringBuilder styledText,
-                StyleCode s, int start, int end) {
-            if (s.isItalics()) {
-                styledText.setSpan(
-                        new StyleSpan(android.graphics.Typeface.ITALIC),
-                        start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-            }
-            if (s.isUnderline()) {
-                styledText.setSpan(
-                        new UnderlineSpan(),
-                        start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-            }
-        }
-
-        SpannableStringBuilder getStyledText(CaptionStyle captionStyle) {
-            SpannableStringBuilder styledText = new SpannableStringBuilder(mDisplayChars);
-            int start = -1, next = 0;
-            int styleStart = -1;
-            StyleCode curStyle = null;
-            while (next < mDisplayChars.length()) {
-                StyleCode newStyle = null;
-                if (mMidRowStyles[next] != null) {
-                    // apply mid-row style change
-                    newStyle = mMidRowStyles[next];
-                } else if (mPACStyles[next] != null
-                    && (styleStart < 0 || start < 0)) {
-                    // apply PAC style change, only if:
-                    // 1. no style set, or
-                    // 2. style set, but prev char is none-displayable
-                    newStyle = mPACStyles[next];
-                }
-                if (newStyle != null) {
-                    curStyle = newStyle;
-                    if (styleStart >= 0 && start >= 0) {
-                        applyStyleSpan(styledText, newStyle, styleStart, next);
-                    }
-                    styleStart = next;
-                }
-
-                if (mDisplayChars.charAt(next) != TS) {
-                    if (start < 0) {
-                        start = next;
-                    }
-                } else if (start >= 0) {
-                    int expandedStart = mDisplayChars.charAt(start) == ' ' ? start : start - 1;
-                    int expandedEnd = mDisplayChars.charAt(next - 1) == ' ' ? next : next + 1;
-                    styledText.setSpan(
-                            new MutableBackgroundColorSpan(captionStyle.backgroundColor),
-                            expandedStart, expandedEnd,
-                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-                    if (styleStart >= 0) {
-                        applyStyleSpan(styledText, curStyle, styleStart, expandedEnd);
-                    }
-                    start = -1;
-                }
-                next++;
-            }
-
-            return styledText;
-        }
-    }
-
-    /*
-     * CCMemory models a console-style display.
-     */
-    private static class CCMemory {
-        private final String mBlankLine;
-        private final CCLineBuilder[] mLines = new CCLineBuilder[MAX_ROWS + 2];
-        private int mRow;
-        private int mCol;
-
-        CCMemory() {
-            char[] blank = new char[MAX_COLS + 2];
-            Arrays.fill(blank, TS);
-            mBlankLine = new String(blank);
-        }
-
-        void erase() {
-            // erase all lines
-            for (int i = 0; i < mLines.length; i++) {
-                mLines[i] = null;
-            }
-            mRow = MAX_ROWS;
-            mCol = 1;
-        }
-
-        void der() {
-            if (mLines[mRow] != null) {
-                for (int i = 0; i < mCol; i++) {
-                    if (mLines[mRow].charAt(i) != TS) {
-                        for (int j = mCol; j < mLines[mRow].length(); j++) {
-                            mLines[j].setCharAt(j, TS);
-                        }
-                        return;
-                    }
-                }
-                mLines[mRow] = null;
-            }
-        }
-
-        void tab(int tabs) {
-            moveCursorByCol(tabs);
-        }
-
-        void bs() {
-            moveCursorByCol(-1);
-            if (mLines[mRow] != null) {
-                mLines[mRow].setCharAt(mCol, TS);
-                if (mCol == MAX_COLS - 1) {
-                    // Spec recommendation:
-                    // if cursor was at col 32, move cursor
-                    // back to col 31 and erase both col 31&32
-                    mLines[mRow].setCharAt(MAX_COLS, TS);
-                }
-            }
-        }
-
-        void cr() {
-            moveCursorTo(mRow + 1, 1);
-        }
-
-        void rollUp(int windowSize) {
-            int i;
-            for (i = 0; i <= mRow - windowSize; i++) {
-                mLines[i] = null;
-            }
-            int startRow = mRow - windowSize + 1;
-            if (startRow < 1) {
-                startRow = 1;
-            }
-            for (i = startRow; i < mRow; i++) {
-                mLines[i] = mLines[i + 1];
-            }
-            for (i = mRow; i < mLines.length; i++) {
-                // clear base row
-                mLines[i] = null;
-            }
-            // default to col 1, in case PAC is not sent
-            mCol = 1;
-        }
-
-        void writeText(String text) {
-            for (int i = 0; i < text.length(); i++) {
-                getLineBuffer(mRow).setCharAt(mCol, text.charAt(i));
-                moveCursorByCol(1);
-            }
-        }
-
-        void writeMidRowCode(StyleCode m) {
-            getLineBuffer(mRow).setMidRowAt(mCol, m);
-            moveCursorByCol(1);
-        }
-
-        void writePAC(PAC pac) {
-            if (pac.isIndentPAC()) {
-                moveCursorTo(pac.getRow(), pac.getCol());
-            } else {
-                moveCursorTo(pac.getRow(), 1);
-            }
-            getLineBuffer(mRow).setPACAt(mCol, pac);
-        }
-
-        SpannableStringBuilder[] getStyledText(CaptionStyle captionStyle) {
-            ArrayList<SpannableStringBuilder> rows = new ArrayList<>(MAX_ROWS);
-            for (int i = 1; i <= MAX_ROWS; i++) {
-                rows.add(mLines[i] != null ?
-                        mLines[i].getStyledText(captionStyle) : null);
-            }
-            return rows.toArray(new SpannableStringBuilder[MAX_ROWS]);
-        }
-
-        private static int clamp(int x, int min, int max) {
-            return x < min ? min : (x > max ? max : x);
-        }
-
-        private void moveCursorTo(int row, int col) {
-            mRow = clamp(row, 1, MAX_ROWS);
-            mCol = clamp(col, 1, MAX_COLS);
-        }
-
-        private void moveCursorToRow(int row) {
-            mRow = clamp(row, 1, MAX_ROWS);
-        }
-
-        private void moveCursorByCol(int col) {
-            mCol = clamp(mCol + col, 1, MAX_COLS);
-        }
-
-        private void moveBaselineTo(int baseRow, int windowSize) {
-            if (mRow == baseRow) {
-                return;
-            }
-            int actualWindowSize = windowSize;
-            if (baseRow < actualWindowSize) {
-                actualWindowSize = baseRow;
-            }
-            if (mRow < actualWindowSize) {
-                actualWindowSize = mRow;
-            }
-
-            int i;
-            if (baseRow < mRow) {
-                // copy from bottom to top row
-                for (i = actualWindowSize - 1; i >= 0; i--) {
-                    mLines[baseRow - i] = mLines[mRow - i];
-                }
-            } else {
-                // copy from top to bottom row
-                for (i = 0; i < actualWindowSize; i++) {
-                    mLines[baseRow - i] = mLines[mRow - i];
-                }
-            }
-            // clear rest of the rows
-            for (i = 0; i <= baseRow - windowSize; i++) {
-                mLines[i] = null;
-            }
-            for (i = baseRow + 1; i < mLines.length; i++) {
-                mLines[i] = null;
-            }
-        }
-
-        private CCLineBuilder getLineBuffer(int row) {
-            if (mLines[row] == null) {
-                mLines[row] = new CCLineBuilder(mBlankLine);
-            }
-            return mLines[row];
-        }
-    }
-
-    /*
-     * CCData parses the raw CC byte pair into displayable chars,
-     * misc control codes, Mid-Row or Preamble Address Codes.
-     */
-    private static class CCData {
-        private final byte mType;
-        private final byte mData1;
-        private final byte mData2;
-
-        private static final String[] mCtrlCodeMap = {
-            "RCL", "BS" , "AOF", "AON",
-            "DER", "RU2", "RU3", "RU4",
-            "FON", "RDC", "TR" , "RTD",
-            "EDM", "CR" , "ENM", "EOC",
-        };
-
-        private static final String[] mSpecialCharMap = {
-            "\u00AE",
-            "\u00B0",
-            "\u00BD",
-            "\u00BF",
-            "\u2122",
-            "\u00A2",
-            "\u00A3",
-            "\u266A", // Eighth note
-            "\u00E0",
-            "\u00A0", // Transparent space
-            "\u00E8",
-            "\u00E2",
-            "\u00EA",
-            "\u00EE",
-            "\u00F4",
-            "\u00FB",
-        };
-
-        private static final String[] mSpanishCharMap = {
-            // Spanish and misc chars
-            "\u00C1", // A
-            "\u00C9", // E
-            "\u00D3", // I
-            "\u00DA", // O
-            "\u00DC", // U
-            "\u00FC", // u
-            "\u2018", // opening single quote
-            "\u00A1", // inverted exclamation mark
-            "*",
-            "'",
-            "\u2014", // em dash
-            "\u00A9", // Copyright
-            "\u2120", // Servicemark
-            "\u2022", // round bullet
-            "\u201C", // opening double quote
-            "\u201D", // closing double quote
-            // French
-            "\u00C0",
-            "\u00C2",
-            "\u00C7",
-            "\u00C8",
-            "\u00CA",
-            "\u00CB",
-            "\u00EB",
-            "\u00CE",
-            "\u00CF",
-            "\u00EF",
-            "\u00D4",
-            "\u00D9",
-            "\u00F9",
-            "\u00DB",
-            "\u00AB",
-            "\u00BB"
-        };
-
-        private static final String[] mProtugueseCharMap = {
-            // Portuguese
-            "\u00C3",
-            "\u00E3",
-            "\u00CD",
-            "\u00CC",
-            "\u00EC",
-            "\u00D2",
-            "\u00F2",
-            "\u00D5",
-            "\u00F5",
-            "{",
-            "}",
-            "\\",
-            "^",
-            "_",
-            "|",
-            "~",
-            // German and misc chars
-            "\u00C4",
-            "\u00E4",
-            "\u00D6",
-            "\u00F6",
-            "\u00DF",
-            "\u00A5",
-            "\u00A4",
-            "\u2502", // vertical bar
-            "\u00C5",
-            "\u00E5",
-            "\u00D8",
-            "\u00F8",
-            "\u250C", // top-left corner
-            "\u2510", // top-right corner
-            "\u2514", // lower-left corner
-            "\u2518", // lower-right corner
-        };
-
-        static CCData[] fromByteArray(byte[] data) {
-            CCData[] ccData = new CCData[data.length / 3];
-
-            for (int i = 0; i < ccData.length; i++) {
-                ccData[i] = new CCData(
-                        data[i * 3],
-                        data[i * 3 + 1],
-                        data[i * 3 + 2]);
-            }
-
-            return ccData;
-        }
-
-        CCData(byte type, byte data1, byte data2) {
-            mType = type;
-            mData1 = data1;
-            mData2 = data2;
-        }
-
-        int getCtrlCode() {
-            if ((mData1 == 0x14 || mData1 == 0x1c)
-                    && mData2 >= 0x20 && mData2 <= 0x2f) {
-                return mData2;
-            }
-            return INVALID;
-        }
-
-        StyleCode getMidRow() {
-            // only support standard Mid-row codes, ignore
-            // optional background/foreground mid-row codes
-            if ((mData1 == 0x11 || mData1 == 0x19)
-                    && mData2 >= 0x20 && mData2 <= 0x2f) {
-                return StyleCode.fromByte(mData2);
-            }
-            return null;
-        }
-
-        PAC getPAC() {
-            if ((mData1 & 0x70) == 0x10
-                    && (mData2 & 0x40) == 0x40
-                    && ((mData1 & 0x07) != 0 || (mData2 & 0x20) == 0)) {
-                return PAC.fromBytes(mData1, mData2);
-            }
-            return null;
-        }
-
-        int getTabOffset() {
-            if ((mData1 == 0x17 || mData1 == 0x1f)
-                    && mData2 >= 0x21 && mData2 <= 0x23) {
-                return mData2 & 0x3;
-            }
-            return 0;
-        }
-
-        boolean isDisplayableChar() {
-            return isBasicChar() || isSpecialChar() || isExtendedChar();
-        }
-
-        String getDisplayText() {
-            String str = getBasicChars();
-
-            if (str == null) {
-                str =  getSpecialChar();
-
-                if (str == null) {
-                    str = getExtendedChar();
-                }
-            }
-
-            return str;
-        }
-
-        private String ctrlCodeToString(int ctrlCode) {
-            return mCtrlCodeMap[ctrlCode - 0x20];
-        }
-
-        private boolean isBasicChar() {
-            return mData1 >= 0x20 && mData1 <= 0x7f;
-        }
-
-        private boolean isSpecialChar() {
-            return ((mData1 == 0x11 || mData1 == 0x19)
-                    && mData2 >= 0x30 && mData2 <= 0x3f);
-        }
-
-        private boolean isExtendedChar() {
-            return ((mData1 == 0x12 || mData1 == 0x1A
-                    || mData1 == 0x13 || mData1 == 0x1B)
-                    && mData2 >= 0x20 && mData2 <= 0x3f);
-        }
-
-        private char getBasicChar(byte data) {
-            char c;
-            // replace the non-ASCII ones
-            switch (data) {
-                case 0x2A: c = '\u00E1'; break;
-                case 0x5C: c = '\u00E9'; break;
-                case 0x5E: c = '\u00ED'; break;
-                case 0x5F: c = '\u00F3'; break;
-                case 0x60: c = '\u00FA'; break;
-                case 0x7B: c = '\u00E7'; break;
-                case 0x7C: c = '\u00F7'; break;
-                case 0x7D: c = '\u00D1'; break;
-                case 0x7E: c = '\u00F1'; break;
-                case 0x7F: c = '\u2588'; break; // Full block
-                default: c = (char) data; break;
-            }
-            return c;
-        }
-
-        private String getBasicChars() {
-            if (mData1 >= 0x20 && mData1 <= 0x7f) {
-                StringBuilder builder = new StringBuilder(2);
-                builder.append(getBasicChar(mData1));
-                if (mData2 >= 0x20 && mData2 <= 0x7f) {
-                    builder.append(getBasicChar(mData2));
-                }
-                return builder.toString();
-            }
-
-            return null;
-        }
-
-        private String getSpecialChar() {
-            if ((mData1 == 0x11 || mData1 == 0x19)
-                    && mData2 >= 0x30 && mData2 <= 0x3f) {
-                return mSpecialCharMap[mData2 - 0x30];
-            }
-
-            return null;
-        }
-
-        private String getExtendedChar() {
-            if ((mData1 == 0x12 || mData1 == 0x1A)
-                    && mData2 >= 0x20 && mData2 <= 0x3f){
-                // 1 Spanish/French char
-                return mSpanishCharMap[mData2 - 0x20];
-            } else if ((mData1 == 0x13 || mData1 == 0x1B)
-                    && mData2 >= 0x20 && mData2 <= 0x3f){
-                // 1 Portuguese/German/Danish char
-                return mProtugueseCharMap[mData2 - 0x20];
-            }
-
-            return null;
-        }
-
-        @Override
-        public String toString() {
-            String str;
-
-            if (mData1 < 0x10 && mData2 < 0x10) {
-                // Null Pad, ignore
-                return String.format("[%d]Null: %02x %02x", mType, mData1, mData2);
-            }
-
-            int ctrlCode = getCtrlCode();
-            if (ctrlCode != INVALID) {
-                return String.format("[%d]%s", mType, ctrlCodeToString(ctrlCode));
-            }
-
-            int tabOffset = getTabOffset();
-            if (tabOffset > 0) {
-                return String.format("[%d]Tab%d", mType, tabOffset);
-            }
-
-            PAC pac = getPAC();
-            if (pac != null) {
-                return String.format("[%d]PAC: %s", mType, pac.toString());
-            }
-
-            StyleCode m = getMidRow();
-            if (m != null) {
-                return String.format("[%d]Mid-row: %s", mType, m.toString());
-            }
-
-            if (isDisplayableChar()) {
-                return String.format("[%d]Displayable: %s (%02x %02x)",
-                        mType, getDisplayText(), mData1, mData2);
-            }
-
-            return String.format("[%d]Invalid: %02x %02x", mType, mData1, mData2);
-        }
-    }
-}
-
-/**
- * Widget capable of rendering CEA-608 closed captions.
- */
-class Cea608CCWidget extends ClosedCaptionWidget implements Cea608CCParser.DisplayListener {
-    private static final Rect mTextBounds = new Rect();
-    private static final String mDummyText = "1234567890123456789012345678901234";
-
-    public Cea608CCWidget(Context context) {
-        this(context, null);
-    }
-
-    public Cea608CCWidget(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public Cea608CCWidget(Context context, AttributeSet attrs, int defStyle) {
-        this(context, attrs, defStyle, 0);
-    }
-
-    public Cea608CCWidget(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    @Override
-    public ClosedCaptionLayout createCaptionLayout(Context context) {
-        return new CCLayout(context);
-    }
-
-    @Override
-    public void onDisplayChanged(SpannableStringBuilder[] styledTexts) {
-        ((CCLayout) mClosedCaptionLayout).update(styledTexts);
-
-        if (mListener != null) {
-            mListener.onChanged(this);
-        }
-    }
-
-    @Override
-    public CaptionStyle getCaptionStyle() {
-        return mCaptionStyle;
-    }
-
-    private static class CCLineBox extends TextView {
-        private static final float FONT_PADDING_RATIO = 0.75f;
-        private static final float EDGE_OUTLINE_RATIO = 0.1f;
-        private static final float EDGE_SHADOW_RATIO = 0.05f;
-        private float mOutlineWidth;
-        private float mShadowRadius;
-        private float mShadowOffset;
-
-        private int mTextColor = Color.WHITE;
-        private int mBgColor = Color.BLACK;
-        private int mEdgeType = CaptionStyle.EDGE_TYPE_NONE;
-        private int mEdgeColor = Color.TRANSPARENT;
-
-        CCLineBox(Context context) {
-            super(context);
-            setGravity(Gravity.CENTER);
-            setBackgroundColor(Color.TRANSPARENT);
-            setTextColor(Color.WHITE);
-            setTypeface(Typeface.MONOSPACE);
-            setVisibility(View.INVISIBLE);
-
-            final Resources res = getContext().getResources();
-
-            // get the default (will be updated later during measure)
-            mOutlineWidth = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.subtitle_outline_width);
-            mShadowRadius = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.subtitle_shadow_radius);
-            mShadowOffset = res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.subtitle_shadow_offset);
-        }
-
-        void setCaptionStyle(CaptionStyle captionStyle) {
-            mTextColor = captionStyle.foregroundColor;
-            mBgColor = captionStyle.backgroundColor;
-            mEdgeType = captionStyle.edgeType;
-            mEdgeColor = captionStyle.edgeColor;
-
-            setTextColor(mTextColor);
-            if (mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
-                setShadowLayer(mShadowRadius, mShadowOffset, mShadowOffset, mEdgeColor);
-            } else {
-                setShadowLayer(0, 0, 0, 0);
-            }
-            invalidate();
-        }
-
-        @Override
-        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            float fontSize = MeasureSpec.getSize(heightMeasureSpec) * FONT_PADDING_RATIO;
-            setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
-
-            mOutlineWidth = EDGE_OUTLINE_RATIO * fontSize + 1.0f;
-            mShadowRadius = EDGE_SHADOW_RATIO * fontSize + 1.0f;;
-            mShadowOffset = mShadowRadius;
-
-            // set font scale in the X direction to match the required width
-            setScaleX(1.0f);
-            getPaint().getTextBounds(mDummyText, 0, mDummyText.length(), mTextBounds);
-            float actualTextWidth = mTextBounds.width();
-            float requiredTextWidth = MeasureSpec.getSize(widthMeasureSpec);
-            setScaleX(requiredTextWidth / actualTextWidth);
-
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-
-        @Override
-        protected void onDraw(Canvas c) {
-            if (mEdgeType == CaptionStyle.EDGE_TYPE_UNSPECIFIED
-                    || mEdgeType == CaptionStyle.EDGE_TYPE_NONE
-                    || mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
-                // these edge styles don't require a second pass
-                super.onDraw(c);
-                return;
-            }
-
-            if (mEdgeType == CaptionStyle.EDGE_TYPE_OUTLINE) {
-                drawEdgeOutline(c);
-            } else {
-                // Raised or depressed
-                drawEdgeRaisedOrDepressed(c);
-            }
-        }
-
-        private void drawEdgeOutline(Canvas c) {
-            TextPaint textPaint = getPaint();
-
-            Paint.Style previousStyle = textPaint.getStyle();
-            Paint.Join previousJoin = textPaint.getStrokeJoin();
-            float previousWidth = textPaint.getStrokeWidth();
-
-            setTextColor(mEdgeColor);
-            textPaint.setStyle(Paint.Style.FILL_AND_STROKE);
-            textPaint.setStrokeJoin(Paint.Join.ROUND);
-            textPaint.setStrokeWidth(mOutlineWidth);
-
-            // Draw outline and background only.
-            super.onDraw(c);
-
-            // Restore original settings.
-            setTextColor(mTextColor);
-            textPaint.setStyle(previousStyle);
-            textPaint.setStrokeJoin(previousJoin);
-            textPaint.setStrokeWidth(previousWidth);
-
-            // Remove the background.
-            setBackgroundSpans(Color.TRANSPARENT);
-            // Draw foreground only.
-            super.onDraw(c);
-            // Restore the background.
-            setBackgroundSpans(mBgColor);
-        }
-
-        private void drawEdgeRaisedOrDepressed(Canvas c) {
-            TextPaint textPaint = getPaint();
-
-            Paint.Style previousStyle = textPaint.getStyle();
-            textPaint.setStyle(Paint.Style.FILL);
-
-            final boolean raised = mEdgeType == CaptionStyle.EDGE_TYPE_RAISED;
-            final int colorUp = raised ? Color.WHITE : mEdgeColor;
-            final int colorDown = raised ? mEdgeColor : Color.WHITE;
-            final float offset = mShadowRadius / 2f;
-
-            // Draw background and text with shadow up
-            setShadowLayer(mShadowRadius, -offset, -offset, colorUp);
-            super.onDraw(c);
-
-            // Remove the background.
-            setBackgroundSpans(Color.TRANSPARENT);
-
-            // Draw text with shadow down
-            setShadowLayer(mShadowRadius, +offset, +offset, colorDown);
-            super.onDraw(c);
-
-            // Restore settings
-            textPaint.setStyle(previousStyle);
-
-            // Restore the background.
-            setBackgroundSpans(mBgColor);
-        }
-
-        private void setBackgroundSpans(int color) {
-            CharSequence text = getText();
-            if (text instanceof Spannable) {
-                Spannable spannable = (Spannable) text;
-                Cea608CCParser.MutableBackgroundColorSpan[] bgSpans = spannable.getSpans(
-                        0, spannable.length(), Cea608CCParser.MutableBackgroundColorSpan.class);
-                for (int i = 0; i < bgSpans.length; i++) {
-                    bgSpans[i].setBackgroundColor(color);
-                }
-            }
-        }
-    }
-
-    private static class CCLayout extends LinearLayout implements ClosedCaptionLayout {
-        private static final int MAX_ROWS = Cea608CCParser.MAX_ROWS;
-        private static final float SAFE_AREA_RATIO = 0.9f;
-
-        private final CCLineBox[] mLineBoxes = new CCLineBox[MAX_ROWS];
-
-        CCLayout(Context context) {
-            super(context);
-            setGravity(Gravity.START);
-            setOrientation(LinearLayout.VERTICAL);
-            for (int i = 0; i < MAX_ROWS; i++) {
-                mLineBoxes[i] = new CCLineBox(getContext());
-                addView(mLineBoxes[i], LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-            }
-        }
-
-        @Override
-        public void setCaptionStyle(CaptionStyle captionStyle) {
-            for (int i = 0; i < MAX_ROWS; i++) {
-                mLineBoxes[i].setCaptionStyle(captionStyle);
-            }
-        }
-
-        @Override
-        public void setFontScale(float fontScale) {
-            // Ignores the font scale changes of the system wide CC preference.
-        }
-
-        void update(SpannableStringBuilder[] textBuffer) {
-            for (int i = 0; i < MAX_ROWS; i++) {
-                if (textBuffer[i] != null) {
-                    mLineBoxes[i].setText(textBuffer[i], TextView.BufferType.SPANNABLE);
-                    mLineBoxes[i].setVisibility(View.VISIBLE);
-                } else {
-                    mLineBoxes[i].setVisibility(View.INVISIBLE);
-                }
-            }
-        }
-
-        @Override
-        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-            int safeWidth = getMeasuredWidth();
-            int safeHeight = getMeasuredHeight();
-
-            // CEA-608 assumes 4:3 video
-            if (safeWidth * 3 >= safeHeight * 4) {
-                safeWidth = safeHeight * 4 / 3;
-            } else {
-                safeHeight = safeWidth * 3 / 4;
-            }
-            safeWidth *= SAFE_AREA_RATIO;
-            safeHeight *= SAFE_AREA_RATIO;
-
-            int lineHeight = safeHeight / MAX_ROWS;
-            int lineHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                    lineHeight, MeasureSpec.EXACTLY);
-            int lineWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                    safeWidth, MeasureSpec.EXACTLY);
-
-            for (int i = 0; i < MAX_ROWS; i++) {
-                mLineBoxes[i].measure(lineWidthMeasureSpec, lineHeightMeasureSpec);
-            }
-        }
-
-        @Override
-        protected void onLayout(boolean changed, int l, int t, int r, int b) {
-            // safe caption area
-            int viewPortWidth = r - l;
-            int viewPortHeight = b - t;
-            int safeWidth, safeHeight;
-            // CEA-608 assumes 4:3 video
-            if (viewPortWidth * 3 >= viewPortHeight * 4) {
-                safeWidth = viewPortHeight * 4 / 3;
-                safeHeight = viewPortHeight;
-            } else {
-                safeWidth = viewPortWidth;
-                safeHeight = viewPortWidth * 3 / 4;
-            }
-            safeWidth *= SAFE_AREA_RATIO;
-            safeHeight *= SAFE_AREA_RATIO;
-            int left = (viewPortWidth - safeWidth) / 2;
-            int top = (viewPortHeight - safeHeight) / 2;
-
-            for (int i = 0; i < MAX_ROWS; i++) {
-                mLineBoxes[i].layout(
-                        left,
-                        top + safeHeight * i / MAX_ROWS,
-                        left + safeWidth,
-                        top + safeHeight * (i + 1) / MAX_ROWS);
-            }
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index f75b75e..663b813 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -19,67 +19,41 @@
 import android.app.Notification;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import android.media.MediaBrowser2;
-import android.media.MediaBrowser2.BrowserCallback;
 import android.media.MediaController2;
 import android.media.MediaController2.ControllerCallback;
 import android.media.MediaItem2;
-import android.media.MediaLibraryService2;
-import android.media.MediaLibraryService2.LibraryRoot;
-import android.media.MediaLibraryService2.MediaLibrarySession;
-import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
 import android.media.MediaMetadata2;
 import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.SessionCallback;
-import android.media.MediaSessionService2;
-import android.media.MediaSessionService2.MediaNotification;
 import android.media.Rating2;
 import android.media.SessionCommand2;
 import android.media.SessionCommandGroup2;
 import android.media.SessionToken2;
 import android.media.VolumeProvider2;
-import android.media.update.MediaBrowser2Provider;
-import android.media.update.MediaControlView2Provider;
 import android.media.update.MediaController2Provider;
 import android.media.update.MediaItem2Provider;
-import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
 import android.media.update.MediaMetadata2Provider;
 import android.media.update.MediaPlaylistAgentProvider;
 import android.media.update.MediaSession2Provider;
 import android.media.update.MediaSession2Provider.BuilderBaseProvider;
 import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
-import android.media.update.MediaSessionService2Provider;
-import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
 import android.media.update.SessionToken2Provider;
 import android.media.update.StaticProvider;
-import android.media.update.VideoView2Provider;
-import android.media.update.ViewGroupProvider;
 import android.media.update.VolumeProvider2Provider;
 import android.os.Bundle;
 import android.os.IInterface;
-import android.util.AttributeSet;
-import android.widget.MediaControlView2;
-import android.widget.VideoView2;
-
-import androidx.annotation.Nullable;
 
 import com.android.media.IMediaController2;
-import com.android.media.MediaBrowser2Impl;
 import com.android.media.MediaController2Impl;
 import com.android.media.MediaItem2Impl;
-import com.android.media.MediaLibraryService2Impl;
-import com.android.media.MediaLibraryService2Impl.LibraryRootImpl;
 import com.android.media.MediaMetadata2Impl;
 import com.android.media.MediaPlaylistAgentImpl;
 import com.android.media.MediaSession2Impl;
-import com.android.media.MediaSessionService2Impl;
 import com.android.media.Rating2Impl;
 import com.android.media.SessionToken2Impl;
 import com.android.media.VolumeProvider2Impl;
-import com.android.widget.MediaControlView2Impl;
-import com.android.widget.VideoView2Impl;
 
 import java.util.concurrent.Executor;
 
@@ -99,12 +73,6 @@
     }
 
     @Override
-    public MediaBrowser2Provider createMediaBrowser2(Context context, MediaBrowser2 instance,
-            SessionToken2 token, Executor executor, BrowserCallback callback) {
-        return new MediaBrowser2Impl(context, instance, token, executor, callback);
-    }
-
-    @Override
     public MediaSession2Provider.CommandProvider createMediaSession2Command(
             SessionCommand2 instance, int commandCode, String action, Bundle extra) {
         if (action == null && extra == null) {
@@ -150,52 +118,6 @@
     }
 
     @Override
-    public MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance) {
-        return new MediaSessionService2Impl(instance);
-    }
-
-    @Override
-    public MediaNotificationProvider createMediaSessionService2MediaNotification(
-            MediaNotification instance, int notificationId, Notification notification) {
-        return new MediaSessionService2Impl.MediaNotificationImpl(
-                instance, notificationId, notification);
-    }
-
-    @Override
-    public MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance) {
-        return new MediaLibraryService2Impl(instance);
-    }
-
-    @Override
-    public BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
-        createMediaLibraryService2Builder(MediaLibraryService2 service,
-            MediaLibrarySession.Builder instance, Executor callbackExecutor,
-            MediaLibrarySessionCallback callback) {
-        return new MediaLibraryService2Impl.BuilderImpl(service, instance, callbackExecutor,
-                callback);
-    }
-
-    @Override
-    public LibraryRootProvider createMediaLibraryService2LibraryRoot(
-            LibraryRoot instance, String rootId, Bundle extras) {
-        return new LibraryRootImpl(instance, rootId, extras);
-    }
-
-    @Override
-    public MediaControlView2Provider createMediaControlView2(MediaControlView2 instance,
-            ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
-            @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        return new MediaControlView2Impl(instance, superProvider, privateProvider);
-    }
-
-    @Override
-    public VideoView2Provider createVideoView2(
-            VideoView2 instance, ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
-            @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        return new VideoView2Impl(instance, superProvider, privateProvider);
-    }
-
-    @Override
     public SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
             String packageName, String serviceName, int uid) {
         return new SessionToken2Impl(context, instance, packageName, serviceName, uid);
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
index dc5e5e2..f918ed3 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
@@ -16,34 +16,11 @@
 
 package com.android.media.update;
 
-import android.annotation.Nullable;
-import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-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 android.view.ViewGroup;
-
-import androidx.annotation.GuardedBy;
-import androidx.appcompat.widget.ButtonBarLayout;
-import androidx.legacy.widget.Space;
-
-import com.android.support.mediarouter.app.MediaRouteButton;
-import com.android.support.mediarouter.app.MediaRouteExpandCollapseButton;
-import com.android.support.mediarouter.app.MediaRouteVolumeSlider;
-import com.android.support.mediarouter.app.OverlayListView;
 
 public final class ApiHelper {
     private static ApplicationInfo sUpdatableInfo;
 
-    @GuardedBy("this")
-    private static Theme sLibTheme;
-
     private ApiHelper() { }
 
     static void initialize(ApplicationInfo updatableInfo) {
@@ -53,90 +30,4 @@
 
         sUpdatableInfo = updatableInfo;
     }
-
-    public static Resources getLibResources(Context context) {
-        return getLibTheme(context).getResources();
-    }
-
-    public static Theme getLibTheme(Context context) {
-        if (sLibTheme != null) return sLibTheme;
-
-        return getLibThemeSynchronized(context);
-    }
-
-    public static Theme getLibTheme(Context context, int themeId) {
-        Theme theme = getLibResources(context).newTheme();
-        theme.applyStyle(themeId, true);
-        return theme;
-    }
-
-    public static LayoutInflater getLayoutInflater(Context context) {
-        return getLayoutInflater(context, null);
-    }
-
-    public static LayoutInflater getLayoutInflater(Context context, Theme theme) {
-        if (theme == null) {
-            theme = getLibTheme(context);
-        }
-
-        // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme.
-        LayoutInflater layoutInflater = LayoutInflater.from(context).cloneInContext(
-                new ContextThemeWrapper(context, theme));
-        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);
-                } else if (MediaRouteVolumeSlider.class.getCanonicalName().equals(name)) {
-                    return new MediaRouteVolumeSlider(context, attrs);
-                } else if (MediaRouteExpandCollapseButton.class.getCanonicalName().equals(name)) {
-                    return new MediaRouteExpandCollapseButton(context, attrs);
-                } else if (OverlayListView.class.getCanonicalName().equals(name)) {
-                    return new OverlayListView(context, attrs);
-                } else if (ButtonBarLayout.class.getCanonicalName().equals(name)) {
-                    return new ButtonBarLayout(context, attrs);
-                } else if (Space.class.getCanonicalName().equals(name)) {
-                    return new Space(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) {
-        return inflateLibLayout(context, getLibTheme(context), libResId, null, false);
-    }
-
-    public static View inflateLibLayout(Context context, Theme theme, int libResId) {
-        return inflateLibLayout(context, theme, libResId, null, false);
-    }
-
-    public static View inflateLibLayout(Context context, Theme theme, int libResId,
-            @Nullable ViewGroup root, boolean attachToRoot) {
-        try (XmlResourceParser parser = getLibResources(context).getLayout(libResId)) {
-            return getLayoutInflater(context, theme).inflate(parser, root, attachToRoot);
-        }
-    }
-
-    private static synchronized Theme getLibThemeSynchronized(Context context) {
-        if (sLibTheme != null) return sLibTheme;
-
-        if (sUpdatableInfo == null) {
-            throw new IllegalStateException("initialize hasn't been called yet");
-        }
-
-        try {
-            return sLibTheme = context.getPackageManager()
-                    .getResourcesForApplication(sUpdatableInfo).newTheme();
-        } catch (NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-    }
 }
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/api24/media/MediaRouterApi24.java b/packages/MediaComponents/src/com/android/support/mediarouter/api24/media/MediaRouterApi24.java
deleted file mode 100644
index 1146af6..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/api24/media/MediaRouterApi24.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-// @@RequiresApi(24)
-final class MediaRouterApi24 {
-    public static final class RouteInfo {
-        public static int getDeviceType(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getDeviceType();
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteActionProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteActionProvider.java
deleted file mode 100644
index 98c0d17..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteActionProvider.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.core.view.ActionProvider;
-
-import com.android.support.mediarouter.media.MediaRouteSelector;
-import com.android.support.mediarouter.media.MediaRouter;
-
-import java.lang.ref.WeakReference;
-
-/**
- * The media route action provider displays a {@link MediaRouteButton media route button}
- * in the application's {@link ActionBar} to allow the user to select routes and
- * to control the currently selected route.
- * <p>
- * The application must specify the kinds of routes that the user should be allowed
- * to select by specifying a {@link MediaRouteSelector selector} with the
- * {@link #setRouteSelector} method.
- * </p><p>
- * Refer to {@link MediaRouteButton} for a description of the button that will
- * appear in the action bar menu.  Note that instead of disabling the button
- * when no routes are available, the action provider will instead make the
- * menu item invisible.  In this way, the button will only be visible when it
- * is possible for the user to discover and select a matching route.
- * </p>
- *
- * <h3>Prerequisites</h3>
- * <p>
- * To use the media route action provider, the activity must be a subclass of
- * {@link AppCompatActivity} from the <code>androidx.appcompat.appcompat</code>
- * support library.  Refer to support library documentation for details.
- * </p>
- *
- * <h3>Example</h3>
- * <p>
- * </p><p>
- * The application should define a menu resource to include the provider in the
- * action bar options menu.  Note that the support library action bar uses attributes
- * that are defined in the application's resource namespace rather than the framework's
- * resource namespace to configure each item.
- * </p><pre>
- * &lt;menu xmlns:android="http://schemas.android.com/apk/res/android"
- *         xmlns:app="http://schemas.android.com/apk/res-auto">
- *     &lt;item android:id="@+id/media_route_menu_item"
- *         android:title="@string/media_route_menu_title"
- *         app:showAsAction="always"
- *         app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"/>
- * &lt;/menu>
- * </pre><p>
- * Then configure the menu and set the route selector for the chooser.
- * </p><pre>
- * public class MyActivity extends AppCompatActivity {
- *     private MediaRouter mRouter;
- *     private MediaRouter.Callback mCallback;
- *     private MediaRouteSelector mSelector;
- *
- *     protected void onCreate(Bundle savedInstanceState) {
- *         super.onCreate(savedInstanceState);
- *
- *         mRouter = Mediarouter.getInstance(this);
- *         mSelector = new MediaRouteSelector.Builder()
- *                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
- *                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
- *                 .build();
- *         mCallback = new MyCallback();
- *     }
- *
- *     // Add the callback on start to tell the media router what kinds of routes
- *     // the application is interested in so that it can try to discover suitable ones.
- *     public void onStart() {
- *         super.onStart();
- *
- *         mediaRouter.addCallback(mSelector, mCallback,
- *                 MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
- *
- *         MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector);
- *         // do something with the route...
- *     }
- *
- *     // Remove the selector on stop to tell the media router that it no longer
- *     // needs to invest effort trying to discover routes of these kinds for now.
- *     public void onStop() {
- *         super.onStop();
- *
- *         mediaRouter.removeCallback(mCallback);
- *     }
- *
- *     public boolean onCreateOptionsMenu(Menu menu) {
- *         super.onCreateOptionsMenu(menu);
- *
- *         getMenuInflater().inflate(R.menu.sample_media_router_menu, menu);
- *
- *         MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
- *         MediaRouteActionProvider mediaRouteActionProvider =
- *                 (MediaRouteActionProvider)MenuItemCompat.getActionProvider(mediaRouteMenuItem);
- *         mediaRouteActionProvider.setRouteSelector(mSelector);
- *         return true;
- *     }
- *
- *     private final class MyCallback extends MediaRouter.Callback {
- *         // Implement callback methods as needed.
- *     }
- * }
- * </pre>
- *
- * @see #setRouteSelector
- */
-public class MediaRouteActionProvider extends ActionProvider {
-    private static final String TAG = "MediaRouteActionProvider";
-
-    private final MediaRouter mRouter;
-    private final MediaRouterCallback mCallback;
-
-    private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
-    private MediaRouteDialogFactory mDialogFactory = MediaRouteDialogFactory.getDefault();
-    private MediaRouteButton mButton;
-
-    /**
-     * Creates the action provider.
-     *
-     * @param context The context.
-     */
-    public MediaRouteActionProvider(Context context) {
-        super(context);
-
-        mRouter = MediaRouter.getInstance(context);
-        mCallback = new MediaRouterCallback(this);
-    }
-
-    /**
-     * Gets the media route selector for filtering the routes that the user can
-     * select using the media route chooser dialog.
-     *
-     * @return The selector, never null.
-     */
-    @NonNull
-    public MediaRouteSelector getRouteSelector() {
-        return mSelector;
-    }
-
-    /**
-     * Sets the media route selector for filtering the routes that the user can
-     * select using the media route chooser dialog.
-     *
-     * @param selector The selector, must not be null.
-     */
-    public void setRouteSelector(@NonNull MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-
-        if (!mSelector.equals(selector)) {
-            // FIXME: We currently have no way of knowing whether the action provider
-            // is still needed by the UI.  Unfortunately this means the action provider
-            // may leak callbacks until garbage collection occurs.  This may result in
-            // media route providers doing more work than necessary in the short term
-            // while trying to discover routes that are no longer of interest to the
-            // application.  To solve this problem, the action provider will need some
-            // indication from the framework that it is being destroyed.
-            if (!mSelector.isEmpty()) {
-                mRouter.removeCallback(mCallback);
-            }
-            if (!selector.isEmpty()) {
-                mRouter.addCallback(selector, mCallback);
-            }
-            mSelector = selector;
-            refreshRoute();
-
-            if (mButton != null) {
-                mButton.setRouteSelector(selector);
-            }
-        }
-    }
-
-    /**
-     * Gets the media route dialog factory to use when showing the route chooser
-     * or controller dialog.
-     *
-     * @return The dialog factory, never null.
-     */
-    @NonNull
-    public MediaRouteDialogFactory getDialogFactory() {
-        return mDialogFactory;
-    }
-
-    /**
-     * Sets the media route dialog factory to use when showing the route chooser
-     * or controller dialog.
-     *
-     * @param factory The dialog factory, must not be null.
-     */
-    public void setDialogFactory(@NonNull MediaRouteDialogFactory factory) {
-        if (factory == null) {
-            throw new IllegalArgumentException("factory must not be null");
-        }
-
-        if (mDialogFactory != factory) {
-            mDialogFactory = factory;
-
-            if (mButton != null) {
-                mButton.setDialogFactory(factory);
-            }
-        }
-    }
-
-    /**
-     * Gets the associated media route button, or null if it has not yet been created.
-     */
-    @Nullable
-    public MediaRouteButton getMediaRouteButton() {
-        return mButton;
-    }
-
-    /**
-     * Called when the media route button is being created.
-     * <p>
-     * Subclasses may override this method to customize the button.
-     * </p>
-     */
-    public MediaRouteButton onCreateMediaRouteButton() {
-        return new MediaRouteButton(getContext());
-    }
-
-    @Override
-    @SuppressWarnings("deprecation")
-    public View onCreateActionView() {
-        if (mButton != null) {
-            Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
-                    "with a menu item. Don't reuse MediaRouteActionProvider instances! " +
-                    "Abandoning the old menu item...");
-        }
-
-        mButton = onCreateMediaRouteButton();
-        mButton.setCheatSheetEnabled(true);
-        mButton.setRouteSelector(mSelector);
-        mButton.setDialogFactory(mDialogFactory);
-        mButton.setLayoutParams(new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT,
-                ViewGroup.LayoutParams.MATCH_PARENT));
-        return mButton;
-    }
-
-    @Override
-    public boolean onPerformDefaultAction() {
-        if (mButton != null) {
-            return mButton.showDialog();
-        }
-        return false;
-    }
-
-    @Override
-    public boolean overridesItemVisibility() {
-        return true;
-    }
-
-    @Override
-    public boolean isVisible() {
-        return mRouter.isRouteAvailable(mSelector,
-                MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
-    }
-
-    void refreshRoute() {
-        refreshVisibility();
-    }
-
-    private static final class MediaRouterCallback extends MediaRouter.Callback {
-        private final WeakReference<MediaRouteActionProvider> mProviderWeak;
-
-        public MediaRouterCallback(MediaRouteActionProvider provider) {
-            mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider);
-        }
-
-        @Override
-        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute(router);
-        }
-
-        @Override
-        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute(router);
-        }
-
-        @Override
-        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute(router);
-        }
-
-        @Override
-        public void onProviderAdded(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            refreshRoute(router);
-        }
-
-        @Override
-        public void onProviderRemoved(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            refreshRoute(router);
-        }
-
-        @Override
-        public void onProviderChanged(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            refreshRoute(router);
-        }
-
-        private void refreshRoute(MediaRouter router) {
-            MediaRouteActionProvider provider = mProviderWeak.get();
-            if (provider != null) {
-                provider.refreshRoute();
-            } else {
-                router.removeCallback(this);
-            }
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
deleted file mode 100644
index e82fcb9..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
+++ /dev/null
@@ -1,630 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import android.annotation.NonNull;
-import android.app.Activity;
-import android.app.FragmentManager;
-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;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.SoundEffectConstants;
-import android.view.View;
-
-import androidx.appcompat.widget.TooltipCompat;
-import androidx.core.graphics.drawable.DrawableCompat;
-
-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;
-
-/**
- * The media route button allows the user to select routes and to control the
- * currently selected route.
- * <p>
- * The application must specify the kinds of routes that the user should be allowed
- * to select by specifying a {@link MediaRouteSelector selector} with the
- * {@link #setRouteSelector} method.
- * </p><p>
- * When the default route is selected or when the currently selected route does not
- * match the {@link #getRouteSelector() selector}, the button will appear in
- * an inactive state indicating that the application is not connected to a
- * route of the kind that it wants to use.  Clicking on the button opens
- * a {@link MediaRouteChooserDialog} to allow the user to select a route.
- * If no non-default routes match the selector and it is not possible for an active
- * scan to discover any matching routes, then the button is disabled and cannot
- * be clicked.
- * </p><p>
- * When a non-default route is selected that matches the selector, the button will
- * appear in an active state indicating that the application is connected
- * to a route of the kind that it wants to use.  The button may also appear
- * in an intermediary connecting state if the route is in the process of connecting
- * to the destination but has not yet completed doing so.  In either case, clicking
- * on the button opens a {@link MediaRouteControllerDialog} to allow the user
- * to control or disconnect from the current route.
- * </p>
- *
- * <h3>Prerequisites</h3>
- * <p>
- * To use the media route button, the activity must be a subclass of
- * {@link FragmentActivity} from the <code>androidx.core./code>
- * support library.  Refer to support library documentation for details.
- * </p>
- *
- * @see MediaRouteActionProvider
- * @see #setRouteSelector
- */
-public class MediaRouteButton extends View {
-    private static final String TAG = "MediaRouteButton";
-
-    private static final String CHOOSER_FRAGMENT_TAG =
-            "androidx.mediarouter.media.outer:MediaRouteChooserDialogFragment";
-    private static final String CONTROLLER_FRAGMENT_TAG =
-            "androidx.mediarouter.media.outer:MediaRouteControllerDialogFragment";
-
-    private final MediaRouter mRouter;
-    private final MediaRouterCallback mCallback;
-
-    private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
-    private int mRouteCallbackFlags;
-    private MediaRouteDialogFactory mDialogFactory = MediaRouteDialogFactory.getDefault();
-
-    private boolean mAttachedToWindow;
-
-    private static final SparseArray<Drawable.ConstantState> sRemoteIndicatorCache =
-            new SparseArray<>(2);
-    private RemoteIndicatorLoader mRemoteIndicatorLoader;
-    private Drawable mRemoteIndicator;
-    private boolean mRemoteActive;
-    private boolean mIsConnecting;
-
-    private ColorStateList mButtonTint;
-    private int mMinWidth;
-    private int mMinHeight;
-
-    // The checked state is used when connected to a remote route.
-    private static final int[] CHECKED_STATE_SET = {
-        android.R.attr.state_checked
-    };
-
-    // The checkable state is used while connecting to a remote route.
-    private static final int[] CHECKABLE_STATE_SET = {
-        android.R.attr.state_checkable
-    };
-
-    public MediaRouteButton(Context context) {
-        this(context, null);
-    }
-
-    public MediaRouteButton(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.mediaRouteButtonStyle);
-    }
-
-    public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(MediaRouterThemeHelper.createThemedButtonContext(context), attrs, defStyleAttr);
-        context = getContext();
-
-        mRouter = MediaRouter.getInstance(context);
-        mCallback = new MediaRouterCallback();
-
-        Resources.Theme theme = ApiHelper.getLibResources(context).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);
-        mMinHeight = a.getDimensionPixelSize(
-                R.styleable.MediaRouteButton_android_minHeight, 0);
-        int remoteIndicatorResId = a.getResourceId(
-                R.styleable.MediaRouteButton_externalRouteEnabledDrawable, 0);
-        a.recycle();
-
-        if (remoteIndicatorResId != 0) {
-            Drawable.ConstantState remoteIndicatorState =
-                    sRemoteIndicatorCache.get(remoteIndicatorResId);
-            if (remoteIndicatorState != null) {
-                setRemoteIndicatorDrawable(remoteIndicatorState.newDrawable());
-            } else {
-                mRemoteIndicatorLoader = new RemoteIndicatorLoader(remoteIndicatorResId);
-                mRemoteIndicatorLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-            }
-        }
-
-        updateContentDescription();
-        setClickable(true);
-    }
-
-    /**
-     * Gets the media route selector for filtering the routes that the user can
-     * select using the media route chooser dialog.
-     *
-     * @return The selector, never null.
-     */
-    @NonNull
-    public MediaRouteSelector getRouteSelector() {
-        return mSelector;
-    }
-
-    /**
-     * Sets the media route selector for filtering the routes that the user can
-     * select using the media route chooser dialog.
-     *
-     * @param selector The selector.
-     */
-    public void setRouteSelector(MediaRouteSelector selector) {
-        setRouteSelector(selector, 0);
-    }
-
-    /**
-     * Sets the media route selector for filtering the routes that the user can
-     * select using the media route chooser dialog.
-     *
-     * @param selector The selector.
-     * @param flags Flags to control the behavior of the callback. May be zero or a combination of
-     *              {@link #MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
-     *              {@link #MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS}.
-     */
-    public void setRouteSelector(MediaRouteSelector selector, int flags) {
-        if (mSelector.equals(selector) && mRouteCallbackFlags == flags) {
-            return;
-        }
-        if (!mSelector.isEmpty()) {
-            mRouter.removeCallback(mCallback);
-        }
-        if (selector == null || selector.isEmpty()) {
-            mSelector = MediaRouteSelector.EMPTY;
-            return;
-        }
-
-        mSelector = selector;
-        mRouteCallbackFlags = flags;
-
-        if (mAttachedToWindow) {
-            mRouter.addCallback(selector, mCallback, flags);
-            refreshRoute();
-        }
-    }
-
-    /**
-     * Gets the media route dialog factory to use when showing the route chooser
-     * or controller dialog.
-     *
-     * @return The dialog factory, never null.
-     */
-    @NonNull
-    public MediaRouteDialogFactory getDialogFactory() {
-        return mDialogFactory;
-    }
-
-    /**
-     * Sets the media route dialog factory to use when showing the route chooser
-     * or controller dialog.
-     *
-     * @param factory The dialog factory, must not be null.
-     */
-    public void setDialogFactory(@NonNull MediaRouteDialogFactory factory) {
-        if (factory == null) {
-            throw new IllegalArgumentException("factory must not be null");
-        }
-
-        mDialogFactory = factory;
-    }
-
-    /**
-     * Show the route chooser or controller dialog.
-     * <p>
-     * If the default route is selected or if the currently selected route does
-     * not match the {@link #getRouteSelector selector}, then shows the route chooser dialog.
-     * Otherwise, shows the route controller dialog to offer the user
-     * a choice to disconnect from the route or perform other control actions
-     * such as setting the route's volume.
-     * </p><p>
-     * The application can customize the dialogs by calling {@link #setDialogFactory}
-     * to provide a customized dialog factory.
-     * </p>
-     *
-     * @return True if the dialog was actually shown.
-     *
-     * @throws IllegalStateException if the activity is not a subclass of
-     * {@link FragmentActivity}.
-     */
-    public boolean showDialog() {
-        if (!mAttachedToWindow) {
-            return false;
-        }
-
-        final FragmentManager fm = getActivity().getFragmentManager();
-        if (fm == null) {
-            throw new IllegalStateException("The activity must be a subclass of FragmentActivity");
-        }
-
-        MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
-        if (route.isDefaultOrBluetooth() || !route.matchesSelector(mSelector)) {
-            if (fm.findFragmentByTag(CHOOSER_FRAGMENT_TAG) != null) {
-                Log.w(TAG, "showDialog(): Route chooser dialog already showing!");
-                return false;
-            }
-            MediaRouteChooserDialogFragment f =
-                    mDialogFactory.onCreateChooserDialogFragment();
-            f.setRouteSelector(mSelector);
-            f.show(fm, CHOOSER_FRAGMENT_TAG);
-        } else {
-            if (fm.findFragmentByTag(CONTROLLER_FRAGMENT_TAG) != null) {
-                Log.w(TAG, "showDialog(): Route controller dialog already showing!");
-                return false;
-            }
-            MediaRouteControllerDialogFragment f =
-                    mDialogFactory.onCreateControllerDialogFragment();
-            f.show(fm, CONTROLLER_FRAGMENT_TAG);
-        }
-        return true;
-    }
-
-
-    private Activity getActivity() {
-        // Gross way of unwrapping the Activity so we can get the FragmentManager
-        Context context = getContext();
-        while (context instanceof ContextWrapper) {
-            if (context instanceof Activity) {
-                return (Activity)context;
-            }
-            context = ((ContextWrapper)context).getBaseContext();
-        }
-        return null;
-    }
-
-    /**
-     * Sets whether to enable showing a toast with the content descriptor of the
-     * button when the button is long pressed.
-     */
-    void setCheatSheetEnabled(boolean enable) {
-        TooltipCompat.setTooltipText(this, enable
-                ? ApiHelper.getLibResources(getContext())
-                    .getString(R.string.mr_button_content_description)
-                : null);
-    }
-
-    @Override
-    public boolean performClick() {
-        // Send the appropriate accessibility events and call listeners
-        boolean handled = super.performClick();
-        if (!handled) {
-            playSoundEffect(SoundEffectConstants.CLICK);
-        }
-        return showDialog() || handled;
-    }
-
-    @Override
-    protected int[] onCreateDrawableState(int extraSpace) {
-        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
-
-        // Technically we should be handling this more completely, but these
-        // are implementation details here. Checkable is used to express the connecting
-        // drawable state and it's mutually exclusive with check for the purposes
-        // of state selection here.
-        if (mIsConnecting) {
-            mergeDrawableStates(drawableState, CHECKABLE_STATE_SET);
-        } else if (mRemoteActive) {
-            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
-        }
-        return drawableState;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-
-        if (mRemoteIndicator != null) {
-            int[] myDrawableState = getDrawableState();
-            mRemoteIndicator.setState(myDrawableState);
-            invalidate();
-        }
-    }
-
-    /**
-     * Sets a drawable to use as the remote route indicator.
-     */
-    public void setRemoteIndicatorDrawable(Drawable d) {
-        if (mRemoteIndicatorLoader != null) {
-            mRemoteIndicatorLoader.cancel(false);
-        }
-
-        if (mRemoteIndicator != null) {
-            mRemoteIndicator.setCallback(null);
-            unscheduleDrawable(mRemoteIndicator);
-        }
-        if (d != null) {
-            if (mButtonTint != null) {
-                d = DrawableCompat.wrap(d.mutate());
-                DrawableCompat.setTintList(d, mButtonTint);
-            }
-            d.setCallback(this);
-            d.setState(getDrawableState());
-            d.setVisible(getVisibility() == VISIBLE, false);
-        }
-        mRemoteIndicator = d;
-
-        refreshDrawableState();
-        if (mAttachedToWindow && mRemoteIndicator != null
-                && mRemoteIndicator.getCurrent() instanceof AnimationDrawable) {
-            AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent();
-            if (mIsConnecting) {
-                if (!curDrawable.isRunning()) {
-                    curDrawable.start();
-                }
-            } else if (mRemoteActive) {
-                if (curDrawable.isRunning()) {
-                    curDrawable.stop();
-                }
-                curDrawable.selectDrawable(curDrawable.getNumberOfFrames() - 1);
-            }
-        }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || who == mRemoteIndicator;
-    }
-
-    @Override
-    public void jumpDrawablesToCurrentState() {
-        // We can't call super to handle the background so we do it ourselves.
-        //super.jumpDrawablesToCurrentState();
-        if (getBackground() != null) {
-            DrawableCompat.jumpToCurrentState(getBackground());
-        }
-
-        // Handle our own remote indicator.
-        if (mRemoteIndicator != null) {
-            DrawableCompat.jumpToCurrentState(mRemoteIndicator);
-        }
-    }
-
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-
-        if (mRemoteIndicator != null) {
-            mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false);
-        }
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        mAttachedToWindow = true;
-        if (!mSelector.isEmpty()) {
-            mRouter.addCallback(mSelector, mCallback, mRouteCallbackFlags);
-        }
-        refreshRoute();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        mAttachedToWindow = false;
-        if (!mSelector.isEmpty()) {
-            mRouter.removeCallback(mCallback);
-        }
-
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-
-        final int width = Math.max(mMinWidth, mRemoteIndicator != null ?
-                mRemoteIndicator.getIntrinsicWidth() + getPaddingLeft() + getPaddingRight() : 0);
-        final int height = Math.max(mMinHeight, mRemoteIndicator != null ?
-                mRemoteIndicator.getIntrinsicHeight() + getPaddingTop() + getPaddingBottom() : 0);
-
-        int measuredWidth;
-        switch (widthMode) {
-            case MeasureSpec.EXACTLY:
-                measuredWidth = widthSize;
-                break;
-            case MeasureSpec.AT_MOST:
-                measuredWidth = Math.min(widthSize, width);
-                break;
-            default:
-            case MeasureSpec.UNSPECIFIED:
-                measuredWidth = width;
-                break;
-        }
-
-        int measuredHeight;
-        switch (heightMode) {
-            case MeasureSpec.EXACTLY:
-                measuredHeight = heightSize;
-                break;
-            case MeasureSpec.AT_MOST:
-                measuredHeight = Math.min(heightSize, height);
-                break;
-            default:
-            case MeasureSpec.UNSPECIFIED:
-                measuredHeight = height;
-                break;
-        }
-
-        setMeasuredDimension(measuredWidth, measuredHeight);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        if (mRemoteIndicator != null) {
-            final int left = getPaddingLeft();
-            final int right = getWidth() - getPaddingRight();
-            final int top = getPaddingTop();
-            final int bottom = getHeight() - getPaddingBottom();
-
-            final int drawWidth = mRemoteIndicator.getIntrinsicWidth();
-            final int drawHeight = mRemoteIndicator.getIntrinsicHeight();
-            final int drawLeft = left + (right - left - drawWidth) / 2;
-            final int drawTop = top + (bottom - top - drawHeight) / 2;
-
-            mRemoteIndicator.setBounds(drawLeft, drawTop,
-                    drawLeft + drawWidth, drawTop + drawHeight);
-            mRemoteIndicator.draw(canvas);
-        }
-    }
-
-    void refreshRoute() {
-        final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
-        final boolean isRemote = !route.isDefaultOrBluetooth() && route.matchesSelector(mSelector);
-        final boolean isConnecting = isRemote && route.isConnecting();
-        boolean needsRefresh = false;
-        if (mRemoteActive != isRemote) {
-            mRemoteActive = isRemote;
-            needsRefresh = true;
-        }
-        if (mIsConnecting != isConnecting) {
-            mIsConnecting = isConnecting;
-            needsRefresh = true;
-        }
-
-        if (needsRefresh) {
-            updateContentDescription();
-            refreshDrawableState();
-        }
-        if (mAttachedToWindow) {
-            setEnabled(mRouter.isRouteAvailable(mSelector,
-                    MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE));
-        }
-        if (mRemoteIndicator != null
-                && mRemoteIndicator.getCurrent() instanceof AnimationDrawable) {
-            AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent();
-            if (mAttachedToWindow) {
-                if ((needsRefresh || isConnecting) && !curDrawable.isRunning()) {
-                    curDrawable.start();
-                }
-            } else if (isRemote && !isConnecting) {
-                // When the route is already connected before the view is attached, show the last
-                // frame of the connected animation immediately.
-                if (curDrawable.isRunning()) {
-                    curDrawable.stop();
-                }
-                curDrawable.selectDrawable(curDrawable.getNumberOfFrames() - 1);
-            }
-        }
-    }
-
-    private void updateContentDescription() {
-        int resId;
-        if (mIsConnecting) {
-            resId = R.string.mr_cast_button_connecting;
-        } else if (mRemoteActive) {
-            resId = R.string.mr_cast_button_connected;
-        } else {
-            resId = R.string.mr_cast_button_disconnected;
-        }
-        setContentDescription(ApiHelper.getLibResources(getContext()).getString(resId));
-    }
-
-    private final class MediaRouterCallback extends MediaRouter.Callback {
-        MediaRouterCallback() {
-        }
-
-        @Override
-        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onProviderAdded(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onProviderRemoved(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onProviderChanged(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            refreshRoute();
-        }
-    }
-
-    private final class RemoteIndicatorLoader extends AsyncTask<Void, Void, Drawable> {
-        private final int mResId;
-
-        RemoteIndicatorLoader(int resId) {
-            mResId = resId;
-        }
-
-        @Override
-        protected Drawable doInBackground(Void... params) {
-            return ApiHelper.getLibResources(getContext()).getDrawable(mResId);
-        }
-
-        @Override
-        protected void onPostExecute(Drawable remoteIndicator) {
-            cacheAndReset(remoteIndicator);
-            setRemoteIndicatorDrawable(remoteIndicator);
-        }
-
-        @Override
-        protected void onCancelled(Drawable remoteIndicator) {
-            cacheAndReset(remoteIndicator);
-        }
-
-        private void cacheAndReset(Drawable remoteIndicator) {
-            if (remoteIndicator != null) {
-                sRemoteIndicatorCache.put(mResId, remoteIndicator.getConstantState());
-            }
-            mRemoteIndicatorLoader = null;
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java
deleted file mode 100644
index f24028a..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import static com.android.support.mediarouter.media.MediaRouter.RouteInfo
-        .CONNECTION_STATE_CONNECTED;
-import static com.android.support.mediarouter.media.MediaRouter.RouteInfo
-        .CONNECTION_STATE_CONNECTING;
-
-import android.annotation.NonNull;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-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;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * This class implements the route chooser dialog for {@link MediaRouter}.
- * <p>
- * This dialog allows the user to choose a route that matches a given selector.
- * </p>
- *
- * @see MediaRouteButton
- * @see MediaRouteActionProvider
- */
-public class MediaRouteChooserDialog extends Dialog {
-    static final String TAG = "MediaRouteChooserDialog";
-
-    // Do not update the route list immediately to avoid unnatural dialog change.
-    private static final long UPDATE_ROUTES_DELAY_MS = 300L;
-    static final int MSG_UPDATE_ROUTES = 1;
-
-    private final MediaRouter mRouter;
-    private final MediaRouterCallback mCallback;
-
-    private TextView mTitleView;
-    private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
-    private ArrayList<MediaRouter.RouteInfo> mRoutes;
-    private RouteAdapter mAdapter;
-    private ListView mListView;
-    private boolean mAttachedToWindow;
-    private long mLastUpdateTime;
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case MSG_UPDATE_ROUTES:
-                    updateRoutes((List<MediaRouter.RouteInfo>) message.obj);
-                    break;
-            }
-        }
-    };
-
-    public MediaRouteChooserDialog(Context context) {
-        this(context, 0);
-    }
-
-    public MediaRouteChooserDialog(Context context, int theme) {
-        // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme.
-        super(new ContextThemeWrapper(context, ApiHelper.getLibTheme(context,
-                MediaRouterThemeHelper.getRouterThemeId(context))), theme);
-        context = getContext();
-
-        mRouter = MediaRouter.getInstance(context);
-        mCallback = new MediaRouterCallback();
-    }
-
-    /**
-     * Gets the media route selector for filtering the routes that the user can select.
-     *
-     * @return The selector, never null.
-     */
-    @NonNull
-    public MediaRouteSelector getRouteSelector() {
-        return mSelector;
-    }
-
-    /**
-     * Sets the media route selector for filtering the routes that the user can select.
-     *
-     * @param selector The selector, must not be null.
-     */
-    public void setRouteSelector(@NonNull MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-
-        if (!mSelector.equals(selector)) {
-            mSelector = selector;
-
-            if (mAttachedToWindow) {
-                mRouter.removeCallback(mCallback);
-                mRouter.addCallback(selector, mCallback,
-                        MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-            }
-
-            refreshRoutes();
-        }
-    }
-
-    /**
-     * Called to filter the set of routes that should be included in the list.
-     * <p>
-     * The default implementation iterates over all routes in the provided list and
-     * removes those for which {@link #onFilterRoute} returns false.
-     * </p>
-     *
-     * @param routes The list of routes to filter in-place, never null.
-     */
-    public void onFilterRoutes(@NonNull List<MediaRouter.RouteInfo> routes) {
-        for (int i = routes.size(); i-- > 0; ) {
-            if (!onFilterRoute(routes.get(i))) {
-                routes.remove(i);
-            }
-        }
-    }
-
-    /**
-     * Returns true if the route should be included in the list.
-     * <p>
-     * The default implementation returns true for enabled non-default routes that
-     * match the selector.  Subclasses can override this method to filter routes
-     * differently.
-     * </p>
-     *
-     * @param route The route to consider, never null.
-     * @return True if the route should be included in the chooser dialog.
-     */
-    public boolean onFilterRoute(@NonNull MediaRouter.RouteInfo route) {
-        return !route.isDefaultOrBluetooth() && route.isEnabled()
-                && route.matchesSelector(mSelector);
-    }
-
-    @Override
-    public void setTitle(CharSequence title) {
-        mTitleView.setText(title);
-    }
-
-    @Override
-    public void setTitle(int titleId) {
-        mTitleView.setText(titleId);
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        setContentView(ApiHelper.inflateLibLayout(getContext(), ApiHelper.getLibTheme(getContext(),
-                MediaRouterThemeHelper.getRouterThemeId(getContext())),
-                R.layout.mr_chooser_dialog));
-
-        mRoutes = new ArrayList<>();
-        mAdapter = new RouteAdapter(getContext(), mRoutes);
-        mListView = (ListView)findViewById(R.id.mr_chooser_list);
-        mListView.setAdapter(mAdapter);
-        mListView.setOnItemClickListener(mAdapter);
-        mListView.setEmptyView(findViewById(android.R.id.empty));
-        mTitleView = findViewById(R.id.mr_chooser_title);
-
-        updateLayout();
-    }
-
-    /**
-     * Sets the width of the dialog. Also called when configuration changes.
-     */
-    void updateLayout() {
-        getWindow().setLayout(MediaRouteDialogHelper.getDialogWidth(getContext()),
-                ViewGroup.LayoutParams.WRAP_CONTENT);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        mAttachedToWindow = true;
-        mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-        refreshRoutes();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        mAttachedToWindow = false;
-        mRouter.removeCallback(mCallback);
-        mHandler.removeMessages(MSG_UPDATE_ROUTES);
-
-        super.onDetachedFromWindow();
-    }
-
-    /**
-     * Refreshes the list of routes that are shown in the chooser dialog.
-     */
-    public void refreshRoutes() {
-        if (mAttachedToWindow) {
-            ArrayList<MediaRouter.RouteInfo> routes = new ArrayList<>(mRouter.getRoutes());
-            onFilterRoutes(routes);
-            Collections.sort(routes, RouteComparator.sInstance);
-            if (SystemClock.uptimeMillis() - mLastUpdateTime >= UPDATE_ROUTES_DELAY_MS) {
-                updateRoutes(routes);
-            } else {
-                mHandler.removeMessages(MSG_UPDATE_ROUTES);
-                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ROUTES, routes),
-                        mLastUpdateTime + UPDATE_ROUTES_DELAY_MS);
-            }
-        }
-    }
-
-    void updateRoutes(List<MediaRouter.RouteInfo> routes) {
-        mLastUpdateTime = SystemClock.uptimeMillis();
-        mRoutes.clear();
-        mRoutes.addAll(routes);
-        mAdapter.notifyDataSetChanged();
-    }
-
-    private final class RouteAdapter extends ArrayAdapter<MediaRouter.RouteInfo>
-            implements ListView.OnItemClickListener {
-        private final Drawable mDefaultIcon;
-        private final Drawable mTvIcon;
-        private final Drawable mSpeakerIcon;
-        private final Drawable mSpeakerGroupIcon;
-
-        public RouteAdapter(Context context, List<MediaRouter.RouteInfo> routes) {
-            super(context, 0, routes);
-
-            TypedArray styledAttributes = ApiHelper.getLibTheme(context,
-                    MediaRouterThemeHelper.getRouterThemeId(context)).obtainStyledAttributes(
-                            new int[] {
-                                R.attr.mediaRouteDefaultIconDrawable,
-                                R.attr.mediaRouteTvIconDrawable,
-                                R.attr.mediaRouteSpeakerIconDrawable,
-                                R.attr.mediaRouteSpeakerGroupIconDrawable
-                            });
-
-            mDefaultIcon = styledAttributes.getDrawable(0);
-            mTvIcon = styledAttributes.getDrawable(1);
-            mSpeakerIcon = styledAttributes.getDrawable(2);
-            mSpeakerGroupIcon = styledAttributes.getDrawable(3);
-            styledAttributes.recycle();
-        }
-
-        @Override
-        public boolean areAllItemsEnabled() {
-            return false;
-        }
-
-        @Override
-        public boolean isEnabled(int position) {
-            return getItem(position).isEnabled();
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            View view = convertView;
-            if (view == null) {
-                view = ApiHelper.inflateLibLayout(getContext(),
-                        ApiHelper.getLibTheme(getContext(),
-                                MediaRouterThemeHelper.getRouterThemeId(getContext())),
-                        R.layout.mr_chooser_list_item, parent, false);
-            }
-
-            MediaRouter.RouteInfo route = getItem(position);
-            TextView text1 = (TextView) view.findViewById(R.id.mr_chooser_route_name);
-            TextView text2 = (TextView) view.findViewById(R.id.mr_chooser_route_desc);
-            text1.setText(route.getName());
-            String description = route.getDescription();
-            boolean isConnectedOrConnecting =
-                    route.getConnectionState() == CONNECTION_STATE_CONNECTED
-                            || route.getConnectionState() == CONNECTION_STATE_CONNECTING;
-            if (isConnectedOrConnecting && !TextUtils.isEmpty(description)) {
-                text1.setGravity(Gravity.BOTTOM);
-                text2.setVisibility(View.VISIBLE);
-                text2.setText(description);
-            } else {
-                text1.setGravity(Gravity.CENTER_VERTICAL);
-                text2.setVisibility(View.GONE);
-                text2.setText("");
-            }
-            view.setEnabled(route.isEnabled());
-
-            ImageView iconView = (ImageView) view.findViewById(R.id.mr_chooser_route_icon);
-            if (iconView != null) {
-                iconView.setImageDrawable(getIconDrawable(route));
-            }
-            return view;
-        }
-
-        @Override
-        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            MediaRouter.RouteInfo route = getItem(position);
-            if (route.isEnabled()) {
-                route.select();
-                dismiss();
-            }
-        }
-
-        private Drawable getIconDrawable(MediaRouter.RouteInfo route) {
-            Uri iconUri = route.getIconUri();
-            if (iconUri != null) {
-                try {
-                    InputStream is = getContext().getContentResolver().openInputStream(iconUri);
-                    Drawable drawable = Drawable.createFromStream(is, null);
-                    if (drawable != null) {
-                        return drawable;
-                    }
-                } catch (IOException e) {
-                    Log.w(TAG, "Failed to load " + iconUri, e);
-                    // Falls back.
-                }
-            }
-            return getDefaultIconDrawable(route);
-        }
-
-        private Drawable getDefaultIconDrawable(MediaRouter.RouteInfo route) {
-            // If the type of the receiver device is specified, use it.
-            switch (route.getDeviceType()) {
-                case  MediaRouter.RouteInfo.DEVICE_TYPE_TV:
-                    return mTvIcon;
-                case MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER:
-                    return mSpeakerIcon;
-            }
-
-            // Otherwise, make the best guess based on other route information.
-            if (route instanceof MediaRouter.RouteGroup) {
-                // Only speakers can be grouped for now.
-                return mSpeakerGroupIcon;
-            }
-            return mDefaultIcon;
-        }
-    }
-
-    private final class MediaRouterCallback extends MediaRouter.Callback {
-        MediaRouterCallback() {
-        }
-
-        @Override
-        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoutes();
-        }
-
-        @Override
-        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoutes();
-        }
-
-        @Override
-        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoutes();
-        }
-
-        @Override
-        public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
-            dismiss();
-        }
-    }
-
-    static final class RouteComparator implements Comparator<MediaRouter.RouteInfo> {
-        public static final RouteComparator sInstance = new RouteComparator();
-
-        @Override
-        public int compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs) {
-            return lhs.getName().compareToIgnoreCase(rhs.getName());
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialogFragment.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialogFragment.java
deleted file mode 100644
index 65e6b29..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialogFragment.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Bundle;
-
-import com.android.support.mediarouter.media.MediaRouteSelector;
-
-/**
- * Media route chooser dialog fragment.
- * <p>
- * Creates a {@link MediaRouteChooserDialog}.  The application may subclass
- * this dialog fragment to customize the media route chooser dialog.
- * </p>
- */
-public class MediaRouteChooserDialogFragment extends DialogFragment {
-    private final String ARGUMENT_SELECTOR = "selector";
-
-    private MediaRouteChooserDialog mDialog;
-    private MediaRouteSelector mSelector;
-
-    /**
-     * Creates a media route chooser dialog fragment.
-     * <p>
-     * All subclasses of this class must also possess a default constructor.
-     * </p>
-     */
-    public MediaRouteChooserDialogFragment() {
-        setCancelable(true);
-    }
-
-    /**
-     * Gets the media route selector for filtering the routes that the user can select.
-     *
-     * @return The selector, never null.
-     */
-    public MediaRouteSelector getRouteSelector() {
-        ensureRouteSelector();
-        return mSelector;
-    }
-
-    private void ensureRouteSelector() {
-        if (mSelector == null) {
-            Bundle args = getArguments();
-            if (args != null) {
-                mSelector = MediaRouteSelector.fromBundle(args.getBundle(ARGUMENT_SELECTOR));
-            }
-            if (mSelector == null) {
-                mSelector = MediaRouteSelector.EMPTY;
-            }
-        }
-    }
-
-    /**
-     * Sets the media route selector for filtering the routes that the user can select.
-     * This method must be called before the fragment is added.
-     *
-     * @param selector The selector to set.
-     */
-    public void setRouteSelector(MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-
-        ensureRouteSelector();
-        if (!mSelector.equals(selector)) {
-            mSelector = selector;
-
-            Bundle args = getArguments();
-            if (args == null) {
-                args = new Bundle();
-            }
-            args.putBundle(ARGUMENT_SELECTOR, selector.asBundle());
-            setArguments(args);
-
-            MediaRouteChooserDialog dialog = (MediaRouteChooserDialog)getDialog();
-            if (dialog != null) {
-                dialog.setRouteSelector(selector);
-            }
-        }
-    }
-
-    /**
-     * Called when the chooser dialog is being created.
-     * <p>
-     * Subclasses may override this method to customize the dialog.
-     * </p>
-     */
-    public MediaRouteChooserDialog onCreateChooserDialog(
-            Context context, Bundle savedInstanceState) {
-        return new MediaRouteChooserDialog(context);
-    }
-
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        mDialog = onCreateChooserDialog(getContext(), savedInstanceState);
-        mDialog.setRouteSelector(getRouteSelector());
-        return mDialog;
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        if (mDialog != null) {
-            mDialog.updateLayout();
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java
deleted file mode 100644
index f6c1d2f..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java
+++ /dev/null
@@ -1,1487 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE;
-import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY;
-import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_PAUSE;
-import static android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP;
-
-import android.app.AlertDialog;
-import android.app.PendingIntent;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.support.v4.media.MediaDescriptionCompat;
-import android.support.v4.media.MediaMetadataCompat;
-import android.support.v4.media.session.MediaControllerCompat;
-import android.support.v4.media.session.MediaSessionCompat;
-import android.support.v4.media.session.PlaybackStateCompat;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.Transformation;
-import android.view.animation.TranslateAnimation;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import androidx.core.util.ObjectsCompat;
-import androidx.core.view.accessibility.AccessibilityEventCompat;
-import androidx.palette.graphics.Palette;
-
-import com.android.media.update.ApiHelper;
-import com.android.media.update.R;
-import com.android.support.mediarouter.app.OverlayListView.OverlayObject;
-import com.android.support.mediarouter.media.MediaRouteSelector;
-import com.android.support.mediarouter.media.MediaRouter;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class implements the route controller dialog for {@link MediaRouter}.
- * <p>
- * This dialog allows the user to control or disconnect from the currently selected route.
- * </p>
- *
- * @see MediaRouteButton
- * @see MediaRouteActionProvider
- */
-public class MediaRouteControllerDialog extends AlertDialog {
-    // Tags should be less than 24 characters long (see docs for android.util.Log.isLoggable())
-    static final String TAG = "MediaRouteCtrlDialog";
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    // Time to wait before updating the volume when the user lets go of the seek bar
-    // to allow the route provider time to propagate the change and publish a new
-    // route descriptor.
-    static final int VOLUME_UPDATE_DELAY_MILLIS = 500;
-    static final int CONNECTION_TIMEOUT_MILLIS = (int) TimeUnit.SECONDS.toMillis(30L);
-
-    private static final int BUTTON_NEUTRAL_RES_ID = android.R.id.button3;
-    static final int BUTTON_DISCONNECT_RES_ID = android.R.id.button2;
-    static final int BUTTON_STOP_RES_ID = android.R.id.button1;
-
-    final MediaRouter mRouter;
-    private final MediaRouterCallback mCallback;
-    final MediaRouter.RouteInfo mRoute;
-
-    Context mContext;
-    private boolean mCreated;
-    private boolean mAttachedToWindow;
-
-    private int mDialogContentWidth;
-
-    private View mCustomControlView;
-
-    private Button mDisconnectButton;
-    private Button mStopCastingButton;
-    private ImageButton mPlaybackControlButton;
-    private ImageButton mCloseButton;
-    private MediaRouteExpandCollapseButton mGroupExpandCollapseButton;
-
-    private FrameLayout mExpandableAreaLayout;
-    private LinearLayout mDialogAreaLayout;
-    FrameLayout mDefaultControlLayout;
-    private FrameLayout mCustomControlLayout;
-    private ImageView mArtView;
-    private TextView mTitleView;
-    private TextView mSubtitleView;
-    private TextView mRouteNameTextView;
-
-    private boolean mVolumeControlEnabled = true;
-    // Layout for media controllers including play/pause button and the main volume slider.
-    private LinearLayout mMediaMainControlLayout;
-    private RelativeLayout mPlaybackControlLayout;
-    private LinearLayout mVolumeControlLayout;
-    private View mDividerView;
-
-    OverlayListView mVolumeGroupList;
-    VolumeGroupAdapter mVolumeGroupAdapter;
-    private List<MediaRouter.RouteInfo> mGroupMemberRoutes;
-    Set<MediaRouter.RouteInfo> mGroupMemberRoutesAdded;
-    private Set<MediaRouter.RouteInfo> mGroupMemberRoutesRemoved;
-    Set<MediaRouter.RouteInfo> mGroupMemberRoutesAnimatingWithBitmap;
-    SeekBar mVolumeSlider;
-    VolumeChangeListener mVolumeChangeListener;
-    MediaRouter.RouteInfo mRouteInVolumeSliderTouched;
-    private int mVolumeGroupListItemIconSize;
-    private int mVolumeGroupListItemHeight;
-    private int mVolumeGroupListMaxHeight;
-    private final int mVolumeGroupListPaddingTop;
-    Map<MediaRouter.RouteInfo, SeekBar> mVolumeSliderMap;
-
-    MediaControllerCompat mMediaController;
-    MediaControllerCallback mControllerCallback;
-    PlaybackStateCompat mState;
-    MediaDescriptionCompat mDescription;
-
-    FetchArtTask mFetchArtTask;
-    Bitmap mArtIconBitmap;
-    Uri mArtIconUri;
-    boolean mArtIconIsLoaded;
-    Bitmap mArtIconLoadedBitmap;
-    int mArtIconBackgroundColor;
-
-    boolean mHasPendingUpdate;
-    boolean mPendingUpdateAnimationNeeded;
-
-    boolean mIsGroupExpanded;
-    boolean mIsGroupListAnimating;
-    boolean mIsGroupListAnimationPending;
-    int mGroupListAnimationDurationMs;
-    private int mGroupListFadeInDurationMs;
-    private int mGroupListFadeOutDurationMs;
-
-    private Interpolator mInterpolator;
-    private Interpolator mLinearOutSlowInInterpolator;
-    private Interpolator mFastOutSlowInInterpolator;
-    private Interpolator mAccelerateDecelerateInterpolator;
-
-    final AccessibilityManager mAccessibilityManager;
-
-    Runnable mGroupListFadeInAnimation = new Runnable() {
-        @Override
-        public void run() {
-            startGroupListFadeInAnimation();
-        }
-    };
-
-    public MediaRouteControllerDialog(Context context) {
-        this(context, 0);
-    }
-
-    public MediaRouteControllerDialog(Context context, int theme) {
-        // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme.
-        super(new ContextThemeWrapper(context, ApiHelper.getLibTheme(context,
-                MediaRouterThemeHelper.getRouterThemeId(context))), theme);
-        mContext = getContext();
-
-        mControllerCallback = new MediaControllerCallback();
-        mRouter = MediaRouter.getInstance(mContext);
-        mCallback = new MediaRouterCallback();
-        mRoute = mRouter.getSelectedRoute();
-        setMediaSession(mRouter.getMediaSessionToken());
-        mVolumeGroupListPaddingTop = ApiHelper.getLibResources(context).getDimensionPixelSize(
-                R.dimen.mr_controller_volume_group_list_padding_top);
-        mAccessibilityManager =
-                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
-                mContext, android.R.interpolator.linear_out_slow_in);
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
-                mContext, android.R.interpolator.fast_out_slow_in);
-        mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();
-    }
-
-    /**
-     * Gets the route that this dialog is controlling.
-     */
-    public MediaRouter.RouteInfo getRoute() {
-        return mRoute;
-    }
-
-    private MediaRouter.RouteGroup getGroup() {
-        if (mRoute instanceof MediaRouter.RouteGroup) {
-            return (MediaRouter.RouteGroup) mRoute;
-        }
-        return null;
-    }
-
-    /**
-     * Provides the subclass an opportunity to create a view that will replace the default media
-     * controls for the currently playing content.
-     *
-     * @param savedInstanceState The dialog's saved instance state.
-     * @return The media control view, or null if none.
-     */
-    public View onCreateMediaControlView(Bundle savedInstanceState) {
-        return null;
-    }
-
-    /**
-     * Gets the media control view that was created by {@link #onCreateMediaControlView(Bundle)}.
-     *
-     * @return The media control view, or null if none.
-     */
-    public View getMediaControlView() {
-        return mCustomControlView;
-    }
-
-    /**
-     * Sets whether to enable the volume slider and volume control using the volume keys
-     * when the route supports it.
-     * <p>
-     * The default value is true.
-     * </p>
-     */
-    public void setVolumeControlEnabled(boolean enable) {
-        if (mVolumeControlEnabled != enable) {
-            mVolumeControlEnabled = enable;
-            if (mCreated) {
-                update(false);
-            }
-        }
-    }
-
-    /**
-     * Returns whether to enable the volume slider and volume control using the volume keys
-     * when the route supports it.
-     */
-    public boolean isVolumeControlEnabled() {
-        return mVolumeControlEnabled;
-    }
-
-    /**
-     * Set the session to use for metadata and transport controls. The dialog
-     * will listen to changes on this session and update the UI automatically in
-     * response to changes.
-     *
-     * @param sessionToken The token for the session to use.
-     */
-    private void setMediaSession(MediaSessionCompat.Token sessionToken) {
-        if (mMediaController != null) {
-            mMediaController.unregisterCallback(mControllerCallback);
-            mMediaController = null;
-        }
-        if (sessionToken == null) {
-            return;
-        }
-        if (!mAttachedToWindow) {
-            return;
-        }
-        try {
-            mMediaController = new MediaControllerCompat(mContext, sessionToken);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error creating media controller in setMediaSession.", e);
-        }
-        if (mMediaController != null) {
-            mMediaController.registerCallback(mControllerCallback);
-        }
-        MediaMetadataCompat metadata = mMediaController == null ? null
-                : mMediaController.getMetadata();
-        mDescription = metadata == null ? null : metadata.getDescription();
-        mState = mMediaController == null ? null : mMediaController.getPlaybackState();
-        updateArtIconIfNeeded();
-        update(false);
-    }
-
-    /**
-     * Gets the session to use for metadata and transport controls.
-     *
-     * @return The token for the session to use or null if none.
-     */
-    public MediaSessionCompat.Token getMediaSession() {
-        return mMediaController == null ? null : mMediaController.getSessionToken();
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        getWindow().setBackgroundDrawableResource(android.R.color.transparent);
-
-        setContentView(ApiHelper.inflateLibLayout(mContext,
-                ApiHelper.getLibTheme(mContext, MediaRouterThemeHelper.getRouterThemeId(mContext)),
-                R.layout.mr_controller_material_dialog_b));
-
-        // Remove the neutral button.
-        findViewById(BUTTON_NEUTRAL_RES_ID).setVisibility(View.GONE);
-
-        ClickListener listener = new ClickListener();
-
-        mExpandableAreaLayout = findViewById(R.id.mr_expandable_area);
-        mExpandableAreaLayout.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                dismiss();
-            }
-        });
-        mDialogAreaLayout = findViewById(R.id.mr_dialog_area);
-        mDialogAreaLayout.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                // Eat unhandled touch events.
-            }
-        });
-        int color = MediaRouterThemeHelper.getButtonTextColor(mContext);
-        mDisconnectButton = findViewById(BUTTON_DISCONNECT_RES_ID);
-        mDisconnectButton.setText(
-                ApiHelper.getLibResources(mContext).getString(R.string.mr_controller_disconnect));
-        mDisconnectButton.setTextColor(color);
-        mDisconnectButton.setOnClickListener(listener);
-
-        mStopCastingButton = findViewById(BUTTON_STOP_RES_ID);
-        mStopCastingButton.setText(
-                ApiHelper.getLibResources(mContext).getString(R.string.mr_controller_stop_casting));
-        mStopCastingButton.setTextColor(color);
-        mStopCastingButton.setOnClickListener(listener);
-
-        mRouteNameTextView = findViewById(R.id.mr_name);
-        mCloseButton = findViewById(R.id.mr_close);
-        mCloseButton.setOnClickListener(listener);
-        mCustomControlLayout = findViewById(R.id.mr_custom_control);
-        mDefaultControlLayout = findViewById(R.id.mr_default_control);
-
-        // Start the session activity when a content item (album art, title or subtitle) is clicked.
-        View.OnClickListener onClickListener = new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mMediaController != null) {
-                    PendingIntent pi = mMediaController.getSessionActivity();
-                    if (pi != null) {
-                        try {
-                            pi.send();
-                            dismiss();
-                        } catch (PendingIntent.CanceledException e) {
-                            Log.e(TAG, pi + " was not sent, it had been canceled.");
-                        }
-                    }
-                }
-            }
-        };
-        mArtView = findViewById(R.id.mr_art);
-        mArtView.setOnClickListener(onClickListener);
-        findViewById(R.id.mr_control_title_container).setOnClickListener(onClickListener);
-
-        mMediaMainControlLayout = findViewById(R.id.mr_media_main_control);
-        mDividerView = findViewById(R.id.mr_control_divider);
-
-        mPlaybackControlLayout = findViewById(R.id.mr_playback_control);
-        mTitleView = findViewById(R.id.mr_control_title);
-        mSubtitleView = findViewById(R.id.mr_control_subtitle);
-        mPlaybackControlButton = findViewById(R.id.mr_control_playback_ctrl);
-        mPlaybackControlButton.setOnClickListener(listener);
-
-        mVolumeControlLayout = findViewById(R.id.mr_volume_control);
-        mVolumeControlLayout.setVisibility(View.GONE);
-        mVolumeSlider = findViewById(R.id.mr_volume_slider);
-        mVolumeSlider.setTag(mRoute);
-        mVolumeChangeListener = new VolumeChangeListener();
-        mVolumeSlider.setOnSeekBarChangeListener(mVolumeChangeListener);
-
-        mVolumeGroupList = findViewById(R.id.mr_volume_group_list);
-        mGroupMemberRoutes = new ArrayList<MediaRouter.RouteInfo>();
-        mVolumeGroupAdapter = new VolumeGroupAdapter(mVolumeGroupList.getContext(),
-                mGroupMemberRoutes);
-        mVolumeGroupList.setAdapter(mVolumeGroupAdapter);
-        mGroupMemberRoutesAnimatingWithBitmap = new HashSet<>();
-
-        MediaRouterThemeHelper.setMediaControlsBackgroundColor(mContext,
-                mMediaMainControlLayout, mVolumeGroupList, getGroup() != null);
-        MediaRouterThemeHelper.setVolumeSliderColor(mContext,
-                (MediaRouteVolumeSlider) mVolumeSlider, mMediaMainControlLayout);
-        mVolumeSliderMap = new HashMap<>();
-        mVolumeSliderMap.put(mRoute, mVolumeSlider);
-
-        mGroupExpandCollapseButton =
-                findViewById(R.id.mr_group_expand_collapse);
-        mGroupExpandCollapseButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mIsGroupExpanded = !mIsGroupExpanded;
-                if (mIsGroupExpanded) {
-                    mVolumeGroupList.setVisibility(View.VISIBLE);
-                }
-                loadInterpolator();
-                updateLayoutHeight(true);
-            }
-        });
-        loadInterpolator();
-        mGroupListAnimationDurationMs = ApiHelper.getLibResources(mContext).getInteger(
-                R.integer.mr_controller_volume_group_list_animation_duration_ms);
-        mGroupListFadeInDurationMs = ApiHelper.getLibResources(mContext).getInteger(
-                R.integer.mr_controller_volume_group_list_fade_in_duration_ms);
-        mGroupListFadeOutDurationMs = ApiHelper.getLibResources(mContext).getInteger(
-                R.integer.mr_controller_volume_group_list_fade_out_duration_ms);
-
-        mCustomControlView = onCreateMediaControlView(savedInstanceState);
-        if (mCustomControlView != null) {
-            mCustomControlLayout.addView(mCustomControlView);
-            mCustomControlLayout.setVisibility(View.VISIBLE);
-        }
-        mCreated = true;
-        updateLayout();
-    }
-
-    /**
-     * Sets the width of the dialog. Also called when configuration changes.
-     */
-    void updateLayout() {
-        int width = MediaRouteDialogHelper.getDialogWidth(mContext);
-        getWindow().setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT);
-
-        View decorView = getWindow().getDecorView();
-        mDialogContentWidth = width - decorView.getPaddingLeft() - decorView.getPaddingRight();
-
-        Resources res = ApiHelper.getLibResources(mContext);
-        mVolumeGroupListItemIconSize = res.getDimensionPixelSize(
-                R.dimen.mr_controller_volume_group_list_item_icon_size);
-        mVolumeGroupListItemHeight = res.getDimensionPixelSize(
-                R.dimen.mr_controller_volume_group_list_item_height);
-        mVolumeGroupListMaxHeight = res.getDimensionPixelSize(
-                R.dimen.mr_controller_volume_group_list_max_height);
-
-        // Fetch art icons again for layout changes to resize it accordingly
-        mArtIconBitmap = null;
-        mArtIconUri = null;
-        updateArtIconIfNeeded();
-        update(false);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mAttachedToWindow = true;
-
-        mRouter.addCallback(MediaRouteSelector.EMPTY, mCallback,
-                MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
-        setMediaSession(mRouter.getMediaSessionToken());
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        mRouter.removeCallback(mCallback);
-        setMediaSession(null);
-        mAttachedToWindow = false;
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
-                || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
-            mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
-            return true;
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
-                || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
-            return true;
-        }
-        return super.onKeyUp(keyCode, event);
-    }
-
-    void update(boolean animate) {
-        // Defer dialog updates if a user is adjusting a volume in the list
-        if (mRouteInVolumeSliderTouched != null) {
-            mHasPendingUpdate = true;
-            mPendingUpdateAnimationNeeded |= animate;
-            return;
-        }
-        mHasPendingUpdate = false;
-        mPendingUpdateAnimationNeeded = false;
-        if (!mRoute.isSelected() || mRoute.isDefaultOrBluetooth()) {
-            dismiss();
-            return;
-        }
-        if (!mCreated) {
-            return;
-        }
-
-        mRouteNameTextView.setText(mRoute.getName());
-        mDisconnectButton.setVisibility(mRoute.canDisconnect() ? View.VISIBLE : View.GONE);
-        if (mCustomControlView == null && mArtIconIsLoaded) {
-            if (isBitmapRecycled(mArtIconLoadedBitmap)) {
-                Log.w(TAG, "Can't set artwork image with recycled bitmap: " + mArtIconLoadedBitmap);
-            } else {
-                mArtView.setImageBitmap(mArtIconLoadedBitmap);
-                mArtView.setBackgroundColor(mArtIconBackgroundColor);
-            }
-            clearLoadedBitmap();
-        }
-        updateVolumeControlLayout();
-        updatePlaybackControlLayout();
-        updateLayoutHeight(animate);
-    }
-
-    private boolean isBitmapRecycled(Bitmap bitmap) {
-        return bitmap != null && bitmap.isRecycled();
-    }
-
-    private boolean canShowPlaybackControlLayout() {
-        return mCustomControlView == null && (mDescription != null || mState != null);
-    }
-
-    /**
-     * Returns the height of main media controller which includes playback control and master
-     * volume control.
-     */
-    private int getMainControllerHeight(boolean showPlaybackControl) {
-        int height = 0;
-        if (showPlaybackControl || mVolumeControlLayout.getVisibility() == View.VISIBLE) {
-            height += mMediaMainControlLayout.getPaddingTop()
-                    + mMediaMainControlLayout.getPaddingBottom();
-            if (showPlaybackControl) {
-                height +=  mPlaybackControlLayout.getMeasuredHeight();
-            }
-            if (mVolumeControlLayout.getVisibility() == View.VISIBLE) {
-                height += mVolumeControlLayout.getMeasuredHeight();
-            }
-            if (showPlaybackControl && mVolumeControlLayout.getVisibility() == View.VISIBLE) {
-                height += mDividerView.getMeasuredHeight();
-            }
-        }
-        return height;
-    }
-
-    private void updateMediaControlVisibility(boolean canShowPlaybackControlLayout) {
-        // TODO: Update the top and bottom padding of the control layout according to the display
-        // height.
-        mDividerView.setVisibility((mVolumeControlLayout.getVisibility() == View.VISIBLE
-                && canShowPlaybackControlLayout) ? View.VISIBLE : View.GONE);
-        mMediaMainControlLayout.setVisibility((mVolumeControlLayout.getVisibility() == View.GONE
-                && !canShowPlaybackControlLayout) ? View.GONE : View.VISIBLE);
-    }
-
-    void updateLayoutHeight(final boolean animate) {
-        // We need to defer the update until the first layout has occurred, as we don't yet know the
-        // overall visible display size in which the window this view is attached to has been
-        // positioned in.
-        mDefaultControlLayout.requestLayout();
-        ViewTreeObserver observer = mDefaultControlLayout.getViewTreeObserver();
-        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
-            @Override
-            public void onGlobalLayout() {
-                mDefaultControlLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
-                if (mIsGroupListAnimating) {
-                    mIsGroupListAnimationPending = true;
-                } else {
-                    updateLayoutHeightInternal(animate);
-                }
-            }
-        });
-    }
-
-    /**
-     * Updates the height of views and hide artwork or metadata if space is limited.
-     */
-    void updateLayoutHeightInternal(boolean animate) {
-        // Measure the size of widgets and get the height of main components.
-        int oldHeight = getLayoutHeight(mMediaMainControlLayout);
-        setLayoutHeight(mMediaMainControlLayout, ViewGroup.LayoutParams.MATCH_PARENT);
-        updateMediaControlVisibility(canShowPlaybackControlLayout());
-        View decorView = getWindow().getDecorView();
-        decorView.measure(
-                MeasureSpec.makeMeasureSpec(getWindow().getAttributes().width, MeasureSpec.EXACTLY),
-                MeasureSpec.UNSPECIFIED);
-        setLayoutHeight(mMediaMainControlLayout, oldHeight);
-        int artViewHeight = 0;
-        if (mCustomControlView == null && mArtView.getDrawable() instanceof BitmapDrawable) {
-            Bitmap art = ((BitmapDrawable) mArtView.getDrawable()).getBitmap();
-            if (art != null) {
-                artViewHeight = getDesiredArtHeight(art.getWidth(), art.getHeight());
-                mArtView.setScaleType(art.getWidth() >= art.getHeight()
-                        ? ImageView.ScaleType.FIT_XY : ImageView.ScaleType.FIT_CENTER);
-            }
-        }
-        int mainControllerHeight = getMainControllerHeight(canShowPlaybackControlLayout());
-        int volumeGroupListCount = mGroupMemberRoutes.size();
-        // Scale down volume group list items in landscape mode.
-        int expandedGroupListHeight = getGroup() == null ? 0 :
-                mVolumeGroupListItemHeight * getGroup().getRoutes().size();
-        if (volumeGroupListCount > 0) {
-            expandedGroupListHeight += mVolumeGroupListPaddingTop;
-        }
-        expandedGroupListHeight = Math.min(expandedGroupListHeight, mVolumeGroupListMaxHeight);
-        int visibleGroupListHeight = mIsGroupExpanded ? expandedGroupListHeight : 0;
-
-        int desiredControlLayoutHeight =
-                Math.max(artViewHeight, visibleGroupListHeight) + mainControllerHeight;
-        Rect visibleRect = new Rect();
-        decorView.getWindowVisibleDisplayFrame(visibleRect);
-        // Height of non-control views in decor view.
-        // This includes title bar, button bar, and dialog's vertical padding which should be
-        // always shown.
-        int nonControlViewHeight = mDialogAreaLayout.getMeasuredHeight()
-                - mDefaultControlLayout.getMeasuredHeight();
-        // Maximum allowed height for controls to fit screen.
-        int maximumControlViewHeight = visibleRect.height() - nonControlViewHeight;
-
-        // Show artwork if it fits the screen.
-        if (mCustomControlView == null && artViewHeight > 0
-                && desiredControlLayoutHeight <= maximumControlViewHeight) {
-            mArtView.setVisibility(View.VISIBLE);
-            setLayoutHeight(mArtView, artViewHeight);
-        } else {
-            if (getLayoutHeight(mVolumeGroupList) + mMediaMainControlLayout.getMeasuredHeight()
-                    >= mDefaultControlLayout.getMeasuredHeight()) {
-                mArtView.setVisibility(View.GONE);
-            }
-            artViewHeight = 0;
-            desiredControlLayoutHeight = visibleGroupListHeight + mainControllerHeight;
-        }
-        // Show the playback control if it fits the screen.
-        if (canShowPlaybackControlLayout()
-                && desiredControlLayoutHeight <= maximumControlViewHeight) {
-            mPlaybackControlLayout.setVisibility(View.VISIBLE);
-        } else {
-            mPlaybackControlLayout.setVisibility(View.GONE);
-        }
-        updateMediaControlVisibility(mPlaybackControlLayout.getVisibility() == View.VISIBLE);
-        mainControllerHeight = getMainControllerHeight(
-                mPlaybackControlLayout.getVisibility() == View.VISIBLE);
-        desiredControlLayoutHeight =
-                Math.max(artViewHeight, visibleGroupListHeight) + mainControllerHeight;
-
-        // Limit the volume group list height to fit the screen.
-        if (desiredControlLayoutHeight > maximumControlViewHeight) {
-            visibleGroupListHeight -= (desiredControlLayoutHeight - maximumControlViewHeight);
-            desiredControlLayoutHeight = maximumControlViewHeight;
-        }
-        // Update the layouts with the computed heights.
-        mMediaMainControlLayout.clearAnimation();
-        mVolumeGroupList.clearAnimation();
-        mDefaultControlLayout.clearAnimation();
-        if (animate) {
-            animateLayoutHeight(mMediaMainControlLayout, mainControllerHeight);
-            animateLayoutHeight(mVolumeGroupList, visibleGroupListHeight);
-            animateLayoutHeight(mDefaultControlLayout, desiredControlLayoutHeight);
-        } else {
-            setLayoutHeight(mMediaMainControlLayout, mainControllerHeight);
-            setLayoutHeight(mVolumeGroupList, visibleGroupListHeight);
-            setLayoutHeight(mDefaultControlLayout, desiredControlLayoutHeight);
-        }
-        // Maximize the window size with a transparent layout in advance for smooth animation.
-        setLayoutHeight(mExpandableAreaLayout, visibleRect.height());
-        rebuildVolumeGroupList(animate);
-    }
-
-    void updateVolumeGroupItemHeight(View item) {
-        LinearLayout container = (LinearLayout) item.findViewById(R.id.volume_item_container);
-        setLayoutHeight(container, mVolumeGroupListItemHeight);
-        View icon = item.findViewById(R.id.mr_volume_item_icon);
-        ViewGroup.LayoutParams lp = icon.getLayoutParams();
-        lp.width = mVolumeGroupListItemIconSize;
-        lp.height = mVolumeGroupListItemIconSize;
-        icon.setLayoutParams(lp);
-    }
-
-    private void animateLayoutHeight(final View view, int targetHeight) {
-        final int startValue = getLayoutHeight(view);
-        final int endValue = targetHeight;
-        Animation anim = new Animation() {
-            @Override
-            protected void applyTransformation(float interpolatedTime, Transformation t) {
-                int height = startValue - (int) ((startValue - endValue) * interpolatedTime);
-                setLayoutHeight(view, height);
-            }
-        };
-        anim.setDuration(mGroupListAnimationDurationMs);
-        if (android.os.Build.VERSION.SDK_INT >= 21) {
-            anim.setInterpolator(mInterpolator);
-        }
-        view.startAnimation(anim);
-    }
-
-    void loadInterpolator() {
-        mInterpolator =
-                mIsGroupExpanded ? mLinearOutSlowInInterpolator : mFastOutSlowInInterpolator;
-    }
-
-    private void updateVolumeControlLayout() {
-        if (isVolumeControlAvailable(mRoute)) {
-            if (mVolumeControlLayout.getVisibility() == View.GONE) {
-                mVolumeControlLayout.setVisibility(View.VISIBLE);
-                mVolumeSlider.setMax(mRoute.getVolumeMax());
-                mVolumeSlider.setProgress(mRoute.getVolume());
-                mGroupExpandCollapseButton.setVisibility(getGroup() == null ? View.GONE
-                        : View.VISIBLE);
-            }
-        } else {
-            mVolumeControlLayout.setVisibility(View.GONE);
-        }
-    }
-
-    private void rebuildVolumeGroupList(boolean animate) {
-        List<MediaRouter.RouteInfo> routes = getGroup() == null ? null : getGroup().getRoutes();
-        if (routes == null) {
-            mGroupMemberRoutes.clear();
-            mVolumeGroupAdapter.notifyDataSetChanged();
-        } else if (MediaRouteDialogHelper.listUnorderedEquals(mGroupMemberRoutes, routes)) {
-            mVolumeGroupAdapter.notifyDataSetChanged();
-        } else {
-            HashMap<MediaRouter.RouteInfo, Rect> previousRouteBoundMap = animate
-                    ? MediaRouteDialogHelper.getItemBoundMap(mVolumeGroupList, mVolumeGroupAdapter)
-                    : null;
-            HashMap<MediaRouter.RouteInfo, BitmapDrawable> previousRouteBitmapMap = animate
-                    ? MediaRouteDialogHelper.getItemBitmapMap(mContext, mVolumeGroupList,
-                            mVolumeGroupAdapter) : null;
-            mGroupMemberRoutesAdded =
-                    MediaRouteDialogHelper.getItemsAdded(mGroupMemberRoutes, routes);
-            mGroupMemberRoutesRemoved = MediaRouteDialogHelper.getItemsRemoved(mGroupMemberRoutes,
-                    routes);
-            mGroupMemberRoutes.addAll(0, mGroupMemberRoutesAdded);
-            mGroupMemberRoutes.removeAll(mGroupMemberRoutesRemoved);
-            mVolumeGroupAdapter.notifyDataSetChanged();
-            if (animate && mIsGroupExpanded
-                    && mGroupMemberRoutesAdded.size() + mGroupMemberRoutesRemoved.size() > 0) {
-                animateGroupListItems(previousRouteBoundMap, previousRouteBitmapMap);
-            } else {
-                mGroupMemberRoutesAdded = null;
-                mGroupMemberRoutesRemoved = null;
-            }
-        }
-    }
-
-    private void animateGroupListItems(final Map<MediaRouter.RouteInfo, Rect> previousRouteBoundMap,
-            final Map<MediaRouter.RouteInfo, BitmapDrawable> previousRouteBitmapMap) {
-        mVolumeGroupList.setEnabled(false);
-        mVolumeGroupList.requestLayout();
-        mIsGroupListAnimating = true;
-        ViewTreeObserver observer = mVolumeGroupList.getViewTreeObserver();
-        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
-            @Override
-            public void onGlobalLayout() {
-                mVolumeGroupList.getViewTreeObserver().removeGlobalOnLayoutListener(this);
-                animateGroupListItemsInternal(previousRouteBoundMap, previousRouteBitmapMap);
-            }
-        });
-    }
-
-    void animateGroupListItemsInternal(
-            Map<MediaRouter.RouteInfo, Rect> previousRouteBoundMap,
-            Map<MediaRouter.RouteInfo, BitmapDrawable> previousRouteBitmapMap) {
-        if (mGroupMemberRoutesAdded == null || mGroupMemberRoutesRemoved == null) {
-            return;
-        }
-        int groupSizeDelta = mGroupMemberRoutesAdded.size() - mGroupMemberRoutesRemoved.size();
-        boolean listenerRegistered = false;
-        Animation.AnimationListener listener = new Animation.AnimationListener() {
-            @Override
-            public void onAnimationStart(Animation animation) {
-                mVolumeGroupList.startAnimationAll();
-                mVolumeGroupList.postDelayed(mGroupListFadeInAnimation,
-                        mGroupListAnimationDurationMs);
-            }
-
-            @Override
-            public void onAnimationEnd(Animation animation) { }
-
-            @Override
-            public void onAnimationRepeat(Animation animation) { }
-        };
-
-        // Animate visible items from previous positions to current positions except routes added
-        // just before. Added routes will remain hidden until translate animation finishes.
-        int first = mVolumeGroupList.getFirstVisiblePosition();
-        for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) {
-            View view = mVolumeGroupList.getChildAt(i);
-            int position = first + i;
-            MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position);
-            Rect previousBounds = previousRouteBoundMap.get(route);
-            int currentTop = view.getTop();
-            int previousTop = previousBounds != null ? previousBounds.top
-                    : (currentTop + mVolumeGroupListItemHeight * groupSizeDelta);
-            AnimationSet animSet = new AnimationSet(true);
-            if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.contains(route)) {
-                previousTop = currentTop;
-                Animation alphaAnim = new AlphaAnimation(0.0f, 0.0f);
-                alphaAnim.setDuration(mGroupListFadeInDurationMs);
-                animSet.addAnimation(alphaAnim);
-            }
-            Animation translationAnim = new TranslateAnimation(0, 0, previousTop - currentTop, 0);
-            translationAnim.setDuration(mGroupListAnimationDurationMs);
-            animSet.addAnimation(translationAnim);
-            animSet.setFillAfter(true);
-            animSet.setFillEnabled(true);
-            animSet.setInterpolator(mInterpolator);
-            if (!listenerRegistered) {
-                listenerRegistered = true;
-                animSet.setAnimationListener(listener);
-            }
-            view.clearAnimation();
-            view.startAnimation(animSet);
-            previousRouteBoundMap.remove(route);
-            previousRouteBitmapMap.remove(route);
-        }
-
-        // If a member route doesn't exist any longer, it can be either removed or moved out of the
-        // ListView layout boundary. In this case, use the previously captured bitmaps for
-        // animation.
-        for (Map.Entry<MediaRouter.RouteInfo, BitmapDrawable> item
-                : previousRouteBitmapMap.entrySet()) {
-            final MediaRouter.RouteInfo route = item.getKey();
-            final BitmapDrawable bitmap = item.getValue();
-            final Rect bounds = previousRouteBoundMap.get(route);
-            OverlayObject object = null;
-            if (mGroupMemberRoutesRemoved.contains(route)) {
-                object = new OverlayObject(bitmap, bounds).setAlphaAnimation(1.0f, 0.0f)
-                        .setDuration(mGroupListFadeOutDurationMs)
-                        .setInterpolator(mInterpolator);
-            } else {
-                int deltaY = groupSizeDelta * mVolumeGroupListItemHeight;
-                object = new OverlayObject(bitmap, bounds).setTranslateYAnimation(deltaY)
-                        .setDuration(mGroupListAnimationDurationMs)
-                        .setInterpolator(mInterpolator)
-                        .setAnimationEndListener(new OverlayObject.OnAnimationEndListener() {
-                            @Override
-                            public void onAnimationEnd() {
-                                mGroupMemberRoutesAnimatingWithBitmap.remove(route);
-                                mVolumeGroupAdapter.notifyDataSetChanged();
-                            }
-                        });
-                mGroupMemberRoutesAnimatingWithBitmap.add(route);
-            }
-            mVolumeGroupList.addOverlayObject(object);
-        }
-    }
-
-    void startGroupListFadeInAnimation() {
-        clearGroupListAnimation(true);
-        mVolumeGroupList.requestLayout();
-        ViewTreeObserver observer = mVolumeGroupList.getViewTreeObserver();
-        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
-            @Override
-            public void onGlobalLayout() {
-                mVolumeGroupList.getViewTreeObserver().removeGlobalOnLayoutListener(this);
-                startGroupListFadeInAnimationInternal();
-            }
-        });
-    }
-
-    void startGroupListFadeInAnimationInternal() {
-        if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.size() != 0) {
-            fadeInAddedRoutes();
-        } else {
-            finishAnimation(true);
-        }
-    }
-
-    void finishAnimation(boolean animate) {
-        mGroupMemberRoutesAdded = null;
-        mGroupMemberRoutesRemoved = null;
-        mIsGroupListAnimating = false;
-        if (mIsGroupListAnimationPending) {
-            mIsGroupListAnimationPending = false;
-            updateLayoutHeight(animate);
-        }
-        mVolumeGroupList.setEnabled(true);
-    }
-
-    private void fadeInAddedRoutes() {
-        Animation.AnimationListener listener = new Animation.AnimationListener() {
-            @Override
-            public void onAnimationStart(Animation animation) { }
-
-            @Override
-            public void onAnimationEnd(Animation animation) {
-                finishAnimation(true);
-            }
-
-            @Override
-            public void onAnimationRepeat(Animation animation) { }
-        };
-        boolean listenerRegistered = false;
-        int first = mVolumeGroupList.getFirstVisiblePosition();
-        for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) {
-            View view = mVolumeGroupList.getChildAt(i);
-            int position = first + i;
-            MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position);
-            if (mGroupMemberRoutesAdded.contains(route)) {
-                Animation alphaAnim = new AlphaAnimation(0.0f, 1.0f);
-                alphaAnim.setDuration(mGroupListFadeInDurationMs);
-                alphaAnim.setFillEnabled(true);
-                alphaAnim.setFillAfter(true);
-                if (!listenerRegistered) {
-                    listenerRegistered = true;
-                    alphaAnim.setAnimationListener(listener);
-                }
-                view.clearAnimation();
-                view.startAnimation(alphaAnim);
-            }
-        }
-    }
-
-    void clearGroupListAnimation(boolean exceptAddedRoutes) {
-        int first = mVolumeGroupList.getFirstVisiblePosition();
-        for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) {
-            View view = mVolumeGroupList.getChildAt(i);
-            int position = first + i;
-            MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position);
-            if (exceptAddedRoutes && mGroupMemberRoutesAdded != null
-                    && mGroupMemberRoutesAdded.contains(route)) {
-                continue;
-            }
-            LinearLayout container = (LinearLayout) view.findViewById(R.id.volume_item_container);
-            container.setVisibility(View.VISIBLE);
-            AnimationSet animSet = new AnimationSet(true);
-            Animation alphaAnim = new AlphaAnimation(1.0f, 1.0f);
-            alphaAnim.setDuration(0);
-            animSet.addAnimation(alphaAnim);
-            Animation translationAnim = new TranslateAnimation(0, 0, 0, 0);
-            translationAnim.setDuration(0);
-            animSet.setFillAfter(true);
-            animSet.setFillEnabled(true);
-            view.clearAnimation();
-            view.startAnimation(animSet);
-        }
-        mVolumeGroupList.stopAnimationAll();
-        if (!exceptAddedRoutes) {
-            finishAnimation(false);
-        }
-    }
-
-    private void updatePlaybackControlLayout() {
-        if (canShowPlaybackControlLayout()) {
-            CharSequence title = mDescription == null ? null : mDescription.getTitle();
-            boolean hasTitle = !TextUtils.isEmpty(title);
-
-            CharSequence subtitle = mDescription == null ? null : mDescription.getSubtitle();
-            boolean hasSubtitle = !TextUtils.isEmpty(subtitle);
-
-            boolean showTitle = false;
-            boolean showSubtitle = false;
-            if (mRoute.getPresentationDisplayId()
-                    != MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE) {
-                // The user is currently casting screen.
-                mTitleView.setText(ApiHelper.getLibResources(mContext).getString(
-                        R.string.mr_controller_casting_screen));
-                showTitle = true;
-            } else if (mState == null || mState.getState() == PlaybackStateCompat.STATE_NONE) {
-                // Show "No media selected" as we don't yet know the playback state.
-                mTitleView.setText(ApiHelper.getLibResources(mContext).getString(
-                        R.string.mr_controller_no_media_selected));
-                showTitle = true;
-            } else if (!hasTitle && !hasSubtitle) {
-                mTitleView.setText(ApiHelper.getLibResources(mContext).getString(
-                        R.string.mr_controller_no_info_available));
-                showTitle = true;
-            } else {
-                if (hasTitle) {
-                    mTitleView.setText(title);
-                    showTitle = true;
-                }
-                if (hasSubtitle) {
-                    mSubtitleView.setText(subtitle);
-                    showSubtitle = true;
-                }
-            }
-            mTitleView.setVisibility(showTitle ? View.VISIBLE : View.GONE);
-            mSubtitleView.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
-
-            if (mState != null) {
-                boolean isPlaying = mState.getState() == PlaybackStateCompat.STATE_BUFFERING
-                        || mState.getState() == PlaybackStateCompat.STATE_PLAYING;
-                Context playbackControlButtonContext = mPlaybackControlButton.getContext();
-                boolean visible = true;
-                int iconDrawableAttr = 0;
-                int iconDescResId = 0;
-                if (isPlaying && isPauseActionSupported()) {
-                    iconDrawableAttr = R.attr.mediaRoutePauseDrawable;
-                    iconDescResId = R.string.mr_controller_pause;
-                } else if (isPlaying && isStopActionSupported()) {
-                    iconDrawableAttr = R.attr.mediaRouteStopDrawable;
-                    iconDescResId = R.string.mr_controller_stop;
-                } else if (!isPlaying && isPlayActionSupported()) {
-                    iconDrawableAttr = R.attr.mediaRoutePlayDrawable;
-                    iconDescResId = R.string.mr_controller_play;
-                } else {
-                    visible = false;
-                }
-                mPlaybackControlButton.setVisibility(visible ? View.VISIBLE : View.GONE);
-                if (visible) {
-                    mPlaybackControlButton.setImageResource(
-                            MediaRouterThemeHelper.getThemeResource(
-                                    playbackControlButtonContext, iconDrawableAttr));
-                    mPlaybackControlButton.setContentDescription(
-                            playbackControlButtonContext.getResources()
-                                    .getText(iconDescResId));
-                }
-            }
-        }
-    }
-
-    private boolean isPlayActionSupported() {
-        return (mState.getActions() & (ACTION_PLAY | ACTION_PLAY_PAUSE)) != 0;
-    }
-
-    private boolean isPauseActionSupported() {
-        return (mState.getActions() & (ACTION_PAUSE | ACTION_PLAY_PAUSE)) != 0;
-    }
-
-    private boolean isStopActionSupported() {
-        return (mState.getActions() & ACTION_STOP) != 0;
-    }
-
-    boolean isVolumeControlAvailable(MediaRouter.RouteInfo route) {
-        return mVolumeControlEnabled && route.getVolumeHandling()
-                == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
-    }
-
-    private static int getLayoutHeight(View view) {
-        return view.getLayoutParams().height;
-    }
-
-    static void setLayoutHeight(View view, int height) {
-        ViewGroup.LayoutParams lp = view.getLayoutParams();
-        lp.height = height;
-        view.setLayoutParams(lp);
-    }
-
-    private static boolean uriEquals(Uri uri1, Uri uri2) {
-        if (uri1 != null && uri1.equals(uri2)) {
-            return true;
-        } else if (uri1 == null && uri2 == null) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns desired art height to fit into controller dialog.
-     */
-    int getDesiredArtHeight(int originalWidth, int originalHeight) {
-        if (originalWidth >= originalHeight) {
-            // For landscape art, fit width to dialog width.
-            return (int) ((float) mDialogContentWidth * originalHeight / originalWidth + 0.5f);
-        }
-        // For portrait art, fit height to 16:9 ratio case's height.
-        return (int) ((float) mDialogContentWidth * 9 / 16 + 0.5f);
-    }
-
-    void updateArtIconIfNeeded() {
-        if (mCustomControlView != null || !isIconChanged()) {
-            return;
-        }
-        if (mFetchArtTask != null) {
-            mFetchArtTask.cancel(true);
-        }
-        mFetchArtTask = new FetchArtTask();
-        mFetchArtTask.execute();
-    }
-
-    /**
-     * Clear the bitmap loaded by FetchArtTask. Will be called after the loaded bitmaps are applied
-     * to artwork, or no longer valid.
-     */
-    void clearLoadedBitmap() {
-        mArtIconIsLoaded = false;
-        mArtIconLoadedBitmap = null;
-        mArtIconBackgroundColor = 0;
-    }
-
-    /**
-     * Returns whether a new art image is different from an original art image. Compares
-     * Bitmap objects first, and then compares URIs only if bitmap is unchanged with
-     * a null value.
-     */
-    private boolean isIconChanged() {
-        Bitmap newBitmap = mDescription == null ? null : mDescription.getIconBitmap();
-        Uri newUri = mDescription == null ? null : mDescription.getIconUri();
-        Bitmap oldBitmap = mFetchArtTask == null ? mArtIconBitmap : mFetchArtTask.getIconBitmap();
-        Uri oldUri = mFetchArtTask == null ? mArtIconUri : mFetchArtTask.getIconUri();
-        if (oldBitmap != newBitmap) {
-            return true;
-        } else if (oldBitmap == null && !uriEquals(oldUri, newUri)) {
-            return true;
-        }
-        return false;
-    }
-
-    private final class MediaRouterCallback extends MediaRouter.Callback {
-        MediaRouterCallback() {
-        }
-
-        @Override
-        public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) {
-            update(false);
-        }
-
-        @Override
-        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
-            update(true);
-        }
-
-        @Override
-        public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
-            SeekBar volumeSlider = mVolumeSliderMap.get(route);
-            int volume = route.getVolume();
-            if (DEBUG) {
-                Log.d(TAG, "onRouteVolumeChanged(), route.getVolume:" + volume);
-            }
-            if (volumeSlider != null && mRouteInVolumeSliderTouched != route) {
-                volumeSlider.setProgress(volume);
-            }
-        }
-    }
-
-    private final class MediaControllerCallback extends MediaControllerCompat.Callback {
-        MediaControllerCallback() {
-        }
-
-        @Override
-        public void onSessionDestroyed() {
-            if (mMediaController != null) {
-                mMediaController.unregisterCallback(mControllerCallback);
-                mMediaController = null;
-            }
-        }
-
-        @Override
-        public void onPlaybackStateChanged(PlaybackStateCompat state) {
-            mState = state;
-            update(false);
-        }
-
-        @Override
-        public void onMetadataChanged(MediaMetadataCompat metadata) {
-            mDescription = metadata == null ? null : metadata.getDescription();
-            updateArtIconIfNeeded();
-            update(false);
-        }
-    }
-
-    private final class ClickListener implements View.OnClickListener {
-        ClickListener() {
-        }
-
-        @Override
-        public void onClick(View v) {
-            int id = v.getId();
-            if (id == BUTTON_STOP_RES_ID || id == BUTTON_DISCONNECT_RES_ID) {
-                if (mRoute.isSelected()) {
-                    mRouter.unselect(id == BUTTON_STOP_RES_ID ?
-                            MediaRouter.UNSELECT_REASON_STOPPED :
-                            MediaRouter.UNSELECT_REASON_DISCONNECTED);
-                }
-                dismiss();
-            } else if (id == R.id.mr_control_playback_ctrl) {
-                if (mMediaController != null && mState != null) {
-                    boolean isPlaying = mState.getState() == PlaybackStateCompat.STATE_PLAYING;
-                    int actionDescResId = 0;
-                    if (isPlaying && isPauseActionSupported()) {
-                        mMediaController.getTransportControls().pause();
-                        actionDescResId = R.string.mr_controller_pause;
-                    } else if (isPlaying && isStopActionSupported()) {
-                        mMediaController.getTransportControls().stop();
-                        actionDescResId = R.string.mr_controller_stop;
-                    } else if (!isPlaying && isPlayActionSupported()){
-                        mMediaController.getTransportControls().play();
-                        actionDescResId = R.string.mr_controller_play;
-                    }
-                    // Announce the action for accessibility.
-                    if (mAccessibilityManager != null && mAccessibilityManager.isEnabled()
-                            && actionDescResId != 0) {
-                        AccessibilityEvent event = AccessibilityEvent.obtain(
-                                AccessibilityEventCompat.TYPE_ANNOUNCEMENT);
-                        event.setPackageName(mContext.getPackageName());
-                        event.setClassName(getClass().getName());
-                        event.getText().add(
-                                ApiHelper.getLibResources(mContext).getString(actionDescResId));
-                        mAccessibilityManager.sendAccessibilityEvent(event);
-                    }
-                }
-            } else if (id == R.id.mr_close) {
-                dismiss();
-            }
-        }
-    }
-
-    private class VolumeChangeListener implements SeekBar.OnSeekBarChangeListener {
-        private final Runnable mStopTrackingTouch = new Runnable() {
-            @Override
-            public void run() {
-                if (mRouteInVolumeSliderTouched != null) {
-                    mRouteInVolumeSliderTouched = null;
-                    if (mHasPendingUpdate) {
-                        update(mPendingUpdateAnimationNeeded);
-                    }
-                }
-            }
-        };
-
-        VolumeChangeListener() {
-        }
-
-        @Override
-        public void onStartTrackingTouch(SeekBar seekBar) {
-            if (mRouteInVolumeSliderTouched != null) {
-                mVolumeSlider.removeCallbacks(mStopTrackingTouch);
-            }
-            mRouteInVolumeSliderTouched = (MediaRouter.RouteInfo) seekBar.getTag();
-        }
-
-        @Override
-        public void onStopTrackingTouch(SeekBar seekBar) {
-            // Defer resetting mVolumeSliderTouched to allow the media route provider
-            // a little time to settle into its new state and publish the final
-            // volume update.
-            mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS);
-        }
-
-        @Override
-        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-            if (fromUser) {
-                MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) seekBar.getTag();
-                if (DEBUG) {
-                    Log.d(TAG, "onProgressChanged(): calling "
-                            + "MediaRouter.RouteInfo.requestSetVolume(" + progress + ")");
-                }
-                route.requestSetVolume(progress);
-            }
-        }
-    }
-
-    private class VolumeGroupAdapter extends ArrayAdapter<MediaRouter.RouteInfo> {
-        final float mDisabledAlpha;
-
-        public VolumeGroupAdapter(Context context, List<MediaRouter.RouteInfo> objects) {
-            super(context, 0, objects);
-            mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context);
-        }
-
-        @Override
-        public boolean isEnabled(int position) {
-            return false;
-        }
-
-        @Override
-        public View getView(final int position, View convertView, ViewGroup parent) {
-            View v = convertView;
-            if (v == null) {
-                v = LayoutInflater.from(parent.getContext()).inflate(
-                        R.layout.mr_controller_volume_item, parent, false);
-            } else {
-                updateVolumeGroupItemHeight(v);
-            }
-
-            MediaRouter.RouteInfo route = getItem(position);
-            if (route != null) {
-                boolean isEnabled = route.isEnabled();
-
-                TextView routeName = (TextView) v.findViewById(R.id.mr_name);
-                routeName.setEnabled(isEnabled);
-                routeName.setText(route.getName());
-
-                MediaRouteVolumeSlider volumeSlider =
-                        (MediaRouteVolumeSlider) v.findViewById(R.id.mr_volume_slider);
-                MediaRouterThemeHelper.setVolumeSliderColor(
-                        parent.getContext(), volumeSlider, mVolumeGroupList);
-                volumeSlider.setTag(route);
-                mVolumeSliderMap.put(route, volumeSlider);
-                volumeSlider.setHideThumb(!isEnabled);
-                volumeSlider.setEnabled(isEnabled);
-                if (isEnabled) {
-                    if (isVolumeControlAvailable(route)) {
-                        volumeSlider.setMax(route.getVolumeMax());
-                        volumeSlider.setProgress(route.getVolume());
-                        volumeSlider.setOnSeekBarChangeListener(mVolumeChangeListener);
-                    } else {
-                        volumeSlider.setMax(100);
-                        volumeSlider.setProgress(100);
-                        volumeSlider.setEnabled(false);
-                    }
-                }
-
-                ImageView volumeItemIcon =
-                        (ImageView) v.findViewById(R.id.mr_volume_item_icon);
-                volumeItemIcon.setAlpha(isEnabled ? 0xFF : (int) (0xFF * mDisabledAlpha));
-
-                // If overlay bitmap exists, real view should remain hidden until
-                // the animation ends.
-                LinearLayout container = (LinearLayout) v.findViewById(R.id.volume_item_container);
-                container.setVisibility(mGroupMemberRoutesAnimatingWithBitmap.contains(route)
-                        ? View.INVISIBLE : View.VISIBLE);
-
-                // Routes which are being added will be invisible until animation ends.
-                if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.contains(route)) {
-                    Animation alphaAnim = new AlphaAnimation(0.0f, 0.0f);
-                    alphaAnim.setDuration(0);
-                    alphaAnim.setFillEnabled(true);
-                    alphaAnim.setFillAfter(true);
-                    v.clearAnimation();
-                    v.startAnimation(alphaAnim);
-                }
-            }
-            return v;
-        }
-    }
-
-    private class FetchArtTask extends AsyncTask<Void, Void, Bitmap> {
-        // Show animation only when fetching takes a long time.
-        private static final long SHOW_ANIM_TIME_THRESHOLD_MILLIS = 120L;
-
-        private final Bitmap mIconBitmap;
-        private final Uri mIconUri;
-        private int mBackgroundColor;
-        private long mStartTimeMillis;
-
-        FetchArtTask() {
-            Bitmap bitmap = mDescription == null ? null : mDescription.getIconBitmap();
-            if (isBitmapRecycled(bitmap)) {
-                Log.w(TAG, "Can't fetch the given art bitmap because it's already recycled.");
-                bitmap = null;
-            }
-            mIconBitmap = bitmap;
-            mIconUri = mDescription == null ? null : mDescription.getIconUri();
-        }
-
-        public Bitmap getIconBitmap() {
-            return mIconBitmap;
-        }
-
-        public Uri getIconUri() {
-            return mIconUri;
-        }
-
-        @Override
-        protected void onPreExecute() {
-            mStartTimeMillis = SystemClock.uptimeMillis();
-            clearLoadedBitmap();
-        }
-
-        @Override
-        protected Bitmap doInBackground(Void... arg) {
-            Bitmap art = null;
-            if (mIconBitmap != null) {
-                art = mIconBitmap;
-            } else if (mIconUri != null) {
-                InputStream stream = null;
-                try {
-                    if ((stream = openInputStreamByScheme(mIconUri)) == null) {
-                        Log.w(TAG, "Unable to open: " + mIconUri);
-                        return null;
-                    }
-                    // Query art size.
-                    BitmapFactory.Options options = new BitmapFactory.Options();
-                    options.inJustDecodeBounds = true;
-                    BitmapFactory.decodeStream(stream, null, options);
-                    if (options.outWidth == 0 || options.outHeight == 0) {
-                        return null;
-                    }
-                    // Rewind the stream in order to restart art decoding.
-                    try {
-                        stream.reset();
-                    } catch (IOException e) {
-                        // Failed to rewind the stream, try to reopen it.
-                        stream.close();
-                        if ((stream = openInputStreamByScheme(mIconUri)) == null) {
-                            Log.w(TAG, "Unable to open: " + mIconUri);
-                            return null;
-                        }
-                    }
-                    // Calculate required size to decode the art and possibly resize it.
-                    options.inJustDecodeBounds = false;
-                    int reqHeight = getDesiredArtHeight(options.outWidth, options.outHeight);
-                    int ratio = options.outHeight / reqHeight;
-                    options.inSampleSize = Math.max(1, Integer.highestOneBit(ratio));
-                    if (isCancelled()) {
-                        return null;
-                    }
-                    art = BitmapFactory.decodeStream(stream, null, options);
-                } catch (IOException e){
-                    Log.w(TAG, "Unable to open: " + mIconUri, e);
-                } finally {
-                    if (stream != null) {
-                        try {
-                            stream.close();
-                        } catch (IOException e) {
-                        }
-                    }
-                }
-            }
-            if (isBitmapRecycled(art)) {
-                Log.w(TAG, "Can't use recycled bitmap: " + art);
-                return null;
-            }
-            if (art != null && art.getWidth() < art.getHeight()) {
-                // Portrait art requires dominant color as background color.
-                Palette palette = new Palette.Builder(art).maximumColorCount(1).generate();
-                mBackgroundColor = palette.getSwatches().isEmpty()
-                        ? 0 : palette.getSwatches().get(0).getRgb();
-            }
-            return art;
-        }
-
-        @Override
-        protected void onPostExecute(Bitmap art) {
-            mFetchArtTask = null;
-            if (!ObjectsCompat.equals(mArtIconBitmap, mIconBitmap)
-                    || !ObjectsCompat.equals(mArtIconUri, mIconUri)) {
-                mArtIconBitmap = mIconBitmap;
-                mArtIconLoadedBitmap = art;
-                mArtIconUri = mIconUri;
-                mArtIconBackgroundColor = mBackgroundColor;
-                mArtIconIsLoaded = true;
-                long elapsedTimeMillis = SystemClock.uptimeMillis() - mStartTimeMillis;
-                // Loaded bitmap will be applied on the next update
-                update(elapsedTimeMillis > SHOW_ANIM_TIME_THRESHOLD_MILLIS);
-            }
-        }
-
-        private InputStream openInputStreamByScheme(Uri uri) throws IOException {
-            String scheme = uri.getScheme().toLowerCase();
-            InputStream stream = null;
-            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
-                    || ContentResolver.SCHEME_CONTENT.equals(scheme)
-                    || ContentResolver.SCHEME_FILE.equals(scheme)) {
-                stream = mContext.getContentResolver().openInputStream(uri);
-            } else {
-                URL url = new URL(uri.toString());
-                URLConnection conn = url.openConnection();
-                conn.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS);
-                conn.setReadTimeout(CONNECTION_TIMEOUT_MILLIS);
-                stream = conn.getInputStream();
-            }
-            return (stream == null) ? null : new BufferedInputStream(stream);
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialogFragment.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialogFragment.java
deleted file mode 100644
index 215d74f..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialogFragment.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Bundle;
-
-/**
- * Media route controller dialog fragment.
- * <p>
- * Creates a {@link MediaRouteControllerDialog}.  The application may subclass
- * this dialog fragment to customize the media route controller dialog.
- * </p>
- */
-public class MediaRouteControllerDialogFragment extends DialogFragment {
-    private MediaRouteControllerDialog mDialog;
-    /**
-     * Creates a media route controller dialog fragment.
-     * <p>
-     * All subclasses of this class must also possess a default constructor.
-     * </p>
-     */
-    public MediaRouteControllerDialogFragment() {
-        setCancelable(true);
-    }
-
-    /**
-     * Called when the controller dialog is being created.
-     * <p>
-     * Subclasses may override this method to customize the dialog.
-     * </p>
-     */
-    public MediaRouteControllerDialog onCreateControllerDialog(
-            Context context, Bundle savedInstanceState) {
-        return new MediaRouteControllerDialog(context);
-    }
-
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        mDialog = onCreateControllerDialog(getContext(), savedInstanceState);
-        return mDialog;
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        if (mDialog != null) {
-            mDialog.clearGroupListAnimation(false);
-        }
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        if (mDialog != null) {
-            mDialog.updateLayout();
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java
deleted file mode 100644
index b5ee63e..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogFactory.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import androidx.annotation.NonNull;
-
-/**
- * The media route dialog factory is responsible for creating the media route
- * chooser and controller dialogs as needed.
- * <p>
- * The application can customize the dialogs by providing a subclass of the
- * dialog factory to the {@link MediaRouteButton} using the
- * {@link MediaRouteButton#setDialogFactory setDialogFactory} method.
- * </p>
- */
-public class MediaRouteDialogFactory {
-    private static final MediaRouteDialogFactory sDefault = new MediaRouteDialogFactory();
-
-    /**
-     * Creates a default media route dialog factory.
-     */
-    public MediaRouteDialogFactory() {
-    }
-
-    /**
-     * Gets the default factory instance.
-     *
-     * @return The default media route dialog factory, never null.
-     */
-    @NonNull
-    public static MediaRouteDialogFactory getDefault() {
-        return sDefault;
-    }
-
-    /**
-     * Called when the chooser dialog is being opened and it is time to create the fragment.
-     * <p>
-     * Subclasses may override this method to create a customized fragment.
-     * </p>
-     *
-     * @return The media route chooser dialog fragment, must not be null.
-     */
-    @NonNull
-    public MediaRouteChooserDialogFragment onCreateChooserDialogFragment() {
-        return new MediaRouteChooserDialogFragment();
-    }
-
-    /**
-     * Called when the controller dialog is being opened and it is time to create the fragment.
-     * <p>
-     * Subclasses may override this method to create a customized fragment.
-     * </p>
-     *
-     * @return The media route controller dialog fragment, must not be null.
-     */
-    @NonNull
-    public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
-        return new MediaRouteControllerDialogFragment();
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogHelper.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogHelper.java
deleted file mode 100644
index 9aabf7b..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDialogHelper.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-import com.android.media.update.ApiHelper;
-import com.android.media.update.R;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-final class MediaRouteDialogHelper {
-    /**
-     * The framework should set the dialog width properly, but somehow it doesn't work, hence
-     * duplicating a similar logic here to determine the appropriate dialog width.
-     */
-    public static int getDialogWidth(Context context) {
-        DisplayMetrics metrics = ApiHelper.getLibResources(context).getDisplayMetrics();
-        boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
-
-        TypedValue value = new TypedValue();
-        ApiHelper.getLibResources(context).getValue(isPortrait
-                ? R.dimen.mr_dialog_fixed_width_minor
-                : R.dimen.mr_dialog_fixed_width_major, value, true);
-        if (value.type == TypedValue.TYPE_DIMENSION) {
-            return (int) value.getDimension(metrics);
-        } else if (value.type == TypedValue.TYPE_FRACTION) {
-            return (int) value.getFraction(metrics.widthPixels, metrics.widthPixels);
-        }
-        return ViewGroup.LayoutParams.WRAP_CONTENT;
-    }
-
-    /**
-     * Compares two lists regardless of order.
-     *
-     * @param list1 A list
-     * @param list2 A list to be compared with {@code list1}
-     * @return True if two lists have exactly same items regardless of order, false otherwise.
-     */
-    public static <E> boolean listUnorderedEquals(List<E> list1, List<E> list2) {
-        HashSet<E> set1 = new HashSet<>(list1);
-        HashSet<E> set2 = new HashSet<>(list2);
-        return set1.equals(set2);
-    }
-
-    /**
-     * Compares two lists and returns a set of items which exist
-     * after-list but before-list, which means newly added items.
-     *
-     * @param before A list
-     * @param after A list to be compared with {@code before}
-     * @return A set of items which contains newly added items while
-     * comparing {@code after} to {@code before}.
-     */
-    public static <E> Set<E> getItemsAdded(List<E> before, List<E> after) {
-        HashSet<E> set = new HashSet<>(after);
-        set.removeAll(before);
-        return set;
-    }
-
-    /**
-     * Compares two lists and returns a set of items which exist
-     * before-list but after-list, which means removed items.
-     *
-     * @param before A list
-     * @param after A list to be compared with {@code before}
-     * @return A set of items which contains removed items while
-     * comparing {@code after} to {@code before}.
-     */
-    public static <E> Set<E> getItemsRemoved(List<E> before, List<E> after) {
-        HashSet<E> set = new HashSet<>(before);
-        set.removeAll(after);
-        return set;
-    }
-
-    /**
-     * Generates an item-Rect map which indicates where member
-     * items are located in the given ListView.
-     *
-     * @param listView A list view
-     * @param adapter An array adapter which contains an array of items.
-     * @return A map of items and bounds of their views located in the given list view.
-     */
-    public static <E> HashMap<E, Rect> getItemBoundMap(ListView listView,
-            ArrayAdapter<E> adapter) {
-        HashMap<E, Rect> itemBoundMap = new HashMap<>();
-        int firstVisiblePosition = listView.getFirstVisiblePosition();
-        for (int i = 0; i < listView.getChildCount(); ++i) {
-            int position = firstVisiblePosition + i;
-            E item = adapter.getItem(position);
-            View view = listView.getChildAt(i);
-            itemBoundMap.put(item,
-                    new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
-        }
-        return itemBoundMap;
-    }
-
-    /**
-     * Generates an item-BitmapDrawable map which stores snapshots
-     * of member items in the given ListView.
-     *
-     * @param context A context
-     * @param listView A list view
-     * @param adapter An array adapter which contains an array of items.
-     * @return A map of items and snapshots of their views in the given list view.
-     */
-    public static <E> HashMap<E, BitmapDrawable> getItemBitmapMap(Context context,
-            ListView listView, ArrayAdapter<E> adapter) {
-        HashMap<E, BitmapDrawable> itemBitmapMap = new HashMap<>();
-        int firstVisiblePosition = listView.getFirstVisiblePosition();
-        for (int i = 0; i < listView.getChildCount(); ++i) {
-            int position = firstVisiblePosition + i;
-            E item = adapter.getItem(position);
-            View view = listView.getChildAt(i);
-            itemBitmapMap.put(item, getViewBitmap(context, view));
-        }
-        return itemBitmapMap;
-    }
-
-    private static BitmapDrawable getViewBitmap(Context context, View view) {
-        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
-                Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        view.draw(canvas);
-        return new BitmapDrawable(context.getResources(), bitmap);
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java
deleted file mode 100644
index 52aecd88..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteDiscoveryFragment.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import android.os.Bundle;
-
-import androidx.fragment.app.Fragment;
-
-import com.android.support.mediarouter.media.MediaRouteSelector;
-import com.android.support.mediarouter.media.MediaRouter;
-
-/**
- * Media route discovery fragment.
- * <p>
- * This fragment takes care of registering a callback for media route discovery
- * during the {@link Fragment#onStart onStart()} phase
- * and removing it during the {@link Fragment#onStop onStop()} phase.
- * </p><p>
- * The application must supply a route selector to specify the kinds of routes
- * to discover.  The application may also override {@link #onCreateCallback} to
- * provide the {@link MediaRouter} callback to register.
- * </p><p>
- * Note that the discovery callback makes the application be connected with all the
- * {@link androidx.mediarouter.media.MediaRouteProviderService media route provider services}
- * while it is registered.
- * </p>
- */
-public class MediaRouteDiscoveryFragment extends Fragment {
-    private final String ARGUMENT_SELECTOR = "selector";
-
-    private MediaRouter mRouter;
-    private MediaRouteSelector mSelector;
-    private MediaRouter.Callback mCallback;
-
-    public MediaRouteDiscoveryFragment() {
-    }
-
-    /**
-     * Gets the media router instance.
-     */
-    public MediaRouter getMediaRouter() {
-        ensureRouter();
-        return mRouter;
-    }
-
-    private void ensureRouter() {
-        if (mRouter == null) {
-            mRouter = MediaRouter.getInstance(getContext());
-        }
-    }
-
-    /**
-     * Gets the media route selector for filtering the routes to be discovered.
-     *
-     * @return The selector, never null.
-     */
-    public MediaRouteSelector getRouteSelector() {
-        ensureRouteSelector();
-        return mSelector;
-    }
-
-    /**
-     * Sets the media route selector for filtering the routes to be discovered.
-     * This method must be called before the fragment is added.
-     *
-     * @param selector The selector to set.
-     */
-    public void setRouteSelector(MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-
-        ensureRouteSelector();
-        if (!mSelector.equals(selector)) {
-            mSelector = selector;
-
-            Bundle args = getArguments();
-            if (args == null) {
-                args = new Bundle();
-            }
-            args.putBundle(ARGUMENT_SELECTOR, selector.asBundle());
-            setArguments(args);
-
-            if (mCallback != null) {
-                mRouter.removeCallback(mCallback);
-                mRouter.addCallback(mSelector, mCallback, onPrepareCallbackFlags());
-            }
-        }
-    }
-
-    private void ensureRouteSelector() {
-        if (mSelector == null) {
-            Bundle args = getArguments();
-            if (args != null) {
-                mSelector = MediaRouteSelector.fromBundle(args.getBundle(ARGUMENT_SELECTOR));
-            }
-            if (mSelector == null) {
-                mSelector = MediaRouteSelector.EMPTY;
-            }
-        }
-    }
-
-    /**
-     * Called to create the {@link androidx.mediarouter.media.MediaRouter.Callback callback}
-     * that will be registered.
-     * <p>
-     * The default callback does nothing.  The application may override this method to
-     * supply its own callback.
-     * </p>
-     *
-     * @return The new callback, or null if no callback should be registered.
-     */
-    public MediaRouter.Callback onCreateCallback() {
-        return new MediaRouter.Callback() { };
-    }
-
-    /**
-     * Called to prepare the callback flags that will be used when the
-     * {@link androidx.mediarouter.media.MediaRouter.Callback callback} is registered.
-     * <p>
-     * The default implementation returns {@link MediaRouter#CALLBACK_FLAG_REQUEST_DISCOVERY}.
-     * </p>
-     *
-     * @return The desired callback flags.
-     */
-    public int onPrepareCallbackFlags() {
-        return MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY;
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        ensureRouteSelector();
-        ensureRouter();
-        mCallback = onCreateCallback();
-        if (mCallback != null) {
-            mRouter.addCallback(mSelector, mCallback, onPrepareCallbackFlags());
-        }
-    }
-
-    @Override
-    public void onStop() {
-        if (mCallback != null) {
-            mRouter.removeCallback(mCallback);
-            mCallback = null;
-        }
-
-        super.onStop();
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java
deleted file mode 100644
index dcca6a0..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import android.content.Context;
-import android.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.AnimationDrawable;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageButton;
-
-import com.android.media.update.ApiHelper;
-import com.android.media.update.R;
-
-/**
- * Chevron/Caret button to expand/collapse group volume list with animation.
- */
-public class MediaRouteExpandCollapseButton extends ImageButton {
-    final AnimationDrawable mExpandAnimationDrawable;
-    final AnimationDrawable mCollapseAnimationDrawable;
-    final String mExpandGroupDescription;
-    final String mCollapseGroupDescription;
-    boolean mIsGroupExpanded;
-    OnClickListener mListener;
-
-    public MediaRouteExpandCollapseButton(Context context) {
-        this(context, null);
-    }
-
-    public MediaRouteExpandCollapseButton(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public MediaRouteExpandCollapseButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mExpandAnimationDrawable = (AnimationDrawable)
-                ApiHelper.getLibResources(context).getDrawable(R.drawable.mr_group_expand);
-        mCollapseAnimationDrawable = (AnimationDrawable)
-                ApiHelper.getLibResources(context).getDrawable(R.drawable.mr_group_collapse);
-
-        ColorFilter filter = new PorterDuffColorFilter(
-                MediaRouterThemeHelper.getControllerColor(context, defStyleAttr),
-                PorterDuff.Mode.SRC_IN);
-        mExpandAnimationDrawable.setColorFilter(filter);
-        mCollapseAnimationDrawable.setColorFilter(filter);
-
-        mExpandGroupDescription =
-                ApiHelper.getLibResources(context).getString(R.string.mr_controller_expand_group);
-        mCollapseGroupDescription =
-                ApiHelper.getLibResources(context).getString(R.string.mr_controller_collapse_group);
-
-        setImageDrawable(mExpandAnimationDrawable.getFrame(0));
-        setContentDescription(mExpandGroupDescription);
-
-        super.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mIsGroupExpanded = !mIsGroupExpanded;
-                if (mIsGroupExpanded) {
-                    setImageDrawable(mExpandAnimationDrawable);
-                    mExpandAnimationDrawable.start();
-                    setContentDescription(mCollapseGroupDescription);
-                } else {
-                    setImageDrawable(mCollapseAnimationDrawable);
-                    mCollapseAnimationDrawable.start();
-                    setContentDescription(mExpandGroupDescription);
-                }
-                if (mListener != null) {
-                    mListener.onClick(view);
-                }
-            }
-        });
-    }
-
-    @Override
-    public void setOnClickListener(OnClickListener listener) {
-        mListener = listener;
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteVolumeSlider.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteVolumeSlider.java
deleted file mode 100644
index d05d20e..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteVolumeSlider.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.SeekBar;
-
-/**
- * Volume slider with showing, hiding, and applying alpha supports to the thumb.
- */
-public class MediaRouteVolumeSlider extends SeekBar {
-    private static final String TAG = "MediaRouteVolumeSlider";
-
-    private final float mDisabledAlpha;
-
-    private boolean mHideThumb;
-    private Drawable mThumb;
-    private int mColor;
-
-    public MediaRouteVolumeSlider(Context context) {
-        this(context, null);
-    }
-
-    public MediaRouteVolumeSlider(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context);
-    }
-
-    public MediaRouteVolumeSlider(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context);
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        int alpha = isEnabled() ? 0xFF : (int) (0xFF * mDisabledAlpha);
-
-        // The thumb drawable is a collection of drawables and its current drawables are changed per
-        // state. Apply the color filter and alpha on every state change.
-        if (mThumb != null) {
-            mThumb.setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
-            mThumb.setAlpha(alpha);
-        }
-
-        getProgressDrawable().setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
-        getProgressDrawable().setAlpha(alpha);
-    }
-
-    @Override
-    public void setThumb(Drawable thumb) {
-        mThumb = thumb;
-        super.setThumb(mHideThumb ? null : mThumb);
-    }
-
-    /**
-     * Sets whether to show or hide thumb.
-     */
-    public void setHideThumb(boolean hideThumb) {
-        if (mHideThumb == hideThumb) {
-            return;
-        }
-        mHideThumb = hideThumb;
-        super.setThumb(mHideThumb ? null : mThumb);
-    }
-
-    /**
-     * Sets the volume slider color. The change takes effect next time drawable state is changed.
-     * <p>
-     * The color cannot be translucent, otherwise the underlying progress bar will be seen through
-     * the thumb.
-     * </p>
-     */
-    public void setColor(int color) {
-        if (mColor == color) {
-            return;
-        }
-        if (Color.alpha(color) != 0xFF) {
-            Log.e(TAG, "Volume slider color cannot be translucent: #" + Integer.toHexString(color));
-        }
-        mColor = color;
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java
deleted file mode 100644
index b4bf8d1..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.View;
-
-import androidx.annotation.IntDef;
-import androidx.core.graphics.ColorUtils;
-
-import com.android.media.update.R;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-final class MediaRouterThemeHelper {
-    private static final float MIN_CONTRAST = 3.0f;
-
-    @IntDef({COLOR_DARK_ON_LIGHT_BACKGROUND, COLOR_WHITE_ON_DARK_BACKGROUND})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface ControllerColorType {}
-
-    static final int COLOR_DARK_ON_LIGHT_BACKGROUND = 0xDE000000; /* Opacity of 87% */
-    static final int COLOR_WHITE_ON_DARK_BACKGROUND = Color.WHITE;
-
-    private MediaRouterThemeHelper() {
-    }
-
-    static Context createThemedButtonContext(Context context) {
-        // Apply base Media Router theme.
-        context = new ContextThemeWrapper(context, getRouterThemeId(context));
-
-        // Apply custom Media Router theme.
-        int style = getThemeResource(context, R.attr.mediaRouteTheme);
-        if (style != 0) {
-            context = new ContextThemeWrapper(context, style);
-        }
-
-        return context;
-    }
-
-    /*
-     * The following two methods are to be used in conjunction. They should be used to prepare
-     * the context and theme for a super class constructor (the latter method relies on the
-     * former method to properly prepare the context):
-     *   super(context = createThemedDialogContext(context, theme),
-     *           createThemedDialogStyle(context));
-     *
-     * It will apply theme in the following order (style lookups will be done in reverse):
-     *   1) Current theme
-     *   2) Supplied theme
-     *   3) Base Media Router theme
-     *   4) Custom Media Router theme, if provided
-     */
-    static Context createThemedDialogContext(Context context, int theme, boolean alertDialog) {
-        // 1) Current theme is already applied to the context
-
-        // 2) If no theme is supplied, look it up from the context (dialogTheme/alertDialogTheme)
-        if (theme == 0) {
-            theme = getThemeResource(context,
-                    !alertDialog ? android.R.attr.dialogTheme : android.R.attr.alertDialogTheme);
-        }
-        //    Apply it
-        context = new ContextThemeWrapper(context, theme);
-
-        // 3) If a custom Media Router theme is provided then apply the base theme
-        if (getThemeResource(context, R.attr.mediaRouteTheme) != 0) {
-            context = new ContextThemeWrapper(context, getRouterThemeId(context));
-        }
-
-        return context;
-    }
-    // This method should be used in conjunction with the previous method.
-    static int createThemedDialogStyle(Context context) {
-        // 4) Apply the custom Media Router theme
-        int theme = getThemeResource(context, R.attr.mediaRouteTheme);
-        if (theme == 0) {
-            // 3) No custom MediaRouter theme was provided so apply the base theme instead
-            theme = getRouterThemeId(context);
-        }
-
-        return theme;
-    }
-    // END. Previous two methods should be used in conjunction.
-
-    static int getThemeResource(Context context, int attr) {
-        TypedValue value = new TypedValue();
-        return context.getTheme().resolveAttribute(attr, value, true) ? value.resourceId : 0;
-    }
-
-    static float getDisabledAlpha(Context context) {
-        TypedValue value = new TypedValue();
-        return context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true)
-                ? value.getFloat() : 0.5f;
-    }
-
-    static @ControllerColorType int getControllerColor(Context context, int style) {
-        int primaryColor = getThemeColor(context, style, android.R.attr.colorPrimary);
-        if (primaryColor == 0) {
-            primaryColor = getThemeColor(context, style, android.R.attr.colorPrimary);
-            if (primaryColor == 0) {
-                primaryColor = 0xFF000000;
-            }
-        }
-        if (ColorUtils.calculateContrast(COLOR_WHITE_ON_DARK_BACKGROUND, primaryColor)
-                >= MIN_CONTRAST) {
-            return COLOR_WHITE_ON_DARK_BACKGROUND;
-        }
-        return COLOR_DARK_ON_LIGHT_BACKGROUND;
-    }
-
-    static int getButtonTextColor(Context context) {
-        int primaryColor = getThemeColor(context, 0, android.R.attr.colorPrimary);
-        int backgroundColor = getThemeColor(context, 0, android.R.attr.colorBackground);
-
-        if (ColorUtils.calculateContrast(primaryColor, backgroundColor) < MIN_CONTRAST) {
-            // Default to colorAccent if the contrast ratio is low.
-            return getThemeColor(context, 0, android.R.attr.colorAccent);
-        }
-        return primaryColor;
-    }
-
-    static void setMediaControlsBackgroundColor(
-            Context context, View mainControls, View groupControls, boolean hasGroup) {
-        int primaryColor = getThemeColor(context, 0, android.R.attr.colorPrimary);
-        int primaryDarkColor = getThemeColor(context, 0, android.R.attr.colorPrimaryDark);
-        if (hasGroup && getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
-            // Instead of showing dark controls in a possibly dark (i.e. the primary dark), model
-            // the white dialog and use the primary color for the group controls.
-            primaryDarkColor = primaryColor;
-            primaryColor = Color.WHITE;
-        }
-        mainControls.setBackgroundColor(primaryColor);
-        groupControls.setBackgroundColor(primaryDarkColor);
-        // Also store the background colors to the view tags. They are used in
-        // setVolumeSliderColor() below.
-        mainControls.setTag(primaryColor);
-        groupControls.setTag(primaryDarkColor);
-    }
-
-    static void setVolumeSliderColor(
-            Context context, MediaRouteVolumeSlider volumeSlider, View backgroundView) {
-        int controllerColor = getControllerColor(context, 0);
-        if (Color.alpha(controllerColor) != 0xFF) {
-            // Composite with the background in order not to show the underlying progress bar
-            // through the thumb.
-            int backgroundColor = (int) backgroundView.getTag();
-            controllerColor = ColorUtils.compositeColors(controllerColor, backgroundColor);
-        }
-        volumeSlider.setColor(controllerColor);
-    }
-
-    private static boolean isLightTheme(Context context) {
-        TypedValue value = new TypedValue();
-        // TODO(sungsoo): Switch to com.android.internal.R.attr.isLightTheme
-        return context.getTheme().resolveAttribute(androidx.appcompat.R.attr.isLightTheme,
-                value, true) && value.data != 0;
-    }
-
-    private static int getThemeColor(Context context, int style, int attr) {
-        if (style != 0) {
-            int[] attrs = { attr };
-            TypedArray ta = context.obtainStyledAttributes(style, attrs);
-            int color = ta.getColor(0, 0);
-            ta.recycle();
-            if (color != 0) {
-                return color;
-            }
-        }
-        TypedValue value = new TypedValue();
-        context.getTheme().resolveAttribute(attr, value, true);
-        if (value.resourceId != 0) {
-            return context.getResources().getColor(value.resourceId);
-        }
-        return value.data;
-    }
-
-    static int getRouterThemeId(Context context) {
-        int themeId;
-        if (isLightTheme(context)) {
-            if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
-                themeId = R.style.Theme_MediaRouter_Light;
-            } else {
-                themeId = R.style.Theme_MediaRouter_Light_DarkControlPanel;
-            }
-        } else {
-            if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
-                themeId = R.style.Theme_MediaRouter_LightControlPanel;
-            } else {
-                themeId = R.style.Theme_MediaRouter;
-            }
-        }
-        return themeId;
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/OverlayListView.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/OverlayListView.java
deleted file mode 100644
index b00dee2..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/OverlayListView.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * 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.support.mediarouter.app;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.util.AttributeSet;
-import android.view.animation.Interpolator;
-import android.widget.ListView;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * A ListView which has an additional overlay layer. {@link BitmapDrawable}
- * can be added to the layer and can be animated.
- */
-public final class OverlayListView extends ListView {
-    private final List<OverlayObject> mOverlayObjects = new ArrayList<>();
-
-    public OverlayListView(Context context) {
-        super(context);
-    }
-
-    public OverlayListView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public OverlayListView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    /**
-     * Adds an object to the overlay layer.
-     *
-     * @param object An object to be added.
-     */
-    public void addOverlayObject(OverlayObject object) {
-        mOverlayObjects.add(object);
-    }
-
-    /**
-     * Starts all animations of objects in the overlay layer.
-     */
-    public void startAnimationAll() {
-        for (OverlayObject object : mOverlayObjects) {
-            if (!object.isAnimationStarted()) {
-                object.startAnimation(getDrawingTime());
-            }
-        }
-    }
-
-    /**
-     * Stops all animations of objects in the overlay layer.
-     */
-    public void stopAnimationAll() {
-        for (OverlayObject object : mOverlayObjects) {
-            object.stopAnimation();
-        }
-    }
-
-    @Override
-    public void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        if (mOverlayObjects.size() > 0) {
-            Iterator<OverlayObject> it = mOverlayObjects.iterator();
-            while (it.hasNext()) {
-                OverlayObject object = it.next();
-                BitmapDrawable bitmap = object.getBitmapDrawable();
-                if (bitmap != null) {
-                    bitmap.draw(canvas);
-                }
-                if (!object.update(getDrawingTime())) {
-                    it.remove();
-                }
-            }
-        }
-    }
-
-    /**
-     * A class that represents an object to be shown in the overlay layer.
-     */
-    public static class OverlayObject {
-        private BitmapDrawable mBitmap;
-        private float mCurrentAlpha = 1.0f;
-        private Rect mCurrentBounds;
-        private Interpolator mInterpolator;
-        private long mDuration;
-        private Rect mStartRect;
-        private int mDeltaY;
-        private float mStartAlpha = 1.0f;
-        private float mEndAlpha = 1.0f;
-        private long mStartTime;
-        private boolean mIsAnimationStarted;
-        private boolean mIsAnimationEnded;
-        private OnAnimationEndListener mListener;
-
-        public OverlayObject(BitmapDrawable bitmap, Rect startRect) {
-            mBitmap = bitmap;
-            mStartRect = startRect;
-            mCurrentBounds = new Rect(startRect);
-            if (mBitmap != null && mCurrentBounds != null) {
-                mBitmap.setAlpha((int) (mCurrentAlpha * 255));
-                mBitmap.setBounds(mCurrentBounds);
-            }
-        }
-
-        /**
-         * Returns the bitmap that this object represents.
-         *
-         * @return BitmapDrawable that this object has.
-         */
-        public BitmapDrawable getBitmapDrawable() {
-            return mBitmap;
-        }
-
-        /**
-         * Returns the started status of the animation.
-         *
-         * @return True if the animation has started, false otherwise.
-         */
-        public boolean isAnimationStarted() {
-            return mIsAnimationStarted;
-        }
-
-        /**
-         * Sets animation for varying alpha.
-         *
-         * @param startAlpha Starting alpha value for the animation, where 1.0 means
-         * fully opaque and 0.0 means fully transparent.
-         * @param endAlpha Ending alpha value for the animation.
-         * @return This OverlayObject to allow for chaining of calls.
-         */
-        public OverlayObject setAlphaAnimation(float startAlpha, float endAlpha) {
-            mStartAlpha = startAlpha;
-            mEndAlpha = endAlpha;
-            return this;
-        }
-
-        /**
-         * Sets animation for moving objects vertically.
-         *
-         * @param deltaY Distance to move in pixels.
-         * @return This OverlayObject to allow for chaining of calls.
-         */
-        public OverlayObject setTranslateYAnimation(int deltaY) {
-            mDeltaY = deltaY;
-            return this;
-        }
-
-        /**
-         * Sets how long the animation will last.
-         *
-         * @param duration Duration in milliseconds
-         * @return This OverlayObject to allow for chaining of calls.
-         */
-        public OverlayObject setDuration(long duration) {
-            mDuration = duration;
-            return this;
-        }
-
-        /**
-         * Sets the acceleration curve for this animation.
-         *
-         * @param interpolator The interpolator which defines the acceleration curve
-         * @return This OverlayObject to allow for chaining of calls.
-         */
-        public OverlayObject setInterpolator(Interpolator interpolator) {
-            mInterpolator = interpolator;
-            return this;
-        }
-
-        /**
-         * Binds an animation end listener to the animation.
-         *
-         * @param listener the animation end listener to be notified.
-         * @return This OverlayObject to allow for chaining of calls.
-         */
-        public OverlayObject setAnimationEndListener(OnAnimationEndListener listener) {
-            mListener = listener;
-            return this;
-        }
-
-        /**
-         * Starts the animation and sets the start time.
-         *
-         * @param startTime Start time to be set in Millis
-         */
-        public void startAnimation(long startTime) {
-            mStartTime = startTime;
-            mIsAnimationStarted = true;
-        }
-
-        /**
-         * Stops the animation.
-         */
-        public void stopAnimation() {
-            mIsAnimationStarted = true;
-            mIsAnimationEnded = true;
-            if (mListener != null) {
-                mListener.onAnimationEnd();
-            }
-        }
-
-        /**
-         * Calculates and updates current bounds and alpha value.
-         *
-         * @param currentTime Current time.in millis
-         */
-        public boolean update(long currentTime) {
-            if (mIsAnimationEnded) {
-                return false;
-            }
-            float normalizedTime = (currentTime - mStartTime) / (float) mDuration;
-            normalizedTime = Math.max(0.0f, Math.min(1.0f, normalizedTime));
-            if (!mIsAnimationStarted) {
-                normalizedTime = 0.0f;
-            }
-            float interpolatedTime = (mInterpolator == null) ? normalizedTime
-                    : mInterpolator.getInterpolation(normalizedTime);
-            int deltaY = (int) (mDeltaY * interpolatedTime);
-            mCurrentBounds.top = mStartRect.top + deltaY;
-            mCurrentBounds.bottom = mStartRect.bottom + deltaY;
-            mCurrentAlpha = mStartAlpha + (mEndAlpha - mStartAlpha) * interpolatedTime;
-            if (mBitmap != null && mCurrentBounds != null) {
-                mBitmap.setAlpha((int) (mCurrentAlpha * 255));
-                mBitmap.setBounds(mCurrentBounds);
-            }
-            if (mIsAnimationStarted && normalizedTime >= 1.0f) {
-                mIsAnimationEnded = true;
-                if (mListener != null) {
-                    mListener.onAnimationEnd();
-                }
-            }
-            return !mIsAnimationEnded;
-        }
-
-        /**
-         * An animation listener that receives notifications when the animation ends.
-         */
-        public interface OnAnimationEndListener {
-            /**
-             * Notifies the end of the animation.
-             */
-            public void onAnimationEnd();
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java b/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java
deleted file mode 100644
index 5a0bc95..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr1/MediaRouterJellybeanMr1.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.os.Build;
-import android.os.Handler;
-import android.util.Log;
-import android.view.Display;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-// @@RequiresApi(17)
-final class MediaRouterJellybeanMr1 {
-    private static final String TAG = "MediaRouterJellybeanMr1";
-
-    public static Object createCallback(Callback callback) {
-        return new CallbackProxy<Callback>(callback);
-    }
-
-    public static final class RouteInfo {
-        public static boolean isEnabled(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).isEnabled();
-        }
-
-        public static Display getPresentationDisplay(Object routeObj) {
-            // android.media.MediaRouter.RouteInfo.getPresentationDisplay() was
-            // added in API 17. However, some factory releases of JB MR1 missed it.
-            try {
-                return ((android.media.MediaRouter.RouteInfo)routeObj).getPresentationDisplay();
-            } catch (NoSuchMethodError ex) {
-                Log.w(TAG, "Cannot get presentation display for the route.", ex);
-            }
-            return null;
-        }
-    }
-
-    public static interface Callback extends MediaRouterJellybean.Callback {
-        public void onRoutePresentationDisplayChanged(Object routeObj);
-    }
-
-    /**
-     * Workaround the fact that the version of MediaRouter.addCallback() that accepts a
-     * flag to perform an active scan does not exist in JB MR1 so we need to force
-     * wifi display scans directly through the DisplayManager.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class ActiveScanWorkaround implements Runnable {
-        // Time between wifi display scans when actively scanning in milliseconds.
-        private static final int WIFI_DISPLAY_SCAN_INTERVAL = 15000;
-
-        private final DisplayManager mDisplayManager;
-        private final Handler mHandler;
-        private Method mScanWifiDisplaysMethod;
-
-        private boolean mActivelyScanningWifiDisplays;
-
-        public ActiveScanWorkaround(Context context, Handler handler) {
-            if (Build.VERSION.SDK_INT != 17) {
-                throw new UnsupportedOperationException();
-            }
-
-            mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
-            mHandler = handler;
-            try {
-                mScanWifiDisplaysMethod = DisplayManager.class.getMethod("scanWifiDisplays");
-            } catch (NoSuchMethodException ex) {
-            }
-        }
-
-        public void setActiveScanRouteTypes(int routeTypes) {
-            // On JB MR1, there is no API to scan wifi display routes.
-            // Instead we must make a direct call into the DisplayManager to scan
-            // wifi displays on this version but only when live video routes are requested.
-            // See also the JellybeanMr2Impl implementation of this method.
-            // This was fixed in JB MR2 by adding a new overload of addCallback() to
-            // enable active scanning on request.
-            if ((routeTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) {
-                if (!mActivelyScanningWifiDisplays) {
-                    if (mScanWifiDisplaysMethod != null) {
-                        mActivelyScanningWifiDisplays = true;
-                        mHandler.post(this);
-                    } else {
-                        Log.w(TAG, "Cannot scan for wifi displays because the "
-                                + "DisplayManager.scanWifiDisplays() method is "
-                                + "not available on this device.");
-                    }
-                }
-            } else {
-                if (mActivelyScanningWifiDisplays) {
-                    mActivelyScanningWifiDisplays = false;
-                    mHandler.removeCallbacks(this);
-                }
-            }
-        }
-
-        @Override
-        public void run() {
-            if (mActivelyScanningWifiDisplays) {
-                try {
-                    mScanWifiDisplaysMethod.invoke(mDisplayManager);
-                } catch (IllegalAccessException ex) {
-                    Log.w(TAG, "Cannot scan for wifi displays.", ex);
-                } catch (InvocationTargetException ex) {
-                    Log.w(TAG, "Cannot scan for wifi displays.", ex);
-                }
-                mHandler.postDelayed(this, WIFI_DISPLAY_SCAN_INTERVAL);
-            }
-        }
-    }
-
-    /**
-     * Workaround the fact that the isConnecting() method does not exist in JB MR1.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class IsConnectingWorkaround {
-        private Method mGetStatusCodeMethod;
-        private int mStatusConnecting;
-
-        public IsConnectingWorkaround() {
-            if (Build.VERSION.SDK_INT != 17) {
-                throw new UnsupportedOperationException();
-            }
-
-            try {
-                Field statusConnectingField =
-                        android.media.MediaRouter.RouteInfo.class.getField("STATUS_CONNECTING");
-                mStatusConnecting = statusConnectingField.getInt(null);
-                mGetStatusCodeMethod =
-                        android.media.MediaRouter.RouteInfo.class.getMethod("getStatusCode");
-            } catch (NoSuchFieldException ex) {
-            } catch (NoSuchMethodException ex) {
-            } catch (IllegalAccessException ex) {
-            }
-        }
-
-        public boolean isConnecting(Object routeObj) {
-            android.media.MediaRouter.RouteInfo route =
-                    (android.media.MediaRouter.RouteInfo)routeObj;
-
-            if (mGetStatusCodeMethod != null) {
-                try {
-                    int statusCode = (Integer)mGetStatusCodeMethod.invoke(route);
-                    return statusCode == mStatusConnecting;
-                } catch (IllegalAccessException ex) {
-                } catch (InvocationTargetException ex) {
-                }
-            }
-
-            // Assume not connecting.
-            return false;
-        }
-    }
-
-    static class CallbackProxy<T extends Callback>
-            extends MediaRouterJellybean.CallbackProxy<T> {
-        public CallbackProxy(T callback) {
-            super(callback);
-        }
-
-        @Override
-        public void onRoutePresentationDisplayChanged(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRoutePresentationDisplayChanged(route);
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr2/MediaRouterJellybeanMr2.java b/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr2/MediaRouterJellybeanMr2.java
deleted file mode 100644
index 1103549..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean-mr2/MediaRouterJellybeanMr2.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-// @@RequiresApi(18)
-final class MediaRouterJellybeanMr2 {
-    public static Object getDefaultRoute(Object routerObj) {
-        return ((android.media.MediaRouter)routerObj).getDefaultRoute();
-    }
-
-    public static void addCallback(Object routerObj, int types, Object callbackObj, int flags) {
-        ((android.media.MediaRouter)routerObj).addCallback(types,
-                (android.media.MediaRouter.Callback)callbackObj, flags);
-    }
-
-    public static final class RouteInfo {
-        public static CharSequence getDescription(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getDescription();
-        }
-
-        public static boolean isConnecting(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).isConnecting();
-        }
-    }
-
-    public static final class UserRouteInfo {
-        public static void setDescription(Object routeObj, CharSequence description) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setDescription(description);
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean/MediaRouterJellybean.java b/packages/MediaComponents/src/com/android/support/mediarouter/jellybean/MediaRouterJellybean.java
deleted file mode 100644
index 0bb59b8..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/jellybean/MediaRouterJellybean.java
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.os.Build;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-
-// @@RequiresApi(16)
-final class MediaRouterJellybean {
-    private static final String TAG = "MediaRouterJellybean";
-
-    // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP = 0x80;
-    // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
-    // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
-    public static final int DEVICE_OUT_BLUETOOTH = 0x80 | 0x100 | 0x200;
-
-    public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1;
-    public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
-    public static final int ROUTE_TYPE_USER = 0x00800000;
-
-    public static final int ALL_ROUTE_TYPES =
-            MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO
-            | MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO
-            | MediaRouterJellybean.ROUTE_TYPE_USER;
-
-    public static Object getMediaRouter(Context context) {
-        return context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public static List getRoutes(Object routerObj) {
-        final android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-        final int count = router.getRouteCount();
-        List out = new ArrayList(count);
-        for (int i = 0; i < count; i++) {
-            out.add(router.getRouteAt(i));
-        }
-        return out;
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public static List getCategories(Object routerObj) {
-        final android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-        final int count = router.getCategoryCount();
-        List out = new ArrayList(count);
-        for (int i = 0; i < count; i++) {
-            out.add(router.getCategoryAt(i));
-        }
-        return out;
-    }
-
-    public static Object getSelectedRoute(Object routerObj, int type) {
-        return ((android.media.MediaRouter)routerObj).getSelectedRoute(type);
-    }
-
-    public static void selectRoute(Object routerObj, int types, Object routeObj) {
-        ((android.media.MediaRouter)routerObj).selectRoute(types,
-                (android.media.MediaRouter.RouteInfo)routeObj);
-    }
-
-    public static void addCallback(Object routerObj, int types, Object callbackObj) {
-        ((android.media.MediaRouter)routerObj).addCallback(types,
-                (android.media.MediaRouter.Callback)callbackObj);
-    }
-
-    public static void removeCallback(Object routerObj, Object callbackObj) {
-        ((android.media.MediaRouter)routerObj).removeCallback(
-                (android.media.MediaRouter.Callback)callbackObj);
-    }
-
-    public static Object createRouteCategory(Object routerObj,
-            String name, boolean isGroupable) {
-        return ((android.media.MediaRouter)routerObj).createRouteCategory(name, isGroupable);
-    }
-
-    public static Object createUserRoute(Object routerObj, Object categoryObj) {
-        return ((android.media.MediaRouter)routerObj).createUserRoute(
-                (android.media.MediaRouter.RouteCategory)categoryObj);
-    }
-
-    public static void addUserRoute(Object routerObj, Object routeObj) {
-        ((android.media.MediaRouter)routerObj).addUserRoute(
-                (android.media.MediaRouter.UserRouteInfo)routeObj);
-    }
-
-    public static void removeUserRoute(Object routerObj, Object routeObj) {
-        ((android.media.MediaRouter)routerObj).removeUserRoute(
-                (android.media.MediaRouter.UserRouteInfo)routeObj);
-    }
-
-    public static Object createCallback(Callback callback) {
-        return new CallbackProxy<Callback>(callback);
-    }
-
-    public static Object createVolumeCallback(VolumeCallback callback) {
-        return new VolumeCallbackProxy<VolumeCallback>(callback);
-    }
-
-    static boolean checkRoutedToBluetooth(Context context) {
-        try {
-            AudioManager audioManager = (AudioManager) context.getSystemService(
-                    Context.AUDIO_SERVICE);
-            Method method = audioManager.getClass().getDeclaredMethod(
-                    "getDevicesForStream", int.class);
-            int device = (Integer) method.invoke(audioManager, AudioManager.STREAM_MUSIC);
-            return (device & DEVICE_OUT_BLUETOOTH) != 0;
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
-    public static final class RouteInfo {
-        public static CharSequence getName(Object routeObj, Context context) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getName(context);
-        }
-
-        public static CharSequence getStatus(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getStatus();
-        }
-
-        public static int getSupportedTypes(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getSupportedTypes();
-        }
-
-        public static Object getCategory(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getCategory();
-        }
-
-        public static Drawable getIconDrawable(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getIconDrawable();
-        }
-
-        public static int getPlaybackType(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackType();
-        }
-
-        public static int getPlaybackStream(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackStream();
-        }
-
-        public static int getVolume(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getVolume();
-        }
-
-        public static int getVolumeMax(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeMax();
-        }
-
-        public static int getVolumeHandling(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeHandling();
-        }
-
-        public static Object getTag(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getTag();
-        }
-
-        public static void setTag(Object routeObj, Object tag) {
-            ((android.media.MediaRouter.RouteInfo)routeObj).setTag(tag);
-        }
-
-        public static void requestSetVolume(Object routeObj, int volume) {
-            ((android.media.MediaRouter.RouteInfo)routeObj).requestSetVolume(volume);
-        }
-
-        public static void requestUpdateVolume(Object routeObj, int direction) {
-            ((android.media.MediaRouter.RouteInfo)routeObj).requestUpdateVolume(direction);
-        }
-
-        public static Object getGroup(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getGroup();
-        }
-
-        public static boolean isGroup(Object routeObj) {
-            return routeObj instanceof android.media.MediaRouter.RouteGroup;
-        }
-    }
-
-    public static final class RouteGroup {
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        public static List getGroupedRoutes(Object groupObj) {
-            final android.media.MediaRouter.RouteGroup group =
-                    (android.media.MediaRouter.RouteGroup)groupObj;
-            final int count = group.getRouteCount();
-            List out = new ArrayList(count);
-            for (int i = 0; i < count; i++) {
-                out.add(group.getRouteAt(i));
-            }
-            return out;
-        }
-    }
-
-    public static final class UserRouteInfo {
-        public static void setName(Object routeObj, CharSequence name) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setName(name);
-        }
-
-        public static void setStatus(Object routeObj, CharSequence status) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setStatus(status);
-        }
-
-        public static void setIconDrawable(Object routeObj, Drawable icon) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setIconDrawable(icon);
-        }
-
-        public static void setPlaybackType(Object routeObj, int type) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackType(type);
-        }
-
-        public static void setPlaybackStream(Object routeObj, int stream) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackStream(stream);
-        }
-
-        public static void setVolume(Object routeObj, int volume) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolume(volume);
-        }
-
-        public static void setVolumeMax(Object routeObj, int volumeMax) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeMax(volumeMax);
-        }
-
-        public static void setVolumeHandling(Object routeObj, int volumeHandling) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeHandling(volumeHandling);
-        }
-
-        public static void setVolumeCallback(Object routeObj, Object volumeCallbackObj) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeCallback(
-                    (android.media.MediaRouter.VolumeCallback)volumeCallbackObj);
-        }
-
-        public static void setRemoteControlClient(Object routeObj, Object rccObj) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setRemoteControlClient(
-                    (android.media.RemoteControlClient)rccObj);
-        }
-    }
-
-    public static final class RouteCategory {
-        public static CharSequence getName(Object categoryObj, Context context) {
-            return ((android.media.MediaRouter.RouteCategory)categoryObj).getName(context);
-        }
-
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        public static List getRoutes(Object categoryObj) {
-            ArrayList out = new ArrayList();
-            ((android.media.MediaRouter.RouteCategory)categoryObj).getRoutes(out);
-            return out;
-        }
-
-        public static int getSupportedTypes(Object categoryObj) {
-            return ((android.media.MediaRouter.RouteCategory)categoryObj).getSupportedTypes();
-        }
-
-        public static boolean isGroupable(Object categoryObj) {
-            return ((android.media.MediaRouter.RouteCategory)categoryObj).isGroupable();
-        }
-    }
-
-    public static interface Callback {
-        public void onRouteSelected(int type, Object routeObj);
-        public void onRouteUnselected(int type, Object routeObj);
-        public void onRouteAdded(Object routeObj);
-        public void onRouteRemoved(Object routeObj);
-        public void onRouteChanged(Object routeObj);
-        public void onRouteGrouped(Object routeObj, Object groupObj, int index);
-        public void onRouteUngrouped(Object routeObj, Object groupObj);
-        public void onRouteVolumeChanged(Object routeObj);
-    }
-
-    public static interface VolumeCallback {
-        public void onVolumeSetRequest(Object routeObj, int volume);
-        public void onVolumeUpdateRequest(Object routeObj, int direction);
-    }
-
-    /**
-     * Workaround for limitations of selectRoute() on JB and JB MR1.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class SelectRouteWorkaround {
-        private Method mSelectRouteIntMethod;
-
-        public SelectRouteWorkaround() {
-            if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) {
-                throw new UnsupportedOperationException();
-            }
-            try {
-                mSelectRouteIntMethod = android.media.MediaRouter.class.getMethod(
-                        "selectRouteInt", int.class, android.media.MediaRouter.RouteInfo.class);
-            } catch (NoSuchMethodException ex) {
-            }
-        }
-
-        public void selectRoute(Object routerObj, int types, Object routeObj) {
-            android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-            android.media.MediaRouter.RouteInfo route =
-                    (android.media.MediaRouter.RouteInfo)routeObj;
-
-            int routeTypes = route.getSupportedTypes();
-            if ((routeTypes & ROUTE_TYPE_USER) == 0) {
-                // Handle non-user routes.
-                // On JB and JB MR1, the selectRoute() API only supports programmatically
-                // selecting user routes.  So instead we rely on the hidden selectRouteInt()
-                // method on these versions of the platform.
-                // This limitation was removed in JB MR2.
-                if (mSelectRouteIntMethod != null) {
-                    try {
-                        mSelectRouteIntMethod.invoke(router, types, route);
-                        return; // success!
-                    } catch (IllegalAccessException ex) {
-                        Log.w(TAG, "Cannot programmatically select non-user route.  "
-                                + "Media routing may not work.", ex);
-                    } catch (InvocationTargetException ex) {
-                        Log.w(TAG, "Cannot programmatically select non-user route.  "
-                                + "Media routing may not work.", ex);
-                    }
-                } else {
-                    Log.w(TAG, "Cannot programmatically select non-user route "
-                            + "because the platform is missing the selectRouteInt() "
-                            + "method.  Media routing may not work.");
-                }
-            }
-
-            // Default handling.
-            router.selectRoute(types, route);
-        }
-    }
-
-    /**
-     * Workaround the fact that the getDefaultRoute() method does not exist in JB and JB MR1.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class GetDefaultRouteWorkaround {
-        private Method mGetSystemAudioRouteMethod;
-
-        public GetDefaultRouteWorkaround() {
-            if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) {
-                throw new UnsupportedOperationException();
-            }
-            try {
-                mGetSystemAudioRouteMethod =
-                        android.media.MediaRouter.class.getMethod("getSystemAudioRoute");
-            } catch (NoSuchMethodException ex) {
-            }
-        }
-
-        public Object getDefaultRoute(Object routerObj) {
-            android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-
-            if (mGetSystemAudioRouteMethod != null) {
-                try {
-                    return mGetSystemAudioRouteMethod.invoke(router);
-                } catch (IllegalAccessException ex) {
-                } catch (InvocationTargetException ex) {
-                }
-            }
-
-            // Could not find the method or it does not work.
-            // Return the first route and hope for the best.
-            return router.getRouteAt(0);
-        }
-    }
-
-    static class CallbackProxy<T extends Callback>
-            extends android.media.MediaRouter.Callback {
-        protected final T mCallback;
-
-        public CallbackProxy(T callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onRouteSelected(android.media.MediaRouter router,
-                int type, android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteSelected(type, route);
-        }
-
-        @Override
-        public void onRouteUnselected(android.media.MediaRouter router,
-                int type, android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteUnselected(type, route);
-        }
-
-        @Override
-        public void onRouteAdded(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteAdded(route);
-        }
-
-        @Override
-        public void onRouteRemoved(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteRemoved(route);
-        }
-
-        @Override
-        public void onRouteChanged(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteChanged(route);
-        }
-
-        @Override
-        public void onRouteGrouped(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route,
-                android.media.MediaRouter.RouteGroup group, int index) {
-            mCallback.onRouteGrouped(route, group, index);
-        }
-
-        @Override
-        public void onRouteUngrouped(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route,
-                android.media.MediaRouter.RouteGroup group) {
-            mCallback.onRouteUngrouped(route, group);
-        }
-
-        @Override
-        public void onRouteVolumeChanged(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteVolumeChanged(route);
-        }
-    }
-
-    static class VolumeCallbackProxy<T extends VolumeCallback>
-            extends android.media.MediaRouter.VolumeCallback {
-        protected final T mCallback;
-
-        public VolumeCallbackProxy(T callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onVolumeSetRequest(android.media.MediaRouter.RouteInfo route,
-                int volume) {
-            mCallback.onVolumeSetRequest(route, volume);
-        }
-
-        @Override
-        public void onVolumeUpdateRequest(android.media.MediaRouter.RouteInfo route,
-                int direction) {
-            mCallback.onVolumeUpdateRequest(route, direction);
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaControlIntent.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaControlIntent.java
deleted file mode 100644
index 1d9e777..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaControlIntent.java
+++ /dev/null
@@ -1,1228 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.net.Uri;
-
-/**
- * Constants for media control intents.
- * <p>
- * This class declares a set of standard media control intent categories and actions that
- * applications can use to identify the capabilities of media routes and control them.
- * </p>
- *
- * <h3>Media control intent categories</h3>
- * <p>
- * Media control intent categories specify means by which applications can
- * send media to the destination of a media route.  Categories are sometimes referred
- * to as describing "types" or "kinds" of routes.
- * </p><p>
- * For example, if a route supports the {@link #CATEGORY_REMOTE_PLAYBACK remote playback category},
- * then an application can ask it to play media remotely by sending a
- * {@link #ACTION_PLAY play} or {@link #ACTION_ENQUEUE enqueue} intent with the Uri of the
- * media content to play.  Such a route may then be referred to as
- * a "remote playback route" because it supports remote playback requests.  It is common
- * for a route to support multiple categories of requests at the same time, such as
- * live audio and live video.
- * </p><p>
- * The following standard route categories are defined.
- * </p><ul>
- * <li>{@link #CATEGORY_LIVE_AUDIO Live audio}: The route supports streaming live audio
- * from the device to the destination.  Live audio routes include local speakers
- * and Bluetooth headsets.
- * <li>{@link #CATEGORY_LIVE_VIDEO Live video}: The route supports streaming live video
- * from the device to the destination.  Live video routes include local displays
- * and wireless displays that support mirroring and
- * {@link android.app.Presentation presentations}.  Live video routes typically also
- * support live audio capabilities.
- * <li>{@link #CATEGORY_REMOTE_PLAYBACK Remote playback}: The route supports sending
- * remote playback requests for media content to the destination.  The content to be
- * played is identified by a Uri and mime-type.
- * </ul><p>
- * Media route providers may define custom media control intent categories of their own in
- * addition to the standard ones.  Custom categories can be used to provide a variety
- * of features to applications that recognize and know how to use them.  For example,
- * a media route provider might define a custom category to indicate that its routes
- * support a special device-specific control interface in addition to other
- * standard features.
- * </p><p>
- * Applications can determine which categories a route supports by using the
- * {@link MediaRouter.RouteInfo#supportsControlCategory MediaRouter.RouteInfo.supportsControlCategory}
- * or {@link MediaRouter.RouteInfo#getControlFilters MediaRouter.RouteInfo.getControlFilters}
- * methods.  Applications can also specify the types of routes that they want to use by
- * creating {@link MediaRouteSelector media route selectors} that contain the desired
- * categories and are used to filter routes in several parts of the media router API.
- * </p>
- *
- * <h3>Media control intent actions</h3>
- * <p>
- * Media control intent actions specify particular functions that applications
- * can ask the destination of a media route to perform.  Media route control requests
- * take the form of intents in a similar manner to other intents used to start activities
- * or send broadcasts.  The difference is that media control intents are directed to
- * routes rather than activity or broadcast receiver components.
- * </p><p>
- * Each media route control intent specifies an action, a category and some number of parameters
- * that are supplied as extras.  Applications send media control requests to routes using the
- * {@link MediaRouter.RouteInfo#sendControlRequest MediaRouter.RouteInfo.sendControlRequest}
- * method and receive results via a callback.
- * </p><p>
- * All media control intent actions are associated with the media control intent categories
- * that support them.  Thus only remote playback routes may perform remote playback actions.
- * The documentation of each action specifies the category to which the action belongs,
- * the parameters it requires, and the results it returns.
- * </p>
- *
- * <h3>Live audio and live video routes</h3>
- * <p>
- * {@link #CATEGORY_LIVE_AUDIO Live audio} and {@link #CATEGORY_LIVE_VIDEO live video}
- * routes present media using standard system interfaces such as audio streams,
- * {@link android.app.Presentation presentations} or display mirroring.  These routes are
- * the easiest to use because applications simply render content locally on the device
- * and the system streams it to the route destination automatically.
- * </p><p>
- * In most cases, applications can stream content to live audio and live video routes in
- * the same way they would play the content locally without any modification.  However,
- * applications may also be able to take advantage of more sophisticated features such
- * as second-screen presentation APIs that are particular to these routes.
- * </p>
- *
- * <h3>Remote playback routes</h3>
- * <p>
- * {@link #CATEGORY_REMOTE_PLAYBACK Remote playback} routes present media remotely
- * by playing content from a Uri.
- * These routes destinations take responsibility for fetching and rendering content
- * on their own.  Applications do not render the content themselves; instead, applications
- * send control requests to initiate play, pause, resume, or stop media items and receive
- * status updates as they change state.
- * </p>
- *
- * <h4>Sessions</h4>
- * <p>
- * Each remote media playback action is conducted within the scope of a session.
- * Sessions are used to prevent applications from accidentally interfering with one
- * another because at most one session can be valid at a time.
- * </p><p>
- * A session can be created using the {@link #ACTION_START_SESSION start session action}
- * and terminated using the {@link #ACTION_END_SESSION end session action} when the
- * route provides explicit session management features.
- * </p><p>
- * Explicit session management was added in a later revision of the protocol so not
- * all routes support it.  If the route does not support explicit session management
- * then implicit session management may still be used.  Implicit session management
- * relies on the use of the {@link #ACTION_PLAY play} and {@link #ACTION_ENQUEUE enqueue}
- * actions which have the side-effect of creating a new session if none is provided
- * as argument.
- * </p><p>
- * When a new session is created, the previous session is invalidated and any ongoing
- * media playback is stopped before the requested action is performed.  Any attempt
- * to use an invalidated session will result in an error.  (Protocol implementations
- * are encouraged to aggressively discard information associated with invalidated sessions
- * since it is no longer of use.)
- * </p><p>
- * Each session is identified by a unique session id that may be used to control
- * the session using actions such as pause, resume, stop and end session.
- * </p>
- *
- * <h4>Media items</h4>
- * <p>
- * Each successful {@link #ACTION_PLAY play} or {@link #ACTION_ENQUEUE enqueue} action
- * returns a unique media item id that an application can use to monitor and control
- * playback.  The media item id may be passed to other actions such as
- * {@link #ACTION_SEEK seek} or {@link #ACTION_GET_STATUS get status}.  It will also appear
- * as a parameter in status update broadcasts to identify the associated playback request.
- * </p><p>
- * Each media item is scoped to the session in which it was created.  Therefore media item
- * ids are only ever used together with session ids.  Media item ids are meaningless
- * on their own.  When the session is invalidated, all of its media items are also
- * invalidated.
- * </p>
- *
- * <h4>The playback queue</h4>
- * <p>
- * Each session has its own playback queue that consists of the media items that
- * are pending, playing, buffering or paused.  Items are added to the queue when
- * a playback request is issued.  Items are removed from the queue when they are no
- * longer eligible for playback (enter terminal states).
- * </p><p>
- * As described in the {@link MediaItemStatus} class, media items initially
- * start in a pending state, transition to the playing (or buffering or paused) state
- * during playback, and end in a finished, canceled, invalidated or error state.
- * Once the current item enters a terminal state, playback proceeds on to the
- * next item.
- * </p><p>
- * The application should determine whether the route supports queuing by checking
- * whether the {@link #ACTION_ENQUEUE} action is declared in the route's control filter
- * using {@link MediaRouter.RouteInfo#supportsControlRequest RouteInfo.supportsControlRequest}.
- * </p><p>
- * If the {@link #ACTION_ENQUEUE} action is supported by the route, then the route promises
- * to allow at least two items (possibly more) to be enqueued at a time.  Enqueued items play
- * back to back one after the other as the previous item completes.  Ideally there should
- * be no audible pause between items for standard audio content types.
- * </p><p>
- * If the {@link #ACTION_ENQUEUE} action is not supported by the route, then the queue
- * effectively contains at most one item at a time.  Each play action has the effect of
- * clearing the queue and resetting its state before the next item is played.
- * </p>
- *
- * <h4>Impact of pause, resume, stop and play actions on the playback queue</h4>
- * <p>
- * The pause, resume and stop actions affect the session's whole queue.  Pause causes
- * the playback queue to be suspended no matter which item is currently playing.
- * Resume reverses the effects of pause.  Stop clears the queue and also resets
- * the pause flag just like resume.
- * </p><p>
- * As described earlier, the play action has the effect of clearing the queue
- * and completely resetting its state (like the stop action) then enqueuing a
- * new media item to be played immediately.  Play is therefore equivalent
- * to stop followed by an action to enqueue an item.
- * </p><p>
- * The play action is also special in that it can be used to create new sessions.
- * An application with simple needs may find that it only needs to use play
- * (and occasionally stop) to control playback.
- * </p>
- *
- * <h4>Resolving conflicts between applications</h4>
- * <p>
- * When an application has a valid session, it is essentially in control of remote playback
- * on the route.  No other application can view or modify the remote playback state
- * of that application's session without knowing its id.
- * </p><p>
- * However, other applications can perform actions that have the effect of stopping
- * playback and invalidating the current session.  When this occurs, the former application
- * will be informed that it has lost control by way of individual media item status
- * update broadcasts that indicate that its queued media items have become
- * {@link MediaItemStatus#PLAYBACK_STATE_INVALIDATED invalidated}.  This broadcast
- * implies that playback was terminated abnormally by an external cause.
- * </p><p>
- * Applications should handle conflicts conservatively to allow other applications to
- * smoothly assume control over the route.  When a conflict occurs, the currently playing
- * application should release its session and allow the new application to use the
- * route until such time as the user intervenes to take over the route again and begin
- * a new playback session.
- * </p>
- *
- * <h4>Basic actions</h4>
- * <p>
- * The following basic actions must be supported (all or nothing) by all remote
- * playback routes.  These actions form the basis of the remote playback protocol
- * and are required in all implementations.
- * </p><ul>
- * <li>{@link #ACTION_PLAY Play}: Starts playing content specified by a given Uri
- * and returns a new media item id to describe the request.  Implicitly creates a new
- * session if no session id was specified as a parameter.
- * <li>{@link #ACTION_SEEK Seek}: Sets the content playback position of a specific media item.
- * <li>{@link #ACTION_GET_STATUS Get status}: Gets the status of a media item
- * including the item's current playback position and progress.
- * <li>{@link #ACTION_PAUSE Pause}: Pauses playback of the queue.
- * <li>{@link #ACTION_RESUME Resume}: Resumes playback of the queue.
- * <li>{@link #ACTION_STOP Stop}: Stops playback, clears the queue, and resets the
- * pause state.
- * </ul>
- *
- * <h4>Queue actions</h4>
- * <p>
- * The following queue actions must be supported (all or nothing) by remote
- * playback routes that offer optional queuing capabilities.
- * </p><ul>
- * <li>{@link #ACTION_ENQUEUE Enqueue}: Enqueues content specified by a given Uri
- * and returns a new media item id to describe the request.  Implicitly creates a new
- * session if no session id was specified as a parameter.
- * <li>{@link #ACTION_REMOVE Remove}: Removes a specified media item from the queue.
- * </ul>
- *
- * <h4>Session actions</h4>
- * <p>
- * The following session actions must be supported (all or nothing) by remote
- * playback routes that offer optional session management capabilities.
- * </p><ul>
- * <li>{@link #ACTION_START_SESSION Start session}: Starts a new session explicitly.
- * <li>{@link #ACTION_GET_SESSION_STATUS Get session status}: Gets the status of a session.
- * <li>{@link #ACTION_END_SESSION End session}: Ends a session explicitly.
- * </ul>
- *
- * <h4>Implementation note</h4>
- * <p>
- * Implementations of the remote playback protocol must implement <em>all</em> of the
- * documented actions, parameters and results.  Note that the documentation is written from
- * the perspective of a client of the protocol.  In particular, whenever a parameter
- * is described as being "optional", it is only from the perspective of the client.
- * Compliant media route provider implementations of this protocol must support all
- * of the features described herein.
- * </p>
- */
-public final class MediaControlIntent {
-    /* Route categories. */
-
-    /**
-     * Media control category: Live audio.
-     * <p>
-     * A route that supports live audio routing will allow the media audio stream
-     * to be sent to supported destinations.  This can include internal speakers or
-     * audio jacks on the device itself, A2DP devices, and more.
-     * </p><p>
-     * When a live audio route is selected, audio routing is transparent to the application.
-     * All audio played on the media stream will be routed to the selected destination.
-     * </p><p>
-     * Refer to the class documentation for details about live audio routes.
-     * </p>
-     */
-    public static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
-
-    /**
-     * Media control category: Live video.
-     * <p>
-     * A route that supports live video routing will allow a mirrored version
-     * of the device's primary display or a customized
-     * {@link android.app.Presentation Presentation} to be sent to supported
-     * destinations.
-     * </p><p>
-     * When a live video route is selected, audio and video routing is transparent
-     * to the application.  By default, audio and video is routed to the selected
-     * destination.  For certain live video routes, the application may also use a
-     * {@link android.app.Presentation Presentation} to replace the mirrored view
-     * on the external display with different content.
-     * </p><p>
-     * Refer to the class documentation for details about live video routes.
-     * </p>
-     *
-     * @see MediaRouter.RouteInfo#getPresentationDisplay()
-     * @see android.app.Presentation
-     */
-    public static final String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
-
-    /**
-     * Media control category: Remote playback.
-     * <p>
-     * A route that supports remote playback routing will allow an application to send
-     * requests to play content remotely to supported destinations.
-     * </p><p>
-     * Remote playback routes destinations operate independently of the local device.
-     * When a remote playback route is selected, the application can control the content
-     * playing on the destination by sending media control actions to the route.
-     * The application may also receive status updates from the route regarding
-     * remote playback.
-     * </p><p>
-     * Refer to the class documentation for details about remote playback routes.
-     * </p>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     */
-    public static final String CATEGORY_REMOTE_PLAYBACK =
-            "android.media.intent.category.REMOTE_PLAYBACK";
-
-    /* Remote playback actions that affect individual items. */
-
-    /**
-     * Remote playback media control action: Play media item.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes a remote playback route to start playing content with
-     * the {@link Uri} specified in the {@link Intent}'s {@link Intent#getData() data uri}.
-     * The action returns a media session id and media item id which can be used
-     * to control playback using other remote playback actions.
-     * </p><p>
-     * Once initiated, playback of the specified content will be managed independently
-     * by the destination.  The application will receive status updates as the state
-     * of the media item changes.
-     * </p><p>
-     * If the data uri specifies an HTTP or HTTPS scheme, then the destination is
-     * responsible for following HTTP redirects to a reasonable depth of at least 3
-     * levels as might typically be handled by a web browser.  If an HTTP error
-     * occurs, then the destination should send a {@link MediaItemStatus status update}
-     * back to the client indicating the {@link MediaItemStatus#PLAYBACK_STATE_ERROR error}
-     * {@link MediaItemStatus#getPlaybackState() playback state}.
-     * </p>
-     *
-     * <h3>One item at a time</h3>
-     * <p>
-     * Each successful play action <em>replaces</em> the previous play action.
-     * If an item is already playing, then it is canceled, the session's playback queue
-     * is cleared and the new item begins playing immediately (regardless of
-     * whether the previously playing item had been paused).
-     * </p><p>
-     * Play is therefore equivalent to {@link #ACTION_STOP stop} followed by an action
-     * to enqueue a new media item to be played immediately.
-     * </p>
-     *
-     * <h3>Sessions</h3>
-     * <p>
-     * This request has the effect of implicitly creating a media session whenever the
-     * application does not specify the {@link #EXTRA_SESSION_ID session id} parameter.
-     * Because there can only be at most one valid session at a time, creating a new session
-     * has the side-effect of invalidating any existing sessions and their media items,
-     * then handling the playback request with a new session.
-     * </p><p>
-     * If the application specifies an invalid session id, then an error is returned.
-     * When this happens, the application should assume that its session
-     * is no longer valid.  To obtain a new session, the application may try again
-     * and omit the session id parameter.  However, the application should
-     * only retry requests due to an explicit action performed by the user,
-     * such as the user clicking on a "play" button in the UI, since another
-     * application may be trying to take control of the route and the former
-     * application should try to stay out of its way.
-     * </p><p>
-     * For more information on sessions, queues and media items, please refer to the
-     * class documentation.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(optional)</em>: Specifies the session id of the
-     * session to which the playback request belongs.  If omitted, a new session
-     * is created implicitly.
-     * <li>{@link #EXTRA_ITEM_CONTENT_POSITION} <em>(optional)</em>: Specifies the initial
-     * content playback position as a long integer number of milliseconds from
-     * the beginning of the content.
-     * <li>{@link #EXTRA_ITEM_METADATA} <em>(optional)</em>: Specifies metadata associated
-     * with the content such as the title of a song.
-     * <li>{@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER} <em>(optional)</em>: Specifies a
-     * {@link PendingIntent} for a broadcast receiver that will receive status updates
-     * about the media item.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(always returned)</em>: Specifies the session id of the
-     * session that was affected by the request.  This will be a new session in
-     * the case where no session id was supplied as a parameter.
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * <li>{@link #EXTRA_ITEM_ID} <em>(always returned)</em>: Specifies an opaque string identifier
-     * to use to refer to the media item in subsequent requests such as
-     * {@link #ACTION_GET_STATUS}.
-     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the initial status of
-     * the new media item.
-     * </ul>
-     *
-     * <h3>Status updates</h3>
-     * <p>
-     * If the client supplies an
-     * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receiver}
-     * then the media route provider is responsible for sending status updates to the receiver
-     * when significant media item state changes occur such as when playback starts or
-     * stops.  The receiver will not be invoked for content playback position changes.
-     * The application may retrieve the current playback position when necessary
-     * using the {@link #ACTION_GET_STATUS} request.
-     * </p><p>
-     * Refer to {@link MediaItemStatus} for details.
-     * </p>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if a session id was provided but is unknown or
-     * no longer valid, if the item Uri or content type is not supported, or if
-     * any other arguments are invalid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * <h3>Example</h3>
-     * <pre>
-     * MediaRouter mediaRouter = MediaRouter.getInstance(context);
-     * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
-     * Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
-     * intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
-     * intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4");
-     * if (route.supportsControlRequest(intent)) {
-     *     MediaRouter.ControlRequestCallback callback = new MediaRouter.ControlRequestCallback() {
-     *         public void onResult(Bundle data) {
-     *             // The request succeeded.
-     *             // Playback may be controlled using the returned session and item id.
-     *             String sessionId = data.getString(MediaControlIntent.EXTRA_SESSION_ID);
-     *             String itemId = data.getString(MediaControlIntent.EXTRA_ITEM_ID);
-     *             MediaItemStatus status = MediaItemStatus.fromBundle(data.getBundle(
-     *                     MediaControlIntent.EXTRA_ITEM_STATUS));
-     *             // ...
-     *         }
-     *
-     *         public void onError(String message, Bundle data) {
-     *             // An error occurred!
-     *         }
-     *     };
-     *     route.sendControlRequest(intent, callback);
-     * }</pre>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #ACTION_SEEK
-     * @see #ACTION_GET_STATUS
-     * @see #ACTION_PAUSE
-     * @see #ACTION_RESUME
-     * @see #ACTION_STOP
-     */
-    public static final String ACTION_PLAY = "android.media.intent.action.PLAY";
-
-    /**
-     * Remote playback media control action: Enqueue media item.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action works just like {@link #ACTION_PLAY play} except that it does
-     * not clear the queue or reset the pause state when it enqueues the
-     * new media item into the session's playback queue.  This action only
-     * enqueues a media item with no other side-effects on the queue.
-     * </p><p>
-     * If the queue is currently empty and then the item will play immediately
-     * (assuming the queue is not paused).  Otherwise, the item will play
-     * after all earlier items in the queue have finished or been removed.
-     * </p><p>
-     * The enqueue action can be used to create new sessions just like play.
-     * Its parameters and results are also the same.  Only the queuing behavior
-     * is different.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     */
-    public static final String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE";
-
-    /**
-     * Remote playback media control action: Seek media item to a new playback position.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes a remote playback route to modify the current playback position
-     * of the specified media item.
-     * </p><p>
-     * This action only affects the playback position of the media item; not its playback state.
-     * If the playback queue is paused, then seeking sets the position but the item
-     * remains paused.  Likewise if the item is playing, then seeking will cause playback
-     * to jump to the new position and continue playing from that point.  If the item has
-     * not yet started playing, then the new playback position is remembered by the
-     * queue and used as the item's initial content position when playback eventually begins.
-     * </p><p>
-     * If successful, the media item's playback position is changed.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * to which the media item belongs.
-     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of
-     * the media item to seek.
-     * <li>{@link #EXTRA_ITEM_CONTENT_POSITION} <em>(required)</em>: Specifies the new
-     * content position for playback as a long integer number of milliseconds from
-     * the beginning of the content.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the new status of
-     * the media item.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id or media item id are unknown
-     * or no longer valid, if the content position is invalid, or if the media item
-     * is in a terminal state.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     */
-    public static final String ACTION_SEEK = "android.media.intent.action.SEEK";
-
-    /**
-     * Remote playback media control action: Get media item playback status
-     * and progress information.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action asks a remote playback route to provide updated playback status and progress
-     * information about the specified media item.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * to which the media item belongs.
-     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of
-     * the media item to query.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the current status of
-     * the media item.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id or media item id are unknown
-     * or no longer valid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #EXTRA_ITEM_STATUS_UPDATE_RECEIVER
-     */
-    public static final String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS";
-
-    /**
-     * Remote playback media control action: Remove media item from session's queue.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action asks a remote playback route to remove the specified media item
-     * from the session's playback queue.  If the current item is removed, then
-     * playback will proceed to the next media item (assuming the queue has not been
-     * paused).
-     * </p><p>
-     * This action does not affect the pause state of the queue.  If the queue was paused
-     * then it remains paused (even if it is now empty) until a resume, stop or play
-     * action is issued that causes the pause state to be cleared.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * to which the media item belongs.
-     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of
-     * the media item to remove.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the new status of
-     * the media item.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id or media item id are unknown
-     * or no longer valid, or if the media item is in a terminal state (and therefore
-     * no longer in the queue).
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     */
-    public static final String ACTION_REMOVE = "android.media.intent.action.REMOVE";
-
-    /* Remote playback actions that affect the whole playback queue. */
-
-    /**
-     * Remote playback media control action: Pause media playback.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes the playback queue of the specified session to be paused.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * whose playback queue is to be paused.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id is unknown or no longer valid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #ACTION_RESUME
-     */
-    public static final String ACTION_PAUSE = "android.media.intent.action.PAUSE";
-
-    /**
-     * Remote playback media control action: Resume media playback (unpause).
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes the playback queue of the specified session to be resumed.
-     * Reverses the effects of {@link #ACTION_PAUSE}.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * whose playback queue is to be resumed.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id is unknown or no longer valid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #ACTION_PAUSE
-     */
-    public static final String ACTION_RESUME = "android.media.intent.action.RESUME";
-
-    /**
-     * Remote playback media control action: Stop media playback (clear queue and unpause).
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes a remote playback route to stop playback, cancel and remove
-     * all media items from the session's media item queue and, reset the queue's
-     * pause state.
-     * </p><p>
-     * If successful, the status of all media items in the queue is set to
-     * {@link MediaItemStatus#PLAYBACK_STATE_CANCELED canceled} and a status update is sent
-     * to the appropriate status update receivers indicating the new status of each item.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
-     * the session whose playback queue is to be stopped (cleared and unpaused).
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id is unknown or no longer valid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     */
-    public static final String ACTION_STOP = "android.media.intent.action.STOP";
-
-    /**
-     * Remote playback media control action: Start session.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes a remote playback route to invalidate the current session
-     * and start a new session.  The new session initially has an empty queue.
-     * </p><p>
-     * If successful, the status of all media items in the previous session's queue is set to
-     * {@link MediaItemStatus#PLAYBACK_STATE_INVALIDATED invalidated} and a status update
-     * is sent to the appropriate status update receivers indicating the new status
-     * of each item.  The previous session becomes no longer valid and the new session
-     * takes control of the route.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER} <em>(optional)</em>: Specifies a
-     * {@link PendingIntent} for a broadcast receiver that will receive status updates
-     * about the media session.
-     * <li>{@link #EXTRA_MESSAGE_RECEIVER} <em>(optional)</em>: Specifies a
-     * {@link PendingIntent} for a broadcast receiver that will receive messages from
-     * the media session.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(always returned)</em>: Specifies the session id of the
-     * session that was started by the request.  This will always be a brand new session
-     * distinct from any other previously created sessions.
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(always returned)</em>: Specifies the
-     * status of the media session.
-     * </ul>
-     *
-     * <h3>Status updates</h3>
-     * <p>
-     * If the client supplies a
-     * {@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER status update receiver}
-     * then the media route provider is responsible for sending status updates to the receiver
-     * when significant media session state changes occur such as when the session's
-     * queue is paused or resumed or when the session is terminated or invalidated.
-     * </p><p>
-     * Refer to {@link MediaSessionStatus} for details.
-     * </p>
-     *
-     * <h3>Custom messages</h3>
-     * <p>
-     * If the client supplies a {@link #EXTRA_MESSAGE_RECEIVER message receiver}
-     * then the media route provider is responsible for sending messages to the receiver
-     * when the session has any messages to send.
-     * </p><p>
-     * Refer to {@link #EXTRA_MESSAGE} for details.
-     * </p>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session could not be created.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     */
-    public static final String ACTION_START_SESSION = "android.media.intent.action.START_SESSION";
-
-    /**
-     * Remote playback media control action: Get media session status information.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action asks a remote playback route to provide updated status information
-     * about the specified media session.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the
-     * session whose status is to be retrieved.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(always returned)</em>: Specifies the
-     * current status of the media session.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id is unknown or no longer valid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #EXTRA_SESSION_STATUS_UPDATE_RECEIVER
-     */
-    public static final String ACTION_GET_SESSION_STATUS =
-            "android.media.intent.action.GET_SESSION_STATUS";
-
-    /**
-     * Remote playback media control action: End session.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes a remote playback route to end the specified session.
-     * The session becomes no longer valid and the route ceases to be under control
-     * of the session.
-     * </p><p>
-     * If successful, the status of the session is set to
-     * {@link MediaSessionStatus#SESSION_STATE_ENDED} and a status update is sent to
-     * the session's status update receiver.
-     * </p><p>
-     * Additionally, the status of all media items in the queue is set to
-     * {@link MediaItemStatus#PLAYBACK_STATE_CANCELED canceled} and a status update is sent
-     * to the appropriate status update receivers indicating the new status of each item.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
-     * the session to end.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(always returned)</em>: Specifies the
-     * status of the media session.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id is unknown or no longer valid.
-     * In other words, it is an error to attempt to end a session other than the
-     * current session.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     */
-    public static final String ACTION_END_SESSION = "android.media.intent.action.END_SESSION";
-
-    /**
-     * Custom media control action: Send {@link #EXTRA_MESSAGE}.
-     * <p>
-     * This action asks a route to handle a message described by EXTRA_MESSAGE.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * to which will handle this message.
-     * <li>{@link #EXTRA_MESSAGE} <em>(required)</em>: Specifies the message to send.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * Any messages defined by each media route provider.
-     *
-     * <h3>Errors</h3>
-     * Any error messages defined by each media route provider.
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     */
-    public static final String ACTION_SEND_MESSAGE = "android.media.intent.action.SEND_MESSAGE";
-
-    /* Extras and related constants. */
-
-    /**
-     * Bundle extra: Media session id.
-     * <p>
-     * An opaque unique identifier that identifies the remote playback media session.
-     * </p><p>
-     * Used with various actions to specify the id of the media session to be controlled.
-     * </p><p>
-     * Included in broadcast intents sent to
-     * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receivers} to identify
-     * the session to which the item in question belongs.
-     * </p><p>
-     * Included in broadcast intents sent to
-     * {@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER session status update receivers} to identify
-     * the session.
-     * </p><p>
-     * The value is a unique string value generated by the media route provider
-     * to represent one particular media session.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_SEEK
-     * @see #ACTION_GET_STATUS
-     * @see #ACTION_PAUSE
-     * @see #ACTION_RESUME
-     * @see #ACTION_STOP
-     * @see #ACTION_START_SESSION
-     * @see #ACTION_GET_SESSION_STATUS
-     * @see #ACTION_END_SESSION
-     */
-    public static final String EXTRA_SESSION_ID =
-            "android.media.intent.extra.SESSION_ID";
-
-    /**
-     * Bundle extra: Media session status.
-     * <p>
-     * Returned as a result from media session actions such as {@link #ACTION_START_SESSION},
-     * {@link #ACTION_PAUSE}, and {@link #ACTION_GET_SESSION_STATUS}
-     * to describe the status of the specified media session.
-     * </p><p>
-     * Included in broadcast intents sent to
-     * {@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER session status update receivers} to provide
-     * updated status information.
-     * </p><p>
-     * The value is a {@link android.os.Bundle} of data that can be converted into
-     * a {@link MediaSessionStatus} object using
-     * {@link MediaSessionStatus#fromBundle MediaSessionStatus.fromBundle}.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_SEEK
-     * @see #ACTION_GET_STATUS
-     * @see #ACTION_PAUSE
-     * @see #ACTION_RESUME
-     * @see #ACTION_STOP
-     * @see #ACTION_START_SESSION
-     * @see #ACTION_GET_SESSION_STATUS
-     * @see #ACTION_END_SESSION
-     */
-    public static final String EXTRA_SESSION_STATUS =
-            "android.media.intent.extra.SESSION_STATUS";
-
-    /**
-     * Bundle extra: Media session status update receiver.
-     * <p>
-     * Used with {@link #ACTION_START_SESSION} to specify a {@link PendingIntent} for a
-     * broadcast receiver that will receive status updates about the media session.
-     * </p><p>
-     * Whenever the status of the media session changes, the media route provider will
-     * send a broadcast to the pending intent with extras that identify the session
-     * id and its updated status.
-     * </p><p>
-     * The value is a {@link PendingIntent}.
-     * </p>
-     *
-     * <h3>Broadcast extras</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
-     * the session.
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(required)</em>: Specifies the status of the
-     * session as a bundle that can be decoded into a {@link MediaSessionStatus} object.
-     * </ul>
-     *
-     * @see #ACTION_START_SESSION
-     */
-    public static final String EXTRA_SESSION_STATUS_UPDATE_RECEIVER =
-            "android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER";
-
-    /**
-     * Bundle extra: Media message receiver.
-     * <p>
-     * Used with {@link #ACTION_START_SESSION} to specify a {@link PendingIntent} for a
-     * broadcast receiver that will receive messages from the media session.
-     * </p><p>
-     * When the media session has a message to send, the media route provider will
-     * send a broadcast to the pending intent with extras that identify the session
-     * id and its message.
-     * </p><p>
-     * The value is a {@link PendingIntent}.
-     * </p>
-     *
-     * <h3>Broadcast extras</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
-     * the session.
-     * <li>{@link #EXTRA_MESSAGE} <em>(required)</em>: Specifies the message from
-     * the session as a bundle object.
-     * </ul>
-     *
-     * @see #ACTION_START_SESSION
-     */
-    public static final String EXTRA_MESSAGE_RECEIVER =
-            "android.media.intent.extra.MESSAGE_RECEIVER";
-
-    /**
-     * Bundle extra: Media item id.
-     * <p>
-     * An opaque unique identifier returned as a result from {@link #ACTION_PLAY} or
-     * {@link #ACTION_ENQUEUE} that represents the media item that was created by the
-     * playback request.
-     * </p><p>
-     * Used with various actions to specify the id of the media item to be controlled.
-     * </p><p>
-     * Included in broadcast intents sent to
-     * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER status update receivers} to identify
-     * the item in question.
-     * </p><p>
-     * The value is a unique string value generated by the media route provider
-     * to represent one particular media item.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     * @see #ACTION_SEEK
-     * @see #ACTION_GET_STATUS
-     */
-    public static final String EXTRA_ITEM_ID =
-            "android.media.intent.extra.ITEM_ID";
-
-    /**
-     * Bundle extra: Media item status.
-     * <p>
-     * Returned as a result from media item actions such as {@link #ACTION_PLAY},
-     * {@link #ACTION_ENQUEUE}, {@link #ACTION_SEEK}, and {@link #ACTION_GET_STATUS}
-     * to describe the status of the specified media item.
-     * </p><p>
-     * Included in broadcast intents sent to
-     * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receivers} to provide
-     * updated status information.
-     * </p><p>
-     * The value is a {@link android.os.Bundle} of data that can be converted into
-     * a {@link MediaItemStatus} object using
-     * {@link MediaItemStatus#fromBundle MediaItemStatus.fromBundle}.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     * @see #ACTION_SEEK
-     * @see #ACTION_GET_STATUS
-     */
-    public static final String EXTRA_ITEM_STATUS =
-            "android.media.intent.extra.ITEM_STATUS";
-
-    /**
-     * Long extra: Media item content position.
-     * <p>
-     * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify the
-     * starting playback position.
-     * </p><p>
-     * Used with {@link #ACTION_SEEK} to set a new playback position.
-     * </p><p>
-     * The value is a long integer number of milliseconds from the beginning of the content.
-     * <p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     * @see #ACTION_SEEK
-     */
-    public static final String EXTRA_ITEM_CONTENT_POSITION =
-            "android.media.intent.extra.ITEM_POSITION";
-
-    /**
-     * Bundle extra: Media item metadata.
-     * <p>
-     * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify metadata
-     * associated with the content of a media item.
-     * </p><p>
-     * The value is a {@link android.os.Bundle} of metadata key-value pairs as defined
-     * in {@link MediaItemMetadata}.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     */
-    public static final String EXTRA_ITEM_METADATA =
-            "android.media.intent.extra.ITEM_METADATA";
-
-    /**
-     * Bundle extra: HTTP request headers.
-     * <p>
-     * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify HTTP request
-     * headers to be included when fetching to the content indicated by the media
-     * item's data Uri.
-     * </p><p>
-     * This extra may be used to provide authentication tokens and other
-     * parameters to the server separately from the media item's data Uri.
-     * </p><p>
-     * The value is a {@link android.os.Bundle} of string based key-value pairs
-     * that describe the HTTP request headers.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     */
-    public static final String EXTRA_ITEM_HTTP_HEADERS =
-            "android.media.intent.extra.HTTP_HEADERS";
-
-    /**
-     * Bundle extra: Media item status update receiver.
-     * <p>
-     * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify
-     * a {@link PendingIntent} for a
-     * broadcast receiver that will receive status updates about a particular
-     * media item.
-     * </p><p>
-     * Whenever the status of the media item changes, the media route provider will
-     * send a broadcast to the pending intent with extras that identify the session
-     * to which the item belongs, the session status, the item's id
-     * and the item's updated status.
-     * </p><p>
-     * The same pending intent and broadcast receiver may be shared by any number of
-     * media items since the broadcast intent includes the media session id
-     * and media item id.
-     * </p><p>
-     * The value is a {@link PendingIntent}.
-     * </p>
-     *
-     * <h3>Broadcast extras</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
-     * the session to which the item in question belongs.
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of the
-     * media item in question.
-     * <li>{@link #EXTRA_ITEM_STATUS} <em>(required)</em>: Specifies the status of the
-     * item as a bundle that can be decoded into a {@link MediaItemStatus} object.
-     * </ul>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     */
-    public static final String EXTRA_ITEM_STATUS_UPDATE_RECEIVER =
-            "android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER";
-
-    /**
-     * Bundle extra: Message.
-     * <p>
-     * Used with {@link #ACTION_SEND_MESSAGE}, and included in broadcast intents sent to
-     * {@link #EXTRA_MESSAGE_RECEIVER message receivers} to describe a message between a
-     * session and a media route provider.
-     * </p><p>
-     * The value is a {@link android.os.Bundle}.
-     * </p>
-     */
-    public static final String EXTRA_MESSAGE = "android.media.intent.extra.MESSAGE";
-
-    /**
-     * Integer extra: Error code.
-     * <p>
-     * Used with all media control requests to describe the cause of an error.
-     * This extra may be omitted when the error is unknown.
-     * </p><p>
-     * The value is one of: {@link #ERROR_UNKNOWN}, {@link #ERROR_UNSUPPORTED_OPERATION},
-     * {@link #ERROR_INVALID_SESSION_ID}, {@link #ERROR_INVALID_ITEM_ID}.
-     * </p>
-     */
-    public static final String EXTRA_ERROR_CODE = "android.media.intent.extra.ERROR_CODE";
-
-    /**
-     * Error code: An unknown error occurred.
-     *
-     * @see #EXTRA_ERROR_CODE
-     */
-    public static final int ERROR_UNKNOWN = 0;
-
-    /**
-     * Error code: The operation is not supported.
-     *
-     * @see #EXTRA_ERROR_CODE
-     */
-    public static final int ERROR_UNSUPPORTED_OPERATION = 1;
-
-    /**
-     * Error code: The session id specified in the request was invalid.
-     *
-     * @see #EXTRA_ERROR_CODE
-     */
-    public static final int ERROR_INVALID_SESSION_ID = 2;
-
-    /**
-     * Error code: The item id specified in the request was invalid.
-     *
-     * @see #EXTRA_ERROR_CODE
-     */
-    public static final int ERROR_INVALID_ITEM_ID = 3;
-
-    private MediaControlIntent() {
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemMetadata.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemMetadata.java
deleted file mode 100644
index d52ddb6..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemMetadata.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.os.Bundle;
-
-/**
- * Constants for specifying metadata about a media item as a {@link Bundle}.
- * <p>
- * This class is part of the remote playback protocol described by the
- * {@link MediaControlIntent MediaControlIntent} class.
- * </p><p>
- * Media item metadata is described as a bundle of key/value pairs as defined
- * in this class.  The documentation specifies the type of value associated
- * with each key.
- * </p><p>
- * An application may specify additional custom metadata keys but there is no guarantee
- * that they will be recognized by the destination.
- * </p>
- */
-public final class MediaItemMetadata {
-    /*
-     * Note: MediaMetadataRetriever also defines a collection of metadata keys that can be
-     * retrieved from a content stream although the representation is somewhat different here
-     * since we are sending the data to a remote endpoint.
-     */
-
-    private MediaItemMetadata() {
-    }
-
-    /**
-     * String key: Album artist name.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
-
-    /**
-     * String key: Album title.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_ALBUM_TITLE = "android.media.metadata.ALBUM_TITLE";
-
-    /**
-     * String key: Artwork Uri.
-     * <p>
-     * The value is a string URI for an image file associated with the media item,
-     * such as album or cover art.
-     * </p>
-     */
-    public static final String KEY_ARTWORK_URI = "android.media.metadata.ARTWORK_URI";
-
-    /**
-     * String key: Artist name.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_ARTIST = "android.media.metadata.ARTIST";
-
-    /**
-     * String key: Author name.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_AUTHOR = "android.media.metadata.AUTHOR";
-
-    /**
-     * String key: Composer name.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_COMPOSER = "android.media.metadata.COMPOSER";
-
-    /**
-     * String key: Track title.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_TITLE = "android.media.metadata.TITLE";
-
-    /**
-     * Integer key: Year of publication.
-     * <p>
-     * The value is an integer year number.
-     * </p>
-     */
-    public static final String KEY_YEAR = "android.media.metadata.YEAR";
-
-    /**
-     * Integer key: Track number (such as a track on a CD).
-     * <p>
-     * The value is a one-based integer track number.
-     * </p>
-     */
-    public static final String KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
-
-    /**
-     * Integer key: Disc number within a collection.
-     * <p>
-     * The value is a one-based integer disc number.
-     * </p>
-     */
-    public static final String KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
-
-    /**
-     * Long key: Item playback duration in milliseconds.
-     * <p>
-     * The value is a <code>long</code> number of milliseconds.
-     * </p><p>
-     * The duration metadata is only a hint to enable a remote media player to
-     * guess the duration of the content before it actually opens the media stream.
-     * The remote media player should still determine the actual content duration from
-     * the media stream itself independent of the value that may be specified by this key.
-     * </p>
-     */
-    public static final String KEY_DURATION = "android.media.metadata.DURATION";
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java
deleted file mode 100644
index 92f608b..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaItemStatus.java
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.app.PendingIntent;
-import android.os.Bundle;
-import android.os.SystemClock;
-
-import androidx.core.util.TimeUtils;
-
-/**
- * Describes the playback status of a media item.
- * <p>
- * This class is part of the remote playback protocol described by the
- * {@link MediaControlIntent MediaControlIntent} class.
- * </p><p>
- * As a media item is played, it transitions through a sequence of states including:
- * {@link #PLAYBACK_STATE_PENDING pending}, {@link #PLAYBACK_STATE_BUFFERING buffering},
- * {@link #PLAYBACK_STATE_PLAYING playing}, {@link #PLAYBACK_STATE_PAUSED paused},
- * {@link #PLAYBACK_STATE_FINISHED finished}, {@link #PLAYBACK_STATE_CANCELED canceled},
- * {@link #PLAYBACK_STATE_INVALIDATED invalidated}, and
- * {@link #PLAYBACK_STATE_ERROR error}.  Refer to the documentation of each state
- * for an explanation of its meaning.
- * </p><p>
- * While the item is playing, the playback status may also include progress information
- * about the {@link #getContentPosition content position} and
- * {@link #getContentDuration content duration} although not all route destinations
- * will report it.
- * </p><p>
- * To monitor playback status, the application should supply a {@link PendingIntent} to use as the
- * {@link MediaControlIntent#EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receiver}
- * for a given {@link MediaControlIntent#ACTION_PLAY playback request}.  Note that
- * the status update receiver will only be invoked for major status changes such as a
- * transition from playing to finished.
- * </p><p class="note">
- * The status update receiver will not be invoked for minor progress updates such as
- * changes to playback position or duration.  If the application wants to monitor
- * playback progress, then it must use the
- * {@link MediaControlIntent#ACTION_GET_STATUS get status request} to poll for changes
- * periodically and estimate the playback position while playing.  Note that there may
- * be a significant power impact to polling so the application is advised only
- * to poll when the screen is on and never more than about once every 5 seconds or so.
- * </p><p>
- * This object is immutable once created using a {@link Builder} instance.
- * </p>
- */
-public final class MediaItemStatus {
-    static final String KEY_TIMESTAMP = "timestamp";
-    static final String KEY_PLAYBACK_STATE = "playbackState";
-    static final String KEY_CONTENT_POSITION = "contentPosition";
-    static final String KEY_CONTENT_DURATION = "contentDuration";
-    static final String KEY_EXTRAS = "extras";
-
-    final Bundle mBundle;
-
-    /**
-     * Playback state: Pending.
-     * <p>
-     * Indicates that the media item has not yet started playback but will be played eventually.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_PENDING = 0;
-
-    /**
-     * Playback state: Playing.
-     * <p>
-     * Indicates that the media item is currently playing.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_PLAYING = 1;
-
-    /**
-     * Playback state: Paused.
-     * <p>
-     * Indicates that playback of the media item has been paused.  Playback can be
-     * resumed using the {@link MediaControlIntent#ACTION_RESUME resume} action.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_PAUSED = 2;
-
-    /**
-     * Playback state: Buffering or seeking to a new position.
-     * <p>
-     * Indicates that the media item has been temporarily interrupted
-     * to fetch more content.  Playback will continue automatically
-     * when enough content has been buffered.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_BUFFERING = 3;
-
-    /**
-     * Playback state: Finished.
-     * <p>
-     * Indicates that the media item played to the end of the content and finished normally.
-     * </p><p>
-     * A finished media item cannot be resumed.  To play the content again, the application
-     * must send a new {@link MediaControlIntent#ACTION_PLAY play} or
-     * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_FINISHED = 4;
-
-    /**
-     * Playback state: Canceled.
-     * <p>
-     * Indicates that the media item was explicitly removed from the queue by the
-     * application.  Items may be canceled and removed from the queue using
-     * the {@link MediaControlIntent#ACTION_REMOVE remove} or
-     * {@link MediaControlIntent#ACTION_STOP stop} action or by issuing
-     * another {@link MediaControlIntent#ACTION_PLAY play} action that has the
-     * side-effect of clearing the queue.
-     * </p><p>
-     * A canceled media item cannot be resumed.  To play the content again, the
-     * application must send a new {@link MediaControlIntent#ACTION_PLAY play} or
-     * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_CANCELED = 5;
-
-    /**
-     * Playback state: Invalidated.
-     * <p>
-     * Indicates that the media item was invalidated permanently and involuntarily.
-     * This state is used to indicate that the media item was invalidated and removed
-     * from the queue because the session to which it belongs was invalidated
-     * (typically by another application taking control of the route).
-     * </p><p>
-     * When invalidation occurs, the application should generally wait for the user
-     * to perform an explicit action, such as clicking on a play button in the UI,
-     * before creating a new media session to avoid unnecessarily interrupting
-     * another application that may have just started using the route.
-     * </p><p>
-     * An invalidated media item cannot be resumed.  To play the content again, the application
-     * must send a new {@link MediaControlIntent#ACTION_PLAY play} or
-     * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_INVALIDATED = 6;
-
-    /**
-     * Playback state: Playback halted or aborted due to an error.
-     * <p>
-     * Examples of errors are no network connectivity when attempting to retrieve content
-     * from a server, or expired user credentials when trying to play subscription-based
-     * content.
-     * </p><p>
-     * A media item in the error state cannot be resumed.  To play the content again,
-     * the application must send a new {@link MediaControlIntent#ACTION_PLAY play} or
-     * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_ERROR = 7;
-
-    /**
-     * Integer extra: HTTP status code.
-     * <p>
-     * Specifies the HTTP status code that was encountered when the content
-     * was requested after all redirects were followed.  This key only needs to
-     * specified when the content uri uses the HTTP or HTTPS scheme and an error
-     * occurred.  This key may be omitted if the content was able to be played
-     * successfully; there is no need to report a 200 (OK) status code.
-     * </p><p>
-     * The value is an integer HTTP status code, such as 401 (Unauthorized),
-     * 404 (Not Found), or 500 (Server Error), or 0 if none.
-     * </p>
-     */
-    public static final String EXTRA_HTTP_STATUS_CODE =
-            "android.media.status.extra.HTTP_STATUS_CODE";
-
-    /**
-     * Bundle extra: HTTP response headers.
-     * <p>
-     * Specifies the HTTP response headers that were returned when the content was
-     * requested from the network.  The headers may include additional information
-     * about the content or any errors conditions that were encountered while
-     * trying to fetch the content.
-     * </p><p>
-     * The value is a {@link android.os.Bundle} of string based key-value pairs
-     * that describe the HTTP response headers.
-     * </p>
-     */
-    public static final String EXTRA_HTTP_RESPONSE_HEADERS =
-            "android.media.status.extra.HTTP_RESPONSE_HEADERS";
-
-    MediaItemStatus(Bundle bundle) {
-        mBundle = bundle;
-    }
-
-    /**
-     * Gets the timestamp associated with the status information in
-     * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
-     *
-     * @return The status timestamp in the {@link SystemClock#elapsedRealtime()} time base.
-     */
-    public long getTimestamp() {
-        return mBundle.getLong(KEY_TIMESTAMP);
-    }
-
-    /**
-     * Gets the playback state of the media item.
-     *
-     * @return The playback state.  One of {@link #PLAYBACK_STATE_PENDING},
-     * {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED},
-     * {@link #PLAYBACK_STATE_BUFFERING}, {@link #PLAYBACK_STATE_FINISHED},
-     * {@link #PLAYBACK_STATE_CANCELED}, {@link #PLAYBACK_STATE_INVALIDATED},
-     * or {@link #PLAYBACK_STATE_ERROR}.
-     */
-    public int getPlaybackState() {
-        return mBundle.getInt(KEY_PLAYBACK_STATE, PLAYBACK_STATE_ERROR);
-    }
-
-    /**
-     * Gets the content playback position as a long integer number of milliseconds
-     * from the beginning of the content.
-     *
-     * @return The content playback position in milliseconds, or -1 if unknown.
-     */
-    public long getContentPosition() {
-        return mBundle.getLong(KEY_CONTENT_POSITION, -1);
-    }
-
-    /**
-     * Gets the total duration of the content to be played as a long integer number of
-     * milliseconds.
-     *
-     * @return The content duration in milliseconds, or -1 if unknown.
-     */
-    public long getContentDuration() {
-        return mBundle.getLong(KEY_CONTENT_DURATION, -1);
-    }
-
-    /**
-     * Gets a bundle of extras for this status object.
-     * The extras will be ignored by the media router but they may be used
-     * by applications.
-     */
-    public Bundle getExtras() {
-        return mBundle.getBundle(KEY_EXTRAS);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("MediaItemStatus{ ");
-        result.append("timestamp=");
-        TimeUtils.formatDuration(SystemClock.elapsedRealtime() - getTimestamp(), result);
-        result.append(" ms ago");
-        result.append(", playbackState=").append(playbackStateToString(getPlaybackState()));
-        result.append(", contentPosition=").append(getContentPosition());
-        result.append(", contentDuration=").append(getContentDuration());
-        result.append(", extras=").append(getExtras());
-        result.append(" }");
-        return result.toString();
-    }
-
-    private static String playbackStateToString(int playbackState) {
-        switch (playbackState) {
-            case PLAYBACK_STATE_PENDING:
-                return "pending";
-            case PLAYBACK_STATE_BUFFERING:
-                return "buffering";
-            case PLAYBACK_STATE_PLAYING:
-                return "playing";
-            case PLAYBACK_STATE_PAUSED:
-                return "paused";
-            case PLAYBACK_STATE_FINISHED:
-                return "finished";
-            case PLAYBACK_STATE_CANCELED:
-                return "canceled";
-            case PLAYBACK_STATE_INVALIDATED:
-                return "invalidated";
-            case PLAYBACK_STATE_ERROR:
-                return "error";
-        }
-        return Integer.toString(playbackState);
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaItemStatus fromBundle(Bundle bundle) {
-        return bundle != null ? new MediaItemStatus(bundle) : null;
-    }
-
-    /**
-     * Builder for {@link MediaItemStatus media item status objects}.
-     */
-    public static final class Builder {
-        private final Bundle mBundle;
-
-        /**
-         * Creates a media item status builder using the current time as the
-         * reference timestamp.
-         *
-         * @param playbackState The item playback state.
-         */
-        public Builder(int playbackState) {
-            mBundle = new Bundle();
-            setTimestamp(SystemClock.elapsedRealtime());
-            setPlaybackState(playbackState);
-        }
-
-        /**
-         * Creates a media item status builder whose initial contents are
-         * copied from an existing status.
-         */
-        public Builder(MediaItemStatus status) {
-            if (status == null) {
-                throw new IllegalArgumentException("status must not be null");
-            }
-
-            mBundle = new Bundle(status.mBundle);
-        }
-
-        /**
-         * Sets the timestamp associated with the status information in
-         * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
-         */
-        public Builder setTimestamp(long elapsedRealtimeTimestamp) {
-            mBundle.putLong(KEY_TIMESTAMP, elapsedRealtimeTimestamp);
-            return this;
-        }
-
-        /**
-         * Sets the playback state of the media item.
-         */
-        public Builder setPlaybackState(int playbackState) {
-            mBundle.putInt(KEY_PLAYBACK_STATE, playbackState);
-            return this;
-        }
-
-        /**
-         * Sets the content playback position as a long integer number of milliseconds
-         * from the beginning of the content.
-         */
-        public Builder setContentPosition(long positionMilliseconds) {
-            mBundle.putLong(KEY_CONTENT_POSITION, positionMilliseconds);
-            return this;
-        }
-
-        /**
-         * Sets the total duration of the content to be played as a long integer number
-         * of milliseconds.
-         */
-        public Builder setContentDuration(long durationMilliseconds) {
-            mBundle.putLong(KEY_CONTENT_DURATION, durationMilliseconds);
-            return this;
-        }
-
-        /**
-         * Sets a bundle of extras for this status object.
-         * The extras will be ignored by the media router but they may be used
-         * by applications.
-         */
-        public Builder setExtras(Bundle extras) {
-            mBundle.putBundle(KEY_EXTRAS, extras);
-            return this;
-        }
-
-        /**
-         * Builds the {@link MediaItemStatus media item status object}.
-         */
-        public MediaItemStatus build() {
-            return new MediaItemStatus(mBundle);
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDescriptor.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDescriptor.java
deleted file mode 100644
index 6bc84fc..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDescriptor.java
+++ /dev/null
@@ -1,693 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.TextUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Describes the properties of a route.
- * <p>
- * Each route is uniquely identified by an opaque id string.  This token
- * may take any form as long as it is unique within the media route provider.
- * </p><p>
- * This object is immutable once created using a {@link Builder} instance.
- * </p>
- */
-public final class MediaRouteDescriptor {
-    static final String KEY_ID = "id";
-    static final String KEY_GROUP_MEMBER_IDS = "groupMemberIds";
-    static final String KEY_NAME = "name";
-    static final String KEY_DESCRIPTION = "status";
-    static final String KEY_ICON_URI = "iconUri";
-    static final String KEY_ENABLED = "enabled";
-    static final String KEY_CONNECTING = "connecting";
-    static final String KEY_CONNECTION_STATE = "connectionState";
-    static final String KEY_CONTROL_FILTERS = "controlFilters";
-    static final String KEY_PLAYBACK_TYPE = "playbackType";
-    static final String KEY_PLAYBACK_STREAM = "playbackStream";
-    static final String KEY_DEVICE_TYPE = "deviceType";
-    static final String KEY_VOLUME = "volume";
-    static final String KEY_VOLUME_MAX = "volumeMax";
-    static final String KEY_VOLUME_HANDLING = "volumeHandling";
-    static final String KEY_PRESENTATION_DISPLAY_ID = "presentationDisplayId";
-    static final String KEY_EXTRAS = "extras";
-    static final String KEY_CAN_DISCONNECT = "canDisconnect";
-    static final String KEY_SETTINGS_INTENT = "settingsIntent";
-    static final String KEY_MIN_CLIENT_VERSION = "minClientVersion";
-    static final String KEY_MAX_CLIENT_VERSION = "maxClientVersion";
-
-    final Bundle mBundle;
-    List<IntentFilter> mControlFilters;
-
-    MediaRouteDescriptor(Bundle bundle, List<IntentFilter> controlFilters) {
-        mBundle = bundle;
-        mControlFilters = controlFilters;
-    }
-
-    /**
-     * Gets the unique id of the route.
-     * <p>
-     * The route id associated with a route descriptor functions as a stable
-     * identifier for the route and must be unique among all routes offered
-     * by the provider.
-     * </p>
-     */
-    public String getId() {
-        return mBundle.getString(KEY_ID);
-    }
-
-    /**
-     * Gets the group member ids of the route.
-     * <p>
-     * A route descriptor that has one or more group member route ids
-     * represents a route group. A member route may belong to another group.
-     * </p>
-     * @hide
-     */
-    // @RestrictTo(LIBRARY_GROUP)
-    public List<String> getGroupMemberIds() {
-        return mBundle.getStringArrayList(KEY_GROUP_MEMBER_IDS);
-    }
-
-    /**
-     * Gets the user-visible name of the route.
-     * <p>
-     * The route name identifies the destination represented by the route.
-     * It may be a user-supplied name, an alias, or device serial number.
-     * </p>
-     */
-    public String getName() {
-        return mBundle.getString(KEY_NAME);
-    }
-
-    /**
-     * Gets the user-visible description of the route.
-     * <p>
-     * The route description describes the kind of destination represented by the route.
-     * It may be a user-supplied string, a model number or brand of device.
-     * </p>
-     */
-    public String getDescription() {
-        return mBundle.getString(KEY_DESCRIPTION);
-    }
-
-    /**
-     * Gets the URI of the icon representing this route.
-     * <p>
-     * This icon will be used in picker UIs if available.
-     * </p>
-     */
-    public Uri getIconUri() {
-        String iconUri = mBundle.getString(KEY_ICON_URI);
-        return iconUri == null ? null : Uri.parse(iconUri);
-    }
-
-    /**
-     * Gets whether the route is enabled.
-     */
-    public boolean isEnabled() {
-        return mBundle.getBoolean(KEY_ENABLED, true);
-    }
-
-    /**
-     * Gets whether the route is connecting.
-     * @deprecated Use {@link #getConnectionState} instead
-     */
-    @Deprecated
-    public boolean isConnecting() {
-        return mBundle.getBoolean(KEY_CONNECTING, false);
-    }
-
-    /**
-     * Gets the connection state of the route.
-     *
-     * @return The connection state of this route:
-     * {@link MediaRouter.RouteInfo#CONNECTION_STATE_DISCONNECTED},
-     * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTING}, or
-     * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTED}.
-     */
-    public int getConnectionState() {
-        return mBundle.getInt(KEY_CONNECTION_STATE,
-                MediaRouter.RouteInfo.CONNECTION_STATE_DISCONNECTED);
-    }
-
-    /**
-     * Gets whether the route can be disconnected without stopping playback.
-     * <p>
-     * The route can normally be disconnected without stopping playback when
-     * the destination device on the route is connected to two or more source
-     * devices. The route provider should update the route immediately when the
-     * number of connected devices changes.
-     * </p><p>
-     * To specify that the route should disconnect without stopping use
-     * {@link MediaRouter#unselect(int)} with
-     * {@link MediaRouter#UNSELECT_REASON_DISCONNECTED}.
-     * </p>
-     */
-    public boolean canDisconnectAndKeepPlaying() {
-        return mBundle.getBoolean(KEY_CAN_DISCONNECT, false);
-    }
-
-    /**
-     * Gets an {@link IntentSender} for starting a settings activity for this
-     * route. The activity may have specific route settings or general settings
-     * for the connected device or route provider.
-     *
-     * @return An {@link IntentSender} to start a settings activity.
-     */
-    public IntentSender getSettingsActivity() {
-        return mBundle.getParcelable(KEY_SETTINGS_INTENT);
-    }
-
-    /**
-     * Gets the route's {@link MediaControlIntent media control intent} filters.
-     */
-    public List<IntentFilter> getControlFilters() {
-        ensureControlFilters();
-        return mControlFilters;
-    }
-
-    void ensureControlFilters() {
-        if (mControlFilters == null) {
-            mControlFilters = mBundle.<IntentFilter>getParcelableArrayList(KEY_CONTROL_FILTERS);
-            if (mControlFilters == null) {
-                mControlFilters = Collections.<IntentFilter>emptyList();
-            }
-        }
-    }
-
-    /**
-     * Gets the type of playback associated with this route.
-     *
-     * @return The type of playback associated with this route:
-     * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_LOCAL} or
-     * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_REMOTE}.
-     */
-    public int getPlaybackType() {
-        return mBundle.getInt(KEY_PLAYBACK_TYPE, MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE);
-    }
-
-    /**
-     * Gets the route's playback stream.
-     */
-    public int getPlaybackStream() {
-        return mBundle.getInt(KEY_PLAYBACK_STREAM, -1);
-    }
-
-    /**
-     * Gets the type of the receiver device associated with this route.
-     *
-     * @return The type of the receiver device associated with this route:
-     * {@link MediaRouter.RouteInfo#DEVICE_TYPE_TV} or
-     * {@link MediaRouter.RouteInfo#DEVICE_TYPE_SPEAKER}.
-     */
-    public int getDeviceType() {
-        return mBundle.getInt(KEY_DEVICE_TYPE);
-    }
-
-    /**
-     * Gets the route's current volume, or 0 if unknown.
-     */
-    public int getVolume() {
-        return mBundle.getInt(KEY_VOLUME);
-    }
-
-    /**
-     * Gets the route's maximum volume, or 0 if unknown.
-     */
-    public int getVolumeMax() {
-        return mBundle.getInt(KEY_VOLUME_MAX);
-    }
-
-    /**
-     * Gets information about how volume is handled on the route.
-     *
-     * @return How volume is handled on the route:
-     * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_FIXED} or
-     * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_VARIABLE}.
-     */
-    public int getVolumeHandling() {
-        return mBundle.getInt(KEY_VOLUME_HANDLING,
-                MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED);
-    }
-
-    /**
-     * Gets the route's presentation display id, or -1 if none.
-     */
-    public int getPresentationDisplayId() {
-        return mBundle.getInt(
-                KEY_PRESENTATION_DISPLAY_ID, MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE);
-    }
-
-    /**
-     * Gets a bundle of extras for this route descriptor.
-     * The extras will be ignored by the media router but they may be used
-     * by applications.
-     */
-    public Bundle getExtras() {
-        return mBundle.getBundle(KEY_EXTRAS);
-    }
-
-    /**
-     * Gets the minimum client version required for this route.
-     * @hide
-     */
-    // @RestrictTo(LIBRARY_GROUP)
-    public int getMinClientVersion() {
-        return mBundle.getInt(KEY_MIN_CLIENT_VERSION,
-                MediaRouteProviderProtocol.CLIENT_VERSION_START);
-    }
-
-    /**
-     * Gets the maximum client version required for this route.
-     * @hide
-     */
-    // @RestrictTo(LIBRARY_GROUP)
-    public int getMaxClientVersion() {
-        return mBundle.getInt(KEY_MAX_CLIENT_VERSION, Integer.MAX_VALUE);
-    }
-
-    /**
-     * Returns true if the route descriptor has all of the required fields.
-     */
-    public boolean isValid() {
-        ensureControlFilters();
-        if (TextUtils.isEmpty(getId())
-                || TextUtils.isEmpty(getName())
-                || mControlFilters.contains(null)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("MediaRouteDescriptor{ ");
-        result.append("id=").append(getId());
-        result.append(", groupMemberIds=").append(getGroupMemberIds());
-        result.append(", name=").append(getName());
-        result.append(", description=").append(getDescription());
-        result.append(", iconUri=").append(getIconUri());
-        result.append(", isEnabled=").append(isEnabled());
-        result.append(", isConnecting=").append(isConnecting());
-        result.append(", connectionState=").append(getConnectionState());
-        result.append(", controlFilters=").append(Arrays.toString(getControlFilters().toArray()));
-        result.append(", playbackType=").append(getPlaybackType());
-        result.append(", playbackStream=").append(getPlaybackStream());
-        result.append(", deviceType=").append(getDeviceType());
-        result.append(", volume=").append(getVolume());
-        result.append(", volumeMax=").append(getVolumeMax());
-        result.append(", volumeHandling=").append(getVolumeHandling());
-        result.append(", presentationDisplayId=").append(getPresentationDisplayId());
-        result.append(", extras=").append(getExtras());
-        result.append(", isValid=").append(isValid());
-        result.append(", minClientVersion=").append(getMinClientVersion());
-        result.append(", maxClientVersion=").append(getMaxClientVersion());
-        result.append(" }");
-        return result.toString();
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaRouteDescriptor fromBundle(Bundle bundle) {
-        return bundle != null ? new MediaRouteDescriptor(bundle, null) : null;
-    }
-
-    /**
-     * Builder for {@link MediaRouteDescriptor media route descriptors}.
-     */
-    public static final class Builder {
-        private final Bundle mBundle;
-        private ArrayList<String> mGroupMemberIds;
-        private ArrayList<IntentFilter> mControlFilters;
-
-        /**
-         * Creates a media route descriptor builder.
-         *
-         * @param id The unique id of the route.
-         * @param name The user-visible name of the route.
-         */
-        public Builder(String id, String name) {
-            mBundle = new Bundle();
-            setId(id);
-            setName(name);
-        }
-
-        /**
-         * Creates a media route descriptor builder whose initial contents are
-         * copied from an existing descriptor.
-         */
-        public Builder(MediaRouteDescriptor descriptor) {
-            if (descriptor == null) {
-                throw new IllegalArgumentException("descriptor must not be null");
-            }
-
-            mBundle = new Bundle(descriptor.mBundle);
-
-            descriptor.ensureControlFilters();
-            if (!descriptor.mControlFilters.isEmpty()) {
-                mControlFilters = new ArrayList<IntentFilter>(descriptor.mControlFilters);
-            }
-        }
-
-        /**
-         * Sets the unique id of the route.
-         * <p>
-         * The route id associated with a route descriptor functions as a stable
-         * identifier for the route and must be unique among all routes offered
-         * by the provider.
-         * </p>
-         */
-        public Builder setId(String id) {
-            mBundle.putString(KEY_ID, id);
-            return this;
-        }
-
-        /**
-         * Adds a group member id of the route.
-         * <p>
-         * A route descriptor that has one or more group member route ids
-         * represents a route group. A member route may belong to another group.
-         * </p>
-         * @hide
-         */
-        // @RestrictTo(LIBRARY_GROUP)
-        public Builder addGroupMemberId(String groupMemberId) {
-            if (TextUtils.isEmpty(groupMemberId)) {
-                throw new IllegalArgumentException("groupMemberId must not be empty");
-            }
-
-            if (mGroupMemberIds == null) {
-                mGroupMemberIds = new ArrayList<>();
-            }
-            if (!mGroupMemberIds.contains(groupMemberId)) {
-                mGroupMemberIds.add(groupMemberId);
-            }
-            return this;
-        }
-
-        /**
-         * Adds a list of group member ids of the route.
-         * <p>
-         * A route descriptor that has one or more group member route ids
-         * represents a route group. A member route may belong to another group.
-         * </p>
-         * @hide
-         */
-        // @RestrictTo(LIBRARY_GROUP)
-        public Builder addGroupMemberIds(Collection<String> groupMemberIds) {
-            if (groupMemberIds == null) {
-                throw new IllegalArgumentException("groupMemberIds must not be null");
-            }
-
-            if (!groupMemberIds.isEmpty()) {
-                for (String groupMemberId : groupMemberIds) {
-                    addGroupMemberId(groupMemberId);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Sets the user-visible name of the route.
-         * <p>
-         * The route name identifies the destination represented by the route.
-         * It may be a user-supplied name, an alias, or device serial number.
-         * </p>
-         */
-        public Builder setName(String name) {
-            mBundle.putString(KEY_NAME, name);
-            return this;
-        }
-
-        /**
-         * Sets the user-visible description of the route.
-         * <p>
-         * The route description describes the kind of destination represented by the route.
-         * It may be a user-supplied string, a model number or brand of device.
-         * </p>
-         */
-        public Builder setDescription(String description) {
-            mBundle.putString(KEY_DESCRIPTION, description);
-            return this;
-        }
-
-        /**
-         * Sets the URI of the icon representing this route.
-         * <p>
-         * This icon will be used in picker UIs if available.
-         * </p><p>
-         * The URI must be one of the following formats:
-         * <ul>
-         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
-         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
-         * </li>
-         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
-         * </ul>
-         * </p>
-         */
-        public Builder setIconUri(Uri iconUri) {
-            if (iconUri == null) {
-                throw new IllegalArgumentException("iconUri must not be null");
-            }
-            mBundle.putString(KEY_ICON_URI, iconUri.toString());
-            return this;
-        }
-
-        /**
-         * Sets whether the route is enabled.
-         * <p>
-         * Disabled routes represent routes that a route provider knows about, such as paired
-         * Wifi Display receivers, but that are not currently available for use.
-         * </p>
-         */
-        public Builder setEnabled(boolean enabled) {
-            mBundle.putBoolean(KEY_ENABLED, enabled);
-            return this;
-        }
-
-        /**
-         * Sets whether the route is in the process of connecting and is not yet
-         * ready for use.
-         * @deprecated Use {@link #setConnectionState} instead.
-         */
-        @Deprecated
-        public Builder setConnecting(boolean connecting) {
-            mBundle.putBoolean(KEY_CONNECTING, connecting);
-            return this;
-        }
-
-        /**
-         * Sets the route's connection state.
-         *
-         * @param connectionState The connection state of the route:
-         * {@link MediaRouter.RouteInfo#CONNECTION_STATE_DISCONNECTED},
-         * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTING}, or
-         * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTED}.
-         */
-        public Builder setConnectionState(int connectionState) {
-            mBundle.putInt(KEY_CONNECTION_STATE, connectionState);
-            return this;
-        }
-
-        /**
-         * Sets whether the route can be disconnected without stopping playback.
-         */
-        public Builder setCanDisconnect(boolean canDisconnect) {
-            mBundle.putBoolean(KEY_CAN_DISCONNECT, canDisconnect);
-            return this;
-        }
-
-        /**
-         * Sets an intent sender for launching the settings activity for this
-         * route.
-         */
-        public Builder setSettingsActivity(IntentSender is) {
-            mBundle.putParcelable(KEY_SETTINGS_INTENT, is);
-            return this;
-        }
-
-        /**
-         * Adds a {@link MediaControlIntent media control intent} filter for the route.
-         */
-        public Builder addControlFilter(IntentFilter filter) {
-            if (filter == null) {
-                throw new IllegalArgumentException("filter must not be null");
-            }
-
-            if (mControlFilters == null) {
-                mControlFilters = new ArrayList<IntentFilter>();
-            }
-            if (!mControlFilters.contains(filter)) {
-                mControlFilters.add(filter);
-            }
-            return this;
-        }
-
-        /**
-         * Adds a list of {@link MediaControlIntent media control intent} filters for the route.
-         */
-        public Builder addControlFilters(Collection<IntentFilter> filters) {
-            if (filters == null) {
-                throw new IllegalArgumentException("filters must not be null");
-            }
-
-            if (!filters.isEmpty()) {
-                for (IntentFilter filter : filters) {
-                    addControlFilter(filter);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Sets the route's playback type.
-         *
-         * @param playbackType The playback type of the route:
-         * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_LOCAL} or
-         * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_REMOTE}.
-         */
-        public Builder setPlaybackType(int playbackType) {
-            mBundle.putInt(KEY_PLAYBACK_TYPE, playbackType);
-            return this;
-        }
-
-        /**
-         * Sets the route's playback stream.
-         */
-        public Builder setPlaybackStream(int playbackStream) {
-            mBundle.putInt(KEY_PLAYBACK_STREAM, playbackStream);
-            return this;
-        }
-
-        /**
-         * Sets the route's receiver device type.
-         *
-         * @param deviceType The receive device type of the route:
-         * {@link MediaRouter.RouteInfo#DEVICE_TYPE_TV} or
-         * {@link MediaRouter.RouteInfo#DEVICE_TYPE_SPEAKER}.
-         */
-        public Builder setDeviceType(int deviceType) {
-            mBundle.putInt(KEY_DEVICE_TYPE, deviceType);
-            return this;
-        }
-
-        /**
-         * Sets the route's current volume, or 0 if unknown.
-         */
-        public Builder setVolume(int volume) {
-            mBundle.putInt(KEY_VOLUME, volume);
-            return this;
-        }
-
-        /**
-         * Sets the route's maximum volume, or 0 if unknown.
-         */
-        public Builder setVolumeMax(int volumeMax) {
-            mBundle.putInt(KEY_VOLUME_MAX, volumeMax);
-            return this;
-        }
-
-        /**
-         * Sets the route's volume handling.
-         *
-         * @param volumeHandling how volume is handled on the route:
-         * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_FIXED} or
-         * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_VARIABLE}.
-         */
-        public Builder setVolumeHandling(int volumeHandling) {
-            mBundle.putInt(KEY_VOLUME_HANDLING, volumeHandling);
-            return this;
-        }
-
-        /**
-         * Sets the route's presentation display id, or -1 if none.
-         */
-        public Builder setPresentationDisplayId(int presentationDisplayId) {
-            mBundle.putInt(KEY_PRESENTATION_DISPLAY_ID, presentationDisplayId);
-            return this;
-        }
-
-        /**
-         * Sets a bundle of extras for this route descriptor.
-         * The extras will be ignored by the media router but they may be used
-         * by applications.
-         */
-        public Builder setExtras(Bundle extras) {
-            mBundle.putBundle(KEY_EXTRAS, extras);
-            return this;
-        }
-
-        /**
-         * Sets the route's minimum client version.
-         * A router whose version is lower than this will not be able to connect to this route.
-         * @hide
-         */
-        // @RestrictTo(LIBRARY_GROUP)
-        public Builder setMinClientVersion(int minVersion) {
-            mBundle.putInt(KEY_MIN_CLIENT_VERSION, minVersion);
-            return this;
-        }
-
-        /**
-         * Sets the route's maximum client version.
-         * A router whose version is higher than this will not be able to connect to this route.
-         * @hide
-         */
-        // @RestrictTo(LIBRARY_GROUP)
-        public Builder setMaxClientVersion(int maxVersion) {
-            mBundle.putInt(KEY_MAX_CLIENT_VERSION, maxVersion);
-            return this;
-        }
-
-        /**
-         * Builds the {@link MediaRouteDescriptor media route descriptor}.
-         */
-        public MediaRouteDescriptor build() {
-            if (mControlFilters != null) {
-                mBundle.putParcelableArrayList(KEY_CONTROL_FILTERS, mControlFilters);
-            }
-            if (mGroupMemberIds != null) {
-                mBundle.putStringArrayList(KEY_GROUP_MEMBER_IDS, mGroupMemberIds);
-            }
-            return new MediaRouteDescriptor(mBundle, mControlFilters);
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDiscoveryRequest.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDiscoveryRequest.java
deleted file mode 100644
index 039627f..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteDiscoveryRequest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.os.Bundle;
-
-/**
- * Describes the kinds of routes that the media router would like to discover
- * and whether to perform active scanning.
- * <p>
- * This object is immutable once created.
- * </p>
- */
-public final class MediaRouteDiscoveryRequest {
-    private static final String KEY_SELECTOR = "selector";
-    private static final String KEY_ACTIVE_SCAN = "activeScan";
-
-    private final Bundle mBundle;
-    private MediaRouteSelector mSelector;
-
-    /**
-     * Creates a media route discovery request.
-     *
-     * @param selector The route selector that specifies the kinds of routes to discover.
-     * @param activeScan True if active scanning should be performed.
-     */
-    public MediaRouteDiscoveryRequest(MediaRouteSelector selector, boolean activeScan) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-
-        mBundle = new Bundle();
-        mSelector = selector;
-        mBundle.putBundle(KEY_SELECTOR, selector.asBundle());
-        mBundle.putBoolean(KEY_ACTIVE_SCAN, activeScan);
-    }
-
-    private MediaRouteDiscoveryRequest(Bundle bundle) {
-        mBundle = bundle;
-    }
-
-    /**
-     * Gets the route selector that specifies the kinds of routes to discover.
-     */
-    public MediaRouteSelector getSelector() {
-        ensureSelector();
-        return mSelector;
-    }
-
-    private void ensureSelector() {
-        if (mSelector == null) {
-            mSelector = MediaRouteSelector.fromBundle(mBundle.getBundle(KEY_SELECTOR));
-            if (mSelector == null) {
-                mSelector = MediaRouteSelector.EMPTY;
-            }
-        }
-    }
-
-    /**
-     * Returns true if active scanning should be performed.
-     *
-     * @see MediaRouter#CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
-     */
-    public boolean isActiveScan() {
-        return mBundle.getBoolean(KEY_ACTIVE_SCAN);
-    }
-
-    /**
-     * Returns true if the discovery request has all of the required fields.
-     */
-    public boolean isValid() {
-        ensureSelector();
-        return mSelector.isValid();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof MediaRouteDiscoveryRequest) {
-            MediaRouteDiscoveryRequest other = (MediaRouteDiscoveryRequest)o;
-            return getSelector().equals(other.getSelector())
-                    && isActiveScan() == other.isActiveScan();
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return getSelector().hashCode() ^ (isActiveScan() ? 1 : 0);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("DiscoveryRequest{ selector=").append(getSelector());
-        result.append(", activeScan=").append(isActiveScan());
-        result.append(", isValid=").append(isValid());
-        result.append(" }");
-        return result.toString();
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaRouteDiscoveryRequest fromBundle(Bundle bundle) {
-        return bundle != null ? new MediaRouteDiscoveryRequest(bundle) : null;
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java
deleted file mode 100644
index 7ea328c..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProvider.java
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Message;
-
-import androidx.core.util.ObjectsCompat;
-
-import com.android.support.mediarouter.media.MediaRouter.ControlRequestCallback;
-
-/**
- * Media route providers are used to publish additional media routes for
- * use within an application.  Media route providers may also be declared
- * as a service to publish additional media routes to all applications
- * in the system.
- * <p>
- * The purpose of a media route provider is to discover media routes that satisfy
- * the criteria specified by the current {@link MediaRouteDiscoveryRequest} and publish a
- * {@link MediaRouteProviderDescriptor} with information about each route by calling
- * {@link #setDescriptor} to notify the currently registered {@link Callback}.
- * </p><p>
- * The provider should watch for changes to the discovery request by implementing
- * {@link #onDiscoveryRequestChanged} and updating the set of routes that it is
- * attempting to discover.  It should also handle route control requests such
- * as volume changes or {@link MediaControlIntent media control intents}
- * by implementing {@link #onCreateRouteController} to return a {@link RouteController}
- * for a particular route.
- * </p><p>
- * A media route provider may be used privately within the scope of a single
- * application process by calling {@link MediaRouter#addProvider MediaRouter.addProvider}
- * to add it to the local {@link MediaRouter}.  A media route provider may also be made
- * available globally to all applications by registering a {@link MediaRouteProviderService}
- * in the provider's manifest.  When the media route provider is registered
- * as a service, all applications that use the media router API will be able to
- * discover and used the provider's routes without having to install anything else.
- * </p><p>
- * This object must only be accessed on the main thread.
- * </p>
- */
-public abstract class MediaRouteProvider {
-    static final int MSG_DELIVER_DESCRIPTOR_CHANGED = 1;
-    static final int MSG_DELIVER_DISCOVERY_REQUEST_CHANGED = 2;
-
-    private final Context mContext;
-    private final ProviderMetadata mMetadata;
-    private final ProviderHandler mHandler = new ProviderHandler();
-
-    private Callback mCallback;
-
-    private MediaRouteDiscoveryRequest mDiscoveryRequest;
-    private boolean mPendingDiscoveryRequestChange;
-
-    private MediaRouteProviderDescriptor mDescriptor;
-    private boolean mPendingDescriptorChange;
-
-    /**
-     * Creates a media route provider.
-     *
-     * @param context The context.
-     */
-    public MediaRouteProvider(@NonNull Context context) {
-        this(context, null);
-    }
-
-    MediaRouteProvider(Context context, ProviderMetadata metadata) {
-        if (context == null) {
-            throw new IllegalArgumentException("context must not be null");
-        }
-
-        mContext = context;
-        if (metadata == null) {
-            mMetadata = new ProviderMetadata(new ComponentName(context, getClass()));
-        } else {
-            mMetadata = metadata;
-        }
-    }
-
-    /**
-     * Gets the context of the media route provider.
-     */
-    public final Context getContext() {
-        return mContext;
-    }
-
-    /**
-     * Gets the provider's handler which is associated with the main thread.
-     */
-    public final Handler getHandler() {
-        return mHandler;
-    }
-
-    /**
-     * Gets some metadata about the provider's implementation.
-     */
-    public final ProviderMetadata getMetadata() {
-        return mMetadata;
-    }
-
-    /**
-     * Sets a callback to invoke when the provider's descriptor changes.
-     *
-     * @param callback The callback to use, or null if none.
-     */
-    public final void setCallback(@Nullable Callback callback) {
-        MediaRouter.checkCallingThread();
-        mCallback = callback;
-    }
-
-    /**
-     * Gets the current discovery request which informs the provider about the
-     * kinds of routes to discover and whether to perform active scanning.
-     *
-     * @return The current discovery request, or null if no discovery is needed at this time.
-     *
-     * @see #onDiscoveryRequestChanged
-     */
-    @Nullable
-    public final MediaRouteDiscoveryRequest getDiscoveryRequest() {
-        return mDiscoveryRequest;
-    }
-
-    /**
-     * Sets a discovery request to inform the provider about the kinds of
-     * routes that its clients would like to discover and whether to perform active scanning.
-     *
-     * @param request The discovery request, or null if no discovery is needed at this time.
-     *
-     * @see #onDiscoveryRequestChanged
-     */
-    public final void setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
-        MediaRouter.checkCallingThread();
-
-        if (ObjectsCompat.equals(mDiscoveryRequest, request)) {
-            return;
-        }
-
-        mDiscoveryRequest = request;
-        if (!mPendingDiscoveryRequestChange) {
-            mPendingDiscoveryRequestChange = true;
-            mHandler.sendEmptyMessage(MSG_DELIVER_DISCOVERY_REQUEST_CHANGED);
-        }
-    }
-
-    void deliverDiscoveryRequestChanged() {
-        mPendingDiscoveryRequestChange = false;
-        onDiscoveryRequestChanged(mDiscoveryRequest);
-    }
-
-    /**
-     * Called by the media router when the {@link MediaRouteDiscoveryRequest discovery request}
-     * has changed.
-     * <p>
-     * Whenever an applications calls {@link MediaRouter#addCallback} to register
-     * a callback, it also provides a selector to specify the kinds of routes that
-     * it is interested in.  The media router combines all of these selectors together
-     * to generate a {@link MediaRouteDiscoveryRequest} and notifies each provider when a change
-     * occurs by calling {@link #setDiscoveryRequest} which posts a message to invoke
-     * this method asynchronously.
-     * </p><p>
-     * The provider should examine the {@link MediaControlIntent media control categories}
-     * in the discovery request's {@link MediaRouteSelector selector} to determine what
-     * kinds of routes it should try to discover and whether it should perform active
-     * or passive scans.  In many cases, the provider may be able to save power by
-     * determining that the selector does not contain any categories that it supports
-     * and it can therefore avoid performing any scans at all.
-     * </p>
-     *
-     * @param request The new discovery request, or null if no discovery is needed at this time.
-     *
-     * @see MediaRouter#addCallback
-     */
-    public void onDiscoveryRequestChanged(@Nullable MediaRouteDiscoveryRequest request) {
-    }
-
-    /**
-     * Gets the provider's descriptor.
-     * <p>
-     * The descriptor describes the state of the media route provider and
-     * the routes that it publishes.  Watch for changes to the descriptor
-     * by registering a {@link Callback callback} with {@link #setCallback}.
-     * </p>
-     *
-     * @return The media route provider descriptor, or null if none.
-     *
-     * @see Callback#onDescriptorChanged
-     */
-    @Nullable
-    public final MediaRouteProviderDescriptor getDescriptor() {
-        return mDescriptor;
-    }
-
-    /**
-     * Sets the provider's descriptor.
-     * <p>
-     * The provider must call this method to notify the currently registered
-     * {@link Callback callback} about the change to the provider's descriptor.
-     * </p>
-     *
-     * @param descriptor The updated route provider descriptor, or null if none.
-     *
-     * @see Callback#onDescriptorChanged
-     */
-    public final void setDescriptor(@Nullable MediaRouteProviderDescriptor descriptor) {
-        MediaRouter.checkCallingThread();
-
-        if (mDescriptor != descriptor) {
-            mDescriptor = descriptor;
-            if (!mPendingDescriptorChange) {
-                mPendingDescriptorChange = true;
-                mHandler.sendEmptyMessage(MSG_DELIVER_DESCRIPTOR_CHANGED);
-            }
-        }
-    }
-
-    void deliverDescriptorChanged() {
-        mPendingDescriptorChange = false;
-
-        if (mCallback != null) {
-            mCallback.onDescriptorChanged(this, mDescriptor);
-        }
-    }
-
-    /**
-     * Called by the media router to obtain a route controller for a particular route.
-     * <p>
-     * The media router will invoke the {@link RouteController#onRelease} method of the route
-     * controller when it is no longer needed to allow it to free its resources.
-     * </p>
-     *
-     * @param routeId The unique id of the route.
-     * @return The route controller.  Returns null if there is no such route or if the route
-     * cannot be controlled using the route controller interface.
-     */
-    @Nullable
-    public RouteController onCreateRouteController(@NonNull String routeId) {
-        if (routeId == null) {
-            throw new IllegalArgumentException("routeId cannot be null");
-        }
-        return null;
-    }
-
-    /**
-     * Called by the media router to obtain a route controller for a particular route which is a
-     * member of {@link MediaRouter.RouteGroup}.
-     * <p>
-     * The media router will invoke the {@link RouteController#onRelease} method of the route
-     * controller when it is no longer needed to allow it to free its resources.
-     * </p>
-     *
-     * @param routeId The unique id of the member route.
-     * @param routeGroupId The unique id of the route group.
-     * @return The route controller.  Returns null if there is no such route or if the route
-     * cannot be controlled using the route controller interface.
-     * @hide
-     */
-    // @RestrictTo(LIBRARY_GROUP)
-    @Nullable
-    public RouteController onCreateRouteController(@NonNull String routeId,
-            @NonNull String routeGroupId) {
-        if (routeId == null) {
-            throw new IllegalArgumentException("routeId cannot be null");
-        }
-        if (routeGroupId == null) {
-            throw new IllegalArgumentException("routeGroupId cannot be null");
-        }
-        return onCreateRouteController(routeId);
-    }
-
-    /**
-     * Describes properties of the route provider's implementation.
-     * <p>
-     * This object is immutable once created.
-     * </p>
-     */
-    public static final class ProviderMetadata {
-        private final ComponentName mComponentName;
-
-        ProviderMetadata(ComponentName componentName) {
-            if (componentName == null) {
-                throw new IllegalArgumentException("componentName must not be null");
-            }
-            mComponentName = componentName;
-        }
-
-        /**
-         * Gets the provider's package name.
-         */
-        public String getPackageName() {
-            return mComponentName.getPackageName();
-        }
-
-        /**
-         * Gets the provider's component name.
-         */
-        public ComponentName getComponentName() {
-            return mComponentName;
-        }
-
-        @Override
-        public String toString() {
-            return "ProviderMetadata{ componentName="
-                    + mComponentName.flattenToShortString() + " }";
-        }
-    }
-
-    /**
-     * Provides control over a particular route.
-     * <p>
-     * The media router obtains a route controller for a route whenever it needs
-     * to control a route.  When a route is selected, the media router invokes
-     * the {@link #onSelect} method of its route controller.  While selected,
-     * the media router may call other methods of the route controller to
-     * request that it perform certain actions to the route.  When a route is
-     * unselected, the media router invokes the {@link #onUnselect} method of its
-     * route controller.  When the media route no longer needs the route controller
-     * it will invoke the {@link #onRelease} method to allow the route controller
-     * to free its resources.
-     * </p><p>
-     * There may be multiple route controllers simultaneously active for the
-     * same route.  Each route controller will be released separately.
-     * </p><p>
-     * All operations on the route controller are asynchronous and
-     * results are communicated via callbacks.
-     * </p>
-     */
-    public static abstract class RouteController {
-        /**
-         * Releases the route controller, allowing it to free its resources.
-         */
-        public void onRelease() {
-        }
-
-        /**
-         * Selects the route.
-         */
-        public void onSelect() {
-        }
-
-        /**
-         * Unselects the route.
-         */
-        public void onUnselect() {
-        }
-
-        /**
-         * Unselects the route and provides a reason. The default implementation
-         * calls {@link #onUnselect()}.
-         * <p>
-         * The reason provided will be one of the following:
-         * <ul>
-         * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li>
-         * </ul>
-         *
-         * @param reason The reason for unselecting the route.
-         */
-        public void onUnselect(int reason) {
-            onUnselect();
-        }
-
-        /**
-         * Requests to set the volume of the route.
-         *
-         * @param volume The new volume value between 0 and {@link MediaRouteDescriptor#getVolumeMax}.
-         */
-        public void onSetVolume(int volume) {
-        }
-
-        /**
-         * Requests an incremental volume update for the route.
-         *
-         * @param delta The delta to add to the current volume.
-         */
-        public void onUpdateVolume(int delta) {
-        }
-
-        /**
-         * Performs a {@link MediaControlIntent media control} request
-         * asynchronously on behalf of the route.
-         *
-         * @param intent A {@link MediaControlIntent media control intent}.
-         * @param callback A {@link ControlRequestCallback} to invoke with the result
-         * of the request, or null if no result is required.
-         * @return True if the controller intends to handle the request and will
-         * invoke the callback when finished.  False if the controller will not
-         * handle the request and will not invoke the callback.
-         *
-         * @see MediaControlIntent
-         */
-        public boolean onControlRequest(Intent intent, @Nullable ControlRequestCallback callback) {
-            return false;
-        }
-    }
-
-    /**
-     * Callback which is invoked when route information becomes available or changes.
-     */
-    public static abstract class Callback {
-        /**
-         * Called when information about a route provider and its routes changes.
-         *
-         * @param provider The media route provider that changed, never null.
-         * @param descriptor The new media route provider descriptor, or null if none.
-         */
-        public void onDescriptorChanged(@NonNull MediaRouteProvider provider,
-                @Nullable MediaRouteProviderDescriptor descriptor) {
-        }
-    }
-
-    private final class ProviderHandler extends Handler {
-        ProviderHandler() {
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_DELIVER_DESCRIPTOR_CHANGED:
-                    deliverDescriptorChanged();
-                    break;
-                case MSG_DELIVER_DISCOVERY_REQUEST_CHANGED:
-                    deliverDiscoveryRequestChanged();
-                    break;
-            }
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderDescriptor.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderDescriptor.java
deleted file mode 100644
index eb1ce09..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderDescriptor.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.os.Bundle;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Describes the state of a media route provider and the routes that it publishes.
- * <p>
- * This object is immutable once created using a {@link Builder} instance.
- * </p>
- */
-public final class MediaRouteProviderDescriptor {
-    private static final String KEY_ROUTES = "routes";
-
-    private final Bundle mBundle;
-    private List<MediaRouteDescriptor> mRoutes;
-
-    private MediaRouteProviderDescriptor(Bundle bundle, List<MediaRouteDescriptor> routes) {
-        mBundle = bundle;
-        mRoutes = routes;
-    }
-
-    /**
-     * Gets the list of all routes that this provider has published.
-     */
-    public List<MediaRouteDescriptor> getRoutes() {
-        ensureRoutes();
-        return mRoutes;
-    }
-
-    private void ensureRoutes() {
-        if (mRoutes == null) {
-            ArrayList<Bundle> routeBundles = mBundle.<Bundle>getParcelableArrayList(KEY_ROUTES);
-            if (routeBundles == null || routeBundles.isEmpty()) {
-                mRoutes = Collections.<MediaRouteDescriptor>emptyList();
-            } else {
-                final int count = routeBundles.size();
-                mRoutes = new ArrayList<MediaRouteDescriptor>(count);
-                for (int i = 0; i < count; i++) {
-                    mRoutes.add(MediaRouteDescriptor.fromBundle(routeBundles.get(i)));
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns true if the route provider descriptor and all of the routes that
-     * it contains have all of the required fields.
-     * <p>
-     * This verification is deep.  If the provider descriptor is known to be
-     * valid then it is not necessary to call {@link #isValid} on each of its routes.
-     * </p>
-     */
-    public boolean isValid() {
-        ensureRoutes();
-        final int routeCount = mRoutes.size();
-        for (int i = 0; i < routeCount; i++) {
-            MediaRouteDescriptor route = mRoutes.get(i);
-            if (route == null || !route.isValid()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("MediaRouteProviderDescriptor{ ");
-        result.append("routes=").append(
-                Arrays.toString(getRoutes().toArray()));
-        result.append(", isValid=").append(isValid());
-        result.append(" }");
-        return result.toString();
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaRouteProviderDescriptor fromBundle(Bundle bundle) {
-        return bundle != null ? new MediaRouteProviderDescriptor(bundle, null) : null;
-    }
-
-    /**
-     * Builder for {@link MediaRouteProviderDescriptor media route provider descriptors}.
-     */
-    public static final class Builder {
-        private final Bundle mBundle;
-        private ArrayList<MediaRouteDescriptor> mRoutes;
-
-        /**
-         * Creates an empty media route provider descriptor builder.
-         */
-        public Builder() {
-            mBundle = new Bundle();
-        }
-
-        /**
-         * Creates a media route provider descriptor builder whose initial contents are
-         * copied from an existing descriptor.
-         */
-        public Builder(MediaRouteProviderDescriptor descriptor) {
-            if (descriptor == null) {
-                throw new IllegalArgumentException("descriptor must not be null");
-            }
-
-            mBundle = new Bundle(descriptor.mBundle);
-
-            descriptor.ensureRoutes();
-            if (!descriptor.mRoutes.isEmpty()) {
-                mRoutes = new ArrayList<MediaRouteDescriptor>(descriptor.mRoutes);
-            }
-        }
-
-        /**
-         * Adds a route.
-         */
-        public Builder addRoute(MediaRouteDescriptor route) {
-            if (route == null) {
-                throw new IllegalArgumentException("route must not be null");
-            }
-
-            if (mRoutes == null) {
-                mRoutes = new ArrayList<MediaRouteDescriptor>();
-            } else if (mRoutes.contains(route)) {
-                throw new IllegalArgumentException("route descriptor already added");
-            }
-            mRoutes.add(route);
-            return this;
-        }
-
-        /**
-         * Adds a list of routes.
-         */
-        public Builder addRoutes(Collection<MediaRouteDescriptor> routes) {
-            if (routes == null) {
-                throw new IllegalArgumentException("routes must not be null");
-            }
-
-            if (!routes.isEmpty()) {
-                for (MediaRouteDescriptor route : routes) {
-                    addRoute(route);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Sets the list of routes.
-         */
-        Builder setRoutes(Collection<MediaRouteDescriptor> routes) {
-            if (routes == null || routes.isEmpty()) {
-                mRoutes = null;
-                mBundle.remove(KEY_ROUTES);
-            } else {
-                mRoutes = new ArrayList<>(routes);
-            }
-            return this;
-        }
-
-        /**
-         * Builds the {@link MediaRouteProviderDescriptor media route provider descriptor}.
-         */
-        public MediaRouteProviderDescriptor build() {
-            if (mRoutes != null) {
-                final int count = mRoutes.size();
-                ArrayList<Bundle> routeBundles = new ArrayList<Bundle>(count);
-                for (int i = 0; i < count; i++) {
-                    routeBundles.add(mRoutes.get(i).asBundle());
-                }
-                mBundle.putParcelableArrayList(KEY_ROUTES, routeBundles);
-            }
-            return new MediaRouteProviderDescriptor(mBundle, mRoutes);
-        }
-    }
-}
\ No newline at end of file
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderProtocol.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderProtocol.java
deleted file mode 100644
index 6be9343..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderProtocol.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.content.Intent;
-import android.os.Messenger;
-
-/**
- * Defines the communication protocol for media route provider services.
- */
-abstract class MediaRouteProviderProtocol {
-    /**
-     * The {@link Intent} that must be declared as handled by the service.
-     * Put this in your manifest.
-     */
-    public static final String SERVICE_INTERFACE =
-            "android.media.MediaRouteProviderService";
-
-    /*
-     * Messages sent from the client to the service.
-     * DO NOT RENUMBER THESE!
-     */
-
-    /** (client v1)
-     * Register client.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : client version
-     */
-    public static final int CLIENT_MSG_REGISTER = 1;
-
-    /** (client v1)
-     * Unregister client.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     */
-    public static final int CLIENT_MSG_UNREGISTER = 2;
-
-    /** (client v1)
-     * Create route controller.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     * - CLIENT_DATA_ROUTE_ID : route id string
-     */
-    public static final int CLIENT_MSG_CREATE_ROUTE_CONTROLLER = 3;
-
-    /** (client v1)
-     * Release route controller.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     */
-    public static final int CLIENT_MSG_RELEASE_ROUTE_CONTROLLER = 4;
-
-    /** (client v1)
-     * Select route.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     */
-    public static final int CLIENT_MSG_SELECT_ROUTE = 5;
-
-    /** (client v1)
-     * Unselect route.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     */
-    public static final int CLIENT_MSG_UNSELECT_ROUTE = 6;
-
-    /** (client v1)
-     * Set route volume.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     * - CLIENT_DATA_VOLUME : volume integer
-     */
-    public static final int CLIENT_MSG_SET_ROUTE_VOLUME = 7;
-
-    /** (client v1)
-     * Update route volume.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     * - CLIENT_DATA_VOLUME : volume delta integer
-     */
-    public static final int CLIENT_MSG_UPDATE_ROUTE_VOLUME = 8;
-
-    /** (client v1)
-     * Route control request.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     * - obj     : media control intent
-     */
-    public static final int CLIENT_MSG_ROUTE_CONTROL_REQUEST = 9;
-
-    /** (client v1)
-     * Sets the discovery request.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - obj     : discovery request bundle, or null if none
-     */
-    public static final int CLIENT_MSG_SET_DISCOVERY_REQUEST = 10;
-
-    public static final String CLIENT_DATA_ROUTE_ID = "routeId";
-    public static final String CLIENT_DATA_ROUTE_LIBRARY_GROUP = "routeGroupId";
-    public static final String CLIENT_DATA_VOLUME = "volume";
-    public static final String CLIENT_DATA_UNSELECT_REASON = "unselectReason";
-
-    /*
-     * Messages sent from the service to the client.
-     * DO NOT RENUMBER THESE!
-     */
-
-    /** (service v1)
-     * Generic failure sent in response to any unrecognized or malformed request.
-     * - arg1    : request id
-     */
-    public static final int SERVICE_MSG_GENERIC_FAILURE = 0;
-
-    /** (service v1)
-     * Generic failure sent in response to a successful message.
-     * - arg1    : request id
-     */
-    public static final int SERVICE_MSG_GENERIC_SUCCESS = 1;
-
-    /** (service v1)
-     * Registration succeeded.
-     * - arg1    : request id
-     * - arg2    : server version
-     * - obj     : route provider descriptor bundle, or null
-     */
-    public static final int SERVICE_MSG_REGISTERED = 2;
-
-    /** (service v1)
-     * Route control request success result.
-     * - arg1    : request id
-     * - obj     : result data bundle, or null
-     */
-    public static final int SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED = 3;
-
-    /** (service v1)
-     * Route control request failure result.
-     * - arg1    : request id
-     * - obj     : result data bundle, or null
-     * - SERVICE_DATA_ERROR: error message
-     */
-    public static final int SERVICE_MSG_CONTROL_REQUEST_FAILED = 4;
-
-    /** (service v1)
-     * Route provider descriptor changed.  (unsolicited event)
-     * - arg1    : reserved (0)
-     * - obj     : route provider descriptor bundle, or null
-     */
-    public static final int SERVICE_MSG_DESCRIPTOR_CHANGED = 5;
-
-    public static final String SERVICE_DATA_ERROR = "error";
-
-    /*
-     * Recognized client version numbers.  (Reserved for future use.)
-     * DO NOT RENUMBER THESE!
-     */
-
-    /**
-     * The client version used from the beginning.
-     */
-    public static final int CLIENT_VERSION_1 = 1;
-
-    /**
-     * The client version used from support library v24.1.0.
-     */
-    public static final int CLIENT_VERSION_2 = 2;
-
-    /**
-     * The current client version.
-     */
-    public static final int CLIENT_VERSION_CURRENT = CLIENT_VERSION_2;
-
-    /*
-     * Recognized server version numbers.  (Reserved for future use.)
-     * DO NOT RENUMBER THESE!
-     */
-
-    /**
-     * The service version used from the beginning.
-     */
-    public static final int SERVICE_VERSION_1 = 1;
-
-    /**
-     * The current service version.
-     */
-    public static final int SERVICE_VERSION_CURRENT = SERVICE_VERSION_1;
-
-    static final int CLIENT_VERSION_START = CLIENT_VERSION_1;
-
-    /**
-     * Returns true if the messenger object is valid.
-     * <p>
-     * The messenger constructor and unparceling code does not check whether the
-     * provided IBinder is a valid IMessenger object.  As a result, it's possible
-     * for a peer to send an invalid IBinder that will result in crashes downstream.
-     * This method checks that the messenger is in a valid state.
-     * </p>
-     */
-    public static boolean isValidRemoteMessenger(Messenger messenger) {
-        try {
-            return messenger != null && messenger.getBinder() != null;
-        } catch (NullPointerException ex) {
-            // If the messenger was constructed with a binder interface other than
-            // IMessenger then the call to getBinder() will crash with an NPE.
-            return false;
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java
deleted file mode 100644
index a186fee..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteProviderService.java
+++ /dev/null
@@ -1,765 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_ID;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_DATA_ROUTE_LIBRARY_GROUP;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_DATA_UNSELECT_REASON;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_VOLUME;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_CREATE_ROUTE_CONTROLLER;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_REGISTER;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_ROUTE_CONTROL_REQUEST;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_SELECT_ROUTE;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_SET_DISCOVERY_REQUEST;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_SET_ROUTE_VOLUME;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_UNREGISTER;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_UNSELECT_ROUTE;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_UPDATE_ROUTE_VOLUME;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_VERSION_1;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_DATA_ERROR;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_CONTROL_REQUEST_FAILED;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_DESCRIPTOR_CHANGED;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_GENERIC_FAILURE;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_GENERIC_SUCCESS;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_REGISTERED;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_VERSION_CURRENT;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .isValidRemoteMessenger;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.core.util.ObjectsCompat;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-
-/**
- * Base class for media route provider services.
- * <p>
- * A media router will bind to media route provider services when a callback is added via
- * {@link MediaRouter#addCallback(MediaRouteSelector, MediaRouter.Callback, int)} with a discovery
- * flag: {@link MediaRouter#CALLBACK_FLAG_REQUEST_DISCOVERY},
- * {@link MediaRouter#CALLBACK_FLAG_FORCE_DISCOVERY}, or
- * {@link MediaRouter#CALLBACK_FLAG_PERFORM_ACTIVE_SCAN}, and will unbind when the callback
- * is removed via {@link MediaRouter#removeCallback(MediaRouter.Callback)}.
- * </p><p>
- * To implement your own media route provider service, extend this class and
- * override the {@link #onCreateMediaRouteProvider} method to return an
- * instance of your {@link MediaRouteProvider}.
- * </p><p>
- * Declare your media route provider service in your application manifest
- * like this:
- * </p>
- * <pre>
- *   &lt;service android:name=".MyMediaRouteProviderService"
- *           android:label="@string/my_media_route_provider_service">
- *       &lt;intent-filter>
- *           &lt;action android:name="android.media.MediaRouteProviderService" />
- *       &lt;/intent-filter>
- *   &lt;/service>
- * </pre>
- */
-public abstract class MediaRouteProviderService extends Service {
-    static final String TAG = "MediaRouteProviderSrv"; // max. 23 chars
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private final ArrayList<ClientRecord> mClients = new ArrayList<ClientRecord>();
-    private final ReceiveHandler mReceiveHandler;
-    private final Messenger mReceiveMessenger;
-    final PrivateHandler mPrivateHandler;
-    private final ProviderCallback mProviderCallback;
-
-    MediaRouteProvider mProvider;
-    private MediaRouteDiscoveryRequest mCompositeDiscoveryRequest;
-
-    /**
-     * The {@link Intent} that must be declared as handled by the service.
-     * Put this in your manifest.
-     */
-    public static final String SERVICE_INTERFACE = MediaRouteProviderProtocol.SERVICE_INTERFACE;
-
-    /*
-     * Private messages used internally.  (Yes, you can renumber these.)
-     */
-
-    static final int PRIVATE_MSG_CLIENT_DIED = 1;
-
-    /**
-     * Creates a media route provider service.
-     */
-    public MediaRouteProviderService() {
-        mReceiveHandler = new ReceiveHandler(this);
-        mReceiveMessenger = new Messenger(mReceiveHandler);
-        mPrivateHandler = new PrivateHandler();
-        mProviderCallback = new ProviderCallback();
-    }
-
-    /**
-     * Called by the system when it is time to create the media route provider.
-     *
-     * @return The media route provider offered by this service, or null if
-     * this service has decided not to offer a media route provider.
-     */
-    public abstract MediaRouteProvider onCreateMediaRouteProvider();
-
-    /**
-     * Gets the media route provider offered by this service.
-     *
-     * @return The media route provider offered by this service, or null if
-     * it has not yet been created.
-     *
-     * @see #onCreateMediaRouteProvider()
-     */
-    public MediaRouteProvider getMediaRouteProvider() {
-        return mProvider;
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        if (intent.getAction().equals(SERVICE_INTERFACE)) {
-            if (mProvider == null) {
-                MediaRouteProvider provider = onCreateMediaRouteProvider();
-                if (provider != null) {
-                    String providerPackage = provider.getMetadata().getPackageName();
-                    if (!providerPackage.equals(getPackageName())) {
-                        throw new IllegalStateException("onCreateMediaRouteProvider() returned "
-                                + "a provider whose package name does not match the package "
-                                + "name of the service.  A media route provider service can "
-                                + "only export its own media route providers.  "
-                                + "Provider package name: " + providerPackage
-                                + ".  Service package name: " + getPackageName() + ".");
-                    }
-                    mProvider = provider;
-                    mProvider.setCallback(mProviderCallback);
-                }
-            }
-            if (mProvider != null) {
-                return mReceiveMessenger.getBinder();
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public boolean onUnbind(Intent intent) {
-        if (mProvider != null) {
-            mProvider.setCallback(null);
-        }
-        return super.onUnbind(intent);
-    }
-
-    boolean onRegisterClient(Messenger messenger, int requestId, int version) {
-        if (version >= CLIENT_VERSION_1) {
-            int index = findClient(messenger);
-            if (index < 0) {
-                ClientRecord client = new ClientRecord(messenger, version);
-                if (client.register()) {
-                    mClients.add(client);
-                    if (DEBUG) {
-                        Log.d(TAG, client + ": Registered, version=" + version);
-                    }
-                    if (requestId != 0) {
-                        MediaRouteProviderDescriptor descriptor = mProvider.getDescriptor();
-                        sendReply(messenger, SERVICE_MSG_REGISTERED,
-                                requestId, SERVICE_VERSION_CURRENT,
-                                createDescriptorBundleForClientVersion(descriptor,
-                                        client.mVersion), null);
-                    }
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    boolean onUnregisterClient(Messenger messenger, int requestId) {
-        int index = findClient(messenger);
-        if (index >= 0) {
-            ClientRecord client = mClients.remove(index);
-            if (DEBUG) {
-                Log.d(TAG, client + ": Unregistered");
-            }
-            client.dispose();
-            sendGenericSuccess(messenger, requestId);
-            return true;
-        }
-        return false;
-    }
-
-    void onBinderDied(Messenger messenger) {
-        int index = findClient(messenger);
-        if (index >= 0) {
-            ClientRecord client = mClients.remove(index);
-            if (DEBUG) {
-                Log.d(TAG, client + ": Binder died");
-            }
-            client.dispose();
-        }
-    }
-
-    boolean onCreateRouteController(Messenger messenger, int requestId,
-            int controllerId, String routeId, String routeGroupId) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            if (client.createRouteController(routeId, routeGroupId, controllerId)) {
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route controller created, controllerId=" + controllerId
-                            + ", routeId=" + routeId + ", routeGroupId=" + routeGroupId);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onReleaseRouteController(Messenger messenger, int requestId,
-            int controllerId) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            if (client.releaseRouteController(controllerId)) {
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route controller released"
-                            + ", controllerId=" + controllerId);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onSelectRoute(Messenger messenger, int requestId,
-            int controllerId) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            MediaRouteProvider.RouteController controller =
-                    client.getRouteController(controllerId);
-            if (controller != null) {
-                controller.onSelect();
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route selected"
-                            + ", controllerId=" + controllerId);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onUnselectRoute(Messenger messenger, int requestId,
-            int controllerId, int reason) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            MediaRouteProvider.RouteController controller =
-                    client.getRouteController(controllerId);
-            if (controller != null) {
-                controller.onUnselect(reason);
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route unselected"
-                            + ", controllerId=" + controllerId);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onSetRouteVolume(Messenger messenger, int requestId,
-            int controllerId, int volume) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            MediaRouteProvider.RouteController controller =
-                    client.getRouteController(controllerId);
-            if (controller != null) {
-                controller.onSetVolume(volume);
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route volume changed"
-                            + ", controllerId=" + controllerId + ", volume=" + volume);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onUpdateRouteVolume(Messenger messenger, int requestId,
-            int controllerId, int delta) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            MediaRouteProvider.RouteController controller =
-                    client.getRouteController(controllerId);
-            if (controller != null) {
-                controller.onUpdateVolume(delta);
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route volume updated"
-                            + ", controllerId=" + controllerId + ", delta=" + delta);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onRouteControlRequest(final Messenger messenger, final int requestId,
-            final int controllerId, final Intent intent) {
-        final ClientRecord client = getClient(messenger);
-        if (client != null) {
-            MediaRouteProvider.RouteController controller =
-                    client.getRouteController(controllerId);
-            if (controller != null) {
-                MediaRouter.ControlRequestCallback callback = null;
-                if (requestId != 0) {
-                    callback = new MediaRouter.ControlRequestCallback() {
-                        @Override
-                        public void onResult(Bundle data) {
-                            if (DEBUG) {
-                                Log.d(TAG, client + ": Route control request succeeded"
-                                        + ", controllerId=" + controllerId
-                                        + ", intent=" + intent
-                                        + ", data=" + data);
-                            }
-                            if (findClient(messenger) >= 0) {
-                                sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED,
-                                        requestId, 0, data, null);
-                            }
-                        }
-
-                        @Override
-                        public void onError(String error, Bundle data) {
-                            if (DEBUG) {
-                                Log.d(TAG, client + ": Route control request failed"
-                                        + ", controllerId=" + controllerId
-                                        + ", intent=" + intent
-                                        + ", error=" + error + ", data=" + data);
-                            }
-                            if (findClient(messenger) >= 0) {
-                                if (error != null) {
-                                    Bundle bundle = new Bundle();
-                                    bundle.putString(SERVICE_DATA_ERROR, error);
-                                    sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED,
-                                            requestId, 0, data, bundle);
-                                } else {
-                                    sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED,
-                                            requestId, 0, data, null);
-                                }
-                            }
-                        }
-                    };
-                }
-                if (controller.onControlRequest(intent, callback)) {
-                    if (DEBUG) {
-                        Log.d(TAG, client + ": Route control request delivered"
-                                + ", controllerId=" + controllerId + ", intent=" + intent);
-                    }
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    boolean onSetDiscoveryRequest(Messenger messenger, int requestId,
-            MediaRouteDiscoveryRequest request) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            boolean actuallyChanged = client.setDiscoveryRequest(request);
-            if (DEBUG) {
-                Log.d(TAG, client + ": Set discovery request, request=" + request
-                        + ", actuallyChanged=" + actuallyChanged
-                        + ", compositeDiscoveryRequest=" + mCompositeDiscoveryRequest);
-            }
-            sendGenericSuccess(messenger, requestId);
-            return true;
-        }
-        return false;
-    }
-
-    void sendDescriptorChanged(MediaRouteProviderDescriptor descriptor) {
-        final int count = mClients.size();
-        for (int i = 0; i < count; i++) {
-            ClientRecord client = mClients.get(i);
-            sendReply(client.mMessenger, SERVICE_MSG_DESCRIPTOR_CHANGED, 0, 0,
-                    createDescriptorBundleForClientVersion(descriptor, client.mVersion), null);
-            if (DEBUG) {
-                Log.d(TAG, client + ": Sent descriptor change event, descriptor=" + descriptor);
-            }
-        }
-    }
-
-    @VisibleForTesting
-    static Bundle createDescriptorBundleForClientVersion(MediaRouteProviderDescriptor descriptor,
-            int clientVersion) {
-        if (descriptor == null) {
-            return null;
-        }
-        MediaRouteProviderDescriptor.Builder builder =
-                new MediaRouteProviderDescriptor.Builder(descriptor);
-        builder.setRoutes(null);
-        for (MediaRouteDescriptor route : descriptor.getRoutes()) {
-            if (clientVersion >= route.getMinClientVersion()
-                    && clientVersion <= route.getMaxClientVersion()) {
-                builder.addRoute(route);
-            }
-        }
-        return builder.build().asBundle();
-    }
-
-    boolean updateCompositeDiscoveryRequest() {
-        MediaRouteDiscoveryRequest composite = null;
-        MediaRouteSelector.Builder selectorBuilder = null;
-        boolean activeScan = false;
-        final int count = mClients.size();
-        for (int i = 0; i < count; i++) {
-            MediaRouteDiscoveryRequest request = mClients.get(i).mDiscoveryRequest;
-            if (request != null
-                    && (!request.getSelector().isEmpty() || request.isActiveScan())) {
-                activeScan |= request.isActiveScan();
-                if (composite == null) {
-                    composite = request;
-                } else {
-                    if (selectorBuilder == null) {
-                        selectorBuilder = new MediaRouteSelector.Builder(composite.getSelector());
-                    }
-                    selectorBuilder.addSelector(request.getSelector());
-                }
-            }
-        }
-        if (selectorBuilder != null) {
-            composite = new MediaRouteDiscoveryRequest(selectorBuilder.build(), activeScan);
-        }
-        if (!ObjectsCompat.equals(mCompositeDiscoveryRequest, composite)) {
-            mCompositeDiscoveryRequest = composite;
-            mProvider.setDiscoveryRequest(composite);
-            return true;
-        }
-        return false;
-    }
-
-    private ClientRecord getClient(Messenger messenger) {
-        int index = findClient(messenger);
-        return index >= 0 ? mClients.get(index) : null;
-    }
-
-    int findClient(Messenger messenger) {
-        final int count = mClients.size();
-        for (int i = 0; i < count; i++) {
-            ClientRecord client = mClients.get(i);
-            if (client.hasMessenger(messenger)) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    static void sendGenericFailure(Messenger messenger, int requestId) {
-        if (requestId != 0) {
-            sendReply(messenger, SERVICE_MSG_GENERIC_FAILURE, requestId, 0, null, null);
-        }
-    }
-
-    private static void sendGenericSuccess(Messenger messenger, int requestId) {
-        if (requestId != 0) {
-            sendReply(messenger, SERVICE_MSG_GENERIC_SUCCESS, requestId, 0, null, null);
-        }
-    }
-
-    static void sendReply(Messenger messenger, int what,
-            int requestId, int arg, Object obj, Bundle data) {
-        Message msg = Message.obtain();
-        msg.what = what;
-        msg.arg1 = requestId;
-        msg.arg2 = arg;
-        msg.obj = obj;
-        msg.setData(data);
-        try {
-            messenger.send(msg);
-        } catch (DeadObjectException ex) {
-            // The client died.
-        } catch (RemoteException ex) {
-            Log.e(TAG, "Could not send message to " + getClientId(messenger), ex);
-        }
-    }
-
-    static String getClientId(Messenger messenger) {
-        return "Client connection " + messenger.getBinder().toString();
-    }
-
-    private final class PrivateHandler extends Handler {
-        PrivateHandler() {
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case PRIVATE_MSG_CLIENT_DIED:
-                    onBinderDied((Messenger)msg.obj);
-                    break;
-            }
-        }
-    }
-
-    private final class ProviderCallback extends MediaRouteProvider.Callback {
-        ProviderCallback() {
-        }
-
-        @Override
-        public void onDescriptorChanged(MediaRouteProvider provider,
-                MediaRouteProviderDescriptor descriptor) {
-            sendDescriptorChanged(descriptor);
-        }
-    }
-
-    private final class ClientRecord implements DeathRecipient {
-        public final Messenger mMessenger;
-        public final int mVersion;
-        public MediaRouteDiscoveryRequest mDiscoveryRequest;
-
-        private final SparseArray<MediaRouteProvider.RouteController> mControllers =
-                new SparseArray<MediaRouteProvider.RouteController>();
-
-        public ClientRecord(Messenger messenger, int version) {
-            mMessenger = messenger;
-            mVersion = version;
-        }
-
-        public boolean register() {
-            try {
-                mMessenger.getBinder().linkToDeath(this, 0);
-                return true;
-            } catch (RemoteException ex) {
-                binderDied();
-            }
-            return false;
-        }
-
-        public void dispose() {
-            int count = mControllers.size();
-            for (int i = 0; i < count; i++) {
-                mControllers.valueAt(i).onRelease();
-            }
-            mControllers.clear();
-
-            mMessenger.getBinder().unlinkToDeath(this, 0);
-
-            setDiscoveryRequest(null);
-        }
-
-        public boolean hasMessenger(Messenger other) {
-            return mMessenger.getBinder() == other.getBinder();
-        }
-
-        public boolean createRouteController(String routeId, String routeGroupId,
-                int controllerId) {
-            if (mControllers.indexOfKey(controllerId) < 0) {
-                MediaRouteProvider.RouteController controller = routeGroupId == null
-                        ? mProvider.onCreateRouteController(routeId)
-                        : mProvider.onCreateRouteController(routeId, routeGroupId);
-                if (controller != null) {
-                    mControllers.put(controllerId, controller);
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public boolean releaseRouteController(int controllerId) {
-            MediaRouteProvider.RouteController controller = mControllers.get(controllerId);
-            if (controller != null) {
-                mControllers.remove(controllerId);
-                controller.onRelease();
-                return true;
-            }
-            return false;
-        }
-
-        public MediaRouteProvider.RouteController getRouteController(int controllerId) {
-            return mControllers.get(controllerId);
-        }
-
-        public boolean setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
-            if (!ObjectsCompat.equals(mDiscoveryRequest, request)) {
-                mDiscoveryRequest = request;
-                return updateCompositeDiscoveryRequest();
-            }
-            return false;
-        }
-
-        // Runs on a binder thread.
-        @Override
-        public void binderDied() {
-            mPrivateHandler.obtainMessage(PRIVATE_MSG_CLIENT_DIED, mMessenger).sendToTarget();
-        }
-
-        @Override
-        public String toString() {
-            return getClientId(mMessenger);
-        }
-    }
-
-    /**
-     * Handler that receives messages from clients.
-     * <p>
-     * This inner class is static and only retains a weak reference to the service
-     * to prevent the service from being leaked in case one of the clients is holding an
-     * active reference to the server's messenger.
-     * </p><p>
-     * This handler should not be used to handle any messages other than those
-     * that come from the client.
-     * </p>
-     */
-    private static final class ReceiveHandler extends Handler {
-        private final WeakReference<MediaRouteProviderService> mServiceRef;
-
-        public ReceiveHandler(MediaRouteProviderService service) {
-            mServiceRef = new WeakReference<MediaRouteProviderService>(service);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            final Messenger messenger = msg.replyTo;
-            if (isValidRemoteMessenger(messenger)) {
-                final int what = msg.what;
-                final int requestId = msg.arg1;
-                final int arg = msg.arg2;
-                final Object obj = msg.obj;
-                final Bundle data = msg.peekData();
-                if (!processMessage(what, messenger, requestId, arg, obj, data)) {
-                    if (DEBUG) {
-                        Log.d(TAG, getClientId(messenger) + ": Message failed, what=" + what
-                                + ", requestId=" + requestId + ", arg=" + arg
-                                + ", obj=" + obj + ", data=" + data);
-                    }
-                    sendGenericFailure(messenger, requestId);
-                }
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, "Ignoring message without valid reply messenger.");
-                }
-            }
-        }
-
-        private boolean processMessage(int what,
-                Messenger messenger, int requestId, int arg, Object obj, Bundle data) {
-            MediaRouteProviderService service = mServiceRef.get();
-            if (service != null) {
-                switch (what) {
-                    case CLIENT_MSG_REGISTER:
-                        return service.onRegisterClient(messenger, requestId, arg);
-
-                    case CLIENT_MSG_UNREGISTER:
-                        return service.onUnregisterClient(messenger, requestId);
-
-                    case CLIENT_MSG_CREATE_ROUTE_CONTROLLER: {
-                        String routeId = data.getString(CLIENT_DATA_ROUTE_ID);
-                        String routeGroupId = data.getString(CLIENT_DATA_ROUTE_LIBRARY_GROUP);
-                        if (routeId != null) {
-                            return service.onCreateRouteController(
-                                    messenger, requestId, arg, routeId, routeGroupId);
-                        }
-                        break;
-                    }
-
-                    case CLIENT_MSG_RELEASE_ROUTE_CONTROLLER:
-                        return service.onReleaseRouteController(messenger, requestId, arg);
-
-                    case CLIENT_MSG_SELECT_ROUTE:
-                        return service.onSelectRoute(messenger, requestId, arg);
-
-                    case CLIENT_MSG_UNSELECT_ROUTE:
-                        int reason = data == null ?
-                                MediaRouter.UNSELECT_REASON_UNKNOWN
-                                : data.getInt(CLIENT_DATA_UNSELECT_REASON,
-                                        MediaRouter.UNSELECT_REASON_UNKNOWN);
-                        return service.onUnselectRoute(messenger, requestId, arg, reason);
-
-                    case CLIENT_MSG_SET_ROUTE_VOLUME: {
-                        int volume = data.getInt(CLIENT_DATA_VOLUME, -1);
-                        if (volume >= 0) {
-                            return service.onSetRouteVolume(
-                                    messenger, requestId, arg, volume);
-                        }
-                        break;
-                    }
-
-                    case CLIENT_MSG_UPDATE_ROUTE_VOLUME: {
-                        int delta = data.getInt(CLIENT_DATA_VOLUME, 0);
-                        if (delta != 0) {
-                            return service.onUpdateRouteVolume(
-                                    messenger, requestId, arg, delta);
-                        }
-                        break;
-                    }
-
-                    case CLIENT_MSG_ROUTE_CONTROL_REQUEST:
-                        if (obj instanceof Intent) {
-                            return service.onRouteControlRequest(
-                                    messenger, requestId, arg, (Intent)obj);
-                        }
-                        break;
-
-                    case CLIENT_MSG_SET_DISCOVERY_REQUEST: {
-                        if (obj == null || obj instanceof Bundle) {
-                            MediaRouteDiscoveryRequest request =
-                                    MediaRouteDiscoveryRequest.fromBundle((Bundle)obj);
-                            return service.onSetDiscoveryRequest(
-                                    messenger, requestId,
-                                    request != null && request.isValid() ? request : null);
-                        }
-                    }
-                }
-            }
-            return false;
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java
deleted file mode 100644
index f20dcc0..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouteSelector.java
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.content.IntentFilter;
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Describes the capabilities of routes that applications would like to discover and use.
- * <p>
- * This object is immutable once created using a {@link Builder} instance.
- * </p>
- *
- * <h3>Example</h3>
- * <pre>
- * MediaRouteSelector selectorBuilder = new MediaRouteSelector.Builder()
- *         .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
- *         .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
- *         .build();
- *
- * MediaRouter router = MediaRouter.getInstance(context);
- * router.addCallback(selector, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
- * </pre>
- */
-public final class MediaRouteSelector {
-    static final String KEY_CONTROL_CATEGORIES = "controlCategories";
-
-    private final Bundle mBundle;
-    List<String> mControlCategories;
-
-    /**
-     * An empty media route selector that will not match any routes.
-     */
-    public static final MediaRouteSelector EMPTY = new MediaRouteSelector(new Bundle(), null);
-
-    MediaRouteSelector(Bundle bundle, List<String> controlCategories) {
-        mBundle = bundle;
-        mControlCategories = controlCategories;
-    }
-
-    /**
-     * Gets the list of {@link MediaControlIntent media control categories} in the selector.
-     *
-     * @return The list of categories.
-     */
-    public List<String> getControlCategories() {
-        ensureControlCategories();
-        return mControlCategories;
-    }
-
-    void ensureControlCategories() {
-        if (mControlCategories == null) {
-            mControlCategories = mBundle.getStringArrayList(KEY_CONTROL_CATEGORIES);
-            if (mControlCategories == null || mControlCategories.isEmpty()) {
-                mControlCategories = Collections.<String>emptyList();
-            }
-        }
-    }
-
-    /**
-     * Returns true if the selector contains the specified category.
-     *
-     * @param category The category to check.
-     * @return True if the category is present.
-     */
-    public boolean hasControlCategory(String category) {
-        if (category != null) {
-            ensureControlCategories();
-            final int categoryCount = mControlCategories.size();
-            for (int i = 0; i < categoryCount; i++) {
-                if (mControlCategories.get(i).equals(category)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the selector matches at least one of the specified control filters.
-     *
-     * @param filters The list of control filters to consider.
-     * @return True if a match is found.
-     */
-    public boolean matchesControlFilters(List<IntentFilter> filters) {
-        if (filters != null) {
-            ensureControlCategories();
-            final int categoryCount = mControlCategories.size();
-            if (categoryCount != 0) {
-                final int filterCount = filters.size();
-                for (int i = 0; i < filterCount; i++) {
-                    final IntentFilter filter = filters.get(i);
-                    if (filter != null) {
-                        for (int j = 0; j < categoryCount; j++) {
-                            if (filter.hasCategory(mControlCategories.get(j))) {
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if this selector contains all of the capabilities described
-     * by the specified selector.
-     *
-     * @param selector The selector to be examined.
-     * @return True if this selector contains all of the capabilities described
-     * by the specified selector.
-     */
-    public boolean contains(MediaRouteSelector selector) {
-        if (selector != null) {
-            ensureControlCategories();
-            selector.ensureControlCategories();
-            return mControlCategories.containsAll(selector.mControlCategories);
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the selector does not specify any capabilities.
-     */
-    public boolean isEmpty() {
-        ensureControlCategories();
-        return mControlCategories.isEmpty();
-    }
-
-    /**
-     * Returns true if the selector has all of the required fields.
-     */
-    public boolean isValid() {
-        ensureControlCategories();
-        if (mControlCategories.contains(null)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof MediaRouteSelector) {
-            MediaRouteSelector other = (MediaRouteSelector)o;
-            ensureControlCategories();
-            other.ensureControlCategories();
-            return mControlCategories.equals(other.mControlCategories);
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        ensureControlCategories();
-        return mControlCategories.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("MediaRouteSelector{ ");
-        result.append("controlCategories=").append(
-                Arrays.toString(getControlCategories().toArray()));
-        result.append(" }");
-        return result.toString();
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaRouteSelector fromBundle(@Nullable Bundle bundle) {
-        return bundle != null ? new MediaRouteSelector(bundle, null) : null;
-    }
-
-    /**
-     * Builder for {@link MediaRouteSelector media route selectors}.
-     */
-    public static final class Builder {
-        private ArrayList<String> mControlCategories;
-
-        /**
-         * Creates an empty media route selector builder.
-         */
-        public Builder() {
-        }
-
-        /**
-         * Creates a media route selector descriptor builder whose initial contents are
-         * copied from an existing selector.
-         */
-        public Builder(@NonNull MediaRouteSelector selector) {
-            if (selector == null) {
-                throw new IllegalArgumentException("selector must not be null");
-            }
-
-            selector.ensureControlCategories();
-            if (!selector.mControlCategories.isEmpty()) {
-                mControlCategories = new ArrayList<String>(selector.mControlCategories);
-            }
-        }
-
-        /**
-         * Adds a {@link MediaControlIntent media control category} to the builder.
-         *
-         * @param category The category to add to the set of desired capabilities, such as
-         * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}.
-         * @return The builder instance for chaining.
-         */
-        @NonNull
-        public Builder addControlCategory(@NonNull String category) {
-            if (category == null) {
-                throw new IllegalArgumentException("category must not be null");
-            }
-
-            if (mControlCategories == null) {
-                mControlCategories = new ArrayList<String>();
-            }
-            if (!mControlCategories.contains(category)) {
-                mControlCategories.add(category);
-            }
-            return this;
-        }
-
-        /**
-         * Adds a list of {@link MediaControlIntent media control categories} to the builder.
-         *
-         * @param categories The list categories to add to the set of desired capabilities,
-         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}.
-         * @return The builder instance for chaining.
-         */
-        @NonNull
-        public Builder addControlCategories(@NonNull Collection<String> categories) {
-            if (categories == null) {
-                throw new IllegalArgumentException("categories must not be null");
-            }
-
-            if (!categories.isEmpty()) {
-                for (String category : categories) {
-                    addControlCategory(category);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Adds the contents of an existing media route selector to the builder.
-         *
-         * @param selector The media route selector whose contents are to be added.
-         * @return The builder instance for chaining.
-         */
-        @NonNull
-        public Builder addSelector(@NonNull MediaRouteSelector selector) {
-            if (selector == null) {
-                throw new IllegalArgumentException("selector must not be null");
-            }
-
-            addControlCategories(selector.getControlCategories());
-            return this;
-        }
-
-        /**
-         * Builds the {@link MediaRouteSelector media route selector}.
-         */
-        @NonNull
-        public MediaRouteSelector build() {
-            if (mControlCategories == null) {
-                return EMPTY;
-            }
-            Bundle bundle = new Bundle();
-            bundle.putStringArrayList(KEY_CONTROL_CATEGORIES, mControlCategories);
-            return new MediaRouteSelector(bundle, mControlCategories);
-        }
-    }
-}
\ No newline at end of file
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java
deleted file mode 100644
index 4b56b19..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaRouter.java
+++ /dev/null
@@ -1,3000 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.support.v4.media.session.MediaSessionCompat;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.Display;
-
-import androidx.core.app.ActivityManagerCompat;
-import androidx.core.hardware.display.DisplayManagerCompat;
-import androidx.core.util.Pair;
-import androidx.media.VolumeProviderCompat;
-
-import com.android.support.mediarouter.media.MediaRouteProvider.ProviderMetadata;
-import com.android.support.mediarouter.media.MediaRouteProvider.RouteController;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * MediaRouter allows applications to control the routing of media channels
- * and streams from the current device to external speakers and destination devices.
- * <p>
- * A MediaRouter instance is retrieved through {@link #getInstance}.  Applications
- * can query the media router about the currently selected route and its capabilities
- * to determine how to send content to the route's destination.  Applications can
- * also {@link RouteInfo#sendControlRequest send control requests} to the route
- * to ask the route's destination to perform certain remote control functions
- * such as playing media.
- * </p><p>
- * See also {@link MediaRouteProvider} for information on how an application
- * can publish new media routes to the media router.
- * </p><p>
- * The media router API is not thread-safe; all interactions with it must be
- * done from the main thread of the process.
- * </p>
- */
-public final class MediaRouter {
-    static final String TAG = "MediaRouter";
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    /**
-     * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the reason the route
-     * was unselected is unknown.
-     */
-    public static final int UNSELECT_REASON_UNKNOWN = 0;
-    /**
-     * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed
-     * the disconnect button to disconnect and keep playing.
-     * <p>
-     *
-     * @see MediaRouteDescriptor#canDisconnectAndKeepPlaying()
-     */
-    public static final int UNSELECT_REASON_DISCONNECTED = 1;
-    /**
-     * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed
-     * the stop casting button.
-     */
-    public static final int UNSELECT_REASON_STOPPED = 2;
-    /**
-     * Passed to {@link androidx.mediarouter.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user selected
-     * a different route.
-     */
-    public static final int UNSELECT_REASON_ROUTE_CHANGED = 3;
-
-    // Maintains global media router state for the process.
-    // This field is initialized in MediaRouter.getInstance() before any
-    // MediaRouter objects are instantiated so it is guaranteed to be
-    // valid whenever any instance method is invoked.
-    static GlobalMediaRouter sGlobal;
-
-    // Context-bound state of the media router.
-    final Context mContext;
-    final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<CallbackRecord>();
-
-    @IntDef(flag = true,
-            value = {
-                    CALLBACK_FLAG_PERFORM_ACTIVE_SCAN,
-                    CALLBACK_FLAG_REQUEST_DISCOVERY,
-                    CALLBACK_FLAG_UNFILTERED_EVENTS
-            }
-    )
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface CallbackFlags {}
-
-    /**
-     * Flag for {@link #addCallback}: Actively scan for routes while this callback
-     * is registered.
-     * <p>
-     * When this flag is specified, the media router will actively scan for new
-     * routes.  Certain routes, such as wifi display routes, may not be discoverable
-     * except when actively scanning.  This flag is typically used when the route picker
-     * dialog has been opened by the user to ensure that the route information is
-     * up to date.
-     * </p><p>
-     * Active scanning may consume a significant amount of power and may have intrusive
-     * effects on wireless connectivity.  Therefore it is important that active scanning
-     * only be requested when it is actually needed to satisfy a user request to
-     * discover and select a new route.
-     * </p><p>
-     * This flag implies {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} but performing
-     * active scans is much more expensive than a normal discovery request.
-     * </p>
-     *
-     * @see #CALLBACK_FLAG_REQUEST_DISCOVERY
-     */
-    public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0;
-
-    /**
-     * Flag for {@link #addCallback}: Do not filter route events.
-     * <p>
-     * When this flag is specified, the callback will be invoked for events that affect any
-     * route even if they do not match the callback's filter.
-     * </p>
-     */
-    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
-
-    /**
-     * Flag for {@link #addCallback}: Request passive route discovery while this
-     * callback is registered, except on {@link ActivityManager#isLowRamDevice low-RAM devices}.
-     * <p>
-     * When this flag is specified, the media router will try to discover routes.
-     * Although route discovery is intended to be efficient, checking for new routes may
-     * result in some network activity and could slowly drain the battery.  Therefore
-     * applications should only specify {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} when
-     * they are running in the foreground and would like to provide the user with the
-     * option of connecting to new routes.
-     * </p><p>
-     * Applications should typically add a callback using this flag in the
-     * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart}
-     * method and remove it in the {@link android.app.Activity#onStop onStop} method.
-     * The {@link androidx.mediarouter.app.MediaRouteDiscoveryFragment} fragment may
-     * also be used for this purpose.
-     * </p><p class="note">
-     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag
-     * will be ignored.  Refer to
-     * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details.
-     * </p>
-     *
-     * @see androidx.mediarouter.app.MediaRouteDiscoveryFragment
-     */
-    public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
-
-    /**
-     * Flag for {@link #addCallback}: Request passive route discovery while this
-     * callback is registered, even on {@link ActivityManager#isLowRamDevice low-RAM devices}.
-     * <p class="note">
-     * This flag has a significant performance impact on low-RAM devices
-     * since it may cause many media route providers to be started simultaneously.
-     * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid
-     * performing passive discovery on these devices altogether.  Refer to
-     * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details.
-     * </p>
-     *
-     * @see androidx.mediarouter.app.MediaRouteDiscoveryFragment
-     */
-    public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 1 << 3;
-
-    /**
-     * Flag for {@link #isRouteAvailable}: Ignore the default route.
-     * <p>
-     * This flag is used to determine whether a matching non-default route is available.
-     * This constraint may be used to decide whether to offer the route chooser dialog
-     * to the user.  There is no point offering the chooser if there are no
-     * non-default choices.
-     * </p>
-     */
-    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
-
-    /**
-     * Flag for {@link #isRouteAvailable}: Require an actual route to be matched.
-     * <p>
-     * If this flag is not set, then {@link #isRouteAvailable} will return true
-     * if it is possible to discover a matching route even if discovery is not in
-     * progress or if no matching route has yet been found.  This feature is used to
-     * save resources by removing the need to perform passive route discovery on
-     * {@link ActivityManager#isLowRamDevice low-RAM devices}.
-     * </p><p>
-     * If this flag is set, then {@link #isRouteAvailable} will only return true if
-     * a matching route has actually been discovered.
-     * </p>
-     */
-    public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 1 << 1;
-
-    private MediaRouter(Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Gets an instance of the media router service associated with the context.
-     * <p>
-     * The application is responsible for holding a strong reference to the returned
-     * {@link MediaRouter} instance, such as by storing the instance in a field of
-     * the {@link android.app.Activity}, to ensure that the media router remains alive
-     * as long as the application is using its features.
-     * </p><p>
-     * In other words, the support library only holds a {@link WeakReference weak reference}
-     * to each media router instance.  When there are no remaining strong references to the
-     * media router instance, all of its callbacks will be removed and route discovery
-     * will no longer be performed on its behalf.
-     * </p>
-     *
-     * @return The media router instance for the context.  The application must hold
-     * a strong reference to this object as long as it is in use.
-     */
-    public static MediaRouter getInstance(@NonNull Context context) {
-        if (context == null) {
-            throw new IllegalArgumentException("context must not be null");
-        }
-        checkCallingThread();
-
-        if (sGlobal == null) {
-            sGlobal = new GlobalMediaRouter(context.getApplicationContext());
-            sGlobal.start();
-        }
-        return sGlobal.getRouter(context);
-    }
-
-    /**
-     * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to
-     * this media router.
-     */
-    public List<RouteInfo> getRoutes() {
-        checkCallingThread();
-        return sGlobal.getRoutes();
-    }
-
-    /**
-     * Gets information about the {@link MediaRouter.ProviderInfo route providers}
-     * currently known to this media router.
-     */
-    public List<ProviderInfo> getProviders() {
-        checkCallingThread();
-        return sGlobal.getProviders();
-    }
-
-    /**
-     * Gets the default route for playing media content on the system.
-     * <p>
-     * The system always provides a default route.
-     * </p>
-     *
-     * @return The default route, which is guaranteed to never be null.
-     */
-    @NonNull
-    public RouteInfo getDefaultRoute() {
-        checkCallingThread();
-        return sGlobal.getDefaultRoute();
-    }
-
-    /**
-     * Gets a bluetooth route for playing media content on the system.
-     *
-     * @return A bluetooth route, if exist, otherwise null.
-     */
-    public RouteInfo getBluetoothRoute() {
-        checkCallingThread();
-        return sGlobal.getBluetoothRoute();
-    }
-
-    /**
-     * Gets the currently selected route.
-     * <p>
-     * The application should examine the route's
-     * {@link RouteInfo#getControlFilters media control intent filters} to assess the
-     * capabilities of the route before attempting to use it.
-     * </p>
-     *
-     * <h3>Example</h3>
-     * <pre>
-     * public boolean playMovie() {
-     *     MediaRouter mediaRouter = MediaRouter.getInstance(context);
-     *     MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
-     *
-     *     // First try using the remote playback interface, if supported.
-     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
-     *         // The route supports remote playback.
-     *         // Try to send it the Uri of the movie to play.
-     *         Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
-     *         intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
-     *         intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4");
-     *         if (route.supportsControlRequest(intent)) {
-     *             route.sendControlRequest(intent, null);
-     *             return true; // sent the request to play the movie
-     *         }
-     *     }
-     *
-     *     // If remote playback was not possible, then play locally.
-     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
-     *         // The route supports live video streaming.
-     *         // Prepare to play content locally in a window or in a presentation.
-     *         return playMovieInWindow();
-     *     }
-     *
-     *     // Neither interface is supported, so we can't play the movie to this route.
-     *     return false;
-     * }
-     * </pre>
-     *
-     * @return The selected route, which is guaranteed to never be null.
-     *
-     * @see RouteInfo#getControlFilters
-     * @see RouteInfo#supportsControlCategory
-     * @see RouteInfo#supportsControlRequest
-     */
-    @NonNull
-    public RouteInfo getSelectedRoute() {
-        checkCallingThread();
-        return sGlobal.getSelectedRoute();
-    }
-
-    /**
-     * Returns the selected route if it matches the specified selector, otherwise
-     * selects the default route and returns it. If there is one live audio route
-     * (usually Bluetooth A2DP), it will be selected instead of default route.
-     *
-     * @param selector The selector to match.
-     * @return The previously selected route if it matched the selector, otherwise the
-     * newly selected default route which is guaranteed to never be null.
-     *
-     * @see MediaRouteSelector
-     * @see RouteInfo#matchesSelector
-     */
-    @NonNull
-    public RouteInfo updateSelectedRoute(@NonNull MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "updateSelectedRoute: " + selector);
-        }
-        RouteInfo route = sGlobal.getSelectedRoute();
-        if (!route.isDefaultOrBluetooth() && !route.matchesSelector(selector)) {
-            route = sGlobal.chooseFallbackRoute();
-            sGlobal.selectRoute(route);
-        }
-        return route;
-    }
-
-    /**
-     * Selects the specified route.
-     *
-     * @param route The route to select.
-     */
-    public void selectRoute(@NonNull RouteInfo route) {
-        if (route == null) {
-            throw new IllegalArgumentException("route must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "selectRoute: " + route);
-        }
-        sGlobal.selectRoute(route);
-    }
-
-    /**
-     * Unselects the current round and selects the default route instead.
-     * <p>
-     * The reason given must be one of:
-     * <ul>
-     * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li>
-     * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li>
-     * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li>
-     * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li>
-     * </ul>
-     *
-     * @param reason The reason for disconnecting the current route.
-     */
-    public void unselect(int reason) {
-        if (reason < MediaRouter.UNSELECT_REASON_UNKNOWN ||
-                reason > MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) {
-            throw new IllegalArgumentException("Unsupported reason to unselect route");
-        }
-        checkCallingThread();
-
-        // Choose the fallback route if it's not already selected.
-        // Otherwise, select the default route.
-        RouteInfo fallbackRoute = sGlobal.chooseFallbackRoute();
-        if (sGlobal.getSelectedRoute() != fallbackRoute) {
-            sGlobal.selectRoute(fallbackRoute, reason);
-        } else {
-            sGlobal.selectRoute(sGlobal.getDefaultRoute(), reason);
-        }
-    }
-
-    /**
-     * Returns true if there is a route that matches the specified selector.
-     * <p>
-     * This method returns true if there are any available routes that match the
-     * selector regardless of whether they are enabled or disabled. If the
-     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
-     * the method will only consider non-default routes.
-     * </p>
-     * <p class="note">
-     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this method
-     * will return true if it is possible to discover a matching route even if
-     * discovery is not in progress or if no matching route has yet been found.
-     * Use {@link #AVAILABILITY_FLAG_REQUIRE_MATCH} to require an actual match.
-     * </p>
-     *
-     * @param selector The selector to match.
-     * @param flags Flags to control the determination of whether a route may be
-     *            available. May be zero or some combination of
-     *            {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and
-     *            {@link #AVAILABILITY_FLAG_REQUIRE_MATCH}.
-     * @return True if a matching route may be available.
-     */
-    public boolean isRouteAvailable(@NonNull MediaRouteSelector selector, int flags) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-        checkCallingThread();
-
-        return sGlobal.isRouteAvailable(selector, flags);
-    }
-
-    /**
-     * Registers a callback to discover routes that match the selector and to receive
-     * events when they change.
-     * <p>
-     * This is a convenience method that has the same effect as calling
-     * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags.
-     * </p>
-     *
-     * @param selector A route selector that indicates the kinds of routes that the
-     * callback would like to discover.
-     * @param callback The callback to add.
-     * @see #removeCallback
-     */
-    public void addCallback(MediaRouteSelector selector, Callback callback) {
-        addCallback(selector, callback, 0);
-    }
-
-    /**
-     * Registers a callback to discover routes that match the selector and to receive
-     * events when they change.
-     * <p>
-     * The selector describes the kinds of routes that the application wants to
-     * discover.  For example, if the application wants to use
-     * live audio routes then it should include the
-     * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category}
-     * in its selector when it adds a callback to the media router.
-     * The selector may include any number of categories.
-     * </p><p>
-     * If the callback has already been registered, then the selector is added to
-     * the set of selectors being monitored by the callback.
-     * </p><p>
-     * By default, the callback will only be invoked for events that affect routes
-     * that match the specified selector.  Event filtering may be disabled by specifying
-     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered.
-     * </p><p>
-     * Applications should use the {@link #isRouteAvailable} method to determine
-     * whether is it possible to discover a route with the desired capabilities
-     * and therefore whether the media route button should be shown to the user.
-     * </p><p>
-     * The {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} flag should be used while the application
-     * is in the foreground to request that passive discovery be performed if there are
-     * sufficient resources to allow continuous passive discovery.
-     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag will be
-     * ignored to conserve resources.
-     * </p><p>
-     * The {@link #CALLBACK_FLAG_FORCE_DISCOVERY} flag should be used when
-     * passive discovery absolutely must be performed, even on low-RAM devices.
-     * This flag has a significant performance impact on low-RAM devices
-     * since it may cause many media route providers to be started simultaneously.
-     * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid
-     * performing passive discovery on these devices altogether.
-     * </p><p>
-     * The {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag should be used when the
-     * media route chooser dialog is showing to confirm the presence of available
-     * routes that the user may connect to.  This flag may use substantially more
-     * power.
-     * </p>
-     *
-     * <h3>Example</h3>
-     * <pre>
-     * public class MyActivity extends Activity {
-     *     private MediaRouter mRouter;
-     *     private MediaRouter.Callback mCallback;
-     *     private MediaRouteSelector mSelector;
-     *
-     *     protected void onCreate(Bundle savedInstanceState) {
-     *         super.onCreate(savedInstanceState);
-     *
-     *         mRouter = Mediarouter.getInstance(this);
-     *         mCallback = new MyCallback();
-     *         mSelector = new MediaRouteSelector.Builder()
-     *                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
-     *                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
-     *                 .build();
-     *     }
-     *
-     *     // Add the callback on start to tell the media router what kinds of routes
-     *     // the application is interested in so that it can try to discover suitable ones.
-     *     public void onStart() {
-     *         super.onStart();
-     *
-     *         mediaRouter.addCallback(mSelector, mCallback,
-     *                 MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
-     *
-     *         MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector);
-     *         // do something with the route...
-     *     }
-     *
-     *     // Remove the selector on stop to tell the media router that it no longer
-     *     // needs to invest effort trying to discover routes of these kinds for now.
-     *     public void onStop() {
-     *         super.onStop();
-     *
-     *         mediaRouter.removeCallback(mCallback);
-     *     }
-     *
-     *     private final class MyCallback extends MediaRouter.Callback {
-     *         // Implement callback methods as needed.
-     *     }
-     * }
-     * </pre>
-     *
-     * @param selector A route selector that indicates the kinds of routes that the
-     * callback would like to discover.
-     * @param callback The callback to add.
-     * @param flags Flags to control the behavior of the callback.
-     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
-     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
-     * @see #removeCallback
-     */
-    public void addCallback(@NonNull MediaRouteSelector selector, @NonNull Callback callback,
-            @CallbackFlags int flags) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-        if (callback == null) {
-            throw new IllegalArgumentException("callback must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "addCallback: selector=" + selector
-                    + ", callback=" + callback + ", flags=" + Integer.toHexString(flags));
-        }
-
-        CallbackRecord record;
-        int index = findCallbackRecord(callback);
-        if (index < 0) {
-            record = new CallbackRecord(this, callback);
-            mCallbackRecords.add(record);
-        } else {
-            record = mCallbackRecords.get(index);
-        }
-        boolean updateNeeded = false;
-        if ((flags & ~record.mFlags) != 0) {
-            record.mFlags |= flags;
-            updateNeeded = true;
-        }
-        if (!record.mSelector.contains(selector)) {
-            record.mSelector = new MediaRouteSelector.Builder(record.mSelector)
-                    .addSelector(selector)
-                    .build();
-            updateNeeded = true;
-        }
-        if (updateNeeded) {
-            sGlobal.updateDiscoveryRequest();
-        }
-    }
-
-    /**
-     * Removes the specified callback.  It will no longer receive events about
-     * changes to media routes.
-     *
-     * @param callback The callback to remove.
-     * @see #addCallback
-     */
-    public void removeCallback(@NonNull Callback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "removeCallback: callback=" + callback);
-        }
-
-        int index = findCallbackRecord(callback);
-        if (index >= 0) {
-            mCallbackRecords.remove(index);
-            sGlobal.updateDiscoveryRequest();
-        }
-    }
-
-    private int findCallbackRecord(Callback callback) {
-        final int count = mCallbackRecords.size();
-        for (int i = 0; i < count; i++) {
-            if (mCallbackRecords.get(i).mCallback == callback) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Registers a media route provider within this application process.
-     * <p>
-     * The provider will be added to the list of providers that all {@link MediaRouter}
-     * instances within this process can use to discover routes.
-     * </p>
-     *
-     * @param providerInstance The media route provider instance to add.
-     *
-     * @see MediaRouteProvider
-     * @see #removeCallback
-     */
-    public void addProvider(@NonNull MediaRouteProvider providerInstance) {
-        if (providerInstance == null) {
-            throw new IllegalArgumentException("providerInstance must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "addProvider: " + providerInstance);
-        }
-        sGlobal.addProvider(providerInstance);
-    }
-
-    /**
-     * Unregisters a media route provider within this application process.
-     * <p>
-     * The provider will be removed from the list of providers that all {@link MediaRouter}
-     * instances within this process can use to discover routes.
-     * </p>
-     *
-     * @param providerInstance The media route provider instance to remove.
-     *
-     * @see MediaRouteProvider
-     * @see #addCallback
-     */
-    public void removeProvider(@NonNull MediaRouteProvider providerInstance) {
-        if (providerInstance == null) {
-            throw new IllegalArgumentException("providerInstance must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "removeProvider: " + providerInstance);
-        }
-        sGlobal.removeProvider(providerInstance);
-    }
-
-    /**
-     * Adds a remote control client to enable remote control of the volume
-     * of the selected route.
-     * <p>
-     * The remote control client must have previously been registered with
-     * the audio manager using the {@link android.media.AudioManager#registerRemoteControlClient
-     * AudioManager.registerRemoteControlClient} method.
-     * </p>
-     *
-     * @param remoteControlClient The {@link android.media.RemoteControlClient} to register.
-     */
-    public void addRemoteControlClient(@NonNull Object remoteControlClient) {
-        if (remoteControlClient == null) {
-            throw new IllegalArgumentException("remoteControlClient must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "addRemoteControlClient: " + remoteControlClient);
-        }
-        sGlobal.addRemoteControlClient(remoteControlClient);
-    }
-
-    /**
-     * Removes a remote control client.
-     *
-     * @param remoteControlClient The {@link android.media.RemoteControlClient}
-     *            to unregister.
-     */
-    public void removeRemoteControlClient(@NonNull Object remoteControlClient) {
-        if (remoteControlClient == null) {
-            throw new IllegalArgumentException("remoteControlClient must not be null");
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "removeRemoteControlClient: " + remoteControlClient);
-        }
-        sGlobal.removeRemoteControlClient(remoteControlClient);
-    }
-
-    /**
-     * Sets the media session to enable remote control of the volume of the
-     * selected route. This should be used instead of
-     * {@link #addRemoteControlClient} when using media sessions. Set the
-     * session to null to clear it.
-     *
-     * @param mediaSession The {@link android.media.session.MediaSession} to
-     *            use.
-     */
-    public void setMediaSession(Object mediaSession) {
-        if (DEBUG) {
-            Log.d(TAG, "addMediaSession: " + mediaSession);
-        }
-        sGlobal.setMediaSession(mediaSession);
-    }
-
-    /**
-     * Sets a compat media session to enable remote control of the volume of the
-     * selected route. This should be used instead of
-     * {@link #addRemoteControlClient} when using {@link MediaSessionCompat}.
-     * Set the session to null to clear it.
-     *
-     * @param mediaSession
-     */
-    public void setMediaSessionCompat(MediaSessionCompat mediaSession) {
-        if (DEBUG) {
-            Log.d(TAG, "addMediaSessionCompat: " + mediaSession);
-        }
-        sGlobal.setMediaSessionCompat(mediaSession);
-    }
-
-    public MediaSessionCompat.Token getMediaSessionToken() {
-        return sGlobal.getMediaSessionToken();
-    }
-
-    /**
-     * Ensures that calls into the media router are on the correct thread.
-     * It pays to be a little paranoid when global state invariants are at risk.
-     */
-    static void checkCallingThread() {
-        if (Looper.myLooper() != Looper.getMainLooper()) {
-            throw new IllegalStateException("The media router service must only be "
-                    + "accessed on the application's main thread.");
-        }
-    }
-
-    static <T> boolean equal(T a, T b) {
-        return a == b || (a != null && b != null && a.equals(b));
-    }
-
-    /**
-     * Provides information about a media route.
-     * <p>
-     * Each media route has a list of {@link MediaControlIntent media control}
-     * {@link #getControlFilters intent filters} that describe the capabilities of the
-     * route and the manner in which it is used and controlled.
-     * </p>
-     */
-    public static class RouteInfo {
-        private final ProviderInfo mProvider;
-        private final String mDescriptorId;
-        private final String mUniqueId;
-        private String mName;
-        private String mDescription;
-        private Uri mIconUri;
-        private boolean mEnabled;
-        private boolean mConnecting;
-        private int mConnectionState;
-        private boolean mCanDisconnect;
-        private final ArrayList<IntentFilter> mControlFilters = new ArrayList<>();
-        private int mPlaybackType;
-        private int mPlaybackStream;
-        private int mDeviceType;
-        private int mVolumeHandling;
-        private int mVolume;
-        private int mVolumeMax;
-        private Display mPresentationDisplay;
-        private int mPresentationDisplayId = PRESENTATION_DISPLAY_ID_NONE;
-        private Bundle mExtras;
-        private IntentSender mSettingsIntent;
-        MediaRouteDescriptor mDescriptor;
-
-        @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
-                CONNECTION_STATE_CONNECTED})
-        @Retention(RetentionPolicy.SOURCE)
-        private @interface ConnectionState {}
-
-        /**
-         * The default connection state indicating the route is disconnected.
-         *
-         * @see #getConnectionState
-         */
-        public static final int CONNECTION_STATE_DISCONNECTED = 0;
-
-        /**
-         * A connection state indicating the route is in the process of connecting and is not yet
-         * ready for use.
-         *
-         * @see #getConnectionState
-         */
-        public static final int CONNECTION_STATE_CONNECTING = 1;
-
-        /**
-         * A connection state indicating the route is connected.
-         *
-         * @see #getConnectionState
-         */
-        public static final int CONNECTION_STATE_CONNECTED = 2;
-
-        @IntDef({PLAYBACK_TYPE_LOCAL,PLAYBACK_TYPE_REMOTE})
-        @Retention(RetentionPolicy.SOURCE)
-        private @interface PlaybackType {}
-
-        /**
-         * The default playback type, "local", indicating the presentation of the media
-         * is happening on the same device (e.g. a phone, a tablet) as where it is
-         * controlled from.
-         *
-         * @see #getPlaybackType
-         */
-        public static final int PLAYBACK_TYPE_LOCAL = 0;
-
-        /**
-         * A playback type indicating the presentation of the media is happening on
-         * a different device (i.e. the remote device) than where it is controlled from.
-         *
-         * @see #getPlaybackType
-         */
-        public static final int PLAYBACK_TYPE_REMOTE = 1;
-
-        @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
-        @Retention(RetentionPolicy.SOURCE)
-        private @interface DeviceType {}
-
-        /**
-         * The default receiver device type of the route indicating the type is unknown.
-         *
-         * @see #getDeviceType
-         * @hide
-         */
-        // @RestrictTo(LIBRARY_GROUP)
-        public static final int DEVICE_TYPE_UNKNOWN = 0;
-
-        /**
-         * A receiver device type of the route indicating the presentation of the media is happening
-         * on a TV.
-         *
-         * @see #getDeviceType
-         */
-        public static final int DEVICE_TYPE_TV = 1;
-
-        /**
-         * A receiver device type of the route indicating the presentation of the media is happening
-         * on a speaker.
-         *
-         * @see #getDeviceType
-         */
-        public static final int DEVICE_TYPE_SPEAKER = 2;
-
-        /**
-         * A receiver device type of the route indicating the presentation of the media is happening
-         * on a bluetooth device such as a bluetooth speaker.
-         *
-         * @see #getDeviceType
-         * @hide
-         */
-        // @RestrictTo(LIBRARY_GROUP)
-        public static final int DEVICE_TYPE_BLUETOOTH = 3;
-
-        @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE})
-        @Retention(RetentionPolicy.SOURCE)
-        private @interface PlaybackVolume {}
-
-        /**
-         * Playback information indicating the playback volume is fixed, i.e. it cannot be
-         * controlled from this object. An example of fixed playback volume is a remote player,
-         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
-         * than attenuate at the source.
-         *
-         * @see #getVolumeHandling
-         */
-        public static final int PLAYBACK_VOLUME_FIXED = 0;
-
-        /**
-         * Playback information indicating the playback volume is variable and can be controlled
-         * from this object.
-         *
-         * @see #getVolumeHandling
-         */
-        public static final int PLAYBACK_VOLUME_VARIABLE = 1;
-
-        /**
-         * The default presentation display id indicating no presentation display is associated
-         * with the route.
-         * @hide
-         */
-        // @RestrictTo(LIBRARY_GROUP)
-        public static final int PRESENTATION_DISPLAY_ID_NONE = -1;
-
-        static final int CHANGE_GENERAL = 1 << 0;
-        static final int CHANGE_VOLUME = 1 << 1;
-        static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2;
-
-        // Should match to SystemMediaRouteProvider.PACKAGE_NAME.
-        static final String SYSTEM_MEDIA_ROUTE_PROVIDER_PACKAGE_NAME = "android";
-
-        RouteInfo(ProviderInfo provider, String descriptorId, String uniqueId) {
-            mProvider = provider;
-            mDescriptorId = descriptorId;
-            mUniqueId = uniqueId;
-        }
-
-        /**
-         * Gets information about the provider of this media route.
-         */
-        public ProviderInfo getProvider() {
-            return mProvider;
-        }
-
-        /**
-         * Gets the unique id of the route.
-         * <p>
-         * The route unique id functions as a stable identifier by which the route is known.
-         * For example, an application can use this id as a token to remember the
-         * selected route across restarts or to communicate its identity to a service.
-         * </p>
-         *
-         * @return The unique id of the route, never null.
-         */
-        @NonNull
-        public String getId() {
-            return mUniqueId;
-        }
-
-        /**
-         * Gets the user-visible name of the route.
-         * <p>
-         * The route name identifies the destination represented by the route.
-         * It may be a user-supplied name, an alias, or device serial number.
-         * </p>
-         *
-         * @return The user-visible name of a media route.  This is the string presented
-         * to users who may select this as the active route.
-         */
-        public String getName() {
-            return mName;
-        }
-
-        /**
-         * Gets the user-visible description of the route.
-         * <p>
-         * The route description describes the kind of destination represented by the route.
-         * It may be a user-supplied string, a model number or brand of device.
-         * </p>
-         *
-         * @return The description of the route, or null if none.
-         */
-        @Nullable
-        public String getDescription() {
-            return mDescription;
-        }
-
-        /**
-         * Gets the URI of the icon representing this route.
-         * <p>
-         * This icon will be used in picker UIs if available.
-         * </p>
-         *
-         * @return The URI of the icon representing this route, or null if none.
-         */
-        public Uri getIconUri() {
-            return mIconUri;
-        }
-
-        /**
-         * Returns true if this route is enabled and may be selected.
-         *
-         * @return True if this route is enabled.
-         */
-        public boolean isEnabled() {
-            return mEnabled;
-        }
-
-        /**
-         * Returns true if the route is in the process of connecting and is not
-         * yet ready for use.
-         *
-         * @return True if this route is in the process of connecting.
-         */
-        public boolean isConnecting() {
-            return mConnecting;
-        }
-
-        /**
-         * Gets the connection state of the route.
-         *
-         * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED},
-         * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}.
-         */
-        @ConnectionState
-        public int getConnectionState() {
-            return mConnectionState;
-        }
-
-        /**
-         * Returns true if this route is currently selected.
-         *
-         * @return True if this route is currently selected.
-         *
-         * @see MediaRouter#getSelectedRoute
-         */
-        public boolean isSelected() {
-            checkCallingThread();
-            return sGlobal.getSelectedRoute() == this;
-        }
-
-        /**
-         * Returns true if this route is the default route.
-         *
-         * @return True if this route is the default route.
-         *
-         * @see MediaRouter#getDefaultRoute
-         */
-        public boolean isDefault() {
-            checkCallingThread();
-            return sGlobal.getDefaultRoute() == this;
-        }
-
-        /**
-         * Returns true if this route is a bluetooth route.
-         *
-         * @return True if this route is a bluetooth route.
-         *
-         * @see MediaRouter#getBluetoothRoute
-         */
-        public boolean isBluetooth() {
-            checkCallingThread();
-            return sGlobal.getBluetoothRoute() == this;
-        }
-
-        /**
-         * Returns true if this route is the default route and the device speaker.
-         *
-         * @return True if this route is the default route and the device speaker.
-         */
-        public boolean isDeviceSpeaker() {
-            int defaultAudioRouteNameResourceId = Resources.getSystem().getIdentifier(
-                    "default_audio_route_name", "string", "android");
-            return isDefault()
-                    && Resources.getSystem().getText(defaultAudioRouteNameResourceId).equals(mName);
-        }
-
-        /**
-         * Gets a list of {@link MediaControlIntent media control intent} filters that
-         * describe the capabilities of this route and the media control actions that
-         * it supports.
-         *
-         * @return A list of intent filters that specifies the media control intents that
-         * this route supports.
-         *
-         * @see MediaControlIntent
-         * @see #supportsControlCategory
-         * @see #supportsControlRequest
-         */
-        public List<IntentFilter> getControlFilters() {
-            return mControlFilters;
-        }
-
-        /**
-         * Returns true if the route supports at least one of the capabilities
-         * described by a media route selector.
-         *
-         * @param selector The selector that specifies the capabilities to check.
-         * @return True if the route supports at least one of the capabilities
-         * described in the media route selector.
-         */
-        public boolean matchesSelector(@NonNull MediaRouteSelector selector) {
-            if (selector == null) {
-                throw new IllegalArgumentException("selector must not be null");
-            }
-            checkCallingThread();
-            return selector.matchesControlFilters(mControlFilters);
-        }
-
-        /**
-         * Returns true if the route supports the specified
-         * {@link MediaControlIntent media control} category.
-         * <p>
-         * Media control categories describe the capabilities of this route
-         * such as whether it supports live audio streaming or remote playback.
-         * </p>
-         *
-         * @param category A {@link MediaControlIntent media control} category
-         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
-         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
-         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
-         * media control category.
-         * @return True if the route supports the specified intent category.
-         *
-         * @see MediaControlIntent
-         * @see #getControlFilters
-         */
-        public boolean supportsControlCategory(@NonNull String category) {
-            if (category == null) {
-                throw new IllegalArgumentException("category must not be null");
-            }
-            checkCallingThread();
-
-            int count = mControlFilters.size();
-            for (int i = 0; i < count; i++) {
-                if (mControlFilters.get(i).hasCategory(category)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Returns true if the route supports the specified
-         * {@link MediaControlIntent media control} category and action.
-         * <p>
-         * Media control actions describe specific requests that an application
-         * can ask a route to perform.
-         * </p>
-         *
-         * @param category A {@link MediaControlIntent media control} category
-         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
-         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
-         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
-         * media control category.
-         * @param action A {@link MediaControlIntent media control} action
-         * such as {@link MediaControlIntent#ACTION_PLAY}.
-         * @return True if the route supports the specified intent action.
-         *
-         * @see MediaControlIntent
-         * @see #getControlFilters
-         */
-        public boolean supportsControlAction(@NonNull String category, @NonNull String action) {
-            if (category == null) {
-                throw new IllegalArgumentException("category must not be null");
-            }
-            if (action == null) {
-                throw new IllegalArgumentException("action must not be null");
-            }
-            checkCallingThread();
-
-            int count = mControlFilters.size();
-            for (int i = 0; i < count; i++) {
-                IntentFilter filter = mControlFilters.get(i);
-                if (filter.hasCategory(category) && filter.hasAction(action)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Returns true if the route supports the specified
-         * {@link MediaControlIntent media control} request.
-         * <p>
-         * Media control requests are used to request the route to perform
-         * actions such as starting remote playback of a media item.
-         * </p>
-         *
-         * @param intent A {@link MediaControlIntent media control intent}.
-         * @return True if the route can handle the specified intent.
-         *
-         * @see MediaControlIntent
-         * @see #getControlFilters
-         */
-        public boolean supportsControlRequest(@NonNull Intent intent) {
-            if (intent == null) {
-                throw new IllegalArgumentException("intent must not be null");
-            }
-            checkCallingThread();
-
-            ContentResolver contentResolver = sGlobal.getContentResolver();
-            int count = mControlFilters.size();
-            for (int i = 0; i < count; i++) {
-                if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Sends a {@link MediaControlIntent media control} request to be performed
-         * asynchronously by the route's destination.
-         * <p>
-         * Media control requests are used to request the route to perform
-         * actions such as starting remote playback of a media item.
-         * </p><p>
-         * This function may only be called on a selected route.  Control requests
-         * sent to unselected routes will fail.
-         * </p>
-         *
-         * @param intent A {@link MediaControlIntent media control intent}.
-         * @param callback A {@link ControlRequestCallback} to invoke with the result
-         * of the request, or null if no result is required.
-         *
-         * @see MediaControlIntent
-         */
-        public void sendControlRequest(@NonNull Intent intent,
-                @Nullable ControlRequestCallback callback) {
-            if (intent == null) {
-                throw new IllegalArgumentException("intent must not be null");
-            }
-            checkCallingThread();
-
-            sGlobal.sendControlRequest(this, intent, callback);
-        }
-
-        /**
-         * Gets the type of playback associated with this route.
-         *
-         * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL}
-         * or {@link #PLAYBACK_TYPE_REMOTE}.
-         */
-        @PlaybackType
-        public int getPlaybackType() {
-            return mPlaybackType;
-        }
-
-        /**
-         * Gets the audio stream over which the playback associated with this route is performed.
-         *
-         * @return The stream over which the playback associated with this route is performed.
-         */
-        public int getPlaybackStream() {
-            return mPlaybackStream;
-        }
-
-        /**
-         * Gets the type of the receiver device associated with this route.
-         *
-         * @return The type of the receiver device associated with this route:
-         * {@link #DEVICE_TYPE_TV} or {@link #DEVICE_TYPE_SPEAKER}.
-         */
-        public int getDeviceType() {
-            return mDeviceType;
-        }
-
-
-        /**
-         * @hide
-         */
-        // @RestrictTo(LIBRARY_GROUP)
-        public boolean isDefaultOrBluetooth() {
-            if (isDefault() || mDeviceType == DEVICE_TYPE_BLUETOOTH) {
-                return true;
-            }
-            // This is a workaround for platform version 23 or below where the system route
-            // provider doesn't specify device type for bluetooth media routes.
-            return isSystemMediaRouteProvider(this)
-                    && supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
-                    && !supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
-        }
-
-        /**
-         * Returns {@code true} if the route is selectable.
-         */
-        boolean isSelectable() {
-            // This tests whether the route is still valid and enabled.
-            // The route descriptor field is set to null when the route is removed.
-            return mDescriptor != null && mEnabled;
-        }
-
-        private static boolean isSystemMediaRouteProvider(MediaRouter.RouteInfo route) {
-            return TextUtils.equals(route.getProviderInstance().getMetadata().getPackageName(),
-                    SYSTEM_MEDIA_ROUTE_PROVIDER_PACKAGE_NAME);
-        }
-
-        /**
-         * Gets information about how volume is handled on the route.
-         *
-         * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED}
-         * or {@link #PLAYBACK_VOLUME_VARIABLE}.
-         */
-        @PlaybackVolume
-        public int getVolumeHandling() {
-            return mVolumeHandling;
-        }
-
-        /**
-         * Gets the current volume for this route. Depending on the route, this may only
-         * be valid if the route is currently selected.
-         *
-         * @return The volume at which the playback associated with this route is performed.
-         */
-        public int getVolume() {
-            return mVolume;
-        }
-
-        /**
-         * Gets the maximum volume at which the playback associated with this route is performed.
-         *
-         * @return The maximum volume at which the playback associated with
-         * this route is performed.
-         */
-        public int getVolumeMax() {
-            return mVolumeMax;
-        }
-
-        /**
-         * Gets whether this route supports disconnecting without interrupting
-         * playback.
-         *
-         * @return True if this route can disconnect without stopping playback,
-         *         false otherwise.
-         */
-        public boolean canDisconnect() {
-            return mCanDisconnect;
-        }
-
-        /**
-         * Requests a volume change for this route asynchronously.
-         * <p>
-         * This function may only be called on a selected route.  It will have
-         * no effect if the route is currently unselected.
-         * </p>
-         *
-         * @param volume The new volume value between 0 and {@link #getVolumeMax}.
-         */
-        public void requestSetVolume(int volume) {
-            checkCallingThread();
-            sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume)));
-        }
-
-        /**
-         * Requests an incremental volume update for this route asynchronously.
-         * <p>
-         * This function may only be called on a selected route.  It will have
-         * no effect if the route is currently unselected.
-         * </p>
-         *
-         * @param delta The delta to add to the current volume.
-         */
-        public void requestUpdateVolume(int delta) {
-            checkCallingThread();
-            if (delta != 0) {
-                sGlobal.requestUpdateVolume(this, delta);
-            }
-        }
-
-        /**
-         * Gets the {@link Display} that should be used by the application to show
-         * a {@link android.app.Presentation} on an external display when this route is selected.
-         * Depending on the route, this may only be valid if the route is currently
-         * selected.
-         * <p>
-         * The preferred presentation display may change independently of the route
-         * being selected or unselected.  For example, the presentation display
-         * of the default system route may change when an external HDMI display is connected
-         * or disconnected even though the route itself has not changed.
-         * </p><p>
-         * This method may return null if there is no external display associated with
-         * the route or if the display is not ready to show UI yet.
-         * </p><p>
-         * The application should listen for changes to the presentation display
-         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
-         * show or dismiss its {@link android.app.Presentation} accordingly when the display
-         * becomes available or is removed.
-         * </p><p>
-         * This method only makes sense for
-         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes.
-         * </p>
-         *
-         * @return The preferred presentation display to use when this route is
-         * selected or null if none.
-         *
-         * @see MediaControlIntent#CATEGORY_LIVE_VIDEO
-         * @see android.app.Presentation
-         */
-        @Nullable
-        public Display getPresentationDisplay() {
-            checkCallingThread();
-            if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) {
-                mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId);
-            }
-            return mPresentationDisplay;
-        }
-
-        /**
-         * Gets the route's presentation display id, or -1 if none.
-         * @hide
-         */
-        // @RestrictTo(LIBRARY_GROUP)
-        public int getPresentationDisplayId() {
-            return mPresentationDisplayId;
-        }
-
-        /**
-         * Gets a collection of extra properties about this route that were supplied
-         * by its media route provider, or null if none.
-         */
-        @Nullable
-        public Bundle getExtras() {
-            return mExtras;
-        }
-
-        /**
-         * Gets an intent sender for launching a settings activity for this
-         * route.
-         */
-        @Nullable
-        public IntentSender getSettingsIntent() {
-            return mSettingsIntent;
-        }
-
-        /**
-         * Selects this media route.
-         */
-        public void select() {
-            checkCallingThread();
-            sGlobal.selectRoute(this);
-        }
-
-        @Override
-        public String toString() {
-            return "MediaRouter.RouteInfo{ uniqueId=" + mUniqueId
-                    + ", name=" + mName
-                    + ", description=" + mDescription
-                    + ", iconUri=" + mIconUri
-                    + ", enabled=" + mEnabled
-                    + ", connecting=" + mConnecting
-                    + ", connectionState=" + mConnectionState
-                    + ", canDisconnect=" + mCanDisconnect
-                    + ", playbackType=" + mPlaybackType
-                    + ", playbackStream=" + mPlaybackStream
-                    + ", deviceType=" + mDeviceType
-                    + ", volumeHandling=" + mVolumeHandling
-                    + ", volume=" + mVolume
-                    + ", volumeMax=" + mVolumeMax
-                    + ", presentationDisplayId=" + mPresentationDisplayId
-                    + ", extras=" + mExtras
-                    + ", settingsIntent=" + mSettingsIntent
-                    + ", providerPackageName=" + mProvider.getPackageName()
-                    + " }";
-        }
-
-        int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) {
-            int changes = 0;
-            if (mDescriptor != descriptor) {
-                changes = updateDescriptor(descriptor);
-            }
-            return changes;
-        }
-
-        int updateDescriptor(MediaRouteDescriptor descriptor) {
-            int changes = 0;
-            mDescriptor = descriptor;
-            if (descriptor != null) {
-                if (!equal(mName, descriptor.getName())) {
-                    mName = descriptor.getName();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (!equal(mDescription, descriptor.getDescription())) {
-                    mDescription = descriptor.getDescription();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (!equal(mIconUri, descriptor.getIconUri())) {
-                    mIconUri = descriptor.getIconUri();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mEnabled != descriptor.isEnabled()) {
-                    mEnabled = descriptor.isEnabled();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mConnecting != descriptor.isConnecting()) {
-                    mConnecting = descriptor.isConnecting();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mConnectionState != descriptor.getConnectionState()) {
-                    mConnectionState = descriptor.getConnectionState();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (!mControlFilters.equals(descriptor.getControlFilters())) {
-                    mControlFilters.clear();
-                    mControlFilters.addAll(descriptor.getControlFilters());
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mPlaybackType != descriptor.getPlaybackType()) {
-                    mPlaybackType = descriptor.getPlaybackType();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mPlaybackStream != descriptor.getPlaybackStream()) {
-                    mPlaybackStream = descriptor.getPlaybackStream();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mDeviceType != descriptor.getDeviceType()) {
-                    mDeviceType = descriptor.getDeviceType();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mVolumeHandling != descriptor.getVolumeHandling()) {
-                    mVolumeHandling = descriptor.getVolumeHandling();
-                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
-                }
-                if (mVolume != descriptor.getVolume()) {
-                    mVolume = descriptor.getVolume();
-                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
-                }
-                if (mVolumeMax != descriptor.getVolumeMax()) {
-                    mVolumeMax = descriptor.getVolumeMax();
-                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
-                }
-                if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) {
-                    mPresentationDisplayId = descriptor.getPresentationDisplayId();
-                    mPresentationDisplay = null;
-                    changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
-                }
-                if (!equal(mExtras, descriptor.getExtras())) {
-                    mExtras = descriptor.getExtras();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (!equal(mSettingsIntent, descriptor.getSettingsActivity())) {
-                    mSettingsIntent = descriptor.getSettingsActivity();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mCanDisconnect != descriptor.canDisconnectAndKeepPlaying()) {
-                    mCanDisconnect = descriptor.canDisconnectAndKeepPlaying();
-                    changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
-                }
-            }
-            return changes;
-        }
-
-        String getDescriptorId() {
-            return mDescriptorId;
-        }
-
-        /** @hide */
-        // @RestrictTo(LIBRARY_GROUP)
-        public MediaRouteProvider getProviderInstance() {
-            return mProvider.getProviderInstance();
-        }
-    }
-
-    /**
-     * Information about a route that consists of multiple other routes in a group.
-     * @hide
-     */
-    // @RestrictTo(LIBRARY_GROUP)
-    public static class RouteGroup extends RouteInfo {
-        private List<RouteInfo> mRoutes = new ArrayList<>();
-
-        RouteGroup(ProviderInfo provider, String descriptorId, String uniqueId) {
-            super(provider, descriptorId, uniqueId);
-        }
-
-        /**
-         * @return The number of routes in this group
-         */
-        public int getRouteCount() {
-            return mRoutes.size();
-        }
-
-        /**
-         * Returns the route in this group at the specified index
-         *
-         * @param index Index to fetch
-         * @return The route at index
-         */
-        public RouteInfo getRouteAt(int index) {
-            return mRoutes.get(index);
-        }
-
-        /**
-         * Returns the routes in this group
-         *
-         * @return The list of the routes in this group
-         */
-        public List<RouteInfo> getRoutes() {
-            return mRoutes;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder(super.toString());
-            sb.append('[');
-            final int count = mRoutes.size();
-            for (int i = 0; i < count; i++) {
-                if (i > 0) sb.append(", ");
-                sb.append(mRoutes.get(i));
-            }
-            sb.append(']');
-            return sb.toString();
-        }
-
-        @Override
-        int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) {
-            boolean changed = false;
-            if (mDescriptor != descriptor) {
-                mDescriptor = descriptor;
-                if (descriptor != null) {
-                    List<String> groupMemberIds = descriptor.getGroupMemberIds();
-                    List<RouteInfo> routes = new ArrayList<>();
-                    changed = groupMemberIds.size() != mRoutes.size();
-                    for (String groupMemberId : groupMemberIds) {
-                        String uniqueId = sGlobal.getUniqueId(getProvider(), groupMemberId);
-                        RouteInfo groupMember = sGlobal.getRoute(uniqueId);
-                        if (groupMember != null) {
-                            routes.add(groupMember);
-                            if (!changed && !mRoutes.contains(groupMember)) {
-                                changed = true;
-                            }
-                        }
-                    }
-                    if (changed) {
-                        mRoutes = routes;
-                    }
-                }
-            }
-            return (changed ? CHANGE_GENERAL : 0) | super.updateDescriptor(descriptor);
-        }
-    }
-
-    /**
-     * Provides information about a media route provider.
-     * <p>
-     * This object may be used to determine which media route provider has
-     * published a particular route.
-     * </p>
-     */
-    public static final class ProviderInfo {
-        private final MediaRouteProvider mProviderInstance;
-        private final List<RouteInfo> mRoutes = new ArrayList<>();
-
-        private final ProviderMetadata mMetadata;
-        private MediaRouteProviderDescriptor mDescriptor;
-        private Resources mResources;
-        private boolean mResourcesNotAvailable;
-
-        ProviderInfo(MediaRouteProvider provider) {
-            mProviderInstance = provider;
-            mMetadata = provider.getMetadata();
-        }
-
-        /**
-         * Gets the provider's underlying {@link MediaRouteProvider} instance.
-         */
-        public MediaRouteProvider getProviderInstance() {
-            checkCallingThread();
-            return mProviderInstance;
-        }
-
-        /**
-         * Gets the package name of the media route provider.
-         */
-        public String getPackageName() {
-            return mMetadata.getPackageName();
-        }
-
-        /**
-         * Gets the component name of the media route provider.
-         */
-        public ComponentName getComponentName() {
-            return mMetadata.getComponentName();
-        }
-
-        /**
-         * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider.
-         */
-        public List<RouteInfo> getRoutes() {
-            checkCallingThread();
-            return mRoutes;
-        }
-
-        Resources getResources() {
-            if (mResources == null && !mResourcesNotAvailable) {
-                String packageName = getPackageName();
-                Context context = sGlobal.getProviderContext(packageName);
-                if (context != null) {
-                    mResources = context.getResources();
-                } else {
-                    Log.w(TAG, "Unable to obtain resources for route provider package: "
-                            + packageName);
-                    mResourcesNotAvailable = true;
-                }
-            }
-            return mResources;
-        }
-
-        boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) {
-            if (mDescriptor != descriptor) {
-                mDescriptor = descriptor;
-                return true;
-            }
-            return false;
-        }
-
-        int findRouteByDescriptorId(String id) {
-            final int count = mRoutes.size();
-            for (int i = 0; i < count; i++) {
-                if (mRoutes.get(i).mDescriptorId.equals(id)) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        @Override
-        public String toString() {
-            return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName()
-                    + " }";
-        }
-    }
-
-    /**
-     * Interface for receiving events about media routing changes.
-     * All methods of this interface will be called from the application's main thread.
-     * <p>
-     * A Callback will only receive events relevant to routes that the callback
-     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
-     * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}.
-     * </p>
-     *
-     * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int)
-     * @see MediaRouter#removeCallback(Callback)
-     */
-    public static abstract class Callback {
-        /**
-         * Called when the supplied media route becomes selected as the active route.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that has been selected.
-         */
-        public void onRouteSelected(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when the supplied media route becomes unselected as the active route.
-         * For detailed reason, override {@link #onRouteUnselected(MediaRouter, RouteInfo, int)}
-         * instead.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that has been unselected.
-         */
-        public void onRouteUnselected(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when the supplied media route becomes unselected as the active route.
-         * The default implementation calls {@link #onRouteUnselected}.
-         * <p>
-         * The reason provided will be one of the following:
-         * <ul>
-         * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li>
-         * </ul>
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that has been unselected.
-         * @param reason The reason for unselecting the route.
-         */
-        public void onRouteUnselected(MediaRouter router, RouteInfo route, int reason) {
-            onRouteUnselected(router, route);
-        }
-
-        /**
-         * Called when a media route has been added.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that has become available for use.
-         */
-        public void onRouteAdded(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when a media route has been removed.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that has been removed from availability.
-         */
-        public void onRouteRemoved(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when a property of the indicated media route has changed.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that was changed.
-         */
-        public void onRouteChanged(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when a media route's volume changes.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route whose volume changed.
-         */
-        public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when a media route's presentation display changes.
-         * <p>
-         * This method is called whenever the route's presentation display becomes
-         * available, is removed or has changes to some of its properties (such as its size).
-         * </p>
-         *
-         * @param router The media router reporting the event.
-         * @param route The route whose presentation display changed.
-         *
-         * @see RouteInfo#getPresentationDisplay()
-         */
-        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when a media route provider has been added.
-         *
-         * @param router The media router reporting the event.
-         * @param provider The provider that has become available for use.
-         */
-        public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
-        }
-
-        /**
-         * Called when a media route provider has been removed.
-         *
-         * @param router The media router reporting the event.
-         * @param provider The provider that has been removed from availability.
-         */
-        public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
-        }
-
-        /**
-         * Called when a property of the indicated media route provider has changed.
-         *
-         * @param router The media router reporting the event.
-         * @param provider The provider that was changed.
-         */
-        public void onProviderChanged(MediaRouter router, ProviderInfo provider) {
-        }
-    }
-
-    /**
-     * Callback which is invoked with the result of a media control request.
-     *
-     * @see RouteInfo#sendControlRequest
-     */
-    public static abstract class ControlRequestCallback {
-        /**
-         * Called when a media control request succeeds.
-         *
-         * @param data Result data, or null if none.
-         * Contents depend on the {@link MediaControlIntent media control action}.
-         */
-        public void onResult(Bundle data) {
-        }
-
-        /**
-         * Called when a media control request fails.
-         *
-         * @param error A localized error message which may be shown to the user, or null
-         * if the cause of the error is unclear.
-         * @param data Error data, or null if none.
-         * Contents depend on the {@link MediaControlIntent media control action}.
-         */
-        public void onError(String error, Bundle data) {
-        }
-    }
-
-    private static final class CallbackRecord {
-        public final MediaRouter mRouter;
-        public final Callback mCallback;
-        public MediaRouteSelector mSelector;
-        public int mFlags;
-
-        public CallbackRecord(MediaRouter router, Callback callback) {
-            mRouter = router;
-            mCallback = callback;
-            mSelector = MediaRouteSelector.EMPTY;
-        }
-
-        public boolean filterRouteEvent(RouteInfo route) {
-            return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
-                    || route.matchesSelector(mSelector);
-        }
-    }
-
-    /**
-     * Global state for the media router.
-     * <p>
-     * Media routes and media route providers are global to the process; their
-     * state and the bulk of the media router implementation lives here.
-     * </p>
-     */
-    private static final class GlobalMediaRouter
-            implements SystemMediaRouteProvider.SyncCallback,
-            RegisteredMediaRouteProviderWatcher.Callback {
-        final Context mApplicationContext;
-        final ArrayList<WeakReference<MediaRouter>> mRouters = new ArrayList<>();
-        private final ArrayList<RouteInfo> mRoutes = new ArrayList<>();
-        private final Map<Pair<String, String>, String> mUniqueIdMap = new HashMap<>();
-        private final ArrayList<ProviderInfo> mProviders = new ArrayList<>();
-        private final ArrayList<RemoteControlClientRecord> mRemoteControlClients =
-                new ArrayList<>();
-        final RemoteControlClientCompat.PlaybackInfo mPlaybackInfo =
-                new RemoteControlClientCompat.PlaybackInfo();
-        private final ProviderCallback mProviderCallback = new ProviderCallback();
-        final CallbackHandler mCallbackHandler = new CallbackHandler();
-        private final DisplayManagerCompat mDisplayManager;
-        final SystemMediaRouteProvider mSystemProvider;
-        private final boolean mLowRam;
-
-        private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher;
-        private RouteInfo mDefaultRoute;
-        private RouteInfo mBluetoothRoute;
-        RouteInfo mSelectedRoute;
-        private RouteController mSelectedRouteController;
-        // A map from route descriptor ID to RouteController for the member routes in the currently
-        // selected route group.
-        private final Map<String, RouteController> mRouteControllerMap = new HashMap<>();
-        private MediaRouteDiscoveryRequest mDiscoveryRequest;
-        private MediaSessionRecord mMediaSession;
-        MediaSessionCompat mRccMediaSession;
-        private MediaSessionCompat mCompatSession;
-        private MediaSessionCompat.OnActiveChangeListener mSessionActiveListener =
-                new MediaSessionCompat.OnActiveChangeListener() {
-            @Override
-            public void onActiveChanged() {
-                if(mRccMediaSession != null) {
-                    if (mRccMediaSession.isActive()) {
-                        addRemoteControlClient(mRccMediaSession.getRemoteControlClient());
-                    } else {
-                        removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
-                    }
-                }
-            }
-        };
-
-        GlobalMediaRouter(Context applicationContext) {
-            mApplicationContext = applicationContext;
-            mDisplayManager = DisplayManagerCompat.getInstance(applicationContext);
-            mLowRam = ActivityManagerCompat.isLowRamDevice(
-                    (ActivityManager)applicationContext.getSystemService(
-                            Context.ACTIVITY_SERVICE));
-
-            // Add the system media route provider for interoperating with
-            // the framework media router.  This one is special and receives
-            // synchronization messages from the media router.
-            mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this);
-        }
-
-        public void start() {
-            addProvider(mSystemProvider);
-
-            // Start watching for routes published by registered media route
-            // provider services.
-            mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher(
-                    mApplicationContext, this);
-            mRegisteredProviderWatcher.start();
-        }
-
-        public MediaRouter getRouter(Context context) {
-            MediaRouter router;
-            for (int i = mRouters.size(); --i >= 0; ) {
-                router = mRouters.get(i).get();
-                if (router == null) {
-                    mRouters.remove(i);
-                } else if (router.mContext == context) {
-                    return router;
-                }
-            }
-            router = new MediaRouter(context);
-            mRouters.add(new WeakReference<MediaRouter>(router));
-            return router;
-        }
-
-        public ContentResolver getContentResolver() {
-            return mApplicationContext.getContentResolver();
-        }
-
-        public Context getProviderContext(String packageName) {
-            if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) {
-                return mApplicationContext;
-            }
-            try {
-                return mApplicationContext.createPackageContext(
-                        packageName, Context.CONTEXT_RESTRICTED);
-            } catch (NameNotFoundException ex) {
-                return null;
-            }
-        }
-
-        public Display getDisplay(int displayId) {
-            return mDisplayManager.getDisplay(displayId);
-        }
-
-        public void sendControlRequest(RouteInfo route,
-                Intent intent, ControlRequestCallback callback) {
-            if (route == mSelectedRoute && mSelectedRouteController != null) {
-                if (mSelectedRouteController.onControlRequest(intent, callback)) {
-                    return;
-                }
-            }
-            if (callback != null) {
-                callback.onError(null, null);
-            }
-        }
-
-        public void requestSetVolume(RouteInfo route, int volume) {
-            if (route == mSelectedRoute && mSelectedRouteController != null) {
-                mSelectedRouteController.onSetVolume(volume);
-            } else if (!mRouteControllerMap.isEmpty()) {
-                RouteController controller = mRouteControllerMap.get(route.mDescriptorId);
-                if (controller != null) {
-                    controller.onSetVolume(volume);
-                }
-            }
-        }
-
-        public void requestUpdateVolume(RouteInfo route, int delta) {
-            if (route == mSelectedRoute && mSelectedRouteController != null) {
-                mSelectedRouteController.onUpdateVolume(delta);
-            }
-        }
-
-        public RouteInfo getRoute(String uniqueId) {
-            for (RouteInfo info : mRoutes) {
-                if (info.mUniqueId.equals(uniqueId)) {
-                    return info;
-                }
-            }
-            return null;
-        }
-
-        public List<RouteInfo> getRoutes() {
-            return mRoutes;
-        }
-
-        List<ProviderInfo> getProviders() {
-            return mProviders;
-        }
-
-        @NonNull RouteInfo getDefaultRoute() {
-            if (mDefaultRoute == null) {
-                // This should never happen once the media router has been fully
-                // initialized but it is good to check for the error in case there
-                // is a bug in provider initialization.
-                throw new IllegalStateException("There is no default route.  "
-                        + "The media router has not yet been fully initialized.");
-            }
-            return mDefaultRoute;
-        }
-
-        RouteInfo getBluetoothRoute() {
-            return mBluetoothRoute;
-        }
-
-        @NonNull RouteInfo getSelectedRoute() {
-            if (mSelectedRoute == null) {
-                // This should never happen once the media router has been fully
-                // initialized but it is good to check for the error in case there
-                // is a bug in provider initialization.
-                throw new IllegalStateException("There is no currently selected route.  "
-                        + "The media router has not yet been fully initialized.");
-            }
-            return mSelectedRoute;
-        }
-
-        void selectRoute(@NonNull RouteInfo route) {
-            selectRoute(route, MediaRouter.UNSELECT_REASON_ROUTE_CHANGED);
-        }
-
-        void selectRoute(@NonNull RouteInfo route, int unselectReason) {
-            if (!mRoutes.contains(route)) {
-                Log.w(TAG, "Ignoring attempt to select removed route: " + route);
-                return;
-            }
-            if (!route.mEnabled) {
-                Log.w(TAG, "Ignoring attempt to select disabled route: " + route);
-                return;
-            }
-            setSelectedRouteInternal(route, unselectReason);
-        }
-
-        public boolean isRouteAvailable(MediaRouteSelector selector, int flags) {
-            if (selector.isEmpty()) {
-                return false;
-            }
-
-            // On low-RAM devices, do not rely on actual discovery results unless asked to.
-            if ((flags & AVAILABILITY_FLAG_REQUIRE_MATCH) == 0 && mLowRam) {
-                return true;
-            }
-
-            // Check whether any existing routes match the selector.
-            final int routeCount = mRoutes.size();
-            for (int i = 0; i < routeCount; i++) {
-                RouteInfo route = mRoutes.get(i);
-                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0
-                        && route.isDefaultOrBluetooth()) {
-                    continue;
-                }
-                if (route.matchesSelector(selector)) {
-                    return true;
-                }
-            }
-
-            // It doesn't look like we can find a matching route right now.
-            return false;
-        }
-
-        public void updateDiscoveryRequest() {
-            // Combine all of the callback selectors and active scan flags.
-            boolean discover = false;
-            boolean activeScan = false;
-            MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
-            for (int i = mRouters.size(); --i >= 0; ) {
-                MediaRouter router = mRouters.get(i).get();
-                if (router == null) {
-                    mRouters.remove(i);
-                } else {
-                    final int count = router.mCallbackRecords.size();
-                    for (int j = 0; j < count; j++) {
-                        CallbackRecord callback = router.mCallbackRecords.get(j);
-                        builder.addSelector(callback.mSelector);
-                        if ((callback.mFlags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
-                            activeScan = true;
-                            discover = true; // perform active scan implies request discovery
-                        }
-                        if ((callback.mFlags & CALLBACK_FLAG_REQUEST_DISCOVERY) != 0) {
-                            if (!mLowRam) {
-                                discover = true;
-                            }
-                        }
-                        if ((callback.mFlags & CALLBACK_FLAG_FORCE_DISCOVERY) != 0) {
-                            discover = true;
-                        }
-                    }
-                }
-            }
-            MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY;
-
-            // Create a new discovery request.
-            if (mDiscoveryRequest != null
-                    && mDiscoveryRequest.getSelector().equals(selector)
-                    && mDiscoveryRequest.isActiveScan() == activeScan) {
-                return; // no change
-            }
-            if (selector.isEmpty() && !activeScan) {
-                // Discovery is not needed.
-                if (mDiscoveryRequest == null) {
-                    return; // no change
-                }
-                mDiscoveryRequest = null;
-            } else {
-                // Discovery is needed.
-                mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan);
-            }
-            if (DEBUG) {
-                Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest);
-            }
-            if (discover && !activeScan && mLowRam) {
-                Log.i(TAG, "Forcing passive route discovery on a low-RAM device, "
-                        + "system performance may be affected.  Please consider using "
-                        + "CALLBACK_FLAG_REQUEST_DISCOVERY instead of "
-                        + "CALLBACK_FLAG_FORCE_DISCOVERY.");
-            }
-
-            // Notify providers.
-            final int providerCount = mProviders.size();
-            for (int i = 0; i < providerCount; i++) {
-                mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest);
-            }
-        }
-
-        @Override
-        public void addProvider(MediaRouteProvider providerInstance) {
-            int index = findProviderInfo(providerInstance);
-            if (index < 0) {
-                // 1. Add the provider to the list.
-                ProviderInfo provider = new ProviderInfo(providerInstance);
-                mProviders.add(provider);
-                if (DEBUG) {
-                    Log.d(TAG, "Provider added: " + provider);
-                }
-                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider);
-                // 2. Create the provider's contents.
-                updateProviderContents(provider, providerInstance.getDescriptor());
-                // 3. Register the provider callback.
-                providerInstance.setCallback(mProviderCallback);
-                // 4. Set the discovery request.
-                providerInstance.setDiscoveryRequest(mDiscoveryRequest);
-            }
-        }
-
-        @Override
-        public void removeProvider(MediaRouteProvider providerInstance) {
-            int index = findProviderInfo(providerInstance);
-            if (index >= 0) {
-                // 1. Unregister the provider callback.
-                providerInstance.setCallback(null);
-                // 2. Clear the discovery request.
-                providerInstance.setDiscoveryRequest(null);
-                // 3. Delete the provider's contents.
-                ProviderInfo provider = mProviders.get(index);
-                updateProviderContents(provider, null);
-                // 4. Remove the provider from the list.
-                if (DEBUG) {
-                    Log.d(TAG, "Provider removed: " + provider);
-                }
-                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider);
-                mProviders.remove(index);
-            }
-        }
-
-        void updateProviderDescriptor(MediaRouteProvider providerInstance,
-                MediaRouteProviderDescriptor descriptor) {
-            int index = findProviderInfo(providerInstance);
-            if (index >= 0) {
-                // Update the provider's contents.
-                ProviderInfo provider = mProviders.get(index);
-                updateProviderContents(provider, descriptor);
-            }
-        }
-
-        private int findProviderInfo(MediaRouteProvider providerInstance) {
-            final int count = mProviders.size();
-            for (int i = 0; i < count; i++) {
-                if (mProviders.get(i).mProviderInstance == providerInstance) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        private void updateProviderContents(ProviderInfo provider,
-                MediaRouteProviderDescriptor providerDescriptor) {
-            if (provider.updateDescriptor(providerDescriptor)) {
-                // Update all existing routes and reorder them to match
-                // the order of their descriptors.
-                int targetIndex = 0;
-                boolean selectedRouteDescriptorChanged = false;
-                if (providerDescriptor != null) {
-                    if (providerDescriptor.isValid()) {
-                        final List<MediaRouteDescriptor> routeDescriptors =
-                                providerDescriptor.getRoutes();
-                        final int routeCount = routeDescriptors.size();
-                        // Updating route group's contents requires all member routes' information.
-                        // Add the groups to the lists and update them later.
-                        List<Pair<RouteInfo, MediaRouteDescriptor>> addedGroups = new ArrayList<>();
-                        List<Pair<RouteInfo, MediaRouteDescriptor>> updatedGroups =
-                                new ArrayList<>();
-                        for (int i = 0; i < routeCount; i++) {
-                            final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i);
-                            final String id = routeDescriptor.getId();
-                            final int sourceIndex = provider.findRouteByDescriptorId(id);
-                            if (sourceIndex < 0) {
-                                // 1. Add the route to the list.
-                                String uniqueId = assignRouteUniqueId(provider, id);
-                                boolean isGroup = routeDescriptor.getGroupMemberIds() != null;
-                                RouteInfo route = isGroup ? new RouteGroup(provider, id, uniqueId) :
-                                        new RouteInfo(provider, id, uniqueId);
-                                provider.mRoutes.add(targetIndex++, route);
-                                mRoutes.add(route);
-                                // 2. Create the route's contents.
-                                if (isGroup) {
-                                    addedGroups.add(new Pair<>(route, routeDescriptor));
-                                } else {
-                                    route.maybeUpdateDescriptor(routeDescriptor);
-                                    // 3. Notify clients about addition.
-                                    if (DEBUG) {
-                                        Log.d(TAG, "Route added: " + route);
-                                    }
-                                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
-                                }
-
-                            } else if (sourceIndex < targetIndex) {
-                                Log.w(TAG, "Ignoring route descriptor with duplicate id: "
-                                        + routeDescriptor);
-                            } else {
-                                // 1. Reorder the route within the list.
-                                RouteInfo route = provider.mRoutes.get(sourceIndex);
-                                Collections.swap(provider.mRoutes,
-                                        sourceIndex, targetIndex++);
-                                // 2. Update the route's contents.
-                                if (route instanceof RouteGroup) {
-                                    updatedGroups.add(new Pair<>(route, routeDescriptor));
-                                } else {
-                                    // 3. Notify clients about changes.
-                                    if (updateRouteDescriptorAndNotify(route, routeDescriptor)
-                                            != 0) {
-                                        if (route == mSelectedRoute) {
-                                            selectedRouteDescriptorChanged = true;
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                        // Update the new and/or existing groups.
-                        for (Pair<RouteInfo, MediaRouteDescriptor> pair : addedGroups) {
-                            RouteInfo route = pair.first;
-                            route.maybeUpdateDescriptor(pair.second);
-                            if (DEBUG) {
-                                Log.d(TAG, "Route added: " + route);
-                            }
-                            mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
-                        }
-                        for (Pair<RouteInfo, MediaRouteDescriptor> pair : updatedGroups) {
-                            RouteInfo route = pair.first;
-                            if (updateRouteDescriptorAndNotify(route, pair.second) != 0) {
-                                if (route == mSelectedRoute) {
-                                    selectedRouteDescriptorChanged = true;
-                                }
-                            }
-                        }
-                    } else {
-                        Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor);
-                    }
-                }
-
-                // Dispose all remaining routes that do not have matching descriptors.
-                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
-                    // 1. Delete the route's contents.
-                    RouteInfo route = provider.mRoutes.get(i);
-                    route.maybeUpdateDescriptor(null);
-                    // 2. Remove the route from the list.
-                    mRoutes.remove(route);
-                }
-
-                // Update the selected route if needed.
-                updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged);
-
-                // Now notify clients about routes that were removed.
-                // We do this after updating the selected route to ensure
-                // that the framework media router observes the new route
-                // selection before the removal since removing the currently
-                // selected route may have side-effects.
-                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
-                    RouteInfo route = provider.mRoutes.remove(i);
-                    if (DEBUG) {
-                        Log.d(TAG, "Route removed: " + route);
-                    }
-                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route);
-                }
-
-                // Notify provider changed.
-                if (DEBUG) {
-                    Log.d(TAG, "Provider changed: " + provider);
-                }
-                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider);
-            }
-        }
-
-        private int updateRouteDescriptorAndNotify(RouteInfo route,
-                MediaRouteDescriptor routeDescriptor) {
-            int changes = route.maybeUpdateDescriptor(routeDescriptor);
-            if (changes != 0) {
-                if ((changes & RouteInfo.CHANGE_GENERAL) != 0) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Route changed: " + route);
-                    }
-                    mCallbackHandler.post(
-                            CallbackHandler.MSG_ROUTE_CHANGED, route);
-                }
-                if ((changes & RouteInfo.CHANGE_VOLUME) != 0) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Route volume changed: " + route);
-                    }
-                    mCallbackHandler.post(
-                            CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route);
-                }
-                if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Route presentation display changed: "
-                                + route);
-                    }
-                    mCallbackHandler.post(CallbackHandler.
-                            MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route);
-                }
-            }
-            return changes;
-        }
-
-        private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) {
-            // Although route descriptor ids are unique within a provider, it's
-            // possible for there to be two providers with the same package name.
-            // Therefore we must dedupe the composite id.
-            String componentName = provider.getComponentName().flattenToShortString();
-            String uniqueId = componentName + ":" + routeDescriptorId;
-            if (findRouteByUniqueId(uniqueId) < 0) {
-                mUniqueIdMap.put(new Pair<>(componentName, routeDescriptorId), uniqueId);
-                return uniqueId;
-            }
-            Log.w(TAG, "Either " + routeDescriptorId + " isn't unique in " + componentName
-                    + " or we're trying to assign a unique ID for an already added route");
-            for (int i = 2; ; i++) {
-                String newUniqueId = String.format(Locale.US, "%s_%d", uniqueId, i);
-                if (findRouteByUniqueId(newUniqueId) < 0) {
-                    mUniqueIdMap.put(new Pair<>(componentName, routeDescriptorId), newUniqueId);
-                    return newUniqueId;
-                }
-            }
-        }
-
-        private int findRouteByUniqueId(String uniqueId) {
-            final int count = mRoutes.size();
-            for (int i = 0; i < count; i++) {
-                if (mRoutes.get(i).mUniqueId.equals(uniqueId)) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        private String getUniqueId(ProviderInfo provider, String routeDescriptorId) {
-            String componentName = provider.getComponentName().flattenToShortString();
-            return mUniqueIdMap.get(new Pair<>(componentName, routeDescriptorId));
-        }
-
-        private void updateSelectedRouteIfNeeded(boolean selectedRouteDescriptorChanged) {
-            // Update default route.
-            if (mDefaultRoute != null && !mDefaultRoute.isSelectable()) {
-                Log.i(TAG, "Clearing the default route because it "
-                        + "is no longer selectable: " + mDefaultRoute);
-                mDefaultRoute = null;
-            }
-            if (mDefaultRoute == null && !mRoutes.isEmpty()) {
-                for (RouteInfo route : mRoutes) {
-                    if (isSystemDefaultRoute(route) && route.isSelectable()) {
-                        mDefaultRoute = route;
-                        Log.i(TAG, "Found default route: " + mDefaultRoute);
-                        break;
-                    }
-                }
-            }
-
-            // Update bluetooth route.
-            if (mBluetoothRoute != null && !mBluetoothRoute.isSelectable()) {
-                Log.i(TAG, "Clearing the bluetooth route because it "
-                        + "is no longer selectable: " + mBluetoothRoute);
-                mBluetoothRoute = null;
-            }
-            if (mBluetoothRoute == null && !mRoutes.isEmpty()) {
-                for (RouteInfo route : mRoutes) {
-                    if (isSystemLiveAudioOnlyRoute(route) && route.isSelectable()) {
-                        mBluetoothRoute = route;
-                        Log.i(TAG, "Found bluetooth route: " + mBluetoothRoute);
-                        break;
-                    }
-                }
-            }
-
-            // Update selected route.
-            if (mSelectedRoute == null || !mSelectedRoute.isSelectable()) {
-                Log.i(TAG, "Unselecting the current route because it "
-                        + "is no longer selectable: " + mSelectedRoute);
-                setSelectedRouteInternal(chooseFallbackRoute(),
-                        MediaRouter.UNSELECT_REASON_UNKNOWN);
-            } else if (selectedRouteDescriptorChanged) {
-                // In case the selected route is a route group, select/unselect route controllers
-                // for the added/removed route members.
-                if (mSelectedRoute instanceof RouteGroup) {
-                    List<RouteInfo> routes = ((RouteGroup) mSelectedRoute).getRoutes();
-                    // Build a set of descriptor IDs for the new route group.
-                    Set<String> idSet = new HashSet<>();
-                    for (RouteInfo route : routes) {
-                        idSet.add(route.mDescriptorId);
-                    }
-                    // Unselect route controllers for the removed routes.
-                    Iterator<Map.Entry<String, RouteController>> iter =
-                            mRouteControllerMap.entrySet().iterator();
-                    while (iter.hasNext()) {
-                        Map.Entry<String, RouteController> entry = iter.next();
-                        if (!idSet.contains(entry.getKey())) {
-                            RouteController controller = entry.getValue();
-                            controller.onUnselect();
-                            controller.onRelease();
-                            iter.remove();
-                        }
-                    }
-                    // Select route controllers for the added routes.
-                    for (RouteInfo route : routes) {
-                        if (!mRouteControllerMap.containsKey(route.mDescriptorId)) {
-                            RouteController controller = route.getProviderInstance()
-                                    .onCreateRouteController(
-                                            route.mDescriptorId, mSelectedRoute.mDescriptorId);
-                            controller.onSelect();
-                            mRouteControllerMap.put(route.mDescriptorId, controller);
-                        }
-                    }
-                }
-                // Update the playback info because the properties of the route have changed.
-                updatePlaybackInfoFromSelectedRoute();
-            }
-        }
-
-        RouteInfo chooseFallbackRoute() {
-            // When the current route is removed or no longer selectable,
-            // we want to revert to a live audio route if there is
-            // one (usually Bluetooth A2DP).  Failing that, use
-            // the default route.
-            for (RouteInfo route : mRoutes) {
-                if (route != mDefaultRoute
-                        && isSystemLiveAudioOnlyRoute(route)
-                        && route.isSelectable()) {
-                    return route;
-                }
-            }
-            return mDefaultRoute;
-        }
-
-        private boolean isSystemLiveAudioOnlyRoute(RouteInfo route) {
-            return route.getProviderInstance() == mSystemProvider
-                    && route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
-                    && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
-        }
-
-        private boolean isSystemDefaultRoute(RouteInfo route) {
-            return route.getProviderInstance() == mSystemProvider
-                    && route.mDescriptorId.equals(
-                            SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
-        }
-
-        private void setSelectedRouteInternal(@NonNull RouteInfo route, int unselectReason) {
-            // TODO: Remove the following logging when no longer needed.
-            if (sGlobal == null || (mBluetoothRoute != null && route.isDefault())) {
-                final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
-                StringBuilder sb = new StringBuilder();
-                // callStack[3] is the caller of this method.
-                for (int i = 3; i < callStack.length; i++) {
-                    StackTraceElement caller = callStack[i];
-                    sb.append(caller.getClassName())
-                            .append(".")
-                            .append(caller.getMethodName())
-                            .append(":")
-                            .append(caller.getLineNumber())
-                            .append("  ");
-                }
-                if (sGlobal == null) {
-                    Log.w(TAG, "setSelectedRouteInternal is called while sGlobal is null: pkgName="
-                            + mApplicationContext.getPackageName() + ", callers=" + sb.toString());
-                } else {
-                    Log.w(TAG, "Default route is selected while a BT route is available: pkgName="
-                            + mApplicationContext.getPackageName() + ", callers=" + sb.toString());
-                }
-            }
-
-            if (mSelectedRoute != route) {
-                if (mSelectedRoute != null) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Route unselected: " + mSelectedRoute + " reason: "
-                                + unselectReason);
-                    }
-                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute,
-                            unselectReason);
-                    if (mSelectedRouteController != null) {
-                        mSelectedRouteController.onUnselect(unselectReason);
-                        mSelectedRouteController.onRelease();
-                        mSelectedRouteController = null;
-                    }
-                    if (!mRouteControllerMap.isEmpty()) {
-                        for (RouteController controller : mRouteControllerMap.values()) {
-                            controller.onUnselect(unselectReason);
-                            controller.onRelease();
-                        }
-                        mRouteControllerMap.clear();
-                    }
-                }
-
-                mSelectedRoute = route;
-                mSelectedRouteController = route.getProviderInstance().onCreateRouteController(
-                        route.mDescriptorId);
-                if (mSelectedRouteController != null) {
-                    mSelectedRouteController.onSelect();
-                }
-                if (DEBUG) {
-                    Log.d(TAG, "Route selected: " + mSelectedRoute);
-                }
-                mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute);
-
-                if (mSelectedRoute instanceof RouteGroup) {
-                    List<RouteInfo> routes = ((RouteGroup) mSelectedRoute).getRoutes();
-                    mRouteControllerMap.clear();
-                    for (RouteInfo r : routes) {
-                        RouteController controller =
-                                r.getProviderInstance().onCreateRouteController(
-                                        r.mDescriptorId, mSelectedRoute.mDescriptorId);
-                        controller.onSelect();
-                        mRouteControllerMap.put(r.mDescriptorId, controller);
-                    }
-                }
-
-                updatePlaybackInfoFromSelectedRoute();
-            }
-        }
-
-        @Override
-        public void onSystemRouteSelectedByDescriptorId(String id) {
-            // System route is selected, do not sync the route we selected before.
-            mCallbackHandler.removeMessages(CallbackHandler.MSG_ROUTE_SELECTED);
-            int providerIndex = findProviderInfo(mSystemProvider);
-            if (providerIndex >= 0) {
-                ProviderInfo provider = mProviders.get(providerIndex);
-                int routeIndex = provider.findRouteByDescriptorId(id);
-                if (routeIndex >= 0) {
-                    provider.mRoutes.get(routeIndex).select();
-                }
-            }
-        }
-
-        public void addRemoteControlClient(Object rcc) {
-            int index = findRemoteControlClientRecord(rcc);
-            if (index < 0) {
-                RemoteControlClientRecord record = new RemoteControlClientRecord(rcc);
-                mRemoteControlClients.add(record);
-            }
-        }
-
-        public void removeRemoteControlClient(Object rcc) {
-            int index = findRemoteControlClientRecord(rcc);
-            if (index >= 0) {
-                RemoteControlClientRecord record = mRemoteControlClients.remove(index);
-                record.disconnect();
-            }
-        }
-
-        public void setMediaSession(Object session) {
-            setMediaSessionRecord(session != null ? new MediaSessionRecord(session) : null);
-        }
-
-        public void setMediaSessionCompat(final MediaSessionCompat session) {
-            mCompatSession = session;
-            if (android.os.Build.VERSION.SDK_INT >= 21) {
-                setMediaSessionRecord(session != null ? new MediaSessionRecord(session) : null);
-            } else if (android.os.Build.VERSION.SDK_INT >= 14) {
-                if (mRccMediaSession != null) {
-                    removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
-                    mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener);
-                }
-                mRccMediaSession = session;
-                if (session != null) {
-                    session.addOnActiveChangeListener(mSessionActiveListener);
-                    if (session.isActive()) {
-                        addRemoteControlClient(session.getRemoteControlClient());
-                    }
-                }
-            }
-        }
-
-        private void setMediaSessionRecord(MediaSessionRecord mediaSessionRecord) {
-            if (mMediaSession != null) {
-                mMediaSession.clearVolumeHandling();
-            }
-            mMediaSession = mediaSessionRecord;
-            if (mediaSessionRecord != null) {
-                updatePlaybackInfoFromSelectedRoute();
-            }
-        }
-
-        public MediaSessionCompat.Token getMediaSessionToken() {
-            if (mMediaSession != null) {
-                return mMediaSession.getToken();
-            } else if (mCompatSession != null) {
-                return mCompatSession.getSessionToken();
-            }
-            return null;
-        }
-
-        private int findRemoteControlClientRecord(Object rcc) {
-            final int count = mRemoteControlClients.size();
-            for (int i = 0; i < count; i++) {
-                RemoteControlClientRecord record = mRemoteControlClients.get(i);
-                if (record.getRemoteControlClient() == rcc) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        private void updatePlaybackInfoFromSelectedRoute() {
-            if (mSelectedRoute != null) {
-                mPlaybackInfo.volume = mSelectedRoute.getVolume();
-                mPlaybackInfo.volumeMax = mSelectedRoute.getVolumeMax();
-                mPlaybackInfo.volumeHandling = mSelectedRoute.getVolumeHandling();
-                mPlaybackInfo.playbackStream = mSelectedRoute.getPlaybackStream();
-                mPlaybackInfo.playbackType = mSelectedRoute.getPlaybackType();
-
-                final int count = mRemoteControlClients.size();
-                for (int i = 0; i < count; i++) {
-                    RemoteControlClientRecord record = mRemoteControlClients.get(i);
-                    record.updatePlaybackInfo();
-                }
-                if (mMediaSession != null) {
-                    if (mSelectedRoute == getDefaultRoute()
-                            || mSelectedRoute == getBluetoothRoute()) {
-                        // Local route
-                        mMediaSession.clearVolumeHandling();
-                    } else {
-                        @VolumeProviderCompat.ControlType int controlType =
-                                VolumeProviderCompat.VOLUME_CONTROL_FIXED;
-                        if (mPlaybackInfo.volumeHandling
-                                == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
-                            controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE;
-                        }
-                        mMediaSession.configureVolume(controlType, mPlaybackInfo.volumeMax,
-                                mPlaybackInfo.volume);
-                    }
-                }
-            } else {
-                if (mMediaSession != null) {
-                    mMediaSession.clearVolumeHandling();
-                }
-            }
-        }
-
-        private final class ProviderCallback extends MediaRouteProvider.Callback {
-            ProviderCallback() {
-            }
-
-            @Override
-            public void onDescriptorChanged(MediaRouteProvider provider,
-                    MediaRouteProviderDescriptor descriptor) {
-                updateProviderDescriptor(provider, descriptor);
-            }
-        }
-
-        private final class MediaSessionRecord {
-            private final MediaSessionCompat mMsCompat;
-
-            private @VolumeProviderCompat.ControlType int mControlType;
-            private int mMaxVolume;
-            private VolumeProviderCompat mVpCompat;
-
-            public MediaSessionRecord(Object mediaSession) {
-                mMsCompat = MediaSessionCompat.fromMediaSession(mApplicationContext, mediaSession);
-            }
-
-            public MediaSessionRecord(MediaSessionCompat mediaSessionCompat) {
-                mMsCompat = mediaSessionCompat;
-            }
-
-            public void configureVolume(@VolumeProviderCompat.ControlType int controlType,
-                    int max, int current) {
-                if (mVpCompat != null && controlType == mControlType && max == mMaxVolume) {
-                    // If we haven't changed control type or max just set the
-                    // new current volume
-                    mVpCompat.setCurrentVolume(current);
-                } else {
-                    // Otherwise create a new provider and update
-                    mVpCompat = new VolumeProviderCompat(controlType, max, current) {
-                        @Override
-                        public void onSetVolumeTo(final int volume) {
-                            mCallbackHandler.post(new Runnable() {
-                                @Override
-                                public void run() {
-                                    if (mSelectedRoute != null) {
-                                        mSelectedRoute.requestSetVolume(volume);
-                                    }
-                                }
-                            });
-                        }
-
-                        @Override
-                        public void onAdjustVolume(final int direction) {
-                            mCallbackHandler.post(new Runnable() {
-                                @Override
-                                public void run() {
-                                    if (mSelectedRoute != null) {
-                                        mSelectedRoute.requestUpdateVolume(direction);
-                                    }
-                                }
-                            });
-                        }
-                    };
-                    mMsCompat.setPlaybackToRemote(mVpCompat);
-                }
-            }
-
-            public void clearVolumeHandling() {
-                mMsCompat.setPlaybackToLocal(mPlaybackInfo.playbackStream);
-                mVpCompat = null;
-            }
-
-            public MediaSessionCompat.Token getToken() {
-                return mMsCompat.getSessionToken();
-            }
-        }
-
-        private final class RemoteControlClientRecord
-                implements RemoteControlClientCompat.VolumeCallback {
-            private final RemoteControlClientCompat mRccCompat;
-            private boolean mDisconnected;
-
-            public RemoteControlClientRecord(Object rcc) {
-                mRccCompat = RemoteControlClientCompat.obtain(mApplicationContext, rcc);
-                mRccCompat.setVolumeCallback(this);
-                updatePlaybackInfo();
-            }
-
-            public Object getRemoteControlClient() {
-                return mRccCompat.getRemoteControlClient();
-            }
-
-            public void disconnect() {
-                mDisconnected = true;
-                mRccCompat.setVolumeCallback(null);
-            }
-
-            public void updatePlaybackInfo() {
-                mRccCompat.setPlaybackInfo(mPlaybackInfo);
-            }
-
-            @Override
-            public void onVolumeSetRequest(int volume) {
-                if (!mDisconnected && mSelectedRoute != null) {
-                    mSelectedRoute.requestSetVolume(volume);
-                }
-            }
-
-            @Override
-            public void onVolumeUpdateRequest(int direction) {
-                if (!mDisconnected && mSelectedRoute != null) {
-                    mSelectedRoute.requestUpdateVolume(direction);
-                }
-            }
-        }
-
-        private final class CallbackHandler extends Handler {
-            private final ArrayList<CallbackRecord> mTempCallbackRecords =
-                    new ArrayList<CallbackRecord>();
-
-            private static final int MSG_TYPE_MASK = 0xff00;
-            private static final int MSG_TYPE_ROUTE = 0x0100;
-            private static final int MSG_TYPE_PROVIDER = 0x0200;
-
-            public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1;
-            public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2;
-            public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3;
-            public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4;
-            public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5;
-            public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6;
-            public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7;
-
-            public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1;
-            public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2;
-            public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3;
-
-            CallbackHandler() {
-            }
-
-            public void post(int msg, Object obj) {
-                obtainMessage(msg, obj).sendToTarget();
-            }
-
-            public void post(int msg, Object obj, int arg) {
-                Message message = obtainMessage(msg, obj);
-                message.arg1 = arg;
-                message.sendToTarget();
-            }
-
-            @Override
-            public void handleMessage(Message msg) {
-                final int what = msg.what;
-                final Object obj = msg.obj;
-                final int arg = msg.arg1;
-
-                if (what == MSG_ROUTE_CHANGED
-                        && getSelectedRoute().getId().equals(((RouteInfo) obj).getId())) {
-                    updateSelectedRouteIfNeeded(true);
-                }
-
-                // Synchronize state with the system media router.
-                syncWithSystemProvider(what, obj);
-
-                // Invoke all registered callbacks.
-                // Build a list of callbacks before invoking them in case callbacks
-                // are added or removed during dispatch.
-                try {
-                    for (int i = mRouters.size(); --i >= 0; ) {
-                        MediaRouter router = mRouters.get(i).get();
-                        if (router == null) {
-                            mRouters.remove(i);
-                        } else {
-                            mTempCallbackRecords.addAll(router.mCallbackRecords);
-                        }
-                    }
-
-                    final int callbackCount = mTempCallbackRecords.size();
-                    for (int i = 0; i < callbackCount; i++) {
-                        invokeCallback(mTempCallbackRecords.get(i), what, obj, arg);
-                    }
-                } finally {
-                    mTempCallbackRecords.clear();
-                }
-            }
-
-            private void syncWithSystemProvider(int what, Object obj) {
-                switch (what) {
-                    case MSG_ROUTE_ADDED:
-                        mSystemProvider.onSyncRouteAdded((RouteInfo) obj);
-                        break;
-                    case MSG_ROUTE_REMOVED:
-                        mSystemProvider.onSyncRouteRemoved((RouteInfo) obj);
-                        break;
-                    case MSG_ROUTE_CHANGED:
-                        mSystemProvider.onSyncRouteChanged((RouteInfo) obj);
-                        break;
-                    case MSG_ROUTE_SELECTED:
-                        mSystemProvider.onSyncRouteSelected((RouteInfo) obj);
-                        break;
-                }
-            }
-
-            private void invokeCallback(CallbackRecord record, int what, Object obj, int arg) {
-                final MediaRouter router = record.mRouter;
-                final MediaRouter.Callback callback = record.mCallback;
-                switch (what & MSG_TYPE_MASK) {
-                    case MSG_TYPE_ROUTE: {
-                        final RouteInfo route = (RouteInfo)obj;
-                        if (!record.filterRouteEvent(route)) {
-                            break;
-                        }
-                        switch (what) {
-                            case MSG_ROUTE_ADDED:
-                                callback.onRouteAdded(router, route);
-                                break;
-                            case MSG_ROUTE_REMOVED:
-                                callback.onRouteRemoved(router, route);
-                                break;
-                            case MSG_ROUTE_CHANGED:
-                                callback.onRouteChanged(router, route);
-                                break;
-                            case MSG_ROUTE_VOLUME_CHANGED:
-                                callback.onRouteVolumeChanged(router, route);
-                                break;
-                            case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED:
-                                callback.onRoutePresentationDisplayChanged(router, route);
-                                break;
-                            case MSG_ROUTE_SELECTED:
-                                callback.onRouteSelected(router, route);
-                                break;
-                            case MSG_ROUTE_UNSELECTED:
-                                callback.onRouteUnselected(router, route, arg);
-                                break;
-                        }
-                        break;
-                    }
-                    case MSG_TYPE_PROVIDER: {
-                        final ProviderInfo provider = (ProviderInfo)obj;
-                        switch (what) {
-                            case MSG_PROVIDER_ADDED:
-                                callback.onProviderAdded(router, provider);
-                                break;
-                            case MSG_PROVIDER_REMOVED:
-                                callback.onProviderRemoved(router, provider);
-                                break;
-                            case MSG_PROVIDER_CHANGED:
-                                callback.onProviderChanged(router, provider);
-                                break;
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java
deleted file mode 100644
index 0e7514c..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/MediaSessionStatus.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.app.PendingIntent;
-import android.os.Bundle;
-import android.os.SystemClock;
-
-import androidx.core.util.TimeUtils;
-
-/**
- * Describes the playback status of a media session.
- * <p>
- * This class is part of the remote playback protocol described by the
- * {@link MediaControlIntent MediaControlIntent} class.
- * </p><p>
- * When a media session is created, it is initially in the
- * {@link #SESSION_STATE_ACTIVE active} state.  When the media session ends
- * normally, it transitions to the {@link #SESSION_STATE_ENDED ended} state.
- * If the media session is invalidated due to another session forcibly taking
- * control of the route, then it transitions to the
- * {@link #SESSION_STATE_INVALIDATED invalidated} state.
- * Refer to the documentation of each state for an explanation of its meaning.
- * </p><p>
- * To monitor session status, the application should supply a {@link PendingIntent} to use as the
- * {@link MediaControlIntent#EXTRA_SESSION_STATUS_UPDATE_RECEIVER session status update receiver}
- * for a given {@link MediaControlIntent#ACTION_START_SESSION session start request}.
- * </p><p>
- * This object is immutable once created using a {@link Builder} instance.
- * </p>
- */
-public final class MediaSessionStatus {
-    static final String KEY_TIMESTAMP = "timestamp";
-    static final String KEY_SESSION_STATE = "sessionState";
-    static final String KEY_QUEUE_PAUSED = "queuePaused";
-    static final String KEY_EXTRAS = "extras";
-
-    final Bundle mBundle;
-
-    /**
-     * Session state: Active.
-     * <p>
-     * Indicates that the media session is active and in control of the route.
-     * </p>
-     */
-    public static final int SESSION_STATE_ACTIVE = 0;
-
-    /**
-     * Session state: Ended.
-     * <p>
-     * Indicates that the media session was ended normally using the
-     * {@link MediaControlIntent#ACTION_END_SESSION end session} action.
-     * </p><p>
-     * A terminated media session cannot be used anymore.  To play more media, the
-     * application must start a new session.
-     * </p>
-     */
-    public static final int SESSION_STATE_ENDED = 1;
-
-    /**
-     * Session state: Invalidated.
-     * <p>
-     * Indicates that the media session was invalidated involuntarily due to
-     * another session taking control of the route.
-     * </p><p>
-     * An invalidated media session cannot be used anymore.  To play more media, the
-     * application must start a new session.
-     * </p>
-     */
-    public static final int SESSION_STATE_INVALIDATED = 2;
-
-    MediaSessionStatus(Bundle bundle) {
-        mBundle = bundle;
-    }
-
-    /**
-     * Gets the timestamp associated with the status information in
-     * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
-     *
-     * @return The status timestamp in the {@link SystemClock#elapsedRealtime()} time base.
-     */
-    public long getTimestamp() {
-        return mBundle.getLong(KEY_TIMESTAMP);
-    }
-
-    /**
-     * Gets the session state.
-     *
-     * @return The session state.  One of {@link #SESSION_STATE_ACTIVE},
-     * {@link #SESSION_STATE_ENDED}, or {@link #SESSION_STATE_INVALIDATED}.
-     */
-    public int getSessionState() {
-        return mBundle.getInt(KEY_SESSION_STATE, SESSION_STATE_INVALIDATED);
-    }
-
-    /**
-     * Returns true if the session's queue is paused.
-     *
-     * @return True if the session's queue is paused.
-     */
-    public boolean isQueuePaused() {
-        return mBundle.getBoolean(KEY_QUEUE_PAUSED);
-    }
-
-    /**
-     * Gets a bundle of extras for this status object.
-     * The extras will be ignored by the media router but they may be used
-     * by applications.
-     */
-    public Bundle getExtras() {
-        return mBundle.getBundle(KEY_EXTRAS);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("MediaSessionStatus{ ");
-        result.append("timestamp=");
-        TimeUtils.formatDuration(SystemClock.elapsedRealtime() - getTimestamp(), result);
-        result.append(" ms ago");
-        result.append(", sessionState=").append(sessionStateToString(getSessionState()));
-        result.append(", queuePaused=").append(isQueuePaused());
-        result.append(", extras=").append(getExtras());
-        result.append(" }");
-        return result.toString();
-    }
-
-    private static String sessionStateToString(int sessionState) {
-        switch (sessionState) {
-            case SESSION_STATE_ACTIVE:
-                return "active";
-            case SESSION_STATE_ENDED:
-                return "ended";
-            case SESSION_STATE_INVALIDATED:
-                return "invalidated";
-        }
-        return Integer.toString(sessionState);
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaSessionStatus fromBundle(Bundle bundle) {
-        return bundle != null ? new MediaSessionStatus(bundle) : null;
-    }
-
-    /**
-     * Builder for {@link MediaSessionStatus media session status objects}.
-     */
-    public static final class Builder {
-        private final Bundle mBundle;
-
-        /**
-         * Creates a media session status builder using the current time as the
-         * reference timestamp.
-         *
-         * @param sessionState The session state.
-         */
-        public Builder(int sessionState) {
-            mBundle = new Bundle();
-            setTimestamp(SystemClock.elapsedRealtime());
-            setSessionState(sessionState);
-        }
-
-        /**
-         * Creates a media session status builder whose initial contents are
-         * copied from an existing status.
-         */
-        public Builder(MediaSessionStatus status) {
-            if (status == null) {
-                throw new IllegalArgumentException("status must not be null");
-            }
-
-            mBundle = new Bundle(status.mBundle);
-        }
-
-        /**
-         * Sets the timestamp associated with the status information in
-         * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
-         */
-        public Builder setTimestamp(long elapsedRealtimeTimestamp) {
-            mBundle.putLong(KEY_TIMESTAMP, elapsedRealtimeTimestamp);
-            return this;
-        }
-
-        /**
-         * Sets the session state.
-         */
-        public Builder setSessionState(int sessionState) {
-            mBundle.putInt(KEY_SESSION_STATE, sessionState);
-            return this;
-        }
-
-        /**
-         * Sets whether the queue is paused.
-         */
-        public Builder setQueuePaused(boolean queuePaused) {
-            mBundle.putBoolean(KEY_QUEUE_PAUSED, queuePaused);
-            return this;
-        }
-
-        /**
-         * Sets a bundle of extras for this status object.
-         * The extras will be ignored by the media router but they may be used
-         * by applications.
-         */
-        public Builder setExtras(Bundle extras) {
-            mBundle.putBundle(KEY_EXTRAS, extras);
-            return this;
-        }
-
-        /**
-         * Builds the {@link MediaSessionStatus media session status object}.
-         */
-        public MediaSessionStatus build() {
-            return new MediaSessionStatus(mBundle);
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java
deleted file mode 100644
index eacf1c8..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProvider.java
+++ /dev/null
@@ -1,746 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_ID;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_DATA_ROUTE_LIBRARY_GROUP;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_DATA_UNSELECT_REASON;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_DATA_VOLUME;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_CREATE_ROUTE_CONTROLLER;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.CLIENT_MSG_REGISTER;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_ROUTE_CONTROL_REQUEST;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_SELECT_ROUTE;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_SET_DISCOVERY_REQUEST;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_SET_ROUTE_VOLUME;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_UNREGISTER;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_UNSELECT_ROUTE;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_UPDATE_ROUTE_VOLUME;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .CLIENT_VERSION_CURRENT;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_DATA_ERROR;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_CONTROL_REQUEST_FAILED;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_DESCRIPTOR_CHANGED;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_GENERIC_FAILURE;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_GENERIC_SUCCESS;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_REGISTERED;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol.SERVICE_VERSION_1;
-import static com.android.support.mediarouter.media.MediaRouteProviderProtocol
-        .isValidRemoteMessenger;
-
-import android.annotation.NonNull;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.support.mediarouter.media.MediaRouter.ControlRequestCallback;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Maintains a connection to a particular media route provider service.
- */
-final class RegisteredMediaRouteProvider extends MediaRouteProvider
-        implements ServiceConnection {
-    static final String TAG = "MediaRouteProviderProxy";  // max. 23 chars
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private final ComponentName mComponentName;
-    final PrivateHandler mPrivateHandler;
-    private final ArrayList<Controller> mControllers = new ArrayList<Controller>();
-
-    private boolean mStarted;
-    private boolean mBound;
-    private Connection mActiveConnection;
-    private boolean mConnectionReady;
-
-    public RegisteredMediaRouteProvider(Context context, ComponentName componentName) {
-        super(context, new ProviderMetadata(componentName));
-
-        mComponentName = componentName;
-        mPrivateHandler = new PrivateHandler();
-    }
-
-    @Override
-    public RouteController onCreateRouteController(@NonNull String routeId) {
-        if (routeId == null) {
-            throw new IllegalArgumentException("routeId cannot be null");
-        }
-        return createRouteController(routeId, null);
-    }
-
-    @Override
-    public RouteController onCreateRouteController(
-            @NonNull String routeId, @NonNull String routeGroupId) {
-        if (routeId == null) {
-            throw new IllegalArgumentException("routeId cannot be null");
-        }
-        if (routeGroupId == null) {
-            throw new IllegalArgumentException("routeGroupId cannot be null");
-        }
-        return createRouteController(routeId, routeGroupId);
-    }
-
-    @Override
-    public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
-        if (mConnectionReady) {
-            mActiveConnection.setDiscoveryRequest(request);
-        }
-        updateBinding();
-    }
-
-    @Override
-    public void onServiceConnected(ComponentName name, IBinder service) {
-        if (DEBUG) {
-            Log.d(TAG, this + ": Connected");
-        }
-
-        if (mBound) {
-            disconnect();
-
-            Messenger messenger = (service != null ? new Messenger(service) : null);
-            if (isValidRemoteMessenger(messenger)) {
-                Connection connection = new Connection(messenger);
-                if (connection.register()) {
-                    mActiveConnection = connection;
-                } else {
-                    if (DEBUG) {
-                        Log.d(TAG, this + ": Registration failed");
-                    }
-                }
-            } else {
-                Log.e(TAG, this + ": Service returned invalid messenger binder");
-            }
-        }
-    }
-
-    @Override
-    public void onServiceDisconnected(ComponentName name) {
-        if (DEBUG) {
-            Log.d(TAG, this + ": Service disconnected");
-        }
-        disconnect();
-    }
-
-    @Override
-    public String toString() {
-        return "Service connection " + mComponentName.flattenToShortString();
-    }
-
-    public boolean hasComponentName(String packageName, String className) {
-        return mComponentName.getPackageName().equals(packageName)
-                && mComponentName.getClassName().equals(className);
-    }
-
-    public void start() {
-        if (!mStarted) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Starting");
-            }
-
-            mStarted = true;
-            updateBinding();
-        }
-    }
-
-    public void stop() {
-        if (mStarted) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Stopping");
-            }
-
-            mStarted = false;
-            updateBinding();
-        }
-    }
-
-    public void rebindIfDisconnected() {
-        if (mActiveConnection == null && shouldBind()) {
-            unbind();
-            bind();
-        }
-    }
-
-    private void updateBinding() {
-        if (shouldBind()) {
-            bind();
-        } else {
-            unbind();
-        }
-    }
-
-    private boolean shouldBind() {
-        if (mStarted) {
-            // Bind whenever there is a discovery request.
-            if (getDiscoveryRequest() != null) {
-                return true;
-            }
-
-            // Bind whenever the application has an active route controller.
-            // This means that one of this provider's routes is selected.
-            if (!mControllers.isEmpty()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void bind() {
-        if (!mBound) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Binding");
-            }
-
-            Intent service = new Intent(MediaRouteProviderProtocol.SERVICE_INTERFACE);
-            service.setComponent(mComponentName);
-            try {
-                mBound = getContext().bindService(service, this, Context.BIND_AUTO_CREATE);
-                if (!mBound && DEBUG) {
-                    Log.d(TAG, this + ": Bind failed");
-                }
-            } catch (SecurityException ex) {
-                if (DEBUG) {
-                    Log.d(TAG, this + ": Bind failed", ex);
-                }
-            }
-        }
-    }
-
-    private void unbind() {
-        if (mBound) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Unbinding");
-            }
-
-            mBound = false;
-            disconnect();
-            getContext().unbindService(this);
-        }
-    }
-
-    private RouteController createRouteController(String routeId, String routeGroupId) {
-        MediaRouteProviderDescriptor descriptor = getDescriptor();
-        if (descriptor != null) {
-            List<MediaRouteDescriptor> routes = descriptor.getRoutes();
-            final int count = routes.size();
-            for (int i = 0; i < count; i++) {
-                final MediaRouteDescriptor route = routes.get(i);
-                if (route.getId().equals(routeId)) {
-                    Controller controller = new Controller(routeId, routeGroupId);
-                    mControllers.add(controller);
-                    if (mConnectionReady) {
-                        controller.attachConnection(mActiveConnection);
-                    }
-                    updateBinding();
-                    return controller;
-                }
-            }
-        }
-        return null;
-    }
-
-    void onConnectionReady(Connection connection) {
-        if (mActiveConnection == connection) {
-            mConnectionReady = true;
-            attachControllersToConnection();
-
-            MediaRouteDiscoveryRequest request = getDiscoveryRequest();
-            if (request != null) {
-                mActiveConnection.setDiscoveryRequest(request);
-            }
-        }
-    }
-
-    void onConnectionDied(Connection connection) {
-        if (mActiveConnection == connection) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Service connection died");
-            }
-            disconnect();
-        }
-    }
-
-    void onConnectionError(Connection connection, String error) {
-        if (mActiveConnection == connection) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Service connection error - " + error);
-            }
-            unbind();
-        }
-    }
-
-    void onConnectionDescriptorChanged(Connection connection,
-            MediaRouteProviderDescriptor descriptor) {
-        if (mActiveConnection == connection) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Descriptor changed, descriptor=" + descriptor);
-            }
-            setDescriptor(descriptor);
-        }
-    }
-
-    private void disconnect() {
-        if (mActiveConnection != null) {
-            setDescriptor(null);
-            mConnectionReady = false;
-            detachControllersFromConnection();
-            mActiveConnection.dispose();
-            mActiveConnection = null;
-        }
-    }
-
-    void onControllerReleased(Controller controller) {
-        mControllers.remove(controller);
-        controller.detachConnection();
-        updateBinding();
-    }
-
-    private void attachControllersToConnection() {
-        int count = mControllers.size();
-        for (int i = 0; i < count; i++) {
-            mControllers.get(i).attachConnection(mActiveConnection);
-        }
-    }
-
-    private void detachControllersFromConnection() {
-        int count = mControllers.size();
-        for (int i = 0; i < count; i++) {
-            mControllers.get(i).detachConnection();
-        }
-    }
-
-    private final class Controller extends RouteController {
-        private final String mRouteId;
-        private final String mRouteGroupId;
-
-        private boolean mSelected;
-        private int mPendingSetVolume = -1;
-        private int mPendingUpdateVolumeDelta;
-
-        private Connection mConnection;
-        private int mControllerId;
-
-        public Controller(String routeId, String routeGroupId) {
-            mRouteId = routeId;
-            mRouteGroupId = routeGroupId;
-        }
-
-        public void attachConnection(Connection connection) {
-            mConnection = connection;
-            mControllerId = connection.createRouteController(mRouteId, mRouteGroupId);
-            if (mSelected) {
-                connection.selectRoute(mControllerId);
-                if (mPendingSetVolume >= 0) {
-                    connection.setVolume(mControllerId, mPendingSetVolume);
-                    mPendingSetVolume = -1;
-                }
-                if (mPendingUpdateVolumeDelta != 0) {
-                    connection.updateVolume(mControllerId, mPendingUpdateVolumeDelta);
-                    mPendingUpdateVolumeDelta = 0;
-                }
-            }
-        }
-
-        public void detachConnection() {
-            if (mConnection != null) {
-                mConnection.releaseRouteController(mControllerId);
-                mConnection = null;
-                mControllerId = 0;
-            }
-        }
-
-        @Override
-        public void onRelease() {
-            onControllerReleased(this);
-        }
-
-        @Override
-        public void onSelect() {
-            mSelected = true;
-            if (mConnection != null) {
-                mConnection.selectRoute(mControllerId);
-            }
-        }
-
-        @Override
-        public void onUnselect() {
-            onUnselect(MediaRouter.UNSELECT_REASON_UNKNOWN);
-        }
-
-        @Override
-        public void onUnselect(int reason) {
-            mSelected = false;
-            if (mConnection != null) {
-                mConnection.unselectRoute(mControllerId, reason);
-            }
-        }
-
-        @Override
-        public void onSetVolume(int volume) {
-            if (mConnection != null) {
-                mConnection.setVolume(mControllerId, volume);
-            } else {
-                mPendingSetVolume = volume;
-                mPendingUpdateVolumeDelta = 0;
-            }
-        }
-
-        @Override
-        public void onUpdateVolume(int delta) {
-            if (mConnection != null) {
-                mConnection.updateVolume(mControllerId, delta);
-            } else {
-                mPendingUpdateVolumeDelta += delta;
-            }
-        }
-
-        @Override
-        public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
-            if (mConnection != null) {
-                return mConnection.sendControlRequest(mControllerId, intent, callback);
-            }
-            return false;
-        }
-    }
-
-    private final class Connection implements DeathRecipient {
-        private final Messenger mServiceMessenger;
-        private final ReceiveHandler mReceiveHandler;
-        private final Messenger mReceiveMessenger;
-
-        private int mNextRequestId = 1;
-        private int mNextControllerId = 1;
-        private int mServiceVersion; // non-zero when registration complete
-
-        private int mPendingRegisterRequestId;
-        private final SparseArray<ControlRequestCallback> mPendingCallbacks =
-                new SparseArray<ControlRequestCallback>();
-
-        public Connection(Messenger serviceMessenger) {
-            mServiceMessenger = serviceMessenger;
-            mReceiveHandler = new ReceiveHandler(this);
-            mReceiveMessenger = new Messenger(mReceiveHandler);
-        }
-
-        public boolean register() {
-            mPendingRegisterRequestId = mNextRequestId++;
-            if (!sendRequest(CLIENT_MSG_REGISTER,
-                    mPendingRegisterRequestId,
-                    CLIENT_VERSION_CURRENT, null, null)) {
-                return false;
-            }
-
-            try {
-                mServiceMessenger.getBinder().linkToDeath(this, 0);
-                return true;
-            } catch (RemoteException ex) {
-                binderDied();
-            }
-            return false;
-        }
-
-        public void dispose() {
-            sendRequest(CLIENT_MSG_UNREGISTER, 0, 0, null, null);
-            mReceiveHandler.dispose();
-            mServiceMessenger.getBinder().unlinkToDeath(this, 0);
-
-            mPrivateHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    failPendingCallbacks();
-                }
-            });
-        }
-
-        void failPendingCallbacks() {
-            int count = 0;
-            for (int i = 0; i < mPendingCallbacks.size(); i++) {
-                mPendingCallbacks.valueAt(i).onError(null, null);
-            }
-            mPendingCallbacks.clear();
-        }
-
-        public boolean onGenericFailure(int requestId) {
-            if (requestId == mPendingRegisterRequestId) {
-                mPendingRegisterRequestId = 0;
-                onConnectionError(this, "Registration failed");
-            }
-            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
-            if (callback != null) {
-                mPendingCallbacks.remove(requestId);
-                callback.onError(null, null);
-            }
-            return true;
-        }
-
-        public boolean onGenericSuccess(int requestId) {
-            return true;
-        }
-
-        public boolean onRegistered(int requestId, int serviceVersion,
-                Bundle descriptorBundle) {
-            if (mServiceVersion == 0
-                    && requestId == mPendingRegisterRequestId
-                    && serviceVersion >= SERVICE_VERSION_1) {
-                mPendingRegisterRequestId = 0;
-                mServiceVersion = serviceVersion;
-                onConnectionDescriptorChanged(this,
-                        MediaRouteProviderDescriptor.fromBundle(descriptorBundle));
-                onConnectionReady(this);
-                return true;
-            }
-            return false;
-        }
-
-        public boolean onDescriptorChanged(Bundle descriptorBundle) {
-            if (mServiceVersion != 0) {
-                onConnectionDescriptorChanged(this,
-                        MediaRouteProviderDescriptor.fromBundle(descriptorBundle));
-                return true;
-            }
-            return false;
-        }
-
-        public boolean onControlRequestSucceeded(int requestId, Bundle data) {
-            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
-            if (callback != null) {
-                mPendingCallbacks.remove(requestId);
-                callback.onResult(data);
-                return true;
-            }
-            return false;
-        }
-
-        public boolean onControlRequestFailed(int requestId, String error, Bundle data) {
-            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
-            if (callback != null) {
-                mPendingCallbacks.remove(requestId);
-                callback.onError(error, data);
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public void binderDied() {
-            mPrivateHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    onConnectionDied(Connection.this);
-                }
-            });
-        }
-
-        public int createRouteController(String routeId, String routeGroupId) {
-            int controllerId = mNextControllerId++;
-            Bundle data = new Bundle();
-            data.putString(CLIENT_DATA_ROUTE_ID, routeId);
-            data.putString(CLIENT_DATA_ROUTE_LIBRARY_GROUP, routeGroupId);
-            sendRequest(CLIENT_MSG_CREATE_ROUTE_CONTROLLER,
-                    mNextRequestId++, controllerId, null, data);
-            return controllerId;
-        }
-
-        public void releaseRouteController(int controllerId) {
-            sendRequest(CLIENT_MSG_RELEASE_ROUTE_CONTROLLER,
-                    mNextRequestId++, controllerId, null, null);
-        }
-
-        public void selectRoute(int controllerId) {
-            sendRequest(CLIENT_MSG_SELECT_ROUTE,
-                    mNextRequestId++, controllerId, null, null);
-        }
-
-        public void unselectRoute(int controllerId, int reason) {
-            Bundle extras = new Bundle();
-            extras.putInt(CLIENT_DATA_UNSELECT_REASON, reason);
-            sendRequest(CLIENT_MSG_UNSELECT_ROUTE,
-                    mNextRequestId++, controllerId, null, extras);
-        }
-
-        public void setVolume(int controllerId, int volume) {
-            Bundle data = new Bundle();
-            data.putInt(CLIENT_DATA_VOLUME, volume);
-            sendRequest(CLIENT_MSG_SET_ROUTE_VOLUME,
-                    mNextRequestId++, controllerId, null, data);
-        }
-
-        public void updateVolume(int controllerId, int delta) {
-            Bundle data = new Bundle();
-            data.putInt(CLIENT_DATA_VOLUME, delta);
-            sendRequest(CLIENT_MSG_UPDATE_ROUTE_VOLUME,
-                    mNextRequestId++, controllerId, null, data);
-        }
-
-        public boolean sendControlRequest(int controllerId, Intent intent,
-                ControlRequestCallback callback) {
-            int requestId = mNextRequestId++;
-            if (sendRequest(CLIENT_MSG_ROUTE_CONTROL_REQUEST,
-                    requestId, controllerId, intent, null)) {
-                if (callback != null) {
-                    mPendingCallbacks.put(requestId, callback);
-                }
-                return true;
-            }
-            return false;
-        }
-
-        public void setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
-            sendRequest(CLIENT_MSG_SET_DISCOVERY_REQUEST,
-                    mNextRequestId++, 0, request != null ? request.asBundle() : null, null);
-        }
-
-        private boolean sendRequest(int what, int requestId, int arg, Object obj, Bundle data) {
-            Message msg = Message.obtain();
-            msg.what = what;
-            msg.arg1 = requestId;
-            msg.arg2 = arg;
-            msg.obj = obj;
-            msg.setData(data);
-            msg.replyTo = mReceiveMessenger;
-            try {
-                mServiceMessenger.send(msg);
-                return true;
-            } catch (DeadObjectException ex) {
-                // The service died.
-            } catch (RemoteException ex) {
-                if (what != CLIENT_MSG_UNREGISTER) {
-                    Log.e(TAG, "Could not send message to service.", ex);
-                }
-            }
-            return false;
-        }
-    }
-
-    private static final class PrivateHandler extends Handler {
-        PrivateHandler() {
-        }
-    }
-
-    /**
-     * Handler that receives messages from the server.
-     * <p>
-     * This inner class is static and only retains a weak reference to the connection
-     * to prevent the client from being leaked in case the service is holding an
-     * active reference to the client's messenger.
-     * </p><p>
-     * This handler should not be used to handle any messages other than those
-     * that come from the service.
-     * </p>
-     */
-    private static final class ReceiveHandler extends Handler {
-        private final WeakReference<Connection> mConnectionRef;
-
-        public ReceiveHandler(Connection connection) {
-            mConnectionRef = new WeakReference<Connection>(connection);
-        }
-
-        public void dispose() {
-            mConnectionRef.clear();
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            Connection connection = mConnectionRef.get();
-            if (connection != null) {
-                final int what = msg.what;
-                final int requestId = msg.arg1;
-                final int arg = msg.arg2;
-                final Object obj = msg.obj;
-                final Bundle data = msg.peekData();
-                if (!processMessage(connection, what, requestId, arg, obj, data)) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Unhandled message from server: " + msg);
-                    }
-                }
-            }
-        }
-
-        private boolean processMessage(Connection connection,
-                int what, int requestId, int arg, Object obj, Bundle data) {
-            switch (what) {
-                case SERVICE_MSG_GENERIC_FAILURE:
-                    connection.onGenericFailure(requestId);
-                    return true;
-
-                case SERVICE_MSG_GENERIC_SUCCESS:
-                    connection.onGenericSuccess(requestId);
-                    return true;
-
-                case SERVICE_MSG_REGISTERED:
-                    if (obj == null || obj instanceof Bundle) {
-                        return connection.onRegistered(requestId, arg, (Bundle)obj);
-                    }
-                    break;
-
-                case SERVICE_MSG_DESCRIPTOR_CHANGED:
-                    if (obj == null || obj instanceof Bundle) {
-                        return connection.onDescriptorChanged((Bundle)obj);
-                    }
-                    break;
-
-                case SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED:
-                    if (obj == null || obj instanceof Bundle) {
-                        return connection.onControlRequestSucceeded(
-                                requestId, (Bundle)obj);
-                    }
-                    break;
-
-                case SERVICE_MSG_CONTROL_REQUEST_FAILED:
-                    if (obj == null || obj instanceof Bundle) {
-                        String error = (data == null ? null :
-                                data.getString(SERVICE_DATA_ERROR));
-                        return connection.onControlRequestFailed(
-                                requestId, error, (Bundle)obj);
-                    }
-                    break;
-            }
-            return false;
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProviderWatcher.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProviderWatcher.java
deleted file mode 100644
index ba1f647..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RegisteredMediaRouteProviderWatcher.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Handler;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-/**
- * Watches for media route provider services to be installed.
- * Adds a provider to the media router for each registered service.
- *
- * @see RegisteredMediaRouteProvider
- */
-final class RegisteredMediaRouteProviderWatcher {
-    private final Context mContext;
-    private final Callback mCallback;
-    private final Handler mHandler;
-    private final PackageManager mPackageManager;
-
-    private final ArrayList<RegisteredMediaRouteProvider> mProviders =
-            new ArrayList<RegisteredMediaRouteProvider>();
-    private boolean mRunning;
-
-    public RegisteredMediaRouteProviderWatcher(Context context, Callback callback) {
-        mContext = context;
-        mCallback = callback;
-        mHandler = new Handler();
-        mPackageManager = context.getPackageManager();
-    }
-
-    public void start() {
-        if (!mRunning) {
-            mRunning = true;
-
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-            filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
-            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
-            filter.addDataScheme("package");
-            mContext.registerReceiver(mScanPackagesReceiver, filter, null, mHandler);
-
-            // Scan packages.
-            // Also has the side-effect of restarting providers if needed.
-            mHandler.post(mScanPackagesRunnable);
-        }
-    }
-
-    public void stop() {
-        if (mRunning) {
-            mRunning = false;
-
-            mContext.unregisterReceiver(mScanPackagesReceiver);
-            mHandler.removeCallbacks(mScanPackagesRunnable);
-
-            // Stop all providers.
-            for (int i = mProviders.size() - 1; i >= 0; i--) {
-                mProviders.get(i).stop();
-            }
-        }
-    }
-
-    void scanPackages() {
-        if (!mRunning) {
-            return;
-        }
-
-        // Add providers for all new services.
-        // Reorder the list so that providers left at the end will be the ones to remove.
-        int targetIndex = 0;
-        Intent intent = new Intent(MediaRouteProviderService.SERVICE_INTERFACE);
-        for (ResolveInfo resolveInfo : mPackageManager.queryIntentServices(intent, 0)) {
-            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
-            if (serviceInfo != null) {
-                int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
-                if (sourceIndex < 0) {
-                    RegisteredMediaRouteProvider provider =
-                            new RegisteredMediaRouteProvider(mContext,
-                            new ComponentName(serviceInfo.packageName, serviceInfo.name));
-                    provider.start();
-                    mProviders.add(targetIndex++, provider);
-                    mCallback.addProvider(provider);
-                } else if (sourceIndex >= targetIndex) {
-                    RegisteredMediaRouteProvider provider = mProviders.get(sourceIndex);
-                    provider.start(); // restart the provider if needed
-                    provider.rebindIfDisconnected();
-                    Collections.swap(mProviders, sourceIndex, targetIndex++);
-                }
-            }
-        }
-
-        // Remove providers for missing services.
-        if (targetIndex < mProviders.size()) {
-            for (int i = mProviders.size() - 1; i >= targetIndex; i--) {
-                RegisteredMediaRouteProvider provider = mProviders.get(i);
-                mCallback.removeProvider(provider);
-                mProviders.remove(provider);
-                provider.stop();
-            }
-        }
-    }
-
-    private int findProvider(String packageName, String className) {
-        int count = mProviders.size();
-        for (int i = 0; i < count; i++) {
-            RegisteredMediaRouteProvider provider = mProviders.get(i);
-            if (provider.hasComponentName(packageName, className)) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            scanPackages();
-        }
-    };
-
-    private final Runnable mScanPackagesRunnable = new Runnable() {
-        @Override
-        public void run() {
-            scanPackages();
-        }
-    };
-
-    public interface Callback {
-        void addProvider(MediaRouteProvider provider);
-        void removeProvider(MediaRouteProvider provider);
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java
deleted file mode 100644
index 65c5518..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemoteControlClientCompat.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.os.Build;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Provides access to features of the remote control client.
- *
- * Hidden for now but we might want to make this available to applications
- * in the future.
- */
-abstract class RemoteControlClientCompat {
-    protected final Context mContext;
-    protected final Object mRcc;
-    protected VolumeCallback mVolumeCallback;
-
-    protected RemoteControlClientCompat(Context context, Object rcc) {
-        mContext = context;
-        mRcc = rcc;
-    }
-
-    public static RemoteControlClientCompat obtain(Context context, Object rcc) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return new JellybeanImpl(context, rcc);
-        }
-        return new LegacyImpl(context, rcc);
-    }
-
-    public Object getRemoteControlClient() {
-        return mRcc;
-    }
-
-    /**
-     * Sets the current playback information.
-     * Must be called at least once to attach to the remote control client.
-     *
-     * @param info The playback information.  Must not be null.
-     */
-    public void setPlaybackInfo(PlaybackInfo info) {
-    }
-
-    /**
-     * Sets a callback to receive volume change requests from the remote control client.
-     *
-     * @param callback The volume callback to use or null if none.
-     */
-    public void setVolumeCallback(VolumeCallback callback) {
-        mVolumeCallback = callback;
-    }
-
-    /**
-     * Specifies information about the playback.
-     */
-    public static final class PlaybackInfo {
-        public int volume;
-        public int volumeMax;
-        public int volumeHandling = MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
-        public int playbackStream = AudioManager.STREAM_MUSIC;
-        public int playbackType = MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
-    }
-
-    /**
-     * Called when volume updates are requested by the remote control client.
-     */
-    public interface VolumeCallback {
-        /**
-         * Called when the volume should be increased or decreased.
-         *
-         * @param direction An integer indicating whether the volume is to be increased
-         * (positive value) or decreased (negative value).
-         * For bundled changes, the absolute value indicates the number of changes
-         * in the same direction, e.g. +3 corresponds to three "volume up" changes.
-         */
-        public void onVolumeUpdateRequest(int direction);
-
-        /**
-         * Called when the volume for the route should be set to the given value.
-         *
-         * @param volume An integer indicating the new volume value that should be used,
-         * always between 0 and the value set by {@link PlaybackInfo#volumeMax}.
-         */
-        public void onVolumeSetRequest(int volume);
-    }
-
-    /**
-     * Legacy implementation for platform versions prior to Jellybean.
-     * Does nothing.
-     */
-    static class LegacyImpl extends RemoteControlClientCompat {
-        public LegacyImpl(Context context, Object rcc) {
-            super(context, rcc);
-        }
-    }
-
-    /**
-     * Implementation for Jellybean.
-     *
-     * The basic idea of this implementation is to attach the RCC to a UserRouteInfo
-     * in order to hook up stream metadata and volume callbacks because there is no
-     * other API available to do so in this platform version.  The UserRouteInfo itself
-     * is not attached to the MediaRouter so it is transparent to the user.
-     */
-    // @@RequiresApi(16)
-    static class JellybeanImpl extends RemoteControlClientCompat {
-        private final Object mRouterObj;
-        private final Object mUserRouteCategoryObj;
-        private final Object mUserRouteObj;
-        private boolean mRegistered;
-
-        public JellybeanImpl(Context context, Object rcc) {
-            super(context, rcc);
-
-            mRouterObj = MediaRouterJellybean.getMediaRouter(context);
-            mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory(
-                    mRouterObj, "", false);
-            mUserRouteObj = MediaRouterJellybean.createUserRoute(
-                    mRouterObj, mUserRouteCategoryObj);
-        }
-
-        @Override
-        public void setPlaybackInfo(PlaybackInfo info) {
-            MediaRouterJellybean.UserRouteInfo.setVolume(
-                    mUserRouteObj, info.volume);
-            MediaRouterJellybean.UserRouteInfo.setVolumeMax(
-                    mUserRouteObj, info.volumeMax);
-            MediaRouterJellybean.UserRouteInfo.setVolumeHandling(
-                    mUserRouteObj, info.volumeHandling);
-            MediaRouterJellybean.UserRouteInfo.setPlaybackStream(
-                    mUserRouteObj, info.playbackStream);
-            MediaRouterJellybean.UserRouteInfo.setPlaybackType(
-                    mUserRouteObj, info.playbackType);
-
-            if (!mRegistered) {
-                mRegistered = true;
-                MediaRouterJellybean.UserRouteInfo.setVolumeCallback(mUserRouteObj,
-                        MediaRouterJellybean.createVolumeCallback(
-                                new VolumeCallbackWrapper(this)));
-                MediaRouterJellybean.UserRouteInfo.setRemoteControlClient(mUserRouteObj, mRcc);
-            }
-        }
-
-        private static final class VolumeCallbackWrapper
-                implements MediaRouterJellybean.VolumeCallback {
-            // Unfortunately, the framework never unregisters its volume observer from
-            // the audio service so the UserRouteInfo object may leak along with
-            // any callbacks that we attach to it.  Use a weak reference to prevent
-            // the volume callback from holding strong references to anything important.
-            private final WeakReference<JellybeanImpl> mImplWeak;
-
-            public VolumeCallbackWrapper(JellybeanImpl impl) {
-                mImplWeak = new WeakReference<JellybeanImpl>(impl);
-            }
-
-            @Override
-            public void onVolumeUpdateRequest(Object routeObj, int direction) {
-                JellybeanImpl impl = mImplWeak.get();
-                if (impl != null && impl.mVolumeCallback != null) {
-                    impl.mVolumeCallback.onVolumeUpdateRequest(direction);
-                }
-            }
-
-            @Override
-            public void onVolumeSetRequest(Object routeObj, int volume) {
-                JellybeanImpl impl = mImplWeak.get();
-                if (impl != null && impl.mVolumeCallback != null) {
-                    impl.mVolumeCallback.onVolumeSetRequest(volume);
-                }
-            }
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java
deleted file mode 100644
index e76564e..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/RemotePlaybackClient.java
+++ /dev/null
@@ -1,1045 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-
-import androidx.core.util.ObjectsCompat;
-
-/**
- * A helper class for playing media on remote routes using the remote playback protocol
- * defined by {@link MediaControlIntent}.
- * <p>
- * The client maintains session state and offers a simplified interface for issuing
- * remote playback media control intents to a single route.
- * </p>
- */
-public class RemotePlaybackClient {
-    static final String TAG = "RemotePlaybackClient";
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private final Context mContext;
-    private final MediaRouter.RouteInfo mRoute;
-    private final ActionReceiver mActionReceiver;
-    private final PendingIntent mItemStatusPendingIntent;
-    private final PendingIntent mSessionStatusPendingIntent;
-    private final PendingIntent mMessagePendingIntent;
-
-    private boolean mRouteSupportsRemotePlayback;
-    private boolean mRouteSupportsQueuing;
-    private boolean mRouteSupportsSessionManagement;
-    private boolean mRouteSupportsMessaging;
-
-    String mSessionId;
-    StatusCallback mStatusCallback;
-    OnMessageReceivedListener mOnMessageReceivedListener;
-
-    /**
-     * Creates a remote playback client for a route.
-     *
-     * @param route The media route.
-     */
-    public RemotePlaybackClient(Context context, MediaRouter.RouteInfo route) {
-        if (context == null) {
-            throw new IllegalArgumentException("context must not be null");
-        }
-        if (route == null) {
-            throw new IllegalArgumentException("route must not be null");
-        }
-
-        mContext = context;
-        mRoute = route;
-
-        IntentFilter actionFilter = new IntentFilter();
-        actionFilter.addAction(ActionReceiver.ACTION_ITEM_STATUS_CHANGED);
-        actionFilter.addAction(ActionReceiver.ACTION_SESSION_STATUS_CHANGED);
-        actionFilter.addAction(ActionReceiver.ACTION_MESSAGE_RECEIVED);
-        mActionReceiver = new ActionReceiver();
-        context.registerReceiver(mActionReceiver, actionFilter);
-
-        Intent itemStatusIntent = new Intent(ActionReceiver.ACTION_ITEM_STATUS_CHANGED);
-        itemStatusIntent.setPackage(context.getPackageName());
-        mItemStatusPendingIntent = PendingIntent.getBroadcast(
-                context, 0, itemStatusIntent, 0);
-
-        Intent sessionStatusIntent = new Intent(ActionReceiver.ACTION_SESSION_STATUS_CHANGED);
-        sessionStatusIntent.setPackage(context.getPackageName());
-        mSessionStatusPendingIntent = PendingIntent.getBroadcast(
-                context, 0, sessionStatusIntent, 0);
-
-        Intent messageIntent = new Intent(ActionReceiver.ACTION_MESSAGE_RECEIVED);
-        messageIntent.setPackage(context.getPackageName());
-        mMessagePendingIntent = PendingIntent.getBroadcast(
-                context, 0, messageIntent, 0);
-        detectFeatures();
-    }
-
-    /**
-     * Releases resources owned by the client.
-     */
-    public void release() {
-        mContext.unregisterReceiver(mActionReceiver);
-    }
-
-    /**
-     * Returns true if the route supports remote playback.
-     * <p>
-     * If the route does not support remote playback, then none of the functionality
-     * offered by the client will be available.
-     * </p><p>
-     * This method returns true if the route supports all of the following
-     * actions: {@link MediaControlIntent#ACTION_PLAY play},
-     * {@link MediaControlIntent#ACTION_SEEK seek},
-     * {@link MediaControlIntent#ACTION_GET_STATUS get status},
-     * {@link MediaControlIntent#ACTION_PAUSE pause},
-     * {@link MediaControlIntent#ACTION_RESUME resume},
-     * {@link MediaControlIntent#ACTION_STOP stop}.
-     * </p>
-     *
-     * @return True if remote playback is supported.
-     */
-    public boolean isRemotePlaybackSupported() {
-        return mRouteSupportsRemotePlayback;
-    }
-
-    /**
-     * Returns true if the route supports queuing features.
-     * <p>
-     * If the route does not support queuing, then at most one media item can be played
-     * at a time and the {@link #enqueue} method will not be available.
-     * </p><p>
-     * This method returns true if the route supports all of the basic remote playback
-     * actions and all of the following actions:
-     * {@link MediaControlIntent#ACTION_ENQUEUE enqueue},
-     * {@link MediaControlIntent#ACTION_REMOVE remove}.
-     * </p>
-     *
-     * @return True if queuing is supported.  Implies {@link #isRemotePlaybackSupported}
-     * is also true.
-     *
-     * @see #isRemotePlaybackSupported
-     */
-    public boolean isQueuingSupported() {
-        return mRouteSupportsQueuing;
-    }
-
-    /**
-     * Returns true if the route supports session management features.
-     * <p>
-     * If the route does not support session management, then the session will
-     * not be created until the first media item is played.
-     * </p><p>
-     * This method returns true if the route supports all of the basic remote playback
-     * actions and all of the following actions:
-     * {@link MediaControlIntent#ACTION_START_SESSION start session},
-     * {@link MediaControlIntent#ACTION_GET_SESSION_STATUS get session status},
-     * {@link MediaControlIntent#ACTION_END_SESSION end session}.
-     * </p>
-     *
-     * @return True if session management is supported.
-     * Implies {@link #isRemotePlaybackSupported} is also true.
-     *
-     * @see #isRemotePlaybackSupported
-     */
-    public boolean isSessionManagementSupported() {
-        return mRouteSupportsSessionManagement;
-    }
-
-    /**
-     * Returns true if the route supports messages.
-     * <p>
-     * This method returns true if the route supports all of the basic remote playback
-     * actions and all of the following actions:
-     * {@link MediaControlIntent#ACTION_START_SESSION start session},
-     * {@link MediaControlIntent#ACTION_SEND_MESSAGE send message},
-     * {@link MediaControlIntent#ACTION_END_SESSION end session}.
-     * </p>
-     *
-     * @return True if session management is supported.
-     * Implies {@link #isRemotePlaybackSupported} is also true.
-     *
-     * @see #isRemotePlaybackSupported
-     */
-    public boolean isMessagingSupported() {
-        return mRouteSupportsMessaging;
-    }
-
-    /**
-     * Gets the current session id if there is one.
-     *
-     * @return The current session id, or null if none.
-     */
-    public String getSessionId() {
-        return mSessionId;
-    }
-
-    /**
-     * Sets the current session id.
-     * <p>
-     * It is usually not necessary to set the session id explicitly since
-     * it is created as a side-effect of other requests such as
-     * {@link #play}, {@link #enqueue}, and {@link #startSession}.
-     * </p>
-     *
-     * @param sessionId The new session id, or null if none.
-     */
-    public void setSessionId(String sessionId) {
-        if (!ObjectsCompat.equals(mSessionId, sessionId)) {
-            if (DEBUG) {
-                Log.d(TAG, "Session id is now: " + sessionId);
-            }
-            mSessionId = sessionId;
-            if (mStatusCallback != null) {
-                mStatusCallback.onSessionChanged(sessionId);
-            }
-        }
-    }
-
-    /**
-     * Returns true if the client currently has a session.
-     * <p>
-     * Equivalent to checking whether {@link #getSessionId} returns a non-null result.
-     * </p>
-     *
-     * @return True if there is a current session.
-     */
-    public boolean hasSession() {
-        return mSessionId != null;
-    }
-
-    /**
-     * Sets a callback that should receive status updates when the state of
-     * media sessions or media items created by this instance of the remote
-     * playback client changes.
-     * <p>
-     * The callback should be set before the session is created or any play
-     * commands are issued.
-     * </p>
-     *
-     * @param callback The callback to set.  May be null to remove the previous callback.
-     */
-    public void setStatusCallback(StatusCallback callback) {
-        mStatusCallback = callback;
-    }
-
-    /**
-     * Sets a callback that should receive messages when a message is sent from
-     * media sessions created by this instance of the remote playback client changes.
-     * <p>
-     * The callback should be set before the session is created.
-     * </p>
-     *
-     * @param listener The callback to set.  May be null to remove the previous callback.
-     */
-    public void setOnMessageReceivedListener(OnMessageReceivedListener listener) {
-        mOnMessageReceivedListener = listener;
-    }
-
-    /**
-     * Sends a request to play a media item.
-     * <p>
-     * Clears the queue and starts playing the new item immediately.  If the queue
-     * was previously paused, then it is resumed as a side-effect of this request.
-     * </p><p>
-     * The request is issued in the current session.  If no session is available, then
-     * one is created implicitly.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_PLAY ACTION_PLAY} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param contentUri The content Uri to play.
-     * @param mimeType The mime type of the content, or null if unknown.
-     * @param positionMillis The initial content position for the item in milliseconds,
-     * or <code>0</code> to start at the beginning.
-     * @param metadata The media item metadata bundle, or null if none.
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_PLAY} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws UnsupportedOperationException if the route does not support remote playback.
-     *
-     * @see MediaControlIntent#ACTION_PLAY
-     * @see #isRemotePlaybackSupported
-     */
-    public void play(Uri contentUri, String mimeType, Bundle metadata,
-            long positionMillis, Bundle extras, ItemActionCallback callback) {
-        playOrEnqueue(contentUri, mimeType, metadata, positionMillis,
-                extras, callback, MediaControlIntent.ACTION_PLAY);
-    }
-
-    /**
-     * Sends a request to enqueue a media item.
-     * <p>
-     * Enqueues a new item to play.  If the queue was previously paused, then will
-     * remain paused.
-     * </p><p>
-     * The request is issued in the current session.  If no session is available, then
-     * one is created implicitly.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_ENQUEUE ACTION_ENQUEUE} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param contentUri The content Uri to enqueue.
-     * @param mimeType The mime type of the content, or null if unknown.
-     * @param positionMillis The initial content position for the item in milliseconds,
-     * or <code>0</code> to start at the beginning.
-     * @param metadata The media item metadata bundle, or null if none.
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_ENQUEUE} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws UnsupportedOperationException if the route does not support queuing.
-     *
-     * @see MediaControlIntent#ACTION_ENQUEUE
-     * @see #isRemotePlaybackSupported
-     * @see #isQueuingSupported
-     */
-    public void enqueue(Uri contentUri, String mimeType, Bundle metadata,
-            long positionMillis, Bundle extras, ItemActionCallback callback) {
-        playOrEnqueue(contentUri, mimeType, metadata, positionMillis,
-                extras, callback, MediaControlIntent.ACTION_ENQUEUE);
-    }
-
-    private void playOrEnqueue(Uri contentUri, String mimeType, Bundle metadata,
-            long positionMillis, Bundle extras,
-            final ItemActionCallback callback, String action) {
-        if (contentUri == null) {
-            throw new IllegalArgumentException("contentUri must not be null");
-        }
-        throwIfRemotePlaybackNotSupported();
-        if (action.equals(MediaControlIntent.ACTION_ENQUEUE)) {
-            throwIfQueuingNotSupported();
-        }
-
-        Intent intent = new Intent(action);
-        intent.setDataAndType(contentUri, mimeType);
-        intent.putExtra(MediaControlIntent.EXTRA_ITEM_STATUS_UPDATE_RECEIVER,
-                mItemStatusPendingIntent);
-        if (metadata != null) {
-            intent.putExtra(MediaControlIntent.EXTRA_ITEM_METADATA, metadata);
-        }
-        if (positionMillis != 0) {
-            intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, positionMillis);
-        }
-        performItemAction(intent, mSessionId, null, extras, callback);
-    }
-
-    /**
-     * Sends a request to seek to a new position in a media item.
-     * <p>
-     * Seeks to a new position.  If the queue was previously paused then it
-     * remains paused but the item's new position is still remembered.
-     * </p><p>
-     * The request is issued in the current session.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_SEEK ACTION_SEEK} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param itemId The item id.
-     * @param positionMillis The new content position for the item in milliseconds,
-     * or <code>0</code> to start at the beginning.
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_SEEK} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     *
-     * @see MediaControlIntent#ACTION_SEEK
-     * @see #isRemotePlaybackSupported
-     */
-    public void seek(String itemId, long positionMillis, Bundle extras,
-            ItemActionCallback callback) {
-        if (itemId == null) {
-            throw new IllegalArgumentException("itemId must not be null");
-        }
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_SEEK);
-        intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, positionMillis);
-        performItemAction(intent, mSessionId, itemId, extras, callback);
-    }
-
-    /**
-     * Sends a request to get the status of a media item.
-     * <p>
-     * The request is issued in the current session.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_GET_STATUS ACTION_GET_STATUS} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param itemId The item id.
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_GET_STATUS} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     *
-     * @see MediaControlIntent#ACTION_GET_STATUS
-     * @see #isRemotePlaybackSupported
-     */
-    public void getStatus(String itemId, Bundle extras, ItemActionCallback callback) {
-        if (itemId == null) {
-            throw new IllegalArgumentException("itemId must not be null");
-        }
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_GET_STATUS);
-        performItemAction(intent, mSessionId, itemId, extras, callback);
-    }
-
-    /**
-     * Sends a request to remove a media item from the queue.
-     * <p>
-     * The request is issued in the current session.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_REMOVE ACTION_REMOVE} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param itemId The item id.
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_REMOVE} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     * @throws UnsupportedOperationException if the route does not support queuing.
-     *
-     * @see MediaControlIntent#ACTION_REMOVE
-     * @see #isRemotePlaybackSupported
-     * @see #isQueuingSupported
-     */
-    public void remove(String itemId, Bundle extras, ItemActionCallback callback) {
-        if (itemId == null) {
-            throw new IllegalArgumentException("itemId must not be null");
-        }
-        throwIfQueuingNotSupported();
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_REMOVE);
-        performItemAction(intent, mSessionId, itemId, extras, callback);
-    }
-
-    /**
-     * Sends a request to pause media playback.
-     * <p>
-     * The request is issued in the current session.  If playback is already paused
-     * then the request has no effect.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_PAUSE ACTION_PAUSE} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_PAUSE} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     *
-     * @see MediaControlIntent#ACTION_PAUSE
-     * @see #isRemotePlaybackSupported
-     */
-    public void pause(Bundle extras, SessionActionCallback callback) {
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_PAUSE);
-        performSessionAction(intent, mSessionId, extras, callback);
-    }
-
-    /**
-     * Sends a request to resume (unpause) media playback.
-     * <p>
-     * The request is issued in the current session.  If playback is not paused
-     * then the request has no effect.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_RESUME ACTION_RESUME} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_RESUME} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     *
-     * @see MediaControlIntent#ACTION_RESUME
-     * @see #isRemotePlaybackSupported
-     */
-    public void resume(Bundle extras, SessionActionCallback callback) {
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_RESUME);
-        performSessionAction(intent, mSessionId, extras, callback);
-    }
-
-    /**
-     * Sends a request to stop media playback and clear the media playback queue.
-     * <p>
-     * The request is issued in the current session.  If the queue is already
-     * empty then the request has no effect.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_STOP ACTION_STOP} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_STOP} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     *
-     * @see MediaControlIntent#ACTION_STOP
-     * @see #isRemotePlaybackSupported
-     */
-    public void stop(Bundle extras, SessionActionCallback callback) {
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_STOP);
-        performSessionAction(intent, mSessionId, extras, callback);
-    }
-
-    /**
-     * Sends a request to start a new media playback session.
-     * <p>
-     * The application must wait for the callback to indicate that this request
-     * is complete before issuing other requests that affect the session.  If this
-     * request is successful then the previous session will be invalidated.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_START_SESSION ACTION_START_SESSION}
-     * for more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_START_SESSION} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws UnsupportedOperationException if the route does not support session management.
-     *
-     * @see MediaControlIntent#ACTION_START_SESSION
-     * @see #isRemotePlaybackSupported
-     * @see #isSessionManagementSupported
-     */
-    public void startSession(Bundle extras, SessionActionCallback callback) {
-        throwIfSessionManagementNotSupported();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_START_SESSION);
-        intent.putExtra(MediaControlIntent.EXTRA_SESSION_STATUS_UPDATE_RECEIVER,
-                mSessionStatusPendingIntent);
-        if (mRouteSupportsMessaging) {
-            intent.putExtra(MediaControlIntent.EXTRA_MESSAGE_RECEIVER, mMessagePendingIntent);
-        }
-        performSessionAction(intent, null, extras, callback);
-    }
-
-    /**
-     * Sends a message.
-     * <p>
-     * The request is issued in the current session.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_SEND_MESSAGE} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param message A bundle message denoting {@link MediaControlIntent#EXTRA_MESSAGE}.
-     * @param callback A callback to invoke when the request has been processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     * @throws UnsupportedOperationException if the route does not support messages.
-     *
-     * @see MediaControlIntent#ACTION_SEND_MESSAGE
-     * @see #isMessagingSupported
-     */
-    public void sendMessage(Bundle message, SessionActionCallback callback) {
-        throwIfNoCurrentSession();
-        throwIfMessageNotSupported();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_SEND_MESSAGE);
-        performSessionAction(intent, mSessionId, message, callback);
-    }
-
-    /**
-     * Sends a request to get the status of the media playback session.
-     * <p>
-     * The request is issued in the current session.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_GET_SESSION_STATUS
-     * ACTION_GET_SESSION_STATUS} for more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_GET_SESSION_STATUS} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     * @throws UnsupportedOperationException if the route does not support session management.
-     *
-     * @see MediaControlIntent#ACTION_GET_SESSION_STATUS
-     * @see #isRemotePlaybackSupported
-     * @see #isSessionManagementSupported
-     */
-    public void getSessionStatus(Bundle extras, SessionActionCallback callback) {
-        throwIfSessionManagementNotSupported();
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_GET_SESSION_STATUS);
-        performSessionAction(intent, mSessionId, extras, callback);
-    }
-
-    /**
-     * Sends a request to end the media playback session.
-     * <p>
-     * The request is issued in the current session.  If this request is successful,
-     * the {@link #getSessionId session id property} will be set to null after
-     * the callback is invoked.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_END_SESSION ACTION_END_SESSION}
-     * for more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_END_SESSION} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     * @throws UnsupportedOperationException if the route does not support session management.
-     *
-     * @see MediaControlIntent#ACTION_END_SESSION
-     * @see #isRemotePlaybackSupported
-     * @see #isSessionManagementSupported
-     */
-    public void endSession(Bundle extras, SessionActionCallback callback) {
-        throwIfSessionManagementNotSupported();
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_END_SESSION);
-        performSessionAction(intent, mSessionId, extras, callback);
-    }
-
-    private void performItemAction(final Intent intent,
-            final String sessionId, final String itemId,
-            Bundle extras, final ItemActionCallback callback) {
-        intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
-        if (sessionId != null) {
-            intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sessionId);
-        }
-        if (itemId != null) {
-            intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, itemId);
-        }
-        if (extras != null) {
-            intent.putExtras(extras);
-        }
-        logRequest(intent);
-        mRoute.sendControlRequest(intent, new MediaRouter.ControlRequestCallback() {
-            @Override
-            public void onResult(Bundle data) {
-                if (data != null) {
-                    String sessionIdResult = inferMissingResult(sessionId,
-                            data.getString(MediaControlIntent.EXTRA_SESSION_ID));
-                    MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle(
-                            data.getBundle(MediaControlIntent.EXTRA_SESSION_STATUS));
-                    String itemIdResult = inferMissingResult(itemId,
-                            data.getString(MediaControlIntent.EXTRA_ITEM_ID));
-                    MediaItemStatus itemStatus = MediaItemStatus.fromBundle(
-                            data.getBundle(MediaControlIntent.EXTRA_ITEM_STATUS));
-                    adoptSession(sessionIdResult);
-                    if (sessionIdResult != null && itemIdResult != null && itemStatus != null) {
-                        if (DEBUG) {
-                            Log.d(TAG, "Received result from " + intent.getAction()
-                                    + ": data=" + bundleToString(data)
-                                    + ", sessionId=" + sessionIdResult
-                                    + ", sessionStatus=" + sessionStatus
-                                    + ", itemId=" + itemIdResult
-                                    + ", itemStatus=" + itemStatus);
-                        }
-                        callback.onResult(data, sessionIdResult, sessionStatus,
-                                itemIdResult, itemStatus);
-                        return;
-                    }
-                }
-                handleInvalidResult(intent, callback, data);
-            }
-
-            @Override
-            public void onError(String error, Bundle data) {
-                handleError(intent, callback, error, data);
-            }
-        });
-    }
-
-    private void performSessionAction(final Intent intent, final String sessionId,
-            Bundle extras, final SessionActionCallback callback) {
-        intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
-        if (sessionId != null) {
-            intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sessionId);
-        }
-        if (extras != null) {
-            intent.putExtras(extras);
-        }
-        logRequest(intent);
-        mRoute.sendControlRequest(intent, new MediaRouter.ControlRequestCallback() {
-            @Override
-            public void onResult(Bundle data) {
-                if (data != null) {
-                    String sessionIdResult = inferMissingResult(sessionId,
-                            data.getString(MediaControlIntent.EXTRA_SESSION_ID));
-                    MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle(
-                            data.getBundle(MediaControlIntent.EXTRA_SESSION_STATUS));
-                    adoptSession(sessionIdResult);
-                    if (sessionIdResult != null) {
-                        if (DEBUG) {
-                            Log.d(TAG, "Received result from " + intent.getAction()
-                                    + ": data=" + bundleToString(data)
-                                    + ", sessionId=" + sessionIdResult
-                                    + ", sessionStatus=" + sessionStatus);
-                        }
-                        try {
-                            callback.onResult(data, sessionIdResult, sessionStatus);
-                        } finally {
-                            if (intent.getAction().equals(MediaControlIntent.ACTION_END_SESSION)
-                                    && sessionIdResult.equals(mSessionId)) {
-                                setSessionId(null);
-                            }
-                        }
-                        return;
-                    }
-                }
-                handleInvalidResult(intent, callback, data);
-            }
-
-            @Override
-            public void onError(String error, Bundle data) {
-                handleError(intent, callback, error, data);
-            }
-        });
-    }
-
-    void adoptSession(String sessionId) {
-        if (sessionId != null) {
-            setSessionId(sessionId);
-        }
-    }
-
-    void handleInvalidResult(Intent intent, ActionCallback callback,
-            Bundle data) {
-        Log.w(TAG, "Received invalid result data from " + intent.getAction()
-                + ": data=" + bundleToString(data));
-        callback.onError(null, MediaControlIntent.ERROR_UNKNOWN, data);
-    }
-
-    void handleError(Intent intent, ActionCallback callback,
-            String error, Bundle data) {
-        final int code;
-        if (data != null) {
-            code = data.getInt(MediaControlIntent.EXTRA_ERROR_CODE,
-                    MediaControlIntent.ERROR_UNKNOWN);
-        } else {
-            code = MediaControlIntent.ERROR_UNKNOWN;
-        }
-        if (DEBUG) {
-            Log.w(TAG, "Received error from " + intent.getAction()
-                    + ": error=" + error
-                    + ", code=" + code
-                    + ", data=" + bundleToString(data));
-        }
-        callback.onError(error, code, data);
-    }
-
-    private void detectFeatures() {
-        mRouteSupportsRemotePlayback = routeSupportsAction(MediaControlIntent.ACTION_PLAY)
-                && routeSupportsAction(MediaControlIntent.ACTION_SEEK)
-                && routeSupportsAction(MediaControlIntent.ACTION_GET_STATUS)
-                && routeSupportsAction(MediaControlIntent.ACTION_PAUSE)
-                && routeSupportsAction(MediaControlIntent.ACTION_RESUME)
-                && routeSupportsAction(MediaControlIntent.ACTION_STOP);
-        mRouteSupportsQueuing = mRouteSupportsRemotePlayback
-                && routeSupportsAction(MediaControlIntent.ACTION_ENQUEUE)
-                && routeSupportsAction(MediaControlIntent.ACTION_REMOVE);
-        mRouteSupportsSessionManagement = mRouteSupportsRemotePlayback
-                && routeSupportsAction(MediaControlIntent.ACTION_START_SESSION)
-                && routeSupportsAction(MediaControlIntent.ACTION_GET_SESSION_STATUS)
-                && routeSupportsAction(MediaControlIntent.ACTION_END_SESSION);
-        mRouteSupportsMessaging = doesRouteSupportMessaging();
-    }
-
-    private boolean routeSupportsAction(String action) {
-        return mRoute.supportsControlAction(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK, action);
-    }
-
-    private boolean doesRouteSupportMessaging() {
-        for (IntentFilter filter : mRoute.getControlFilters()) {
-            if (filter.hasAction(MediaControlIntent.ACTION_SEND_MESSAGE)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void throwIfRemotePlaybackNotSupported() {
-        if (!mRouteSupportsRemotePlayback) {
-            throw new UnsupportedOperationException("The route does not support remote playback.");
-        }
-    }
-
-    private void throwIfQueuingNotSupported() {
-        if (!mRouteSupportsQueuing) {
-            throw new UnsupportedOperationException("The route does not support queuing.");
-        }
-    }
-
-    private void throwIfSessionManagementNotSupported() {
-        if (!mRouteSupportsSessionManagement) {
-            throw new UnsupportedOperationException("The route does not support "
-                    + "session management.");
-        }
-    }
-
-    private void throwIfMessageNotSupported() {
-        if (!mRouteSupportsMessaging) {
-            throw new UnsupportedOperationException("The route does not support message.");
-        }
-    }
-
-    private void throwIfNoCurrentSession() {
-        if (mSessionId == null) {
-            throw new IllegalStateException("There is no current session.");
-        }
-    }
-
-    static String inferMissingResult(String request, String result) {
-        if (result == null) {
-            // Result is missing.
-            return request;
-        }
-        if (request == null || request.equals(result)) {
-            // Request didn't specify a value or result matches request.
-            return result;
-        }
-        // Result conflicts with request.
-        return null;
-    }
-
-    private static void logRequest(Intent intent) {
-        if (DEBUG) {
-            Log.d(TAG, "Sending request: " + intent);
-        }
-    }
-
-    static String bundleToString(Bundle bundle) {
-        if (bundle != null) {
-            bundle.size(); // force bundle to be unparcelled
-            return bundle.toString();
-        }
-        return "null";
-    }
-
-    private final class ActionReceiver extends BroadcastReceiver {
-        public static final String ACTION_ITEM_STATUS_CHANGED =
-                "androidx.mediarouter.media.actions.ACTION_ITEM_STATUS_CHANGED";
-        public static final String ACTION_SESSION_STATUS_CHANGED =
-                "androidx.mediarouter.media.actions.ACTION_SESSION_STATUS_CHANGED";
-        public static final String ACTION_MESSAGE_RECEIVED =
-                "androidx.mediarouter.media.actions.ACTION_MESSAGE_RECEIVED";
-
-        ActionReceiver() {
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String sessionId = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
-            if (sessionId == null || !sessionId.equals(mSessionId)) {
-                Log.w(TAG, "Discarding spurious status callback "
-                        + "with missing or invalid session id: sessionId=" + sessionId);
-                return;
-            }
-
-            MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle(
-                    intent.getBundleExtra(MediaControlIntent.EXTRA_SESSION_STATUS));
-            String action = intent.getAction();
-            if (action.equals(ACTION_ITEM_STATUS_CHANGED)) {
-                String itemId = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
-                if (itemId == null) {
-                    Log.w(TAG, "Discarding spurious status callback with missing item id.");
-                    return;
-                }
-
-                MediaItemStatus itemStatus = MediaItemStatus.fromBundle(
-                        intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_STATUS));
-                if (itemStatus == null) {
-                    Log.w(TAG, "Discarding spurious status callback with missing item status.");
-                    return;
-                }
-
-                if (DEBUG) {
-                    Log.d(TAG, "Received item status callback: sessionId=" + sessionId
-                            + ", sessionStatus=" + sessionStatus
-                            + ", itemId=" + itemId
-                            + ", itemStatus=" + itemStatus);
-                }
-
-                if (mStatusCallback != null) {
-                    mStatusCallback.onItemStatusChanged(intent.getExtras(),
-                            sessionId, sessionStatus, itemId, itemStatus);
-                }
-            } else if (action.equals(ACTION_SESSION_STATUS_CHANGED)) {
-                if (sessionStatus == null) {
-                    Log.w(TAG, "Discarding spurious media status callback with "
-                            +"missing session status.");
-                    return;
-                }
-
-                if (DEBUG) {
-                    Log.d(TAG, "Received session status callback: sessionId=" + sessionId
-                            + ", sessionStatus=" + sessionStatus);
-                }
-
-                if (mStatusCallback != null) {
-                    mStatusCallback.onSessionStatusChanged(intent.getExtras(),
-                            sessionId, sessionStatus);
-                }
-            } else if (action.equals(ACTION_MESSAGE_RECEIVED)) {
-                if (DEBUG) {
-                    Log.d(TAG, "Received message callback: sessionId=" + sessionId);
-                }
-
-                if (mOnMessageReceivedListener != null) {
-                    mOnMessageReceivedListener.onMessageReceived(sessionId,
-                            intent.getBundleExtra(MediaControlIntent.EXTRA_MESSAGE));
-                }
-            }
-        }
-    }
-
-    /**
-     * A callback that will receive media status updates.
-     */
-    public static abstract class StatusCallback {
-        /**
-         * Called when the status of a media item changes.
-         *
-         * @param data The result data bundle.
-         * @param sessionId The session id.
-         * @param sessionStatus The session status, or null if unknown.
-         * @param itemId The item id.
-         * @param itemStatus The item status.
-         */
-        public void onItemStatusChanged(Bundle data,
-                String sessionId, MediaSessionStatus sessionStatus,
-                String itemId, MediaItemStatus itemStatus) {
-        }
-
-        /**
-         * Called when the status of a media session changes.
-         *
-         * @param data The result data bundle.
-         * @param sessionId The session id.
-         * @param sessionStatus The session status, or null if unknown.
-         */
-        public void onSessionStatusChanged(Bundle data,
-                String sessionId, MediaSessionStatus sessionStatus) {
-        }
-
-        /**
-         * Called when the session of the remote playback client changes.
-         *
-         * @param sessionId The new session id.
-         */
-        public void onSessionChanged(String sessionId) {
-        }
-    }
-
-    /**
-     * Base callback type for remote playback requests.
-     */
-    public static abstract class ActionCallback {
-        /**
-         * Called when a media control request fails.
-         *
-         * @param error A localized error message which may be shown to the user, or null
-         * if the cause of the error is unclear.
-         * @param code The error code, or {@link MediaControlIntent#ERROR_UNKNOWN} if unknown.
-         * @param data The error data bundle, or null if none.
-         */
-        public void onError(String error, int code, Bundle data) {
-        }
-    }
-
-    /**
-     * Callback for remote playback requests that operate on items.
-     */
-    public static abstract class ItemActionCallback extends ActionCallback {
-        /**
-         * Called when the request succeeds.
-         *
-         * @param data The result data bundle.
-         * @param sessionId The session id.
-         * @param sessionStatus The session status, or null if unknown.
-         * @param itemId The item id.
-         * @param itemStatus The item status.
-         */
-        public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
-                String itemId, MediaItemStatus itemStatus) {
-        }
-    }
-
-    /**
-     * Callback for remote playback requests that operate on sessions.
-     */
-    public static abstract class SessionActionCallback extends ActionCallback {
-        /**
-         * Called when the request succeeds.
-         *
-         * @param data The result data bundle.
-         * @param sessionId The session id.
-         * @param sessionStatus The session status, or null if unknown.
-         */
-        public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
-        }
-    }
-
-    /**
-     * A callback that will receive messages from media sessions.
-     */
-    public interface OnMessageReceivedListener {
-        /**
-         * Called when a message received.
-         *
-         * @param sessionId The session id.
-         * @param message A bundle message denoting {@link MediaControlIntent#EXTRA_MESSAGE}.
-         */
-        void onMessageReceived(String sessionId, Bundle message);
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java b/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java
deleted file mode 100644
index 53901a4..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/SystemMediaRouteProvider.java
+++ /dev/null
@@ -1,882 +0,0 @@
-/*
- * 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.support.mediarouter.media;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
-import android.media.AudioManager;
-import android.os.Build;
-import android.view.Display;
-
-import com.android.media.update.ApiHelper;
-import com.android.media.update.R;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Provides routes for built-in system destinations such as the local display
- * and speaker.  On Jellybean and newer platform releases, queries the framework
- * MediaRouter for framework-provided routes and registers non-framework-provided
- * routes as user routes.
- */
-abstract class SystemMediaRouteProvider extends MediaRouteProvider {
-    private static final String TAG = "SystemMediaRouteProvider";
-
-    public static final String PACKAGE_NAME = "android";
-    public static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
-
-    protected SystemMediaRouteProvider(Context context) {
-        super(context, new ProviderMetadata(new ComponentName(PACKAGE_NAME,
-                SystemMediaRouteProvider.class.getName())));
-    }
-
-    public static SystemMediaRouteProvider obtain(Context context, SyncCallback syncCallback) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return new Api24Impl(context, syncCallback);
-        }
-        if (Build.VERSION.SDK_INT >= 18) {
-            return new JellybeanMr2Impl(context, syncCallback);
-        }
-        if (Build.VERSION.SDK_INT >= 17) {
-            return new JellybeanMr1Impl(context, syncCallback);
-        }
-        if (Build.VERSION.SDK_INT >= 16) {
-            return new JellybeanImpl(context, syncCallback);
-        }
-        return new LegacyImpl(context);
-    }
-
-    /**
-     * Called by the media router when a route is added to synchronize state with
-     * the framework media router.
-     */
-    public void onSyncRouteAdded(MediaRouter.RouteInfo route) {
-    }
-
-    /**
-     * Called by the media router when a route is removed to synchronize state with
-     * the framework media router.
-     */
-    public void onSyncRouteRemoved(MediaRouter.RouteInfo route) {
-    }
-
-    /**
-     * Called by the media router when a route is changed to synchronize state with
-     * the framework media router.
-     */
-    public void onSyncRouteChanged(MediaRouter.RouteInfo route) {
-    }
-
-    /**
-     * Called by the media router when a route is selected to synchronize state with
-     * the framework media router.
-     */
-    public void onSyncRouteSelected(MediaRouter.RouteInfo route) {
-    }
-
-    /**
-     * Callbacks into the media router to synchronize state with the framework media router.
-     */
-    public interface SyncCallback {
-        void onSystemRouteSelectedByDescriptorId(String id);
-    }
-
-    protected Object getDefaultRoute() {
-        return null;
-    }
-
-    protected Object getSystemRoute(MediaRouter.RouteInfo route) {
-        return null;
-    }
-
-    /**
-     * Legacy implementation for platform versions prior to Jellybean.
-     */
-    static class LegacyImpl extends SystemMediaRouteProvider {
-        static final int PLAYBACK_STREAM = AudioManager.STREAM_MUSIC;
-
-        private static final ArrayList<IntentFilter> CONTROL_FILTERS;
-        static {
-            IntentFilter f = new IntentFilter();
-            f.addCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO);
-            f.addCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
-
-            CONTROL_FILTERS = new ArrayList<IntentFilter>();
-            CONTROL_FILTERS.add(f);
-        }
-
-        final AudioManager mAudioManager;
-        private final VolumeChangeReceiver mVolumeChangeReceiver;
-        int mLastReportedVolume = -1;
-
-        public LegacyImpl(Context context) {
-            super(context);
-            mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
-            mVolumeChangeReceiver = new VolumeChangeReceiver();
-
-            context.registerReceiver(mVolumeChangeReceiver,
-                    new IntentFilter(VolumeChangeReceiver.VOLUME_CHANGED_ACTION));
-            publishRoutes();
-        }
-
-        void publishRoutes() {
-            Resources r = getContext().getResources();
-            int maxVolume = mAudioManager.getStreamMaxVolume(PLAYBACK_STREAM);
-            mLastReportedVolume = mAudioManager.getStreamVolume(PLAYBACK_STREAM);
-            MediaRouteDescriptor defaultRoute = new MediaRouteDescriptor.Builder(
-                    DEFAULT_ROUTE_ID, r.getString(R.string.mr_system_route_name))
-                    .addControlFilters(CONTROL_FILTERS)
-                    .setPlaybackStream(PLAYBACK_STREAM)
-                    .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_LOCAL)
-                    .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
-                    .setVolumeMax(maxVolume)
-                    .setVolume(mLastReportedVolume)
-                    .build();
-
-            MediaRouteProviderDescriptor providerDescriptor =
-                    new MediaRouteProviderDescriptor.Builder()
-                    .addRoute(defaultRoute)
-                    .build();
-            setDescriptor(providerDescriptor);
-        }
-
-        @Override
-        public RouteController onCreateRouteController(String routeId) {
-            if (routeId.equals(DEFAULT_ROUTE_ID)) {
-                return new DefaultRouteController();
-            }
-            return null;
-        }
-
-        final class DefaultRouteController extends RouteController {
-            @Override
-            public void onSetVolume(int volume) {
-                mAudioManager.setStreamVolume(PLAYBACK_STREAM, volume, 0);
-                publishRoutes();
-            }
-
-            @Override
-            public void onUpdateVolume(int delta) {
-                int volume = mAudioManager.getStreamVolume(PLAYBACK_STREAM);
-                int maxVolume = mAudioManager.getStreamMaxVolume(PLAYBACK_STREAM);
-                int newVolume = Math.min(maxVolume, Math.max(0, volume + delta));
-                if (newVolume != volume) {
-                    mAudioManager.setStreamVolume(PLAYBACK_STREAM, volume, 0);
-                }
-                publishRoutes();
-            }
-        }
-
-        final class VolumeChangeReceiver extends BroadcastReceiver {
-            // These constants come from AudioManager.
-            public static final String VOLUME_CHANGED_ACTION =
-                    "android.media.VOLUME_CHANGED_ACTION";
-            public static final String EXTRA_VOLUME_STREAM_TYPE =
-                    "android.media.EXTRA_VOLUME_STREAM_TYPE";
-            public static final String EXTRA_VOLUME_STREAM_VALUE =
-                    "android.media.EXTRA_VOLUME_STREAM_VALUE";
-
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (intent.getAction().equals(VOLUME_CHANGED_ACTION)) {
-                    final int streamType = intent.getIntExtra(EXTRA_VOLUME_STREAM_TYPE, -1);
-                    if (streamType == PLAYBACK_STREAM) {
-                        final int volume = intent.getIntExtra(EXTRA_VOLUME_STREAM_VALUE, -1);
-                        if (volume >= 0 && volume != mLastReportedVolume) {
-                            publishRoutes();
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Jellybean implementation.
-     */
-    // @@RequiresApi(16)
-    static class JellybeanImpl extends SystemMediaRouteProvider
-            implements MediaRouterJellybean.Callback, MediaRouterJellybean.VolumeCallback {
-        private static final ArrayList<IntentFilter> LIVE_AUDIO_CONTROL_FILTERS;
-        static {
-            IntentFilter f = new IntentFilter();
-            f.addCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO);
-
-            LIVE_AUDIO_CONTROL_FILTERS = new ArrayList<IntentFilter>();
-            LIVE_AUDIO_CONTROL_FILTERS.add(f);
-        }
-
-        private static final ArrayList<IntentFilter> LIVE_VIDEO_CONTROL_FILTERS;
-        static {
-            IntentFilter f = new IntentFilter();
-            f.addCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
-
-            LIVE_VIDEO_CONTROL_FILTERS = new ArrayList<IntentFilter>();
-            LIVE_VIDEO_CONTROL_FILTERS.add(f);
-        }
-
-        private final SyncCallback mSyncCallback;
-
-        protected final Object mRouterObj;
-        protected final Object mCallbackObj;
-        protected final Object mVolumeCallbackObj;
-        protected final Object mUserRouteCategoryObj;
-        protected int mRouteTypes;
-        protected boolean mActiveScan;
-        protected boolean mCallbackRegistered;
-
-        // Maintains an association from framework routes to support library routes.
-        // Note that we cannot use the tag field for this because an application may
-        // have published its own user routes to the framework media router and already
-        // used the tag for its own purposes.
-        protected final ArrayList<SystemRouteRecord> mSystemRouteRecords =
-                new ArrayList<SystemRouteRecord>();
-
-        // Maintains an association from support library routes to framework routes.
-        protected final ArrayList<UserRouteRecord> mUserRouteRecords =
-                new ArrayList<UserRouteRecord>();
-
-        private MediaRouterJellybean.SelectRouteWorkaround mSelectRouteWorkaround;
-        private MediaRouterJellybean.GetDefaultRouteWorkaround mGetDefaultRouteWorkaround;
-
-        public JellybeanImpl(Context context, SyncCallback syncCallback) {
-            super(context);
-            mSyncCallback = syncCallback;
-            mRouterObj = MediaRouterJellybean.getMediaRouter(context);
-            mCallbackObj = createCallbackObj();
-            mVolumeCallbackObj = createVolumeCallbackObj();
-
-            Resources r = ApiHelper.getLibResources(context);
-            mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory(
-                    mRouterObj, r.getString(R.string.mr_user_route_category_name), false);
-
-            updateSystemRoutes();
-        }
-
-        @Override
-        public RouteController onCreateRouteController(String routeId) {
-            int index = findSystemRouteRecordByDescriptorId(routeId);
-            if (index >= 0) {
-                SystemRouteRecord record = mSystemRouteRecords.get(index);
-                return new SystemRouteController(record.mRouteObj);
-            }
-            return null;
-        }
-
-        @Override
-        public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
-            int newRouteTypes = 0;
-            boolean newActiveScan = false;
-            if (request != null) {
-                final MediaRouteSelector selector = request.getSelector();
-                final List<String> categories = selector.getControlCategories();
-                final int count = categories.size();
-                for (int i = 0; i < count; i++) {
-                    String category = categories.get(i);
-                    if (category.equals(MediaControlIntent.CATEGORY_LIVE_AUDIO)) {
-                        newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO;
-                    } else if (category.equals(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
-                        newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO;
-                    } else {
-                        newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_USER;
-                    }
-                }
-                newActiveScan = request.isActiveScan();
-            }
-
-            if (mRouteTypes != newRouteTypes || mActiveScan != newActiveScan) {
-                mRouteTypes = newRouteTypes;
-                mActiveScan = newActiveScan;
-                updateSystemRoutes();
-            }
-        }
-
-        @Override
-        public void onRouteAdded(Object routeObj) {
-            if (addSystemRouteNoPublish(routeObj)) {
-                publishRoutes();
-            }
-        }
-
-        private void updateSystemRoutes() {
-            updateCallback();
-            boolean changed = false;
-            for (Object routeObj : MediaRouterJellybean.getRoutes(mRouterObj)) {
-                changed |= addSystemRouteNoPublish(routeObj);
-            }
-            if (changed) {
-                publishRoutes();
-            }
-        }
-
-        private boolean addSystemRouteNoPublish(Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null
-                    && findSystemRouteRecord(routeObj) < 0) {
-                String id = assignRouteId(routeObj);
-                SystemRouteRecord record = new SystemRouteRecord(routeObj, id);
-                updateSystemRouteDescriptor(record);
-                mSystemRouteRecords.add(record);
-                return true;
-            }
-            return false;
-        }
-
-        private String assignRouteId(Object routeObj) {
-            // TODO: The framework media router should supply a unique route id that
-            // we can use here.  For now we use a hash of the route name and take care
-            // to dedupe it.
-            boolean isDefault = (getDefaultRoute() == routeObj);
-            String id = isDefault ? DEFAULT_ROUTE_ID :
-                    String.format(Locale.US, "ROUTE_%08x", getRouteName(routeObj).hashCode());
-            if (findSystemRouteRecordByDescriptorId(id) < 0) {
-                return id;
-            }
-            for (int i = 2; ; i++) {
-                String newId = String.format(Locale.US, "%s_%d", id, i);
-                if (findSystemRouteRecordByDescriptorId(newId) < 0) {
-                    return newId;
-                }
-            }
-        }
-
-        @Override
-        public void onRouteRemoved(Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null) {
-                int index = findSystemRouteRecord(routeObj);
-                if (index >= 0) {
-                    mSystemRouteRecords.remove(index);
-                    publishRoutes();
-                }
-            }
-        }
-
-        @Override
-        public void onRouteChanged(Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null) {
-                int index = findSystemRouteRecord(routeObj);
-                if (index >= 0) {
-                    SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    updateSystemRouteDescriptor(record);
-                    publishRoutes();
-                }
-            }
-        }
-
-        @Override
-        public void onRouteVolumeChanged(Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null) {
-                int index = findSystemRouteRecord(routeObj);
-                if (index >= 0) {
-                    SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    int newVolume = MediaRouterJellybean.RouteInfo.getVolume(routeObj);
-                    if (newVolume != record.mRouteDescriptor.getVolume()) {
-                        record.mRouteDescriptor =
-                                new MediaRouteDescriptor.Builder(record.mRouteDescriptor)
-                                .setVolume(newVolume)
-                                .build();
-                        publishRoutes();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void onRouteSelected(int type, Object routeObj) {
-            if (routeObj != MediaRouterJellybean.getSelectedRoute(mRouterObj,
-                    MediaRouterJellybean.ALL_ROUTE_TYPES)) {
-                // The currently selected route has already changed so this callback
-                // is stale.  Drop it to prevent getting into sync loops.
-                return;
-            }
-
-            UserRouteRecord userRouteRecord = getUserRouteRecord(routeObj);
-            if (userRouteRecord != null) {
-                userRouteRecord.mRoute.select();
-            } else {
-                // Select the route if it already exists in the compat media router.
-                // If not, we will select it instead when the route is added.
-                int index = findSystemRouteRecord(routeObj);
-                if (index >= 0) {
-                    SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    mSyncCallback.onSystemRouteSelectedByDescriptorId(record.mRouteDescriptorId);
-                }
-            }
-        }
-
-        @Override
-        public void onRouteUnselected(int type, Object routeObj) {
-            // Nothing to do when a route is unselected.
-            // We only need to handle when a route is selected.
-        }
-
-        @Override
-        public void onRouteGrouped(Object routeObj, Object groupObj, int index) {
-            // Route grouping is deprecated and no longer supported.
-        }
-
-        @Override
-        public void onRouteUngrouped(Object routeObj, Object groupObj) {
-            // Route grouping is deprecated and no longer supported.
-        }
-
-        @Override
-        public void onVolumeSetRequest(Object routeObj, int volume) {
-            UserRouteRecord record = getUserRouteRecord(routeObj);
-            if (record != null) {
-                record.mRoute.requestSetVolume(volume);
-            }
-        }
-
-        @Override
-        public void onVolumeUpdateRequest(Object routeObj, int direction) {
-            UserRouteRecord record = getUserRouteRecord(routeObj);
-            if (record != null) {
-                record.mRoute.requestUpdateVolume(direction);
-            }
-        }
-
-        @Override
-        public void onSyncRouteAdded(MediaRouter.RouteInfo route) {
-            if (route.getProviderInstance() != this) {
-                Object routeObj = MediaRouterJellybean.createUserRoute(
-                        mRouterObj, mUserRouteCategoryObj);
-                UserRouteRecord record = new UserRouteRecord(route, routeObj);
-                MediaRouterJellybean.RouteInfo.setTag(routeObj, record);
-                MediaRouterJellybean.UserRouteInfo.setVolumeCallback(routeObj, mVolumeCallbackObj);
-                updateUserRouteProperties(record);
-                mUserRouteRecords.add(record);
-                MediaRouterJellybean.addUserRoute(mRouterObj, routeObj);
-            } else {
-                // If the newly added route is the counterpart of the currently selected
-                // route in the framework media router then ensure it is selected in
-                // the compat media router.
-                Object routeObj = MediaRouterJellybean.getSelectedRoute(
-                        mRouterObj, MediaRouterJellybean.ALL_ROUTE_TYPES);
-                int index = findSystemRouteRecord(routeObj);
-                if (index >= 0) {
-                    SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    if (record.mRouteDescriptorId.equals(route.getDescriptorId())) {
-                        route.select();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void onSyncRouteRemoved(MediaRouter.RouteInfo route) {
-            if (route.getProviderInstance() != this) {
-                int index = findUserRouteRecord(route);
-                if (index >= 0) {
-                    UserRouteRecord record = mUserRouteRecords.remove(index);
-                    MediaRouterJellybean.RouteInfo.setTag(record.mRouteObj, null);
-                    MediaRouterJellybean.UserRouteInfo.setVolumeCallback(record.mRouteObj, null);
-                    MediaRouterJellybean.removeUserRoute(mRouterObj, record.mRouteObj);
-                }
-            }
-        }
-
-        @Override
-        public void onSyncRouteChanged(MediaRouter.RouteInfo route) {
-            if (route.getProviderInstance() != this) {
-                int index = findUserRouteRecord(route);
-                if (index >= 0) {
-                    UserRouteRecord record = mUserRouteRecords.get(index);
-                    updateUserRouteProperties(record);
-                }
-            }
-        }
-
-        @Override
-        public void onSyncRouteSelected(MediaRouter.RouteInfo route) {
-            if (!route.isSelected()) {
-                // The currently selected route has already changed so this callback
-                // is stale.  Drop it to prevent getting into sync loops.
-                return;
-            }
-
-            if (route.getProviderInstance() != this) {
-                int index = findUserRouteRecord(route);
-                if (index >= 0) {
-                    UserRouteRecord record = mUserRouteRecords.get(index);
-                    selectRoute(record.mRouteObj);
-                }
-            } else {
-                int index = findSystemRouteRecordByDescriptorId(route.getDescriptorId());
-                if (index >= 0) {
-                    SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    selectRoute(record.mRouteObj);
-                }
-            }
-        }
-
-        protected void publishRoutes() {
-            MediaRouteProviderDescriptor.Builder builder =
-                    new MediaRouteProviderDescriptor.Builder();
-            int count = mSystemRouteRecords.size();
-            for (int i = 0; i < count; i++) {
-                builder.addRoute(mSystemRouteRecords.get(i).mRouteDescriptor);
-            }
-
-            setDescriptor(builder.build());
-        }
-
-        protected int findSystemRouteRecord(Object routeObj) {
-            final int count = mSystemRouteRecords.size();
-            for (int i = 0; i < count; i++) {
-                if (mSystemRouteRecords.get(i).mRouteObj == routeObj) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        protected int findSystemRouteRecordByDescriptorId(String id) {
-            final int count = mSystemRouteRecords.size();
-            for (int i = 0; i < count; i++) {
-                if (mSystemRouteRecords.get(i).mRouteDescriptorId.equals(id)) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        protected int findUserRouteRecord(MediaRouter.RouteInfo route) {
-            final int count = mUserRouteRecords.size();
-            for (int i = 0; i < count; i++) {
-                if (mUserRouteRecords.get(i).mRoute == route) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        protected UserRouteRecord getUserRouteRecord(Object routeObj) {
-            Object tag = MediaRouterJellybean.RouteInfo.getTag(routeObj);
-            return tag instanceof UserRouteRecord ? (UserRouteRecord)tag : null;
-        }
-
-        protected void updateSystemRouteDescriptor(SystemRouteRecord record) {
-            // We must always recreate the route descriptor when making any changes
-            // because they are intended to be immutable once published.
-            MediaRouteDescriptor.Builder builder = new MediaRouteDescriptor.Builder(
-                    record.mRouteDescriptorId, getRouteName(record.mRouteObj));
-            onBuildSystemRouteDescriptor(record, builder);
-            record.mRouteDescriptor = builder.build();
-        }
-
-        protected String getRouteName(Object routeObj) {
-            // Routes should not have null names but it may happen for badly configured
-            // user routes.  We tolerate this by using an empty name string here but
-            // such unnamed routes will be discarded by the media router upstream
-            // (with a log message so we can track down the problem).
-            CharSequence name = MediaRouterJellybean.RouteInfo.getName(routeObj, getContext());
-            return name != null ? name.toString() : "";
-        }
-
-        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
-                MediaRouteDescriptor.Builder builder) {
-            int supportedTypes = MediaRouterJellybean.RouteInfo.getSupportedTypes(
-                    record.mRouteObj);
-            if ((supportedTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO) != 0) {
-                builder.addControlFilters(LIVE_AUDIO_CONTROL_FILTERS);
-            }
-            if ((supportedTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) {
-                builder.addControlFilters(LIVE_VIDEO_CONTROL_FILTERS);
-            }
-
-            builder.setPlaybackType(
-                    MediaRouterJellybean.RouteInfo.getPlaybackType(record.mRouteObj));
-            builder.setPlaybackStream(
-                    MediaRouterJellybean.RouteInfo.getPlaybackStream(record.mRouteObj));
-            builder.setVolume(
-                    MediaRouterJellybean.RouteInfo.getVolume(record.mRouteObj));
-            builder.setVolumeMax(
-                    MediaRouterJellybean.RouteInfo.getVolumeMax(record.mRouteObj));
-            builder.setVolumeHandling(
-                    MediaRouterJellybean.RouteInfo.getVolumeHandling(record.mRouteObj));
-        }
-
-        protected void updateUserRouteProperties(UserRouteRecord record) {
-            MediaRouterJellybean.UserRouteInfo.setName(
-                    record.mRouteObj, record.mRoute.getName());
-            MediaRouterJellybean.UserRouteInfo.setPlaybackType(
-                    record.mRouteObj, record.mRoute.getPlaybackType());
-            MediaRouterJellybean.UserRouteInfo.setPlaybackStream(
-                    record.mRouteObj, record.mRoute.getPlaybackStream());
-            MediaRouterJellybean.UserRouteInfo.setVolume(
-                    record.mRouteObj, record.mRoute.getVolume());
-            MediaRouterJellybean.UserRouteInfo.setVolumeMax(
-                    record.mRouteObj, record.mRoute.getVolumeMax());
-            MediaRouterJellybean.UserRouteInfo.setVolumeHandling(
-                    record.mRouteObj, record.mRoute.getVolumeHandling());
-        }
-
-        protected void updateCallback() {
-            if (mCallbackRegistered) {
-                mCallbackRegistered = false;
-                MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj);
-            }
-
-            if (mRouteTypes != 0) {
-                mCallbackRegistered = true;
-                MediaRouterJellybean.addCallback(mRouterObj, mRouteTypes, mCallbackObj);
-            }
-        }
-
-        protected Object createCallbackObj() {
-            return MediaRouterJellybean.createCallback(this);
-        }
-
-        protected Object createVolumeCallbackObj() {
-            return MediaRouterJellybean.createVolumeCallback(this);
-        }
-
-        protected void selectRoute(Object routeObj) {
-            if (mSelectRouteWorkaround == null) {
-                mSelectRouteWorkaround = new MediaRouterJellybean.SelectRouteWorkaround();
-            }
-            mSelectRouteWorkaround.selectRoute(mRouterObj,
-                    MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
-        }
-
-        @Override
-        protected Object getDefaultRoute() {
-            if (mGetDefaultRouteWorkaround == null) {
-                mGetDefaultRouteWorkaround = new MediaRouterJellybean.GetDefaultRouteWorkaround();
-            }
-            return mGetDefaultRouteWorkaround.getDefaultRoute(mRouterObj);
-        }
-
-        @Override
-        protected Object getSystemRoute(MediaRouter.RouteInfo route) {
-            if (route == null) {
-                return null;
-            }
-            int index = findSystemRouteRecordByDescriptorId(route.getDescriptorId());
-            if (index >= 0) {
-                return mSystemRouteRecords.get(index).mRouteObj;
-            }
-            return null;
-        }
-
-        /**
-         * Represents a route that is provided by the framework media router
-         * and published by this route provider to the support library media router.
-         */
-        protected static final class SystemRouteRecord {
-            public final Object mRouteObj;
-            public final String mRouteDescriptorId;
-            public MediaRouteDescriptor mRouteDescriptor; // assigned immediately after creation
-
-            public SystemRouteRecord(Object routeObj, String id) {
-                mRouteObj = routeObj;
-                mRouteDescriptorId = id;
-            }
-        }
-
-        /**
-         * Represents a route that is provided by the support library media router
-         * and published by this route provider to the framework media router.
-         */
-        protected static final class UserRouteRecord {
-            public final MediaRouter.RouteInfo mRoute;
-            public final Object mRouteObj;
-
-            public UserRouteRecord(MediaRouter.RouteInfo route, Object routeObj) {
-                mRoute = route;
-                mRouteObj = routeObj;
-            }
-        }
-
-        protected static final class SystemRouteController extends RouteController {
-            private final Object mRouteObj;
-
-            public SystemRouteController(Object routeObj) {
-                mRouteObj = routeObj;
-            }
-
-            @Override
-            public void onSetVolume(int volume) {
-                MediaRouterJellybean.RouteInfo.requestSetVolume(mRouteObj, volume);
-            }
-
-            @Override
-            public void onUpdateVolume(int delta) {
-                MediaRouterJellybean.RouteInfo.requestUpdateVolume(mRouteObj, delta);
-            }
-        }
-    }
-
-    /**
-     * Jellybean MR1 implementation.
-     */
-    // @@RequiresApi(17)
-    private static class JellybeanMr1Impl extends JellybeanImpl
-            implements MediaRouterJellybeanMr1.Callback {
-        private MediaRouterJellybeanMr1.ActiveScanWorkaround mActiveScanWorkaround;
-        private MediaRouterJellybeanMr1.IsConnectingWorkaround mIsConnectingWorkaround;
-
-        public JellybeanMr1Impl(Context context, SyncCallback syncCallback) {
-            super(context, syncCallback);
-        }
-
-        @Override
-        public void onRoutePresentationDisplayChanged(Object routeObj) {
-            int index = findSystemRouteRecord(routeObj);
-            if (index >= 0) {
-                SystemRouteRecord record = mSystemRouteRecords.get(index);
-                Display newPresentationDisplay =
-                        MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(routeObj);
-                int newPresentationDisplayId = (newPresentationDisplay != null
-                        ? newPresentationDisplay.getDisplayId() : -1);
-                if (newPresentationDisplayId
-                        != record.mRouteDescriptor.getPresentationDisplayId()) {
-                    record.mRouteDescriptor =
-                            new MediaRouteDescriptor.Builder(record.mRouteDescriptor)
-                            .setPresentationDisplayId(newPresentationDisplayId)
-                            .build();
-                    publishRoutes();
-                }
-            }
-        }
-
-        @Override
-        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
-                MediaRouteDescriptor.Builder builder) {
-            super.onBuildSystemRouteDescriptor(record, builder);
-
-            if (!MediaRouterJellybeanMr1.RouteInfo.isEnabled(record.mRouteObj)) {
-                builder.setEnabled(false);
-            }
-
-            if (isConnecting(record)) {
-                builder.setConnecting(true);
-            }
-
-            Display presentationDisplay =
-                    MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(record.mRouteObj);
-            if (presentationDisplay != null) {
-                builder.setPresentationDisplayId(presentationDisplay.getDisplayId());
-            }
-        }
-
-        @Override
-        protected void updateCallback() {
-            super.updateCallback();
-
-            if (mActiveScanWorkaround == null) {
-                mActiveScanWorkaround = new MediaRouterJellybeanMr1.ActiveScanWorkaround(
-                        getContext(), getHandler());
-            }
-            mActiveScanWorkaround.setActiveScanRouteTypes(mActiveScan ? mRouteTypes : 0);
-        }
-
-        @Override
-        protected Object createCallbackObj() {
-            return MediaRouterJellybeanMr1.createCallback(this);
-        }
-
-        protected boolean isConnecting(SystemRouteRecord record) {
-            if (mIsConnectingWorkaround == null) {
-                mIsConnectingWorkaround = new MediaRouterJellybeanMr1.IsConnectingWorkaround();
-            }
-            return mIsConnectingWorkaround.isConnecting(record.mRouteObj);
-        }
-    }
-
-    /**
-     * Jellybean MR2 implementation.
-     */
-    // @@RequiresApi(18)
-    private static class JellybeanMr2Impl extends JellybeanMr1Impl {
-        public JellybeanMr2Impl(Context context, SyncCallback syncCallback) {
-            super(context, syncCallback);
-        }
-
-        @Override
-        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
-                MediaRouteDescriptor.Builder builder) {
-            super.onBuildSystemRouteDescriptor(record, builder);
-
-            CharSequence description =
-                    MediaRouterJellybeanMr2.RouteInfo.getDescription(record.mRouteObj);
-            if (description != null) {
-                builder.setDescription(description.toString());
-            }
-        }
-
-        @Override
-        protected void selectRoute(Object routeObj) {
-            MediaRouterJellybean.selectRoute(mRouterObj,
-                    MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
-        }
-
-        @Override
-        protected Object getDefaultRoute() {
-            return MediaRouterJellybeanMr2.getDefaultRoute(mRouterObj);
-        }
-
-        @Override
-        protected void updateUserRouteProperties(UserRouteRecord record) {
-            super.updateUserRouteProperties(record);
-
-            MediaRouterJellybeanMr2.UserRouteInfo.setDescription(
-                    record.mRouteObj, record.mRoute.getDescription());
-        }
-
-        @Override
-        protected void updateCallback() {
-            if (mCallbackRegistered) {
-                MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj);
-            }
-
-            mCallbackRegistered = true;
-            MediaRouterJellybeanMr2.addCallback(mRouterObj, mRouteTypes, mCallbackObj,
-                    MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS
-                    | (mActiveScan ? MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN : 0));
-        }
-
-        @Override
-        protected boolean isConnecting(SystemRouteRecord record) {
-            return MediaRouterJellybeanMr2.RouteInfo.isConnecting(record.mRouteObj);
-        }
-    }
-
-    /**
-     * Api24 implementation.
-     */
-    // @@RequiresApi(24)
-    private static class Api24Impl extends JellybeanMr2Impl {
-        public Api24Impl(Context context, SyncCallback syncCallback) {
-            super(context, syncCallback);
-        }
-
-        @Override
-        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
-                                                    MediaRouteDescriptor.Builder builder) {
-            super.onBuildSystemRouteDescriptor(record, builder);
-
-            builder.setDeviceType(MediaRouterApi24.RouteInfo.getDeviceType(record.mRouteObj));
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/media/package.html b/packages/MediaComponents/src/com/android/support/mediarouter/media/package.html
deleted file mode 100644
index be2aaf2..0000000
--- a/packages/MediaComponents/src/com/android/support/mediarouter/media/package.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<html>
-
-<body>
-
-<p>Contains APIs that control the routing of media channels and streams from the current device
-  to external speakers and destination devices.</p>
-
-</body>
-</html>
diff --git a/packages/MediaComponents/src/com/android/widget/BaseLayout.java b/packages/MediaComponents/src/com/android/widget/BaseLayout.java
deleted file mode 100644
index fb6471d..0000000
--- a/packages/MediaComponents/src/com/android/widget/BaseLayout.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * 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.widget;
-
-import android.graphics.drawable.Drawable;
-import android.media.update.ViewGroupProvider;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.ViewGroup.MarginLayoutParams;
-
-import java.util.ArrayList;
-
-public class BaseLayout extends ViewGroupImpl {
-    private final ViewGroup mInstance;
-    private final ViewGroupProvider mSuperProvider;
-    private final ViewGroupProvider mPrivateProvider;
-
-    private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
-
-    public BaseLayout(ViewGroup instance,
-            ViewGroupProvider superProvider, ViewGroupProvider privateProvider) {
-        super(instance, superProvider, privateProvider);
-        mInstance = instance;
-        mSuperProvider = superProvider;
-        mPrivateProvider = privateProvider;
-    }
-
-    @Override
-    public boolean checkLayoutParams_impl(LayoutParams p) {
-        return p instanceof MarginLayoutParams;
-    }
-
-    @Override
-    public LayoutParams generateDefaultLayoutParams_impl() {
-        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
-        return new MarginLayoutParams(mInstance.getContext(), attrs);
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
-        if (lp instanceof MarginLayoutParams) {
-            return lp;
-        }
-        return new MarginLayoutParams(lp);
-    }
-
-    @Override
-    public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
-        int count = mInstance.getChildCount();
-
-        final boolean measureMatchParentChildren =
-                View.MeasureSpec.getMode(widthMeasureSpec) != View.MeasureSpec.EXACTLY ||
-                        View.MeasureSpec.getMode(heightMeasureSpec) != View.MeasureSpec.EXACTLY;
-        mMatchParentChildren.clear();
-
-        int maxHeight = 0;
-        int maxWidth = 0;
-        int childState = 0;
-
-        for (int i = 0; i < count; i++) {
-            final View child = mInstance.getChildAt(i);
-            if (child.getVisibility() != View.GONE) {
-                mPrivateProvider.measureChildWithMargins_impl(
-                        child, widthMeasureSpec, 0, heightMeasureSpec, 0);
-                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
-                maxWidth = Math.max(maxWidth,
-                        child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
-                maxHeight = Math.max(maxHeight,
-                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
-                childState = childState | child.getMeasuredState();
-                if (measureMatchParentChildren) {
-                    if (lp.width == LayoutParams.MATCH_PARENT ||
-                            lp.height == LayoutParams.MATCH_PARENT) {
-                        mMatchParentChildren.add(child);
-                    }
-                }
-            }
-        }
-
-        // Account for padding too
-        maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
-        maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
-
-        // Check against our minimum height and width
-        maxHeight = Math.max(maxHeight, mPrivateProvider.getSuggestedMinimumHeight_impl());
-        maxWidth = Math.max(maxWidth, mPrivateProvider.getSuggestedMinimumWidth_impl());
-
-        // Check against our foreground's minimum height and width
-        final Drawable drawable = mInstance.getForeground();
-        if (drawable != null) {
-            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
-            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
-        }
-
-        mPrivateProvider.setMeasuredDimension_impl(
-                mInstance.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
-                mInstance.resolveSizeAndState(maxHeight, heightMeasureSpec,
-                        childState << View.MEASURED_HEIGHT_STATE_SHIFT));
-
-        count = mMatchParentChildren.size();
-        if (count > 1) {
-            for (int i = 0; i < count; i++) {
-                final View child = mMatchParentChildren.get(i);
-                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
-
-                final int childWidthMeasureSpec;
-                if (lp.width == LayoutParams.MATCH_PARENT) {
-                    final int width = Math.max(0, mInstance.getMeasuredWidth()
-                            - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
-                            - lp.leftMargin - lp.rightMargin);
-                    childWidthMeasureSpec = View.MeasureSpec.makeMeasureSpec(
-                            width, View.MeasureSpec.EXACTLY);
-                } else {
-                    childWidthMeasureSpec = mInstance.getChildMeasureSpec(widthMeasureSpec,
-                            getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
-                                    lp.leftMargin + lp.rightMargin,
-                            lp.width);
-                }
-
-                final int childHeightMeasureSpec;
-                if (lp.height == LayoutParams.MATCH_PARENT) {
-                    final int height = Math.max(0, mInstance.getMeasuredHeight()
-                            - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
-                            - lp.topMargin - lp.bottomMargin);
-                    childHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
-                            height, View.MeasureSpec.EXACTLY);
-                } else {
-                    childHeightMeasureSpec = mInstance.getChildMeasureSpec(heightMeasureSpec,
-                            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
-                                    lp.topMargin + lp.bottomMargin,
-                            lp.height);
-                }
-
-                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-            }
-        }
-    }
-
-    @Override
-    public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
-        final int count = mInstance.getChildCount();
-
-        final int parentLeft = getPaddingLeftWithForeground();
-        final int parentRight = right - left - getPaddingRightWithForeground();
-
-        final int parentTop = getPaddingTopWithForeground();
-        final int parentBottom = bottom - top - getPaddingBottomWithForeground();
-
-        for (int i = 0; i < count; i++) {
-            final View child = mInstance.getChildAt(i);
-            if (child.getVisibility() != View.GONE) {
-                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
-
-                final int width = child.getMeasuredWidth();
-                final int height = child.getMeasuredHeight();
-
-                int childLeft;
-                int childTop;
-
-                childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
-                        lp.leftMargin - lp.rightMargin;
-
-                childTop = parentTop + (parentBottom - parentTop - height) / 2 +
-                        lp.topMargin - lp.bottomMargin;
-
-                child.layout(childLeft, childTop, childLeft + width, childTop + height);
-            }
-        }
-    }
-
-    @Override
-    public boolean shouldDelayChildPressedState_impl() {
-        return false;
-    }
-
-    private int getPaddingLeftWithForeground() {
-        return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingLeft(), 0) :
-                mInstance.getPaddingLeft() + 0;
-    }
-
-    private int getPaddingRightWithForeground() {
-        return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingRight(), 0) :
-                mInstance.getPaddingRight() + 0;
-    }
-
-    private int getPaddingTopWithForeground() {
-        return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingTop(), 0) :
-                mInstance.getPaddingTop() + 0;
-    }
-
-    private int getPaddingBottomWithForeground() {
-        return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingBottom(), 0) :
-                mInstance.getPaddingBottom() + 0;
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
deleted file mode 100644
index ad85af4..0000000
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ /dev/null
@@ -1,1721 +0,0 @@
-/*
- * 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.
- * 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.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.media.MediaMetadata;
-import android.media.SessionToken2;
-import android.media.session.MediaController;
-import android.media.session.PlaybackState;
-import android.media.update.MediaControlView2Provider;
-import android.media.update.ViewGroupProvider;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.MediaControlView2;
-import android.widget.PopupWindow;
-import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.media.update.ApiHelper;
-import com.android.media.update.R;
-import com.android.support.mediarouter.app.MediaRouteButton;
-import com.android.support.mediarouter.media.MediaRouteSelector;
-import com.android.support.mediarouter.media.MediaRouter;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Formatter;
-import java.util.List;
-import java.util.Locale;
-
-public class MediaControlView2Impl extends BaseLayout implements MediaControlView2Provider {
-    private static final String TAG = "MediaControlView2";
-
-    private final MediaControlView2 mInstance;
-
-    static final String ARGUMENT_KEY_FULLSCREEN = "fullScreen";
-
-    // TODO: Move these constants to public api to support custom video view.
-    // TODO: Combine these constants into one regarding TrackInfo.
-    static final String KEY_VIDEO_TRACK_COUNT = "VideoTrackCount";
-    static final String KEY_AUDIO_TRACK_COUNT = "AudioTrackCount";
-    static final String KEY_SUBTITLE_TRACK_COUNT = "SubtitleTrackCount";
-    static final String KEY_PLAYBACK_SPEED = "PlaybackSpeed";
-    static final String KEY_SELECTED_AUDIO_INDEX = "SelectedAudioIndex";
-    static final String KEY_SELECTED_SUBTITLE_INDEX = "SelectedSubtitleIndex";
-    static final String EVENT_UPDATE_TRACK_STATUS = "UpdateTrackStatus";
-
-    // TODO: Remove this once integrating with MediaSession2 & MediaMetadata2
-    static final String KEY_STATE_IS_ADVERTISEMENT = "MediaTypeAdvertisement";
-    static final String EVENT_UPDATE_MEDIA_TYPE_STATUS = "UpdateMediaTypeStatus";
-
-    // String for sending command to show subtitle to MediaSession.
-    static final String COMMAND_SHOW_SUBTITLE = "showSubtitle";
-    // String for sending command to hide subtitle to MediaSession.
-    static final String COMMAND_HIDE_SUBTITLE = "hideSubtitle";
-    // TODO: remove once the implementation is revised
-    public static final String COMMAND_SET_FULLSCREEN = "setFullscreen";
-    // String for sending command to select audio track to MediaSession.
-    static final String COMMAND_SELECT_AUDIO_TRACK = "SelectTrack";
-    // String for sending command to set playback speed to MediaSession.
-    static final String COMMAND_SET_PLAYBACK_SPEED = "SetPlaybackSpeed";
-    // String for sending command to mute audio to MediaSession.
-    static final String COMMAND_MUTE= "Mute";
-    // String for sending command to unmute audio to MediaSession.
-    static final String COMMAND_UNMUTE = "Unmute";
-
-    private static final int SETTINGS_MODE_AUDIO_TRACK = 0;
-    private static final int SETTINGS_MODE_PLAYBACK_SPEED = 1;
-    private static final int SETTINGS_MODE_HELP = 2;
-    private static final int SETTINGS_MODE_SUBTITLE_TRACK = 3;
-    private static final int SETTINGS_MODE_VIDEO_QUALITY = 4;
-    private static final int SETTINGS_MODE_MAIN = 5;
-    private static final int PLAYBACK_SPEED_1x_INDEX = 3;
-
-    private static final int MEDIA_TYPE_DEFAULT = 0;
-    private static final int MEDIA_TYPE_MUSIC = 1;
-    private static final int MEDIA_TYPE_ADVERTISEMENT = 2;
-
-    private static final int SIZE_TYPE_EMBEDDED = 0;
-    private static final int SIZE_TYPE_FULL = 1;
-    // TODO: add support for Minimal size type.
-    private static final int SIZE_TYPE_MINIMAL = 2;
-
-    private static final int MAX_PROGRESS = 1000;
-    private static final int DEFAULT_PROGRESS_UPDATE_TIME_MS = 1000;
-    private static final int REWIND_TIME_MS = 10000;
-    private static final int FORWARD_TIME_MS = 30000;
-    private static final int AD_SKIP_WAIT_TIME_MS = 5000;
-    private static final int RESOURCE_NON_EXISTENT = -1;
-    private static final String RESOURCE_EMPTY = "";
-
-    private Resources mResources;
-    private MediaController mController;
-    private MediaController.TransportControls mControls;
-    private PlaybackState mPlaybackState;
-    private MediaMetadata mMetadata;
-    private int mDuration;
-    private int mPrevState;
-    private int mPrevWidth;
-    private int mPrevHeight;
-    private int mOriginalLeftBarWidth;
-    private int mVideoTrackCount;
-    private int mAudioTrackCount;
-    private int mSubtitleTrackCount;
-    private int mSettingsMode;
-    private int mSelectedSubtitleTrackIndex;
-    private int mSelectedAudioTrackIndex;
-    private int mSelectedVideoQualityIndex;
-    private int mSelectedSpeedIndex;
-    private int mEmbeddedSettingsItemWidth;
-    private int mFullSettingsItemWidth;
-    private int mEmbeddedSettingsItemHeight;
-    private int mFullSettingsItemHeight;
-    private int mSettingsWindowMargin;
-    private int mMediaType;
-    private int mSizeType;
-    private long mPlaybackActions;
-    private boolean mDragging;
-    private boolean mIsFullScreen;
-    private boolean mOverflowExpanded;
-    private boolean mIsStopped;
-    private boolean mSubtitleIsEnabled;
-    private boolean mSeekAvailable;
-    private boolean mIsAdvertisement;
-    private boolean mIsMute;
-    private boolean mNeedUXUpdate;
-
-    // Relating to Title Bar View
-    private ViewGroup mRoot;
-    private View mTitleBar;
-    private TextView mTitleView;
-    private View mAdExternalLink;
-    private ImageButton mBackButton;
-    private MediaRouteButton mRouteButton;
-    private MediaRouteSelector mRouteSelector;
-
-    // Relating to Center View
-    private ViewGroup mCenterView;
-    private View mTransportControls;
-    private ImageButton mPlayPauseButton;
-    private ImageButton mFfwdButton;
-    private ImageButton mRewButton;
-    private ImageButton mNextButton;
-    private ImageButton mPrevButton;
-
-    // Relating to Minimal Extra View
-    private LinearLayout mMinimalExtraView;
-
-    // Relating to Progress Bar View
-    private ProgressBar mProgress;
-    private View mProgressBuffer;
-
-    // Relating to Bottom Bar View
-    private ViewGroup mBottomBar;
-
-    // Relating to Bottom Bar Left View
-    private ViewGroup mBottomBarLeftView;
-    private ViewGroup mTimeView;
-    private TextView mEndTime;
-    private TextView mCurrentTime;
-    private TextView mAdSkipView;
-    private StringBuilder mFormatBuilder;
-    private Formatter mFormatter;
-
-    // Relating to Bottom Bar Right View
-    private ViewGroup mBottomBarRightView;
-    private ViewGroup mBasicControls;
-    private ViewGroup mExtraControls;
-    private ViewGroup mCustomButtons;
-    private ImageButton mSubtitleButton;
-    private ImageButton mFullScreenButton;
-    private ImageButton mOverflowButtonRight;
-    private ImageButton mOverflowButtonLeft;
-    private ImageButton mMuteButton;
-    private ImageButton mVideoQualityButton;
-    private ImageButton mSettingsButton;
-    private TextView mAdRemainingView;
-
-    // Relating to Settings List View
-    private ListView mSettingsListView;
-    private PopupWindow mSettingsWindow;
-    private SettingsAdapter mSettingsAdapter;
-    private SubSettingsAdapter mSubSettingsAdapter;
-    private List<String> mSettingsMainTextsList;
-    private List<String> mSettingsSubTextsList;
-    private List<Integer> mSettingsIconIdsList;
-    private List<String> mSubtitleDescriptionsList;
-    private List<String> mAudioTrackList;
-    private List<String> mVideoQualityList;
-    private List<String> mPlaybackSpeedTextList;
-    private List<Float> mPlaybackSpeedList;
-
-    public MediaControlView2Impl(MediaControlView2 instance,
-            ViewGroupProvider superProvider, ViewGroupProvider privateProvider) {
-        super(instance, superProvider, privateProvider);
-        mInstance = instance;
-    }
-
-    @Override
-    public void initialize(@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        mResources = ApiHelper.getLibResources(mInstance.getContext());
-        // Inflate MediaControlView2 from XML
-        mRoot = makeControllerView();
-        mInstance.addView(mRoot);
-    }
-
-    @Override
-    public void setMediaSessionToken_impl(SessionToken2 token) {
-        // TODO: implement this
-    }
-
-    @Override
-    public void setOnFullScreenListener_impl(MediaControlView2.OnFullScreenListener l) {
-        // TODO: implement this
-    }
-
-    @Override
-    public void setController_impl(MediaController controller) {
-        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 setButtonVisibility_impl(int button, int visibility) {
-        // TODO: add member variables for Fast-Forward/Prvious/Rewind buttons to save visibility in
-        // order to prevent being overriden inside updateLayout().
-        switch (button) {
-            case MediaControlView2.BUTTON_PLAY_PAUSE:
-                if (mPlayPauseButton != null && canPause()) {
-                    mPlayPauseButton.setVisibility(visibility);
-                }
-                break;
-            case MediaControlView2.BUTTON_FFWD:
-                if (mFfwdButton != null && canSeekForward()) {
-                    mFfwdButton.setVisibility(visibility);
-                }
-                break;
-            case MediaControlView2.BUTTON_REW:
-                if (mRewButton != null && canSeekBackward()) {
-                    mRewButton.setVisibility(visibility);
-                }
-                break;
-            case MediaControlView2.BUTTON_NEXT:
-                if (mNextButton != null) {
-                    mNextButton.setVisibility(visibility);
-                }
-                break;
-            case MediaControlView2.BUTTON_PREV:
-                if (mPrevButton != null) {
-                    mPrevButton.setVisibility(visibility);
-                }
-                break;
-            case MediaControlView2.BUTTON_SUBTITLE:
-                if (mSubtitleButton != null && mSubtitleTrackCount > 0) {
-                    mSubtitleButton.setVisibility(visibility);
-                }
-                break;
-            case MediaControlView2.BUTTON_FULL_SCREEN:
-                if (mFullScreenButton != null) {
-                    mFullScreenButton.setVisibility(visibility);
-                }
-                break;
-            case MediaControlView2.BUTTON_OVERFLOW:
-                if (mOverflowButtonRight != null) {
-                    mOverflowButtonRight.setVisibility(visibility);
-                }
-                break;
-            case MediaControlView2.BUTTON_MUTE:
-                if (mMuteButton != null) {
-                    mMuteButton.setVisibility(visibility);
-                }
-                break;
-            case MediaControlView2.BUTTON_SETTINGS:
-                if (mSettingsButton != null) {
-                    mSettingsButton.setVisibility(visibility);
-                }
-                break;
-            default:
-                break;
-        }
-    }
-
-    @Override
-    public void requestPlayButtonFocus_impl() {
-        if (mPlayPauseButton != null) {
-            mPlayPauseButton.requestFocus();
-        }
-    }
-
-    @Override
-    public CharSequence getAccessibilityClassName_impl() {
-        return MediaControlView2.class.getName();
-    }
-
-    @Override
-    public boolean onTouchEvent_impl(MotionEvent ev) {
-        return false;
-    }
-
-    // TODO: Should this function be removed?
-    @Override
-    public boolean onTrackballEvent_impl(MotionEvent ev) {
-        return false;
-    }
-
-    @Override
-    public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure_impl(widthMeasureSpec, heightMeasureSpec);
-
-        // Update layout when this view's width changes in order to avoid any UI overlap between
-        // transport controls.
-        if (mPrevWidth != mInstance.getMeasuredWidth()
-                || mPrevHeight != mInstance.getMeasuredHeight() || mNeedUXUpdate) {
-            // Dismiss SettingsWindow if it is showing.
-            mSettingsWindow.dismiss();
-
-            // These views may not have been initialized yet.
-            if (mTransportControls.getWidth() == 0 || mTimeView.getWidth() == 0) {
-                return;
-            }
-
-            int currWidth = mInstance.getMeasuredWidth();
-            int currHeight = mInstance.getMeasuredHeight();
-            WindowManager manager = (WindowManager) mInstance.getContext().getApplicationContext()
-                    .getSystemService(Context.WINDOW_SERVICE);
-            Point screenSize = new Point();
-            manager.getDefaultDisplay().getSize(screenSize);
-            int screenWidth = screenSize.x;
-            int screenHeight = screenSize.y;
-            int fullIconSize = mResources.getDimensionPixelSize(R.dimen.mcv2_full_icon_size);
-            int embeddedIconSize = mResources.getDimensionPixelSize(
-                    R.dimen.mcv2_embedded_icon_size);
-            int marginSize = mResources.getDimensionPixelSize(R.dimen.mcv2_icon_margin);
-
-            // TODO: add support for Advertisement Mode.
-            if (mMediaType == MEDIA_TYPE_DEFAULT) {
-                // Max number of icons inside BottomBarRightView for Music mode is 4.
-                int maxIconCount = 4;
-                updateLayout(maxIconCount, fullIconSize, embeddedIconSize, marginSize, currWidth,
-                        currHeight, screenWidth, screenHeight);
-            } else if (mMediaType == MEDIA_TYPE_MUSIC) {
-                if (mNeedUXUpdate) {
-                    // One-time operation for Music media type
-                    mBasicControls.removeView(mMuteButton);
-                    mExtraControls.addView(mMuteButton, 0);
-                    mVideoQualityButton.setVisibility(View.GONE);
-                    if (mFfwdButton != null) {
-                        mFfwdButton.setVisibility(View.GONE);
-                    }
-                    if (mRewButton != null) {
-                        mRewButton.setVisibility(View.GONE);
-                    }
-                }
-                mNeedUXUpdate = false;
-
-                // Max number of icons inside BottomBarRightView for Music mode is 3.
-                int maxIconCount = 3;
-                updateLayout(maxIconCount, fullIconSize, embeddedIconSize, marginSize, currWidth,
-                        currHeight, screenWidth, screenHeight);
-            }
-            mPrevWidth = currWidth;
-            mPrevHeight = currHeight;
-        }
-        // TODO: move this to a different location.
-        // Update title bar parameters in order to avoid overlap between title view and the right
-        // side of the title bar.
-        updateTitleBarLayout();
-    }
-
-    @Override
-    public void setEnabled_impl(boolean enabled) {
-        super.setEnabled_impl(enabled);
-
-        // TODO: Merge the below code with disableUnsupportedButtons().
-        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();
-    }
-
-    @Override
-    public void onVisibilityAggregated_impl(boolean isVisible) {
-        super.onVisibilityAggregated_impl(isVisible);
-
-        if (isVisible) {
-            disableUnsupportedButtons();
-            mInstance.removeCallbacks(mUpdateProgress);
-            mInstance.post(mUpdateProgress);
-        } else {
-            mInstance.removeCallbacks(mUpdateProgress);
-        }
-    }
-
-    public void setRouteSelector(MediaRouteSelector selector) {
-        mRouteSelector = selector;
-        if (mRouteSelector != null && !mRouteSelector.isEmpty()) {
-            mRouteButton.setRouteSelector(selector, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-            mRouteButton.setVisibility(View.VISIBLE);
-        } else {
-            mRouteButton.setRouteSelector(MediaRouteSelector.EMPTY);
-            mRouteButton.setVisibility(View.GONE);
-        }
-    }
-
-    ///////////////////////////////////////////////////
-    // 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) {
-            long bufferedPos = mPlaybackState.getBufferedPosition();
-            return (bufferedPos == -1) ? -1 : (int) (bufferedPos * 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 ViewGroup makeControllerView() {
-        ViewGroup root = (ViewGroup) ApiHelper.inflateLibLayout(mInstance.getContext(),
-                R.layout.media_controller);
-        initControllerView(root);
-        return root;
-    }
-
-    private void initControllerView(ViewGroup v) {
-        // Relating to Title Bar View
-        mTitleBar = v.findViewById(R.id.title_bar);
-        mTitleView = v.findViewById(R.id.title_text);
-        mAdExternalLink = v.findViewById(R.id.ad_external_link);
-        mBackButton = v.findViewById(R.id.back);
-        if (mBackButton != null) {
-            mBackButton.setOnClickListener(mBackListener);
-            mBackButton.setVisibility(View.GONE);
-        }
-        mRouteButton = v.findViewById(R.id.cast);
-
-        // Relating to Center View
-        mCenterView = v.findViewById(R.id.center_view);
-        mTransportControls = inflateTransportControls(R.layout.embedded_transport_controls);
-        mCenterView.addView(mTransportControls);
-
-        // Relating to Minimal Extra View
-        mMinimalExtraView = (LinearLayout) v.findViewById(R.id.minimal_extra_view);
-        LinearLayout.LayoutParams params =
-                (LinearLayout.LayoutParams) mMinimalExtraView.getLayoutParams();
-        int iconSize = mResources.getDimensionPixelSize(R.dimen.mcv2_embedded_icon_size);
-        int marginSize = mResources.getDimensionPixelSize(R.dimen.mcv2_icon_margin);
-        params.setMargins(0, (iconSize + marginSize * 2) * (-1), 0, 0);
-        mMinimalExtraView.setLayoutParams(params);
-        mMinimalExtraView.setVisibility(View.GONE);
-
-        // Relating to Progress Bar View
-        mProgress = v.findViewById(R.id.progress);
-        if (mProgress != null) {
-            if (mProgress instanceof SeekBar) {
-                SeekBar seeker = (SeekBar) mProgress;
-                seeker.setOnSeekBarChangeListener(mSeekListener);
-                seeker.setProgressDrawable(mResources.getDrawable(R.drawable.custom_progress));
-                seeker.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb));
-            }
-            mProgress.setMax(MAX_PROGRESS);
-        }
-        mProgressBuffer = v.findViewById(R.id.progress_buffer);
-
-        // Relating to Bottom Bar View
-        mBottomBar = v.findViewById(R.id.bottom_bar);
-
-        // Relating to Bottom Bar Left View
-        mBottomBarLeftView = v.findViewById(R.id.bottom_bar_left);
-        mTimeView = v.findViewById(R.id.time);
-        mEndTime = v.findViewById(R.id.time_end);
-        mCurrentTime = v.findViewById(R.id.time_current);
-        mAdSkipView = v.findViewById(R.id.ad_skip_time);
-        mFormatBuilder = new StringBuilder();
-        mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
-
-        // Relating to Bottom Bar Right View
-        mBottomBarRightView = v.findViewById(R.id.bottom_bar_right);
-        mBasicControls = v.findViewById(R.id.basic_controls);
-        mExtraControls = v.findViewById(R.id.extra_controls);
-        mCustomButtons = v.findViewById(R.id.custom_buttons);
-        mSubtitleButton = v.findViewById(R.id.subtitle);
-        if (mSubtitleButton != null) {
-            mSubtitleButton.setOnClickListener(mSubtitleListener);
-        }
-        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);
-        }
-        mOverflowButtonLeft = v.findViewById(R.id.overflow_left);
-        if (mOverflowButtonLeft != null) {
-            mOverflowButtonLeft.setOnClickListener(mOverflowLeftListener);
-        }
-        mMuteButton = v.findViewById(R.id.mute);
-        if (mMuteButton != null) {
-            mMuteButton.setOnClickListener(mMuteButtonListener);
-        }
-        mSettingsButton = v.findViewById(R.id.settings);
-        if (mSettingsButton != null) {
-            mSettingsButton.setOnClickListener(mSettingsButtonListener);
-        }
-        mVideoQualityButton = v.findViewById(R.id.video_quality);
-        if (mVideoQualityButton != null) {
-            mVideoQualityButton.setOnClickListener(mVideoQualityListener);
-        }
-        mAdRemainingView = v.findViewById(R.id.ad_remaining);
-
-        // Relating to Settings List View
-        initializeSettingsLists();
-        mSettingsListView = (ListView) ApiHelper.inflateLibLayout(mInstance.getContext(),
-                R.layout.settings_list);
-        mSettingsAdapter = new SettingsAdapter(mSettingsMainTextsList, mSettingsSubTextsList,
-                mSettingsIconIdsList);
-        mSubSettingsAdapter = new SubSettingsAdapter(null, 0);
-        mSettingsListView.setAdapter(mSettingsAdapter);
-        mSettingsListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-        mSettingsListView.setOnItemClickListener(mSettingsItemClickListener);
-
-        mEmbeddedSettingsItemWidth = mResources.getDimensionPixelSize(
-                R.dimen.mcv2_embedded_settings_width);
-        mFullSettingsItemWidth = mResources.getDimensionPixelSize(R.dimen.mcv2_full_settings_width);
-        mEmbeddedSettingsItemHeight = mResources.getDimensionPixelSize(
-                R.dimen.mcv2_embedded_settings_height);
-        mFullSettingsItemHeight = mResources.getDimensionPixelSize(
-                R.dimen.mcv2_full_settings_height);
-        mSettingsWindowMargin = (-1) * mResources.getDimensionPixelSize(
-                R.dimen.mcv2_settings_offset);
-        mSettingsWindow = new PopupWindow(mSettingsListView, mEmbeddedSettingsItemWidth,
-                ViewGroup.LayoutParams.WRAP_CONTENT, true);
-    }
-
-    /**
-     * 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 mUpdateProgress = new Runnable() {
-        @Override
-        public void run() {
-            int pos = setProgress();
-            boolean isShowing = mInstance.getVisibility() == View.VISIBLE;
-            if (!mDragging && isShowing && isPlaying()) {
-                mInstance.postDelayed(mUpdateProgress,
-                        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);
-            // If the media is a local file, there is no need to set a buffer, so set secondary
-            // progress to maximum.
-            if (getBufferPercentage() < 0) {
-                mProgress.setSecondaryProgress(MAX_PROGRESS);
-            } else {
-                mProgress.setSecondaryProgress(getBufferPercentage() * 10);
-            }
-        }
-
-        if (mEndTime != null) {
-            mEndTime.setText(stringForTime(mDuration));
-
-        }
-        if (mCurrentTime != null) {
-            mCurrentTime.setText(stringForTime(currentPosition));
-        }
-
-        if (mIsAdvertisement) {
-            // Update the remaining number of seconds until the first 5 seconds of the
-            // advertisement.
-            if (mAdSkipView != null) {
-                if (currentPosition <= AD_SKIP_WAIT_TIME_MS) {
-                    if (mAdSkipView.getVisibility() == View.GONE) {
-                        mAdSkipView.setVisibility(View.VISIBLE);
-                    }
-                    String skipTimeText = mResources.getString(
-                            R.string.MediaControlView2_ad_skip_wait_time,
-                            ((AD_SKIP_WAIT_TIME_MS - currentPosition) / 1000 + 1));
-                    mAdSkipView.setText(skipTimeText);
-                } else {
-                    if (mAdSkipView.getVisibility() == View.VISIBLE) {
-                        mAdSkipView.setVisibility(View.GONE);
-                        mNextButton.setEnabled(true);
-                        mNextButton.clearColorFilter();
-                    }
-                }
-            }
-            // Update the remaining number of seconds of the advertisement.
-            if (mAdRemainingView != null) {
-                int remainingTime =
-                        (mDuration - currentPosition < 0) ? 0 : (mDuration - currentPosition);
-                String remainingTimeText = mResources.getString(
-                        R.string.MediaControlView2_ad_remaining_time,
-                        stringForTime(remainingTime));
-                mAdRemainingView.setText(remainingTimeText);
-            }
-        }
-        return currentPosition;
-    }
-
-    private void togglePausePlayState() {
-        if (isPlaying()) {
-            mControls.pause();
-            mPlayPauseButton.setImageDrawable(
-                    mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
-            mPlayPauseButton.setContentDescription(
-                    mResources.getString(R.string.mcv2_play_button_desc));
-        } else {
-            mControls.play();
-            mPlayPauseButton.setImageDrawable(
-                    mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
-            mPlayPauseButton.setContentDescription(
-                    mResources.getString(R.string.mcv2_pause_button_desc));
-        }
-    }
-
-    // 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;
-            }
-
-            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(mUpdateProgress);
-
-            // 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(
-                        mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
-                mPlayPauseButton.setContentDescription(
-                        mResources.getString(R.string.mcv2_play_button_desc));
-                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 position = (int) (((long) mDuration * progress) / MAX_PROGRESS);
-                mControls.seekTo(position);
-
-                if (mCurrentTime != null) {
-                    mCurrentTime.setText(stringForTime(position));
-                }
-            }
-        }
-
-        @Override
-        public void onStopTrackingTouch(SeekBar bar) {
-            if (!mSeekAvailable) {
-                return;
-            }
-            mDragging = false;
-
-            setProgress();
-
-            // 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(mUpdateProgress);
-        }
-    };
-
-    private final View.OnClickListener mPlayPauseListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            togglePausePlayState();
-        }
-    };
-
-    private final View.OnClickListener mRewListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            int pos = getCurrentPosition() - REWIND_TIME_MS;
-            mControls.seekTo(pos);
-            setProgress();
-        }
-    };
-
-    private final View.OnClickListener mFfwdListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            int pos = getCurrentPosition() + FORWARD_TIME_MS;
-            mControls.seekTo(pos);
-            setProgress();
-        }
-    };
-
-    private final View.OnClickListener mNextListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mControls.skipToNext();
-        }
-    };
-
-    private final View.OnClickListener mPrevListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mControls.skipToPrevious();
-        }
-    };
-
-    private final View.OnClickListener mBackListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            // TODO: implement
-        }
-    };
-
-    private final View.OnClickListener mSubtitleListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mSettingsMode = SETTINGS_MODE_SUBTITLE_TRACK;
-            mSubSettingsAdapter.setTexts(mSubtitleDescriptionsList);
-            mSubSettingsAdapter.setCheckPosition(mSelectedSubtitleTrackIndex);
-            displaySettingsWindow(mSubSettingsAdapter);
-        }
-    };
-
-    private final View.OnClickListener mVideoQualityListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mSettingsMode = SETTINGS_MODE_VIDEO_QUALITY;
-            mSubSettingsAdapter.setTexts(mVideoQualityList);
-            mSubSettingsAdapter.setCheckPosition(mSelectedVideoQualityIndex);
-            displaySettingsWindow(mSubSettingsAdapter);
-        }
-    };
-
-    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(
-                        mResources.getDrawable(R.drawable.ic_fullscreen_exit, null));
-            } else {
-                mFullScreenButton.setImageDrawable(
-                        mResources.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;
-        }
-    };
-
-    private final View.OnClickListener mOverflowRightListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mBasicControls.setVisibility(View.GONE);
-            mExtraControls.setVisibility(View.VISIBLE);
-        }
-    };
-
-    private final View.OnClickListener mOverflowLeftListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mBasicControls.setVisibility(View.VISIBLE);
-            mExtraControls.setVisibility(View.GONE);
-        }
-    };
-
-    private final View.OnClickListener mMuteButtonListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            if (!mIsMute) {
-                mMuteButton.setImageDrawable(
-                        mResources.getDrawable(R.drawable.ic_mute, null));
-                mMuteButton.setContentDescription(
-                        mResources.getString(R.string.mcv2_muted_button_desc));
-                mIsMute = true;
-                mController.sendCommand(COMMAND_MUTE, null, null);
-            } else {
-                mMuteButton.setImageDrawable(
-                        mResources.getDrawable(R.drawable.ic_unmute, null));
-                mMuteButton.setContentDescription(
-                        mResources.getString(R.string.mcv2_unmuted_button_desc));
-                mIsMute = false;
-                mController.sendCommand(COMMAND_UNMUTE, null, null);
-            }
-        }
-    };
-
-    private final View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            mSettingsMode = SETTINGS_MODE_MAIN;
-            mSettingsAdapter.setSubTexts(mSettingsSubTextsList);
-            displaySettingsWindow(mSettingsAdapter);
-        }
-    };
-
-    private final AdapterView.OnItemClickListener mSettingsItemClickListener
-            = new AdapterView.OnItemClickListener() {
-        @Override
-        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            switch (mSettingsMode) {
-                case SETTINGS_MODE_MAIN:
-                    if (position == SETTINGS_MODE_AUDIO_TRACK) {
-                        mSubSettingsAdapter.setTexts(mAudioTrackList);
-                        mSubSettingsAdapter.setCheckPosition(mSelectedAudioTrackIndex);
-                        mSettingsMode = SETTINGS_MODE_AUDIO_TRACK;
-                    } else if (position == SETTINGS_MODE_PLAYBACK_SPEED) {
-                        mSubSettingsAdapter.setTexts(mPlaybackSpeedTextList);
-                        mSubSettingsAdapter.setCheckPosition(mSelectedSpeedIndex);
-                        mSettingsMode = SETTINGS_MODE_PLAYBACK_SPEED;
-                    } else if (position == SETTINGS_MODE_HELP) {
-                        // TODO: implement this.
-                        mSettingsWindow.dismiss();
-                        return;
-                    }
-                    displaySettingsWindow(mSubSettingsAdapter);
-                    break;
-                case SETTINGS_MODE_AUDIO_TRACK:
-                    if (position != mSelectedAudioTrackIndex) {
-                        mSelectedAudioTrackIndex = position;
-                        if (mAudioTrackCount > 0) {
-                            Bundle extra = new Bundle();
-                            extra.putInt(KEY_SELECTED_AUDIO_INDEX, position);
-                            mController.sendCommand(COMMAND_SELECT_AUDIO_TRACK, extra, null);
-                        }
-                        mSettingsSubTextsList.set(SETTINGS_MODE_AUDIO_TRACK,
-                                mSubSettingsAdapter.getMainText(position));
-                    }
-                    mSettingsWindow.dismiss();
-                    break;
-                case SETTINGS_MODE_PLAYBACK_SPEED:
-                    if (position != mSelectedSpeedIndex) {
-                        mSelectedSpeedIndex = position;
-                        Bundle extra = new Bundle();
-                        extra.putFloat(KEY_PLAYBACK_SPEED, mPlaybackSpeedList.get(position));
-                        mController.sendCommand(COMMAND_SET_PLAYBACK_SPEED, extra, null);
-                        mSettingsSubTextsList.set(SETTINGS_MODE_PLAYBACK_SPEED,
-                                mSubSettingsAdapter.getMainText(position));
-                    }
-                    mSettingsWindow.dismiss();
-                    break;
-                case SETTINGS_MODE_HELP:
-                    // TODO: implement this.
-                    break;
-                case SETTINGS_MODE_SUBTITLE_TRACK:
-                    if (position != mSelectedSubtitleTrackIndex) {
-                        mSelectedSubtitleTrackIndex = position;
-                        if (position > 0) {
-                            Bundle extra = new Bundle();
-                            extra.putInt(KEY_SELECTED_SUBTITLE_INDEX, position - 1);
-                            mController.sendCommand(
-                                    MediaControlView2Impl.COMMAND_SHOW_SUBTITLE, extra, null);
-                            mSubtitleButton.setImageDrawable(
-                                    mResources.getDrawable(R.drawable.ic_subtitle_on, null));
-                            mSubtitleButton.setContentDescription(
-                                    mResources.getString(R.string.mcv2_cc_is_on));
-                            mSubtitleIsEnabled = true;
-                        } else {
-                            mController.sendCommand(
-                                    MediaControlView2Impl.COMMAND_HIDE_SUBTITLE, null, null);
-                            mSubtitleButton.setImageDrawable(
-                                    mResources.getDrawable(R.drawable.ic_subtitle_off, null));
-                            mSubtitleButton.setContentDescription(
-                                    mResources.getString(R.string.mcv2_cc_is_off));
-
-                            mSubtitleIsEnabled = false;
-                        }
-                    }
-                    mSettingsWindow.dismiss();
-                    break;
-                case SETTINGS_MODE_VIDEO_QUALITY:
-                    // TODO: add support for video quality
-                    mSelectedVideoQualityIndex = position;
-                    mSettingsWindow.dismiss();
-                    break;
-            }
-        }
-    };
-
-    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));
-            }
-        }
-    }
-
-    // The title bar is made up of two separate LinearLayouts. If the sum of the two bars are
-    // greater than the length of the title bar, reduce the size of the left bar (which makes the
-    // TextView that contains the title of the media file shrink).
-    private void updateTitleBarLayout() {
-        if (mTitleBar != null) {
-            int titleBarWidth = mTitleBar.getWidth();
-
-            View leftBar = mTitleBar.findViewById(R.id.title_bar_left);
-            View rightBar = mTitleBar.findViewById(R.id.title_bar_right);
-            int leftBarWidth = leftBar.getWidth();
-            int rightBarWidth = rightBar.getWidth();
-
-            RelativeLayout.LayoutParams params =
-                    (RelativeLayout.LayoutParams) leftBar.getLayoutParams();
-            if (leftBarWidth + rightBarWidth > titleBarWidth) {
-                params.width = titleBarWidth - rightBarWidth;
-                mOriginalLeftBarWidth = leftBarWidth;
-            } else if (leftBarWidth + rightBarWidth < titleBarWidth && mOriginalLeftBarWidth != 0) {
-                params.width = mOriginalLeftBarWidth;
-                mOriginalLeftBarWidth = 0;
-            }
-            leftBar.setLayoutParams(params);
-        }
-    }
-
-    private void updateAudioMetadata() {
-        if (mMediaType != MEDIA_TYPE_MUSIC) {
-            return;
-        }
-
-        if (mMetadata != null) {
-            String titleText = "";
-            String artistText = "";
-            if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_TITLE)) {
-                titleText = mMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
-            } else {
-                titleText = mResources.getString(R.string.mcv2_music_title_unknown_text);
-            }
-
-            if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) {
-                artistText = mMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
-            } else {
-                artistText = mResources.getString(R.string.mcv2_music_artist_unknown_text);
-            }
-
-            // Update title for Embedded size type
-            mTitleView.setText(titleText + " - " + artistText);
-
-            // Set to true to update layout inside onMeasure()
-            mNeedUXUpdate = true;
-        }
-    }
-
-    private void updateLayout() {
-        if (mIsAdvertisement) {
-            mRewButton.setVisibility(View.GONE);
-            mFfwdButton.setVisibility(View.GONE);
-            mPrevButton.setVisibility(View.GONE);
-            mTimeView.setVisibility(View.GONE);
-
-            mAdSkipView.setVisibility(View.VISIBLE);
-            mAdRemainingView.setVisibility(View.VISIBLE);
-            mAdExternalLink.setVisibility(View.VISIBLE);
-
-            mProgress.setEnabled(false);
-            mNextButton.setEnabled(false);
-            mNextButton.setColorFilter(R.color.gray);
-        } else {
-            mRewButton.setVisibility(View.VISIBLE);
-            mFfwdButton.setVisibility(View.VISIBLE);
-            mPrevButton.setVisibility(View.VISIBLE);
-            mTimeView.setVisibility(View.VISIBLE);
-
-            mAdSkipView.setVisibility(View.GONE);
-            mAdRemainingView.setVisibility(View.GONE);
-            mAdExternalLink.setVisibility(View.GONE);
-
-            mProgress.setEnabled(true);
-            mNextButton.setEnabled(true);
-            mNextButton.clearColorFilter();
-            disableUnsupportedButtons();
-        }
-    }
-
-    private void updateLayout(int maxIconCount, int fullIconSize, int embeddedIconSize,
-             int marginSize, int currWidth, int currHeight, int screenWidth, int screenHeight) {
-        int fullBottomBarRightWidthMax = fullIconSize * maxIconCount
-                + marginSize * (maxIconCount * 2);
-        int embeddedBottomBarRightWidthMax = embeddedIconSize * maxIconCount
-                + marginSize * (maxIconCount * 2);
-        int fullWidth = mTransportControls.getWidth() + mTimeView.getWidth()
-                + fullBottomBarRightWidthMax;
-        int embeddedWidth = mTimeView.getWidth() + embeddedBottomBarRightWidthMax;
-        int screenMaxLength = Math.max(screenWidth, screenHeight);
-
-        if (fullWidth > screenMaxLength) {
-            // TODO: screen may be smaller than the length needed for Full size.
-        }
-
-        boolean isFullSize = (mMediaType == MEDIA_TYPE_DEFAULT) ? (currWidth == screenMaxLength) :
-                (currWidth == screenWidth && currHeight == screenHeight);
-
-        if (isFullSize) {
-            if (mSizeType != SIZE_TYPE_FULL) {
-                updateLayoutForSizeChange(SIZE_TYPE_FULL);
-                if (mMediaType == MEDIA_TYPE_MUSIC) {
-                    mTitleView.setVisibility(View.GONE);
-                }
-            }
-        } else if (embeddedWidth <= currWidth) {
-            if (mSizeType != SIZE_TYPE_EMBEDDED) {
-                updateLayoutForSizeChange(SIZE_TYPE_EMBEDDED);
-                if (mMediaType == MEDIA_TYPE_MUSIC) {
-                    mTitleView.setVisibility(View.VISIBLE);
-                }
-            }
-        } else {
-            if (mSizeType != SIZE_TYPE_MINIMAL) {
-                updateLayoutForSizeChange(SIZE_TYPE_MINIMAL);
-                if (mMediaType == MEDIA_TYPE_MUSIC) {
-                    mTitleView.setVisibility(View.GONE);
-                }
-            }
-        }
-    }
-
-    private void updateLayoutForSizeChange(int sizeType) {
-        mSizeType = sizeType;
-        RelativeLayout.LayoutParams timeViewParams =
-                (RelativeLayout.LayoutParams) mTimeView.getLayoutParams();
-        SeekBar seeker = (SeekBar) mProgress;
-        switch (mSizeType) {
-            case SIZE_TYPE_EMBEDDED:
-                // Relating to Title Bar
-                mTitleBar.setVisibility(View.VISIBLE);
-                mBackButton.setVisibility(View.GONE);
-
-                // Relating to Full Screen Button
-                mMinimalExtraView.setVisibility(View.GONE);
-                mFullScreenButton = mBottomBarRightView.findViewById(R.id.fullscreen);
-                mFullScreenButton.setOnClickListener(mFullScreenListener);
-
-                // Relating to Center View
-                mCenterView.removeAllViews();
-                mBottomBarLeftView.removeView(mTransportControls);
-                mBottomBarLeftView.setVisibility(View.GONE);
-                mTransportControls = inflateTransportControls(R.layout.embedded_transport_controls);
-                mCenterView.addView(mTransportControls);
-
-                // Relating to Progress Bar
-                seeker.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb));
-                mProgressBuffer.setVisibility(View.VISIBLE);
-
-                // Relating to Bottom Bar
-                mBottomBar.setVisibility(View.VISIBLE);
-                if (timeViewParams.getRule(RelativeLayout.LEFT_OF) != 0) {
-                    timeViewParams.removeRule(RelativeLayout.LEFT_OF);
-                    timeViewParams.addRule(RelativeLayout.RIGHT_OF, R.id.bottom_bar_left);
-                }
-                break;
-            case SIZE_TYPE_FULL:
-                // Relating to Title Bar
-                mTitleBar.setVisibility(View.VISIBLE);
-                mBackButton.setVisibility(View.VISIBLE);
-
-                // Relating to Full Screen Button
-                mMinimalExtraView.setVisibility(View.GONE);
-                mFullScreenButton = mBottomBarRightView.findViewById(R.id.fullscreen);
-                mFullScreenButton.setOnClickListener(mFullScreenListener);
-
-                // Relating to Center View
-                mCenterView.removeAllViews();
-                mBottomBarLeftView.removeView(mTransportControls);
-                mTransportControls = inflateTransportControls(R.layout.full_transport_controls);
-                mBottomBarLeftView.addView(mTransportControls, 0);
-                mBottomBarLeftView.setVisibility(View.VISIBLE);
-
-                // Relating to Progress Bar
-                seeker.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb));
-                mProgressBuffer.setVisibility(View.VISIBLE);
-
-                // Relating to Bottom Bar
-                mBottomBar.setVisibility(View.VISIBLE);
-                if (timeViewParams.getRule(RelativeLayout.RIGHT_OF) != 0) {
-                    timeViewParams.removeRule(RelativeLayout.RIGHT_OF);
-                    timeViewParams.addRule(RelativeLayout.LEFT_OF, R.id.bottom_bar_right);
-                }
-                break;
-            case SIZE_TYPE_MINIMAL:
-                // Relating to Title Bar
-                mTitleBar.setVisibility(View.GONE);
-                mBackButton.setVisibility(View.GONE);
-
-                // Relating to Full Screen Button
-                mMinimalExtraView.setVisibility(View.VISIBLE);
-                mFullScreenButton = mMinimalExtraView.findViewById(R.id.fullscreen);
-                mFullScreenButton.setOnClickListener(mFullScreenListener);
-
-                // Relating to Center View
-                mCenterView.removeAllViews();
-                mBottomBarLeftView.removeView(mTransportControls);
-                mTransportControls = inflateTransportControls(R.layout.minimal_transport_controls);
-                mCenterView.addView(mTransportControls);
-
-                // Relating to Progress Bar
-                seeker.setThumb(null);
-                mProgressBuffer.setVisibility(View.GONE);
-
-                // Relating to Bottom Bar
-                mBottomBar.setVisibility(View.GONE);
-                break;
-        }
-        mTimeView.setLayoutParams(timeViewParams);
-
-        if (isPlaying()) {
-            mPlayPauseButton.setImageDrawable(
-                    mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
-            mPlayPauseButton.setContentDescription(
-                    mResources.getString(R.string.mcv2_pause_button_desc));
-        } else {
-            mPlayPauseButton.setImageDrawable(
-                    mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
-            mPlayPauseButton.setContentDescription(
-                    mResources.getString(R.string.mcv2_play_button_desc));
-        }
-
-        if (mIsFullScreen) {
-            mFullScreenButton.setImageDrawable(
-                    mResources.getDrawable(R.drawable.ic_fullscreen_exit, null));
-        } else {
-            mFullScreenButton.setImageDrawable(
-                    mResources.getDrawable(R.drawable.ic_fullscreen, null));
-        }
-    }
-
-    private View inflateTransportControls(int layoutId) {
-        View v = ApiHelper.inflateLibLayout(mInstance.getContext(), layoutId);
-        mPlayPauseButton = v.findViewById(R.id.pause);
-        if (mPlayPauseButton != null) {
-            mPlayPauseButton.requestFocus();
-            mPlayPauseButton.setOnClickListener(mPlayPauseListener);
-        }
-        mFfwdButton = v.findViewById(R.id.ffwd);
-        if (mFfwdButton != null) {
-            mFfwdButton.setOnClickListener(mFfwdListener);
-            if (mMediaType == MEDIA_TYPE_MUSIC) {
-                mFfwdButton.setVisibility(View.GONE);
-            }
-        }
-        mRewButton = v.findViewById(R.id.rew);
-        if (mRewButton != null) {
-            mRewButton.setOnClickListener(mRewListener);
-            if (mMediaType == MEDIA_TYPE_MUSIC) {
-                mRewButton.setVisibility(View.GONE);
-            }
-        }
-        // TODO: Add support for Next and Previous buttons
-        mNextButton = v.findViewById(R.id.next);
-        if (mNextButton != null) {
-            mNextButton.setOnClickListener(mNextListener);
-            mNextButton.setVisibility(View.GONE);
-        }
-        mPrevButton = v.findViewById(R.id.prev);
-        if (mPrevButton != null) {
-            mPrevButton.setOnClickListener(mPrevListener);
-            mPrevButton.setVisibility(View.GONE);
-        }
-        return v;
-    }
-
-    private void initializeSettingsLists() {
-        mSettingsMainTextsList = new ArrayList<String>();
-        mSettingsMainTextsList.add(
-                mResources.getString(R.string.MediaControlView2_audio_track_text));
-        mSettingsMainTextsList.add(
-                mResources.getString(R.string.MediaControlView2_playback_speed_text));
-        mSettingsMainTextsList.add(
-                mResources.getString(R.string.MediaControlView2_help_text));
-
-        mSettingsSubTextsList = new ArrayList<String>();
-        mSettingsSubTextsList.add(
-                mResources.getString(R.string.MediaControlView2_audio_track_none_text));
-        mSettingsSubTextsList.add(
-                mResources.getStringArray(
-                        R.array.MediaControlView2_playback_speeds)[PLAYBACK_SPEED_1x_INDEX]);
-        mSettingsSubTextsList.add(RESOURCE_EMPTY);
-
-        mSettingsIconIdsList = new ArrayList<Integer>();
-        mSettingsIconIdsList.add(R.drawable.ic_audiotrack);
-        mSettingsIconIdsList.add(R.drawable.ic_play_circle_filled);
-        mSettingsIconIdsList.add(R.drawable.ic_help);
-
-        mAudioTrackList = new ArrayList<String>();
-        mAudioTrackList.add(
-                mResources.getString(R.string.MediaControlView2_audio_track_none_text));
-
-        mVideoQualityList = new ArrayList<String>();
-        mVideoQualityList.add(
-                mResources.getString(R.string.MediaControlView2_video_quality_auto_text));
-
-        mPlaybackSpeedTextList = new ArrayList<String>(Arrays.asList(
-                mResources.getStringArray(R.array.MediaControlView2_playback_speeds)));
-        // Select the "1x" speed as the default value.
-        mSelectedSpeedIndex = PLAYBACK_SPEED_1x_INDEX;
-
-        mPlaybackSpeedList = new ArrayList<Float>();
-        int[] speeds = mResources.getIntArray(R.array.speed_multiplied_by_100);
-        for (int i = 0; i < speeds.length; i++) {
-            float speed = (float) speeds[i] / 100.0f;
-            mPlaybackSpeedList.add(speed);
-        }
-    }
-
-    private void displaySettingsWindow(BaseAdapter adapter) {
-        // Set Adapter
-        mSettingsListView.setAdapter(adapter);
-
-        // Set width of window
-        int itemWidth = (mSizeType == SIZE_TYPE_EMBEDDED)
-                ? mEmbeddedSettingsItemWidth : mFullSettingsItemWidth;
-        mSettingsWindow.setWidth(itemWidth);
-
-        // Calculate height of window and show
-        int itemHeight = (mSizeType == SIZE_TYPE_EMBEDDED)
-                ? mEmbeddedSettingsItemHeight : mFullSettingsItemHeight;
-        int totalHeight = adapter.getCount() * itemHeight;
-        mSettingsWindow.dismiss();
-        mSettingsWindow.showAsDropDown(mInstance, mSettingsWindowMargin,
-                mSettingsWindowMargin - totalHeight, Gravity.BOTTOM | Gravity.RIGHT);
-    }
-
-    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(
-                                mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
-                        mPlayPauseButton.setContentDescription(
-                                mResources.getString(R.string.mcv2_pause_button_desc));
-                        mInstance.removeCallbacks(mUpdateProgress);
-                        mInstance.post(mUpdateProgress);
-                        break;
-                    case PlaybackState.STATE_PAUSED:
-                        mPlayPauseButton.setImageDrawable(
-                                mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
-                        mPlayPauseButton.setContentDescription(
-                                mResources.getString(R.string.mcv2_play_button_desc));
-                        break;
-                    case PlaybackState.STATE_STOPPED:
-                        mPlayPauseButton.setImageDrawable(
-                                mResources.getDrawable(R.drawable.ic_replay_circle_filled, null));
-                        mPlayPauseButton.setContentDescription(
-                                mResources.getString(R.string.mcv2_replay_button_desc));
-                        mIsStopped = true;
-                        break;
-                    default:
-                        break;
-                }
-                mPrevState = mPlaybackState.getState();
-            }
-
-            if (mPlaybackActions != mPlaybackState.getActions()) {
-                long newActions = mPlaybackState.getActions();
-                if ((newActions & PlaybackState.ACTION_PAUSE) != 0) {
-                    mPlayPauseButton.setVisibility(View.VISIBLE);
-                }
-                if ((newActions & PlaybackState.ACTION_REWIND) != 0
-                        && mMediaType != MEDIA_TYPE_MUSIC) {
-                    if (mRewButton != null) {
-                        mRewButton.setVisibility(View.VISIBLE);
-                    }
-                }
-                if ((newActions & PlaybackState.ACTION_FAST_FORWARD) != 0
-                        && mMediaType != MEDIA_TYPE_MUSIC) {
-                    if (mFfwdButton != null) {
-                        mFfwdButton.setVisibility(View.VISIBLE);
-                    }
-                }
-                if ((newActions & PlaybackState.ACTION_SEEK_TO) != 0) {
-                    mSeekAvailable = true;
-                } else {
-                    mSeekAvailable = false;
-                }
-                mPlaybackActions = newActions;
-            }
-
-            // Add buttons if custom actions are present.
-            List<PlaybackState.CustomAction> customActions = mPlaybackState.getCustomActions();
-            mCustomButtons.removeAllViews();
-            if (customActions.size() > 0) {
-                for (PlaybackState.CustomAction action : customActions) {
-                    ImageButton button = new ImageButton(mInstance.getContext(),
-                            null /* AttributeSet */, 0 /* Style */);
-                    // TODO: Apply R.style.BottomBarButton to this button using library context.
-                    // Refer Constructor with argument (int defStyleRes) of View.java
-                    button.setImageResource(action.getIcon());
-                    button.setTooltipText(action.getName());
-                    final String actionString = action.getAction().toString();
-                    button.setOnClickListener(new View.OnClickListener() {
-                        @Override
-                        public void onClick(View v) {
-                            // TODO: Currently, we are just sending extras that came from session.
-                            // Is it the right behavior?
-                            mControls.sendCustomAction(actionString, action.getExtras());
-                            mInstance.setVisibility(View.VISIBLE);
-                        }
-                    });
-                    mCustomButtons.addView(button);
-                }
-            }
-        }
-
-        @Override
-        public void onMetadataChanged(MediaMetadata metadata) {
-            mMetadata = metadata;
-            updateDuration();
-            updateTitle();
-            updateAudioMetadata();
-        }
-
-        @Override
-        public void onSessionEvent(String event, Bundle extras) {
-            switch (event) {
-                case EVENT_UPDATE_TRACK_STATUS:
-                    mVideoTrackCount = extras.getInt(KEY_VIDEO_TRACK_COUNT);
-                    // If there is one or more audio tracks, and this information has not been
-                    // reflected into the Settings window yet, automatically check the first track.
-                    // Otherwise, the Audio Track selection will be defaulted to "None".
-                    mAudioTrackCount = extras.getInt(KEY_AUDIO_TRACK_COUNT);
-                    mAudioTrackList = new ArrayList<String>();
-                    if (mAudioTrackCount > 0) {
-                        // TODO: add more text about track info.
-                        for (int i = 0; i < mAudioTrackCount; i++) {
-                            String track = mResources.getString(
-                                    R.string.MediaControlView2_audio_track_number_text, i + 1);
-                            mAudioTrackList.add(track);
-                        }
-                        // Change sub text inside the Settings window.
-                        mSettingsSubTextsList.set(SETTINGS_MODE_AUDIO_TRACK,
-                                mAudioTrackList.get(0));
-                    } else {
-                        mAudioTrackList.add(mResources.getString(
-                                R.string.MediaControlView2_audio_track_none_text));
-                    }
-                    if (mVideoTrackCount == 0 && mAudioTrackCount > 0) {
-                        mMediaType = MEDIA_TYPE_MUSIC;
-                    }
-
-                    mSubtitleTrackCount = extras.getInt(KEY_SUBTITLE_TRACK_COUNT);
-                    mSubtitleDescriptionsList = new ArrayList<String>();
-                    if (mSubtitleTrackCount > 0) {
-                        mSubtitleButton.setVisibility(View.VISIBLE);
-                        mSubtitleButton.setEnabled(true);
-                        mSubtitleDescriptionsList.add(mResources.getString(
-                                R.string.MediaControlView2_subtitle_off_text));
-                        for (int i = 0; i < mSubtitleTrackCount; i++) {
-                            String track = mResources.getString(
-                                    R.string.MediaControlView2_subtitle_track_number_text, i + 1);
-                            mSubtitleDescriptionsList.add(track);
-                        }
-                    } else {
-                        mSubtitleButton.setVisibility(View.GONE);
-                        mSubtitleButton.setEnabled(false);
-                    }
-                    break;
-                case EVENT_UPDATE_MEDIA_TYPE_STATUS:
-                    boolean newStatus = extras.getBoolean(KEY_STATE_IS_ADVERTISEMENT);
-                    if (newStatus != mIsAdvertisement) {
-                        mIsAdvertisement = newStatus;
-                        updateLayout();
-                    }
-                    break;
-            }
-        }
-    }
-
-    private class SettingsAdapter extends BaseAdapter {
-        private List<Integer> mIconIds;
-        private List<String> mMainTexts;
-        private List<String> mSubTexts;
-
-        public SettingsAdapter(List<String> mainTexts, @Nullable List<String> subTexts,
-                @Nullable List<Integer> iconIds) {
-            mMainTexts = mainTexts;
-            mSubTexts = subTexts;
-            mIconIds = iconIds;
-        }
-
-        public void updateSubTexts(List<String> subTexts) {
-            mSubTexts = subTexts;
-            notifyDataSetChanged();
-        }
-
-        public String getMainText(int position) {
-            if (mMainTexts != null) {
-                if (position < mMainTexts.size()) {
-                    return mMainTexts.get(position);
-                }
-            }
-            return RESOURCE_EMPTY;
-        }
-
-        @Override
-        public int getCount() {
-            return (mMainTexts == null) ? 0 : mMainTexts.size();
-        }
-
-        @Override
-        public long getItemId(int position) {
-            // Auto-generated method stub--does not have any purpose here
-            // TODO: implement this.
-            return 0;
-        }
-
-        @Override
-        public Object getItem(int position) {
-            // Auto-generated method stub--does not have any purpose here
-            // TODO: implement this.
-            return null;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup container) {
-            View row;
-            if (mSizeType == SIZE_TYPE_FULL) {
-                row = ApiHelper.inflateLibLayout(mInstance.getContext(),
-                        R.layout.full_settings_list_item);
-            } else {
-                row = ApiHelper.inflateLibLayout(mInstance.getContext(),
-                        R.layout.embedded_settings_list_item);
-            }
-            TextView mainTextView = (TextView) row.findViewById(R.id.main_text);
-            TextView subTextView = (TextView) row.findViewById(R.id.sub_text);
-            ImageView iconView = (ImageView) row.findViewById(R.id.icon);
-
-            // Set main text
-            mainTextView.setText(mMainTexts.get(position));
-
-            // Remove sub text and center the main text if sub texts do not exist at all or the sub
-            // text at this particular position is empty.
-            if (mSubTexts == null || mSubTexts.get(position) == RESOURCE_EMPTY) {
-                subTextView.setVisibility(View.GONE);
-            } else {
-                // Otherwise, set sub text.
-                subTextView.setText(mSubTexts.get(position));
-            }
-
-            // Remove main icon and set visibility to gone if icons are set to null or the icon at
-            // this particular position is set to RESOURCE_NON_EXISTENT.
-            if (mIconIds == null || mIconIds.get(position) == RESOURCE_NON_EXISTENT) {
-                iconView.setVisibility(View.GONE);
-            } else {
-                // Otherwise, set main icon.
-                iconView.setImageDrawable(mResources.getDrawable(mIconIds.get(position), null));
-            }
-            return row;
-        }
-
-        public void setSubTexts(List<String> subTexts) {
-            mSubTexts = subTexts;
-        }
-    }
-
-    // TODO: extend this class from SettingsAdapter
-    private class SubSettingsAdapter extends BaseAdapter {
-        private List<String> mTexts;
-        private int mCheckPosition;
-
-        public SubSettingsAdapter(List<String> texts, int checkPosition) {
-            mTexts = texts;
-            mCheckPosition = checkPosition;
-        }
-
-        public String getMainText(int position) {
-            if (mTexts != null) {
-                if (position < mTexts.size()) {
-                    return mTexts.get(position);
-                }
-            }
-            return RESOURCE_EMPTY;
-        }
-
-        @Override
-        public int getCount() {
-            return (mTexts == null) ? 0 : mTexts.size();
-        }
-
-        @Override
-        public long getItemId(int position) {
-            // Auto-generated method stub--does not have any purpose here
-            // TODO: implement this.
-            return 0;
-        }
-
-        @Override
-        public Object getItem(int position) {
-            // Auto-generated method stub--does not have any purpose here
-            // TODO: implement this.
-            return null;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup container) {
-            View row;
-            if (mSizeType == SIZE_TYPE_FULL) {
-                row = ApiHelper.inflateLibLayout(mInstance.getContext(),
-                        R.layout.full_sub_settings_list_item);
-            } else {
-                row = ApiHelper.inflateLibLayout(mInstance.getContext(),
-                        R.layout.embedded_sub_settings_list_item);
-            }
-            TextView textView = (TextView) row.findViewById(R.id.text);
-            ImageView checkView = (ImageView) row.findViewById(R.id.check);
-
-            textView.setText(mTexts.get(position));
-            if (position != mCheckPosition) {
-                checkView.setVisibility(View.INVISIBLE);
-            }
-            return row;
-        }
-
-        public void setTexts(List<String> texts) {
-            mTexts = texts;
-        }
-
-        public void setCheckPosition(int checkPosition) {
-            mCheckPosition = checkPosition;
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
deleted file mode 100644
index 061bc5b..0000000
--- a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * 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.widget;
-
-import static android.widget.VideoView2.VIEW_TYPE_SURFACEVIEW;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.media.MediaPlayer2;
-import android.media.VideoSize;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-
-class VideoSurfaceView extends SurfaceView implements VideoViewInterface, SurfaceHolder.Callback {
-    private static final String TAG = "VideoSurfaceView";
-    private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG);
-    private SurfaceHolder mSurfaceHolder = null;
-    private SurfaceListener mSurfaceListener = null;
-    private MediaPlayer2 mMediaPlayer;
-    // A flag to indicate taking over other view should be proceed.
-    private boolean mIsTakingOverOldView;
-    private VideoViewInterface mOldView;
-
-
-    public VideoSurfaceView(Context context) {
-        this(context, null);
-    }
-
-    public VideoSurfaceView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public VideoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public VideoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr,
-                            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        getHolder().addCallback(this);
-    }
-
-    ////////////////////////////////////////////////////
-    // implements VideoViewInterface
-    ////////////////////////////////////////////////////
-
-    @Override
-    public boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp) {
-        Log.d(TAG, "assignSurfaceToMediaPlayer(): mSurfaceHolder: " + mSurfaceHolder);
-        if (mp == null || !hasAvailableSurface()) {
-            return false;
-        }
-        mp.setDisplay(mSurfaceHolder);
-        return true;
-    }
-
-    @Override
-    public void setSurfaceListener(SurfaceListener l) {
-        mSurfaceListener = l;
-    }
-
-    @Override
-    public int getViewType() {
-        return VIEW_TYPE_SURFACEVIEW;
-    }
-
-    @Override
-    public void setMediaPlayer(MediaPlayer2 mp) {
-        mMediaPlayer = mp;
-        if (mIsTakingOverOldView) {
-            takeOver(mOldView);
-        }
-    }
-
-    @Override
-    public void takeOver(@NonNull VideoViewInterface oldView) {
-        if (assignSurfaceToMediaPlayer(mMediaPlayer)) {
-            ((View) oldView).setVisibility(GONE);
-            mIsTakingOverOldView = false;
-            mOldView = null;
-            if (mSurfaceListener != null) {
-                mSurfaceListener.onSurfaceTakeOverDone(this);
-            }
-        } else {
-            mIsTakingOverOldView = true;
-            mOldView = oldView;
-        }
-    }
-
-    @Override
-    public boolean hasAvailableSurface() {
-        return (mSurfaceHolder != null && mSurfaceHolder.getSurface() != null);
-    }
-
-    ////////////////////////////////////////////////////
-    // implements SurfaceHolder.Callback
-    ////////////////////////////////////////////////////
-
-    @Override
-    public void surfaceCreated(SurfaceHolder holder) {
-        Log.d(TAG, "surfaceCreated: mSurfaceHolder: " + mSurfaceHolder + ", new holder: " + holder);
-        mSurfaceHolder = holder;
-        if (mIsTakingOverOldView) {
-            takeOver(mOldView);
-        } else {
-            assignSurfaceToMediaPlayer(mMediaPlayer);
-        }
-
-        if (mSurfaceListener != null) {
-            Rect rect = mSurfaceHolder.getSurfaceFrame();
-            mSurfaceListener.onSurfaceCreated(this, rect.width(), rect.height());
-        }
-    }
-
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-        if (mSurfaceListener != null) {
-            mSurfaceListener.onSurfaceChanged(this, width, height);
-        }
-    }
-
-    @Override
-    public void surfaceDestroyed(SurfaceHolder holder) {
-        // After we return from this we can't use the surface any more
-        mSurfaceHolder = null;
-        if (mSurfaceListener != null) {
-            mSurfaceListener.onSurfaceDestroyed(this);
-        }
-    }
-
-    // TODO: Investigate the way to move onMeasure() code into FrameLayout.
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int videoWidth = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getWidth();
-        int videoHeight = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getHeight();
-        if (DEBUG) {
-            Log.d(TAG, "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
-                    + MeasureSpec.toString(heightMeasureSpec) + ")");
-            Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight());
-            Log.i(TAG, " viewSize: " + getWidth() + "/" + getHeight());
-            Log.i(TAG, " mVideoWidth/height: " + videoWidth + ", " + videoHeight);
-        }
-
-        int width = getDefaultSize(videoWidth, widthMeasureSpec);
-        int height = getDefaultSize(videoHeight, heightMeasureSpec);
-
-        if (videoWidth > 0 && videoHeight > 0) {
-            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
-            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
-
-            width = widthSpecSize;
-            height = heightSpecSize;
-
-            // for compatibility, we adjust size based on aspect ratio
-            if (videoWidth * height < width * videoHeight) {
-                width = height * videoWidth / videoHeight;
-                if (DEBUG) {
-                    Log.d(TAG, "image too wide, correcting. width: " + width);
-                }
-            } else if (videoWidth * height > width * videoHeight) {
-                height = width * videoHeight / videoWidth;
-                if (DEBUG) {
-                    Log.d(TAG, "image too tall, correcting. height: " + height);
-                }
-            }
-        } else {
-            // no size yet, just adopt the given spec sizes
-        }
-        setMeasuredDimension(width, height);
-        if (DEBUG) {
-            Log.i(TAG, "end of onMeasure()");
-            Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight());
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "ViewType: SurfaceView / Visibility: " + getVisibility()
-                + " / surfaceHolder: " + mSurfaceHolder;
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
deleted file mode 100644
index c2c1ca6..0000000
--- a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * 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.widget;
-
-import static android.widget.VideoView2.VIEW_TYPE_TEXTUREVIEW;
-
-import android.content.Context;
-import android.graphics.SurfaceTexture;
-import android.media.MediaPlayer2;
-import android.media.VideoSize;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Surface;
-import android.view.TextureView;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
-
-@RequiresApi(26)
-class VideoTextureView extends TextureView
-        implements VideoViewInterface, TextureView.SurfaceTextureListener {
-    private static final String TAG = "VideoTextureView";
-    private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG);
-
-    private SurfaceTexture mSurfaceTexture;
-    private Surface mSurface;
-    private SurfaceListener mSurfaceListener;
-    private MediaPlayer2 mMediaPlayer;
-    // A flag to indicate taking over other view should be proceed.
-    private boolean mIsTakingOverOldView;
-    private VideoViewInterface mOldView;
-
-    public VideoTextureView(Context context) {
-        this(context, null);
-    }
-
-    public VideoTextureView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public VideoTextureView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public VideoTextureView(
-            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        setSurfaceTextureListener(this);
-    }
-
-    ////////////////////////////////////////////////////
-    // implements VideoViewInterface
-    ////////////////////////////////////////////////////
-
-    @Override
-    public boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp) {
-        Log.d(TAG, "assignSurfaceToMediaPlayer(): mSurfaceTexture: " + mSurfaceTexture);
-        if (mp == null || !hasAvailableSurface()) {
-            // Surface is not ready.
-            return false;
-        }
-        mp.setSurface(mSurface);
-        return true;
-    }
-
-    @Override
-    public void setSurfaceListener(SurfaceListener l) {
-        mSurfaceListener = l;
-    }
-
-    @Override
-    public int getViewType() {
-        return VIEW_TYPE_TEXTUREVIEW;
-    }
-
-    @Override
-    public void setMediaPlayer(MediaPlayer2 mp) {
-        mMediaPlayer = mp;
-        if (mIsTakingOverOldView) {
-            takeOver(mOldView);
-        }
-    }
-
-    @Override
-    public void takeOver(@NonNull VideoViewInterface oldView) {
-        if (assignSurfaceToMediaPlayer(mMediaPlayer)) {
-            ((View) oldView).setVisibility(GONE);
-            mIsTakingOverOldView = false;
-            mOldView = null;
-            if (mSurfaceListener != null) {
-                mSurfaceListener.onSurfaceTakeOverDone(this);
-            }
-        } else {
-            mIsTakingOverOldView = true;
-            mOldView = oldView;
-        }
-    }
-
-    @Override
-    public boolean hasAvailableSurface() {
-        return (mSurfaceTexture != null && !mSurfaceTexture.isReleased() && mSurface != null);
-    }
-
-    ////////////////////////////////////////////////////
-    // implements TextureView.SurfaceTextureListener
-    ////////////////////////////////////////////////////
-
-    @Override
-    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
-        Log.d(TAG, "onSurfaceTextureAvailable: mSurfaceTexture: " + mSurfaceTexture
-                + ", new surface: " + surfaceTexture);
-        mSurfaceTexture = surfaceTexture;
-        mSurface = new Surface(mSurfaceTexture);
-        if (mIsTakingOverOldView) {
-            takeOver(mOldView);
-        } else {
-            assignSurfaceToMediaPlayer(mMediaPlayer);
-        }
-        if (mSurfaceListener != null) {
-            mSurfaceListener.onSurfaceCreated(this, width, height);
-        }
-    }
-
-    @Override
-    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
-        if (mSurfaceListener != null) {
-            mSurfaceListener.onSurfaceChanged(this, width, height);
-        }
-        // requestLayout();  // TODO: figure out if it should be called here?
-    }
-
-    @Override
-    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
-        // no-op
-    }
-
-    @Override
-    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
-        if (mSurfaceListener != null) {
-            mSurfaceListener.onSurfaceDestroyed(this);
-        }
-        mSurfaceTexture = null;
-        mSurface = null;
-        return true;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int videoWidth = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getWidth();
-        int videoHeight = (mMediaPlayer == null) ? 0 : mMediaPlayer.getVideoSize().getHeight();
-        if (DEBUG) {
-            Log.d(TAG, "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
-                    + MeasureSpec.toString(heightMeasureSpec) + ")");
-            Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight());
-            Log.i(TAG, " viewSize: " + getWidth() + "/" + getHeight());
-            Log.i(TAG, " mVideoWidth/height: " + videoWidth + ", " + videoHeight);
-        }
-
-        int width = getDefaultSize(videoWidth, widthMeasureSpec);
-        int height = getDefaultSize(videoHeight, heightMeasureSpec);
-
-        if (videoWidth > 0 && videoHeight > 0) {
-            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
-            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
-
-            width = widthSpecSize;
-            height = heightSpecSize;
-
-            // for compatibility, we adjust size based on aspect ratio
-            if (videoWidth * height < width * videoHeight) {
-                width = height * videoWidth / videoHeight;
-                if (DEBUG) {
-                    Log.d(TAG, "image too wide, correcting. width: " + width);
-                }
-            } else if (videoWidth * height > width * videoHeight) {
-                height = width * videoHeight / videoWidth;
-                if (DEBUG) {
-                    Log.d(TAG, "image too tall, correcting. height: " + height);
-                }
-            }
-        } else {
-            // no size yet, just adopt the given spec sizes
-        }
-        setMeasuredDimension(width, height);
-        if (DEBUG) {
-            Log.i(TAG, "end of onMeasure()");
-            Log.i(TAG, " measuredSize: " + getMeasuredWidth() + "/" + getMeasuredHeight());
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "ViewType: TextureView / Visibility: " + getVisibility()
-                + " / surfaceTexture: " + mSurfaceTexture;
-
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
deleted file mode 100644
index 5eb5ba6..0000000
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ /dev/null
@@ -1,1383 +0,0 @@
-/*
- * 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.widget;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Point;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.media.AudioAttributes;
-import android.media.AudioFocusRequest;
-import android.media.AudioManager;
-import android.media.DataSourceDesc;
-import android.media.MediaItem2;
-import android.media.MediaMetadata;
-import android.media.MediaMetadata2;
-import android.media.MediaMetadataRetriever;
-import android.media.MediaPlayer2;
-import android.media.MediaPlayer2.EventCallback;
-import android.media.MediaPlayer2Impl;
-import android.media.PlaybackParams;
-import android.media.SessionToken2;
-import android.media.SubtitleData;
-import android.media.TimedText;
-import android.media.VideoSize;
-import android.media.session.MediaController;
-import android.media.session.MediaController.PlaybackInfo;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
-import android.media.update.VideoView2Provider;
-import android.media.update.ViewGroupProvider;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.Pair;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.ImageView;
-import android.widget.MediaControlView2;
-import android.widget.TextView;
-import android.widget.VideoView2;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.graphics.palette.Palette;
-import com.android.media.RoutePlayer;
-import com.android.media.subtitle.ClosedCaptionRenderer;
-import com.android.media.subtitle.SubtitleController;
-import com.android.media.subtitle.SubtitleTrack;
-import com.android.media.update.ApiHelper;
-import com.android.media.update.R;
-import com.android.support.mediarouter.media.MediaControlIntent;
-import com.android.support.mediarouter.media.MediaItemStatus;
-import com.android.support.mediarouter.media.MediaRouteSelector;
-import com.android.support.mediarouter.media.MediaRouter;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Executor;
-
-public class VideoView2Impl extends BaseLayout
-        implements VideoView2Provider, VideoViewInterface.SurfaceListener {
-    private static final String TAG = "VideoView2";
-    private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG);
-    private static final long DEFAULT_SHOW_CONTROLLER_INTERVAL_MS = 2000;
-
-    private final VideoView2 mInstance;
-
-    private static final int STATE_ERROR = -1;
-    private static final int STATE_IDLE = 0;
-    private static final int STATE_PREPARING = 1;
-    private static final int STATE_PREPARED = 2;
-    private static final int STATE_PLAYING = 3;
-    private static final int STATE_PAUSED = 4;
-    private static final int STATE_PLAYBACK_COMPLETED = 5;
-
-    private static final int INVALID_TRACK_INDEX = -1;
-    private static final float INVALID_SPEED = 0f;
-
-    private static final int SIZE_TYPE_EMBEDDED = 0;
-    private static final int SIZE_TYPE_FULL = 1;
-    // TODO: add support for Minimal size type.
-    private static final int SIZE_TYPE_MINIMAL = 2;
-
-    private AccessibilityManager mAccessibilityManager;
-    private AudioManager mAudioManager;
-    private AudioAttributes mAudioAttributes;
-    private int mAudioFocusType = AudioManager.AUDIOFOCUS_GAIN; // legacy focus gain
-
-    private Pair<Executor, VideoView2.OnCustomActionListener> mCustomActionListenerRecord;
-    private VideoView2.OnViewTypeChangedListener mViewTypeChangedListener;
-    private VideoView2.OnFullScreenRequestListener mFullScreenRequestListener;
-
-    private VideoViewInterface mCurrentView;
-    private VideoTextureView mTextureView;
-    private VideoSurfaceView mSurfaceView;
-
-    private MediaPlayer2 mMediaPlayer;
-    private DataSourceDesc mDsd;
-    private MediaControlView2 mMediaControlView;
-    private MediaSession mMediaSession;
-    private MediaController mMediaController;
-    private boolean mSeekable;
-    private MediaMetadata2 mMediaMetadata;
-    private MediaMetadataRetriever mRetriever;
-    private boolean mNeedUpdateMediaType;
-    private Bundle mMediaTypeData;
-    private String mTitle;
-
-    // TODO: move music view inside SurfaceView/TextureView or implement VideoViewInterface.
-    private WindowManager mManager;
-    private Resources mResources;
-    private View mMusicView;
-    private Drawable mMusicAlbumDrawable;
-    private String mMusicTitleText;
-    private String mMusicArtistText;
-    private boolean mIsMusicMediaType;
-    private int mPrevWidth;
-    private int mPrevHeight;
-    private int mDominantColor;
-    private int mSizeType;
-
-    private PlaybackState.Builder mStateBuilder;
-    private List<PlaybackState.CustomAction> mCustomActionList;
-    private int mTargetState = STATE_IDLE;
-    private int mCurrentState = STATE_IDLE;
-    private int mCurrentBufferPercentage;
-    private long mSeekWhenPrepared;  // recording the seek position while preparing
-
-    private int mVideoWidth;
-    private int mVideoHeight;
-
-    private ArrayList<Integer> mVideoTrackIndices;
-    private ArrayList<Integer> mAudioTrackIndices;
-    private ArrayList<Pair<Integer, SubtitleTrack>> mSubtitleTrackIndices;
-    private SubtitleController mSubtitleController;
-
-    // selected video/audio/subtitle track index as MediaPlayer2 returns
-    private int mSelectedVideoTrackIndex;
-    private int mSelectedAudioTrackIndex;
-    private int mSelectedSubtitleTrackIndex;
-
-    private SubtitleView mSubtitleView;
-    private boolean mSubtitleEnabled;
-
-    private float mSpeed;
-    // TODO: Remove mFallbackSpeed when integration with MediaPlayer2's new setPlaybackParams().
-    // Refer: https://docs.google.com/document/d/1nzAfns6i2hJ3RkaUre3QMT6wsDedJ5ONLiA_OOBFFX8/edit
-    private float mFallbackSpeed;  // keep the original speed before 'pause' is called.
-    private float mVolumeLevelFloat;
-    private int mVolumeLevel;
-
-    private long mShowControllerIntervalMs;
-
-    private MediaRouter mMediaRouter;
-    private MediaRouteSelector mRouteSelector;
-    private MediaRouter.RouteInfo mRoute;
-    private RoutePlayer mRoutePlayer;
-
-    private final MediaRouter.Callback mRouterCallback = new MediaRouter.Callback() {
-        @Override
-        public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
-            if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
-                // Stop local playback (if necessary)
-                resetPlayer();
-                mRoute = route;
-                mRoutePlayer = new RoutePlayer(mInstance.getContext(), route);
-                mRoutePlayer.setPlayerEventCallback(new RoutePlayer.PlayerEventCallback() {
-                    @Override
-                    public void onPlayerStateChanged(MediaItemStatus itemStatus) {
-                        PlaybackState.Builder psBuilder = new PlaybackState.Builder();
-                        psBuilder.setActions(RoutePlayer.PLAYBACK_ACTIONS);
-                        long position = itemStatus.getContentPosition();
-                        switch (itemStatus.getPlaybackState()) {
-                            case MediaItemStatus.PLAYBACK_STATE_PENDING:
-                                psBuilder.setState(PlaybackState.STATE_NONE, position, 0);
-                                mCurrentState = STATE_IDLE;
-                                break;
-                            case MediaItemStatus.PLAYBACK_STATE_PLAYING:
-                                psBuilder.setState(PlaybackState.STATE_PLAYING, position, 1);
-                                mCurrentState = STATE_PLAYING;
-                                break;
-                            case MediaItemStatus.PLAYBACK_STATE_PAUSED:
-                                psBuilder.setState(PlaybackState.STATE_PAUSED, position, 0);
-                                mCurrentState = STATE_PAUSED;
-                                break;
-                            case MediaItemStatus.PLAYBACK_STATE_BUFFERING:
-                                psBuilder.setState(PlaybackState.STATE_BUFFERING, position, 0);
-                                mCurrentState = STATE_PAUSED;
-                                break;
-                            case MediaItemStatus.PLAYBACK_STATE_FINISHED:
-                                psBuilder.setState(PlaybackState.STATE_STOPPED, position, 0);
-                                mCurrentState = STATE_PLAYBACK_COMPLETED;
-                                break;
-                        }
-
-                        PlaybackState pbState = psBuilder.build();
-                        mMediaSession.setPlaybackState(pbState);
-
-                        MediaMetadata.Builder mmBuilder = new MediaMetadata.Builder();
-                        mmBuilder.putLong(MediaMetadata.METADATA_KEY_DURATION,
-                                itemStatus.getContentDuration());
-                        mMediaSession.setMetadata(mmBuilder.build());
-                    }
-                });
-                // Start remote playback (if necessary)
-                mRoutePlayer.openVideo(mDsd);
-            }
-        }
-
-        @Override
-        public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route, int reason) {
-            if (mRoute != null && mRoutePlayer != null) {
-                mRoutePlayer.release();
-                mRoutePlayer = null;
-            }
-            if (mRoute == route) {
-                mRoute = null;
-            }
-            if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) {
-                // TODO: Resume local playback  (if necessary)
-                openVideo(mDsd);
-            }
-        }
-    };
-
-    public VideoView2Impl(VideoView2 instance,
-            ViewGroupProvider superProvider, ViewGroupProvider privateProvider) {
-        super(instance, superProvider, privateProvider);
-        mInstance = instance;
-    }
-
-    @Override
-    public void initialize(@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        mVideoWidth = 0;
-        mVideoHeight = 0;
-        mSpeed = 1.0f;
-        mFallbackSpeed = mSpeed;
-        mSelectedSubtitleTrackIndex = INVALID_TRACK_INDEX;
-        mSeekable = true;
-        // TODO: add attributes to get this value.
-        mShowControllerIntervalMs = DEFAULT_SHOW_CONTROLLER_INTERVAL_MS;
-
-        mAccessibilityManager = AccessibilityManager.getInstance(mInstance.getContext());
-
-        mAudioManager = (AudioManager) mInstance.getContext()
-                .getSystemService(Context.AUDIO_SERVICE);
-        mAudioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA)
-                .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE).build();
-        mInstance.setFocusable(true);
-        mInstance.setFocusableInTouchMode(true);
-        mInstance.requestFocus();
-
-        // TODO: try to keep a single child at a time rather than always having both.
-        mTextureView = new VideoTextureView(mInstance.getContext());
-        mSurfaceView = new VideoSurfaceView(mInstance.getContext());
-        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
-                LayoutParams.MATCH_PARENT);
-        mTextureView.setLayoutParams(params);
-        mSurfaceView.setLayoutParams(params);
-        mTextureView.setSurfaceListener(this);
-        mSurfaceView.setSurfaceListener(this);
-        mInstance.addView(mTextureView);
-        mInstance.addView(mSurfaceView);
-
-        mSubtitleView = new SubtitleView(mInstance.getContext());
-        mSubtitleView.setLayoutParams(params);
-        mSubtitleView.setBackgroundColor(0);
-        mInstance.addView(mSubtitleView);
-
-        boolean enableControlView = (attrs == null) || attrs.getAttributeBooleanValue(
-                "http://schemas.android.com/apk/res/android",
-                "enableControlView", true);
-        if (enableControlView) {
-            mMediaControlView = new MediaControlView2(mInstance.getContext());
-        }
-
-        mSubtitleEnabled = (attrs == null) || attrs.getAttributeBooleanValue(
-                "http://schemas.android.com/apk/res/android",
-                "enableSubtitle", false);
-
-        // TODO: Choose TextureView when SurfaceView cannot be created.
-        // Choose surface view by default
-        int viewType = (attrs == null) ? VideoView2.VIEW_TYPE_SURFACEVIEW
-                : attrs.getAttributeIntValue(
-                "http://schemas.android.com/apk/res/android",
-                "viewType", VideoView2.VIEW_TYPE_SURFACEVIEW);
-        if (viewType == VideoView2.VIEW_TYPE_SURFACEVIEW) {
-            Log.d(TAG, "viewType attribute is surfaceView.");
-            mTextureView.setVisibility(View.GONE);
-            mSurfaceView.setVisibility(View.VISIBLE);
-            mCurrentView = mSurfaceView;
-        } else if (viewType == VideoView2.VIEW_TYPE_TEXTUREVIEW) {
-            Log.d(TAG, "viewType attribute is textureView.");
-            mTextureView.setVisibility(View.VISIBLE);
-            mSurfaceView.setVisibility(View.GONE);
-            mCurrentView = mTextureView;
-        }
-
-        MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
-        builder.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
-        builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO);
-        builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
-        mRouteSelector = builder.build();
-    }
-
-    @Override
-    public void setMediaControlView2_impl(MediaControlView2 mediaControlView, long intervalMs) {
-        mMediaControlView = mediaControlView;
-        mShowControllerIntervalMs = intervalMs;
-        // TODO: Call MediaControlView2.setRouteSelector only when cast availalbe.
-        ((MediaControlView2Impl) mMediaControlView.getProvider()).setRouteSelector(mRouteSelector);
-
-        if (mInstance.isAttachedToWindow()) {
-            attachMediaControlView();
-        }
-    }
-
-    @Override
-    public MediaController getMediaController_impl() {
-        if (mMediaSession == null) {
-            throw new IllegalStateException("MediaSession instance is not available.");
-        }
-        return mMediaController;
-    }
-
-    @Override
-    public SessionToken2 getMediaSessionToken_impl() {
-        // TODO: implement this
-        return null;
-    }
-
-    @Override
-    public MediaControlView2 getMediaControlView2_impl() {
-        return mMediaControlView;
-    }
-
-    @Override
-    public MediaMetadata2 getMediaMetadata_impl() {
-        return mMediaMetadata;
-    }
-
-    @Override
-    public void setMediaMetadata_impl(MediaMetadata2 metadata) {
-        // TODO: integrate this with MediaSession2#MediaItem2
-        mMediaMetadata = metadata;
-
-        // TODO: add support for handling website link
-        mMediaTypeData = new Bundle();
-        boolean isAd = metadata == null ?
-                false : metadata.getLong(MediaMetadata2.METADATA_KEY_ADVERTISEMENT) != 0;
-        mMediaTypeData.putBoolean(
-                MediaControlView2Impl.KEY_STATE_IS_ADVERTISEMENT, isAd);
-
-        if (mMediaSession != null) {
-            mMediaSession.sendSessionEvent(
-                    MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS, mMediaTypeData);
-        } else {
-            // Update later inside OnPreparedListener after MediaSession is initialized.
-            mNeedUpdateMediaType = true;
-        }
-    }
-
-    @Override
-    public void setSubtitleEnabled_impl(boolean enable) {
-        if (enable != mSubtitleEnabled) {
-            selectOrDeselectSubtitle(enable);
-        }
-        mSubtitleEnabled = enable;
-    }
-
-    @Override
-    public boolean isSubtitleEnabled_impl() {
-        return mSubtitleEnabled;
-    }
-
-    // TODO: remove setSpeed_impl once MediaController2 is ready.
-    @Override
-    public void setSpeed_impl(float speed) {
-        if (speed <= 0.0f) {
-            Log.e(TAG, "Unsupported speed (" + speed + ") is ignored.");
-            return;
-        }
-        mSpeed = speed;
-        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
-            applySpeed();
-        }
-        updatePlaybackState();
-    }
-
-    @Override
-    public void setAudioFocusRequest_impl(int focusGain) {
-        if (focusGain != AudioManager.AUDIOFOCUS_NONE
-                && focusGain != AudioManager.AUDIOFOCUS_GAIN
-                && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
-                && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
-                && focusGain != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) {
-            throw new IllegalArgumentException("Illegal audio focus type " + focusGain);
-        }
-        mAudioFocusType = focusGain;
-    }
-
-    @Override
-    public void setAudioAttributes_impl(AudioAttributes attributes) {
-        if (attributes == null) {
-            throw new IllegalArgumentException("Illegal null AudioAttributes");
-        }
-        mAudioAttributes = attributes;
-    }
-
-    @Override
-    public void setVideoPath_impl(String path) {
-        mInstance.setVideoUri(Uri.parse(path));
-    }
-
-    @Override
-    public void setVideoUri_impl(Uri uri) {
-        mInstance.setVideoUri(uri, null);
-    }
-
-    @Override
-    public void setVideoUri_impl(Uri uri, Map<String, String> headers) {
-        DataSourceDesc.Builder builder = new DataSourceDesc.Builder();
-        builder.setDataSource(mInstance.getContext(), uri, headers, null);
-        mInstance.setDataSource(builder.build());
-    }
-
-    @Override
-    public void setMediaItem_impl(MediaItem2 mediaItem) {
-        // TODO: implement this
-    }
-
-    @Override
-    public void setDataSource_impl(DataSourceDesc dsd) {
-        mDsd = dsd;
-        mSeekWhenPrepared = 0;
-        openVideo(dsd);
-    }
-
-    @Override
-    public void setViewType_impl(int viewType) {
-        if (viewType == mCurrentView.getViewType()) {
-            return;
-        }
-        VideoViewInterface targetView;
-        if (viewType == VideoView2.VIEW_TYPE_TEXTUREVIEW) {
-            Log.d(TAG, "switching to TextureView");
-            targetView = mTextureView;
-        } else if (viewType == VideoView2.VIEW_TYPE_SURFACEVIEW) {
-            Log.d(TAG, "switching to SurfaceView");
-            targetView = mSurfaceView;
-        } else {
-            throw new IllegalArgumentException("Unknown view type: " + viewType);
-        }
-        ((View) targetView).setVisibility(View.VISIBLE);
-        targetView.takeOver(mCurrentView);
-        mInstance.requestLayout();
-    }
-
-    @Override
-    public int getViewType_impl() {
-        return mCurrentView.getViewType();
-    }
-
-    @Override
-    public void setCustomActions_impl(
-            List<PlaybackState.CustomAction> actionList,
-            Executor executor, VideoView2.OnCustomActionListener listener) {
-        mCustomActionList = actionList;
-        mCustomActionListenerRecord = new Pair<>(executor, listener);
-
-        // Create a new playback builder in order to clear existing the custom actions.
-        mStateBuilder = null;
-        updatePlaybackState();
-    }
-
-    @Override
-    public void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l) {
-        mViewTypeChangedListener = l;
-    }
-
-    @Override
-    public void setFullScreenRequestListener_impl(VideoView2.OnFullScreenRequestListener l) {
-        mFullScreenRequestListener = l;
-    }
-
-    @Override
-    public void onAttachedToWindow_impl() {
-        super.onAttachedToWindow_impl();
-
-        // Create MediaSession
-        mMediaSession = new MediaSession(mInstance.getContext(), "VideoView2MediaSession");
-        mMediaSession.setCallback(new MediaSessionCallback());
-        mMediaSession.setActive(true);
-        mMediaController = mMediaSession.getController();
-        mMediaRouter = MediaRouter.getInstance(mInstance.getContext());
-        mMediaRouter.setMediaSession(mMediaSession);
-        mMediaRouter.addCallback(mRouteSelector, mRouterCallback);
-        attachMediaControlView();
-        // TODO: remove this after moving MediaSession creating code inside initializing VideoView2
-        if (mCurrentState == STATE_PREPARED) {
-            extractTracks();
-            extractMetadata();
-            extractAudioMetadata();
-            if (mNeedUpdateMediaType) {
-                mMediaSession.sendSessionEvent(
-                        MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS,
-                        mMediaTypeData);
-                mNeedUpdateMediaType = false;
-            }
-        }
-    }
-
-    @Override
-    public void onDetachedFromWindow_impl() {
-        super.onDetachedFromWindow_impl();
-
-        mMediaSession.release();
-        mMediaSession = null;
-        mMediaController = null;
-    }
-
-    @Override
-    public CharSequence getAccessibilityClassName_impl() {
-        return VideoView2.class.getName();
-    }
-
-    @Override
-    public boolean onTouchEvent_impl(MotionEvent ev) {
-        if (DEBUG) {
-            Log.d(TAG, "onTouchEvent(). mCurrentState=" + mCurrentState
-                    + ", mTargetState=" + mTargetState);
-        }
-        if (ev.getAction() == MotionEvent.ACTION_UP && mMediaControlView != null) {
-            if (!mIsMusicMediaType || mSizeType != SIZE_TYPE_FULL) {
-                toggleMediaControlViewVisibility();
-            }
-        }
-
-        return super.onTouchEvent_impl(ev);
-    }
-
-    @Override
-    public boolean onTrackballEvent_impl(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_UP && mMediaControlView != null) {
-            if (!mIsMusicMediaType || mSizeType != SIZE_TYPE_FULL) {
-                toggleMediaControlViewVisibility();
-            }
-        }
-
-        return super.onTrackballEvent_impl(ev);
-    }
-
-    @Override
-    public boolean dispatchTouchEvent_impl(MotionEvent ev) {
-        // TODO: Test touch event handling logic thoroughly and simplify the logic.
-        return super.dispatchTouchEvent_impl(ev);
-    }
-
-    @Override
-    public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure_impl(widthMeasureSpec, heightMeasureSpec);
-
-        if (mIsMusicMediaType) {
-            if (mPrevWidth != mInstance.getMeasuredWidth()
-                    || mPrevHeight != mInstance.getMeasuredHeight()) {
-                int currWidth = mInstance.getMeasuredWidth();
-                int currHeight = mInstance.getMeasuredHeight();
-                Point screenSize = new Point();
-                mManager.getDefaultDisplay().getSize(screenSize);
-                int screenWidth = screenSize.x;
-                int screenHeight = screenSize.y;
-
-                if (currWidth == screenWidth && currHeight == screenHeight) {
-                    int orientation = retrieveOrientation();
-                    if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
-                        inflateMusicView(R.layout.full_landscape_music);
-                    } else {
-                        inflateMusicView(R.layout.full_portrait_music);
-                    }
-
-                    if (mSizeType != SIZE_TYPE_FULL) {
-                        mSizeType = SIZE_TYPE_FULL;
-                        // Remove existing mFadeOut callback
-                        mMediaControlView.removeCallbacks(mFadeOut);
-                        mMediaControlView.setVisibility(View.VISIBLE);
-                    }
-                } else {
-                    if (mSizeType != SIZE_TYPE_EMBEDDED) {
-                        mSizeType = SIZE_TYPE_EMBEDDED;
-                        inflateMusicView(R.layout.embedded_music);
-                        // Add new mFadeOut callback
-                        mMediaControlView.postDelayed(mFadeOut, mShowControllerIntervalMs);
-                    }
-                }
-                mPrevWidth = currWidth;
-                mPrevHeight = currHeight;
-            }
-        }
-    }
-
-    ///////////////////////////////////////////////////
-    // Implements VideoViewInterface.SurfaceListener
-    ///////////////////////////////////////////////////
-
-    @Override
-    public void onSurfaceCreated(View view, int width, int height) {
-        if (DEBUG) {
-            Log.d(TAG, "onSurfaceCreated(). mCurrentState=" + mCurrentState
-                    + ", mTargetState=" + mTargetState + ", width/height: " + width + "/" + height
-                    + ", " + view.toString());
-        }
-        if (needToStart()) {
-            mMediaController.getTransportControls().play();
-        }
-    }
-
-    @Override
-    public void onSurfaceDestroyed(View view) {
-        if (DEBUG) {
-            Log.d(TAG, "onSurfaceDestroyed(). mCurrentState=" + mCurrentState
-                    + ", mTargetState=" + mTargetState + ", " + view.toString());
-        }
-    }
-
-    @Override
-    public void onSurfaceChanged(View view, int width, int height) {
-        // TODO: Do we need to call requestLayout here?
-        if (DEBUG) {
-            Log.d(TAG, "onSurfaceChanged(). width/height: " + width + "/" + height
-                    + ", " + view.toString());
-        }
-    }
-
-    @Override
-    public void onSurfaceTakeOverDone(VideoViewInterface view) {
-        if (DEBUG) {
-            Log.d(TAG, "onSurfaceTakeOverDone(). Now current view is: " + view);
-        }
-        mCurrentView = view;
-        if (mViewTypeChangedListener != null) {
-            mViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
-        }
-        if (needToStart()) {
-            mMediaController.getTransportControls().play();
-        }
-    }
-
-    ///////////////////////////////////////////////////
-    // 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 || mRoutePlayer != null)
-                && mCurrentState != STATE_ERROR
-                && mCurrentState != STATE_IDLE
-                && mCurrentState != STATE_PREPARING;
-    }
-
-    private boolean needToStart() {
-        return (mMediaPlayer != null || mRoutePlayer != null)
-                && mCurrentState != STATE_PLAYING
-                && mTargetState == STATE_PLAYING;
-    }
-
-    // Creates a MediaPlayer2 instance and prepare playback.
-    private void openVideo(DataSourceDesc dsd) {
-        Uri uri = dsd.getUri();
-        Map<String, String> headers = dsd.getUriHeaders();
-        resetPlayer();
-        if (isRemotePlayback()) {
-            mRoutePlayer.openVideo(dsd);
-            return;
-        }
-        if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
-            // TODO this should have a focus listener
-            AudioFocusRequest focusRequest;
-            focusRequest = new AudioFocusRequest.Builder(mAudioFocusType)
-                    .setAudioAttributes(mAudioAttributes)
-                    .build();
-            mAudioManager.requestAudioFocus(focusRequest);
-        }
-
-        try {
-            final Context context = mInstance.getContext();
-
-            Log.d(TAG, "openVideo(): creating new MediaPlayer2 instance.");
-            mMediaPlayer = new MediaPlayer2Impl(context);
-            mSurfaceView.setMediaPlayer(mMediaPlayer);
-            mTextureView.setMediaPlayer(mMediaPlayer);
-            mCurrentView.assignSurfaceToMediaPlayer(mMediaPlayer);
-
-            // TODO: Add timely firing logic for more accurate sync between CC and video frame
-            mSubtitleController = new SubtitleController(context);
-            mSubtitleController.registerRenderer(new ClosedCaptionRenderer(context));
-            mSubtitleController.setAnchor((SubtitleController.Anchor) mSubtitleView);
-            Executor executor = new Executor() {
-                @Override
-                public void execute(Runnable runnable) {
-                    runnable.run();
-                }
-            };
-            mMediaPlayer.registerEventCallback(executor, mMediaPlayer2Callback);
-
-            mCurrentBufferPercentage = -1;
-            mMediaPlayer.setDataSource(dsd);
-            mMediaPlayer.setAudioAttributes(mAudioAttributes);
-            // we don't set the target state here either, but preserve the
-            // target state that was there before.
-            mCurrentState = STATE_PREPARING;
-            mMediaPlayer.prepare();
-
-            // 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();
-            }
-            mRetriever = new MediaMetadataRetriever();
-            mRetriever.setDataSource(mInstance.getContext(), uri);
-
-            if (DEBUG) {
-                Log.d(TAG, "openVideo(). mCurrentState=" + mCurrentState
-                        + ", mTargetState=" + mTargetState);
-            }
-        } catch (IllegalArgumentException ex) {
-            Log.w(TAG, "Unable to open content: " + uri, ex);
-            mCurrentState = STATE_ERROR;
-            mTargetState = STATE_ERROR;
-            mMediaPlayer2Callback.onError(mMediaPlayer, dsd,
-                    MediaPlayer2.MEDIA_ERROR_UNKNOWN, MediaPlayer2.MEDIA_ERROR_IO);
-        }
-    }
-
-    /*
-     * Reset the media player in any state
-     */
-    private void resetPlayer() {
-        if (mMediaPlayer != null) {
-            final MediaPlayer2 player = mMediaPlayer;
-            new AsyncTask<MediaPlayer2, Void, Void>() {
-                @Override
-                protected Void doInBackground(MediaPlayer2... players) {
-                    // TODO: Fix NPE while MediaPlayer2.close()
-                    //players[0].close();
-                    return null;
-                }
-            }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, player);
-            mMediaPlayer = null;
-            mTextureView.setMediaPlayer(null);
-            mSurfaceView.setMediaPlayer(null);
-            //mPendingSubtitleTracks.clear();
-            mCurrentState = STATE_IDLE;
-            mTargetState = STATE_IDLE;
-            if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
-                mAudioManager.abandonAudioFocus(null);
-            }
-        }
-        mVideoWidth = 0;
-        mVideoHeight = 0;
-    }
-
-    private void updatePlaybackState() {
-        if (mStateBuilder == null) {
-            // Add Play action as default
-            long playbackActions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE;
-            if (mSeekable) {
-                playbackActions |= (PlaybackState.ACTION_REWIND |
-                        PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_SEEK_TO);
-            }
-            mStateBuilder = new PlaybackState.Builder();
-            mStateBuilder.setActions(playbackActions);
-
-            if (mCustomActionList != null) {
-                for (PlaybackState.CustomAction action : mCustomActionList) {
-                    mStateBuilder.addCustomAction(action);
-                }
-            }
-        }
-        mStateBuilder.setState(getCorrespondingPlaybackState(),
-                mMediaPlayer.getCurrentPosition(), mSpeed);
-        if (mCurrentState != STATE_ERROR
-            && mCurrentState != STATE_IDLE
-            && mCurrentState != STATE_PREPARING) {
-            // TODO: this should be replaced with MediaPlayer2.getBufferedPosition() once it is
-            // implemented.
-            if (mCurrentBufferPercentage == -1) {
-                mStateBuilder.setBufferedPosition(-1);
-            } else {
-                mStateBuilder.setBufferedPosition(
-                        (long) (mCurrentBufferPercentage / 100.0 * mMediaPlayer.getDuration()));
-            }
-        }
-
-        // Set PlaybackState for MediaSession
-        if (mMediaSession != null) {
-            PlaybackState state = mStateBuilder.build();
-            mMediaSession.setPlaybackState(state);
-        }
-    }
-
-    private int getCorrespondingPlaybackState() {
-        switch (mCurrentState) {
-            case STATE_ERROR:
-                return PlaybackState.STATE_ERROR;
-            case STATE_IDLE:
-                return PlaybackState.STATE_NONE;
-            case STATE_PREPARING:
-                return PlaybackState.STATE_CONNECTING;
-            case STATE_PREPARED:
-                return PlaybackState.STATE_PAUSED;
-            case STATE_PLAYING:
-                return PlaybackState.STATE_PLAYING;
-            case STATE_PAUSED:
-                return PlaybackState.STATE_PAUSED;
-            case STATE_PLAYBACK_COMPLETED:
-                return PlaybackState.STATE_STOPPED;
-            default:
-                return -1;
-        }
-    }
-
-    private final Runnable mFadeOut = new Runnable() {
-        @Override
-        public void run() {
-            if (mCurrentState == STATE_PLAYING) {
-                mMediaControlView.setVisibility(View.GONE);
-            }
-        }
-    };
-
-    private void showController() {
-        // TODO: Decide what to show when the state is not in playback state
-        if (mMediaControlView == null || !isInPlaybackState()
-                || (mIsMusicMediaType && mSizeType == SIZE_TYPE_FULL)) {
-            return;
-        }
-        mMediaControlView.removeCallbacks(mFadeOut);
-        mMediaControlView.setVisibility(View.VISIBLE);
-        if (mShowControllerIntervalMs != 0
-            && !mAccessibilityManager.isTouchExplorationEnabled()) {
-            mMediaControlView.postDelayed(mFadeOut, mShowControllerIntervalMs);
-        }
-    }
-
-    private void toggleMediaControlViewVisibility() {
-        if (mMediaControlView.getVisibility() == View.VISIBLE) {
-            mMediaControlView.removeCallbacks(mFadeOut);
-            mMediaControlView.setVisibility(View.GONE);
-        } else {
-            showController();
-        }
-    }
-
-    private void applySpeed() {
-        PlaybackParams params = mMediaPlayer.getPlaybackParams().allowDefaults();
-        if (mSpeed != params.getSpeed()) {
-            try {
-                params.setSpeed(mSpeed);
-                mMediaPlayer.setPlaybackParams(params);
-                mFallbackSpeed = mSpeed;
-            } catch (IllegalArgumentException e) {
-                Log.e(TAG, "PlaybackParams has unsupported value: " + e);
-                // TODO: should revise this part after integrating with MP2.
-                // If mSpeed had an illegal value for speed rate, system will determine best
-                // handling (see PlaybackParams.AUDIO_FALLBACK_MODE_DEFAULT).
-                // Note: The pre-MP2 returns 0.0f when it is paused. In this case, VideoView2 will
-                // use mFallbackSpeed instead.
-                float fallbackSpeed = mMediaPlayer.getPlaybackParams().allowDefaults().getSpeed();
-                if (fallbackSpeed > 0.0f) {
-                    mFallbackSpeed = fallbackSpeed;
-                }
-                mSpeed = mFallbackSpeed;
-            }
-        }
-    }
-
-    private boolean isRemotePlayback() {
-        if (mMediaController == null) {
-            return false;
-        }
-        PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo();
-        return playbackInfo != null
-                && playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE;
-    }
-
-    private void selectOrDeselectSubtitle(boolean select) {
-        if (!isInPlaybackState()) {
-            return;
-        }
-        if (select) {
-            if (mSubtitleTrackIndices.size() > 0) {
-                // TODO: make this selection dynamic
-                mSelectedSubtitleTrackIndex = mSubtitleTrackIndices.get(0).first;
-                mSubtitleController.selectTrack(mSubtitleTrackIndices.get(0).second);
-                mMediaPlayer.selectTrack(mSelectedSubtitleTrackIndex);
-                mSubtitleView.setVisibility(View.VISIBLE);
-            }
-        } else {
-            if (mSelectedSubtitleTrackIndex != INVALID_TRACK_INDEX) {
-                mMediaPlayer.deselectTrack(mSelectedSubtitleTrackIndex);
-                mSelectedSubtitleTrackIndex = INVALID_TRACK_INDEX;
-                mSubtitleView.setVisibility(View.GONE);
-            }
-        }
-    }
-
-    private void extractTracks() {
-        List<MediaPlayer2.TrackInfo> trackInfos = mMediaPlayer.getTrackInfo();
-        mVideoTrackIndices = new ArrayList<>();
-        mAudioTrackIndices = new ArrayList<>();
-        mSubtitleTrackIndices = new ArrayList<>();
-        mSubtitleController.reset();
-        for (int i = 0; i < trackInfos.size(); ++i) {
-            int trackType = trackInfos.get(i).getTrackType();
-            if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_VIDEO) {
-                mVideoTrackIndices.add(i);
-            } else if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_AUDIO) {
-                mAudioTrackIndices.add(i);
-            } else if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
-                    || trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
-                SubtitleTrack track = mSubtitleController.addTrack(trackInfos.get(i).getFormat());
-                if (track != null) {
-                    mSubtitleTrackIndices.add(new Pair<>(i, track));
-                }
-            }
-        }
-        // Select first tracks as default
-        if (mVideoTrackIndices.size() > 0) {
-            mSelectedVideoTrackIndex = 0;
-        }
-        if (mAudioTrackIndices.size() > 0) {
-            mSelectedAudioTrackIndex = 0;
-        }
-        if (mVideoTrackIndices.size() == 0 && mAudioTrackIndices.size() > 0) {
-            mIsMusicMediaType = true;
-        }
-
-        Bundle data = new Bundle();
-        data.putInt(MediaControlView2Impl.KEY_VIDEO_TRACK_COUNT, mVideoTrackIndices.size());
-        data.putInt(MediaControlView2Impl.KEY_AUDIO_TRACK_COUNT, mAudioTrackIndices.size());
-        data.putInt(MediaControlView2Impl.KEY_SUBTITLE_TRACK_COUNT, mSubtitleTrackIndices.size());
-        if (mSubtitleTrackIndices.size() > 0) {
-            selectOrDeselectSubtitle(mSubtitleEnabled);
-        }
-        mMediaSession.sendSessionEvent(MediaControlView2Impl.EVENT_UPDATE_TRACK_STATUS, data);
-    }
-
-    private void extractMetadata() {
-        // Get and set duration and title values as MediaMetadata for MediaControlView2
-        MediaMetadata.Builder builder = new MediaMetadata.Builder();
-        builder.putString(MediaMetadata.METADATA_KEY_TITLE, mTitle);
-        builder.putLong(
-                MediaMetadata.METADATA_KEY_DURATION, mMediaPlayer.getDuration());
-
-        if (mMediaSession != null) {
-            mMediaSession.setMetadata(builder.build());
-        }
-    }
-
-    private void extractAudioMetadata() {
-        if (!mIsMusicMediaType) {
-            return;
-        }
-
-        mResources = ApiHelper.getLibResources(mInstance.getContext());
-        mManager = (WindowManager) mInstance.getContext().getApplicationContext()
-                .getSystemService(Context.WINDOW_SERVICE);
-
-        byte[] album = mRetriever.getEmbeddedPicture();
-        if (album != null) {
-            Bitmap bitmap = BitmapFactory.decodeByteArray(album, 0, album.length);
-            mMusicAlbumDrawable = new BitmapDrawable(bitmap);
-
-            // TODO: replace with visualizer
-            Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
-                public void onGenerated(Palette palette) {
-                    // TODO: add dominant color for default album image.
-                    mDominantColor = palette.getDominantColor(0);
-                    if (mMusicView != null) {
-                        mMusicView.setBackgroundColor(mDominantColor);
-                    }
-                }
-            });
-        } else {
-            mMusicAlbumDrawable = mResources.getDrawable(R.drawable.ic_default_album_image);
-        }
-
-        String title = mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
-        if (title != null) {
-            mMusicTitleText = title;
-        } else {
-            mMusicTitleText = mResources.getString(R.string.mcv2_music_title_unknown_text);
-        }
-
-        String artist = mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
-        if (artist != null) {
-            mMusicArtistText = artist;
-        } else {
-            mMusicArtistText = mResources.getString(R.string.mcv2_music_artist_unknown_text);
-        }
-
-        // Send title and artist string to MediaControlView2
-        MediaMetadata.Builder builder = new MediaMetadata.Builder();
-        builder.putString(MediaMetadata.METADATA_KEY_TITLE, mMusicTitleText);
-        builder.putString(MediaMetadata.METADATA_KEY_ARTIST, mMusicArtistText);
-        mMediaSession.setMetadata(builder.build());
-
-        // Display Embedded mode as default
-        mInstance.removeView(mSurfaceView);
-        mInstance.removeView(mTextureView);
-        inflateMusicView(R.layout.embedded_music);
-    }
-
-    private int retrieveOrientation() {
-        DisplayMetrics dm = Resources.getSystem().getDisplayMetrics();
-        int width = dm.widthPixels;
-        int height = dm.heightPixels;
-
-        return (height > width) ?
-                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT :
-                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-    }
-
-    private void inflateMusicView(int layoutId) {
-        mInstance.removeView(mMusicView);
-
-        View v = ApiHelper.inflateLibLayout(mInstance.getContext(), layoutId);
-        v.setBackgroundColor(mDominantColor);
-
-        ImageView albumView = v.findViewById(R.id.album);
-        if (albumView != null) {
-            albumView.setImageDrawable(mMusicAlbumDrawable);
-        }
-
-        TextView titleView = v.findViewById(R.id.title);
-        if (titleView != null) {
-            titleView.setText(mMusicTitleText);
-        }
-
-        TextView artistView = v.findViewById(R.id.artist);
-        if (artistView != null) {
-            artistView.setText(mMusicArtistText);
-        }
-
-        mMusicView = v;
-        mInstance.addView(mMusicView, 0);
-    }
-
-    EventCallback mMediaPlayer2Callback =
-            new EventCallback() {
-                @Override
-                public void onSubtitleData(MediaPlayer2 mp, DataSourceDesc dsd, SubtitleData data) {
-                    if (DEBUG) {
-                        Log.d(TAG, "onSubtitleData(): getTrackIndex: " + data.getTrackIndex()
-                                + ", getCurrentPosition: " + mp.getCurrentPosition()
-                                + ", getStartTimeUs(): " + data.getStartTimeUs()
-                                + ", diff: "
-                                + (data.getStartTimeUs()/1000 - mp.getCurrentPosition())
-                                + "ms, getDurationUs(): " + data.getDurationUs()
-                                );
-
-                    }
-                    final int index = data.getTrackIndex();
-                    if (index != mSelectedSubtitleTrackIndex) {
-                        Log.d(TAG, "onSubtitleData(): getTrackIndex: " + data.getTrackIndex()
-                                + ", selected track index: " + mSelectedSubtitleTrackIndex);
-                        return;
-                    }
-                    for (Pair<Integer, SubtitleTrack> p : mSubtitleTrackIndices) {
-                        if (p.first == index) {
-                            SubtitleTrack track = p.second;
-                            track.onData(data);
-                        }
-                    }
-                }
-
-                @Override
-                public void onVideoSizeChanged(
-                        MediaPlayer2 mp, DataSourceDesc dsd, VideoSize size) {
-                    if (DEBUG) {
-                        Log.d(TAG, "onVideoSizeChanged(): size: " + size.getWidth() + "/"
-                                + size.getHeight());
-                    }
-                    mVideoWidth = mp.getVideoSize().getWidth();
-                    mVideoHeight = mp.getVideoSize().getHeight();
-                    if (DEBUG) {
-                        Log.d(TAG, "onVideoSizeChanged(): mVideoSize:" + mVideoWidth + "/"
-                                + mVideoHeight);
-                    }
-                    if (mVideoWidth != 0 && mVideoHeight != 0) {
-                        mInstance.requestLayout();
-                    }
-                }
-
-                // TODO: Remove timed text related code later once relevant Renderer is defined.
-                // This is just for debugging purpose.
-                @Override
-                public void onTimedText(
-                        MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) {
-                        Log.d(TAG, "TimedText: " + text.getText());
-                }
-
-                @Override
-                public void onInfo(
-                        MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
-                    if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
-                        extractTracks();
-                    } else if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
-                        this.onPrepared(mp, dsd);
-                    } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_END) {
-                        this.onCompletion(mp, dsd);
-                    } else if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
-                        this.onBufferingUpdate(mp, dsd, extra);
-                    } else if (what == MediaPlayer2.MEDIA_INFO_NOT_SEEKABLE) {
-                        mSeekable = false;
-                    }
-                }
-
-                @Override
-                public void onError(
-                        MediaPlayer2 mp, DataSourceDesc dsd, int frameworkErr, int implErr) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Error: " + frameworkErr + "," + implErr);
-                    }
-                    mCurrentState = STATE_ERROR;
-                    mTargetState = STATE_ERROR;
-                    mSeekable = true;
-                    updatePlaybackState();
-
-                    if (mMediaControlView != null) {
-                        mMediaControlView.setVisibility(View.GONE);
-                    }
-                }
-
-                @Override
-                public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what,
-                        int status) {
-                    if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO && status == 0) {
-                        updatePlaybackState();
-                    }
-                }
-
-                private void onPrepared(MediaPlayer2 mp, DataSourceDesc dsd) {
-                    if (DEBUG) {
-                        Log.d(TAG, "OnPreparedListener(). mCurrentState=" + mCurrentState
-                                + ", mTargetState=" + mTargetState);
-                    }
-                    mCurrentState = STATE_PREPARED;
-                    // Create and set playback state for MediaControlView2
-                    updatePlaybackState();
-
-                    // TODO: change this to send TrackInfos to MediaControlView2
-                    // TODO: create MediaSession when initializing VideoView2
-                    if (mMediaSession != null) {
-                        extractTracks();
-                        extractMetadata();
-                        extractAudioMetadata();
-                    }
-
-                    if (mMediaControlView != null) {
-                        mMediaControlView.setEnabled(true);
-                    }
-                    int videoWidth = mp.getVideoSize().getWidth();
-                    int videoHeight = mp.getVideoSize().getHeight();
-
-                    // mSeekWhenPrepared may be changed after seekTo() call
-                    long seekToPosition = mSeekWhenPrepared;
-                    if (seekToPosition != 0) {
-                        mMediaController.getTransportControls().seekTo(seekToPosition);
-                    }
-
-                    if (videoWidth != 0 && videoHeight != 0) {
-                        if (videoWidth != mVideoWidth || videoHeight != mVideoHeight) {
-                            if (DEBUG) {
-                                Log.i(TAG, "OnPreparedListener() : ");
-                                Log.i(TAG, " video size: " + videoWidth + "/" + videoHeight);
-                                Log.i(TAG, " measuredSize: " + mInstance.getMeasuredWidth() + "/"
-                                        + mInstance.getMeasuredHeight());
-                                Log.i(TAG, " viewSize: " + mInstance.getWidth() + "/"
-                                        + mInstance.getHeight());
-                            }
-                            mVideoWidth = videoWidth;
-                            mVideoHeight = videoHeight;
-                            mInstance.requestLayout();
-                        }
-
-                        if (needToStart()) {
-                            mMediaController.getTransportControls().play();
-                        }
-                    } else {
-                        // We don't know the video size yet, but should start anyway.
-                        // The video size might be reported to us later.
-                        if (needToStart()) {
-                            mMediaController.getTransportControls().play();
-                        }
-                    }
-                }
-
-                private void onCompletion(MediaPlayer2 mp, DataSourceDesc dsd) {
-                    mCurrentState = STATE_PLAYBACK_COMPLETED;
-                    mTargetState = STATE_PLAYBACK_COMPLETED;
-                    mSeekable = true;
-                    updatePlaybackState();
-                    if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
-                        mAudioManager.abandonAudioFocus(null);
-                    }
-                }
-
-                private void onBufferingUpdate(MediaPlayer2 mp, DataSourceDesc dsd, int percent) {
-                    mCurrentBufferPercentage = percent;
-                    updatePlaybackState();
-                }
-            };
-
-    private class MediaSessionCallback extends MediaSession.Callback {
-        @Override
-        public void onCommand(String command, Bundle args, ResultReceiver receiver) {
-            if (isRemotePlayback()) {
-                mRoutePlayer.onCommand(command, args, receiver);
-            } else {
-                switch (command) {
-                    case MediaControlView2Impl.COMMAND_SHOW_SUBTITLE:
-                        int subtitleIndex = args.getInt(
-                                MediaControlView2Impl.KEY_SELECTED_SUBTITLE_INDEX,
-                                INVALID_TRACK_INDEX);
-                        if (subtitleIndex != INVALID_TRACK_INDEX) {
-                            int subtitleTrackIndex = mSubtitleTrackIndices.get(subtitleIndex).first;
-                            if (subtitleTrackIndex != mSelectedSubtitleTrackIndex) {
-                                mSelectedSubtitleTrackIndex = subtitleTrackIndex;
-                                mInstance.setSubtitleEnabled(true);
-                            }
-                        }
-                        break;
-                    case MediaControlView2Impl.COMMAND_HIDE_SUBTITLE:
-                        mInstance.setSubtitleEnabled(false);
-                        break;
-                    case MediaControlView2Impl.COMMAND_SET_FULLSCREEN:
-                        if (mFullScreenRequestListener != null) {
-                            mFullScreenRequestListener.onFullScreenRequest(
-                                    mInstance,
-                                    args.getBoolean(MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN));
-                        }
-                        break;
-                    case MediaControlView2Impl.COMMAND_SELECT_AUDIO_TRACK:
-                        int audioIndex = args.getInt(MediaControlView2Impl.KEY_SELECTED_AUDIO_INDEX,
-                                INVALID_TRACK_INDEX);
-                        if (audioIndex != INVALID_TRACK_INDEX) {
-                            int audioTrackIndex = mAudioTrackIndices.get(audioIndex);
-                            if (audioTrackIndex != mSelectedAudioTrackIndex) {
-                                mSelectedAudioTrackIndex = audioTrackIndex;
-                                mMediaPlayer.selectTrack(mSelectedAudioTrackIndex);
-                            }
-                        }
-                        break;
-                    case MediaControlView2Impl.COMMAND_SET_PLAYBACK_SPEED:
-                        float speed = args.getFloat(
-                                MediaControlView2Impl.KEY_PLAYBACK_SPEED, INVALID_SPEED);
-                        if (speed != INVALID_SPEED && speed != mSpeed) {
-                            mInstance.setSpeed(speed);
-                            mSpeed = speed;
-                        }
-                        break;
-                    case MediaControlView2Impl.COMMAND_MUTE:
-                        mVolumeLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
-                        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
-                        break;
-                    case MediaControlView2Impl.COMMAND_UNMUTE:
-                        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mVolumeLevel, 0);
-                        break;
-                }
-            }
-            showController();
-        }
-
-        @Override
-        public void onCustomAction(String action, Bundle extras) {
-            mCustomActionListenerRecord.first.execute(() ->
-                    mCustomActionListenerRecord.second.onCustomAction(action, extras));
-            showController();
-        }
-
-        @Override
-        public void onPlay() {
-            if (isInPlaybackState() && (mCurrentView.hasAvailableSurface() || mIsMusicMediaType)) {
-                if (isRemotePlayback()) {
-                    mRoutePlayer.onPlay();
-                } else {
-                    applySpeed();
-                    mMediaPlayer.play();
-                    mCurrentState = STATE_PLAYING;
-                    updatePlaybackState();
-                }
-                mCurrentState = STATE_PLAYING;
-            }
-            mTargetState = STATE_PLAYING;
-            if (DEBUG) {
-                Log.d(TAG, "onPlay(). mCurrentState=" + mCurrentState
-                        + ", mTargetState=" + mTargetState);
-            }
-            showController();
-        }
-
-        @Override
-        public void onPause() {
-            if (isInPlaybackState()) {
-                if (isRemotePlayback()) {
-                    mRoutePlayer.onPause();
-                    mCurrentState = STATE_PAUSED;
-                } else if (mMediaPlayer.isPlaying()) {
-                    mMediaPlayer.pause();
-                    mCurrentState = STATE_PAUSED;
-                    updatePlaybackState();
-                }
-            }
-            mTargetState = STATE_PAUSED;
-            if (DEBUG) {
-                Log.d(TAG, "onPause(). mCurrentState=" + mCurrentState
-                        + ", mTargetState=" + mTargetState);
-            }
-            showController();
-        }
-
-        @Override
-        public void onSeekTo(long pos) {
-            if (isInPlaybackState()) {
-                if (isRemotePlayback()) {
-                    mRoutePlayer.onSeekTo(pos);
-                } else {
-                    mMediaPlayer.seekTo(pos, MediaPlayer2.SEEK_PREVIOUS_SYNC);
-                    mSeekWhenPrepared = 0;
-                }
-            } else {
-                mSeekWhenPrepared = pos;
-            }
-            showController();
-        }
-
-        @Override
-        public void onStop() {
-            if (isRemotePlayback()) {
-                mRoutePlayer.onStop();
-            } else {
-                resetPlayer();
-            }
-            showController();
-        }
-    }
-}
diff --git a/packages/MediaComponents/src/com/android/widget/ViewGroupImpl.java b/packages/MediaComponents/src/com/android/widget/ViewGroupImpl.java
deleted file mode 100644
index 5a06826..0000000
--- a/packages/MediaComponents/src/com/android/widget/ViewGroupImpl.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.widget;
-
-import android.media.update.ViewGroupProvider;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-public abstract class ViewGroupImpl implements ViewGroupProvider {
-    private final ViewGroupProvider mSuperProvider;
-
-    public ViewGroupImpl(ViewGroup instance,
-            ViewGroupProvider superProvider, ViewGroupProvider privateProvider) {
-        mSuperProvider = superProvider;
-    }
-
-    @Override
-    public void onAttachedToWindow_impl() {
-        mSuperProvider.onAttachedToWindow_impl();
-    }
-
-    @Override
-    public void onDetachedFromWindow_impl() {
-        mSuperProvider.onDetachedFromWindow_impl();
-    }
-
-    @Override
-    public CharSequence getAccessibilityClassName_impl() {
-        return mSuperProvider.getAccessibilityClassName_impl();
-    }
-
-    @Override
-    public boolean onTouchEvent_impl(MotionEvent ev) {
-        return mSuperProvider.onTouchEvent_impl(ev);
-    }
-
-    @Override
-    public boolean onTrackballEvent_impl(MotionEvent ev) {
-        return mSuperProvider.onTrackballEvent_impl(ev);
-    }
-
-    @Override
-    public void onFinishInflate_impl() {
-        mSuperProvider.onFinishInflate_impl();
-    }
-
-    @Override
-    public void setEnabled_impl(boolean enabled) {
-        mSuperProvider.setEnabled_impl(enabled);
-    }
-
-    @Override
-    public void onVisibilityAggregated_impl(boolean isVisible) {
-        mSuperProvider.onVisibilityAggregated_impl(isVisible);
-    }
-
-    @Override
-    public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
-        mSuperProvider.onLayout_impl(changed, left, top, right, bottom);
-    }
-
-    @Override
-    public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
-        mSuperProvider.onMeasure_impl(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    public int getSuggestedMinimumWidth_impl() {
-        return mSuperProvider.getSuggestedMinimumWidth_impl();
-    }
-
-    @Override
-    public int getSuggestedMinimumHeight_impl() {
-        return mSuperProvider.getSuggestedMinimumHeight_impl();
-    }
-
-    @Override
-    public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) {
-        mSuperProvider.setMeasuredDimension_impl(measuredWidth, measuredHeight);
-    }
-
-    @Override
-    public boolean dispatchTouchEvent_impl(MotionEvent ev) {
-        return mSuperProvider.dispatchTouchEvent_impl(ev);
-    }
-
-    @Override
-    public boolean checkLayoutParams_impl(ViewGroup.LayoutParams p) {
-        return mSuperProvider.checkLayoutParams_impl(p);
-    }
-
-    @Override
-    public ViewGroup.LayoutParams generateDefaultLayoutParams_impl() {
-        return mSuperProvider.generateDefaultLayoutParams_impl();
-    }
-
-    @Override
-    public ViewGroup.LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
-        return mSuperProvider.generateLayoutParams_impl(attrs);
-    }
-
-    @Override
-    public ViewGroup.LayoutParams generateLayoutParams_impl(ViewGroup.LayoutParams lp) {
-        return mSuperProvider.generateLayoutParams_impl(lp);
-    }
-
-    @Override
-    public boolean shouldDelayChildPressedState_impl() {
-        return mSuperProvider.shouldDelayChildPressedState_impl();
-    }
-
-    @Override
-    public void measureChildWithMargins_impl(View child,
-        int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
-        mSuperProvider.measureChildWithMargins_impl(child,
-                parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
-    }
-}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 52a8fa8..f833cf7 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3185,7 +3185,6 @@
     if (mType == OFFLOAD || mType == DIRECT) {
         mTimestampVerifier.setDiscontinuityMode(mTimestampVerifier.DISCONTINUITY_MODE_ZERO);
     }
-    audio_utils::Statistics<double> downstreamLatencyStatMs(0.999 /* alpha */);
     audio_patch_handle_t lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE;
 
     while (!exitPending())
@@ -3215,25 +3214,28 @@
                         downstreamPatchHandle = swPatches[0].getPatchHandle();
                 }
                 if (downstreamPatchHandle != lastDownstreamPatchHandle) {
-                    downstreamLatencyStatMs.reset();
+                    mDownstreamLatencyStatMs.reset();
                     lastDownstreamPatchHandle = downstreamPatchHandle;
                 }
                 if (status == OK) {
                     // verify downstream latency (we assume a max reasonable
-                    // latency of 1 second).
-                    if (latencyMs >= 0. && latencyMs <= 1000.) {
+                    // latency of 5 seconds).
+                    const double minLatency = 0., maxLatency = 5000.;
+                    if (latencyMs >= minLatency && latencyMs <= maxLatency) {
                         ALOGV("new downstream latency %lf ms", latencyMs);
-                        downstreamLatencyStatMs.add(latencyMs);
                     } else {
                         ALOGD("out of range downstream latency %lf ms", latencyMs);
+                        if (latencyMs < minLatency) latencyMs = minLatency;
+                        else if (latencyMs > maxLatency) latencyMs = maxLatency;
                     }
+                    mDownstreamLatencyStatMs.add(latencyMs);
                 }
                 mAudioFlinger->mLock.unlock();
             }
         } else {
             if (lastDownstreamPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
                 // our device is no longer AUDIO_DEVICE_OUT_BUS, reset patch handle and stats.
-                downstreamLatencyStatMs.reset();
+                mDownstreamLatencyStatMs.reset();
                 lastDownstreamPatchHandle = AUDIO_PATCH_HANDLE_NONE;
             }
         }
@@ -3282,10 +3284,10 @@
                             (long long)timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]);
 
                     // Note: Downstream latency only added if timestamp correction enabled.
-                    if (downstreamLatencyStatMs.getN() > 0) { // we have latency info.
+                    if (mDownstreamLatencyStatMs.getN() > 0) { // we have latency info.
                         const int64_t newPosition =
                                 timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]
-                                - int64_t(downstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
+                                - int64_t(mDownstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
                         // prevent retrograde
                         timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = max(
                                 newPosition,
@@ -3744,6 +3746,15 @@
         uint64_t position64;
         if (mOutput->getPresentationPosition(&position64, &timestamp.mTime) == OK) {
             timestamp.mPosition = (uint32_t)position64;
+            if (mDownstreamLatencyStatMs.getN() > 0) {
+                const uint32_t positionOffset =
+                    (uint32_t)(mDownstreamLatencyStatMs.getMean() * mSampleRate * 1e-3);
+                if (positionOffset > timestamp.mPosition) {
+                    timestamp.mPosition = 0;
+                } else {
+                    timestamp.mPosition -= positionOffset;
+                }
+            }
             return NO_ERROR;
         }
     }
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 61f7baf..49fc234 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1082,6 +1082,8 @@
     static const size_t     kFastMixerLogSize = 8 * 1024;
     sp<NBLog::Writer>       mFastMixerNBLogWriter;
 
+    // Downstream patch latency, available if mDownstreamLatencyStatMs.getN() > 0.
+    audio_utils::Statistics<double> mDownstreamLatencyStatMs{0.999};
 
 public:
     virtual     bool        hasFastMixer() const = 0;
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index a1163ed..9f2515e 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -53,6 +53,11 @@
         "device3/DistortionMapper.cpp",
         "gui/RingBufferConsumer.cpp",
         "utils/CameraThreadState.cpp",
+        "hidl/AidlCameraDeviceCallbacks.cpp",
+        "hidl/AidlCameraServiceListener.cpp",
+        "hidl/Convert.cpp",
+        "hidl/HidlCameraDeviceUser.cpp",
+        "hidl/HidlCameraService.cpp",
         "utils/CameraTraces.cpp",
         "utils/AutoConditionLock.cpp",
         "utils/TagMonitor.cpp",
@@ -67,6 +72,7 @@
         "libbinder",
         "libcutils",
         "libmedia",
+        "libmediandk",
         "libmediautils",
         "libcamera_client",
         "libcamera_metadata",
@@ -78,6 +84,10 @@
         "libhidltransport",
         "libjpeg",
         "libmemunreachable",
+        "libstagefright_foundation",
+        "android.frameworks.cameraservice.common@2.0",
+        "android.frameworks.cameraservice.service@2.0",
+        "android.frameworks.cameraservice.device@2.0",
         "android.hardware.camera.common@1.0",
         "android.hardware.camera.provider@2.4",
         "android.hardware.camera.device@1.0",
@@ -96,6 +106,7 @@
     include_dirs: [
         "system/media/private/camera/include",
         "frameworks/native/include/media/openmax",
+        "frameworks/av/media/ndk",
     ],
 
     export_include_dirs: ["."],
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index e69ce1f..4dacd02 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -47,6 +47,8 @@
 #include <cutils/misc.h>
 #include <gui/Surface.h>
 #include <hardware/hardware.h>
+#include "hidl/HidlCameraService.h"
+#include <hidl/HidlTransportSupport.h>
 #include <memunreachable/memunreachable.h>
 #include <media/AudioSystem.h>
 #include <media/IMediaHTTPService.h>
@@ -78,6 +80,7 @@
 namespace android {
 
 using binder::Status;
+using frameworks::cameraservice::service::V2_0::implementation::HidlCameraService;
 using hardware::ICamera;
 using hardware::ICameraClient;
 using hardware::ICameraServiceProxy;
@@ -142,6 +145,11 @@
 
     mUidPolicy = new UidPolicy(this);
     mUidPolicy->registerSelf();
+    sp<HidlCameraService> hcs = HidlCameraService::getInstance(this);
+    if (hcs->registerAsService() != android::OK) {
+        ALOGE("%s: Failed to register default android.frameworks.cameraservice.service@1.0",
+              __FUNCTION__);
+    }
 }
 
 status_t CameraService::enumerateProviders() {
@@ -209,6 +217,14 @@
     proxyBinder->pingForUserUpdate();
 }
 
+void CameraService::broadcastTorchModeStatus(const String8& cameraId, TorchModeStatus status) {
+    Mutex::Autolock lock(mStatusListenerLock);
+
+    for (auto& i : mListenerList) {
+        i->onTorchStatusChanged(mapToInterface(status), String16{cameraId});
+    }
+}
+
 CameraService::~CameraService() {
     VendorTagDescriptor::clearGlobalVendorTagDescriptor();
     mUidPolicy->unregisterSelf();
@@ -247,6 +263,8 @@
     if (mFlashlight->hasFlashUnit(id)) {
         Mutex::Autolock al(mTorchStatusMutex);
         mTorchStatusMap.add(id, TorchModeStatus::AVAILABLE_OFF);
+
+        broadcastTorchModeStatus(id, TorchModeStatus::AVAILABLE_OFF);
     }
 
     updateCameraNumAndIds();
@@ -399,12 +417,7 @@
         }
     }
 
-    {
-        Mutex::Autolock lock(mStatusListenerLock);
-        for (auto& i : mListenerList) {
-            i->onTorchStatusChanged(mapToInterface(newStatus), String16{cameraId});
-        }
-    }
+    broadcastTorchModeStatus(cameraId, newStatus);
 }
 
 Status CameraService::getNumberOfCameras(int32_t type, int32_t* numCameras) {
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 80d9ef4..064863f 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -857,6 +857,8 @@
     static sp<hardware::ICameraServiceProxy> getCameraServiceProxy();
     static void pingCameraServiceProxy();
 
+    void broadcastTorchModeStatus(const String8& cameraId,
+            hardware::camera::common::V1_0::TorchModeStatus status);
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index a94e886..2542ab2 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -425,6 +425,102 @@
     }
 }
 
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::fixupMonochromeTags() {
+    status_t res = OK;
+    auto& c = mCameraCharacteristics;
+
+    // Override static metadata for MONOCHROME camera with older device version
+    if (mVersion.get_major() == 3 && mVersion.get_minor() < 5) {
+        camera_metadata_entry cap = c.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+        for (size_t i = 0; i < cap.count; i++) {
+            if (cap.data.u8[i] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME) {
+                // ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+                uint8_t cfa = ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO;
+                res = c.update(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT, &cfa, 1);
+                if (res != OK) {
+                    ALOGE("%s: Failed to update COLOR_FILTER_ARRANGEMENT: %s (%d)",
+                          __FUNCTION__, strerror(-res), res);
+                    return res;
+                }
+
+                // ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS
+                const std::vector<uint32_t> sKeys = {
+                        ANDROID_SENSOR_REFERENCE_ILLUMINANT1,
+                        ANDROID_SENSOR_REFERENCE_ILLUMINANT2,
+                        ANDROID_SENSOR_CALIBRATION_TRANSFORM1,
+                        ANDROID_SENSOR_CALIBRATION_TRANSFORM2,
+                        ANDROID_SENSOR_COLOR_TRANSFORM1,
+                        ANDROID_SENSOR_COLOR_TRANSFORM2,
+                        ANDROID_SENSOR_FORWARD_MATRIX1,
+                        ANDROID_SENSOR_FORWARD_MATRIX2,
+                };
+                res = removeAvailableKeys(c, sKeys,
+                        ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+                if (res != OK) {
+                    ALOGE("%s: Failed to update REQUEST_AVAILABLE_CHARACTERISTICS_KEYS: %s (%d)",
+                            __FUNCTION__, strerror(-res), res);
+                    return res;
+                }
+
+                // ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS
+                const std::vector<uint32_t> reqKeys = {
+                        ANDROID_COLOR_CORRECTION_MODE,
+                        ANDROID_COLOR_CORRECTION_TRANSFORM,
+                        ANDROID_COLOR_CORRECTION_GAINS,
+                };
+                res = removeAvailableKeys(c, reqKeys, ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
+                if (res != OK) {
+                    ALOGE("%s: Failed to update REQUEST_AVAILABLE_REQUEST_KEYS: %s (%d)",
+                            __FUNCTION__, strerror(-res), res);
+                    return res;
+                }
+
+                // ANDROID_REQUEST_AVAILABLE_RESULT_KEYS
+                const std::vector<uint32_t> resKeys = {
+                        ANDROID_SENSOR_GREEN_SPLIT,
+                        ANDROID_SENSOR_NEUTRAL_COLOR_POINT,
+                        ANDROID_COLOR_CORRECTION_MODE,
+                        ANDROID_COLOR_CORRECTION_TRANSFORM,
+                        ANDROID_COLOR_CORRECTION_GAINS,
+                };
+                res = removeAvailableKeys(c, resKeys, ANDROID_REQUEST_AVAILABLE_RESULT_KEYS);
+                if (res != OK) {
+                    ALOGE("%s: Failed to update REQUEST_AVAILABLE_RESULT_KEYS: %s (%d)",
+                            __FUNCTION__, strerror(-res), res);
+                    return res;
+                }
+
+                // ANDROID_SENSOR_BLACK_LEVEL_PATTERN
+                camera_metadata_entry blEntry = c.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
+                for (size_t j = 1; j < blEntry.count; j++) {
+                    blEntry.data.i32[j] = blEntry.data.i32[0];
+                }
+            }
+        }
+    }
+    return res;
+}
+
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::removeAvailableKeys(
+        CameraMetadata& c, const std::vector<uint32_t>& keys, uint32_t keyTag) {
+    status_t res = OK;
+
+    camera_metadata_entry keysEntry = c.find(keyTag);
+    if (keysEntry.count == 0) {
+        ALOGE("%s: Failed to find tag %u: %s (%d)", __FUNCTION__, keyTag, strerror(-res), res);
+        return res;
+    }
+    std::vector<int32_t> vKeys;
+    vKeys.reserve(keysEntry.count);
+    for (size_t i = 0; i < keysEntry.count; i++) {
+        if (std::find(keys.begin(), keys.end(), keysEntry.data.i32[i]) == keys.end()) {
+            vKeys.push_back(keysEntry.data.i32[i]);
+        }
+    }
+    res = c.update(keyTag, vKeys.data(), vKeys.size());
+    return res;
+}
+
 bool CameraProviderManager::isLogicalCamera(const std::string& id,
         std::vector<std::string>* physicalCameraIds) {
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
@@ -1131,6 +1227,12 @@
                 __FUNCTION__, mId.c_str(), CameraProviderManager::statusToString(status), status);
         return;
     }
+    status_t res = fixupMonochromeTags();
+    if (OK != res) {
+        ALOGE("%s: Unable to fix up monochrome tags based for older HAL version: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        return;
+    }
     camera_metadata_entry flashAvailable =
             mCameraCharacteristics.find(ANDROID_FLASH_INFO_AVAILABLE);
     if (flashAvailable.count == 1 &&
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 9016747..c506d35 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -385,6 +385,9 @@
             CameraMetadata mCameraCharacteristics;
             std::unordered_map<std::string, CameraMetadata> mPhysicalCameraCharacteristics;
             void queryPhysicalCameraIds();
+            status_t fixupMonochromeTags();
+            status_t removeAvailableKeys(CameraMetadata& c, const std::vector<uint32_t>& keys,
+                    uint32_t keyTag);
         };
 
     private:
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 53aee7e..32952cc 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -79,11 +79,10 @@
         mNextReprocessShutterFrameNumber(0),
         mListener(NULL),
         mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID),
-        mLastTemplateId(-1)
+        mLastTemplateId(-1),
+        mNeedFixupMonochromeTags(false)
 {
     ATRACE_CALL();
-    camera3_callback_ops::notify = &sNotify;
-    camera3_callback_ops::process_capture_result = &sProcessCaptureResult;
     ALOGV("%s: Created device for camera %s", __FUNCTION__, mId.string());
 }
 
@@ -182,7 +181,14 @@
             });
     }
 
-    mInterface = new HalInterface(session, queue);
+    camera_metadata_entry bufMgrMode =
+            mDeviceInfo.find(ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION);
+    if (bufMgrMode.count > 0) {
+         mUseHalBufManager = (bufMgrMode.data.u8[0] ==
+            ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+    }
+
+    mInterface = new HalInterface(session, queue, mUseHalBufManager);
     std::string providerType;
     mVendorTagId = manager->getProviderTagIdLocked(mId.string());
     mTagMonitor.initialize(mVendorTagId);
@@ -190,6 +196,28 @@
         mTagMonitor.parseTagsToMonitor(String8(monitorTags));
     }
 
+    // Metadata tags needs fixup for monochrome camera device version less
+    // than 3.5.
+    hardware::hidl_version maxVersion{0,0};
+    res = manager->getHighestSupportedVersion(mId.string(), &maxVersion);
+    if (res != OK) {
+        ALOGE("%s: Error in getting camera device version id: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+        return res;
+    }
+    int deviceVersion = HARDWARE_DEVICE_API_VERSION(
+            maxVersion.get_major(), maxVersion.get_minor());
+
+    bool isMonochrome = false;
+    camera_metadata_entry_t entry = mDeviceInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+    for (size_t i = 0; i < entry.count; i++) {
+        uint8_t capability = entry.data.u8[i];
+        if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME) {
+            isMonochrome = true;
+        }
+    }
+    mNeedFixupMonochromeTags = (isMonochrome && deviceVersion < CAMERA_DEVICE_API_VERSION_3_5);
+
     return initializeCommonLocked();
 }
 
@@ -209,6 +237,17 @@
     /** Register in-flight map to the status tracker */
     mInFlightStatusId = mStatusTracker->addComponent();
 
+    if (mUseHalBufManager) {
+        res = mRequestBufferSM.initialize(mStatusTracker);
+        if (res != OK) {
+            SET_ERR_L("Unable to start request buffer state machine: %s (%d)",
+                    strerror(-res), res);
+            mInterface->close();
+            mStatusTracker.clear();
+            return res;
+        }
+    }
+
     /** Create buffer manager */
     mBufferManager = new Camera3BufferManager();
 
@@ -218,8 +257,10 @@
     if (sessionKeysEntry.count > 0) {
         sessionParamKeys.insertArrayAt(sessionKeysEntry.data.i32, 0, sessionKeysEntry.count);
     }
+
     /** Start up request queue thread */
-    mRequestThread = new RequestThread(this, mStatusTracker, mInterface, sessionParamKeys);
+    mRequestThread = new RequestThread(
+            this, mStatusTracker, mInterface, sessionParamKeys, mUseHalBufManager);
     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)",
@@ -271,7 +312,6 @@
             return res;
         }
     }
-
     return OK;
 }
 
@@ -919,6 +959,221 @@
     return res;
 }
 
+hardware::Return<void> Camera3Device::requestStreamBuffers(
+        const hardware::hidl_vec<hardware::camera::device::V3_5::BufferRequest>& bufReqs,
+        requestStreamBuffers_cb _hidl_cb) {
+    using hardware::camera::device::V3_5::BufferRequestStatus;
+    using hardware::camera::device::V3_5::StreamBufferRet;
+    using hardware::camera::device::V3_5::StreamBufferRequestError;
+
+    std::lock_guard<std::mutex> lock(mRequestBufferInterfaceLock);
+
+    hardware::hidl_vec<StreamBufferRet> bufRets;
+    if (!mUseHalBufManager) {
+        ALOGE("%s: Camera %s does not support HAL buffer management",
+                __FUNCTION__, mId.string());
+        _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+        return hardware::Void();
+    }
+
+    SortedVector<int32_t> streamIds;
+    ssize_t sz = streamIds.setCapacity(bufReqs.size());
+    if (sz < 0 || static_cast<size_t>(sz) != bufReqs.size()) {
+        ALOGE("%s: failed to allocate memory for %zu buffer requests",
+                __FUNCTION__, bufReqs.size());
+        _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+        return hardware::Void();
+    }
+
+    if (bufReqs.size() > mOutputStreams.size()) {
+        ALOGE("%s: too many buffer requests (%zu > # of output streams %zu)",
+                __FUNCTION__, bufReqs.size(), mOutputStreams.size());
+        _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+        return hardware::Void();
+    }
+
+    // Check for repeated streamId
+    for (const auto& bufReq : bufReqs) {
+        if (streamIds.indexOf(bufReq.streamId) != NAME_NOT_FOUND) {
+            ALOGE("%s: Stream %d appear multiple times in buffer requests",
+                    __FUNCTION__, bufReq.streamId);
+            _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, bufRets);
+            return hardware::Void();
+        }
+        streamIds.add(bufReq.streamId);
+    }
+
+    if (!mRequestBufferSM.startRequestBuffer()) {
+        ALOGE("%s: request buffer disallowed while camera service is configuring",
+                __FUNCTION__);
+        _hidl_cb(BufferRequestStatus::FAILED_CONFIGURING, bufRets);
+        return hardware::Void();
+    }
+
+    bufRets.resize(bufReqs.size());
+
+    bool allReqsSucceeds = true;
+    bool oneReqSucceeds = false;
+    for (size_t i = 0; i < bufReqs.size(); i++) {
+        const auto& bufReq = bufReqs[i];
+        auto& bufRet = bufRets[i];
+        int32_t streamId = bufReq.streamId;
+        sp<Camera3OutputStreamInterface> outputStream = mOutputStreams.get(streamId);
+        if (outputStream == nullptr) {
+            ALOGE("%s: Output stream id %d not found!", __FUNCTION__, streamId);
+            hardware::hidl_vec<StreamBufferRet> emptyBufRets;
+            _hidl_cb(BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS, emptyBufRets);
+            mRequestBufferSM.endRequestBuffer();
+            return hardware::Void();
+        }
+
+        bufRet.streamId = streamId;
+        uint32_t numBuffersRequested = bufReq.numBuffersRequested;
+        size_t totalHandout = outputStream->getOutstandingBuffersCount() + numBuffersRequested;
+        if (totalHandout > outputStream->asHalStream()->max_buffers) {
+            // Not able to allocate enough buffer. Exit early for this stream
+            bufRet.val.error(StreamBufferRequestError::MAX_BUFFER_EXCEEDED);
+            allReqsSucceeds = false;
+            continue;
+        }
+
+        hardware::hidl_vec<StreamBuffer> tmpRetBuffers(numBuffersRequested);
+        bool currentReqSucceeds = true;
+        std::vector<camera3_stream_buffer_t> streamBuffers(numBuffersRequested);
+        size_t numAllocatedBuffers = 0;
+        size_t numPushedInflightBuffers = 0;
+        for (size_t b = 0; b < numBuffersRequested; b++) {
+            camera3_stream_buffer_t& sb = streamBuffers[b];
+            // Since this method can run concurrently with request thread
+            // We need to update the wait duration everytime we call getbuffer
+            nsecs_t waitDuration = kBaseGetBufferWait + getExpectedInFlightDuration();
+            status_t res = outputStream->getBuffer(&sb, waitDuration);
+            if (res != OK) {
+                ALOGE("%s: Can't get output buffer for stream %d: %s (%d)",
+                        __FUNCTION__, streamId, strerror(-res), res);
+                if (res == NO_INIT || res == DEAD_OBJECT) {
+                    bufRet.val.error(StreamBufferRequestError::STREAM_DISCONNECTED);
+                } else if (res == TIMED_OUT || res == NO_MEMORY) {
+                    bufRet.val.error(StreamBufferRequestError::NO_BUFFER_AVAILABLE);
+                } else {
+                    bufRet.val.error(StreamBufferRequestError::UNKNOWN_ERROR);
+                }
+                currentReqSucceeds = false;
+                break;
+            }
+            numAllocatedBuffers++;
+
+            buffer_handle_t *buffer = sb.buffer;
+            auto pair = mInterface->getBufferId(*buffer, streamId);
+            bool isNewBuffer = pair.first;
+            uint64_t bufferId = pair.second;
+            StreamBuffer& hBuf = tmpRetBuffers[b];
+
+            hBuf.streamId = streamId;
+            hBuf.bufferId = bufferId;
+            hBuf.buffer = (isNewBuffer) ? *buffer : nullptr;
+            hBuf.status = BufferStatus::OK;
+            hBuf.releaseFence = nullptr;
+
+            native_handle_t *acquireFence = nullptr;
+            if (sb.acquire_fence != -1) {
+                acquireFence = native_handle_create(1,0);
+                acquireFence->data[0] = sb.acquire_fence;
+            }
+            hBuf.acquireFence.setTo(acquireFence, /*shouldOwn*/true);
+            hBuf.releaseFence = nullptr;
+
+            res = mInterface->pushInflightRequestBuffer(bufferId, buffer);
+            if (res != OK) {
+                ALOGE("%s: Can't get register request buffers for stream %d: %s (%d)",
+                        __FUNCTION__, streamId, strerror(-res), res);
+                bufRet.val.error(StreamBufferRequestError::UNKNOWN_ERROR);
+                currentReqSucceeds = false;
+                break;
+            }
+            numPushedInflightBuffers++;
+        }
+        if (currentReqSucceeds) {
+            bufRet.val.buffers(std::move(tmpRetBuffers));
+            oneReqSucceeds = true;
+        } else {
+            allReqsSucceeds = false;
+            for (size_t b = 0; b < numPushedInflightBuffers; b++) {
+                StreamBuffer& hBuf = tmpRetBuffers[b];
+                buffer_handle_t* buffer;
+                status_t res = mInterface->popInflightRequestBuffer(hBuf.bufferId, &buffer);
+                if (res != OK) {
+                    SET_ERR("%s: popInflightRequestBuffer failed for stream %d: %s (%d)",
+                            __FUNCTION__, streamId, strerror(-res), res);
+                }
+            }
+            for (size_t b = 0; b < numAllocatedBuffers; b++) {
+                camera3_stream_buffer_t& sb = streamBuffers[b];
+                sb.acquire_fence = -1;
+                sb.status = CAMERA3_BUFFER_STATUS_ERROR;
+            }
+            returnOutputBuffers(streamBuffers.data(), numAllocatedBuffers, 0);
+        }
+    }
+
+    _hidl_cb(allReqsSucceeds ? BufferRequestStatus::OK :
+            oneReqSucceeds ? BufferRequestStatus::FAILED_PARTIAL :
+                             BufferRequestStatus::FAILED_UNKNOWN,
+            bufRets);
+    mRequestBufferSM.endRequestBuffer();
+    return hardware::Void();
+}
+
+hardware::Return<void> Camera3Device::returnStreamBuffers(
+        const hardware::hidl_vec<hardware::camera::device::V3_2::StreamBuffer>& buffers) {
+    if (!mUseHalBufManager) {
+        ALOGE("%s: Camera %s does not support HAL buffer managerment",
+                __FUNCTION__, mId.string());
+        return hardware::Void();
+    }
+
+    for (const auto& buf : buffers) {
+        if (buf.bufferId == HalInterface::BUFFER_ID_NO_BUFFER) {
+            ALOGE("%s: cannot return a buffer without bufferId", __FUNCTION__);
+            continue;
+        }
+
+        buffer_handle_t* buffer;
+        status_t res = mInterface->popInflightRequestBuffer(buf.bufferId, &buffer);
+
+        if (res != OK) {
+            ALOGE("%s: cannot find in-flight buffer %" PRIu64 " for stream %d",
+                    __FUNCTION__, buf.bufferId, buf.streamId);
+            continue;
+        }
+
+        camera3_stream_buffer_t streamBuffer;
+        streamBuffer.buffer = buffer;
+        streamBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+        streamBuffer.acquire_fence = -1;
+        streamBuffer.release_fence = -1;
+
+        if (buf.releaseFence == nullptr) {
+            streamBuffer.release_fence = -1;
+        } else if (buf.releaseFence->numFds == 1) {
+            streamBuffer.release_fence = dup(buf.releaseFence->data[0]);
+        } else {
+            ALOGE("%s: Invalid release fence, fd count is %d, not 1",
+                    __FUNCTION__, buf.releaseFence->numFds);
+            continue;
+        }
+
+        sp<Camera3StreamInterface> stream = mOutputStreams.get(buf.streamId);
+        if (stream == nullptr) {
+            ALOGE("%s: Output stream id %d not found!", __FUNCTION__, buf.streamId);
+            continue;
+        }
+        streamBuffer.stream = stream->asHalStream();
+        returnOutputBuffers(&streamBuffer, /*size*/1, /*timestamp*/ 0);
+    }
+    return hardware::Void();
+}
+
 hardware::Return<void> Camera3Device::processCaptureResult_3_4(
         const hardware::hidl_vec<
                 hardware::camera::device::V3_4::CaptureResult>& results) {
@@ -1067,27 +1322,51 @@
         auto& bDst = outputBuffers[i];
         const StreamBuffer &bSrc = result.outputBuffers[i];
 
-        ssize_t idx = mOutputStreams.indexOfKey(bSrc.streamId);
-        if (idx == NAME_NOT_FOUND) {
+        sp<Camera3StreamInterface> stream = mOutputStreams.get(bSrc.streamId);
+        if (stream == nullptr) {
             ALOGE("%s: Frame %d: Buffer %zu: Invalid output stream id %d",
                     __FUNCTION__, result.frameNumber, i, bSrc.streamId);
             return;
         }
-        bDst.stream = mOutputStreams.valueAt(idx)->asHalStream();
+        bDst.stream = stream->asHalStream();
 
-        buffer_handle_t *buffer;
-        res = mInterface->popInflightBuffer(result.frameNumber, bSrc.streamId, &buffer);
+        bool noBufferReturned = false;
+        buffer_handle_t *buffer = nullptr;
+        if (mUseHalBufManager) {
+            // This is suspicious most of the time but can be correct during flush where HAL
+            // has to return capture result before a buffer is requested
+            if (bSrc.bufferId == HalInterface::BUFFER_ID_NO_BUFFER) {
+                if (bSrc.status == BufferStatus::OK) {
+                    ALOGE("%s: Frame %d: Buffer %zu: No bufferId for stream %d",
+                            __FUNCTION__, result.frameNumber, i, bSrc.streamId);
+                    // Still proceeds so other buffers can be returned
+                }
+                noBufferReturned = true;
+            }
+            if (noBufferReturned) {
+                res = OK;
+            } else {
+                res = mInterface->popInflightRequestBuffer(bSrc.bufferId, &buffer);
+            }
+        } else {
+            res = mInterface->popInflightBuffer(result.frameNumber, bSrc.streamId, &buffer);
+        }
+
         if (res != OK) {
             ALOGE("%s: Frame %d: Buffer %zu: No in-flight buffer for stream %d",
                     __FUNCTION__, result.frameNumber, i, bSrc.streamId);
             return;
         }
+
         bDst.buffer = buffer;
         bDst.status = mapHidlBufferStatus(bSrc.status);
         bDst.acquire_fence = -1;
         if (bSrc.releaseFence == nullptr) {
             bDst.release_fence = -1;
         } else if (bSrc.releaseFence->numFds == 1) {
+            if (noBufferReturned) {
+                ALOGE("%s: got releaseFence without output buffer!", __FUNCTION__);
+            }
             bDst.release_fence = dup(bSrc.releaseFence->data[0]);
         } else {
             ALOGE("%s: Frame %d: Invalid release fence for buffer %zu, fd count is %d, not 1",
@@ -1163,13 +1442,13 @@
             m.type = CAMERA3_MSG_ERROR;
             m.message.error.frame_number = msg.msg.error.frameNumber;
             if (msg.msg.error.errorStreamId >= 0) {
-                ssize_t idx = mOutputStreams.indexOfKey(msg.msg.error.errorStreamId);
-                if (idx == NAME_NOT_FOUND) {
-                    ALOGE("%s: Frame %d: Invalid error stream id %d",
-                            __FUNCTION__, m.message.error.frame_number, msg.msg.error.errorStreamId);
+                sp<Camera3StreamInterface> stream = mOutputStreams.get(msg.msg.error.errorStreamId);
+                if (stream == nullptr) {
+                    ALOGE("%s: Frame %d: Invalid error stream id %d", __FUNCTION__,
+                            m.message.error.frame_number, msg.msg.error.errorStreamId);
                     return;
                 }
-                m.message.error.error_stream = mOutputStreams.valueAt(idx)->asHalStream();
+                m.message.error.error_stream = stream->asHalStream();
             } else {
                 m.message.error.error_stream = nullptr;
             }
@@ -1350,6 +1629,56 @@
     return OK;
 }
 
+status_t Camera3Device::StreamSet::add(
+        int streamId, sp<camera3::Camera3OutputStreamInterface> stream) {
+    if (stream == nullptr) {
+        ALOGE("%s: cannot add null stream", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    std::lock_guard<std::mutex> lock(mLock);
+    return mData.add(streamId, stream);
+}
+
+ssize_t Camera3Device::StreamSet::remove(int streamId) {
+    std::lock_guard<std::mutex> lock(mLock);
+    return mData.removeItem(streamId);
+}
+
+sp<camera3::Camera3OutputStreamInterface>
+Camera3Device::StreamSet::get(int streamId) {
+    std::lock_guard<std::mutex> lock(mLock);
+    ssize_t idx = mData.indexOfKey(streamId);
+    if (idx == NAME_NOT_FOUND) {
+        return nullptr;
+    }
+    return mData.editValueAt(idx);
+}
+
+sp<camera3::Camera3OutputStreamInterface>
+Camera3Device::StreamSet::operator[] (size_t index) {
+    std::lock_guard<std::mutex> lock(mLock);
+    return mData.editValueAt(index);
+}
+
+size_t Camera3Device::StreamSet::size() const {
+    std::lock_guard<std::mutex> lock(mLock);
+    return mData.size();
+}
+
+void Camera3Device::StreamSet::clear() {
+    std::lock_guard<std::mutex> lock(mLock);
+    return mData.clear();
+}
+
+std::vector<int> Camera3Device::StreamSet::getStreamIds() {
+    std::lock_guard<std::mutex> lock(mLock);
+    std::vector<int> streamIds(mData.size());
+    for (size_t i = 0; i < mData.size(); i++) {
+        streamIds[i] = mData.keyAt(i);
+    }
+    return streamIds;
+}
+
 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,
@@ -1456,7 +1785,8 @@
     } else if (isShared) {
         newStream = new Camera3SharedOutputStream(mNextStreamId, consumers,
                 width, height, format, consumerUsage, dataSpace, rotation,
-                mTimestampOffset, physicalCameraId, streamSetId);
+                mTimestampOffset, physicalCameraId, streamSetId,
+                mUseHalBufManager);
     } else if (consumers.size() == 0 && hasDeferredConsumer) {
         newStream = new Camera3OutputStream(mNextStreamId,
                 width, height, format, consumerUsage, dataSpace, rotation,
@@ -1533,20 +1863,20 @@
             return INVALID_OPERATION;
     }
 
-    ssize_t idx = mOutputStreams.indexOfKey(id);
-    if (idx == NAME_NOT_FOUND) {
+    sp<Camera3StreamInterface> stream = mOutputStreams.get(id);
+    if (stream == nullptr) {
         CLOGE("Stream %d is unknown", id);
-        return idx;
+        return BAD_VALUE;
     }
 
-    streamInfo->width  = mOutputStreams[idx]->getWidth();
-    streamInfo->height = mOutputStreams[idx]->getHeight();
-    streamInfo->format = mOutputStreams[idx]->getFormat();
-    streamInfo->dataSpace = mOutputStreams[idx]->getDataSpace();
-    streamInfo->formatOverridden = mOutputStreams[idx]->isFormatOverridden();
-    streamInfo->originalFormat = mOutputStreams[idx]->getOriginalFormat();
-    streamInfo->dataSpaceOverridden = mOutputStreams[idx]->isDataSpaceOverridden();
-    streamInfo->originalDataSpace = mOutputStreams[idx]->getOriginalDataSpace();
+    streamInfo->width  = stream->getWidth();
+    streamInfo->height = stream->getHeight();
+    streamInfo->format = stream->getFormat();
+    streamInfo->dataSpace = stream->getDataSpace();
+    streamInfo->formatOverridden = stream->isFormatOverridden();
+    streamInfo->originalFormat = stream->getOriginalFormat();
+    streamInfo->dataSpaceOverridden = stream->isDataSpaceOverridden();
+    streamInfo->originalDataSpace = stream->getOriginalDataSpace();
     return OK;
 }
 
@@ -1573,14 +1903,12 @@
             return INVALID_OPERATION;
     }
 
-    ssize_t idx = mOutputStreams.indexOfKey(id);
-    if (idx == NAME_NOT_FOUND) {
-        CLOGE("Stream %d does not exist",
-                id);
+    sp<Camera3OutputStreamInterface> stream = mOutputStreams.get(id);
+    if (stream == nullptr) {
+        CLOGE("Stream %d does not exist", id);
         return BAD_VALUE;
     }
-
-    return mOutputStreams.editValueAt(idx)->setTransform(transform);
+    return stream->setTransform(transform);
 }
 
 status_t Camera3Device::deleteStream(int id) {
@@ -1605,21 +1933,21 @@
     }
 
     sp<Camera3StreamInterface> deletedStream;
-    ssize_t outputStreamIdx = mOutputStreams.indexOfKey(id);
+    sp<Camera3StreamInterface> stream = mOutputStreams.get(id);
     if (mInputStream != NULL && id == mInputStream->getId()) {
         deletedStream = mInputStream;
         mInputStream.clear();
     } else {
-        if (outputStreamIdx == NAME_NOT_FOUND) {
+        if (stream == nullptr) {
             CLOGE("Stream %d does not exist", id);
             return BAD_VALUE;
         }
     }
 
     // Delete output stream or the output part of a bi-directional stream.
-    if (outputStreamIdx != NAME_NOT_FOUND) {
-        deletedStream = mOutputStreams.editValueAt(outputStreamIdx);
-        mOutputStreams.removeItem(id);
+    if (stream != nullptr) {
+        deletedStream = stream;
+        mOutputStreams.remove(id);
     }
 
     // Free up the stream endpoint so that it can be used by some other stream
@@ -1856,6 +2184,15 @@
 
     mStatusWaiters++;
 
+    // Notify HAL to start draining. We need to notify the HalInterface layer
+    // even when the device is already IDLE, so HalInterface can reject incoming
+    // requestStreamBuffers call.
+    if (!active && mUseHalBufManager) {
+        auto streamIds = mOutputStreams.getStreamIds();
+        mRequestThread->signalPipelineDrain(streamIds);
+        mRequestBufferSM.onWaitUntilIdle();
+    }
+
     bool stateSeen = false;
     do {
         if (active == (mStatus == STATUS_ACTIVE)) {
@@ -2038,15 +2375,12 @@
     Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
-    sp<Camera3StreamInterface> stream;
-    ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId);
-    if (outputStreamIdx == NAME_NOT_FOUND) {
+    sp<Camera3StreamInterface> stream = mOutputStreams.get(streamId);
+    if (stream == nullptr) {
         CLOGE("Stream %d does not exist", streamId);
         return BAD_VALUE;
     }
 
-    stream = mOutputStreams.editValueAt(outputStreamIdx);
-
     if (stream->isUnpreparable() || stream->hasOutstandingBuffers() ) {
         CLOGE("Stream %d has already been a request target", streamId);
         return BAD_VALUE;
@@ -2066,15 +2400,12 @@
     Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
-    sp<Camera3StreamInterface> stream;
-    ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId);
-    if (outputStreamIdx == NAME_NOT_FOUND) {
+    sp<Camera3StreamInterface> stream = mOutputStreams.get(streamId);
+    if (stream == nullptr) {
         CLOGE("Stream %d does not exist", streamId);
         return BAD_VALUE;
     }
 
-    stream = mOutputStreams.editValueAt(outputStreamIdx);
-
     if (stream->hasOutstandingBuffers() || mRequestThread->isStreamPending(stream)) {
         CLOGE("Stream %d is a target of a in-progress request", streamId);
         return BAD_VALUE;
@@ -2090,14 +2421,11 @@
     Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
-    sp<Camera3StreamInterface> stream;
-    ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId);
-    if (outputStreamIdx == NAME_NOT_FOUND) {
+    sp<Camera3StreamInterface> stream = mOutputStreams.get(streamId);
+    if (stream == nullptr) {
         CLOGE("Stream %d does not exist", streamId);
         return BAD_VALUE;
     }
-
-    stream = mOutputStreams.editValueAt(outputStreamIdx);
     stream->addBufferListener(listener);
 
     return OK;
@@ -2156,12 +2484,11 @@
         return BAD_VALUE;
     }
 
-    ssize_t idx = mOutputStreams.indexOfKey(streamId);
-    if (idx == NAME_NOT_FOUND) {
+    sp<Camera3OutputStreamInterface> stream = mOutputStreams.get(streamId);
+    if (stream == nullptr) {
         CLOGE("Stream %d is unknown", streamId);
-        return idx;
+        return BAD_VALUE;
     }
-    sp<Camera3OutputStreamInterface> stream = mOutputStreams[idx];
     status_t res = stream->setConsumers(consumers);
     if (res != OK) {
         CLOGE("Stream %d set consumer failed (error %d %s) ", streamId, res, strerror(-res));
@@ -2206,10 +2533,10 @@
     Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
-    ssize_t idx = mOutputStreams.indexOfKey(streamId);
-    if (idx == NAME_NOT_FOUND) {
+    sp<Camera3OutputStreamInterface> stream = mOutputStreams.get(streamId);
+    if (stream == nullptr) {
         CLOGE("Stream %d is unknown", streamId);
-        return idx;
+        return BAD_VALUE;
     }
 
     for (const auto &it : removedSurfaceIds) {
@@ -2219,7 +2546,6 @@
         }
     }
 
-    sp<Camera3OutputStreamInterface> stream = mOutputStreams[idx];
     status_t res = stream->updateStream(newSurfaces, outputInfo, removedSurfaceIds, outputMap);
     if (res != OK) {
         CLOGE("Stream %d failed to update stream (error %d %s) ",
@@ -2238,13 +2564,11 @@
     Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
-    int idx = mOutputStreams.indexOfKey(streamId);
-    if (idx == NAME_NOT_FOUND) {
+    sp<Camera3OutputStreamInterface> stream = mOutputStreams.get(streamId);
+    if (stream == nullptr) {
         ALOGE("%s: Stream %d is not found.", __FUNCTION__, streamId);
         return BAD_VALUE;
     }
-
-    sp<Camera3OutputStreamInterface> stream = mOutputStreams.editValueAt(idx);
     return stream->dropBuffers(dropping);
 }
 
@@ -2298,15 +2622,12 @@
     }
 
     for (size_t i = 0; i < streams.count; i++) {
-        int idx = mOutputStreams.indexOfKey(streams.data.i32[i]);
-        if (idx == NAME_NOT_FOUND) {
+        sp<Camera3OutputStreamInterface> stream = mOutputStreams.get(streams.data.i32[i]);
+        if (stream == nullptr) {
             CLOGE("Request references unknown stream %d",
-                    streams.data.u8[i]);
+                    streams.data.i32[i]);
             return NULL;
         }
-        sp<Camera3OutputStreamInterface> stream =
-                mOutputStreams.editValueAt(idx);
-
         // It is illegal to include a deferred consumer output stream into a request
         auto iter = surfaceMap.find(streams.data.i32[i]);
         if (iter != surfaceMap.end()) {
@@ -2367,7 +2688,7 @@
     }
 
     for (size_t i = 0; i < mOutputStreams.size(); i++) {
-        sp<Camera3OutputStreamInterface> outputStream = mOutputStreams.editValueAt(i);
+        sp<Camera3OutputStreamInterface> outputStream = mOutputStreams[i];
         if (outputStream->isConfiguring()) {
             res = outputStream->cancelConfiguration();
             if (res != OK) {
@@ -2502,7 +2823,7 @@
         }
 
         camera3_stream_t *outputStream;
-        outputStream = mOutputStreams.editValueAt(i)->startConfiguration();
+        outputStream = mOutputStreams[i]->startConfiguration();
         if (outputStream == NULL) {
             CLOGE("Can't start output stream configuration");
             cancelStreamsConfigurationLocked();
@@ -2560,8 +2881,7 @@
     }
 
     for (size_t i = 0; i < mOutputStreams.size(); i++) {
-        sp<Camera3OutputStreamInterface> outputStream =
-            mOutputStreams.editValueAt(i);
+        sp<Camera3OutputStreamInterface> outputStream = mOutputStreams[i];
         if (outputStream->isConfiguring() && !outputStream->isConsumerConfigurationDeferred()) {
             res = outputStream->finishConfiguration();
             if (res != OK) {
@@ -2624,6 +2944,10 @@
         return rc;
     }
 
+    if (mDummyStreamId == NO_STREAM) {
+        mRequestBufferSM.onStreamsConfigured();
+    }
+
     return OK;
 }
 
@@ -2668,15 +2992,12 @@
     // Ok, have a dummy stream and there's at least one other output stream,
     // so remove the dummy
 
-    sp<Camera3StreamInterface> deletedStream;
-    ssize_t outputStreamIdx = mOutputStreams.indexOfKey(mDummyStreamId);
-    if (outputStreamIdx == NAME_NOT_FOUND) {
+    sp<Camera3StreamInterface> deletedStream = mOutputStreams.get(mDummyStreamId);
+    if (deletedStream == nullptr) {
         SET_ERR_L("Dummy stream %d does not appear to exist", mDummyStreamId);
         return INVALID_OPERATION;
     }
-
-    deletedStream = mOutputStreams.editValueAt(outputStreamIdx);
-    mOutputStreams.removeItemsAt(outputStreamIdx);
+    mOutputStreams.remove(mDummyStreamId);
 
     // Free up the stream endpoint so that it can be used by some other stream
     res = deletedStream->disconnect();
@@ -2751,13 +3072,14 @@
         int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
         bool hasAppCallback, nsecs_t maxExpectedDuration,
         std::set<String8>& physicalCameraIds, bool isStillCapture,
-        bool isZslCapture) {
+        bool isZslCapture, const SurfaceMap& outputSurfaces) {
     ATRACE_CALL();
     Mutex::Autolock l(mInFlightLock);
 
     ssize_t res;
     res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
-            hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture));
+            hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture,
+            outputSurfaces));
     if (res < 0) return res;
 
     if (mInFlightMap.size() == 1) {
@@ -2775,18 +3097,65 @@
 
 void Camera3Device::returnOutputBuffers(
         const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
-        nsecs_t timestamp, bool timestampIncreasing) {
+        nsecs_t timestamp, bool timestampIncreasing,
+        const SurfaceMap& outputSurfaces,
+        const CaptureResultExtras &inResultExtras) {
 
     for (size_t i = 0; i < numBuffers; i++)
     {
-        Camera3Stream *stream = Camera3Stream::cast(outputBuffers[i].stream);
-        status_t res = stream->returnBuffer(outputBuffers[i], timestamp, timestampIncreasing);
+        if (outputBuffers[i].buffer == nullptr) {
+            if (!mUseHalBufManager) {
+                // With HAL buffer management API, HAL sometimes will have to return buffers that
+                // has not got a output buffer handle filled yet. This is though illegal if HAL
+                // buffer management API is not being used.
+                ALOGE("%s: cannot return a null buffer!", __FUNCTION__);
+            }
+            continue;
+        }
+
+        Camera3StreamInterface *stream = Camera3Stream::cast(outputBuffers[i].stream);
+        int streamId = stream->getId();
+        const auto& it = outputSurfaces.find(streamId);
+        status_t res = OK;
+        if (it != outputSurfaces.end()) {
+            res = stream->returnBuffer(
+                    outputBuffers[i], timestamp, timestampIncreasing, it->second);
+        } else {
+            res = stream->returnBuffer(
+                    outputBuffers[i], timestamp, timestampIncreasing);
+        }
+
         // Note: stream may be deallocated at this point, if this buffer was
         // the last reference to it.
         if (res != OK) {
             ALOGE("Can't return buffer to its stream: %s (%d)",
                 strerror(-res), res);
         }
+
+        // Long processing consumers can cause returnBuffer timeout for shared stream
+        // If that happens, cancel the buffer and send a buffer error to client
+        if (it != outputSurfaces.end() && res == TIMED_OUT &&
+                outputBuffers[i].status == CAMERA3_BUFFER_STATUS_OK) {
+            // cancel the buffer
+            camera3_stream_buffer_t sb = outputBuffers[i];
+            sb.status = CAMERA3_BUFFER_STATUS_ERROR;
+            stream->returnBuffer(sb, /*timestamp*/0, timestampIncreasing);
+
+            // notify client buffer error
+            sp<NotificationListener> listener;
+            {
+                Mutex::Autolock l(mOutputLock);
+                listener = mListener.promote();
+            }
+
+            if (listener != nullptr) {
+                CaptureResultExtras extras = inResultExtras;
+                extras.errorStreamId = streamId;
+                listener->notifyError(
+                        hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER,
+                        extras);
+            }
+        }
     }
 }
 
@@ -2797,6 +3166,7 @@
 
     // Indicate idle inFlightMap to the status tracker
     if (mInFlightMap.size() == 0) {
+        mRequestBufferSM.onInflightMapEmpty();
         // Hold a separate dedicated tracker lock to prevent race with disconnect and also
         // avoid a deadlock during reprocess requests.
         Mutex::Autolock l(mTrackerLock);
@@ -2844,7 +3214,8 @@
         assert(request.requestStatus != OK ||
                request.pendingOutputBuffers.size() == 0);
         returnOutputBuffers(request.pendingOutputBuffers.array(),
-            request.pendingOutputBuffers.size(), 0);
+            request.pendingOutputBuffers.size(), 0, /*timestampIncreasing*/true,
+            request.outputSurfaces, request.resultExtras);
 
         removeInFlightMapEntryLocked(idx);
         ALOGVV("%s: removed frame %d from InFlightMap", __FUNCTION__, frameNumber);
@@ -2868,7 +3239,9 @@
         for (size_t idx = 0; idx < mInFlightMap.size(); idx++) {
             const InFlightRequest &request = mInFlightMap.valueAt(idx);
             returnOutputBuffers(request.pendingOutputBuffers.array(),
-                request.pendingOutputBuffers.size(), 0);
+                request.pendingOutputBuffers.size(), 0,
+                /*timestampIncreasing*/true, request.outputSurfaces,
+                request.resultExtras);
         }
         mInFlightMap.clear();
         mExpectedInflightDuration = 0;
@@ -2943,12 +3316,12 @@
                       frameNumber, streamId, strerror(-res), res);
             }
         } else {
-            ssize_t idx = mOutputStreams.indexOfKey(streamId);
-            if (idx == NAME_NOT_FOUND) {
+            sp<Camera3StreamInterface> stream = mOutputStreams.get(streamId);
+            if (stream == nullptr) {
                 ALOGE("%s: Output stream id %d not found!", __FUNCTION__, streamId);
                 continue;
             }
-            streamBuffer.stream = mOutputStreams.valueAt(idx)->asHalStream();
+            streamBuffer.stream = stream->asHalStream();
             returnOutputBuffers(&streamBuffer, /*size*/1, /*timestamp*/ 0);
         }
     }
@@ -2996,6 +3369,13 @@
     captureResult.mResultExtras = resultExtras;
     captureResult.mMetadata = partialResult;
 
+    // Fix up result metadata for monochrome camera.
+    status_t res = fixupMonochromeTags(mDeviceInfo, captureResult.mMetadata);
+    if (res != OK) {
+        SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+        return;
+    }
+
     insertResultLocked(&captureResult, frameNumber);
 }
 
@@ -3067,6 +3447,21 @@
                 frameNumber, strerror(res), res);
         return;
     }
+    // Fix up result metadata for monochrome camera.
+    res = fixupMonochromeTags(mDeviceInfo, captureResult.mMetadata);
+    if (res != OK) {
+        SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+        return;
+    }
+    for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
+        String8 cameraId8(physicalMetadata.mPhysicalCameraId);
+        res = fixupMonochromeTags(mPhysicalDeviceInfoMap.at(cameraId8.c_str()),
+                physicalMetadata.mPhysicalCameraMetadata);
+        if (res != OK) {
+            SET_ERR("Failed to override result metadata: %s (%d)", strerror(-res), res);
+            return;
+        }
+    }
 
     mTagMonitor.monitorMetadata(TagMonitor::RESULT,
             frameNumber, timestamp.data.i64[0], captureResult.mMetadata);
@@ -3226,7 +3621,8 @@
         } else {
             bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer);
             returnOutputBuffers(result->output_buffers,
-                result->num_output_buffers, shutterTimestamp, timestampIncreasing);
+                result->num_output_buffers, shutterTimestamp, timestampIncreasing,
+                request.outputSurfaces, request.resultExtras);
         }
 
         if (result->result != NULL && !isPartialResult) {
@@ -3239,7 +3635,7 @@
             if (shutterTimestamp == 0) {
                 request.pendingMetadata = result->result;
                 request.collectedPartialResult = collectedPartialResult;
-           } else if (request.hasCallback) {
+            } else if (request.hasCallback) {
                 CameraMetadata metadata;
                 metadata = result->result;
                 sendCaptureResult(metadata, request.resultExtras,
@@ -3359,6 +3755,9 @@
                         // In case of missing result check whether the buffers
                         // returned. If they returned, then remove inflight
                         // request.
+                        // TODO: should we call this for ERROR_CAMERA_REQUEST as well?
+                        //       otherwise we are depending on HAL to send the buffers back after
+                        //       calling notifyError. Not sure if that's in the spec.
                         removeInFlightRequestIfReadyLocked(idx);
                     }
                 } else {
@@ -3434,7 +3833,8 @@
             }
             bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer);
             returnOutputBuffers(r.pendingOutputBuffers.array(),
-                r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing);
+                    r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing,
+                    r.outputSurfaces, r.resultExtras);
             r.pendingOutputBuffers.clear();
 
             removeInFlightRequestIfReadyLocked(idx);
@@ -3470,11 +3870,17 @@
 
 Camera3Device::HalInterface::HalInterface(
             sp<ICameraDeviceSession> &session,
-            std::shared_ptr<RequestMetadataQueue> queue) :
+            std::shared_ptr<RequestMetadataQueue> queue,
+            bool useHalBufManager) :
         mHidlSession(session),
-        mRequestMetadataQueue(queue) {
+        mRequestMetadataQueue(queue),
+        mUseHalBufManager(useHalBufManager) {
     // Check with hardware service manager if we can downcast these interfaces
     // Somewhat expensive, so cache the results at startup
+    auto castResult_3_5 = device::V3_5::ICameraDeviceSession::castFrom(mHidlSession);
+    if (castResult_3_5.isOk()) {
+        mHidlSession_3_5 = castResult_3_5;
+    }
     auto castResult_3_4 = device::V3_4::ICameraDeviceSession::castFrom(mHidlSession);
     if (castResult_3_4.isOk()) {
         mHidlSession_3_4 = castResult_3_4;
@@ -3485,11 +3891,12 @@
     }
 }
 
-Camera3Device::HalInterface::HalInterface() {}
+Camera3Device::HalInterface::HalInterface() : mUseHalBufManager(false) {}
 
 Camera3Device::HalInterface::HalInterface(const HalInterface& other) :
         mHidlSession(other.mHidlSession),
-        mRequestMetadataQueue(other.mRequestMetadataQueue) {}
+        mRequestMetadataQueue(other.mRequestMetadataQueue),
+        mUseHalBufManager(other.mUseHalBufManager) {}
 
 bool Camera3Device::HalInterface::valid() {
     return (mHidlSession != nullptr);
@@ -3651,26 +4058,49 @@
 
     // Invoke configureStreams
     device::V3_3::HalStreamConfiguration finalConfiguration;
+    device::V3_4::HalStreamConfiguration finalConfiguration3_4;
     common::V1_0::Status status;
 
-    // See if we have v3.4 or v3.3 HAL
-    if (mHidlSession_3_4 != nullptr) {
-        // We do; use v3.4 for the call
-        ALOGV("%s: v3.4 device found", __FUNCTION__);
-        device::V3_4::HalStreamConfiguration finalConfiguration3_4;
-        auto err = mHidlSession_3_4->configureStreams_3_4(requestedConfiguration3_4,
-            [&status, &finalConfiguration3_4]
+    auto configStream34Cb = [&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;
+            };
+
+    auto postprocConfigStream34 = [&finalConfiguration, &finalConfiguration3_4]
+            (hardware::Return<void>& err) -> status_t {
+                if (!err.isOk()) {
+                    ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+                    return DEAD_OBJECT;
+                }
+                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;
+                }
+                return OK;
+            };
+
+    // See if we have v3.4 or v3.3 HAL
+    if (mHidlSession_3_5 != nullptr) {
+        ALOGV("%s: v3.5 device found", __FUNCTION__);
+        device::V3_5::StreamConfiguration requestedConfiguration3_5;
+        requestedConfiguration3_5.v3_4 = requestedConfiguration3_4;
+        requestedConfiguration3_5.streamConfigCounter = mNextStreamConfigCounter++;
+        auto err = mHidlSession_3_5->configureStreams_3_5(
+                requestedConfiguration3_5, configStream34Cb);
+        res = postprocConfigStream34(err);
+        if (res != OK) {
+            return res;
         }
-        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_4 != nullptr) {
+        // We do; use v3.4 for the call
+        ALOGV("%s: v3.4 device found", __FUNCTION__);
+        device::V3_4::HalStreamConfiguration finalConfiguration3_4;
+        auto err = mHidlSession_3_4->configureStreams_3_4(
+                requestedConfiguration3_4, configStream34Cb);
+        res = postprocConfigStream34(err);
+        if (res != OK) {
+            return res;
         }
     } else if (mHidlSession_3_3 != nullptr) {
         // We do; use v3.3 for the call
@@ -3782,14 +4212,14 @@
     return res;
 }
 
-void Camera3Device::HalInterface::wrapAsHidlRequest(camera3_capture_request_t* request,
+status_t Camera3Device::HalInterface::wrapAsHidlRequest(camera3_capture_request_t* request,
         /*out*/device::V3_2::CaptureRequest* captureRequest,
         /*out*/std::vector<native_handle_t*>* handlesCreated) {
     ATRACE_CALL();
     if (captureRequest == nullptr || handlesCreated == nullptr) {
         ALOGE("%s: captureRequest (%p) and handlesCreated (%p) must not be null",
                 __FUNCTION__, captureRequest, handlesCreated);
-        return;
+        return BAD_VALUE;
     }
 
     captureRequest->frameNumber = request->frame_number;
@@ -3830,26 +4260,37 @@
             const camera3_stream_buffer_t *src = request->output_buffers + i;
             StreamBuffer &dst = captureRequest->outputBuffers[i];
             int32_t streamId = Camera3Stream::cast(src->stream)->getId();
-            buffer_handle_t buf = *(src->buffer);
-            auto pair = getBufferId(buf, streamId);
-            bool isNewBuffer = pair.first;
-            dst.streamId = streamId;
-            dst.bufferId = pair.second;
-            dst.buffer = isNewBuffer ? buf : nullptr;
-            dst.status = BufferStatus::OK;
-            native_handle_t *acquireFence = nullptr;
-            if (src->acquire_fence != -1) {
-                acquireFence = native_handle_create(1,0);
-                acquireFence->data[0] = src->acquire_fence;
-                handlesCreated->push_back(acquireFence);
+            if (src->buffer != nullptr) {
+                buffer_handle_t buf = *(src->buffer);
+                auto pair = getBufferId(buf, streamId);
+                bool isNewBuffer = pair.first;
+                dst.bufferId = pair.second;
+                dst.buffer = isNewBuffer ? buf : nullptr;
+                native_handle_t *acquireFence = nullptr;
+                if (src->acquire_fence != -1) {
+                    acquireFence = native_handle_create(1,0);
+                    acquireFence->data[0] = src->acquire_fence;
+                    handlesCreated->push_back(acquireFence);
+                }
+                dst.acquireFence = acquireFence;
+            } else if (mUseHalBufManager) {
+                // HAL buffer management path
+                dst.bufferId = BUFFER_ID_NO_BUFFER;
+                dst.buffer = nullptr;
+                dst.acquireFence = nullptr;
+            } else {
+                ALOGE("%s: cannot send a null buffer in capture request!", __FUNCTION__);
+                return BAD_VALUE;
             }
-            dst.acquireFence = acquireFence;
+            dst.streamId = streamId;
+            dst.status = BufferStatus::OK;
             dst.releaseFence = nullptr;
 
             pushInflightBufferLocked(captureRequest->frameNumber, streamId,
                     src->buffer, src->acquire_fence);
         }
     }
+    return OK;
 }
 
 status_t Camera3Device::HalInterface::processBatchCaptureRequests(
@@ -3873,12 +4314,17 @@
     }
     std::vector<native_handle_t*> handlesCreated;
 
+    status_t res = OK;
     for (size_t i = 0; i < batchSize; i++) {
         if (hidlSession_3_4 != nullptr) {
-            wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_4[i].v3_2,
+            res = wrapAsHidlRequest(requests[i], /*out*/&captureRequests_3_4[i].v3_2,
                     /*out*/&handlesCreated);
         } else {
-            wrapAsHidlRequest(requests[i], /*out*/&captureRequests[i], /*out*/&handlesCreated);
+            res = wrapAsHidlRequest(requests[i],
+                    /*out*/&captureRequests[i], /*out*/&handlesCreated);
+        }
+        if (res != OK) {
+            return res;
         }
     }
 
@@ -4041,6 +4487,20 @@
     return res;
 }
 
+void Camera3Device::HalInterface::signalPipelineDrain(const std::vector<int>& streamIds) {
+    ATRACE_NAME("CameraHal::signalPipelineDrain");
+    if (!valid() || mHidlSession_3_5 == nullptr) {
+        ALOGE("%s called on invalid camera!", __FUNCTION__);
+        return;
+    }
+
+    auto err = mHidlSession_3_5->signalStreamFlush(streamIds, mNextStreamConfigCounter);
+    if (!err.isOk()) {
+        ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+        return;
+    }
+}
+
 void Camera3Device::HalInterface::getInflightBufferKeys(
         std::vector<std::pair<int32_t, int32_t>>* out) {
     std::lock_guard<std::mutex> lock(mInflightLock);
@@ -4081,6 +4541,33 @@
     return OK;
 }
 
+status_t Camera3Device::HalInterface::pushInflightRequestBuffer(
+        uint64_t bufferId, buffer_handle_t* buf) {
+    std::lock_guard<std::mutex> lock(mRequestedBuffersLock);
+    auto pair = mRequestedBuffers.insert({bufferId, buf});
+    if (!pair.second) {
+        ALOGE("%s: bufId %" PRIu64 " is already inflight!",
+                __FUNCTION__, bufferId);
+        return BAD_VALUE;
+    }
+    return OK;
+}
+
+// Find and pop a buffer_handle_t based on bufferId
+status_t Camera3Device::HalInterface::popInflightRequestBuffer(
+        uint64_t bufferId, /*out*/ buffer_handle_t **buffer) {
+    std::lock_guard<std::mutex> lock(mRequestedBuffersLock);
+    auto it = mRequestedBuffers.find(bufferId);
+    if (it == mRequestedBuffers.end()) {
+        ALOGE("%s: bufId %" PRIu64 " is not inflight!",
+                __FUNCTION__, bufferId);
+        return BAD_VALUE;
+    }
+    *buffer = it->second;
+    mRequestedBuffers.erase(it);
+    return OK;
+}
+
 std::pair<bool, uint64_t> Camera3Device::HalInterface::getBufferId(
         const buffer_handle_t& buf, int streamId) {
     std::lock_guard<std::mutex> lock(mBufferIdMapLock);
@@ -4129,7 +4616,8 @@
 
 Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
         sp<StatusTracker> statusTracker,
-        sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys) :
+        sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys,
+        bool useHalBufManager) :
         Thread(/*canCallJava*/false),
         mParent(parent),
         mStatusTracker(statusTracker),
@@ -4139,6 +4627,7 @@
         mReconfigured(false),
         mDoPause(false),
         mPaused(true),
+        mNotifyPipelineDrain(false),
         mFrameNumber(0),
         mLatestRequestId(NAME_NOT_FOUND),
         mCurrentAfTriggerId(0),
@@ -4149,7 +4638,8 @@
         mConstrainedMode(false),
         mRequestLatency(kRequestLatencyBinSize),
         mSessionParamKeys(sessionParamKeys),
-        mLatestSessionParams(sessionParamKeys.size()) {
+        mLatestSessionParams(sessionParamKeys.size()),
+        mUseHalBufManager(useHalBufManager) {
     mStatusId = statusTracker->addComponent();
 }
 
@@ -4764,6 +5254,13 @@
     nsecs_t tRequestEnd = systemTime(SYSTEM_TIME_MONOTONIC);
     mRequestLatency.add(tRequestStart, tRequestEnd);
 
+    if (submitRequestSuccess) {
+        sp<Camera3Device> parent = mParent.promote();
+        if (parent != nullptr) {
+            parent->mRequestBufferSM.onRequestSubmitted();
+        }
+    }
+
     if (useFlushLock) {
         mFlushLock.unlock();
     }
@@ -4908,8 +5405,11 @@
         }
         nsecs_t waitDuration = kBaseGetBufferWait + parent->getExpectedInFlightDuration();
 
+        SurfaceMap uniqueSurfaceIdMap;
         for (size_t j = 0; j < captureRequest->mOutputStreams.size(); j++) {
-            sp<Camera3OutputStreamInterface> outputStream = captureRequest->mOutputStreams.editItemAt(j);
+            sp<Camera3OutputStreamInterface> outputStream =
+                    captureRequest->mOutputStreams.editItemAt(j);
+            int streamId = outputStream->getId();
 
             // Prepare video buffers for high speed recording on the first video request.
             if (mPrepareVideoStream && outputStream->isVideoStream()) {
@@ -4928,16 +5428,45 @@
                 }
             }
 
-            res = outputStream->getBuffer(&outputBuffers->editItemAt(j),
-                    waitDuration,
-                    captureRequest->mOutputSurfaces[outputStream->getId()]);
-            if (res != OK) {
-                // Can't get output buffer from gralloc queue - this could be due to
-                // abandoned queue or other consumer misbehavior, so not a fatal
-                // error
-                ALOGE("RequestThread: Can't get output buffer, skipping request:"
-                        " %s (%d)", strerror(-res), res);
-                return TIMED_OUT;
+            std::vector<size_t> uniqueSurfaceIds;
+            res = outputStream->getUniqueSurfaceIds(
+                    captureRequest->mOutputSurfaces[streamId],
+                    &uniqueSurfaceIds);
+            // INVALID_OPERATION is normal output for streams not supporting surfaceIds
+            if (res != OK && res != INVALID_OPERATION) {
+                ALOGE("%s: failed to query stream %d unique surface IDs",
+                        __FUNCTION__, streamId);
+                return res;
+            }
+            if (res == OK) {
+                uniqueSurfaceIdMap.insert({streamId, std::move(uniqueSurfaceIds)});
+            }
+
+            if (mUseHalBufManager) {
+                if (outputStream->isAbandoned()) {
+                    ALOGE("%s: stream %d is abandoned.", __FUNCTION__, streamId);
+                    return TIMED_OUT;
+                }
+                // HAL will request buffer through requestStreamBuffer API
+                camera3_stream_buffer_t& buffer = outputBuffers->editItemAt(j);
+                buffer.stream = outputStream->asHalStream();
+                buffer.buffer = nullptr;
+                buffer.status = CAMERA3_BUFFER_STATUS_OK;
+                buffer.acquire_fence = -1;
+                buffer.release_fence = -1;
+            } else {
+                res = outputStream->getBuffer(&outputBuffers->editItemAt(j),
+                        waitDuration,
+                        captureRequest->mOutputSurfaces[streamId]);
+                if (res != OK) {
+                    // Can't get output buffer from gralloc queue - this could be due to
+                    // abandoned queue or other consumer misbehavior, so not a fatal
+                    // error
+                    ALOGE("RequestThread: Can't get output buffer, skipping request:"
+                            " %s (%d)", strerror(-res), res);
+
+                    return TIMED_OUT;
+                }
             }
 
             String8 physicalCameraId = outputStream->getPhysicalCameraId();
@@ -4982,7 +5511,9 @@
                 /*hasInput*/halRequest->input_buffer != NULL,
                 hasCallback,
                 calculateMaxExpectedDuration(halRequest->settings),
-                requestedPhysicalCameras, isStillCapture, isZslCapture);
+                requestedPhysicalCameras, isStillCapture, isZslCapture,
+                (mUseHalBufManager) ? uniqueSurfaceIdMap :
+                                      SurfaceMap{});
         ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
                ", burstId = %" PRId32 ".",
                 __FUNCTION__,
@@ -5058,7 +5589,7 @@
             if (s.first == streamId) {
                 const auto &it = std::find(s.second.begin(), s.second.end(), surfaceId);
                 if (it != s.second.end()) {
-                  return true;
+                    return true;
                 }
             }
         }
@@ -5069,7 +5600,7 @@
             if (s.first == streamId) {
                 const auto &it = std::find(s.second.begin(), s.second.end(), surfaceId);
                 if (it != s.second.end()) {
-                  return true;
+                    return true;
                 }
             }
         }
@@ -5078,6 +5609,22 @@
     return false;
 }
 
+void Camera3Device::RequestThread::signalPipelineDrain(const std::vector<int>& streamIds) {
+    if (!mUseHalBufManager) {
+        ALOGE("%s called for camera device not supporting HAL buffer management", __FUNCTION__);
+        return;
+    }
+
+    Mutex::Autolock pl(mPauseLock);
+    if (mPaused) {
+        mInterface->signalPipelineDrain(streamIds);
+        return;
+    }
+    // If request thread is still busy, wait until paused then notify HAL
+    mNotifyPipelineDrain = true;
+    mStreamIdsToBeDrained = streamIds;
+}
+
 nsecs_t Camera3Device::getExpectedInFlightDuration() {
     ATRACE_CALL();
     Mutex::Autolock al(mInFlightLock);
@@ -5255,6 +5802,15 @@
                 if (statusTracker != 0) {
                     statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
                 }
+                if (mNotifyPipelineDrain) {
+                    mInterface->signalPipelineDrain(mStreamIdsToBeDrained);
+                    mNotifyPipelineDrain = false;
+                    mStreamIdsToBeDrained.clear();
+                }
+                sp<Camera3Device> parent = mParent.promote();
+                if (parent != nullptr) {
+                    parent->mRequestBufferSM.onRequestThreadPaused();
+                }
             }
             // Stop waiting for now and let thread management happen
             return NULL;
@@ -5339,6 +5895,15 @@
             if (statusTracker != 0) {
                 statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
             }
+            if (mNotifyPipelineDrain) {
+                mInterface->signalPipelineDrain(mStreamIdsToBeDrained);
+                mNotifyPipelineDrain = false;
+                mStreamIdsToBeDrained.clear();
+            }
+            sp<Camera3Device> parent = mParent.promote();
+            if (parent != nullptr) {
+                parent->mRequestBufferSM.onRequestThreadPaused();
+            }
         }
 
         res = mDoPauseSignal.waitRelative(mPauseLock, kRequestTimeout);
@@ -5794,23 +6359,172 @@
     return true;
 }
 
-/**
- * Static callback forwarding methods from HAL to instance
- */
+status_t Camera3Device::RequestBufferStateMachine::initialize(
+        sp<camera3::StatusTracker> statusTracker) {
+    if (statusTracker == nullptr) {
+        ALOGE("%s: statusTracker is null", __FUNCTION__);
+        return BAD_VALUE;
+    }
 
-void Camera3Device::sProcessCaptureResult(const camera3_callback_ops *cb,
-        const camera3_capture_result *result) {
-    Camera3Device *d =
-            const_cast<Camera3Device*>(static_cast<const Camera3Device*>(cb));
-
-    d->processCaptureResult(result);
+    std::lock_guard<std::mutex> lock(mLock);
+    mStatusTracker = statusTracker;
+    mRequestBufferStatusId = statusTracker->addComponent();
+    return OK;
 }
 
-void Camera3Device::sNotify(const camera3_callback_ops *cb,
-        const camera3_notify_msg *msg) {
-    Camera3Device *d =
-            const_cast<Camera3Device*>(static_cast<const Camera3Device*>(cb));
-    d->notify(msg);
+bool Camera3Device::RequestBufferStateMachine::startRequestBuffer() {
+    std::lock_guard<std::mutex> lock(mLock);
+    if (mStatus == RB_STATUS_READY || mStatus == RB_STATUS_PENDING_STOP) {
+        mRequestBufferOngoing = true;
+        notifyTrackerLocked(/*active*/true);
+        return true;
+    }
+    return false;
+}
+
+void Camera3Device::RequestBufferStateMachine::endRequestBuffer() {
+    std::lock_guard<std::mutex> lock(mLock);
+    if (!mRequestBufferOngoing) {
+        ALOGE("%s called without a successful startRequestBuffer call first!", __FUNCTION__);
+        return;
+    }
+    mRequestBufferOngoing = false;
+    if (mStatus == RB_STATUS_PENDING_STOP) {
+        checkSwitchToStopLocked();
+    }
+    notifyTrackerLocked(/*active*/false);
+}
+
+void Camera3Device::RequestBufferStateMachine::onStreamsConfigured() {
+    std::lock_guard<std::mutex> lock(mLock);
+    mStatus = RB_STATUS_READY;
+    return;
+}
+
+void Camera3Device::RequestBufferStateMachine::onRequestSubmitted() {
+    std::lock_guard<std::mutex> lock(mLock);
+    mRequestThreadPaused = false;
+    mInflightMapEmpty = false;
+    if (mStatus == RB_STATUS_STOPPED) {
+        mStatus = RB_STATUS_READY;
+    }
+    return;
+}
+
+void Camera3Device::RequestBufferStateMachine::onRequestThreadPaused() {
+    std::lock_guard<std::mutex> lock(mLock);
+    mRequestThreadPaused = true;
+    if (mStatus == RB_STATUS_PENDING_STOP) {
+        checkSwitchToStopLocked();
+    }
+    return;
+}
+
+void Camera3Device::RequestBufferStateMachine::onInflightMapEmpty() {
+    std::lock_guard<std::mutex> lock(mLock);
+    mInflightMapEmpty = true;
+    if (mStatus == RB_STATUS_PENDING_STOP) {
+        checkSwitchToStopLocked();
+    }
+    return;
+}
+
+void Camera3Device::RequestBufferStateMachine::onWaitUntilIdle() {
+    std::lock_guard<std::mutex> lock(mLock);
+    if (!checkSwitchToStopLocked()) {
+        mStatus = RB_STATUS_PENDING_STOP;
+    }
+    return;
+}
+
+void Camera3Device::RequestBufferStateMachine::notifyTrackerLocked(bool active) {
+    sp<StatusTracker> statusTracker = mStatusTracker.promote();
+    if (statusTracker != nullptr) {
+        if (active) {
+            statusTracker->markComponentActive(mRequestBufferStatusId);
+        } else {
+            statusTracker->markComponentIdle(mRequestBufferStatusId, Fence::NO_FENCE);
+        }
+    }
+}
+
+bool Camera3Device::RequestBufferStateMachine::checkSwitchToStopLocked() {
+    if (mInflightMapEmpty && mRequestThreadPaused && !mRequestBufferOngoing) {
+        mStatus = RB_STATUS_STOPPED;
+        return true;
+    }
+    return false;
+}
+
+status_t Camera3Device::fixupMonochromeTags(const CameraMetadata& deviceInfo,
+        CameraMetadata& resultMetadata) {
+    status_t res = OK;
+    if (!mNeedFixupMonochromeTags) {
+        return res;
+    }
+
+    // Remove tags that are not applicable to monochrome camera.
+    int32_t tagsToRemove[] = {
+           ANDROID_SENSOR_GREEN_SPLIT,
+           ANDROID_SENSOR_NEUTRAL_COLOR_POINT,
+           ANDROID_COLOR_CORRECTION_MODE,
+           ANDROID_COLOR_CORRECTION_TRANSFORM,
+           ANDROID_COLOR_CORRECTION_GAINS,
+    };
+    for (auto tag : tagsToRemove) {
+        res = resultMetadata.erase(tag);
+        if (res != OK) {
+            ALOGE("%s: Failed to remove tag %d for monochrome camera", __FUNCTION__, tag);
+            return res;
+        }
+    }
+
+    // ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL
+    camera_metadata_entry blEntry = resultMetadata.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
+    for (size_t i = 1; i < blEntry.count; i++) {
+        blEntry.data.f[i] = blEntry.data.f[0];
+    }
+
+    // ANDROID_SENSOR_NOISE_PROFILE
+    camera_metadata_entry npEntry = resultMetadata.find(ANDROID_SENSOR_NOISE_PROFILE);
+    if (npEntry.count > 0 && npEntry.count % 2 == 0) {
+        double np[] = {npEntry.data.d[0], npEntry.data.d[1]};
+        res = resultMetadata.update(ANDROID_SENSOR_NOISE_PROFILE, np, 2);
+        if (res != OK) {
+             ALOGE("%s: Failed to update SENSOR_NOISE_PROFILE: %s (%d)",
+                    __FUNCTION__, strerror(-res), res);
+            return res;
+        }
+    }
+
+    // ANDROID_STATISTICS_LENS_SHADING_MAP
+    camera_metadata_ro_entry lsSizeEntry = deviceInfo.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
+    camera_metadata_entry lsEntry = resultMetadata.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
+    if (lsSizeEntry.count == 2 && lsEntry.count > 0
+            && (int32_t)lsEntry.count == 4 * lsSizeEntry.data.i32[0] * lsSizeEntry.data.i32[1]) {
+        for (int32_t i = 0; i < lsSizeEntry.data.i32[0] * lsSizeEntry.data.i32[1]; i++) {
+            lsEntry.data.f[4*i+1] = lsEntry.data.f[4*i];
+            lsEntry.data.f[4*i+2] = lsEntry.data.f[4*i];
+            lsEntry.data.f[4*i+3] = lsEntry.data.f[4*i];
+        }
+    }
+
+    // ANDROID_TONEMAP_CURVE_BLUE
+    // ANDROID_TONEMAP_CURVE_GREEN
+    // ANDROID_TONEMAP_CURVE_RED
+    camera_metadata_entry tcbEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_BLUE);
+    camera_metadata_entry tcgEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_GREEN);
+    camera_metadata_entry tcrEntry = resultMetadata.find(ANDROID_TONEMAP_CURVE_RED);
+    if (tcbEntry.count > 0
+            && tcbEntry.count == tcgEntry.count
+            && tcbEntry.count == tcrEntry.count) {
+        for (size_t i = 0; i < tcbEntry.count; i++) {
+            tcbEntry.data.f[i] = tcrEntry.data.f[i];
+            tcgEntry.data.f[i] = tcrEntry.data.f[i];
+        }
+    }
+
+    return res;
 }
 
 }; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 5e749b6..419ac42 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -33,10 +33,11 @@
 #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.5/ICameraDeviceSession.h>
 #include <android/hardware/camera/device/3.2/ICameraDeviceCallback.h>
 #include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h>
+#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
 #include <fmq/MessageQueue.h>
-#include <hardware/camera3.h>
 
 #include <camera/CaptureResult.h>
 
@@ -50,20 +51,6 @@
 
 using android::camera3::OutputStreamInfo;
 
-/**
- * Function pointer types with C calling convention to
- * use for HAL callback functions.
- */
-extern "C" {
-    typedef void (callbacks_process_capture_result_t)(
-        const struct camera3_callback_ops *,
-        const camera3_capture_result_t *);
-
-    typedef void (callbacks_notify_t)(
-        const struct camera3_callback_ops *,
-        const camera3_notify_msg_t *);
-}
-
 namespace android {
 
 namespace camera3 {
@@ -80,8 +67,7 @@
  */
 class Camera3Device :
             public CameraDeviceBase,
-            virtual public hardware::camera::device::V3_4::ICameraDeviceCallback,
-            private camera3_callback_ops {
+            virtual public hardware::camera::device::V3_5::ICameraDeviceCallback {
   public:
 
     explicit Camera3Device(const String8& id);
@@ -270,7 +256,8 @@
     class HalInterface : public camera3::Camera3StreamBufferFreedListener {
       public:
         HalInterface(sp<hardware::camera::device::V3_2::ICameraDeviceSession> &session,
-                     std::shared_ptr<RequestMetadataQueue> queue);
+                     std::shared_ptr<RequestMetadataQueue> queue,
+                     bool useHalBufManager);
         HalInterface(const HalInterface &other);
         HalInterface();
 
@@ -299,14 +286,27 @@
         status_t dump(int fd);
         status_t close();
 
+        void signalPipelineDrain(const std::vector<int>& streamIds);
+
+        // method to extract buffer's unique ID
+        // return pair of (newlySeenBuffer?, bufferId)
+        std::pair<bool, uint64_t> getBufferId(const buffer_handle_t& buf, int streamId);
+
         // Find a buffer_handle_t based on frame number and stream ID
         status_t popInflightBuffer(int32_t frameNumber, int32_t streamId,
                 /*out*/ buffer_handle_t **buffer);
 
+        // Register a bufId/buffer_handle_t to inflight request buffer
+        status_t pushInflightRequestBuffer(uint64_t bufferId, buffer_handle_t* buf);
+
+        // Find a buffer_handle_t based on bufferId
+        status_t popInflightRequestBuffer(uint64_t bufferId, /*out*/ buffer_handle_t **buffer);
+
         // Get a vector of (frameNumber, streamId) pair of currently inflight
         // buffers
         void getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out);
 
+        static const uint64_t BUFFER_ID_NO_BUFFER = 0;
       private:
         // Always valid
         sp<hardware::camera::device::V3_2::ICameraDeviceSession> mHidlSession;
@@ -314,6 +314,8 @@
         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;
+        // Valid if ICameraDeviceSession is @3.5 or newer
+        sp<hardware::camera::device::V3_5::ICameraDeviceSession> mHidlSession_3_5;
 
         std::shared_ptr<RequestMetadataQueue> mRequestMetadataQueue;
 
@@ -321,7 +323,7 @@
 
         // The output HIDL request still depends on input camera3_capture_request_t
         // Do not free input camera3_capture_request_t before output HIDL request
-        void wrapAsHidlRequest(camera3_capture_request_t* in,
+        status_t wrapAsHidlRequest(camera3_capture_request_t* in,
                 /*out*/hardware::camera::device::V3_2::CaptureRequest* out,
                 /*out*/std::vector<native_handle_t*>* handlesCreated);
 
@@ -365,19 +367,18 @@
         // stream ID -> per stream buffer ID map
         std::unordered_map<int, BufferIdMap> mBufferIdMaps;
         uint64_t mNextBufferId = 1; // 0 means no buffer
-        static const uint64_t BUFFER_ID_NO_BUFFER = 0;
-
-        // method to extract buffer's unique ID
-        // TODO: we should switch to use gralloc mapper's getBackingStore API
-        //       once we ran in binderized gralloc mode, but before that is ready,
-        //       we need to rely on the conventional buffer queue behavior where
-        //       buffer_handle_t's FD won't change.
-        // return pair of (newlySeenBuffer?, bufferId)
-        std::pair<bool, uint64_t> getBufferId(const buffer_handle_t& buf, int streamId);
 
         virtual void onBufferFreed(int streamId, const native_handle_t* handle) override;
 
         std::vector<std::pair<int, uint64_t>> mFreedBuffers;
+
+        // Buffers given to HAL through requestStreamBuffer API
+        std::mutex mRequestedBuffersLock;
+        std::unordered_map<uint64_t, buffer_handle_t*> mRequestedBuffers;
+
+        uint32_t mNextStreamConfigCounter = 1;
+
+        const bool mUseHalBufManager;
     };
 
     sp<HalInterface> mInterface;
@@ -412,9 +413,22 @@
     // Tracking cause of fatal errors when in STATUS_ERROR
     String8                    mErrorCause;
 
-    // Mapping of stream IDs to stream instances
-    typedef KeyedVector<int, sp<camera3::Camera3OutputStreamInterface> >
-            StreamSet;
+    // Synchronized mapping of stream IDs to stream instances
+    class StreamSet {
+      public:
+        status_t add(int streamId, sp<camera3::Camera3OutputStreamInterface>);
+        ssize_t remove(int streamId);
+        sp<camera3::Camera3OutputStreamInterface> get(int streamId);
+        // get by (underlying) vector index
+        sp<camera3::Camera3OutputStreamInterface> operator[] (size_t index);
+        size_t size() const;
+        std::vector<int> getStreamIds();
+        void clear();
+
+      private:
+        mutable std::mutex mLock;
+        KeyedVector<int, sp<camera3::Camera3OutputStreamInterface>> mData;
+    };
 
     StreamSet                  mOutputStreams;
     sp<camera3::Camera3Stream> mInputStream;
@@ -483,8 +497,9 @@
 
 
     /**
-     * Implementation of android::hardware::camera::device::V3_4::ICameraDeviceCallback
+     * Implementation of android::hardware::camera::device::V3_5::ICameraDeviceCallback
      */
+
     hardware::Return<void> processCaptureResult_3_4(
             const hardware::hidl_vec<
                     hardware::camera::device::V3_4::CaptureResult>& results) override;
@@ -495,6 +510,15 @@
             const hardware::hidl_vec<
                     hardware::camera::device::V3_2::NotifyMsg>& msgs) override;
 
+    hardware::Return<void> requestStreamBuffers(
+            const hardware::hidl_vec<
+                    hardware::camera::device::V3_5::BufferRequest>& bufReqs,
+            requestStreamBuffers_cb _hidl_cb) override;
+
+    hardware::Return<void> returnStreamBuffers(
+            const hardware::hidl_vec<
+                    hardware::camera::device::V3_2::StreamBuffer>& buffers) override;
+
     // Handle one capture result. Assume that mProcessCaptureResultLock is held.
     void processOneCaptureResultLocked(
             const hardware::camera::device::V3_2::CaptureResult& result,
@@ -702,7 +726,9 @@
 
         RequestThread(wp<Camera3Device> parent,
                 sp<camera3::StatusTracker> statusTracker,
-                sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys);
+                sp<HalInterface> interface,
+                const Vector<int32_t>& sessionParamKeys,
+                bool useHalBufManager);
         ~RequestThread();
 
         void     setNotificationListener(wp<NotificationListener> listener);
@@ -790,6 +816,8 @@
             mRequestLatency.dump(fd, name);
         }
 
+        void signalPipelineDrain(const std::vector<int>& streamIds);
+
       protected:
 
         virtual bool threadLoop();
@@ -899,12 +927,13 @@
 
         bool               mReconfigured;
 
-        // Used by waitIfPaused, waitForNextRequest, and waitUntilPaused
+        // Used by waitIfPaused, waitForNextRequest, waitUntilPaused, and signalPipelineDrain
         Mutex              mPauseLock;
         bool               mDoPause;
         Condition          mDoPauseSignal;
         bool               mPaused;
-        Condition          mPausedSignal;
+        bool               mNotifyPipelineDrain;
+        std::vector<int>   mStreamIdsToBeDrained;
 
         sp<CaptureRequest> mPrevRequest;
         int32_t            mPrevTriggers;
@@ -937,6 +966,8 @@
 
         Vector<int32_t>    mSessionParamKeys;
         CameraMetadata     mLatestSessionParams;
+
+        const bool         mUseHalBufManager;
     };
     sp<RequestThread> mRequestThread;
 
@@ -1003,6 +1034,9 @@
         // Indicates a ZSL capture request
         bool zslCapture;
 
+        // What shared surfaces an output should go to
+        SurfaceMap outputSurfaces;
+
         // Default constructor needed by KeyedVector
         InFlightRequest() :
                 shutterTimestamp(0),
@@ -1020,8 +1054,9 @@
 
         InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
                 bool hasAppCallback, nsecs_t maxDuration,
-                const std::set<String8>& physicalCameraIdSet, bool isStillCapture, 
-                bool isZslCapture) :
+                const std::set<String8>& physicalCameraIdSet, bool isStillCapture,
+                bool isZslCapture,
+                const SurfaceMap& outSurfaces = SurfaceMap{}) :
                 shutterTimestamp(0),
                 sensorTimestamp(0),
                 requestStatus(OK),
@@ -1034,7 +1069,8 @@
                 skipResultMetadata(false),
                 physicalCameraIds(physicalCameraIdSet),
                 stillCapture(isStillCapture),
-                zslCapture(isZslCapture) {
+                zslCapture(isZslCapture),
+                outputSurfaces(outSurfaces) {
         }
     };
 
@@ -1051,7 +1087,8 @@
     status_t registerInFlight(uint32_t frameNumber,
             int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
             bool callback, nsecs_t maxExpectedDuration, std::set<String8>& physicalCameraIds,
-            bool isStillCapture, bool isZslCapture);
+            bool isStillCapture, bool isZslCapture,
+            const SurfaceMap& outputSurfaces);
 
     /**
      * Returns the maximum expected time it'll take for all currently in-flight
@@ -1161,7 +1198,11 @@
 
     // helper function to return the output buffers to the streams.
     void returnOutputBuffers(const camera3_stream_buffer_t *outputBuffers,
-            size_t numBuffers, nsecs_t timestamp, bool timestampIncreasing = true);
+            size_t numBuffers, nsecs_t timestamp, bool timestampIncreasing = true,
+            // The following arguments are only meant for surface sharing use case
+            const SurfaceMap& outputSurfaces = SurfaceMap{},
+            // Used to send buffer error callback when failing to return buffer
+            const CaptureResultExtras &resultExtras = CaptureResultExtras{});
 
     // Send a partial capture result.
     void sendPartialCaptureResult(const camera_metadata_t * partialResult,
@@ -1217,16 +1258,88 @@
     // Cached last requested template id
     int mLastTemplateId;
 
-    /**
-     * Static callback forwarding methods from HAL to instance
-     */
-    static callbacks_process_capture_result_t sProcessCaptureResult;
-
-    static callbacks_notify_t sNotify;
-
     // Synchronizes access to status tracker between inflight updates and disconnect.
     // b/79972865
     Mutex mTrackerLock;
+
+    // Whether HAL request buffers through requestStreamBuffers API
+    bool mUseHalBufManager = false;
+
+    // Lock to ensure requestStreamBuffers() callbacks are serialized
+    std::mutex mRequestBufferInterfaceLock;
+
+    // The state machine to control when requestStreamBuffers should allow
+    // HAL to request buffers.
+    enum RequestBufferState {
+        /**
+         * This is the initial state.
+         * requestStreamBuffers call will return FAILED_CONFIGURING in this state.
+         * Will switch to RB_STATUS_READY after a successful configureStreams or
+         * processCaptureRequest call.
+         */
+        RB_STATUS_STOPPED,
+
+        /**
+         * requestStreamBuffers call will proceed in this state.
+         * When device is asked to stay idle via waitUntilStateThenRelock() call:
+         *     - Switch to RB_STATUS_STOPPED if there is no inflight requests and
+         *       request thread is paused.
+         *     - Switch to RB_STATUS_PENDING_STOP otherwise
+         */
+        RB_STATUS_READY,
+
+        /**
+         * requestStreamBuffers call will proceed in this state.
+         * Switch to RB_STATUS_STOPPED when all inflight requests are fulfilled
+         * and request thread is paused
+         */
+        RB_STATUS_PENDING_STOP,
+    };
+
+    class RequestBufferStateMachine {
+      public:
+        status_t initialize(sp<camera3::StatusTracker> statusTracker);
+
+        // Return if the state machine currently allows for requestBuffers
+        // If the state allows for it, mRequestBufferOngoing will be set to true
+        // and caller must call endRequestBuffer() later to unset the flag
+        bool startRequestBuffer();
+        void endRequestBuffer();
+
+        // Events triggered by application API call
+        void onStreamsConfigured();
+        void onWaitUntilIdle();
+
+        // Events usually triggered by hwBinder processCaptureResult callback thread
+        // But can also be triggered on request thread for failed request, or on
+        // hwbinder notify callback thread for shutter/error callbacks
+        void onInflightMapEmpty();
+
+        // Events triggered by RequestThread
+        void onRequestSubmitted();
+        void onRequestThreadPaused();
+
+      private:
+        void notifyTrackerLocked(bool active);
+
+        // Switch to STOPPED state and return true if all conditions allows for it.
+        // Otherwise do nothing and return false.
+        bool checkSwitchToStopLocked();
+
+        std::mutex mLock;
+        RequestBufferState mStatus = RB_STATUS_STOPPED;
+
+        bool mRequestThreadPaused = true;
+        bool mInflightMapEmpty = true;
+        bool mRequestBufferOngoing = false;
+
+        wp<camera3::StatusTracker> mStatusTracker;
+        int  mRequestBufferStatusId;
+    } mRequestBufferSM;
+
+    // Fix up result metadata for monochrome camera.
+    bool mNeedFixupMonochromeTags;
+    status_t fixupMonochromeTags(const CameraMetadata& deviceInfo, CameraMetadata& resultMetadata);
 }; // class Camera3Device
 
 }; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index fb1ff77..b637160 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -48,7 +48,7 @@
 
 status_t Camera3DummyStream::returnBufferLocked(
         const camera3_stream_buffer &,
-        nsecs_t) {
+        nsecs_t, const std::vector<size_t>&) {
     ATRACE_CALL();
     ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", __FUNCTION__, mId);
     return INVALID_OPERATION;
@@ -58,6 +58,7 @@
             const camera3_stream_buffer &,
             nsecs_t,
             bool,
+            const std::vector<size_t>&,
             /*out*/
             sp<Fence>*) {
     ATRACE_CALL();
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index 4627548..4b67ea5 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -87,6 +87,9 @@
      */
     virtual ssize_t getSurfaceId(const sp<Surface> &/*surface*/) { return 0; }
 
+    virtual status_t getUniqueSurfaceIds(const std::vector<size_t>&,
+            /*out*/std::vector<size_t>*) { return INVALID_OPERATION; };
+
     /**
      * Update the stream output surfaces.
      */
@@ -104,6 +107,7 @@
             const camera3_stream_buffer &buffer,
             nsecs_t timestamp,
             bool output,
+            const std::vector<size_t>& surface_ids,
             /*out*/
             sp<Fence> *releaseFenceOut);
 
@@ -128,7 +132,7 @@
             const std::vector<size_t>& surface_ids = std::vector<size_t>());
     virtual status_t returnBufferLocked(
             const camera3_stream_buffer &buffer,
-            nsecs_t timestamp);
+            nsecs_t timestamp, const std::vector<size_t>& surface_ids);
 
     virtual status_t configureQueueLocked();
 
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 3c1e43d..2e909a0 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -116,7 +116,7 @@
     return mTotalBufferCount;
 }
 
-size_t Camera3IOStreamBase::getHandoutOutputBufferCountLocked() {
+size_t Camera3IOStreamBase::getHandoutOutputBufferCountLocked() const {
     return mHandoutOutputBufferCount;
 }
 
@@ -219,7 +219,8 @@
 status_t Camera3IOStreamBase::returnAnyBufferLocked(
         const camera3_stream_buffer &buffer,
         nsecs_t timestamp,
-        bool output) {
+        bool output,
+        const std::vector<size_t>& surface_ids) {
     status_t res;
 
     // returnBuffer may be called from a raw pointer, not a sp<>, and we'll be
@@ -235,7 +236,7 @@
     }
 
     sp<Fence> releaseFence;
-    res = returnBufferCheckedLocked(buffer, timestamp, output,
+    res = returnBufferCheckedLocked(buffer, timestamp, output, surface_ids,
                                     &releaseFence);
     // Res may be an error, but we still want to decrement our owned count
     // to enable clean shutdown. So we'll just return the error but otherwise
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 0a31d44..750f64d 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -66,12 +66,14 @@
     status_t         returnAnyBufferLocked(
             const camera3_stream_buffer &buffer,
             nsecs_t timestamp,
-            bool output);
+            bool output,
+            const std::vector<size_t>& surface_ids = std::vector<size_t>());
 
     virtual status_t returnBufferCheckedLocked(
             const camera3_stream_buffer &buffer,
             nsecs_t timestamp,
             bool output,
+            const std::vector<size_t>& surface_ids,
             /*out*/
             sp<Fence> *releaseFenceOut) = 0;
 
@@ -82,7 +84,7 @@
 
     virtual size_t   getBufferCountLocked();
 
-    virtual size_t   getHandoutOutputBufferCountLocked();
+    virtual size_t   getHandoutOutputBufferCountLocked() const;
 
     virtual size_t   getHandoutInputBufferCountLocked();
 
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 017d7be..fc83684 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -98,6 +98,7 @@
             const camera3_stream_buffer &buffer,
             nsecs_t timestamp,
             bool output,
+            const std::vector<size_t>&,
             /*out*/
             sp<Fence> *releaseFenceOut) {
 
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index 0732464..97a627a 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -62,6 +62,7 @@
             const camera3_stream_buffer &buffer,
             nsecs_t timestamp,
             bool output,
+            const std::vector<size_t>& surface_ids,
             /*out*/
             sp<Fence> *releaseFenceOut);
 
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index ecbcf76..219cc24 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -187,16 +187,17 @@
 }
 
 status_t Camera3OutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
-            ANativeWindowBuffer* buffer, int anwReleaseFence) {
+            ANativeWindowBuffer* buffer, int anwReleaseFence,
+            const std::vector<size_t>&) {
     return consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
 }
 
 status_t Camera3OutputStream::returnBufferLocked(
         const camera3_stream_buffer &buffer,
-        nsecs_t timestamp) {
+        nsecs_t timestamp, const std::vector<size_t>& surface_ids) {
     ATRACE_CALL();
 
-    status_t res = returnAnyBufferLocked(buffer, timestamp, /*output*/true);
+    status_t res = returnAnyBufferLocked(buffer, timestamp, /*output*/true, surface_ids);
 
     if (res != OK) {
         return res;
@@ -212,6 +213,7 @@
             const camera3_stream_buffer &buffer,
             nsecs_t timestamp,
             bool output,
+            const std::vector<size_t>& surface_ids,
             /*out*/
             sp<Fence> *releaseFenceOut) {
 
@@ -281,7 +283,7 @@
             return res;
         }
 
-        res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence);
+        res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence, surface_ids);
         if (res != OK) {
             ALOGE("%s: Stream %d: Error queueing buffer to native window: "
                   "%s (%d)", __FUNCTION__, mId, strerror(-res), res);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 6f36f92..410905d 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -190,6 +190,9 @@
      */
     virtual ssize_t getSurfaceId(const sp<Surface> &/*surface*/) { return 0; }
 
+    virtual status_t getUniqueSurfaceIds(const std::vector<size_t>&,
+            /*out*/std::vector<size_t>*) { return INVALID_OPERATION; };
+
     /**
      * Update the stream output surfaces.
      */
@@ -213,6 +216,7 @@
             const camera3_stream_buffer &buffer,
             nsecs_t timestamp,
             bool output,
+            const std::vector<size_t>& surface_ids,
             /*out*/
             sp<Fence> *releaseFenceOut);
 
@@ -285,10 +289,11 @@
 
     virtual status_t returnBufferLocked(
             const camera3_stream_buffer &buffer,
-            nsecs_t timestamp);
+            nsecs_t timestamp, const std::vector<size_t>& surface_ids);
 
     virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
-            ANativeWindowBuffer* buffer, int anwReleaseFence);
+            ANativeWindowBuffer* buffer, int anwReleaseFence,
+            const std::vector<size_t>& surface_ids);
 
     virtual status_t configureQueueLocked();
 
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index a711a6d..2bde949 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -67,6 +67,18 @@
     virtual ssize_t getSurfaceId(const sp<Surface> &surface) = 0;
 
     /**
+     * Query the unique surface IDs of current surfaceIds.
+     * When passing unique surface IDs in returnBuffer(), if the
+     * surfaceId has been removed from the stream, the output corresponding to
+     * the unique surface ID will be ignored and not delivered to client.
+     *
+     * Return INVALID_OPERATION if and only if the stream does not support
+     * surface sharing.
+     */
+    virtual status_t getUniqueSurfaceIds(const std::vector<size_t>& surfaceIds,
+            /*out*/std::vector<size_t>* outUniqueIds) = 0;
+
+    /**
      * Update the stream output surfaces.
      */
     virtual status_t updateStream(const std::vector<sp<Surface>> &outputSurfaces,
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index fb3ce4c..e3b74d7 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "Camera3-SharedOuStrm"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
 #include "Camera3SharedOutputStream.h"
 
 namespace android {
@@ -28,16 +32,17 @@
         uint64_t consumerUsage, android_dataspace dataSpace,
         camera3_stream_rotation_t rotation,
         nsecs_t timestampOffset, const String8& physicalCameraId,
-        int setId) :
+        int setId, bool useHalBufManager) :
         Camera3OutputStream(id, CAMERA3_STREAM_OUTPUT, width, height,
                             format, dataSpace, rotation, physicalCameraId,
-                            consumerUsage, timestampOffset, setId) {
+                            consumerUsage, timestampOffset, setId),
+        mUseHalBufManager(useHalBufManager) {
     size_t consumerCount = std::min(surfaces.size(), kMaxOutputs);
     if (surfaces.size() > consumerCount) {
         ALOGE("%s: Trying to add more consumers than the maximum ", __func__);
     }
     for (size_t i = 0; i < consumerCount; i++) {
-        mSurfaces[i] = surfaces[i];
+        mSurfaceUniqueIds[i] = std::make_pair(surfaces[i], mNextUniqueSurfaceId++);
     }
 }
 
@@ -48,15 +53,15 @@
 status_t Camera3SharedOutputStream::connectStreamSplitterLocked() {
     status_t res = OK;
 
-    mStreamSplitter = new Camera3StreamSplitter();
+    mStreamSplitter = new Camera3StreamSplitter(mUseHalBufManager);
 
     uint64_t usage;
     getEndpointUsage(&usage);
 
     std::unordered_map<size_t, sp<Surface>> initialSurfaces;
     for (size_t i = 0; i < kMaxOutputs; i++) {
-        if (mSurfaces[i] != nullptr) {
-            initialSurfaces.emplace(i, mSurfaces[i]);
+        if (mSurfaceUniqueIds[i].first != nullptr) {
+            initialSurfaces.emplace(i, mSurfaceUniqueIds[i].first);
         }
     }
 
@@ -71,6 +76,31 @@
     return res;
 }
 
+status_t Camera3SharedOutputStream::attachBufferToSplitterLocked(
+        ANativeWindowBuffer* anb,
+        const std::vector<size_t>& surface_ids) {
+    status_t res = OK;
+
+    // Attach the buffer to the splitter output queues. This could block if
+    // the output queue doesn't have any empty slot. So unlock during the course
+    // of attachBufferToOutputs.
+    sp<Camera3StreamSplitter> splitter = mStreamSplitter;
+    mLock.unlock();
+    res = splitter->attachBufferToOutputs(anb, surface_ids);
+    mLock.lock();
+    if (res != OK) {
+        ALOGE("%s: Stream %d: Cannot attach stream splitter buffer to outputs: %s (%d)",
+                __FUNCTION__, mId, strerror(-res), res);
+        // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
+        // let prepareNextBuffer handle the error.)
+        if (res == NO_INIT && mState == STATE_CONFIGURED) {
+            mState = STATE_ABANDONED;
+        }
+    }
+    return res;
+}
+
+
 status_t Camera3SharedOutputStream::notifyBufferReleased(ANativeWindowBuffer *anwBuffer) {
     Mutex::Autolock l(mLock);
     status_t res = OK;
@@ -89,7 +119,7 @@
         return true;
     }
 
-    return (mSurfaces[surface_id] == nullptr);
+    return (mSurfaceUniqueIds[surface_id].first == nullptr);
 }
 
 status_t Camera3SharedOutputStream::setConsumers(const std::vector<sp<Surface>>& surfaces) {
@@ -112,7 +142,7 @@
             return NO_MEMORY;
         }
 
-        mSurfaces[id] = surface;
+        mSurfaceUniqueIds[id] = std::make_pair(surface, mNextUniqueSurfaceId++);
 
         // Only call addOutput if the splitter has been connected.
         if (mStreamSplitter != nullptr) {
@@ -128,7 +158,7 @@
 }
 
 status_t Camera3SharedOutputStream::getBufferLocked(camera3_stream_buffer *buffer,
-        const std::vector<size_t>& surface_ids) {
+        const std::vector<size_t>& surfaceIds) {
     ANativeWindowBuffer* anb;
     int fenceFd = -1;
 
@@ -138,23 +168,11 @@
         return res;
     }
 
-    // Attach the buffer to the splitter output queues. This could block if
-    // the output queue doesn't have any empty slot. So unlock during the course
-    // of attachBufferToOutputs.
-    sp<Camera3StreamSplitter> splitter = mStreamSplitter;
-    mLock.unlock();
-    res = splitter->attachBufferToOutputs(anb, surface_ids);
-    mLock.lock();
-    if (res != OK) {
-        ALOGE("%s: Stream %d: Cannot attach stream splitter buffer to outputs: %s (%d)",
-                __FUNCTION__, mId, strerror(-res), res);
-        // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
-        // let prepareNextBuffer handle the error.)
-        if (res == NO_INIT && mState == STATE_CONFIGURED) {
-            mState = STATE_ABANDONED;
+    if (!mUseHalBufManager) {
+        res = attachBufferToSplitterLocked(anb, surfaceIds);
+        if (res != OK) {
+            return res;
         }
-
-        return res;
     }
 
     /**
@@ -168,8 +186,38 @@
 }
 
 status_t Camera3SharedOutputStream::queueBufferToConsumer(sp<ANativeWindow>& consumer,
-            ANativeWindowBuffer* buffer, int anwReleaseFence) {
-    status_t res = consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
+            ANativeWindowBuffer* buffer, int anwReleaseFence,
+            const std::vector<size_t>& uniqueSurfaceIds) {
+    status_t res = OK;
+    if (mUseHalBufManager) {
+        if (uniqueSurfaceIds.size() == 0) {
+            ALOGE("%s: uniqueSurfaceIds must not be empty!", __FUNCTION__);
+            return BAD_VALUE;
+        }
+        Mutex::Autolock l(mLock);
+        std::vector<size_t> surfaceIds;
+        for (const auto& uniqueId : uniqueSurfaceIds) {
+            bool uniqueIdFound = false;
+            for (size_t i = 0; i < kMaxOutputs; i++) {
+                if (mSurfaceUniqueIds[i].second == uniqueId) {
+                    surfaceIds.push_back(i);
+                    uniqueIdFound = true;
+                    break;
+                }
+            }
+            if (!uniqueIdFound) {
+                ALOGV("%s: unknown unique surface ID %zu for stream %d: "
+                        "output might have been removed.",
+                        __FUNCTION__, uniqueId, mId);
+            }
+        }
+        res = attachBufferToSplitterLocked(buffer, surfaceIds);
+        if (res != OK) {
+            return res;
+        }
+    }
+
+    res = consumer->queueBuffer(consumer.get(), buffer, anwReleaseFence);
 
     // After queuing buffer to the internal consumer queue, check whether the buffer is
     // successfully queued to the output queues.
@@ -228,8 +276,8 @@
         *usage = getPresetConsumerUsage();
 
         for (size_t id = 0; id < kMaxOutputs; id++) {
-            if (mSurfaces[id] != nullptr) {
-                res = getEndpointUsageForSurface(&u, mSurfaces[id]);
+            if (mSurfaceUniqueIds[id].first != nullptr) {
+                res = getEndpointUsageForSurface(&u, mSurfaceUniqueIds[id].first);
                 *usage |= u;
             }
         }
@@ -245,7 +293,7 @@
 ssize_t Camera3SharedOutputStream::getNextSurfaceIdLocked() {
     ssize_t id = -1;
     for (size_t i = 0; i < kMaxOutputs; i++) {
-        if (mSurfaces[i] == nullptr) {
+        if (mSurfaceUniqueIds[i].first == nullptr) {
             id = i;
             break;
         }
@@ -258,7 +306,7 @@
     Mutex::Autolock l(mLock);
     ssize_t id = -1;
     for (size_t i = 0; i < kMaxOutputs; i++) {
-        if (mSurfaces[i] == surface) {
+        if (mSurfaceUniqueIds[i].first == surface) {
             id = i;
             break;
         }
@@ -267,6 +315,26 @@
     return id;
 }
 
+status_t Camera3SharedOutputStream::getUniqueSurfaceIds(
+        const std::vector<size_t>& surfaceIds,
+        /*out*/std::vector<size_t>* outUniqueIds) {
+    Mutex::Autolock l(mLock);
+    if (outUniqueIds == nullptr || surfaceIds.size() > kMaxOutputs) {
+        return BAD_VALUE;
+    }
+
+    outUniqueIds->clear();
+    outUniqueIds->reserve(surfaceIds.size());
+
+    for (const auto& surfaceId : surfaceIds) {
+        if (surfaceId >= kMaxOutputs) {
+            return BAD_VALUE;
+        }
+        outUniqueIds->push_back(mSurfaceUniqueIds[surfaceId].second);
+    }
+    return OK;
+}
+
 status_t Camera3SharedOutputStream::revertPartialUpdateLocked(
         const KeyedVector<sp<Surface>, size_t> &removedSurfaces,
         const KeyedVector<sp<Surface>, size_t> &attachedSurfaces) {
@@ -280,7 +348,7 @@
                 return UNKNOWN_ERROR;
             }
         }
-        mSurfaces[index] = nullptr;
+        mSurfaceUniqueIds[index] = std::make_pair(nullptr, mNextUniqueSurfaceId++);
     }
 
     for (size_t i = 0; i < removedSurfaces.size(); i++) {
@@ -291,7 +359,8 @@
                 return UNKNOWN_ERROR;
             }
         }
-        mSurfaces[index] = removedSurfaces.keyAt(i);
+        mSurfaceUniqueIds[index] = std::make_pair(
+                removedSurfaces.keyAt(i), mNextUniqueSurfaceId++);
     }
 
     return ret;
@@ -343,8 +412,8 @@
 
             }
         }
-        mSurfaces[it] = nullptr;
-        removedSurfaces.add(mSurfaces[it], it);
+        removedSurfaces.add(mSurfaceUniqueIds[it].first, it);
+        mSurfaceUniqueIds[it] = std::make_pair(nullptr, mNextUniqueSurfaceId++);
     }
 
     //Next add the new outputs
@@ -369,7 +438,7 @@
                 return ret;
             }
         }
-        mSurfaces[surfaceId] = it;
+        mSurfaceUniqueIds[surfaceId] = std::make_pair(it, mNextUniqueSurfaceId++);
         outputMap->add(it, surfaceId);
     }
 
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
index 02b1c09..b5e37c2 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_SERVERS_CAMERA3_SHARED_OUTPUT_STREAM_H
 #define ANDROID_SERVERS_CAMERA3_SHARED_OUTPUT_STREAM_H
 
+#include <array>
 #include "Camera3StreamSplitter.h"
 #include "Camera3OutputStream.h"
 
@@ -37,7 +38,8 @@
             uint64_t consumerUsage, android_dataspace dataSpace,
             camera3_stream_rotation_t rotation, nsecs_t timestampOffset,
             const String8& physicalCameraId,
-            int setId = CAMERA3_STREAM_SET_ID_INVALID);
+            int setId = CAMERA3_STREAM_SET_ID_INVALID,
+            bool useHalBufManager = false);
 
     virtual ~Camera3SharedOutputStream();
 
@@ -49,6 +51,15 @@
 
     virtual ssize_t getSurfaceId(const sp<Surface> &surface);
 
+    /**
+     * Query the unique surface IDs of current surfaceIds.
+     * When passing unique surface IDs in returnBuffer(), if the
+     * surfaceId has been removed from the stream, the output corresponding to
+     * the unique surface ID will be ignored and not delivered to client.
+     */
+    virtual status_t getUniqueSurfaceIds(const std::vector<size_t>& surfaceIds,
+            /*out*/std::vector<size_t>* outUniqueIds) override;
+
     virtual status_t updateStream(const std::vector<sp<Surface>> &outputSurfaces,
             const std::vector<OutputStreamInfo> &outputInfo,
             const std::vector<size_t> &removedSurfaceIds,
@@ -58,8 +69,17 @@
 
     static const size_t kMaxOutputs = 4;
 
-    // Map surfaceId -> output surfaces
-    sp<Surface> mSurfaces[kMaxOutputs];
+    // Whether HAL is in control for buffer management. Surface sharing behavior
+    // depends on this flag.
+    const bool mUseHalBufManager;
+
+    // Pair of an output Surface and its unique ID
+    typedef std::pair<sp<Surface>, size_t> SurfaceUniqueId;
+
+    // Map surfaceId -> (output surface, unique surface ID)
+    std::array<SurfaceUniqueId, kMaxOutputs> mSurfaceUniqueIds;
+
+    size_t mNextUniqueSurfaceId = 0;
 
     ssize_t getNextSurfaceIdLocked();
 
@@ -78,13 +98,24 @@
     status_t connectStreamSplitterLocked();
 
     /**
+     * Attach the output buffer to stream splitter.
+     * When camera service is doing buffer management, this method will be called
+     * before the buffer is handed out to HAL in request thread.
+     * When HAL is doing buffer management, this method will be called when
+     * the buffer is returned from HAL in hwbinder callback thread.
+     */
+    status_t attachBufferToSplitterLocked(ANativeWindowBuffer* anb,
+            const std::vector<size_t>& surface_ids);
+
+    /**
      * Internal Camera3Stream interface
      */
     virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
             const std::vector<size_t>& surface_ids);
 
     virtual status_t queueBufferToConsumer(sp<ANativeWindow>& consumer,
-            ANativeWindowBuffer* buffer, int anwReleaseFence);
+            ANativeWindowBuffer* buffer, int anwReleaseFence,
+            const std::vector<size_t>& uniqueSurfaceIds);
 
     virtual status_t configureQueueLocked();
 
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index ee989e1..24d1c1b 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -655,7 +655,8 @@
 }
 
 status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer,
-        nsecs_t timestamp, bool timestampIncreasing) {
+        nsecs_t timestamp, bool timestampIncreasing,
+         const std::vector<size_t>& surface_ids) {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
 
@@ -684,7 +685,7 @@
      *
      * Do this for getBuffer as well.
      */
-    status_t res = returnBufferLocked(b, timestamp);
+    status_t res = returnBufferLocked(b, timestamp, surface_ids);
     if (res == OK) {
         fireBufferListenersLocked(b, /*acquired*/false, /*output*/true);
     }
@@ -795,6 +796,12 @@
     return hasOutstandingBuffersLocked();
 }
 
+size_t Camera3Stream::getOutstandingBuffersCount() const {
+    ATRACE_CALL();
+    Mutex::Autolock l(mLock);
+    return getHandoutOutputBufferCountLocked();
+}
+
 status_t Camera3Stream::setStatusTracker(sp<StatusTracker> statusTracker) {
     Mutex::Autolock l(mLock);
     sp<StatusTracker> oldTracker = mStatusTracker.promote();
@@ -837,7 +844,7 @@
     return INVALID_OPERATION;
 }
 status_t Camera3Stream::returnBufferLocked(const camera3_stream_buffer &,
-                                           nsecs_t) {
+                                           nsecs_t, const std::vector<size_t>&) {
     ALOGE("%s: This type of stream does not support output", __FUNCTION__);
     return INVALID_OPERATION;
 }
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 1c67fb2..ddba9f6 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -322,11 +322,17 @@
     /**
      * Return a buffer to the stream after use by the HAL.
      *
+     * Multiple surfaces could share the same HAL stream, but a request may
+     * be only for a subset of surfaces. In this case, the
+     * Camera3StreamInterface object needs the surface ID information to attach
+     * buffers for those surfaces.
+     *
      * This method may only be called for buffers provided by getBuffer().
      * For bidirectional streams, this method applies to the output-side buffers
      */
     status_t         returnBuffer(const camera3_stream_buffer &buffer,
-            nsecs_t timestamp, bool timestampIncreasing);
+            nsecs_t timestamp, bool timestampIncreasing,
+            const std::vector<size_t>& surface_ids = std::vector<size_t>());
 
     /**
      * Fill in the camera3_stream_buffer with the next valid buffer for this
@@ -360,6 +366,11 @@
      */
     bool             hasOutstandingBuffers() const;
 
+    /**
+     * Get number of buffers currently handed out to HAL
+     */
+    size_t           getOutstandingBuffersCount() const;
+
     enum {
         TIMEOUT_NEVER = -1
     };
@@ -473,7 +484,8 @@
     virtual status_t getBufferLocked(camera3_stream_buffer *buffer,
             const std::vector<size_t>& surface_ids = std::vector<size_t>());
     virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer,
-            nsecs_t timestamp);
+            nsecs_t timestamp,
+            const std::vector<size_t>& surface_ids = std::vector<size_t>());
     virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer);
     virtual status_t returnInputBufferLocked(
             const camera3_stream_buffer &buffer);
@@ -495,7 +507,7 @@
     virtual size_t   getBufferCountLocked() = 0;
 
     // Get handout output buffer count.
-    virtual size_t   getHandoutOutputBufferCountLocked() = 0;
+    virtual size_t   getHandoutOutputBufferCountLocked() const = 0;
 
     // Get handout input buffer count.
     virtual size_t   getHandoutInputBufferCountLocked() = 0;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 5758ac8..a84720b 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -248,11 +248,18 @@
     /**
      * Return a buffer to the stream after use by the HAL.
      *
+     * Multiple surfaces could share the same HAL stream, but a request may
+     * be only for a subset of surfaces. In this case, the
+     * Camera3StreamInterface object needs the surface ID information to attach
+     * buffers for those surfaces. For the case of single surface for a HAL
+     * stream, surface_ids parameter has no effect.
+     *
      * This method may only be called for buffers provided by getBuffer().
      * For bidirectional streams, this method applies to the output-side buffers
      */
     virtual status_t returnBuffer(const camera3_stream_buffer &buffer,
-            nsecs_t timestamp, bool timestampIncreasing = true) = 0;
+            nsecs_t timestamp, bool timestampIncreasing = true,
+            const std::vector<size_t>& surface_ids = std::vector<size_t>()) = 0;
 
     /**
      * Fill in the camera3_stream_buffer with the next valid buffer for this
@@ -289,6 +296,11 @@
      */
     virtual bool     hasOutstandingBuffers() const = 0;
 
+    /**
+     * Get number of buffers currently handed out to HAL
+     */
+    virtual size_t   getOutstandingBuffersCount() const = 0;
+
     enum {
         TIMEOUT_NEVER = -1
     };
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index 6d08842..84c2ec7 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -152,6 +152,8 @@
     SP_LOGV("%s: Disconnected", __FUNCTION__);
 }
 
+Camera3StreamSplitter::Camera3StreamSplitter(bool useHalBufManager) :
+        mUseHalBufManager(useHalBufManager) {}
 
 Camera3StreamSplitter::~Camera3StreamSplitter() {
     disconnect();
@@ -241,7 +243,9 @@
     uint64_t usage = 0;
     res = native_window_get_consumer_usage(static_cast<ANativeWindow*>(outputQueue.get()), &usage);
     if (!(usage & (GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE))) {
-        outputQueue->setDequeueTimeout(kDequeueBufferTimeout);
+        nsecs_t timeout = mUseHalBufManager ?
+                kHalBufMgrDequeueBufferTimeout : kNormalDequeueBufferTimeout;
+        outputQueue->setDequeueTimeout(timeout);
     }
 
     res = gbp->allowAllocation(false);
@@ -253,6 +257,10 @@
     // Add new entry into mOutputs
     mOutputs[surfaceId] = gbp;
     mConsumerBufferCount[surfaceId] = maxConsumerBuffers;
+    if (mConsumerBufferCount[surfaceId] > mMaxHalBuffers) {
+        SP_LOGW("%s: Consumer buffer count %zu larger than max. Hal buffers: %zu", __FUNCTION__,
+                mConsumerBufferCount[surfaceId], mMaxHalBuffers);
+    }
     mNotifiers[gbp] = listener;
     mOutputSlots[gbp] = std::make_unique<OutputSlots>(totalBufferCount);
 
@@ -324,11 +332,7 @@
     }
 
     mNotifiers[gbp] = nullptr;
-    if (mConsumerBufferCount[surfaceId] < mMaxHalBuffers) {
-        mMaxConsumerBuffers -= mConsumerBufferCount[surfaceId];
-    } else {
-        SP_LOGE("%s: Cached consumer buffer count mismatch!", __FUNCTION__);
-    }
+    mMaxConsumerBuffers -= mConsumerBufferCount[surfaceId];
     mConsumerBufferCount[surfaceId] = 0;
 
     return res;
@@ -436,8 +440,9 @@
         res = gbp->attachBuffer(&slot, gb);
         mMutex.lock();
         if (res != OK) {
-            SP_LOGE("%s: Cannot acquireBuffer from GraphicBufferProducer %p: %s (%d)",
+            SP_LOGE("%s: Cannot attachBuffer from GraphicBufferProducer %p: %s (%d)",
                     __FUNCTION__, gbp.get(), strerror(-res), res);
+            // TODO: might need to detach/cleanup the already attached buffers before return?
             return res;
         }
         if ((slot < 0) || (slot > BufferQueue::NUM_BUFFER_SLOTS)) {
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index 1eaf2bd..960f7aa 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
@@ -49,7 +49,7 @@
 public:
 
     // Constructor
-    Camera3StreamSplitter() = default;
+    Camera3StreamSplitter(bool useHalBufManager = false);
 
     // Connect to the stream splitter by creating buffer queue and connecting it
     // with output surfaces.
@@ -226,7 +226,10 @@
     android::PixelFormat mFormat = android::PIXEL_FORMAT_NONE;
     uint64_t mProducerUsage = 0;
 
-    static const nsecs_t kDequeueBufferTimeout   = s2ns(1); // 1 sec
+    // The attachBuffer call will happen on different thread according to mUseHalBufManager and have
+    // different timing constraint.
+    static const nsecs_t kNormalDequeueBufferTimeout    = s2ns(1);  // 1 sec
+    static const nsecs_t kHalBufMgrDequeueBufferTimeout = ms2ns(1); // 1 msec
 
     Mutex mMutex;
 
@@ -273,6 +276,8 @@
     size_t mAcquiredInputBuffers;
 
     String8 mConsumerName;
+
+    const bool mUseHalBufManager;
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp
new file mode 100644
index 0000000..e5e5024
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.cpp
@@ -0,0 +1,205 @@
+/*
+ * 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 <hardware/camera.h>
+
+#include <hidl/AidlCameraDeviceCallbacks.h>
+#include <hidl/Convert.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace device {
+namespace V2_0 {
+namespace implementation {
+
+using hardware::hidl_vec;
+using HCaptureResultExtras = android::frameworks::cameraservice::device::V2_0::CaptureResultExtras;
+using HPhysicalCaptureResultInfo = android::frameworks::cameraservice::device::V2_0::PhysicalCaptureResultInfo;
+using HCameraMetadata = android::frameworks::cameraservice::device::V2_0::CameraMetadata;
+
+const char *H2BCameraDeviceCallbacks::kResultKey = "CaptureResult";
+
+H2BCameraDeviceCallbacks::H2BCameraDeviceCallbacks(const sp<HalInterface>& base) : CBase(base) { }
+
+bool H2BCameraDeviceCallbacks::initializeLooper() {
+    mCbLooper = new ALooper;
+    mCbLooper->setName("cs-looper");
+    status_t err = mCbLooper->start(/*runOnCallingThread*/ false, /*canCallJava*/ false,
+                                    PRIORITY_DEFAULT);
+    if (err !=OK) {
+        ALOGE("Unable to start camera device callback looper");
+        return false;
+    }
+    mHandler = new CallbackHandler(this);
+    mCbLooper->registerHandler(mHandler);
+    return true;
+}
+
+H2BCameraDeviceCallbacks::~H2BCameraDeviceCallbacks() {
+    if (mCbLooper != nullptr) {
+        if (mHandler != nullptr) {
+            mCbLooper->unregisterHandler(mHandler->id());
+        }
+        mCbLooper->stop();
+    }
+    mCbLooper.clear();
+    mHandler.clear();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onDeviceError(
+    int32_t errorCode, const CaptureResultExtras& resultExtras) {
+    using hardware::cameraservice::utils::conversion::convertToHidl;
+    HCaptureResultExtras hCaptureResultExtras = convertToHidl(resultExtras);
+    auto ret = mBase->onDeviceError(convertToHidl(errorCode), hCaptureResultExtras);
+    if (!ret.isOk()) {
+        ALOGE("%s OnDeviceError callback failed due to %s",__FUNCTION__,
+              ret.description().c_str());
+    }
+    return binder::Status::ok();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onDeviceIdle() {
+    auto ret = mBase->onDeviceIdle();
+    if (!ret.isOk()) {
+          ALOGE("%s OnDeviceIdle callback failed due to %s",__FUNCTION__,
+                ret.description().c_str());
+    }
+    return binder::Status::ok();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onCaptureStarted(
+    const CaptureResultExtras& resultExtras, int64_t timestamp) {
+    using hardware::cameraservice::utils::conversion::convertToHidl;
+    HCaptureResultExtras hCaptureResultExtras = convertToHidl(resultExtras);
+    auto ret = mBase->onCaptureStarted(hCaptureResultExtras, timestamp);
+    if (!ret.isOk()) {
+        ALOGE("%s OnCaptureCallback failed due to %s",__FUNCTION__,
+              ret.description().c_str());
+    }
+    return binder::Status::ok();
+}
+
+void H2BCameraDeviceCallbacks::convertResultMetadataToHidl(const camera_metadata_t *rawMetadata,
+                                                           FmqSizeOrMetadata *hResultMetadata) {
+    // First try writing to fmq.
+    size_t metadata_size = get_camera_metadata_size(rawMetadata);
+    if ((metadata_size > 0) &&
+        (mCaptureResultMetadataQueue->availableToWrite() > 0)) {
+        if (mCaptureResultMetadataQueue->write((uint8_t *)rawMetadata, metadata_size)) {
+            hResultMetadata->fmqMetadataSize(metadata_size);
+        } else {
+            ALOGW("%s Couldn't use fmq, falling back to hwbinder", __FUNCTION__);
+            HCameraMetadata metadata;
+            hardware::cameraservice::utils::conversion::convertToHidl(rawMetadata, &metadata);
+            hResultMetadata->metadata(std::move(metadata));
+        }
+    }
+}
+
+void H2BCameraDeviceCallbacks::CallbackHandler::onMessageReceived(const sp<AMessage> &msg) {
+    sp<RefBase> obj = nullptr;
+    sp<ResultWrapper> resultWrapper = nullptr;
+    bool found = false;
+    switch (msg->what()) {
+        case kWhatResultReceived:
+            found = msg->findObject(kResultKey, &obj);
+            if (!found || obj == nullptr) {
+                ALOGE("Cannot find result object in callback message");
+                return;
+            }
+            resultWrapper = static_cast<ResultWrapper *>(obj.get());
+            processResultMessage(resultWrapper);
+            break;
+        default:
+            ALOGE("Unknown callback sent");
+            break;
+    }
+    return;
+}
+
+void H2BCameraDeviceCallbacks::CallbackHandler::processResultMessage(
+    sp<ResultWrapper> &resultWrapper) {
+    sp<H2BCameraDeviceCallbacks> converter = mConverter.promote();
+    if (converter == nullptr) {
+        ALOGE("Callback wrapper has died, result callback cannot be made");
+        return;
+    }
+    CameraMetadataNative &result = resultWrapper->mResult;
+    auto resultExtras = resultWrapper->mResultExtras;
+    auto &physicalCaptureResultInfos = resultWrapper->mPhysicalCaptureResultInfos;
+    HCaptureResultExtras hResultExtras =
+            hardware::cameraservice::utils::conversion::convertToHidl(resultExtras);
+    hidl_vec<HPhysicalCaptureResultInfo> hPhysicalCaptureResultInfos =
+            hardware::cameraservice::utils::conversion::convertToHidl(
+                    physicalCaptureResultInfos, converter->mCaptureResultMetadataQueue);
+
+    // Convert Metadata into HCameraMetadata;
+    FmqSizeOrMetadata hResult;
+    const camera_metadata_t *rawMetadata = result.getAndLock();
+    converter->convertResultMetadataToHidl(rawMetadata, &hResult);
+    result.unlock(rawMetadata);
+    auto ret = converter->mBase->onResultReceived(hResult, hResultExtras,
+                                                  hPhysicalCaptureResultInfos);
+    if (!ret.isOk()) {
+          ALOGE("%s OnResultReceived callback failed due to %s",__FUNCTION__,
+                ret.description().c_str());
+    }
+}
+
+binder::Status H2BCameraDeviceCallbacks::onResultReceived(
+    const CameraMetadataNative& result,
+    const CaptureResultExtras& resultExtras,
+    const ::std::vector<PhysicalCaptureResultInfo>& physicalCaptureResultInfos) {
+    // Wrap CameraMetadata, resultExtras and physicalCaptureResultInfos in on
+    // sp<RefBase>-able structure and post it.
+    sp<ResultWrapper> resultWrapper = new ResultWrapper(const_cast<CameraMetadataNative &>(result),
+                                                        resultExtras, physicalCaptureResultInfos);
+    sp<AMessage> msg = new AMessage(kWhatResultReceived, mHandler);
+    msg->setObject(kResultKey, resultWrapper);
+    msg->post();
+    return binder::Status::ok();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onPrepared(int32_t streamId) {
+    // not implemented
+    // To silence Wunused-parameter.
+    (void) streamId;
+    return binder::Status::ok();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onRepeatingRequestError(
+    int64_t lastFrameNumber,
+    int32_t repeatingRequestId) {
+    auto ret =
+        mBase->onRepeatingRequestError(lastFrameNumber, repeatingRequestId);
+    if (!ret.isOk()) {
+        ALOGE("%s OnRepeatingRequestEror callback failed due to %s",__FUNCTION__,
+              ret.description().c_str());
+    }
+    return binder::Status::ok();
+}
+
+binder::Status H2BCameraDeviceCallbacks::onRequestQueueEmpty() {
+    // not implemented
+    return binder::Status::ok();
+}
+
+} // implementation
+} // V2_0
+} // device
+} // cameraservice
+} // frameworks
+} // android
diff --git a/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.h b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.h
new file mode 100644
index 0000000..dbf520a
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/AidlCameraDeviceCallbacks.h
@@ -0,0 +1,132 @@
+/*
+ * 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_FRAMEWORKS_AIDL_CAMERADEVICECALLBACKS_H
+#define ANDROID_FRAMEWORKS_AIDL_CAMERADEVICECALLBACKS_H
+
+#include <mutex>
+#include <thread>
+
+#include <android/frameworks/cameraservice/common/2.0/types.h>
+#include <android/frameworks/cameraservice/service/2.0/types.h>
+#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceCallback.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <android/hardware/camera2/BnCameraDeviceCallbacks.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <CameraService.h>
+#include <hidl/CameraHybridInterface.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace device {
+namespace V2_0 {
+namespace implementation {
+
+using camerahybrid::H2BConverter;
+using HCameraDeviceCallback = cameraservice::device::V2_0::ICameraDeviceCallback;
+using HPhysicalCaptureResultInfo = cameraservice::device::V2_0::PhysicalCaptureResultInfo;
+using android::frameworks::cameraservice::device::V2_0::FmqSizeOrMetadata;
+
+using hardware::camera2::BnCameraDeviceCallbacks;
+using hardware::camera2::ICameraDeviceCallbacks;
+using hardware::camera2::impl::CaptureResultExtras;
+using hardware::camera2::impl::CameraMetadataNative;
+using hardware::camera2::impl::PhysicalCaptureResultInfo;
+using hardware::kSynchronizedReadWrite;
+using hardware::MessageQueue;
+using CaptureResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+struct H2BCameraDeviceCallbacks :
+    public H2BConverter<HCameraDeviceCallback, ICameraDeviceCallbacks, BnCameraDeviceCallbacks> {
+    H2BCameraDeviceCallbacks(const sp<HalInterface>& base);
+
+    ~H2BCameraDeviceCallbacks();
+
+    bool initializeLooper();
+
+    virtual binder::Status onDeviceError(int32_t errorCode,
+                                         const CaptureResultExtras& resultExtras) override;
+
+    virtual binder::Status onDeviceIdle() override;
+
+    virtual binder::Status onCaptureStarted(const CaptureResultExtras& resultExtras,
+                                            int64_t timestamp) override;
+
+    virtual binder::Status onResultReceived(
+        const CameraMetadataNative& result, const CaptureResultExtras& resultExtras,
+        const std::vector<PhysicalCaptureResultInfo>& physicalCaptureResultInfos) override;
+
+    virtual binder::Status onPrepared(int32_t streamId) override;
+
+    virtual binder::Status onRepeatingRequestError(int64_t lastFrameNumber,
+                                                   int32_t repeatingRequestId) override;
+
+    virtual binder::Status onRequestQueueEmpty() override;
+
+    void setCaptureResultMetadataQueue(std::shared_ptr<CaptureResultMetadataQueue> metadataQueue) {
+        mCaptureResultMetadataQueue = metadataQueue;
+    }
+
+ private:
+    // Wrapper struct so that parameters to onResultReceived callback may be
+    // sent through an AMessage.
+    struct ResultWrapper : public RefBase {
+        CameraMetadataNative mResult;
+        CaptureResultExtras mResultExtras;
+        std::vector<PhysicalCaptureResultInfo> mPhysicalCaptureResultInfos;
+        ResultWrapper(CameraMetadataNative &result,
+                      const CaptureResultExtras resultExtras,
+                      const std::vector<PhysicalCaptureResultInfo> &physicalCaptureResultInfos) :
+      // TODO: make this std::movable
+      mResult(result), mResultExtras(resultExtras), mPhysicalCaptureResultInfos(physicalCaptureResultInfos) { }
+    };
+
+    struct CallbackHandler : public AHandler {
+        public:
+            void onMessageReceived(const sp<AMessage> &msg) override;
+            CallbackHandler(H2BCameraDeviceCallbacks *converter) : mConverter(converter) { }
+        private:
+            void processResultMessage(sp<ResultWrapper> &resultWrapper);
+            wp<H2BCameraDeviceCallbacks> mConverter = nullptr;
+            Mutex mMetadataQueueLock;
+    };
+
+    void convertResultMetadataToHidl(const camera_metadata *rawMetadata,
+                                     FmqSizeOrMetadata *resultMetadata);
+    enum {
+        kWhatResultReceived,
+    };
+
+    static const char *kResultKey;
+
+    std::shared_ptr<CaptureResultMetadataQueue> mCaptureResultMetadataQueue = nullptr;
+    sp<CallbackHandler> mHandler = nullptr;
+    sp<ALooper> mCbLooper = nullptr;
+};
+
+} // implementation
+} // V2_0
+} // device
+} // cameraservice
+} // frameworks
+} // android
+#endif // ANDROID_FRAMEWORKS_AIDL_CAMERADEVICECALLBACKS_H
diff --git a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.cpp b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.cpp
new file mode 100644
index 0000000..110ef8e
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 <hidl/AidlCameraServiceListener.h>
+#include <hidl/Convert.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace service {
+namespace V2_0 {
+namespace implementation {
+
+using hardware::cameraservice::utils::conversion::convertToHidlCameraDeviceStatus;
+
+binder::Status H2BCameraServiceListener::onStatusChanged(
+    int32_t status, const ::android::String16& cameraId) {
+  HCameraDeviceStatus hCameraDeviceStatus = convertToHidlCameraDeviceStatus(status);
+  CameraStatusAndId cameraStatusAndId;
+  cameraStatusAndId.deviceStatus = hCameraDeviceStatus;
+  cameraStatusAndId.cameraId = String8(cameraId).string();
+  auto ret = mBase->onStatusChanged(cameraStatusAndId);
+  if (!ret.isOk()) {
+      ALOGE("%s OnStatusChanged callback failed due to %s",__FUNCTION__,
+            ret.description().c_str());
+  }
+  return binder::Status::ok();
+}
+
+::android::binder::Status H2BCameraServiceListener::onTorchStatusChanged(
+    int32_t, const ::android::String16&) {
+  // We don't implement onTorchStatusChanged
+  return binder::Status::ok();
+}
+
+} // implementation
+} // V2_0
+} // common
+} // cameraservice
+} // frameworks
+} // android
diff --git a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
new file mode 100644
index 0000000..ca9143d
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
@@ -0,0 +1,60 @@
+/*
+ * 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 <mutex>
+#include <thread>
+
+#include <android/frameworks/cameraservice/common/2.0/types.h>
+#include <android/frameworks/cameraservice/service/2.0/ICameraServiceListener.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <android/hardware/BnCameraServiceListener.h>
+#include <android/hardware/BpCameraServiceListener.h>
+
+#include <hidl/Status.h>
+#include <hidl/CameraHybridInterface.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace service {
+namespace V2_0 {
+namespace implementation {
+
+using hardware::BnCameraServiceListener;
+using hardware::BpCameraServiceListener;
+using camerahybrid::H2BConverter;
+using HCameraDeviceStatus = frameworks::cameraservice::service::V2_0::CameraDeviceStatus;
+typedef frameworks::cameraservice::service::V2_0::ICameraServiceListener HCameraServiceListener;
+
+struct H2BCameraServiceListener :
+    public H2BConverter<HCameraServiceListener, ICameraServiceListener, BnCameraServiceListener> {
+    H2BCameraServiceListener(const sp<HalInterface>& base) : CBase(base) { }
+
+    ~H2BCameraServiceListener() { }
+
+    virtual ::android::binder::Status onStatusChanged(int32_t status,
+                                                      const ::android::String16& cameraId) override;
+
+    virtual ::android::binder::Status onTorchStatusChanged(
+        int32_t status, const ::android::String16& cameraId) override;
+};
+
+} // implementation
+} // V2_0
+} // service
+} // cameraservice
+} // frameworks
+} // android
diff --git a/services/camera/libcameraservice/hidl/CameraHybridInterface.h b/services/camera/libcameraservice/hidl/CameraHybridInterface.h
new file mode 100644
index 0000000..baf0112
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/CameraHybridInterface.h
@@ -0,0 +1,138 @@
+/*
+ * 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_CAMERA_HYBRIDINTERFACE_H
+#define ANDROID_CAMERA_HYBRIDINTERFACE_H
+
+#include <vector>
+#include <mutex>
+
+#include <binder/Parcel.h>
+#include <hidl/HidlSupport.h>
+
+namespace android {
+namespace camerahybrid {
+typedef ::android::hidl::base::V1_0::IBase HInterface;
+
+template <
+        typename HINTERFACE,
+        typename INTERFACE,
+        typename BNINTERFACE >
+class H2BConverter : public BNINTERFACE {
+public:
+    typedef H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE> CBase; // Converter Base
+    typedef INTERFACE BaseInterface;
+    typedef HINTERFACE HalInterface;
+
+    H2BConverter(const sp<HalInterface>& base) : mBase(base) {}
+    virtual sp<HalInterface> getHalInterface() { return mBase; }
+    virtual status_t linkToDeath(
+            const sp<IBinder::DeathRecipient>& recipient,
+            void* cookie = nullptr,
+            uint32_t flags = 0);
+    virtual status_t unlinkToDeath(
+            const wp<IBinder::DeathRecipient>& recipient,
+            void* cookie = nullptr,
+            uint32_t flags = 0,
+            wp<IBinder::DeathRecipient>* outRecipient = nullptr);
+
+protected:
+    sp<HalInterface> mBase;
+    struct Obituary : public hardware::hidl_death_recipient {
+        wp<IBinder::DeathRecipient> recipient;
+        void* cookie;
+        uint32_t flags;
+        wp<IBinder> who;
+        Obituary(
+                const wp<IBinder::DeathRecipient>& r,
+                void* c, uint32_t f,
+                const wp<IBinder>& w) :
+            recipient(r), cookie(c), flags(f), who(w) {
+        }
+        Obituary(const Obituary& o) :
+            recipient(o.recipient),
+            cookie(o.cookie),
+            flags(o.flags),
+            who(o.who) {
+        }
+        Obituary& operator=(const Obituary& o) {
+            recipient = o.recipient;
+            cookie = o.cookie;
+            flags = o.flags;
+            who = o.who;
+            return *this;
+        }
+        void serviceDied(uint64_t, const wp<HInterface>&) override {
+            sp<IBinder::DeathRecipient> dr = recipient.promote();
+            if (dr != nullptr) {
+                dr->binderDied(who);
+            }
+        }
+    };
+    std::mutex mObituariesLock;
+    std::vector<sp<Obituary> > mObituaries;
+};
+
+template <
+        typename HINTERFACE,
+        typename INTERFACE,
+        typename BNINTERFACE>
+status_t H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE>::
+        linkToDeath(
+        const sp<IBinder::DeathRecipient>& recipient,
+        void* cookie, uint32_t flags) {
+    LOG_ALWAYS_FATAL_IF(recipient == nullptr,
+            "linkToDeath(): recipient must be non-nullptr");
+    {
+        std::lock_guard<std::mutex> lock(mObituariesLock);
+        mObituaries.push_back(new Obituary(recipient, cookie, flags, this));
+        if (!mBase->linkToDeath(mObituaries.back(), 0)) {
+           return DEAD_OBJECT;
+        }
+    }
+    return NO_ERROR;
+}
+
+template <
+        typename HINTERFACE,
+        typename INTERFACE,
+        typename BNINTERFACE>
+status_t H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE>::
+        unlinkToDeath(
+        const wp<IBinder::DeathRecipient>& recipient,
+        void* cookie, uint32_t flags,
+        wp<IBinder::DeathRecipient>* outRecipient) {
+    std::lock_guard<std::mutex> lock(mObituariesLock);
+    for (auto i = mObituaries.begin(); i != mObituaries.end(); ++i) {
+        if ((flags = (*i)->flags) && (
+                (recipient == (*i)->recipient) ||
+                ((recipient == nullptr) && (cookie == (*i)->cookie)))) {
+            if (outRecipient != nullptr) {
+                *outRecipient = (*i)->recipient;
+            }
+            bool success = mBase->unlinkToDeath(*i);
+            mObituaries.erase(i);
+            return success ? NO_ERROR : DEAD_OBJECT;
+        }
+    }
+    return NAME_NOT_FOUND;
+}
+
+} // namespace camerahybrid
+} // namespace android
+
+#endif // ANDROID_CAMERA_HYBRIDINTERFACE_H
+
diff --git a/services/camera/libcameraservice/hidl/Convert.cpp b/services/camera/libcameraservice/hidl/Convert.cpp
new file mode 100644
index 0000000..22e879e
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/Convert.cpp
@@ -0,0 +1,264 @@
+/*
+ * 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 <hidl/Convert.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+#include <NdkImageReaderPriv.h>
+
+namespace android {
+namespace hardware {
+namespace cameraservice {
+namespace utils {
+namespace conversion {
+
+using hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer;
+
+// Note: existing data in dst will be gone. Caller still owns the memory of src
+void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst) {
+    if (src == nullptr) {
+        ALOGW("%s:attempt to convert empty metadata to Hidl", __FUNCTION__);
+        return;
+    }
+    size_t size = get_camera_metadata_size(src);
+    dst->setToExternal((uint8_t *) src, size);
+    return;
+}
+
+int32_t convertFromHidl(HStreamConfigurationMode streamConfigurationMode) {
+    switch (streamConfigurationMode) {
+        case HStreamConfigurationMode::CONSTRAINED_HIGH_SPEED_MODE:
+            return camera2::ICameraDeviceUser::CONSTRAINED_HIGH_SPEED_MODE;
+        case HStreamConfigurationMode::NORMAL_MODE:
+            return camera2::ICameraDeviceUser::NORMAL_MODE;
+        default:
+            // TODO: Fix this
+            return camera2::ICameraDeviceUser::VENDOR_MODE_START;
+    }
+}
+
+int32_t convertFromHidl(HTemplateId templateId) {
+    switch(templateId) {
+        case HTemplateId::PREVIEW:
+            return camera2::ICameraDeviceUser::TEMPLATE_PREVIEW;
+        case HTemplateId::STILL_CAPTURE:
+            return camera2::ICameraDeviceUser::TEMPLATE_STILL_CAPTURE;
+        case HTemplateId::RECORD:
+            return camera2::ICameraDeviceUser::TEMPLATE_RECORD;
+        case HTemplateId::VIDEO_SNAPSHOT:
+            return camera2::ICameraDeviceUser::TEMPLATE_VIDEO_SNAPSHOT;
+        case HTemplateId::ZERO_SHUTTER_LAG:
+            return camera2::ICameraDeviceUser::TEMPLATE_ZERO_SHUTTER_LAG;
+        case HTemplateId::MANUAL:
+            return camera2::ICameraDeviceUser::TEMPLATE_MANUAL;
+    }
+}
+
+int convertFromHidl(HOutputConfiguration::Rotation rotation) {
+    switch(rotation) {
+        case HOutputConfiguration::Rotation::R0:
+            return 0;
+        case HOutputConfiguration::Rotation::R90:
+            return 1;
+        case HOutputConfiguration::Rotation::R180:
+            return 2;
+        case HOutputConfiguration::Rotation::R270:
+            return 3;
+    }
+}
+
+hardware::camera2::params::OutputConfiguration convertFromHidl(
+    const HOutputConfiguration &hOutputConfiguration) {
+    std::vector<sp<IGraphicBufferProducer>> iGBPs;
+    auto &windowHandles = hOutputConfiguration.windowHandles;
+    iGBPs.reserve(windowHandles.size());
+    for (auto &handle : windowHandles) {
+        iGBPs.push_back(new H2BGraphicBufferProducer(AImageReader_getHGBPFromHandle(handle)));
+    }
+    hardware::camera2::params::OutputConfiguration outputConfiguration(
+        iGBPs, convertFromHidl(hOutputConfiguration.rotation),
+        hOutputConfiguration.windowGroupId, OutputConfiguration::SURFACE_TYPE_UNKNOWN, 0, 0,
+        (windowHandles.size() > 1));
+    return outputConfiguration;
+}
+
+// The camera metadata here is cloned. Since we're reading metadata over
+// hwbinder we would need to clone it in order to avoid aligment issues.
+bool convertFromHidl(const HCameraMetadata &src, CameraMetadata *dst) {
+    const camera_metadata_t *buffer = reinterpret_cast<const camera_metadata_t*>(src.data());
+    size_t expectedSize = src.size();
+    int res = validate_camera_metadata_structure(buffer, &expectedSize);
+    if (res == OK || res == CAMERA_METADATA_VALIDATION_SHIFTED) {
+        *dst = buffer;
+    } else {
+        ALOGE("%s: Malformed camera metadata received from HAL", __FUNCTION__);
+        return false;
+    }
+    return true;
+}
+
+HCameraDeviceStatus convertToHidlCameraDeviceStatus(int32_t status) {
+    HCameraDeviceStatus deviceStatus = HCameraDeviceStatus::STATUS_UNKNOWN;
+    switch(status) {
+        case hardware::ICameraServiceListener::STATUS_NOT_PRESENT:
+            deviceStatus = HCameraDeviceStatus::STATUS_NOT_PRESENT;
+            break;
+        case hardware::ICameraServiceListener::STATUS_PRESENT:
+            deviceStatus = HCameraDeviceStatus::STATUS_PRESENT;
+            break;
+        case hardware::ICameraServiceListener::STATUS_ENUMERATING:
+            deviceStatus = HCameraDeviceStatus::STATUS_ENUMERATING;
+            break;
+        case hardware::ICameraServiceListener::STATUS_NOT_AVAILABLE:
+            deviceStatus = HCameraDeviceStatus::STATUS_NOT_AVAILABLE;
+            break;
+        default:
+            break;
+    }
+    return deviceStatus;
+}
+
+HCaptureResultExtras convertToHidl(const CaptureResultExtras &captureResultExtras) {
+    HCaptureResultExtras hCaptureResultExtras;
+    hCaptureResultExtras.burstId = captureResultExtras.burstId;
+    hCaptureResultExtras.frameNumber = captureResultExtras.frameNumber;
+    hCaptureResultExtras.partialResultCount = captureResultExtras.partialResultCount;
+    hCaptureResultExtras.errorStreamId = captureResultExtras.errorStreamId;
+    return hCaptureResultExtras;
+}
+
+HErrorCode convertToHidl(int32_t errorCode) {
+    switch(errorCode) {
+        case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED:
+            return HErrorCode::CAMERA_DISCONNECTED;
+        case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE :
+            return HErrorCode::CAMERA_DEVICE;
+        case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_SERVICE:
+            return HErrorCode::CAMERA_SERVICE;
+        case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST:
+            return HErrorCode::CAMERA_REQUEST;
+        case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT:
+            return HErrorCode::CAMERA_RESULT;
+        case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER:
+            return HErrorCode::CAMERA_BUFFER;
+        case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_DISABLED:
+            return HErrorCode::CAMERA_DISABLED;
+        case camera2::ICameraDeviceCallbacks::ERROR_CAMERA_INVALID_ERROR:
+            return HErrorCode::CAMERA_INVALID_ERROR;
+        default:
+            return HErrorCode::CAMERA_UNKNOWN_ERROR;
+    }
+}
+
+void convertToHidl(const std::vector<hardware::CameraStatus> &src,
+                   hidl_vec<HCameraStatusAndId>* dst) {
+    dst->resize(src.size());
+    size_t i = 0;
+    for (auto &statusAndId : src) {
+        auto &a = (*dst)[i++];
+        a.cameraId = statusAndId.cameraId.c_str();
+        a.deviceStatus = convertToHidlCameraDeviceStatus(statusAndId.status);
+    }
+    return;
+}
+
+void convertToHidl(
+    const hardware::camera2::utils::SubmitInfo &submitInfo,
+    frameworks::cameraservice::device::V2_0::SubmitInfo *hSubmitInfo) {
+    hSubmitInfo->requestId = submitInfo.mRequestId;
+    hSubmitInfo->lastFrameNumber = submitInfo.mLastFrameNumber;
+}
+
+HStatus B2HStatus(const binder::Status &bStatus) {
+    HStatus status = HStatus::NO_ERROR;
+    if (bStatus.isOk()) {
+        // NO Error here
+        return status;
+    }
+    switch(bStatus.serviceSpecificErrorCode()) {
+        case hardware::ICameraService::ERROR_DISCONNECTED:
+            status = HStatus::DISCONNECTED;
+            break;
+        case hardware::ICameraService::ERROR_CAMERA_IN_USE:
+            status = HStatus::CAMERA_IN_USE;
+            break;
+        case hardware::ICameraService::ERROR_MAX_CAMERAS_IN_USE:
+            status = HStatus::MAX_CAMERAS_IN_USE;
+            break;
+        case hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT:
+            status = HStatus::ILLEGAL_ARGUMENT;
+            break;
+        case hardware::ICameraService::ERROR_DEPRECATED_HAL:
+            // Should not reach here since we filtered legacy HALs earlier
+            status = HStatus::DEPRECATED_HAL;
+            break;
+        case hardware::ICameraService::ERROR_DISABLED:
+            status = HStatus::DISABLED;
+            break;
+        case hardware::ICameraService::ERROR_PERMISSION_DENIED:
+            status = HStatus::PERMISSION_DENIED;
+            break;
+        case hardware::ICameraService::ERROR_INVALID_OPERATION:
+            status = HStatus::INVALID_OPERATION;
+            break;
+        default:
+            status = HStatus::UNKNOWN_ERROR;
+            break;
+    }
+  return status;
+}
+
+HPhysicalCaptureResultInfo convertToHidl(
+    const PhysicalCaptureResultInfo &physicalCaptureResultInfo,
+    std::shared_ptr<CaptureResultMetadataQueue> &captureResultMetadataQueue) {
+    HPhysicalCaptureResultInfo hPhysicalCaptureResultInfo;
+    hPhysicalCaptureResultInfo.physicalCameraId =
+        String8(physicalCaptureResultInfo.mPhysicalCameraId).string();
+    const camera_metadata_t *rawMetadata =
+        physicalCaptureResultInfo.mPhysicalCameraMetadata.getAndLock();
+    // Try using fmq at first.
+    size_t metadata_size = get_camera_metadata_size(rawMetadata);
+    if ((metadata_size > 0) && (captureResultMetadataQueue->availableToWrite() > 0)) {
+        if (captureResultMetadataQueue->write((uint8_t *)rawMetadata, metadata_size)) {
+            hPhysicalCaptureResultInfo.physicalCameraMetadata.fmqMetadataSize(metadata_size);
+        } else {
+            ALOGW("%s Couldn't use fmq, falling back to hwbinder", __FUNCTION__);
+            HCameraMetadata metadata;
+            convertToHidl(rawMetadata, &metadata);
+            hPhysicalCaptureResultInfo.physicalCameraMetadata.metadata(std::move(metadata));
+        }
+    }
+    physicalCaptureResultInfo.mPhysicalCameraMetadata.unlock(rawMetadata);
+    return hPhysicalCaptureResultInfo;
+}
+
+hidl_vec<HPhysicalCaptureResultInfo> convertToHidl(
+    const std::vector<PhysicalCaptureResultInfo> &physicalCaptureResultInfos,
+    std::shared_ptr<CaptureResultMetadataQueue> &captureResultMetadataQueue) {
+    hidl_vec<HPhysicalCaptureResultInfo> hPhysicalCaptureResultInfos;
+    hPhysicalCaptureResultInfos.resize(physicalCaptureResultInfos.size());
+    size_t i = 0;
+    for (auto &physicalCaptureResultInfo : physicalCaptureResultInfos) {
+        hPhysicalCaptureResultInfos[i++] = convertToHidl(physicalCaptureResultInfo,
+                                                         captureResultMetadataQueue);
+    }
+    return hPhysicalCaptureResultInfos;
+}
+
+} //conversion
+} // utils
+} //cameraservice
+} // hardware
+} // android
diff --git a/services/camera/libcameraservice/hidl/Convert.h b/services/camera/libcameraservice/hidl/Convert.h
new file mode 100644
index 0000000..82937a3
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/Convert.h
@@ -0,0 +1,97 @@
+/*
+ * 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 CAMERASERVER_CONVERT_HIDL
+#define CAMERASERVER_CONVERT_HIDL
+
+#include <vector>
+
+#include <android/frameworks/cameraservice/service/2.0/ICameraService.h>
+#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceUser.h>
+#include <android/frameworks/cameraservice/common/2.0/types.h>
+#include <android/frameworks/cameraservice/service/2.0/types.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <android/hardware/camera/common/1.0/types.h>
+#include <android/hardware/camera2/ICameraDeviceUser.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+#include <android/hardware/ICameraService.h>
+#include <fmq/MessageQueue.h>
+#include <hardware/camera.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+namespace hardware {
+namespace cameraservice {
+namespace utils {
+namespace conversion {
+
+using hardware::camera2::impl::CaptureResultExtras;
+using hardware::camera2::impl::PhysicalCaptureResultInfo;
+
+using CaptureResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using HCameraMetadata = frameworks::cameraservice::service::V2_0::CameraMetadata;
+using HCameraDeviceStatus = frameworks::cameraservice::service::V2_0::CameraDeviceStatus;
+using HCameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId;
+using HCameraDeviceUser = frameworks::cameraservice::device::V2_0::ICameraDeviceUser;
+using HCaptureResultExtras = frameworks::cameraservice::device::V2_0::CaptureResultExtras;
+using HCaptureRequest = frameworks::cameraservice::device::V2_0::CaptureRequest;
+using HErrorCode = frameworks::cameraservice::device::V2_0::ErrorCode;
+using HGraphicBufferProducer = hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer;
+using HOutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration;
+using HPhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings;
+using HPhysicalCaptureResultInfo = frameworks::cameraservice::device::V2_0::PhysicalCaptureResultInfo;
+using HSubmitInfo = frameworks::cameraservice::device::V2_0::SubmitInfo;
+using HStatus = frameworks::cameraservice::common::V2_0::Status;
+using HStreamConfigurationMode = frameworks::cameraservice::device::V2_0::StreamConfigurationMode;
+using HTemplateId = frameworks::cameraservice::device::V2_0::TemplateId;
+
+// Note: existing data in dst will be gone. Caller still owns the memory of src
+void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst);
+
+int32_t convertFromHidl(HStreamConfigurationMode streamConfigurationMode);
+
+int32_t convertFromHidl(HTemplateId templateId);
+
+bool convertFromHidl(const HCameraMetadata &src, CameraMetadata *dst);
+
+hardware::camera2::params::OutputConfiguration convertFromHidl(
+    const HOutputConfiguration &hOutputConfiguration);
+
+HCameraDeviceStatus convertToHidlCameraDeviceStatus(int32_t status);
+
+void convertToHidl(const std::vector<hardware::CameraStatus> &src,
+                   hidl_vec<HCameraStatusAndId>* dst);
+
+void convertToHidl(const hardware::camera2::utils::SubmitInfo &submitInfo,
+                   HSubmitInfo *hSubmitInfo);
+
+HErrorCode convertToHidl(int32_t errorCode);
+
+HCaptureResultExtras convertToHidl(const CaptureResultExtras &captureResultExtras);
+
+hidl_vec<HPhysicalCaptureResultInfo> convertToHidl(
+    const std::vector<PhysicalCaptureResultInfo> &physicalCaptureResultInfos,
+    std::shared_ptr<CaptureResultMetadataQueue> &captureResultMetadataQueue);
+
+HStatus B2HStatus(const binder::Status &bStatus);
+
+} // conversion
+} // utils
+} // cameraservice
+} // hardware
+} //android
+
+#endif //CAMERASERVER_CONVERT_TO_HIDL
diff --git a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp
new file mode 100644
index 0000000..d22ba5a
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.cpp
@@ -0,0 +1,263 @@
+/*
+ * 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 <android/hardware/camera/device/3.2/types.h>
+#include <cutils/properties.h>
+#include <gui/Surface.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
+
+#include <hidl/AidlCameraDeviceCallbacks.h>
+#include <hidl/Convert.h>
+#include <hidl/HidlCameraDeviceUser.h>
+#include <android/hardware/camera/device/3.2/types.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace device {
+namespace V2_0 {
+namespace implementation {
+
+using hardware::cameraservice::utils::conversion::convertToHidl;
+using hardware::cameraservice::utils::conversion::convertFromHidl;
+using hardware::cameraservice::utils::conversion::B2HStatus;
+
+using hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer;
+using hardware::hidl_vec;
+using hardware::Return;
+using hardware::Void;
+using HSubmitInfo = device::V2_0::SubmitInfo;
+using hardware::camera2::params::OutputConfiguration;
+
+static constexpr int32_t CAMERA_REQUEST_METADATA_QUEUE_SIZE = 1 << 20 /* 1 MB */;
+static constexpr int32_t CAMERA_RESULT_METADATA_QUEUE_SIZE = 1 << 20 /* 1 MB */;
+
+Return<void> HidlCameraDeviceUser::disconnect() {
+    mDeviceRemote->disconnect();
+    return Void();
+}
+
+HidlCameraDeviceUser::HidlCameraDeviceUser(
+    const sp<hardware::camera2::ICameraDeviceUser> &deviceRemote)
+  : mDeviceRemote(deviceRemote) {
+    mInitSuccess = initDevice();
+}
+
+bool HidlCameraDeviceUser::initDevice() {
+    // TODO: Get request and result metadata queue size from a system property.
+    int32_t reqFMQSize = CAMERA_REQUEST_METADATA_QUEUE_SIZE;
+
+    mCaptureRequestMetadataQueue =
+        std::make_unique<CaptureRequestMetadataQueue>(static_cast<size_t>(reqFMQSize),
+                                                      false /* non blocking */);
+    if (!mCaptureRequestMetadataQueue->isValid()) {
+        ALOGE("%s: invalid request fmq", __FUNCTION__);
+        return false;
+    }
+
+    int32_t resFMQSize = CAMERA_RESULT_METADATA_QUEUE_SIZE;
+    mCaptureResultMetadataQueue =
+        std::make_shared<CaptureResultMetadataQueue>(static_cast<size_t>(resFMQSize),
+                                                     false /* non blocking */);
+    if (!mCaptureResultMetadataQueue->isValid()) {
+        ALOGE("%s: invalid result fmq", __FUNCTION__);
+        return false;
+    }
+    return true;
+}
+
+Return<void> HidlCameraDeviceUser::getCaptureRequestMetadataQueue(
+    getCaptureRequestMetadataQueue_cb _hidl_cb) {
+    if (mInitSuccess) {
+        _hidl_cb(*mCaptureRequestMetadataQueue->getDesc());
+    }
+    return Void();
+}
+
+Return<void> HidlCameraDeviceUser::getCaptureResultMetadataQueue(
+    getCaptureResultMetadataQueue_cb _hidl_cb) {
+    if (mInitSuccess) {
+        _hidl_cb(*mCaptureResultMetadataQueue->getDesc());
+    }
+    return Void();
+}
+
+/**
+ * To be used only by submitRequestList implementation, since it requires
+ * clients to call this method serially, incase fmq is used to send metadata.
+ */
+bool HidlCameraDeviceUser::copyPhysicalCameraSettings(
+    const hidl_vec<HPhysicalCameraSettings> &hPhysicalCameraSettings,
+    std::vector<CaptureRequest::PhysicalCameraSettings> *physicalCameraSettings) {
+    bool converted = false;
+    for (auto &e : hPhysicalCameraSettings) {
+        physicalCameraSettings->emplace_back();
+        CaptureRequest::PhysicalCameraSettings &physicalCameraSetting =
+            physicalCameraSettings->back();
+        physicalCameraSetting.id = e.id.c_str();
+
+        // Read the settings either from the fmq or straightaway from the
+        // request. We don't need any synchronization, since submitRequestList
+        // is guaranteed to be called serially by the client if it decides to
+        // use fmq.
+        if (e.settings.getDiscriminator() ==
+            FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) {
+            /**
+             * Get settings from the fmq.
+             */
+            HCameraMetadata settingsFmq;
+            settingsFmq.resize(e.settings.fmqMetadataSize());
+            bool read = mCaptureRequestMetadataQueue->read(settingsFmq.data(),
+                                                           e.settings.fmqMetadataSize());
+            if (!read) {
+                ALOGE("%s capture request settings could't be read from fmq size",
+                      __FUNCTION__);
+                converted = false;
+            } else {
+                converted = convertFromHidl(settingsFmq, &physicalCameraSetting.settings);
+            }
+        } else {
+            /**
+             * The settings metadata is contained in request settings field.
+             */
+            converted =
+                convertFromHidl(e.settings.metadata(),
+                                &physicalCameraSetting.settings);
+        }
+        if (!converted) {
+          ALOGE("%s: Unable to convert physicalCameraSettings from HIDL to AIDL.", __FUNCTION__);
+          return false;
+        }
+    }
+    return true;
+}
+
+bool HidlCameraDeviceUser::convertRequestFromHidl(const HCaptureRequest &hRequest,
+                                                  CaptureRequest *request) {
+    // No reprocessing support.
+    request->mIsReprocess = false;
+    for (const auto &streamAndWindowId : hRequest.streamAndWindowIds) {
+        request->mStreamIdxList.push_back(streamAndWindowId.streamId);
+        request->mSurfaceIdxList.push_back(streamAndWindowId.windowId);
+    }
+    return copyPhysicalCameraSettings(hRequest.physicalCameraSettings,
+                                      &(request->mPhysicalCameraSettings));
+}
+
+Return<void> HidlCameraDeviceUser::submitRequestList(const hidl_vec<HCaptureRequest>& hRequestList,
+                                                     bool streaming,
+                                                     submitRequestList_cb _hidl_cb) {
+    hardware::camera2::utils::SubmitInfo submitInfo;
+    HSubmitInfo hSubmitInfo;
+    /**
+     * Create AIDL CaptureRequest from requestList and graphicBufferProducers.
+     */
+    std::vector<hardware::camera2::CaptureRequest> requests;
+    for (auto &hRequest : hRequestList) {
+        requests.emplace_back();
+        auto &request = requests.back();
+        if (!convertRequestFromHidl(hRequest, &request)) {
+            _hidl_cb(HStatus::ILLEGAL_ARGUMENT, hSubmitInfo);
+            return Void();
+        }
+    }
+    mDeviceRemote->submitRequestList(requests, streaming, &submitInfo);
+    mRequestId = submitInfo.mRequestId;
+    convertToHidl(submitInfo, &hSubmitInfo);
+    _hidl_cb(HStatus::NO_ERROR, hSubmitInfo);
+    return Void();
+}
+
+Return<void> HidlCameraDeviceUser::cancelRepeatingRequest(cancelRepeatingRequest_cb _hidl_cb) {
+    int64_t lastFrameNumber = 0;
+    binder::Status ret = mDeviceRemote->cancelRequest(mRequestId, &lastFrameNumber);
+    _hidl_cb(B2HStatus(ret), lastFrameNumber);
+    return Void();
+}
+
+Return<HStatus> HidlCameraDeviceUser::beginConfigure() {
+    binder::Status ret = mDeviceRemote->beginConfigure();
+    return B2HStatus(ret);
+}
+
+Return<HStatus> HidlCameraDeviceUser::endConfigure(StreamConfigurationMode operatingMode,
+                                                   const hidl_vec<uint8_t>& sessionParams) {
+    android::CameraMetadata cameraMetadata;
+    if (!convertFromHidl(sessionParams, &cameraMetadata)) {
+        return HStatus::ILLEGAL_ARGUMENT;
+    }
+
+    binder::Status ret = mDeviceRemote->endConfigure(convertFromHidl(operatingMode),
+                                                     cameraMetadata);
+    return B2HStatus(ret);
+}
+
+Return<HStatus> HidlCameraDeviceUser::deleteStream(int32_t streamId) {
+    binder::Status ret = mDeviceRemote->deleteStream(streamId);
+    return B2HStatus(ret);
+}
+
+Return<void> HidlCameraDeviceUser::createStream(const HOutputConfiguration& hOutputConfiguration,
+                                                createStream_cb hidl_cb_) {
+    OutputConfiguration outputConfiguration =
+        convertFromHidl(hOutputConfiguration);
+    int32_t newStreamId = 0;
+    binder::Status ret = mDeviceRemote->createStream(outputConfiguration, &newStreamId);
+    HStatus status = B2HStatus(ret);
+    hidl_cb_(status, newStreamId);
+    return Void();
+}
+
+Return<void> HidlCameraDeviceUser::createDefaultRequest(TemplateId templateId,
+                                                        createDefaultRequest_cb _hidl_cb) {
+    android::CameraMetadata cameraMetadata;
+    binder::Status ret = mDeviceRemote->createDefaultRequest(convertFromHidl(templateId),
+                                                             &cameraMetadata);
+    HStatus hStatus = B2HStatus(ret);
+    HCameraMetadata hidlMetadata;
+    const camera_metadata_t *rawMetadata = cameraMetadata.getAndLock();
+    convertToHidl(rawMetadata, &hidlMetadata);
+    _hidl_cb(hStatus, hidlMetadata);
+    cameraMetadata.unlock(rawMetadata);
+    return Void();
+}
+
+Return<HStatus> HidlCameraDeviceUser::waitUntilIdle() {
+    binder::Status ret = mDeviceRemote->waitUntilIdle();
+    return B2HStatus(ret);
+}
+
+Return<void> HidlCameraDeviceUser::flush(flush_cb _hidl_cb) {
+    int64_t lastFrameNumber = 0;
+    binder::Status ret = mDeviceRemote->flush(&lastFrameNumber);
+    _hidl_cb(B2HStatus(ret),lastFrameNumber);
+    return Void();
+}
+
+Return<HStatus> HidlCameraDeviceUser::updateOutputConfiguration(
+    int32_t streamId,
+    const HOutputConfiguration& hOutputConfiguration) {
+    OutputConfiguration outputConfiguration = convertFromHidl(hOutputConfiguration);
+    binder::Status ret = mDeviceRemote->updateOutputConfiguration(streamId, outputConfiguration);
+    return B2HStatus(ret);
+}
+
+} // implementation
+} // V2_0
+} // device
+} // cameraservice
+} // frameworks
+} // android
diff --git a/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h
new file mode 100644
index 0000000..be8f1d6
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/HidlCameraDeviceUser.h
@@ -0,0 +1,128 @@
+/*
+ * 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_FRAMEWORKS_CAMERADEVICEUSER_V2_0_CAMERADEVICEUSER_H
+#define ANDROID_FRAMEWORKS_CAMERADEVICEUSER_V2_0_CAMERADEVICEUSER_H
+
+#include <mutex>
+#include <memory>
+#include <thread>
+
+#include <android/frameworks/cameraservice/common/2.0/types.h>
+#include <android/frameworks/cameraservice/service/2.0/types.h>
+#include <android/frameworks/cameraservice/device/2.0/ICameraDeviceUser.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+#include <android/hardware/camera2/ICameraDeviceCallbacks.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <CameraService.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace device {
+namespace V2_0 {
+namespace implementation {
+
+using frameworks::cameraservice::device::V2_0::StreamConfigurationMode;
+using hardware::camera2::CaptureRequest;
+using hardware::hidl_vec;
+using hardware::kSynchronizedReadWrite;
+using hardware::MessageQueue;
+using hardware::MQDescriptorSync;
+using hardware::Return;
+using CaptureResultMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using CaptureRequestMetadataQueue = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using TemplateId = frameworks::cameraservice::device::V2_0::TemplateId;
+
+using HCameraDeviceUser = device::V2_0::ICameraDeviceUser;
+using HCameraMetadata = cameraservice::service::V2_0::CameraMetadata;
+using HCaptureRequest = device::V2_0::CaptureRequest;
+using HOutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration;
+using HPhysicalCameraSettings = frameworks::cameraservice::device::V2_0::PhysicalCameraSettings;
+using HStatus = frameworks::cameraservice::common::V2_0::Status;
+
+static constexpr int32_t REQUEST_ID_NONE = -1;
+
+struct HidlCameraDeviceUser final : public HCameraDeviceUser {
+    HidlCameraDeviceUser(const sp<hardware::camera2::ICameraDeviceUser> &deviceRemote);
+
+    ~HidlCameraDeviceUser() { }
+
+    virtual Return<void> disconnect() override;
+
+    virtual Return<void> getCaptureRequestMetadataQueue(
+        getCaptureRequestMetadataQueue_cb _hidl_cb) override;
+
+    virtual Return<void> getCaptureResultMetadataQueue(
+        getCaptureResultMetadataQueue_cb _hidl_cb) override;
+
+    virtual Return<void> submitRequestList(const hidl_vec<HCaptureRequest>& requestList,
+                                           bool streaming, submitRequestList_cb _hidl_cb) override;
+
+    virtual Return<void> cancelRepeatingRequest(cancelRepeatingRequest_cb _hidl_cb) override;
+
+    virtual Return<HStatus> beginConfigure() override;
+
+    virtual Return<HStatus> endConfigure(StreamConfigurationMode operatingMode,
+                                         const hidl_vec<uint8_t>& sessionParams);
+
+    virtual Return<HStatus> deleteStream(int32_t streamId) override;
+
+    virtual Return<void> createStream(const HOutputConfiguration& outputConfiguration,
+                                      createStream_cb _hidl_cb) override;
+
+    Return<void> createDefaultRequest(TemplateId templateId,
+                                      createDefaultRequest_cb _hidl_cb) override;
+
+    virtual Return<HStatus> waitUntilIdle() override;
+
+    virtual Return<void> flush(flush_cb _hidl_cb) override;
+
+    virtual Return<HStatus> updateOutputConfiguration(
+        int32_t streamId, const HOutputConfiguration& outputConfiguration) override;
+
+    bool initStatus() { return mInitSuccess; }
+
+    std::shared_ptr<CaptureResultMetadataQueue> getCaptureResultMetadataQueue() {
+        return mCaptureResultMetadataQueue;
+    }
+
+ private:
+    bool initDevice();
+
+    bool convertRequestFromHidl(const HCaptureRequest &hRequest, CaptureRequest *request);
+
+    bool copyPhysicalCameraSettings(
+        const hidl_vec<HPhysicalCameraSettings> &hPhysicalCameraSettings,
+        std::vector<CaptureRequest::PhysicalCameraSettings> *physicalCameraSettings);
+
+    const sp<hardware::camera2::ICameraDeviceUser> mDeviceRemote;
+    std::unique_ptr<CaptureRequestMetadataQueue> mCaptureRequestMetadataQueue = nullptr;
+    std::shared_ptr<CaptureResultMetadataQueue> mCaptureResultMetadataQueue = nullptr;
+    bool mInitSuccess = false;
+    int32_t mRequestId = REQUEST_ID_NONE;
+};
+
+} // implementation
+} // V2_0
+} // device
+} // cameraservice
+} // frameworks
+} // android
+#endif // ANDROID_FRAMEOWORKS_CAMERADEVICEUSER_V2_0_CAMERADEVICEUSER_H
diff --git a/services/camera/libcameraservice/hidl/HidlCameraService.cpp b/services/camera/libcameraservice/hidl/HidlCameraService.cpp
new file mode 100644
index 0000000..31bdf6d
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/HidlCameraService.cpp
@@ -0,0 +1,260 @@
+/*
+ * 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 <hidl/Convert.h>
+
+#include <hidl/HidlCameraService.h>
+
+#include <hidl/HidlCameraDeviceUser.h>
+#include <hidl/AidlCameraDeviceCallbacks.h>
+#include <hidl/AidlCameraServiceListener.h>
+
+#include <hidl/HidlTransportSupport.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace service {
+namespace V2_0 {
+namespace implementation {
+
+using frameworks::cameraservice::service::V2_0::implementation::HidlCameraService;
+using hardware::hidl_vec;
+using hardware::cameraservice::utils::conversion::convertToHidl;
+using hardware::cameraservice::utils::conversion::B2HStatus;
+using hardware::Void;
+
+using device::V2_0::implementation::H2BCameraDeviceCallbacks;
+using device::V2_0::implementation::HidlCameraDeviceUser;
+using service::V2_0::implementation::H2BCameraServiceListener;
+using HCameraMetadataType = android::frameworks::cameraservice::common::V2_0::CameraMetadataType;
+using HVendorTag = android::frameworks::cameraservice::common::V2_0::VendorTag;
+using HVendorTagSection = android::frameworks::cameraservice::common::V2_0::VendorTagSection;
+
+sp<HidlCameraService> gHidlCameraService;
+
+sp<HidlCameraService> HidlCameraService::getInstance(android::CameraService *cs) {
+    gHidlCameraService = new HidlCameraService(cs);
+    return gHidlCameraService;
+}
+
+Return<void>
+HidlCameraService::getCameraCharacteristics(const hidl_string& cameraId,
+                                            getCameraCharacteristics_cb _hidl_cb) {
+    android::CameraMetadata cameraMetadata;
+    HStatus status = HStatus::NO_ERROR;
+    binder::Status serviceRet =
+        mAidlICameraService->getCameraCharacteristics(String16(cameraId.c_str()), &cameraMetadata);
+    HCameraMetadata hidlMetadata;
+    if (!serviceRet.isOk()) {
+        switch(serviceRet.serviceSpecificErrorCode()) {
+            // No ERROR_CAMERA_DISCONNECTED since we're in the same process.
+            case hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT:
+                ALOGE("%s: Camera ID %s does not exist!", __FUNCTION__, cameraId.c_str());
+                status = HStatus::ILLEGAL_ARGUMENT;
+                break;
+            default:
+                ALOGE("Get camera characteristics from camera service failed: %s",
+                      serviceRet.toString8().string());
+                status = B2HStatus(serviceRet);
+          }
+        _hidl_cb(status, hidlMetadata);
+        return Void();
+    }
+    const camera_metadata_t *rawMetadata = cameraMetadata.getAndLock();
+    convertToHidl(rawMetadata, &hidlMetadata);
+    _hidl_cb(status, hidlMetadata);
+    cameraMetadata.unlock(rawMetadata);
+    return Void();
+}
+
+Return<void> HidlCameraService::connectDevice(const sp<HCameraDeviceCallback>& hCallback,
+                                              const hidl_string& cameraId,
+                                              connectDevice_cb _hidl_cb) {
+    // Here, we first get ICameraDeviceUser from mAidlICameraService, then save
+    // that interface in the newly created HidlCameraDeviceUser impl class.
+    if (mAidlICameraService == nullptr) {
+        _hidl_cb(HStatus::UNKNOWN_ERROR, nullptr);
+        return Void();
+    }
+    sp<hardware::camera2::ICameraDeviceUser> deviceRemote = nullptr;
+    // Create a hardware::camera2::ICameraDeviceCallback object which internally
+    // calls callback functions passed through hCallback.
+    sp<H2BCameraDeviceCallbacks> hybridCallbacks = new H2BCameraDeviceCallbacks(hCallback);
+    if (!hybridCallbacks->initializeLooper()) {
+        ALOGE("Unable to handle callbacks on device, cannot connect");
+        _hidl_cb(HStatus::UNKNOWN_ERROR, nullptr);
+        return Void();
+    }
+    sp<hardware::camera2::ICameraDeviceCallbacks> callbacks = hybridCallbacks;
+    binder::Status serviceRet = mAidlICameraService->connectDevice(
+            callbacks, String16(cameraId.c_str()), String16(""),
+            hardware::ICameraService::USE_CALLING_UID, /*out*/&deviceRemote);
+    HStatus status = HStatus::NO_ERROR;
+    if (!serviceRet.isOk()) {
+        ALOGE("%s: Unable to connect to camera device", __FUNCTION__);
+        status = B2HStatus(serviceRet);
+        _hidl_cb(status, nullptr);
+        return Void();
+    }
+    // Now we create a HidlCameraDeviceUser class, store the deviceRemote in it,
+    // and return that back. All calls on that interface will be forwarded to
+    // the AIDL interface.
+    sp<HidlCameraDeviceUser> hDeviceRemote = new HidlCameraDeviceUser(deviceRemote);
+    if (!hDeviceRemote->initStatus()) {
+        ALOGE("%s: Unable to initialize camera device HIDL wrapper", __FUNCTION__);
+        _hidl_cb(HStatus::UNKNOWN_ERROR, nullptr);
+        return Void();
+    }
+    hybridCallbacks->setCaptureResultMetadataQueue(hDeviceRemote->getCaptureResultMetadataQueue());
+    _hidl_cb(status, hDeviceRemote);
+    return Void();
+}
+
+void HidlCameraService::addToListenerCacheLocked(sp<HCameraServiceListener> hListener,
+                                                 sp<hardware::ICameraServiceListener> csListener) {
+        mListeners.emplace_back(std::make_pair(hListener, csListener));
+}
+
+sp<hardware::ICameraServiceListener>
+HidlCameraService::searchListenerCacheLocked(sp<HCameraServiceListener> hListener,
+                                             bool shouldRemove) {
+    // Go through the mListeners list and compare the listener with the HIDL
+    // listener registered.
+    auto it = mListeners.begin();
+    sp<ICameraServiceListener> csListener = nullptr;
+    for (;it != mListeners.end(); it++) {
+        if (hardware::interfacesEqual(it->first, hListener)) {
+            break;
+        }
+    }
+    if (it != mListeners.end()) {
+        csListener = it->second;
+        if (shouldRemove) {
+          mListeners.erase(it);
+        }
+    }
+    return csListener;
+}
+
+Return<void> HidlCameraService::addListener(const sp<HCameraServiceListener>& hCsListener,
+                                            addListener_cb _hidl_cb) {
+    if (mAidlICameraService == nullptr) {
+        _hidl_cb(HStatus::UNKNOWN_ERROR, {});
+        return Void();
+    }
+    if (hCsListener == nullptr) {
+        ALOGE("%s listener must not be NULL", __FUNCTION__);
+        _hidl_cb(HStatus::ILLEGAL_ARGUMENT, {});
+        return Void();
+    }
+    sp<hardware::ICameraServiceListener> csListener = nullptr;
+    // Check the cache for previously registered callbacks
+    {
+        Mutex::Autolock l(mListenerListLock);
+        csListener = searchListenerCacheLocked(hCsListener);
+        if (csListener == nullptr) {
+            // Wrap an hCsListener with AidlCameraServiceListener and pass it to
+            // CameraService.
+            csListener = new H2BCameraServiceListener(hCsListener);
+            // Add to cache
+            addToListenerCacheLocked(hCsListener, csListener);
+        } else {
+            ALOGE("%s: Trying to add a listener %p already registered",
+                  __FUNCTION__, hCsListener.get());
+            _hidl_cb(HStatus::ILLEGAL_ARGUMENT, {});
+            return Void();
+        }
+    }
+    std::vector<hardware::CameraStatus> cameraStatusAndIds{};
+    binder::Status serviceRet = mAidlICameraService->addListener(csListener, &cameraStatusAndIds);
+    HStatus status = HStatus::NO_ERROR;
+    if (!serviceRet.isOk()) {
+      ALOGE("%s: Unable to add camera device status listener", __FUNCTION__);
+      status = B2HStatus(serviceRet);
+      _hidl_cb(status, {});
+      return Void();
+    }
+    hidl_vec<HCameraStatusAndId> hCameraStatusAndIds;
+    //Convert cameraStatusAndIds to HIDL and call callback
+    convertToHidl(cameraStatusAndIds, &hCameraStatusAndIds);
+    _hidl_cb(status, hCameraStatusAndIds);
+    return Void();
+}
+
+Return<HStatus> HidlCameraService::removeListener(const sp<HCameraServiceListener>& hCsListener) {
+    if (hCsListener == nullptr) {
+        ALOGE("%s listener must not be NULL", __FUNCTION__);
+        return HStatus::ILLEGAL_ARGUMENT;
+    }
+    sp<ICameraServiceListener> csListener = nullptr;
+    {
+        Mutex::Autolock l(mListenerListLock);
+        csListener = searchListenerCacheLocked(hCsListener, /*removeIfFound*/true);
+    }
+    if (csListener != nullptr) {
+          mAidlICameraService->removeListener(csListener);
+    } else {
+        ALOGE("%s Removing unregistered listener %p", __FUNCTION__, hCsListener.get());
+        return HStatus::ILLEGAL_ARGUMENT;
+    }
+    return HStatus::NO_ERROR;
+}
+
+Return<void> HidlCameraService::getCameraVendorTagSections(getCameraVendorTagSections_cb _hidl_cb) {
+    hidl_vec<HVendorTagSection> hVendorTagSections;
+    // TODO: Could this be just created on the stack since we don't set it to
+    //       global cache or anything ?
+    HStatus hStatus = HStatus::NO_ERROR;
+    sp<VendorTagDescriptor> desc = new VendorTagDescriptor();
+    binder::Status serviceRet = mAidlICameraService->getCameraVendorTagDescriptor(desc.get());
+
+    if (!serviceRet.isOk()) {
+        ALOGE("%s: Failed to get VendorTagDescriptor", __FUNCTION__);
+        _hidl_cb(B2HStatus(serviceRet), hVendorTagSections);
+        return Void();
+    }
+
+    const SortedVector<String8>* sectionNames = desc->getAllSectionNames();
+    size_t numSections = sectionNames->size();
+    std::vector<std::vector<HVendorTag>> tagsBySection(numSections);
+    int tagCount = desc->getTagCount();
+    std::vector<uint32_t> tags(tagCount);
+    desc->getTagArray(tags.data());
+    for (int i = 0; i < tagCount; i++) {
+        HVendorTag vt;
+        vt.tagId = tags[i];
+        vt.tagName = desc->getTagName(tags[i]);
+        vt.tagType = (HCameraMetadataType) desc->getTagType(tags[i]);
+        ssize_t sectionIdx = desc->getSectionIndex(tags[i]);
+        tagsBySection[sectionIdx].push_back(vt);
+    }
+    hVendorTagSections.resize(numSections);
+    for (size_t s = 0; s < numSections; s++) {
+        hVendorTagSections[s].sectionName = (*sectionNames)[s].string();
+        hVendorTagSections[s].tags = tagsBySection[s];
+    }
+    _hidl_cb(hStatus, hVendorTagSections);
+    return Void();
+}
+
+} // implementation
+} // V2_0
+} // service
+} // cameraservice
+} // frameworks
+} // android
+
diff --git a/services/camera/libcameraservice/hidl/HidlCameraService.h b/services/camera/libcameraservice/hidl/HidlCameraService.h
new file mode 100644
index 0000000..eead0bc
--- /dev/null
+++ b/services/camera/libcameraservice/hidl/HidlCameraService.h
@@ -0,0 +1,98 @@
+/*
+ * 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_FRAMEWORKS_CAMERASERVICE_V2_0_CAMERASERVICE_H
+#define ANDROID_FRAMEWORKS_CAMERASERVICE_V2_0_CAMERASERVICE_H
+
+#include <mutex>
+#include <thread>
+
+#include <android/frameworks/cameraservice/common/2.0/types.h>
+#include <android/frameworks/cameraservice/service/2.0/ICameraService.h>
+#include <android/frameworks/cameraservice/service/2.0/types.h>
+#include <android/frameworks/cameraservice/device/2.0/types.h>
+
+#include <hidl/Status.h>
+
+#include <CameraService.h>
+
+namespace android {
+namespace frameworks {
+namespace cameraservice {
+namespace service {
+namespace V2_0 {
+namespace implementation {
+
+using hardware::hidl_string;
+using hardware::ICameraServiceListener;
+using hardware::Return;
+
+using HCameraDeviceCallback = frameworks::cameraservice::device::V2_0::ICameraDeviceCallback;
+using HCameraMetadata = frameworks::cameraservice::service::V2_0::CameraMetadata;
+using HCameraService = frameworks::cameraservice::service::V2_0::ICameraService;
+using HCameraServiceListener = frameworks::cameraservice::service::V2_0::ICameraServiceListener;
+using HStatus = frameworks::cameraservice::common::V2_0::Status;
+using HCameraStatusAndId = frameworks::cameraservice::service::V2_0::CameraStatusAndId;
+
+struct HidlCameraService final : public HCameraService {
+
+    ~HidlCameraService() { };
+
+    // Methods from ::android::frameworks::cameraservice::service::V2.0::ICameraService follow.
+
+    Return<void> connectDevice(const sp<HCameraDeviceCallback>& callback,
+                               const hidl_string& cameraId, connectDevice_cb _hidl_cb) override;
+
+    Return<void> addListener(const sp<HCameraServiceListener>& listener,
+                             addListener_cb _hidl_cb) override;
+
+    Return<HStatus> removeListener(const sp<HCameraServiceListener>& listener) override;
+
+    Return<void> getCameraCharacteristics(const hidl_string& cameraId,
+                                          getCameraCharacteristics_cb _hidl_cb) override;
+
+    Return<void> getCameraVendorTagSections(getCameraVendorTagSections_cb _hidl_cb) override;
+
+    // This method should only be called by the cameraservers main thread to
+    // instantiate the hidl cameraserver.
+    static sp<HidlCameraService> getInstance(android::CameraService *cs);
+
+private:
+    HidlCameraService(android::CameraService *cs) : mAidlICameraService(cs) { };
+
+    sp<hardware::ICameraServiceListener> searchListenerCacheLocked(
+        sp<HCameraServiceListener> listener, /*removeIfFound*/ bool shouldRemove = false);
+
+    void addToListenerCacheLocked(sp<HCameraServiceListener> hListener,
+                                  sp<hardware::ICameraServiceListener> csListener);
+
+    android::CameraService *const mAidlICameraService = nullptr;
+
+    Mutex mListenerListLock;
+
+    using HIListeners =
+        std::pair<sp<HCameraServiceListener>, sp<ICameraServiceListener>>;
+    std::list<HIListeners> mListeners;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace service
+}  // namespace cameraservice
+}  // namespace frameworks
+}  // namespace android
+
+#endif  // ANDROID_FRAMEWORKS_CAMERASERVICE_V2_0_CAMERASERVICE_H
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
index e870965..227a29d 100644
--- a/services/mediadrm/Android.mk
+++ b/services/mediadrm/Android.mk
@@ -29,7 +29,8 @@
     libhidlmemory \
     libhidltransport \
     android.hardware.drm@1.0 \
-    android.hardware.drm@1.1
+    android.hardware.drm@1.1 \
+    android.hardware.drm@1.2
 
 LOCAL_CFLAGS += -Wall -Wextra -Werror
 
diff --git a/services/soundtrigger/SoundTriggerHalHidl.cpp b/services/soundtrigger/SoundTriggerHalHidl.cpp
index 0f9aa15..1d37a8e 100644
--- a/services/soundtrigger/SoundTriggerHalHidl.cpp
+++ b/services/soundtrigger/SoundTriggerHalHidl.cpp
@@ -356,8 +356,7 @@
     return hidlReturn;
 }
 
-int SoundTriggerHalHidl::getModelState(sound_model_handle_t handle,
-                                       struct sound_trigger_recognition_event** event)
+int SoundTriggerHalHidl::getModelState(sound_model_handle_t handle)
 {
     sp<ISoundTriggerHw> soundtrigger = getService();
     if (soundtrigger == 0) {
@@ -377,24 +376,13 @@
     }
 
     int ret = NO_ERROR;
-    Return<void> hidlReturn;
+    Return<int32_t> hidlReturn(0);
     {
         AutoMutex lock(mHalLock);
-        hidlReturn = soundtrigger_2_2->getModelState(
-            model->mHalHandle,
-            [&](int r, const V2_0_ISoundTriggerHwCallback::RecognitionEvent& halEvent) {
-              ret = r;
-              if (ret != 0) {
-                  ALOGE("getModelState returned error code %d", ret);
-              } else {
-                  *event = convertRecognitionEventFromHal(&halEvent);
-              }
-            });
+        hidlReturn = soundtrigger_2_2->getModelState(model->mHalHandle);
     }
     if (!hidlReturn.isOk()) {
         ALOGE("getModelState error %s", hidlReturn.description().c_str());
-        free(*event);
-        *event = nullptr;
         ret = FAILED_TRANSACTION;
     }
     return ret;
diff --git a/services/soundtrigger/SoundTriggerHalHidl.h b/services/soundtrigger/SoundTriggerHalHidl.h
index 3f4bec3..fb9e39e 100644
--- a/services/soundtrigger/SoundTriggerHalHidl.h
+++ b/services/soundtrigger/SoundTriggerHalHidl.h
@@ -96,12 +96,12 @@
         virtual int stopAllRecognitions();
 
         /* Get the current state of a given model.
-         * Returns 0 or an error code. If successful it also sets indicated the event pointer
-         * and expectes that the caller will free the memory.
+         * Returns 0 or an error code. If successful the state will be returned asynchronously
+         * via a recognition event in the callback method that was registered in the
+         * startRecognition() method.
          * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_2 or above.
          */
-        virtual int getModelState(sound_model_handle_t handle,
-                                  struct sound_trigger_recognition_event** event);
+        virtual int getModelState(sound_model_handle_t handle);
 
         // ISoundTriggerHwCallback
         virtual ::android::hardware::Return<void> recognitionCallback(
diff --git a/services/soundtrigger/SoundTriggerHalInterface.h b/services/soundtrigger/SoundTriggerHalInterface.h
index 076ca23..0183ece 100644
--- a/services/soundtrigger/SoundTriggerHalInterface.h
+++ b/services/soundtrigger/SoundTriggerHalInterface.h
@@ -72,12 +72,12 @@
         virtual int stopAllRecognitions() = 0;
 
         /* Get the current state of a given model.
-         * Returns 0 or an error code. If successful it also sets indicated the event pointer
-         * and expectes that the caller will free the memory.
+         * Returns 0 or an error code. If successful the state will be returned asynchronously
+         * via a recognition event in the callback method that was registered in the
+         * startRecognition() method.
          * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_2 or above.
          */
-        virtual int getModelState(sound_model_handle_t handle,
-                                  struct sound_trigger_recognition_event** event) = 0;
+        virtual int getModelState(sound_model_handle_t handle) = 0;
 
 protected:
         SoundTriggerHalInterface() {}
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 79e9e88..7915068 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -717,8 +717,7 @@
     return NO_ERROR;
 }
 
-status_t SoundTriggerHwService::Module::getModelState(sound_model_handle_t handle,
-                                                      sp<IMemory>& eventMemory)
+status_t SoundTriggerHwService::Module::getModelState(sound_model_handle_t handle)
 {
     ALOGV("getModelState() model handle %d", handle);
     if (mHalInterface == 0) {
@@ -734,21 +733,7 @@
         return INVALID_OPERATION;
     }
 
-    if (model->mType != SOUND_MODEL_TYPE_GENERIC) {
-        return BAD_VALUE;
-    }
-
-    struct sound_trigger_recognition_event* event = nullptr;
-    status_t status = mHalInterface->getModelState(handle, &event);
-    if (status == NO_ERROR) {
-        sp<SoundTriggerHwService> service;
-        service = mService.promote();
-        if (service != 0) {
-            eventMemory = service->prepareRecognitionEvent(event);
-        }
-        free(event);
-    }
-    return status;
+    return mHalInterface->getModelState(handle);
 }
 
 void SoundTriggerHwService::Module::onCallbackEvent(const sp<CallbackEvent>& event)
@@ -784,7 +769,10 @@
             }
 
             recognitionEvent->capture_session = model->mCaptureSession;
-            model->mState = Model::STATE_IDLE;
+            // Don't reset the model state if this recognition event is a get-state response
+            if (recognitionEvent->status != RECOGNITION_STATUS_GET_STATE_RESPONSE) {
+                model->mState = Model::STATE_IDLE;
+            }
             clients.add(model->mModuleClient);
         }
     } break;
@@ -1052,8 +1040,7 @@
     return module->stopRecognition(handle);
 }
 
-status_t SoundTriggerHwService::ModuleClient::getModelState(sound_model_handle_t handle,
-                                                            sp<IMemory>& eventMemory)
+status_t SoundTriggerHwService::ModuleClient::getModelState(sound_model_handle_t handle)
 {
     ALOGV("getModelState() model handle %d", handle);
     if (!captureHotwordAllowed(IPCThreadState::self()->getCallingPid(),
@@ -1065,7 +1052,7 @@
     if (module == 0) {
         return NO_INIT;
     }
-    return module->getModelState(handle, eventMemory);
+    return module->getModelState(handle);
 }
 
 void SoundTriggerHwService::ModuleClient::setCaptureState_l(bool active)
diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h
index c222cd9..4258ec0 100644
--- a/services/soundtrigger/SoundTriggerHwService.h
+++ b/services/soundtrigger/SoundTriggerHwService.h
@@ -122,8 +122,7 @@
        virtual status_t startRecognition(sound_model_handle_t handle,
                                          const sp<IMemory>& dataMemory);
        virtual status_t stopRecognition(sound_model_handle_t handle);
-       virtual status_t getModelState(sound_model_handle_t handle,
-                                      sp<IMemory>& eventMemory);
+       virtual status_t getModelState(sound_model_handle_t handle);
 
        sp<SoundTriggerHalInterface> halInterface() const { return mHalInterface; }
        struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
@@ -171,8 +170,7 @@
        virtual status_t startRecognition(sound_model_handle_t handle,
                                          const sp<IMemory>& dataMemory);
        virtual status_t stopRecognition(sound_model_handle_t handle);
-       virtual status_t getModelState(sound_model_handle_t handle,
-                                      sp<IMemory>& eventMemory);
+       virtual status_t getModelState(sound_model_handle_t handle);
 
        virtual status_t dump(int fd, const Vector<String16>& args);
 
diff --git a/soundtrigger/ISoundTrigger.cpp b/soundtrigger/ISoundTrigger.cpp
index 32882f1..f5b4b59 100644
--- a/soundtrigger/ISoundTrigger.cpp
+++ b/soundtrigger/ISoundTrigger.cpp
@@ -114,8 +114,7 @@
         return status;
     }
 
-    virtual status_t getModelState(sound_model_handle_t handle,
-                                   sp<IMemory>& eventMemory)
+    virtual status_t getModelState(sound_model_handle_t handle)
     {
         Parcel data, reply;
         data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
@@ -123,9 +122,6 @@
         status_t status = remote()->transact(GET_MODEL_STATE, data, &reply);
         if (status == NO_ERROR) {
             status = (status_t)reply.readInt32();
-            if (status == NO_ERROR) {
-                eventMemory = interface_cast<IMemory>(reply.readStrongBinder());
-            }
         }
         return status;
     }
@@ -192,14 +188,7 @@
             status_t status = UNKNOWN_ERROR;
             status_t ret = data.read(&handle, sizeof(sound_model_handle_t));
             if (ret == NO_ERROR) {
-                sp<IMemory> eventMemory;
-                status = getModelState(handle, eventMemory);
-                if (eventMemory != NULL) {
-                    ret = reply->writeStrongBinder(
-                        IInterface::asBinder(eventMemory));
-                } else {
-                    ret = NO_MEMORY;
-                }
+                status = getModelState(handle);
             }
             reply->writeInt32(status);
             return ret;
diff --git a/soundtrigger/SoundTrigger.cpp b/soundtrigger/SoundTrigger.cpp
index bb0650f..d1eb367 100644
--- a/soundtrigger/SoundTrigger.cpp
+++ b/soundtrigger/SoundTrigger.cpp
@@ -188,14 +188,13 @@
     return mISoundTrigger->stopRecognition(handle);
 }
 
-status_t SoundTrigger::getModelState(sound_model_handle_t handle,
-                                     sp<IMemory>& eventMemory)
+status_t SoundTrigger::getModelState(sound_model_handle_t handle)
 {
     Mutex::Autolock _l(mLock);
     if (mISoundTrigger == 0) {
         return NO_INIT;
     }
-    return mISoundTrigger->getModelState(handle, eventMemory);
+    return mISoundTrigger->getModelState(handle);
 }
 
 // BpSoundTriggerClient