[automerger skipped] RESTRICT AUTOMERGE: Camera: fix use after free in sensor timestamp am: 1859a38c4d -s ours

am skip reason: subject contains skip directive

Change-Id: I5f37c2d4a6ec2854b6a2e5337c2a009817983e4d
diff --git a/Android.bp b/Android.bp
deleted file mode 100644
index e4f12c8..0000000
--- a/Android.bp
+++ /dev/null
@@ -1,7 +0,0 @@
-subdirs = [
-    "camera",
-    "drm/*",
-    "media/*",
-    "services/*",
-    "soundtrigger",
-]
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 793cbf4..e584ffb 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -81,6 +81,7 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libstagefright_xmlparser@1.0.so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libstagefright_soft_*)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk/libstagefright_soft_*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicyengineconfig*)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/apex/Android.bp b/apex/Android.bp
new file mode 100644
index 0000000..42a620b
--- /dev/null
+++ b/apex/Android.bp
@@ -0,0 +1,121 @@
+// 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_defaults {
+    name: "com.android.media-defaults",
+    java_libs: ["updatable-media"],
+    multilib: {
+        first: {
+            // Extractor process runs only with the primary ABI.
+            native_shared_libs: [
+                // Extractor plugins
+                "libaacextractor",
+                "libamrextractor",
+                "libflacextractor",
+                "libmidiextractor",
+                "libmkvextractor",
+                "libmp3extractor",
+                "libmp4extractor",
+                "libmpeg2extractor",
+                "liboggextractor",
+                "libwavextractor",
+            ],
+        },
+    },
+    prebuilts: [
+        "mediaextractor.policy",
+    ],
+    key: "com.android.media.key",
+    certificate: ":com.android.media.certificate",
+
+    // Use a custom AndroidManifest.xml used for API targeting.
+    androidManifest: ":com.android.media-androidManifest",
+}
+
+apex {
+    name: "com.android.media",
+    manifest: "manifest.json",
+    defaults: ["com.android.media-defaults"],
+}
+
+filegroup {
+    name: "com.android.media-androidManifest",
+    srcs: ["AndroidManifest-media.xml"],
+}
+
+filegroup {
+    name: "com.android.media.swcodec-androidManifest",
+    srcs: ["AndroidManifest-swcodec.xml"],
+}
+
+apex_defaults {
+    name: "com.android.media.swcodec-defaults",
+    binaries: [
+        "mediaswcodec",
+    ],
+    prebuilts: [
+        "com.android.media.swcodec-mediaswcodec.rc",
+        "com.android.media.swcodec-ld.config.txt",
+        "mediaswcodec.policy",
+        "mediaswcodec.xml",
+    ],
+    use_vendor: true,
+    key: "com.android.media.swcodec.key",
+    certificate: ":com.android.media.swcodec.certificate",
+
+    // Use a custom AndroidManifest.xml used for API targeting.
+    androidManifest: ":com.android.media.swcodec-androidManifest",
+}
+
+prebuilt_etc {
+    name: "com.android.media.swcodec-mediaswcodec.rc",
+    src: "mediaswcodec.rc",
+    filename: "init.rc",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "com.android.media.swcodec-ld.config.txt",
+    src: "ld.config.txt",
+    filename: "ld.config.txt",
+    installable: false,
+}
+
+apex {
+    name: "com.android.media.swcodec",
+    manifest: "manifest_codec.json",
+    defaults: ["com.android.media.swcodec-defaults"],
+}
+
+apex_key {
+    name: "com.android.media.key",
+    public_key: "com.android.media.avbpubkey",
+    private_key: "com.android.media.pem",
+}
+
+apex_key {
+    name: "com.android.media.swcodec.key",
+    public_key: "com.android.media.swcodec.avbpubkey",
+    private_key: "com.android.media.swcodec.pem",
+}
+
+android_app_certificate {
+    name: "com.android.media.certificate",
+    certificate: "com.android.media",
+}
+
+android_app_certificate {
+    name: "com.android.media.swcodec.certificate",
+    certificate: "com.android.media.swcodec",
+}
diff --git a/apex/AndroidManifest-media.xml b/apex/AndroidManifest-media.xml
new file mode 100644
index 0000000..1af4586
--- /dev/null
+++ b/apex/AndroidManifest-media.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  package="com.android.media">
+  <!-- APEX does not have classes.dex -->
+  <application android:hasCode="false" />
+  <!-- Setting maxSdk to lock the module to Q. minSdk is auto-set by build system -->
+  <uses-sdk
+      android:maxSdkVersion="29"
+      android:targetSdkVersion="29"
+  />
+</manifest>
diff --git a/apex/AndroidManifest-swcodec.xml b/apex/AndroidManifest-swcodec.xml
new file mode 100644
index 0000000..de50864
--- /dev/null
+++ b/apex/AndroidManifest-swcodec.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  package="com.android.media.swcodec">
+  <!-- APEX does not have classes.dex -->
+  <application android:hasCode="false" />
+  <!-- Setting maxSdk to lock the module to Q. minSdk is auto-set by build system -->
+  <uses-sdk
+      android:maxSdkVersion="29"
+      android:targetSdkVersion="29"
+  />
+</manifest>
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/TEST_MAPPING b/apex/TEST_MAPPING
new file mode 100644
index 0000000..a2e98cc
--- /dev/null
+++ b/apex/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "system/apex/tests"
+    }
+  ]
+}
diff --git a/apex/com.android.media.avbpubkey b/apex/com.android.media.avbpubkey
new file mode 100644
index 0000000..c0c8fd3
--- /dev/null
+++ b/apex/com.android.media.avbpubkey
Binary files differ
diff --git a/apex/com.android.media.pem b/apex/com.android.media.pem
new file mode 100644
index 0000000..8daa50e
--- /dev/null
+++ b/apex/com.android.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/apex/com.android.media.pk8 b/apex/com.android.media.pk8
new file mode 100644
index 0000000..6df741e
--- /dev/null
+++ b/apex/com.android.media.pk8
Binary files differ
diff --git a/apex/com.android.media.swcodec.avbpubkey b/apex/com.android.media.swcodec.avbpubkey
new file mode 100644
index 0000000..0459723
--- /dev/null
+++ b/apex/com.android.media.swcodec.avbpubkey
Binary files differ
diff --git a/apex/com.android.media.swcodec.pem b/apex/com.android.media.swcodec.pem
new file mode 100644
index 0000000..e379cd3
--- /dev/null
+++ b/apex/com.android.media.swcodec.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEAvB6lOEEOR58KMcFlayEZjsXuPgcfHi/OPxcvfpiGLCpOcK3l
+OPChWUvDRcIHMB7BO+Csfxs0HgsHvvZfNyoNIm99WcjFbboiO7WrBBArIszPr14X
+cfY2NxzT9LBj+EqAnbdL+4OQW1/npLHNE0qtDcxL71ipbjSuKNb58E9qGL0KwvkS
+fwwueWj++bg/iz7dq0mz7iKpnxYscNm4RhJjqcG2Usmg4Ejhr8h5UmOUmTJbObC/
+vzClXQqeeuzS8NTtGVgQ/CI9gC2WN9upq2p/2T7P4U3o3CWvBytUoLKR0UyC83ey
+S8XJgOa42uWR3T/eJOI1ZS4H6Srg1o2XC8Yb8EprFI/NM6/+/5DX/FgDimsslAP6
+Qq4+pSte9v/FjWGqy7QBQaefFRGRuS63xHcSZhXC9J2CFdnxo8+65QT8r4yfQEei
+Ax/0Q94yB7VIL1pIJxHEonKjtd3iKdFEKQzADJ3edsmtHybERdJKCxcm9QIrDCsC
+4YVT9nX0OoC9RD5d5EVD7W5I6eEnRu6igrKIKgUVppeYFQNZD+o+eiNJa4yoggRl
+h8sT0/xLKjMRxAQ7fafi1j+LB2O7UgJmIDDAnidq8Aoz7h3pNi139rNWrjNfwhus
+nMcZvL9dTVv26JWeESHP/zAfAX7j1rfkDwR02ocRLquwEUs1+UHA27Wi1bMCAwEA
+AQKCAgAO3PT83tb7/arWh4s1zaWxTB4otHNW9Tv8bB6KiA6Bys3rxTGJMCnvXjcN
+eekLekKWMoguer3BaemwwtJ/D0l+YQSsZVqD8uLliNL8PTLLSxdVqb98d5GNBTAR
+8yXS5kAHNgZA1wI+1fL9ZjbnwUyu/Gc7f+vTE0J3Y5TX0c52KemBwiAd/Z5mZU9P
+96i9nbfx4p7ev8pbEWttdyZCEw3gybdYDyowzlFWjCZZxhhlij7+7eIYfwVxtncT
+C0cXVBtvly+wXBwz1mY5/5cGPiHfzkCqcndlfWy4ykmjcLhoqvzls51Ys0Xac2BD
+m0PNEVDB5UWGuv5RA9xD12gJvBtU3D7ggMw6C5RcXJT+jSYmSFtD2klWi+It8A/N
+Hv42soKskt6JqYAWE1cvJ3PEqH9ASEJNq2R0Z/PmuM000UJyzU9KId3SNwjXA1Xc
+Kn9hRga4uf9elHTjkTDt79/8+Xv7hjer9sF/S/np7g04rUjIWkuFkC/7NK2tQSh1
+mljV1sD9SF4DPfVK75LwJJaQUlI7TtGd5KJ7FzZwvb+w8ODrpW3hkt6FcI6KwE/a
+QT1T2Z9DknXJSYNdWGrj6vMHsYMiyz8IdAHSCrOB6eXCQxpitbn6W71Raw7f9UaZ
+VDK5AhTU493hkGj1no4cJwecXInMigg/c4ywk2Ibh6IV8O0nkQKCAQEA2+dQQ0IF
+vvVgmQ2WxHBD7M2mbOyf43YBY6Ka6oPBNGPVpZE8X8LoTavQLV+SgCkH8T6gY7XS
+5L4Ze0JFxfua0o1rm6+L2XrOx5F/A2Y40YcPclEik5h1woSwH/J1iHGiEhY8Nqeu
+9GCvjQojkgXx/Rn3Nz+lpvZ329O3H85RWWGF0l60RwLOkig0ZwUb619t8affmGIl
+sxdDv2nfy7OtJX8iGDua7Kf64dvVWQKKtACWkARrlkcWX3uoESxkpSDxue+z7ndH
+o7uHLfM8Tx+Rn+QvYWuRW5TPLbEDMbIYrX65ISt2r/T7v/04XdAC8YpCQRytlqPI
+fpDm15htyHBizQKCAQEA2v+5otRoY56JduISaVGlsJ/IbvKzDAIyWmM14EpRCR5E
+lu+MpTcRAiFQAbHGXr1tMlTFdVFD090WAzIKaKtADFVLXobIHeGspnRCq5OpVp9W
+RvLtVwLxflHAc2yN9/LNtnBqHUgt+S01LBPElybdGHQRTtqAKXhkp31sM21O34Go
+Pri/IxgupWxykMaW44Kig1Cr5DKvc8cwUsGuyDdJm8oBQeNPTMWqSnXtqoTWSaYg
+2kxiMTFokrkSXgufb8wng6OXt/QelywrhG3hAsldPO3GdKidDSxhWZSgpUXXFdAX
+y4GO0IcRJBF/WJtYTYtR+l84nQA2/1Ye4ujFlT0afwKCAQEAmXrXpSm2cvI2CnzW
+hqJIdkWOa6W3bn1VOOIrt5Rfy54GZnl4pumVU2igcpqq2HJKzdDFBvLHj8kyZbn6
+ktUp2NzFhzK9q/uvyNA+0vOMoojeeg4w0MzvG+WaO6Hw8FtHH9KPEiJ01LGKtSin
+bOpjXCC8T75HcsHBJBefTz6jvnt3eD2LG6jU3mPbNy/0rZG8XZaqU2PlJhsNuNI/
+VaBBL9OMy1cGqTgQvYS+YlKI1ls2uqurH4bcEaZvxhSy5iGZNQodDkoIITnofmSu
+6haBgBQ2EYuPN1kkRKKwNQY1fRneQk1gmCynbPdiWO+urkCuP12xtlr3u4aM51rG
+/Meb3QKCAQEA12SxZm9XhLOHLIBJ74A4YLGm50iZxXPbpn7xnHo7naZBe9p8EHtK
+pTeygxggrUnOPrSVyT92YMiQP/BVwIC+a+LwUDZsWMd/ke/DKxH+eY4Zw4pm2S+x
+6bXqfRwFvhr3LTr/g3FcljlalNGUh73Xs5dk9pN9fkxFY16+rw4Rh0709Uur4o6E
+QnuZar+H5Ji10kXj6nvXiR4ebybEC3QlV66k8fLqKe44AShf61jfkmxs34hFA3E/
+EyAn6ouv8rtvGdArBuh5teHho0yXBLCcnbKXgGHepfhCf2LpZeR9GZ0j6iqxFnPh
+7gGvqKyReyNOK9y/x9tQPG6tzit3OcNxbQKCAQEAuDheDOSkXPopXMvLAKOHBIdZ
+rbQ7oFTDHt59gbucM7U8/l2YtaxuKOQFBLnzQa/amIkkrtklYwOsz8E24d7Nm39w
+ykLHwX0XrmjAm6M4XKmDv66a+kSnSV6LEbKZdjvXP02DV+tGeZ5VsnNvJDquxMsD
+fvRTspB8j8CpU96szekxl/tCRhqbdw/4kVTSj5BF++OaRRcJrAyj1B2qf1ynAZE1
+gUvVPkEYa914zcrxg9XIXT4M7yqB7i8KJegOtOtcWjJ7uTiP+638AvygJLMJnSrV
++HjFZWG6P9btZmLHSEBRvwGOAilp0qejXo866l0fmnlGy7ehKz8u3PzvnlPYjQ==
+-----END RSA PRIVATE KEY-----
diff --git a/apex/com.android.media.swcodec.pk8 b/apex/com.android.media.swcodec.pk8
new file mode 100644
index 0000000..05a4216
--- /dev/null
+++ b/apex/com.android.media.swcodec.pk8
Binary files differ
diff --git a/apex/com.android.media.swcodec.x509.pem b/apex/com.android.media.swcodec.x509.pem
new file mode 100644
index 0000000..67b9b4f
--- /dev/null
+++ b/apex/com.android.media.swcodec.x509.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF3jCCA8agAwIBAgIJAIM72JpD4v6XMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD
+VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g
+VmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEiMCAGA1UE
+AwwZY29tLmFuZHJvaWQubWVkaWEuc3djb2RlYzAgFw0xOTAyMTEwMjExMTFaGA80
+NzU3MDEwNzAyMTExMVowgYIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9y
+bmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAw
+DgYDVQQLDAdBbmRyb2lkMSIwIAYDVQQDDBljb20uYW5kcm9pZC5tZWRpYS5zd2Nv
+ZGVjMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsqXE0AIWpLW9Tgq2
+nQGph7KZ6L2Q9oxviqCVHxIaPqfhM2SwTbycADIQeqrrlRxhddVkjLuMUkJa7mev
+fERmgpiOfnPIlGK6PTs2gljCkskZhF3bgfeyuHt0tsYO+UaN8MVoZD7/QdiE46w2
+OMDClG1UqgiqOBhLTEN/cHXObnUiiVXUYqN8aYZf6L6Fs3yQi2ZZgfbxTVFewqdv
+aLLOqCYnVYXZH+ZxbXESA0M+WXKgRKsYTj2GYs3eko1rFi4Y6uHVLx45yaoT5u/i
+SxPEkocyMCKvGJWu4XlSOd3EjSOMaqCOYVyGLxdlnQWQU7PZDqBSJ0SysWgpFHpB
+I15c2jhRdXOCfQ9ZtDfPZkE0a2A8kJDAoF1mzTp6IvBAWUsl5nHPw5CWkFpNad/h
+tqqGCScWbiKZuvrQ4/RQNm3f1K+mxX9TrjFigpqNO6d4pGAo1fa6sHR3xWPw/myq
+h5ZJjVnXU5Yq64S4xWOssfjpOg7RfNuvzuk3ok3MYs1mbx3vhZOj5km1f3qrgX9c
+mXjYnyXD0jJBm4uAJWXLdK9PlZvlXbztMCzYj832Io4pFLCtSxkzX75t1em36Nv0
+mNp6NtSSy6SFSq8l7IsXV2FNyUiyHWxS/UQm8pYg5Q5dWHvEEF78P6lV0wRa6FQl
+BBSgpqTAI092KIjDDtB7GQCgV5ECAwEAAaNTMFEwHQYDVR0OBBYEFAFIdFTDEDft
+ewSSAS7Fa3OZ5TXzMB8GA1UdIwQYMBaAFAFIdFTDEDftewSSAS7Fa3OZ5TXzMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAC5e3zXythJCGmz1FmAV
+8Y/UI+Glg6G0x/k04WaRG0DPLLjlJ1F0LM1/IReBSgXcYAL0CAgPycf/rGPOgMFm
+tQxYyjBUxKdjpIqU5DJoV1feanGveIRpto1YRKNgHuzG9rZGR4AgPnt6X4Yxlq04
+lI7QpWadXe1myARJhj3niSNY9+2wEInkx4ZuCO1LtIGqnbdc8jQ8YoVqIE5N4kuM
+ccyPYgsdABtopbjN92rueu8sfF8R6ROy+tNgb6OjpAAevtnBfZ2LXqfObKirHCK+
+k6w4WSB1UUoZ3Xgz8sJtXgokvYeInkN8tHuTagHYU2VQTcA0rdBGMN/1OljJpWlN
+0UUq4fAYU6cN4lHxr2LM9If4WvAzdLAWvaIZrDqaU4i/zYT9l6rR4lC2KW3EHWov
+nPXfgEJJ8AP1iRGibvew3i3SB6XTWFQYTUIBeJfDz/KDXQabP+yzXWISdZCUMUpx
+f+Raqsb5MoKaJdVgnSL0mBunjCyJDzzg34J7oGx6/BnwoiOrwLN4Qaz5U8jbrPSx
+p9LfleCcO7ZdeE8GKqx0X1T4d7tradtmxOS8Iwr4niskkHGRkzozvVvuyGKmoN2k
+162Vfjq+ddj7qEpSh3BS6hHU+vlMbC9L0trGxPxFEAHDrwu0KwGNduTkiu/3jvfB
+JTgH8P9mD1loYxRdo+vet8eQ
+-----END CERTIFICATE-----
diff --git a/apex/com.android.media.x509.pem b/apex/com.android.media.x509.pem
new file mode 100644
index 0000000..e7908fa6
--- /dev/null
+++ b/apex/com.android.media.x509.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFzDCCA7SgAwIBAgIJAO05DBBusaaLMA0GCSqGSIb3DQEBCwUAMHoxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+aWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRowGAYDVQQD
+DBFjb20uYW5kcm9pZC5tZWRpYTAgFw0xOTAxMjUxNzE3MTdaGA80NzU2MTIyMTE3
+MTcxN1owejELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNV
+BAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0Fu
+ZHJvaWQxGjAYBgNVBAMMEWNvbS5hbmRyb2lkLm1lZGlhMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAmNkVxUbp/bLbeGbvKqYXzwBycSDpmOhh///lNGYQ
+/AMUD0q6EaZzU2bd4aL0rOGqfoYlhKd0kMVmMUmfdE9ODAfKxleEeEaRl2GJS8a9
+ABi770l3GHbB2xMI2sEWeOD9xsPFF6+ByPZmoUuNhMr4pUbXsDpE3h8ljrgXHtIg
+bh7ofbvddruwBV0lS1k9OZ9jPVGhEKkJnhgQa67cwgdjizAMbI0Dcz9gtMMawsDj
+Z2aQd1r+vxgh1/XkI/NMmXCnG2ERytXcJeC5S4gEtHfTTPoP0FuVgSB6y6dalMuZ
+F0NBZw8Mvgdy3QJip0uNa36J63CMZKTJWbTdlFpPL2hk0PgaYvje8C5Xtk5282wT
+dMocc8n2zIXbzbnSXGvjcNZib3Pfu55YUnX6eTqZ1BxlJ0FHZAsC4quFFWXxYBYD
+LCRoNNFEtIDQpuvuHF2DuHNDULpAQjy2y6+7eot0KEsVoDmZ4H8BpuAVVu2SxYNb
+gYflR9SmM0tmYeAcRT48q3xrocGyEHMqvgQRUpPfvct/8l8xVcDzOI/sJVDqmYzM
+u0Cj3fkSypGDJOMF/esFSmVvoI01tS7kaNS5vvtKYib//xqKRC9f0dCsGfFLnuUK
+o4KYbYWYwMyJqEd/5/ZvXyKIPAEeJL174L9+wTkc3cQpoBwJN4t+2E5MnhOEq6do
+5L0CAwEAAaNTMFEwHQYDVR0OBBYEFHjNK/GZko1RdZp+8iavWXL5xz9wMB8GA1Ud
+IwQYMBaAFHjNK/GZko1RdZp+8iavWXL5xz9wMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
+KoZIhvcNAQELBQADggIBACmPQMksuLrNV1vbI44S1f70I0FHdBxchFGB39zuLbcn
+SsYom/LPtYJiD0Dl4bB4eb+ZnxkQP2XeP6pycmUH2j1EWexFwvdUvlfe8Qz+wAec
+ap4AxiX4Z2Ke2ivYotIZFUHdZOLkX20js8Wex1mzY43MLQn5APl9gK1VZTxDggeR
+EObH1S+JVjGwQqYZj2e6gNZH34Q25NQ698RL85GDkYtSISAifJtaJsU/B3vKm82I
+k9xMiCooCH6bRdGHG1jze4SRpidjxEm8cxkiaQagfcuXeCLziXJr3qAMKYiEY6bp
+0+bAqCt3S8OrrN3RQZfQrnlwitsM1jJJ/+C+WoDg4eY5AFrXDLvNeKh1qO/f8xv+
+fCXkQPcVVphLfRH9oxNrSgOWBP5/qIDH4s1YUL9luGT6H+08dlue3RkbzDbBqsQu
+7fQ/BbrIG/GuVKgyEM+a7C9gv7zc86YlueVYJEyxKidnn7RxOqyDBqyyfXA3zvme
+Rro7xIrMHPL7Nu3AWjwjXzbp/w0z+tEFPsfVB+OOHKsWPcUG0HUTJGkyeO/uHRjN
+qPEkkf7BHHUO4V2gjOIdCsELxKwHf7vsZTOk40EV751fZ7FDHMr1eddQkgH4eqAb
+DB79uP+SLfUo+42n4q6eMmoqw8d76bBXRoUhIo/Ms4sebhV0sRtAS67OQioc9UUg
+-----END CERTIFICATE-----
diff --git a/apex/ld.config.txt b/apex/ld.config.txt
new file mode 100644
index 0000000..a5937fd
--- /dev/null
+++ b/apex/ld.config.txt
@@ -0,0 +1,131 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Bionic loader config file for the media swcodec APEX.
+#
+# There are no versioned APEX paths here - this APEX module does not support
+# having several versions mounted.
+
+dir.swcodec = /apex/com.android.media.swcodec/bin/
+
+[swcodec]
+additional.namespaces = platform,sphal
+
+###############################################################################
+# "default" namespace
+#
+# This namespace is for the binaries and libraries on the swcodec APEX.
+###############################################################################
+
+namespace.default.isolated = true
+namespace.default.visible = true
+
+namespace.default.search.paths      = /apex/com.android.media.swcodec/${LIB}
+namespace.default.asan.search.paths = /apex/com.android.media.swcodec/${LIB}
+
+namespace.default.links = platform
+
+# TODO: replace the following when apex has a way to auto-generate this list
+# namespace.default.link.platform.shared_libs  = %LLNDK_LIBRARIES%
+# namespace.default.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libdl_android.so:libvulkan.so
+
+###############################################################################
+# "platform" namespace
+#
+# This namespace is for linking to LLNDK and ASAN libraries on the system.
+###############################################################################
+
+namespace.platform.isolated = true
+
+namespace.platform.search.paths = /system/${LIB}
+namespace.platform.asan.search.paths  = /data/asan/system/${LIB}
+namespace.platform.asan.search.paths +=           /system/${LIB}
+
+# /system/lib/libc.so, etc are symlinks to /apex/com.android.lib/lib/bionic/libc.so, etc.
+# Add /apex/... pat to the permitted paths because linker uses realpath(3)
+# to check the accessibility of the lib. We could add this to search.paths
+# instead but that makes the resolution of bionic libs be dependent on
+# the order of /system/lib and /apex/... in search.paths. If /apex/...
+# is after /system/lib, then /apex/... is never tried because libc.so
+# is always found in /system/lib but fails to pass the accessibility test
+# because of its realpath.  It's better to not depend on the ordering if
+# possible.
+namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
+namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
+
+###############################################################################
+# "sphal" namespace
+#
+###############################################################################
+namespace.sphal.isolated = true
+namespace.sphal.visible = true
+
+# Keep the below in sync with "sphal" namespace in system's /etc/ld.config.txt
+# Codec2 has dependencies on some SP-hals (eg. android.hardware.graphics.mapper@2.0)
+# These are dlopen'ed by libvndksupport.so.
+namespace.sphal.search.paths  = /odm/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}
+
+namespace.sphal.permitted.paths  = /odm/${LIB}
+namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /vendor/${LIB}/hw
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
+
+namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.search.paths +=           /odm/${LIB}
+namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.search.paths +=           /vendor/${LIB}
+
+namespace.sphal.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.permitted.paths +=           /odm/${LIB}
+namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}
+
+# Keep the below in sync with "vndk" namespace in system's /etc/ld.config.txt
+# System's sphal namespace links to vndk namespace for %VNDK_SAMEPROCESS_LIBRARIES%,
+# since we don't have a good way to auto-expand %VNDK_SAMEPROCESS_LIBRARIES%,
+# we'll add the vndk paths below.
+
+namespace.sphal.search.paths += /odm/${LIB}/vndk-sp
+namespace.sphal.search.paths += /vendor/${LIB}/vndk-sp
+namespace.sphal.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+
+namespace.sphal.permitted.paths += /odm/${LIB}/hw
+namespace.sphal.permitted.paths += /odm/${LIB}/egl
+namespace.sphal.permitted.paths += /vendor/${LIB}/hw
+namespace.sphal.permitted.paths += /vendor/${LIB}/egl
+namespace.sphal.permitted.paths += /system/vendor/${LIB}/hw
+namespace.sphal.permitted.paths += /system/vendor/${LIB}/egl
+# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
+namespace.sphal.permitted.paths += /system/${LIB}/vndk-sp${VNDK_VER}/hw
+
+namespace.sphal.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
+namespace.sphal.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.sphal.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.sphal.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.sphal.asan.search.paths +=           /system/${LIB}/vndk-sp${VNDK_VER}
+
+namespace.sphal.asan.permitted.paths += /data/asan/odm/${LIB}/hw
+namespace.sphal.asan.permitted.paths +=           /odm/${LIB}/hw
+namespace.sphal.asan.permitted.paths += /data/asan/odm/${LIB}/egl
+namespace.sphal.asan.permitted.paths +=           /odm/${LIB}/egl
+namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}/hw
+namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}/hw
+namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
+namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}/egl
+
+namespace.sphal.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}/hw
+namespace.sphal.asan.permitted.paths +=           /system/${LIB}/vndk-sp${VNDK_VER}/hw
+
+# Once in this namespace, access to libraries in /system/lib is restricted. Only
+# libs listed here can be used.
+namespace.sphal.links = platform
+
+# TODO: replace the following when apex has a way to auto-generate this list
+# namespace.sphal.link.platform.shared_libs  = %LLNDK_LIBRARIES%
+# namespace.sphal.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.sphal.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so
+
+# Add a link for libz.so which is llndk on devices where VNDK is not enforced.
+namespace.sphal.link.platform.shared_libs += libz.so
diff --git a/apex/manifest.json b/apex/manifest.json
new file mode 100644
index 0000000..3011ee8
--- /dev/null
+++ b/apex/manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.media",
+  "version": 290000000
+}
diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json
new file mode 100644
index 0000000..83a5178
--- /dev/null
+++ b/apex/manifest_codec.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.media.swcodec",
+  "version": 290000000
+}
diff --git a/apex/mediaswcodec.rc b/apex/mediaswcodec.rc
new file mode 100644
index 0000000..d17481b
--- /dev/null
+++ b/apex/mediaswcodec.rc
@@ -0,0 +1,7 @@
+service media.swcodec /apex/com.android.media.swcodec/bin/mediaswcodec
+    class main
+    user mediacodec
+    group camera drmrpc mediadrm
+    override
+    ioprio rt 4
+    writepid /dev/cpuset/foreground/tasks
diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp
new file mode 100644
index 0000000..701ced7
--- /dev/null
+++ b/apex/testing/Android.bp
@@ -0,0 +1,29 @@
+// 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: "test_com.android.media",
+    manifest: "test_manifest.json",
+    file_contexts: "com.android.media",
+    defaults: ["com.android.media-defaults"],
+    installable: false,
+}
+
+apex {
+    name: "test_com.android.media.swcodec",
+    manifest: "test_manifest_codec.json",
+    file_contexts: "com.android.media.swcodec",
+    defaults: ["com.android.media.swcodec-defaults"],
+    installable: false,
+}
diff --git a/apex/testing/test_manifest.json b/apex/testing/test_manifest.json
new file mode 100644
index 0000000..ddd642e
--- /dev/null
+++ b/apex/testing/test_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.media",
+  "version": 300000000
+}
diff --git a/apex/testing/test_manifest_codec.json b/apex/testing/test_manifest_codec.json
new file mode 100644
index 0000000..2320fd7
--- /dev/null
+++ b/apex/testing/test_manifest_codec.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.media.swcodec",
+  "version": 300000000
+}
diff --git a/camera/Android.bp b/camera/Android.bp
index 24b3918..2800595 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-subdirs = ["ndk"]
-
 cc_library_shared {
     name: "libcamera_client",
 
@@ -43,6 +41,7 @@
         "ICameraRecordingProxyListener.cpp",
         "camera2/CaptureRequest.cpp",
         "camera2/OutputConfiguration.cpp",
+        "camera2/SessionConfiguration.cpp",
         "camera2/SubmitInfo.cpp",
         "CameraBase.cpp",
         "CameraUtils.cpp",
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index e143e05..92fe84b 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -22,7 +22,6 @@
 
 #include <binder/Parcel.h>
 #include <camera/CameraMetadata.h>
-#include <camera/VendorTagDescriptor.h>
 
 namespace android {
 
@@ -409,6 +408,79 @@
     return res;
 }
 
+status_t CameraMetadata::removePermissionEntries(metadata_vendor_id_t vendorId,
+        std::vector<int32_t> *tagsRemoved) {
+    uint32_t tagCount = 0;
+    std::vector<uint32_t> tagsToRemove;
+
+    if (tagsRemoved == nullptr) {
+        return BAD_VALUE;
+    }
+
+    sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor();
+    if ((nullptr == vTags.get()) || (0 >= vTags->getTagCount())) {
+        sp<VendorTagDescriptorCache> cache =
+            VendorTagDescriptorCache::getGlobalVendorTagCache();
+        if (cache.get()) {
+            cache->getVendorTagDescriptor(vendorId, &vTags);
+        }
+    }
+
+    if ((nullptr != vTags.get()) && (vTags->getTagCount() > 0)) {
+        tagCount = vTags->getTagCount();
+        uint32_t *vendorTags = new uint32_t[tagCount];
+        if (nullptr == vendorTags) {
+            return NO_MEMORY;
+        }
+        vTags->getTagArray(vendorTags);
+
+        tagsToRemove.reserve(tagCount);
+        tagsToRemove.insert(tagsToRemove.begin(), vendorTags, vendorTags + tagCount);
+
+        delete [] vendorTags;
+        tagCount = 0;
+    }
+
+    auto tagsNeedingPermission = get_camera_metadata_permission_needed(&tagCount);
+    if (tagCount > 0) {
+        tagsToRemove.reserve(tagsToRemove.capacity() + tagCount);
+        tagsToRemove.insert(tagsToRemove.end(), tagsNeedingPermission,
+                tagsNeedingPermission + tagCount);
+    }
+
+    tagsRemoved->reserve(tagsToRemove.size());
+    for (const auto &it : tagsToRemove) {
+        if (exists(it)) {
+            auto rc = erase(it);
+            if (NO_ERROR != rc) {
+                ALOGE("%s: Failed to erase tag: %x", __func__, it);
+                return rc;
+            }
+            tagsRemoved->push_back(it);
+        }
+    }
+
+    // Update the available characterstics accordingly
+    if (exists(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS)) {
+        std::vector<uint32_t> currentKeys;
+
+        std::sort(tagsRemoved->begin(), tagsRemoved->end());
+        auto keys = find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+        currentKeys.reserve(keys.count);
+        currentKeys.insert(currentKeys.end(), keys.data.i32, keys.data.i32 + keys.count);
+        std::sort(currentKeys.begin(), currentKeys.end());
+
+        std::vector<int32_t> newKeys(keys.count);
+        auto end = std::set_difference(currentKeys.begin(), currentKeys.end(), tagsRemoved->begin(),
+                tagsRemoved->end(), newKeys.begin());
+        newKeys.resize(end - newKeys.begin());
+
+        update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, newKeys.data(), newKeys.size());
+    }
+
+    return NO_ERROR;
+}
+
 void CameraMetadata::dump(int fd, int verbosity, int indentation) const {
     dump_indented_camera_metadata(mBuffer, fd, verbosity, indentation);
 }
diff --git a/camera/CaptureResult.cpp b/camera/CaptureResult.cpp
index 928a6bc..1d8e8c4 100644
--- a/camera/CaptureResult.cpp
+++ b/camera/CaptureResult.cpp
@@ -39,6 +39,16 @@
     parcel->readInt64(&frameNumber);
     parcel->readInt32(&partialResultCount);
     parcel->readInt32(&errorStreamId);
+    auto physicalCameraIdPresent = parcel->readBool();
+    if (physicalCameraIdPresent) {
+        String16 cameraId;
+        status_t res = OK;
+        if ((res = parcel->readString16(&cameraId)) != OK) {
+            ALOGE("%s: Failed to read camera id: %d", __FUNCTION__, res);
+            return res;
+        }
+        errorPhysicalCameraId = cameraId;
+    }
 
     return OK;
 }
@@ -56,6 +66,16 @@
     parcel->writeInt64(frameNumber);
     parcel->writeInt32(partialResultCount);
     parcel->writeInt32(errorStreamId);
+    if (errorPhysicalCameraId.size() > 0) {
+        parcel->writeBool(true);
+        status_t res = OK;
+        if ((res = parcel->writeString16(errorPhysicalCameraId)) != OK) {
+            ALOGE("%s: Failed to write physical camera ID to parcel: %d", __FUNCTION__, res);
+            return res;
+        }
+    } else {
+        parcel->writeBool(false);
+    }
 
     return OK;
 }
diff --git a/camera/OWNERS b/camera/OWNERS
index 18acfee..d6b95da 100644
--- a/camera/OWNERS
+++ b/camera/OWNERS
@@ -1,6 +1,8 @@
-cychen@google.com
 epeev@google.com
 etalvala@google.com
+jchowdhary@google.com
 shuzhenwang@google.com
 yinchiayeh@google.com
+# backup owner
+cychen@google.com
 zhijunhe@google.com
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index 4c28789..d713d2d 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();
@@ -407,6 +411,11 @@
     return res;
 }
 
+const std::unordered_map<metadata_vendor_id_t, sp<android::VendorTagDescriptor>> &
+            VendorTagDescriptorCache::getVendorIdsAndTagDescriptors() {
+    return mVendorMap;
+}
+
 int VendorTagDescriptorCache::getTagCount(metadata_vendor_id_t id) const {
     int ret = 0;
     auto desc = mVendorMap.find(id);
@@ -566,7 +575,7 @@
 
     for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) {
         uint32_t tag = tagArray[i];
-        String8 sectionString = tagToSectionMap.valueFor(tag);
+        const String8& sectionString = tagToSectionMap.valueFor(tag);
 
         // Set up tag to section index map
         ssize_t index = sections.indexOf(sectionString);
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index 9c0f28b..3e8992a 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -108,7 +108,7 @@
      *
      * Also returns the set of currently-known camera IDs and state of each device.
      * Adding a listener will trigger the torch status listener to fire for all
-     * devices that have a flash unit
+     * devices that have a flash unit.
      */
     CameraStatus[] addListener(ICameraServiceListener listener);
 
@@ -149,8 +149,10 @@
     const int API_VERSION_1 = 1;
     const int API_VERSION_2 = 2;
 
-    // Determines if a particular API version is supported directly
+    // Determines if a particular API version is supported directly for a cameraId.
     boolean supportsCameraApi(String cameraId, int apiVersion);
+    // Determines if a cameraId is a hidden physical camera of a logical multi-camera.
+    boolean isHiddenPhysicalCamera(String cameraId);
 
     void setTorchMode(String cameraId, boolean enabled, IBinder clientBinder);
 
@@ -160,6 +162,28 @@
      * Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission.
      */
     const int EVENT_NONE = 0;
-    const int EVENT_USER_SWITCHED = 1;
+    const int EVENT_USER_SWITCHED = 1; // The argument is the set of new foreground user IDs.
     oneway void notifySystemEvent(int eventId, in int[] args);
+
+    /**
+     * Notify the camera service of a device physical status change. May only be called from
+     * a privileged process.
+     *
+     * newState is a bitfield consisting of DEVICE_STATE_* values combined together. Valid state
+     * combinations are device-specific. At device startup, the camera service will assume the device
+     * state is NORMAL until otherwise notified.
+     *
+     * Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission.
+     */
+    oneway void notifyDeviceStateChange(long newState);
+
+    // Bitfield constants for notifyDeviceStateChange
+    // All bits >= 32 are for custom vendor states
+    // Written as ints since AIDL does not support long constants.
+    const int DEVICE_STATE_NORMAL = 0;
+    const int DEVICE_STATE_BACK_COVERED = 1;
+    const int DEVICE_STATE_FRONT_COVERED = 2;
+    const int DEVICE_STATE_FOLDED = 4;
+    const int DEVICE_STATE_LAST_FRAMEWORK_BIT = 0x80000000; // 1 << 31;
+
 }
diff --git a/camera/aidl/android/hardware/ICameraServiceListener.aidl b/camera/aidl/android/hardware/ICameraServiceListener.aidl
index f871ce4..e9dcbdb 100644
--- a/camera/aidl/android/hardware/ICameraServiceListener.aidl
+++ b/camera/aidl/android/hardware/ICameraServiceListener.aidl
@@ -76,4 +76,11 @@
     const int TORCH_STATUS_UNKNOWN = -1;
 
     oneway void onTorchStatusChanged(int status, String cameraId);
+
+    /**
+     * Notify registered clients about camera access priority changes.
+     * Clients which were previously unable to open a certain camera device
+     * can retry after receiving this callback.
+     */
+    oneway void onCameraAccessPrioritiesChanged();
 }
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index 4ced08c..49dfde8 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -19,6 +19,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.utils.SubmitInfo;
 import android.view.Surface;
 
@@ -83,6 +84,16 @@
      */
     void endConfigure(int operatingMode, in CameraMetadataNative sessionParams);
 
+    /**
+      * Check whether a particular session configuration has camera device
+      * support.
+      *
+      * @param sessionConfiguration Specific session configuration to be verified.
+      * @return true  - in case the stream combination is supported.
+      *         false - in case there is no device support.
+      */
+    boolean isSessionConfigurationSupported(in SessionConfiguration sessionConfiguration);
+
     void deleteStream(int streamId);
 
     /**
diff --git a/camera/aidl/android/hardware/camera2/params/SessionConfiguration.aidl b/camera/aidl/android/hardware/camera2/params/SessionConfiguration.aidl
new file mode 100644
index 0000000..abf1556
--- /dev/null
+++ b/camera/aidl/android/hardware/camera2/params/SessionConfiguration.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package android.hardware.camera2.params;
+
+/** @hide */
+parcelable SessionConfiguration cpp_header "camera/camera2/SessionConfiguration.h";
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index feb04c2..4e9b27d 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -167,14 +167,24 @@
 }
 
 OutputConfiguration::OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation,
+        const String16& physicalId,
         int surfaceSetID, bool isShared) {
     mGbps.push_back(gbp);
     mRotation = rotation;
     mSurfaceSetID = surfaceSetID;
     mIsDeferred = false;
     mIsShared = isShared;
+    mPhysicalCameraId = physicalId;
 }
 
+OutputConfiguration::OutputConfiguration(
+        const std::vector<sp<IGraphicBufferProducer>>& gbps,
+    int rotation, const String16& physicalCameraId, 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),
+    mPhysicalCameraId(physicalCameraId) { }
+
 status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const {
 
     if (parcel == nullptr) return BAD_VALUE;
diff --git a/camera/camera2/SessionConfiguration.cpp b/camera/camera2/SessionConfiguration.cpp
new file mode 100644
index 0000000..a431a33
--- /dev/null
+++ b/camera/camera2/SessionConfiguration.cpp
@@ -0,0 +1,133 @@
+/*
+**
+** 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_TAG "SessionConfiguration"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+
+#include <camera/camera2/SessionConfiguration.h>
+#include <camera/camera2/OutputConfiguration.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+status_t SessionConfiguration::readFromParcel(const android::Parcel* parcel) {
+    status_t err = OK;
+    int operatingMode = 0;
+
+    if (parcel == nullptr) return BAD_VALUE;
+
+    if ((err = parcel->readInt32(&operatingMode)) != OK) {
+        ALOGE("%s: Failed to read operating mode from parcel", __FUNCTION__);
+        return err;
+    }
+
+    int inputWidth = 0;
+    if ((err = parcel->readInt32(&inputWidth)) != OK) {
+        ALOGE("%s: Failed to read input width from parcel", __FUNCTION__);
+        return err;
+    }
+
+    int inputHeight = 0;
+    if ((err = parcel->readInt32(&inputHeight)) != OK) {
+        ALOGE("%s: Failed to read input height from parcel", __FUNCTION__);
+        return err;
+    }
+
+    int inputFormat = -1;
+    if ((err = parcel->readInt32(&inputFormat)) != OK) {
+        ALOGE("%s: Failed to read input format from parcel", __FUNCTION__);
+        return err;
+    }
+
+    std::vector<OutputConfiguration> outputStreams;
+    if ((err = parcel->readParcelableVector(&outputStreams)) != OK) {
+        ALOGE("%s: Failed to read output configurations from parcel", __FUNCTION__);
+        return err;
+    }
+
+    mOperatingMode = operatingMode;
+    mInputWidth = inputWidth;
+    mInputHeight = inputHeight;
+    mInputFormat = inputFormat;
+    for (auto& stream : outputStreams) {
+        mOutputStreams.push_back(stream);
+    }
+
+
+    return err;
+}
+
+status_t SessionConfiguration::writeToParcel(android::Parcel* parcel) const {
+
+    if (parcel == nullptr) return BAD_VALUE;
+    status_t err = OK;
+
+    err = parcel->writeInt32(mOperatingMode);
+    if (err != OK) return err;
+
+    err = parcel->writeInt32(mInputWidth);
+    if (err != OK) return err;
+
+    err = parcel->writeInt32(mInputHeight);
+    if (err != OK) return err;
+
+    err = parcel->writeInt32(mInputFormat);
+    if (err != OK) return err;
+
+    err = parcel->writeParcelableVector(mOutputStreams);
+    if (err != OK) return err;
+
+    return OK;
+}
+
+bool SessionConfiguration::outputsEqual(const SessionConfiguration& other) const {
+    const std::vector<OutputConfiguration>& otherOutputStreams =
+            other.getOutputConfigurations();
+
+    if (mOutputStreams.size() !=  otherOutputStreams.size()) {
+        return false;
+    }
+
+    for (size_t i = 0; i < mOutputStreams.size(); i++) {
+        if (mOutputStreams[i] != otherOutputStreams[i]) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool SessionConfiguration::outputsLessThan(const SessionConfiguration& other) const {
+    const std::vector<OutputConfiguration>& otherOutputStreams =
+            other.getOutputConfigurations();
+
+    if (mOutputStreams.size() !=  otherOutputStreams.size()) {
+        return mOutputStreams.size() < otherOutputStreams.size();
+    }
+
+    for (size_t i = 0; i < mOutputStreams.size(); i++) {
+        if (mOutputStreams[i] != otherOutputStreams[i]) {
+            return mOutputStreams[i] < otherOutputStreams[i];
+        }
+    }
+
+    return false;
+}
+
+}; // namespace android
diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp
new file mode 100644
index 0000000..ecaba3a
--- /dev/null
+++ b/camera/cameraserver/Android.bp
@@ -0,0 +1,49 @@
+// 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",
+        "libhidlbase",
+        "libhidltransport",
+        "android.hardware.camera.common@1.0",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.camera.provider@2.5",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.4",
+    ],
+    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/CameraMetadata.h b/camera/include/camera/CameraMetadata.h
index d284477..844bb80 100644
--- a/camera/include/camera/CameraMetadata.h
+++ b/camera/include/camera/CameraMetadata.h
@@ -22,6 +22,7 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 #include <binder/Parcelable.h>
+#include <camera/VendorTagDescriptor.h>
 
 namespace android {
 
@@ -170,6 +171,12 @@
     status_t erase(uint32_t tag);
 
     /**
+     * Remove metadata entries that need additional permissions.
+     */
+    status_t removePermissionEntries(metadata_vendor_id_t vendorId,
+            std::vector<int32_t> *tagsRemoved /*out*/);
+
+    /**
      * Swap the underlying camera metadata between this and the other
      * metadata object.
      */
diff --git a/camera/include/camera/CaptureResult.h b/camera/include/camera/CaptureResult.h
index 56fa178..ef830b5 100644
--- a/camera/include/camera/CaptureResult.h
+++ b/camera/include/camera/CaptureResult.h
@@ -70,6 +70,13 @@
     int32_t errorStreamId;
 
     /**
+     * For capture result errors, the physical camera ID in case the respective request contains
+     * a reference to physical camera device.
+     * Empty otherwise.
+     */
+    String16  errorPhysicalCameraId;
+
+    /**
      * Constructor initializes object as invalid by setting requestId to be -1.
      */
     CaptureResultExtras()
@@ -79,7 +86,8 @@
           precaptureTriggerId(0),
           frameNumber(0),
           partialResultCount(0),
-          errorStreamId(-1) {
+          errorStreamId(-1),
+          errorPhysicalCameraId() {
     }
 
     /**
diff --git a/camera/include/camera/VendorTagDescriptor.h b/camera/include/camera/VendorTagDescriptor.h
index 904fba2..6f55890 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.
@@ -206,6 +211,9 @@
      */
     void dump(int fd, int verbosity, int indentation) const;
 
+    const std::unordered_map<metadata_vendor_id_t, sp<android::VendorTagDescriptor>> &
+            getVendorIdsAndTagDescriptors();
+
   protected:
     std::unordered_map<metadata_vendor_id_t, sp<android::VendorTagDescriptor>> mVendorMap;
     struct vendor_tag_cache_ops mVendorCacheOps;
diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h
index a80f44b..95c4f39 100644
--- a/camera/include/camera/camera2/OutputConfiguration.h
+++ b/camera/include/camera/camera2/OutputConfiguration.h
@@ -65,8 +65,15 @@
     OutputConfiguration(const android::Parcel& parcel);
 
     OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation,
+            const String16& physicalCameraId,
             int surfaceSetID = INVALID_SET_ID, bool isShared = false);
 
+    OutputConfiguration(const std::vector<sp<IGraphicBufferProducer>>& gbps,
+                        int rotation, const String16& physicalCameraId,
+                        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/include/camera/camera2/SessionConfiguration.h b/camera/include/camera/camera2/SessionConfiguration.h
new file mode 100644
index 0000000..64288ed
--- /dev/null
+++ b/camera/include/camera/camera2/SessionConfiguration.h
@@ -0,0 +1,116 @@
+/*
+ * 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_CAMERA2_SESSIONCONFIGURATION_H
+#define ANDROID_HARDWARE_CAMERA2_SESSIONCONFIGURATION_H
+
+#include <binder/Parcelable.h>
+
+namespace android {
+
+namespace hardware {
+namespace camera2 {
+namespace params {
+
+class OutputConfiguration;
+
+class SessionConfiguration : public android::Parcelable {
+public:
+
+    const std::vector<OutputConfiguration>& getOutputConfigurations() const {
+        return mOutputStreams;
+    }
+
+    int getInputWidth() const { return mInputWidth; }
+    int getInputHeight() const { return mInputHeight; }
+    int getInputFormat() const { return mInputFormat; }
+    int getOperatingMode() const { return mOperatingMode; }
+
+    virtual status_t writeToParcel(android::Parcel* parcel) const override;
+    virtual status_t readFromParcel(const android::Parcel* parcel) override;
+
+    SessionConfiguration() :
+            mInputWidth(0),
+            mInputHeight(0),
+            mInputFormat(-1),
+            mOperatingMode(-1) {}
+
+    SessionConfiguration(const android::Parcel& parcel) {
+        readFromParcel(&parcel);
+    }
+
+    SessionConfiguration(int inputWidth, int inputHeight, int inputFormat, int operatingMode) :
+        mInputWidth(inputWidth), mInputHeight(inputHeight), mInputFormat(inputFormat),
+        mOperatingMode(operatingMode) {}
+
+    bool operator == (const SessionConfiguration& other) const {
+        return (outputsEqual(other) &&
+                mInputWidth == other.mInputWidth &&
+                mInputHeight == other.mInputHeight &&
+                mInputFormat == other.mInputFormat &&
+                mOperatingMode == other.mOperatingMode);
+    }
+
+    bool operator != (const SessionConfiguration& other) const {
+        return !(*this == other);
+    }
+
+    bool operator < (const SessionConfiguration& other) const {
+        if (*this == other) return false;
+
+        if (mInputWidth != other.mInputWidth) {
+            return mInputWidth < other.mInputWidth;
+        }
+
+        if (mInputHeight != other.mInputHeight) {
+            return mInputHeight < other.mInputHeight;
+        }
+
+        if (mInputFormat != other.mInputFormat) {
+            return mInputFormat < other.mInputFormat;
+        }
+
+        if (mOperatingMode != other.mOperatingMode) {
+            return mOperatingMode < other.mOperatingMode;
+        }
+
+        return outputsLessThan(other);
+    }
+
+    bool operator > (const SessionConfiguration& other) const {
+        return (*this != other && !(*this < other));
+    }
+
+    bool outputsEqual(const SessionConfiguration& other) const;
+    bool outputsLessThan(const SessionConfiguration& other) const;
+    void addOutputConfiguration(const OutputConfiguration &config) {
+        mOutputStreams.push_back(config);
+    }
+
+private:
+
+    std::vector<OutputConfiguration> mOutputStreams;
+    int                              mInputWidth, mInputHeight, mInputFormat, mOperatingMode;
+};
+} // namespace params
+} // namespace camera2
+} // namespace hardware
+
+using hardware::camera2::params::SessionConfiguration;
+
+}; // namespace android
+
+#endif
diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp
index 97cf6bf..a2ee65d 100644
--- a/camera/ndk/Android.bp
+++ b/camera/ndk/Android.bp
@@ -30,3 +30,133 @@
     srcs: ["include/camera/**/*.h"],
     license: "NOTICE",
 }
+
+cc_library_shared {
+    name: "libcamera2ndk",
+    srcs: [
+        "NdkCameraManager.cpp",
+        "NdkCameraMetadata.cpp",
+        "NdkCameraDevice.cpp",
+        "NdkCaptureRequest.cpp",
+        "NdkCameraCaptureSession.cpp",
+        "impl/ACameraManager.cpp",
+        "impl/ACameraMetadata.cpp",
+        "impl/ACameraDevice.cpp",
+        "impl/ACameraCaptureSession.cpp",
+    ],
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libgui",
+        "libutils",
+        "libandroid_runtime",
+        "libcamera_client",
+        "libstagefright_foundation",
+        "libcutils",
+        "libcamera_metadata",
+        "libmediandk",
+        "libnativewindow",
+    ],
+    cflags: [
+        "-fvisibility=hidden",
+        "-DEXPORT=__attribute__ ((visibility (\"default\")))",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    // TODO: jchowdhary@, use header_libs instead b/131165718
+    include_dirs: [
+        "system/media/private/camera/include",
+    ],
+    export_include_dirs: ["include"],
+    export_shared_lib_headers: [
+        "libnativewindow",
+    ],
+    version_script: "libcamera2ndk.map.txt",
+}
+
+cc_library_shared {
+    name: "libcamera2ndk_vendor",
+    vendor: 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",
+    ],
+    // TODO: jchowdhary@, use header_libs instead b/131165718
+    include_dirs: [
+        "system/media/private/camera/include",
+    ],
+    product_variables: {
+        pdk: {
+            enabled: false,
+        },
+    },
+}
+
+cc_test {
+    name: "AImageReaderVendorTest",
+    vendor: true,
+    srcs: ["ndk_vendor/tests/AImageReaderVendorTest.cpp"],
+    shared_libs: [
+        "libhwbinder",
+        "libcamera2ndk_vendor",
+        "libcamera_metadata",
+        "libmediandk",
+        "libnativewindow",
+        "libutils",
+        "libui",
+        "libcutils",
+        "liblog",
+    ],
+    static_libs: [
+        "android.hardware.camera.common@1.0-helper",
+    ],
+    cflags: [
+        "-D__ANDROID_VNDK__",
+    ],
+}
diff --git a/camera/ndk/Android.mk b/camera/ndk/Android.mk
deleted file mode 100644
index f5ff69d..0000000
--- a/camera/ndk/Android.mk
+++ /dev/null
@@ -1,56 +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.
-#
-
-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_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/NdkCameraCaptureSession.cpp b/camera/ndk/NdkCameraCaptureSession.cpp
index fd95296..1ac8482 100644
--- a/camera/ndk/NdkCameraCaptureSession.cpp
+++ b/camera/ndk/NdkCameraCaptureSession.cpp
@@ -28,6 +28,8 @@
 #include <camera/NdkCameraCaptureSession.h>
 #include "impl/ACameraCaptureSession.h"
 
+#include "impl/ACameraCaptureSession.inc"
+
 using namespace android;
 
 EXPORT
@@ -78,11 +80,39 @@
 
     if (session->isClosed()) {
         ALOGE("%s: session %p is already closed", __FUNCTION__, session);
-        *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE;
+        if (captureSequenceId != nullptr) {
+            *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE;
+        }
         return ACAMERA_ERROR_SESSION_CLOSED;
     }
 
-    return session->capture(cbs, numRequests, requests, captureSequenceId);
+    return session->capture(
+            cbs, numRequests, requests, captureSequenceId);
+}
+
+EXPORT
+camera_status_t ACameraCaptureSession_logicalCamera_capture(
+        ACameraCaptureSession* session,
+        /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    ATRACE_CALL();
+    if (session == nullptr || requests == nullptr || numRequests < 1) {
+        ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p",
+                __FUNCTION__, session, numRequests, requests);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+
+    if (session->isClosed()) {
+        ALOGE("%s: session %p is already closed", __FUNCTION__, session);
+        if (captureSequenceId) {
+            *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE;
+        }
+        return ACAMERA_ERROR_SESSION_CLOSED;
+    }
+
+    return session->capture(
+            lcbs, numRequests, requests, captureSequenceId);
 }
 
 EXPORT
@@ -99,7 +129,9 @@
 
     if (session->isClosed()) {
         ALOGE("%s: session %p is already closed", __FUNCTION__, session);
-        *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE;
+        if (captureSequenceId) {
+            *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE;
+        }
         return ACAMERA_ERROR_SESSION_CLOSED;
     }
 
@@ -107,6 +139,30 @@
 }
 
 EXPORT
+camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequest(
+        ACameraCaptureSession* session,
+        /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    ATRACE_CALL();
+    if (session == nullptr || requests == nullptr || numRequests < 1) {
+        ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p",
+                __FUNCTION__, session, numRequests, requests);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+
+    if (session->isClosed()) {
+        ALOGE("%s: session %p is already closed", __FUNCTION__, session);
+        if (captureSequenceId) {
+            *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE;
+        }
+        return ACAMERA_ERROR_SESSION_CLOSED;
+    }
+
+    return session->setRepeatingRequest(lcbs, numRequests, requests, captureSequenceId);
+}
+
+EXPORT
 camera_status_t ACameraCaptureSession_stopRepeating(ACameraCaptureSession* session) {
     ATRACE_CALL();
     if (session == nullptr) {
diff --git a/camera/ndk/NdkCameraDevice.cpp b/camera/ndk/NdkCameraDevice.cpp
index 812a312..691996b 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) {
@@ -70,7 +78,34 @@
             ALOGE("%s: unknown template ID %d", __FUNCTION__, templateId);
             return ACAMERA_ERROR_INVALID_PARAMETER;
     }
-    return device->createCaptureRequest(templateId, request);
+    return device->createCaptureRequest(templateId, nullptr /*physicalIdList*/, request);
+}
+
+EXPORT
+camera_status_t ACameraDevice_createCaptureRequest_withPhysicalIds(
+        const ACameraDevice* device,
+        ACameraDevice_request_template templateId,
+        const ACameraIdList* physicalCameraIdList,
+        ACaptureRequest** request) {
+    ATRACE_CALL();
+    if (device == nullptr || request == nullptr || physicalCameraIdList == nullptr) {
+        ALOGE("%s: invalid argument! device %p request %p, physicalCameraIdList %p",
+                __FUNCTION__, device, request, physicalCameraIdList);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    switch (templateId) {
+        case TEMPLATE_PREVIEW:
+        case TEMPLATE_STILL_CAPTURE:
+        case TEMPLATE_RECORD:
+        case TEMPLATE_VIDEO_SNAPSHOT:
+        case TEMPLATE_ZERO_SHUTTER_LAG:
+        case TEMPLATE_MANUAL:
+            break;
+        default:
+            ALOGE("%s: unknown template ID %d", __FUNCTION__, templateId);
+            return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    return device->createCaptureRequest(templateId, physicalCameraIdList, request);
 }
 
 EXPORT
@@ -96,7 +131,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 +144,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",
@@ -121,8 +156,22 @@
 }
 
 EXPORT
+camera_status_t ACaptureSessionPhysicalOutput_create(
+        ACameraWindowType* window, const char* physicalId,
+        /*out*/ACaptureSessionOutput** out) {
+    ATRACE_CALL();
+    if (window == nullptr || physicalId == nullptr || out == nullptr) {
+        ALOGE("%s: Error: bad argument. window %p, physicalId %p, out %p",
+                __FUNCTION__, window, physicalId, out);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    *out = new ACaptureSessionOutput(window, false, physicalId);
+    return ACAMERA_OK;
+}
+
+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 +183,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 +196,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 +208,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;
@@ -238,3 +287,16 @@
     }
     return device->createCaptureSession(outputs, sessionParameters, callbacks, session);
 }
+
+EXPORT
+camera_status_t ACameraDevice_isSessionConfigurationSupported(
+        const ACameraDevice* device,
+        const ACaptureSessionOutputContainer* sessionOutputContainer) {
+    ATRACE_CALL();
+    if (device == nullptr || sessionOutputContainer == nullptr) {
+        ALOGE("%s: Error: invalid input: device %p, sessionOutputContainer %p",
+                __FUNCTION__, device, sessionOutputContainer);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    return device->isSessionConfigurationSupported(sessionOutputContainer);
+}
diff --git a/camera/ndk/NdkCameraManager.cpp b/camera/ndk/NdkCameraManager.cpp
index 60b4763..3d231a8 100644
--- a/camera/ndk/NdkCameraManager.cpp
+++ b/camera/ndk/NdkCameraManager.cpp
@@ -22,9 +22,15 @@
 #include <utils/Trace.h>
 
 #include <camera/NdkCameraManager.h>
-#include "impl/ACameraManager.h"
 
-using namespace android;
+#ifdef __ANDROID_VNDK__
+#include "ndk_vendor/impl/ACameraManager.h"
+#else
+#include "impl/ACameraManager.h"
+#endif
+#include "impl/ACameraMetadata.h"
+
+using namespace android::acam;
 
 EXPORT
 ACameraManager* ACameraManager_create() {
@@ -99,6 +105,60 @@
 }
 
 EXPORT
+camera_status_t ACameraManager_registerExtendedAvailabilityCallback(
+        ACameraManager* /*manager*/, const ACameraManager_ExtendedAvailabilityCallbacks *callback) {
+    ATRACE_CALL();
+    if (callback == nullptr) {
+        ALOGE("%s: invalid argument! callback is null!", __FUNCTION__);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    if ((callback->availabilityCallbacks.onCameraAvailable == nullptr) ||
+            (callback->availabilityCallbacks.onCameraUnavailable == nullptr) ||
+            (callback->onCameraAccessPrioritiesChanged == nullptr)) {
+        ALOGE("%s: invalid argument! callback %p, "
+                "onCameraAvailable %p, onCameraUnavailable %p onCameraAccessPrioritiesChanged %p",
+               __FUNCTION__, callback,
+               callback->availabilityCallbacks.onCameraAvailable,
+               callback->availabilityCallbacks.onCameraUnavailable,
+               callback->onCameraAccessPrioritiesChanged);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    auto reservedEntriesCount = sizeof(callback->reserved) / sizeof(callback->reserved[0]);
+    for (size_t i = 0; i < reservedEntriesCount; i++) {
+        if (callback->reserved[i] != nullptr) {
+            ALOGE("%s: invalid argument! callback reserved entries must be set to NULL",
+                    __FUNCTION__);
+            return ACAMERA_ERROR_INVALID_PARAMETER;
+        }
+    }
+    CameraManagerGlobal::getInstance().registerExtendedAvailabilityCallback(callback);
+    return ACAMERA_OK;
+}
+
+EXPORT
+camera_status_t ACameraManager_unregisterExtendedAvailabilityCallback(
+        ACameraManager* /*manager*/, const ACameraManager_ExtendedAvailabilityCallbacks *callback) {
+    ATRACE_CALL();
+    if (callback == nullptr) {
+        ALOGE("%s: invalid argument! callback is null!", __FUNCTION__);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    if ((callback->availabilityCallbacks.onCameraAvailable == nullptr) ||
+            (callback->availabilityCallbacks.onCameraUnavailable == nullptr) ||
+            (callback->onCameraAccessPrioritiesChanged == nullptr)) {
+        ALOGE("%s: invalid argument! callback %p, "
+                "onCameraAvailable %p, onCameraUnavailable %p onCameraAccessPrioritiesChanged %p",
+               __FUNCTION__, callback,
+               callback->availabilityCallbacks.onCameraAvailable,
+               callback->availabilityCallbacks.onCameraUnavailable,
+               callback->onCameraAccessPrioritiesChanged);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    CameraManagerGlobal::getInstance().unregisterExtendedAvailabilityCallback(callback);
+    return ACAMERA_OK;
+}
+
+EXPORT
 camera_status_t ACameraManager_getCameraCharacteristics(
         ACameraManager* mgr, const char* cameraId, ACameraMetadata** chars){
     ATRACE_CALL();
@@ -107,7 +167,14 @@
                 __FUNCTION__, mgr, cameraId, chars);
         return ACAMERA_ERROR_INVALID_PARAMETER;
     }
-    return mgr->getCameraCharacteristics(cameraId, chars);
+    sp<ACameraMetadata> spChars;
+    camera_status_t status = mgr->getCameraCharacteristics(cameraId, &spChars);
+    if (status != ACAMERA_OK) {
+        return status;
+    }
+    spChars->incStrong((void*) ACameraManager_getCameraCharacteristics);
+    *chars = spChars.get();
+    return ACAMERA_OK;
 }
 
 EXPORT
@@ -123,3 +190,17 @@
     }
     return mgr->openCamera(cameraId, callback, device);
 }
+
+#ifdef __ANDROID_VNDK__
+EXPORT
+camera_status_t ACameraManager_getTagFromName(ACameraManager *mgr, const char* cameraId,
+        const char *name, /*out*/uint32_t *tag) {
+    ATRACE_CALL();
+    if (mgr == nullptr || cameraId == nullptr || name == nullptr) {
+        ALOGE("%s: invalid argument! mgr %p cameraId %p name %p",
+                __FUNCTION__, mgr, cameraId, name);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    return mgr->getTagFromName(cameraId, name, tag);
+}
+#endif
diff --git a/camera/ndk/NdkCameraMetadata.cpp b/camera/ndk/NdkCameraMetadata.cpp
index 65de81f..9a39ed8 100644
--- a/camera/ndk/NdkCameraMetadata.cpp
+++ b/camera/ndk/NdkCameraMetadata.cpp
@@ -57,13 +57,32 @@
         ALOGE("%s: src is null!", __FUNCTION__);
         return nullptr;
     }
-    return new ACameraMetadata(*src);
+    ACameraMetadata* copy = new ACameraMetadata(*src);
+    copy->incStrong((void*) ACameraMetadata_copy);
+    return copy;
 }
 
 EXPORT
 void ACameraMetadata_free(ACameraMetadata* metadata) {
     ATRACE_CALL();
     if (metadata != nullptr) {
-        delete metadata;
+        metadata->decStrong((void*) ACameraMetadata_free);
     }
 }
+
+EXPORT
+bool ACameraMetadata_isLogicalMultiCamera(const ACameraMetadata* staticMetadata,
+        /*out*/size_t* numPhysicalCameras, /*out*/const char*const** physicalCameraIds) {
+    ATRACE_CALL();
+    if (numPhysicalCameras == nullptr || physicalCameraIds == nullptr) {
+        ALOGE("%s: Invalid input: numPhysicalCameras %p, physicalCameraIds %p",
+                 __FUNCTION__, numPhysicalCameras, physicalCameraIds);
+        return false;
+    }
+    if (staticMetadata == nullptr) {
+        ALOGE("%s: Invalid input: staticMetadata is null.", __FUNCTION__);
+        return false;
+    }
+
+    return staticMetadata->isLogicalMultiCamera(numPhysicalCameras, physicalCameraIds);
+}
diff --git a/camera/ndk/NdkCaptureRequest.cpp b/camera/ndk/NdkCaptureRequest.cpp
index ac1856b..87de4a9 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__);
@@ -98,6 +98,27 @@
 }
 
 EXPORT
+camera_status_t ACaptureRequest_getConstEntry_physicalCamera(
+        const ACaptureRequest* req, const char* physicalId,
+        uint32_t tag, ACameraMetadata_const_entry* entry) {
+    ATRACE_CALL();
+    if (req == nullptr || entry == nullptr || physicalId == nullptr) {
+        ALOGE("%s: invalid argument! req %p, tag 0x%x, entry %p, physicalId %p",
+               __FUNCTION__, req, tag, entry, physicalId);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+
+    const auto& physicalSettings = req->physicalSettings.find(physicalId);
+    if (physicalSettings == req->physicalSettings.end()) {
+        ALOGE("%s: Failed to find metadata for physical camera id  %s",
+                __FUNCTION__, physicalId);
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+
+    return physicalSettings->second->getConstEntry(tag, entry);
+}
+
+EXPORT
 camera_status_t ACaptureRequest_getAllTags(
         const ACaptureRequest* req, /*out*/int32_t* numTags, /*out*/const uint32_t** tags) {
     ATRACE_CALL();
@@ -131,13 +152,42 @@
 
 #undef SET_ENTRY
 
+#define SET_PHYSICAL_ENTRY(NAME,NDK_TYPE)                                               \
+EXPORT                                                                                  \
+camera_status_t ACaptureRequest_setEntry_physicalCamera_##NAME(                         \
+        ACaptureRequest* req, const char* physicalId, uint32_t tag,                     \
+        uint32_t count, const NDK_TYPE* data) {                                         \
+    ATRACE_CALL();                                                                      \
+    if (req == nullptr || (count > 0 && data == nullptr) || physicalId == nullptr) {    \
+        ALOGE("%s: invalid argument! req %p, tag 0x%x, count %d, data 0x%p, physicalId %p", \
+               __FUNCTION__, req, tag, count, data, physicalId);                        \
+        return ACAMERA_ERROR_INVALID_PARAMETER;                                         \
+    }                                                                                   \
+    if (req->physicalSettings.find(physicalId) == req->physicalSettings.end()) {        \
+        ALOGE("%s: Failed to find metadata for physical camera id %s",                  \
+            __FUNCTION__, physicalId);                                                  \
+      return ACAMERA_ERROR_INVALID_PARAMETER;                                           \
+    }                                                                                   \
+    return req->physicalSettings[physicalId]->update(tag, count, data);                 \
+}
+
+SET_PHYSICAL_ENTRY(u8,uint8_t)
+SET_PHYSICAL_ENTRY(i32,int32_t)
+SET_PHYSICAL_ENTRY(float,float)
+SET_PHYSICAL_ENTRY(double,double)
+SET_PHYSICAL_ENTRY(i64,int64_t)
+SET_PHYSICAL_ENTRY(rational,ACameraMetadata_rational)
+
+#undef SET_PHYSICAL_ENTRY
+
 EXPORT
 void ACaptureRequest_free(ACaptureRequest* request) {
     ATRACE_CALL();
     if (request == nullptr) {
         return;
     }
-    delete request->settings;
+    request->settings.clear();
+    request->physicalSettings.clear();
     delete request->targets;
     delete request;
     return;
@@ -174,6 +224,9 @@
 
     ACaptureRequest* pRequest = new ACaptureRequest();
     pRequest->settings = new ACameraMetadata(*(src->settings));
+    for (const auto& entry : src->physicalSettings) {
+        pRequest->physicalSettings[entry.first] = new ACameraMetadata(*(entry.second));
+    }
     pRequest->targets  = new ACameraOutputTargets();
     *(pRequest->targets)  = *(src->targets);
     pRequest->context = src->context;
diff --git a/camera/ndk/impl/ACameraCaptureSession.cpp b/camera/ndk/impl/ACameraCaptureSession.cpp
index f60e5fd..d6f1412 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;
@@ -107,49 +107,8 @@
     return ret;
 }
 
-camera_status_t
-ACameraCaptureSession::setRepeatingRequest(
-        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
-        int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId) {
-    sp<CameraDevice> dev = getDeviceSp();
-    if (dev == nullptr) {
-        ALOGE("Error: Device associated with session %p has been closed!", this);
-        return ACAMERA_ERROR_SESSION_CLOSED;
-    }
-
-    camera_status_t ret;
-    dev->lockDeviceForSessionOps();
-    {
-        Mutex::Autolock _l(mSessionLock);
-        ret = dev->setRepeatingRequestsLocked(
-                this, cbs, numRequests, requests, captureSequenceId);
-    }
-    dev->unlockDevice();
-    return ret;
-}
-
-camera_status_t ACameraCaptureSession::capture(
-        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
-        int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId) {
-    sp<CameraDevice> dev = getDeviceSp();
-    if (dev == nullptr) {
-        ALOGE("Error: Device associated with session %p has been closed!", this);
-        return ACAMERA_ERROR_SESSION_CLOSED;
-    }
-    camera_status_t ret;
-    dev->lockDeviceForSessionOps();
-    {
-        Mutex::Autolock _l(mSessionLock);
-        ret = dev->captureLocked(this, cbs, numRequests, requests, captureSequenceId);
-    }
-    dev->unlockDevice();
-    return ret;
-}
-
 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 +127,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 +141,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..08a9226 100644
--- a/camera/ndk/impl/ACameraCaptureSession.h
+++ b/camera/ndk/impl/ACameraCaptureSession.h
@@ -17,15 +17,22 @@
 #define _ACAMERA_CAPTURE_SESSION_H
 
 #include <set>
+#include <string>
 #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) :
-            mWindow(window), mIsShared(isShared) {};
+    explicit ACaptureSessionOutput(ACameraWindowType* window, bool isShared = false,
+            const char* physicalCameraId = "") :
+            mWindow(window), mIsShared(isShared), mPhysicalCameraId(physicalCameraId) {};
 
     bool operator == (const ACaptureSessionOutput& other) const {
         return mWindow == other.mWindow;
@@ -40,11 +47,13 @@
         return mWindow > other.mWindow;
     }
 
-    ANativeWindow* mWindow;
-    std::set<ANativeWindow *> mSharedWindows;
+    ACameraWindowType* mWindow;
+    std::set<ACameraWindowType *> mSharedWindows;
     bool           mIsShared;
     int            mRotation = CAMERA3_STREAM_ROTATION_0;
+    std::string mPhysicalCameraId;
 };
+#endif
 
 struct ACaptureSessionOutputContainer {
     std::set<ACaptureSessionOutput> mOutputs;
@@ -60,7 +69,7 @@
             int id,
             const ACaptureSessionOutputContainer* outputs,
             const ACameraCaptureSession_stateCallbacks* cb,
-            CameraDevice* device) :
+            android::acam::CameraDevice* device) :
             mId(id), mOutput(*outputs), mUserSessionCallback(*cb),
             mDevice(device) {}
 
@@ -82,13 +91,15 @@
 
     camera_status_t abortCaptures();
 
+    template<class T>
     camera_status_t setRepeatingRequest(
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*optional*/int* captureSequenceId);
 
+    template<class T>
     camera_status_t capture(
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*optional*/int* captureSequenceId);
 
@@ -97,18 +108,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/ACameraCaptureSession.inc b/camera/ndk/impl/ACameraCaptureSession.inc
new file mode 100644
index 0000000..86bf8a5
--- /dev/null
+++ b/camera/ndk/impl/ACameraCaptureSession.inc
@@ -0,0 +1,68 @@
+/*
+ * 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 "ACameraCaptureSession.h"
+
+#ifdef __ANDROID_VNDK__
+#include "ndk_vendor/impl/ACameraDeviceVendor.inc"
+#else
+#include "ACameraDevice.inc"
+#endif
+
+using namespace android;
+
+template <class T>
+camera_status_t
+ACameraCaptureSession::setRepeatingRequest(
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    sp<acam::CameraDevice> dev = getDeviceSp();
+    if (dev == nullptr) {
+        ALOGE("Error: Device associated with session %p has been closed!", this);
+        return ACAMERA_ERROR_SESSION_CLOSED;
+    }
+
+    camera_status_t ret;
+    dev->lockDeviceForSessionOps();
+    {
+        Mutex::Autolock _l(mSessionLock);
+        ret = dev->setRepeatingRequestsLocked(
+                this, cbs, numRequests, requests, captureSequenceId);
+    }
+    dev->unlockDevice();
+    return ret;
+}
+
+template <class T>
+camera_status_t ACameraCaptureSession::capture(
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    sp<acam::CameraDevice> dev = getDeviceSp();
+    if (dev == nullptr) {
+        ALOGE("Error: Device associated with session %p has been closed!", this);
+        return ACAMERA_ERROR_SESSION_CLOSED;
+    }
+    camera_status_t ret;
+    dev->lockDeviceForSessionOps();
+    {
+        Mutex::Autolock _l(mSessionLock);
+        ret = dev->captureLocked(this, cbs, numRequests, requests, captureSequenceId);
+    }
+    dev->unlockDevice();
+    return ret;
+}
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 907debc..d24cb81 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -20,16 +20,21 @@
 #include <vector>
 #include <inttypes.h>
 #include <android/hardware/ICameraService.h>
-#include <camera2/SubmitInfo.h>
 #include <gui/Surface.h>
 #include "ACameraDevice.h"
 #include "ACameraMetadata.h"
 #include "ACaptureRequest.h"
 #include "ACameraCaptureSession.h"
 
-using namespace android;
+#include "ACameraCaptureSession.inc"
+
+ACameraDevice::~ACameraDevice() {
+    mDevice->stopLooper();
+}
 
 namespace android {
+namespace acam {
+
 // Static member definitions
 const char* CameraDevice::kContextKey        = "Context";
 const char* CameraDevice::kDeviceKey         = "Device";
@@ -39,10 +44,12 @@
 const char* CameraDevice::kCaptureRequestKey = "CaptureRequest";
 const char* CameraDevice::kTimeStampKey      = "TimeStamp";
 const char* CameraDevice::kCaptureResultKey  = "CaptureResult";
+const char* CameraDevice::kPhysicalCaptureResultKey = "PhysicalCaptureResult";
 const char* CameraDevice::kCaptureFailureKey = "CaptureFailure";
 const char* CameraDevice::kSequenceIdKey     = "SequenceId";
 const char* CameraDevice::kFrameNumberKey    = "FrameNumber";
 const char* CameraDevice::kAnwKey            = "Anw";
+const char* CameraDevice::kFailingPhysicalCameraId= "FailingPhysicalCameraId";
 
 /**
  * CameraDevice Implementation
@@ -50,11 +57,11 @@
 CameraDevice::CameraDevice(
         const char* id,
         ACameraDevice_StateCallbacks* cb,
-        std::unique_ptr<ACameraMetadata> chars,
+        sp<ACameraMetadata> chars,
         ACameraDevice* wrapper) :
         mCameraId(id),
         mAppCallbacks(*cb),
-        mChars(std::move(chars)),
+        mChars(chars),
         mServiceCallback(new ServiceCallback(this)),
         mWrapper(wrapper),
         mInError(false),
@@ -74,7 +81,7 @@
                 __FUNCTION__, strerror(-err), err);
         setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
     }
-    mHandler = new CallbackHandler();
+    mHandler = new CallbackHandler(id);
     mCbLooper->registerHandler(mHandler);
 
     const CameraMetadata& metadata = mChars->getInternalData();
@@ -95,6 +102,14 @@
         mShadingMapSize[0] = entry.data.i32[0];
         mShadingMapSize[1] = entry.data.i32[1];
     }
+
+    size_t physicalIdCnt = 0;
+    const char*const* physicalCameraIds;
+    if (mChars->isLogicalMultiCamera(&physicalIdCnt, &physicalCameraIds)) {
+        for (size_t i = 0; i < physicalIdCnt; i++) {
+            mPhysicalIds.push_back(physicalCameraIds[i]);
+        }
+    }
 }
 
 // Device close implementaiton
@@ -105,14 +120,10 @@
         if (!isClosed()) {
             disconnectLocked(session);
         }
+        LOG_ALWAYS_FATAL_IF(mCbLooper != nullptr,
+                "CameraDevice looper should've been stopped before ~CameraDevice");
         mCurrentSession = nullptr;
-        if (mCbLooper != nullptr) {
-            mCbLooper->unregisterHandler(mHandler->id());
-            mCbLooper->stop();
-        }
     }
-    mCbLooper.clear();
-    mHandler.clear();
 }
 
 void
@@ -127,8 +138,29 @@
 camera_status_t
 CameraDevice::createCaptureRequest(
         ACameraDevice_request_template templateId,
+        const ACameraIdList* physicalIdList,
         ACaptureRequest** request) const {
     Mutex::Autolock _l(mDeviceLock);
+
+    if (physicalIdList != nullptr) {
+        if (physicalIdList->numCameras > static_cast<int>(mPhysicalIds.size())) {
+            ALOGE("%s: physicalIdList size %d exceeds number of available physical cameras %zu",
+                    __FUNCTION__, physicalIdList->numCameras, mPhysicalIds.size());
+            return ACAMERA_ERROR_INVALID_PARAMETER;
+        }
+        for (auto i = 0; i < physicalIdList->numCameras; i++) {
+            if (physicalIdList->cameraIds[i] == nullptr) {
+                ALOGE("%s: physicalId is null!", __FUNCTION__);
+                return ACAMERA_ERROR_INVALID_PARAMETER;
+            }
+            if (mPhysicalIds.end() == std::find(
+                    mPhysicalIds.begin(), mPhysicalIds.end(), physicalIdList->cameraIds[i])) {
+                ALOGE("%s: Invalid physicalId %s!", __FUNCTION__, physicalIdList->cameraIds[i]);
+                return ACAMERA_ERROR_INVALID_PARAMETER;
+            }
+        }
+    }
+
     camera_status_t ret = checkCameraClosedOrErrorLocked();
     if (ret != ACAMERA_OK) {
         return ret;
@@ -149,6 +181,12 @@
     }
     ACaptureRequest* outReq = new ACaptureRequest();
     outReq->settings = new ACameraMetadata(rawRequest.release(), ACameraMetadata::ACM_REQUEST);
+    if (physicalIdList != nullptr) {
+        for (auto i = 0; i < physicalIdList->numCameras; i++) {
+            outReq->physicalSettings.emplace(physicalIdList->cameraIds[i],
+                    new ACameraMetadata(*(outReq->settings)));
+        }
+    }
     outReq->targets  = new ACameraOutputTargets();
     *request = outReq;
     return ACAMERA_OK;
@@ -190,104 +228,53 @@
     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);
-}
-
-camera_status_t
-CameraDevice::submitRequestsLocked(
-        sp<ACameraCaptureSession> session,
-        /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
-        int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId,
-        bool isRepeating) {
+camera_status_t CameraDevice::isSessionConfigurationSupported(
+        const ACaptureSessionOutputContainer* sessionOutputContainer) const {
+    Mutex::Autolock _l(mDeviceLock);
     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<hardware::camera2::CaptureRequest> requestList;
-    Vector<sp<CaptureRequest> > requestsV;
-    requestsV.setCapacity(numRequests);
-    for (int i = 0; i < numRequests; i++) {
-        sp<CaptureRequest> req;
-        ret = allocateCaptureRequest(requests[i], req);
+    SessionConfiguration sessionConfiguration(0 /*inputWidth*/, 0 /*inputHeight*/,
+            -1 /*inputFormat*/, CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE);
+    for (const auto& output : sessionOutputContainer->mOutputs) {
+        sp<IGraphicBufferProducer> iGBP(nullptr);
+        ret = getIGBPfromAnw(output.mWindow, iGBP);
         if (ret != ACAMERA_OK) {
-            ALOGE("Convert capture request to internal format failure! ret %d", ret);
+            ALOGE("Camera device %s failed to extract graphic producer from native window",
+                    getId());
             return ret;
         }
-        if (req->mSurfaceList.empty()) {
-            ALOGE("Capture request without output target cannot be submitted!");
-            return ACAMERA_ERROR_INVALID_PARAMETER;
+
+        String16 physicalId16(output.mPhysicalCameraId.c_str());
+        OutputConfiguration outConfig(iGBP, output.mRotation, physicalId16,
+                OutputConfiguration::INVALID_SET_ID, true);
+
+        for (auto& anw : output.mSharedWindows) {
+            ret = getIGBPfromAnw(anw, iGBP);
+            if (ret != ACAMERA_OK) {
+                ALOGE("Camera device %s failed to extract graphic producer from native window",
+                        getId());
+                return ret;
+            }
+            outConfig.addGraphicProducer(iGBP);
         }
-        requestList.push_back(*(req.get()));
-        requestsV.push_back(req);
+
+        sessionConfiguration.addOutputConfiguration(outConfig);
     }
 
-    if (isRepeating) {
-        ret = stopRepeatingLocked();
-        if (ret != ACAMERA_OK) {
-            ALOGE("Camera %s stop repeating failed! ret %d", getId(), ret);
-            return ret;
-        }
-    }
-
-    binder::Status remoteRet;
-    hardware::camera2::utils::SubmitInfo info;
-    remoteRet = mRemote->submitRequestList(requestList, isRepeating, &info);
-    int sequenceId = info.mRequestId;
-    int64_t lastFrameNumber = info.mLastFrameNumber;
-    if (sequenceId < 0) {
-        ALOGE("Camera %s submit request remote failure: ret %d", getId(), sequenceId);
+    bool supported = false;
+    binder::Status remoteRet = mRemote->isSessionConfigurationSupported(
+            sessionConfiguration, &supported);
+    if (remoteRet.serviceSpecificErrorCode() ==
+            hardware::ICameraService::ERROR_INVALID_OPERATION) {
+        return ACAMERA_ERROR_UNSUPPORTED_OPERATION;
+    } else if (!remoteRet.isOk()) {
         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));
+        return supported ? ACAMERA_OK : ACAMERA_ERROR_STREAM_CONFIGURE_FAIL;
     }
-
-    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) {
@@ -325,8 +312,9 @@
         return ret;
     }
 
-    OutputConfiguration outConfig(iGBP, output->mRotation, OutputConfiguration::INVALID_SET_ID,
-            true);
+    String16 physicalId16(output->mPhysicalCameraId.c_str());
+    OutputConfiguration outConfig(iGBP, output->mRotation, physicalId16,
+            OutputConfiguration::INVALID_SET_ID, true);
 
     for (auto& anw : output->mSharedWindows) {
         ret = getIGBPfromAnw(anw, iGBP);
@@ -372,8 +360,12 @@
         const ACaptureRequest* request, /*out*/sp<CaptureRequest>& outReq) {
     camera_status_t ret;
     sp<CaptureRequest> req(new CaptureRequest());
-    req->mPhysicalCameraSettings.push_back({std::string(mCameraId.string()),
+    req->mPhysicalCameraSettings.push_back({getId(),
             request->settings->getInternalData()});
+    for (auto& entry : request->physicalSettings) {
+        req->mPhysicalCameraSettings.push_back({entry.first,
+                entry.second->getInternalData()});
+    }
     req->mIsReprocess = false; // NDK does not support reprocessing yet
     req->mContext = request->context;
     req->mSurfaceConverted = true; // set to true, and fill in stream/surface idx to speed up IPC
@@ -417,10 +409,17 @@
 }
 
 ACaptureRequest*
-CameraDevice::allocateACaptureRequest(sp<CaptureRequest>& req) {
+CameraDevice::allocateACaptureRequest(sp<CaptureRequest>& req, const std::string& deviceId) {
     ACaptureRequest* pRequest = new ACaptureRequest();
-    CameraMetadata clone = req->mPhysicalCameraSettings.begin()->settings;
-    pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST);
+    for (auto& entry : req->mPhysicalCameraSettings) {
+        CameraMetadata clone = entry.settings;
+        if (entry.id == deviceId) {
+            pRequest->settings = new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST);
+        } else {
+            pRequest->physicalSettings.emplace(entry.id,
+                    new ACameraMetadata(clone.release(), ACameraMetadata::ACM_REQUEST));
+        }
+    }
     pRequest->targets  = new ACameraOutputTargets();
     for (size_t i = 0; i < req->mSurfaceList.size(); i++) {
         ANativeWindow* anw = static_cast<ANativeWindow*>(req->mSurfaceList[i].get());
@@ -436,7 +435,8 @@
     if (req == nullptr) {
         return;
     }
-    delete req->settings;
+    req->settings.clear();
+    req->physicalSettings.clear();
     delete req->targets;
     delete req;
 }
@@ -633,15 +633,16 @@
     }
 
     std::set<std::pair<ANativeWindow*, OutputConfiguration>> outputSet;
-    for (auto outConfig : outputs->mOutputs) {
+    for (const auto& outConfig : outputs->mOutputs) {
         ANativeWindow* anw = outConfig.mWindow;
         sp<IGraphicBufferProducer> iGBP(nullptr);
         ret = getIGBPfromAnw(anw, iGBP);
         if (ret != ACAMERA_OK) {
             return ret;
         }
+        String16 physicalId16(outConfig.mPhysicalCameraId.c_str());
         outputSet.insert(std::make_pair(
-                anw, OutputConfiguration(iGBP, outConfig.mRotation,
+                anw, OutputConfiguration(iGBP, outConfig.mRotation, physicalId16,
                         OutputConfiguration::INVALID_SET_ID, outConfig.mIsShared)));
     }
     auto addSet = outputSet;
@@ -706,7 +707,7 @@
     }
 
     // add new streams
-    for (auto outputPair : addSet) {
+    for (const auto& outputPair : addSet) {
         int streamId;
         remoteRet = mRemote->createStream(outputPair.second, &streamId);
         if (!remoteRet.isOk()) {
@@ -829,7 +830,7 @@
     if (errorCode == hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER) {
         int32_t streamId = resultExtras.errorStreamId;
         ACameraCaptureSession_captureCallback_bufferLost onBufferLost =
-                cbh.mCallbacks.onCaptureBufferLost;
+                cbh.mOnCaptureBufferLost;
         auto outputPairIt = mConfiguredOutputs.find(streamId);
         if (outputPairIt == mConfiguredOutputs.end()) {
             ALOGE("%s: Error: stream id %d does not exist", __FUNCTION__, streamId);
@@ -839,14 +840,14 @@
 
         const auto& gbps = outputPairIt->second.second.getGraphicBufferProducers();
         for (const auto& outGbp : gbps) {
-            for (auto surface : request->mSurfaceList) {
+            for (const auto& surface : request->mSurfaceList) {
                 if (surface->getIGraphicBufferProducer() == outGbp) {
                     ANativeWindow* anw = static_cast<ANativeWindow*>(surface.get());
                     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->setPointer(kContextKey, cbh.mContext);
                     msg->setObject(kSessionSpKey, session);
                     msg->setPointer(kCallbackFpKey, (void*) onBufferLost);
                     msg->setObject(kCaptureRequestKey, request);
@@ -858,7 +859,7 @@
         }
     } else { // Handle other capture failures
         // Fire capture failure callback if there is one registered
-        ACameraCaptureSession_captureCallback_failed onError = cbh.mCallbacks.onCaptureFailed;
+        ACameraCaptureSession_captureCallback_failed onError = cbh.mOnCaptureFailed;
         sp<CameraCaptureFailure> failure(new CameraCaptureFailure());
         failure->frameNumber = frameNumber;
         // TODO: refine this when implementing flush
@@ -867,10 +868,19 @@
         failure->wasImageCaptured = (errorCode ==
                 hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT);
 
-        sp<AMessage> msg = new AMessage(kWhatCaptureFail, mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        sp<AMessage> msg = new AMessage(cbh.mIsLogicalCameraCallback ? kWhatLogicalCaptureFail :
+                kWhatCaptureFail, mHandler);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, session);
-        msg->setPointer(kCallbackFpKey, (void*) onError);
+        if (cbh.mIsLogicalCameraCallback) {
+            if (resultExtras.errorPhysicalCameraId.size() > 0) {
+                String8 cameraId(resultExtras.errorPhysicalCameraId);
+                msg->setString(kFailingPhysicalCameraId, cameraId.string(), cameraId.size());
+            }
+            msg->setPointer(kCallbackFpKey, (void*) cbh.mOnLogicalCameraCaptureFailed);
+        } else {
+            msg->setPointer(kCallbackFpKey, (void*) onError);
+        }
         msg->setObject(kCaptureRequestKey, request);
         msg->setObject(kCaptureFailureKey, failure);
         postSessionMsgAndCleanup(msg);
@@ -882,6 +892,19 @@
     return;
 }
 
+void CameraDevice::stopLooper() {
+    Mutex::Autolock _l(mDeviceLock);
+    if (mCbLooper != nullptr) {
+      mCbLooper->unregisterHandler(mHandler->id());
+      mCbLooper->stop();
+    }
+    mCbLooper.clear();
+    mHandler.clear();
+}
+
+CameraDevice::CallbackHandler::CallbackHandler(const char* id) : mId(id) {
+}
+
 void CameraDevice::CallbackHandler::onMessageReceived(
         const sp<AMessage> &msg) {
     switch (msg->what()) {
@@ -890,7 +913,9 @@
         case kWhatSessionStateCb:
         case kWhatCaptureStart:
         case kWhatCaptureResult:
+        case kWhatLogicalCaptureResult:
         case kWhatCaptureFail:
+        case kWhatLogicalCaptureFail:
         case kWhatCaptureSeqEnd:
         case kWhatCaptureSeqAbort:
         case kWhatCaptureBufferLost:
@@ -960,7 +985,9 @@
         case kWhatSessionStateCb:
         case kWhatCaptureStart:
         case kWhatCaptureResult:
+        case kWhatLogicalCaptureResult:
         case kWhatCaptureFail:
+        case kWhatLogicalCaptureFail:
         case kWhatCaptureSeqEnd:
         case kWhatCaptureSeqAbort:
         case kWhatCaptureBufferLost:
@@ -977,7 +1004,9 @@
             switch (msg->what()) {
                 case kWhatCaptureStart:
                 case kWhatCaptureResult:
+                case kWhatLogicalCaptureResult:
                 case kWhatCaptureFail:
+                case kWhatLogicalCaptureFail:
                 case kWhatCaptureBufferLost:
                     found = msg->findObject(kCaptureRequestKey, &obj);
                     if (!found) {
@@ -1020,7 +1049,7 @@
                         ALOGE("%s: Cannot find timestamp!", __FUNCTION__);
                         return;
                     }
-                    ACaptureRequest* request = allocateACaptureRequest(requestSp);
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp, mId);
                     (*onStart)(context, session.get(), request, timestamp);
                     freeACaptureRequest(request);
                     break;
@@ -1043,11 +1072,69 @@
                         return;
                     }
                     sp<ACameraMetadata> result(static_cast<ACameraMetadata*>(obj.get()));
-                    ACaptureRequest* request = allocateACaptureRequest(requestSp);
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp, mId);
                     (*onResult)(context, session.get(), request, result.get());
                     freeACaptureRequest(request);
                     break;
                 }
+                case kWhatLogicalCaptureResult:
+                {
+                    ACameraCaptureSession_logicalCamera_captureCallback_result onResult;
+                    found = msg->findPointer(kCallbackFpKey, (void**) &onResult);
+                    if (!found) {
+                        ALOGE("%s: Cannot find logicalCamera 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()));
+
+                    found = msg->findObject(kPhysicalCaptureResultKey, &obj);
+                    if (!found) {
+                        ALOGE("%s: Cannot find physical capture result!", __FUNCTION__);
+                        return;
+                    }
+                    sp<ACameraPhysicalCaptureResultInfo> physicalResult(
+                            static_cast<ACameraPhysicalCaptureResultInfo*>(obj.get()));
+                    std::vector<PhysicalCaptureResultInfo>& physicalResultInfo =
+                            physicalResult->mPhysicalResultInfo;
+
+                    std::vector<std::string> physicalCameraIds;
+                    std::vector<sp<ACameraMetadata>> physicalMetadataCopy;
+                    for (size_t i = 0; i < physicalResultInfo.size(); i++) {
+                        String8 physicalId8(physicalResultInfo[i].mPhysicalCameraId);
+                        physicalCameraIds.push_back(physicalId8.c_str());
+
+                        CameraMetadata clone = physicalResultInfo[i].mPhysicalCameraMetadata;
+                        clone.update(ANDROID_SYNC_FRAME_NUMBER,
+                                &physicalResult->mFrameNumber, /*data_count*/1);
+                        sp<ACameraMetadata> metadata =
+                                new ACameraMetadata(clone.release(), ACameraMetadata::ACM_RESULT);
+                        physicalMetadataCopy.push_back(metadata);
+                    }
+
+                    std::vector<const char*> physicalCameraIdPtrs;
+                    std::vector<const ACameraMetadata*> physicalMetadataCopyPtrs;
+                    for (size_t i = 0; i < physicalResultInfo.size(); i++) {
+                        physicalCameraIdPtrs.push_back(physicalCameraIds[i].c_str());
+                        physicalMetadataCopyPtrs.push_back(physicalMetadataCopy[i].get());
+                    }
+
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp, mId);
+                    (*onResult)(context, session.get(), request, result.get(),
+                            physicalResultInfo.size(), physicalCameraIdPtrs.data(),
+                            physicalMetadataCopyPtrs.data());
+                    freeACaptureRequest(request);
+                    break;
+                }
                 case kWhatCaptureFail:
                 {
                     ACameraCaptureSession_captureCallback_failed onFail;
@@ -1069,11 +1156,44 @@
                             static_cast<CameraCaptureFailure*>(obj.get()));
                     ACameraCaptureFailure* failure =
                             static_cast<ACameraCaptureFailure*>(failureSp.get());
-                    ACaptureRequest* request = allocateACaptureRequest(requestSp);
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp, mId);
                     (*onFail)(context, session.get(), request, failure);
                     freeACaptureRequest(request);
                     break;
                 }
+                case kWhatLogicalCaptureFail:
+                {
+                    ACameraCaptureSession_logicalCamera_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()));
+                    ALogicalCameraCaptureFailure failure;
+                    AString physicalCameraId;
+                    found = msg->findString(kFailingPhysicalCameraId, &physicalCameraId);
+                    if (found && !physicalCameraId.empty()) {
+                        failure.physicalCameraId = physicalCameraId.c_str();
+                    } else {
+                        failure.physicalCameraId = nullptr;
+                    }
+                    failure.captureFailure = *failureSp;
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp, mId);
+                    (*onFail)(context, session.get(), request, &failure);
+                    freeACaptureRequest(request);
+                    break;
+                }
                 case kWhatCaptureSeqEnd:
                 {
                     ACameraCaptureSession_captureCallback_sequenceEnd onSeqEnd;
@@ -1146,7 +1266,7 @@
                         return;
                     }
 
-                    ACaptureRequest* request = allocateACaptureRequest(requestSp);
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp, mId);
                     (*onBufferLost)(context, session.get(), request, anw, frameNumber);
                     freeACaptureRequest(request);
                     break;
@@ -1158,12 +1278,36 @@
 }
 
 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)) {}
+        sp<ACameraCaptureSession>          session,
+        const Vector<sp<CaptureRequest> >& requests,
+        bool                               isRepeating,
+        ACameraCaptureSession_captureCallbacks* cbs) :
+        mSession(session), mRequests(requests),
+        mIsRepeating(isRepeating),
+        mIsLogicalCameraCallback(false) {
+    initCaptureCallbacks(cbs);
+
+    if (cbs != nullptr) {
+        mOnCaptureCompleted = cbs->onCaptureCompleted;
+        mOnCaptureFailed = cbs->onCaptureFailed;
+    }
+}
+
+CameraDevice::CallbackHolder::CallbackHolder(
+        sp<ACameraCaptureSession>          session,
+        const Vector<sp<CaptureRequest> >& requests,
+        bool                               isRepeating,
+        ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs) :
+        mSession(session), mRequests(requests),
+        mIsRepeating(isRepeating),
+        mIsLogicalCameraCallback(true) {
+    initCaptureCallbacks(lcbs);
+
+    if (lcbs != nullptr) {
+        mOnLogicalCameraCaptureCompleted = lcbs->onLogicalCameraCaptureCompleted;
+        mOnLogicalCameraCaptureFailed = lcbs->onLogicalCameraCaptureFailed;
+    }
+}
 
 void
 CameraDevice::checkRepeatingSequenceCompleteLocked(
@@ -1180,9 +1324,9 @@
         mSequenceCallbackMap.erase(cbIt);
         // send seq aborted callback
         sp<AMessage> msg = new AMessage(kWhatCaptureSeqAbort, mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, cbh.mSession);
-        msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceAborted);
+        msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceAborted);
         msg->setInt32(kSequenceIdKey, sequenceId);
         postSessionMsgAndCleanup(msg);
     } else {
@@ -1230,9 +1374,9 @@
             mSequenceCallbackMap.erase(cbIt);
             // send seq complete callback
             sp<AMessage> msg = new AMessage(kWhatCaptureSeqEnd, mHandler);
-            msg->setPointer(kContextKey, cbh.mCallbacks.context);
+            msg->setPointer(kContextKey, cbh.mContext);
             msg->setObject(kSessionSpKey, cbh.mSession);
-            msg->setPointer(kCallbackFpKey, (void*) cbh.mCallbacks.onCaptureSequenceCompleted);
+            msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceCompleted);
             msg->setInt32(kSequenceIdKey, sequenceId);
             msg->setInt64(kFrameNumberKey, lastFrameNumber);
 
@@ -1290,16 +1434,21 @@
         }
         default:
             ALOGE("Unknown error from camera device: %d", errorCode);
-            // no break
+            [[fallthrough]];
         case ERROR_CAMERA_DEVICE:
         case ERROR_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 ERROR_CAMERA_DEVICE:
                     dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_DEVICE);
                     break;
                 case ERROR_CAMERA_SERVICE:
                     dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+                    errorVal = ::ERROR_CAMERA_SERVICE;
                     break;
                 default:
                     dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_UNKNOWN);
@@ -1309,7 +1458,7 @@
             msg->setPointer(kContextKey, dev->mAppCallbacks.context);
             msg->setPointer(kDeviceKey, (void*) dev->getWrapper());
             msg->setPointer(kCallbackFpKey, (void*) dev->mAppCallbacks.onError);
-            msg->setInt32(kErrorCodeKey, errorCode);
+            msg->setInt32(kErrorCodeKey, errorVal);
             msg->post();
             break;
         }
@@ -1384,7 +1533,7 @@
     auto it = dev->mSequenceCallbackMap.find(sequenceId);
     if (it != dev->mSequenceCallbackMap.end()) {
         CallbackHolder cbh = (*it).second;
-        ACameraCaptureSession_captureCallback_start onStart = cbh.mCallbacks.onCaptureStarted;
+        ACameraCaptureSession_captureCallback_start onStart = cbh.mOnCaptureStarted;
         sp<ACameraCaptureSession> session = cbh.mSession;
         if ((size_t) burstId >= cbh.mRequests.size()) {
             ALOGE("%s: Error: request index %d out of bound (size %zu)",
@@ -1393,7 +1542,7 @@
         }
         sp<CaptureRequest> request = cbh.mRequests[burstId];
         sp<AMessage> msg = new AMessage(kWhatCaptureStart, dev->mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, session);
         msg->setPointer(kCallbackFpKey, (void*) onStart);
         msg->setObject(kCaptureRequestKey, request);
@@ -1408,7 +1557,6 @@
         const CameraMetadata& metadata,
         const CaptureResultExtras& resultExtras,
         const std::vector<PhysicalCaptureResultInfo>& physicalResultInfos) {
-    (void) physicalResultInfos;
     binder::Status ret = binder::Status::ok();
 
     sp<CameraDevice> dev = mDevice.promote();
@@ -1444,9 +1592,6 @@
     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)",
@@ -1456,13 +1601,27 @@
         sp<CaptureRequest> request = cbh.mRequests[burstId];
         sp<ACameraMetadata> result(new ACameraMetadata(
                 metadataCopy.release(), ACameraMetadata::ACM_RESULT));
+        sp<ACameraPhysicalCaptureResultInfo> physicalResult(
+                new ACameraPhysicalCaptureResultInfo(physicalResultInfos, frameNumber));
 
-        sp<AMessage> msg = new AMessage(kWhatCaptureResult, dev->mHandler);
-        msg->setPointer(kContextKey, cbh.mCallbacks.context);
+        sp<AMessage> msg = new AMessage(
+                cbh.mIsLogicalCameraCallback ? kWhatLogicalCaptureResult : kWhatCaptureResult,
+                dev->mHandler);
+        msg->setPointer(kContextKey, cbh.mContext);
         msg->setObject(kSessionSpKey, session);
-        msg->setPointer(kCallbackFpKey, (void*) onResult);
         msg->setObject(kCaptureRequestKey, request);
         msg->setObject(kCaptureResultKey, result);
+        if (isPartialResult) {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnCaptureProgressed);
+        } else if (cbh.mIsLogicalCameraCallback) {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnLogicalCameraCaptureCompleted);
+            msg->setObject(kPhysicalCaptureResultKey, physicalResult);
+        } else {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnCaptureCompleted);
+        }
         dev->postSessionMsgAndCleanup(msg);
     }
 
@@ -1508,5 +1667,5 @@
     return ret;
 }
 
-
+} // namespace acam
 } // namespace android
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 1369148..7a35bf0 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -21,6 +21,7 @@
 #include <set>
 #include <atomic>
 #include <utility>
+#include <vector>
 #include <utils/StrongPointer.h>
 #include <utils/Mutex.h>
 #include <utils/String8.h>
@@ -34,6 +35,7 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <camera/CaptureResult.h>
 #include <camera/camera2/OutputConfiguration.h>
+#include <camera/camera2/SessionConfiguration.h>
 #include <camera/camera2/CaptureRequest.h>
 
 #include <camera/NdkCameraManager.h>
@@ -41,14 +43,25 @@
 #include "ACameraMetadata.h"
 
 namespace android {
+namespace acam {
 
 // Wrap ACameraCaptureFailure so it can be ref-counted
 struct CameraCaptureFailure : public RefBase, public ACameraCaptureFailure {};
 
+// Wrap PhysicalCaptureResultInfo so that it can be ref-counted
+struct ACameraPhysicalCaptureResultInfo: public RefBase {
+    ACameraPhysicalCaptureResultInfo(const std::vector<PhysicalCaptureResultInfo>& info,
+            int64_t frameNumber) :
+        mPhysicalResultInfo(info), mFrameNumber(frameNumber) {}
+
+    std::vector<PhysicalCaptureResultInfo> mPhysicalResultInfo;
+    int64_t mFrameNumber;
+};
+
 class CameraDevice final : public RefBase {
   public:
     CameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
-                  std::unique_ptr<ACameraMetadata> chars,
+                  sp<ACameraMetadata> chars,
                   ACameraDevice* wrapper);
     ~CameraDevice();
 
@@ -56,6 +69,7 @@
 
     camera_status_t createCaptureRequest(
             ACameraDevice_request_template templateId,
+            const ACameraIdList* physicalIdList,
             ACaptureRequest** request) const;
 
     camera_status_t createCaptureSession(
@@ -64,6 +78,9 @@
             const ACameraCaptureSession_stateCallbacks* callbacks,
             /*out*/ACameraCaptureSession** session);
 
+    camera_status_t isSessionConfigurationSupported(
+            const ACaptureSessionOutputContainer* sessionOutputContainer) const;
+
     // Callbacks from camera service
     class ServiceCallback : public hardware::camera2::BnCameraDeviceCallbacks {
       public:
@@ -92,6 +109,9 @@
 
     inline ACameraDevice* getWrapper() const { return mWrapper; };
 
+    // Stop the looper thread and unregister the handler
+    void stopLooper();
+
   private:
     friend ACameraCaptureSession;
     camera_status_t checkCameraClosedOrErrorLocked() const;
@@ -108,19 +128,22 @@
     camera_status_t waitUntilIdleLocked();
 
 
+    template<class T>
     camera_status_t captureLocked(sp<ACameraCaptureSession> session,
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*optional*/int* captureSequenceId);
 
+    template<class T>
     camera_status_t setRepeatingRequestsLocked(sp<ACameraCaptureSession> session,
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*optional*/int* captureSequenceId);
 
+    template<class T>
     camera_status_t submitRequestsLocked(
             sp<ACameraCaptureSession> session,
-            /*optional*/ACameraCaptureSession_captureCallbacks* cbs,
+            /*optional*/T* cbs,
             int numRequests, ACaptureRequest** requests,
             /*out*/int* captureSequenceId,
             bool isRepeating);
@@ -130,7 +153,8 @@
     camera_status_t allocateCaptureRequest(
             const ACaptureRequest* request, sp<CaptureRequest>& outReq);
 
-    static ACaptureRequest* allocateACaptureRequest(sp<CaptureRequest>& req);
+    static ACaptureRequest* allocateACaptureRequest(sp<CaptureRequest>& req,
+            const std::string& deviceId);
     static void freeACaptureRequest(ACaptureRequest*);
 
     // only For session to hold device lock
@@ -156,7 +180,7 @@
     mutable Mutex mDeviceLock;
     const String8 mCameraId;                          // Camera ID
     const ACameraDevice_StateCallbacks mAppCallbacks; // Callback to app
-    const std::unique_ptr<ACameraMetadata> mChars;    // Camera characteristics
+    const sp<ACameraMetadata> mChars;                 // Camera characteristics
     const sp<ServiceCallback> mServiceCallback;
     ACameraDevice* mWrapper;
 
@@ -191,7 +215,9 @@
         // Capture callbacks
         kWhatCaptureStart,     // onCaptureStarted
         kWhatCaptureResult,    // onCaptureProgressed, onCaptureCompleted
+        kWhatLogicalCaptureResult, // onLogicalCameraCaptureCompleted
         kWhatCaptureFail,      // onCaptureFailed
+        kWhatLogicalCaptureFail, // onLogicalCameraCaptureFailed
         kWhatCaptureSeqEnd,    // onCaptureSequenceCompleted
         kWhatCaptureSeqAbort,  // onCaptureSequenceAborted
         kWhatCaptureBufferLost,// onCaptureBufferLost
@@ -206,16 +232,20 @@
     static const char* kCaptureRequestKey;
     static const char* kTimeStampKey;
     static const char* kCaptureResultKey;
+    static const char* kPhysicalCaptureResultKey;
     static const char* kCaptureFailureKey;
     static const char* kSequenceIdKey;
     static const char* kFrameNumberKey;
     static const char* kAnwKey;
+    static const char* kFailingPhysicalCameraId;
 
     class CallbackHandler : public AHandler {
       public:
+        explicit CallbackHandler(const char* id);
         void onMessageReceived(const sp<AMessage> &msg) override;
 
       private:
+        std::string mId;
         // 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
@@ -244,19 +274,47 @@
                        const Vector<sp<CaptureRequest> >& requests,
                        bool                               isRepeating,
                        ACameraCaptureSession_captureCallbacks* cbs);
+        CallbackHolder(sp<ACameraCaptureSession>          session,
+                       const Vector<sp<CaptureRequest> >& requests,
+                       bool                               isRepeating,
+                       ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs);
 
-        static ACameraCaptureSession_captureCallbacks fillCb(
-                ACameraCaptureSession_captureCallbacks* cbs) {
+        template <class T>
+        void initCaptureCallbacks(T* cbs) {
+            mContext = nullptr;
+            mOnCaptureStarted = nullptr;
+            mOnCaptureProgressed = nullptr;
+            mOnCaptureCompleted = nullptr;
+            mOnLogicalCameraCaptureCompleted = nullptr;
+            mOnLogicalCameraCaptureFailed = nullptr;
+            mOnCaptureFailed = nullptr;
+            mOnCaptureSequenceCompleted = nullptr;
+            mOnCaptureSequenceAborted = nullptr;
+            mOnCaptureBufferLost = nullptr;
             if (cbs != nullptr) {
-                return *cbs;
+                mContext = cbs->context;
+                mOnCaptureStarted = cbs->onCaptureStarted;
+                mOnCaptureProgressed = cbs->onCaptureProgressed;
+                mOnCaptureSequenceCompleted = cbs->onCaptureSequenceCompleted;
+                mOnCaptureSequenceAborted = cbs->onCaptureSequenceAborted;
+                mOnCaptureBufferLost = cbs->onCaptureBufferLost;
             }
-            return { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
         }
-
         sp<ACameraCaptureSession>   mSession;
         Vector<sp<CaptureRequest> > mRequests;
         const bool                  mIsRepeating;
-        ACameraCaptureSession_captureCallbacks mCallbacks;
+        const bool                  mIsLogicalCameraCallback;
+
+        void*                       mContext;
+        ACameraCaptureSession_captureCallback_start mOnCaptureStarted;
+        ACameraCaptureSession_captureCallback_result mOnCaptureProgressed;
+        ACameraCaptureSession_captureCallback_result mOnCaptureCompleted;
+        ACameraCaptureSession_logicalCamera_captureCallback_result mOnLogicalCameraCaptureCompleted;
+        ACameraCaptureSession_logicalCamera_captureCallback_failed mOnLogicalCameraCaptureFailed;
+        ACameraCaptureSession_captureCallback_failed mOnCaptureFailed;
+        ACameraCaptureSession_captureCallback_sequenceEnd mOnCaptureSequenceCompleted;
+        ACameraCaptureSession_captureCallback_sequenceAbort mOnCaptureSequenceAborted;
+        ACameraCaptureSession_captureCallback_bufferLost mOnCaptureBufferLost;
     };
     // sequence id -> callbacks map
     std::map<int, CallbackHolder> mSequenceCallbackMap;
@@ -283,9 +341,11 @@
     // Misc variables
     int32_t mShadingMapSize[2];   // const after constructor
     int32_t mPartialResultCount;  // const after constructor
+    std::vector<std::string> mPhysicalIds; // const after constructor
 
 };
 
+} // namespace acam;
 } // namespace android;
 
 /**
@@ -294,10 +354,10 @@
  */
 struct ACameraDevice {
     ACameraDevice(const char* id, ACameraDevice_StateCallbacks* cb,
-                  std::unique_ptr<ACameraMetadata> chars) :
-            mDevice(new CameraDevice(id, cb, std::move(chars), this)) {}
+                  sp<ACameraMetadata> chars) :
+            mDevice(new android::acam::CameraDevice(id, cb, chars, this)) {}
 
-    ~ACameraDevice() {};
+    ~ACameraDevice();
 
     /*******************
      * NDK public APIs *
@@ -306,8 +366,9 @@
 
     camera_status_t createCaptureRequest(
             ACameraDevice_request_template templateId,
+            const ACameraIdList* physicalCameraIdList,
             ACaptureRequest** request) const {
-        return mDevice->createCaptureRequest(templateId, request);
+        return mDevice->createCaptureRequest(templateId, physicalCameraIdList, request);
     }
 
     camera_status_t createCaptureSession(
@@ -318,6 +379,11 @@
         return mDevice->createCaptureSession(outputs, sessionParameters, callbacks, session);
     }
 
+    camera_status_t isSessionConfigurationSupported(
+            const ACaptureSessionOutputContainer* sessionOutputContainer) const {
+        return mDevice->isSessionConfigurationSupported(sessionOutputContainer);
+    }
+
     /***********************
      * Device interal APIs *
      ***********************/
@@ -331,7 +397,7 @@
     }
 
   private:
-    android::sp<android::CameraDevice> mDevice;
+    android::sp<android::acam::CameraDevice> mDevice;
 };
 
 #endif // _ACAMERA_DEVICE_H
diff --git a/camera/ndk/impl/ACameraDevice.inc b/camera/ndk/impl/ACameraDevice.inc
new file mode 100644
index 0000000..1fc5352
--- /dev/null
+++ b/camera/ndk/impl/ACameraDevice.inc
@@ -0,0 +1,130 @@
+/*
+ * 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 <vector>
+#include <inttypes.h>
+#include "ACameraDevice.h"
+#include "ACameraMetadata.h"
+#include "ACaptureRequest.h"
+#include "ACameraCaptureSession.h"
+
+namespace android {
+namespace acam {
+
+template<class T>
+camera_status_t
+CameraDevice::captureLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    return submitRequestsLocked(
+            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false);
+}
+
+template<class T>
+camera_status_t
+CameraDevice::setRepeatingRequestsLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    return submitRequestsLocked(
+            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true);
+}
+
+template<class T>
+camera_status_t CameraDevice::submitRequestsLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* 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<hardware::camera2::CaptureRequest> requestList;
+    Vector<sp<CaptureRequest> > requestsV;
+    requestsV.setCapacity(numRequests);
+    for (int i = 0; i < numRequests; i++) {
+        sp<CaptureRequest> req;
+        ret = allocateCaptureRequest(requests[i], req);
+        if (ret != ACAMERA_OK) {
+            ALOGE("Convert capture request to internal format failure! ret %d", ret);
+            return ret;
+        }
+        if (req->mSurfaceList.empty()) {
+            ALOGE("Capture request without output target cannot be submitted!");
+            return ACAMERA_ERROR_INVALID_PARAMETER;
+        }
+        requestList.push_back(*(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;
+        }
+    }
+
+    binder::Status remoteRet;
+    hardware::camera2::utils::SubmitInfo info;
+    remoteRet = mRemote->submitRequestList(requestList, isRepeating, &info);
+    int sequenceId = info.mRequestId;
+    int64_t lastFrameNumber = info.mLastFrameNumber;
+    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;
+}
+
+} // namespace acam
+} // namespace android
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index c59d0e7..9d40fd7 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";
@@ -192,6 +193,20 @@
     }
 }
 
+void CameraManagerGlobal::registerExtendedAvailabilityCallback(
+        const ACameraManager_ExtendedAvailabilityCallbacks *callback) {
+    Mutex::Autolock _l(mLock);
+    Callback cb(callback);
+    mCallbacks.insert(cb);
+}
+
+void CameraManagerGlobal::unregisterExtendedAvailabilityCallback(
+        const ACameraManager_ExtendedAvailabilityCallbacks *callback) {
+    Mutex::Autolock _l(mLock);
+    Callback cb(callback);
+    mCallbacks.erase(cb);
+}
+
 void CameraManagerGlobal::registerAvailabilityCallback(
         const ACameraManager_AvailabilityCallbacks *callback) {
     Mutex::Autolock _l(mLock);
@@ -288,12 +303,40 @@
             (*cb)(context, cameraId.c_str());
             break;
         }
+        case kWhatSendSingleAccessCallback:
+        {
+            ACameraManager_AccessPrioritiesChangedCallback 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;
+            }
+            (*cb)(context);
+            break;
+        }
         default:
             ALOGE("%s: unknown message type %d", __FUNCTION__, msg->what());
             break;
     }
 }
 
+binder::Status CameraManagerGlobal::CameraServiceListener::onCameraAccessPrioritiesChanged() {
+    sp<CameraManagerGlobal> cm = mCameraManager.promote();
+    if (cm != nullptr) {
+        cm->onCameraAccessPrioritiesChanged();
+    } else {
+        ALOGE("Cannot deliver camera access priority callback. Global camera manager died");
+    }
+    return binder::Status::ok();
+}
+
 binder::Status CameraManagerGlobal::CameraServiceListener::onStatusChanged(
         int32_t status, const String16& cameraId) {
     sp<CameraManagerGlobal> cm = mCameraManager.promote();
@@ -305,6 +348,19 @@
     return binder::Status::ok();
 }
 
+void CameraManagerGlobal::onCameraAccessPrioritiesChanged() {
+    Mutex::Autolock _l(mLock);
+    for (auto cb : mCallbacks) {
+        sp<AMessage> msg = new AMessage(kWhatSendSingleAccessCallback, mHandler);
+        ACameraManager_AccessPrioritiesChangedCallback cbFp = cb.mAccessPriorityChanged;
+        if (cbFp != nullptr) {
+            msg->setPointer(kCallbackFpKey, (void *) cbFp);
+            msg->setPointer(kContextKey, cb.mContext);
+            msg->post();
+        }
+    }
+}
+
 void CameraManagerGlobal::onStatusChanged(
         int32_t status, const String8& cameraId) {
     Mutex::Autolock _l(mLock);
@@ -345,6 +401,7 @@
     }
 }
 
+} // namespace acam
 } // namespace android
 
 /**
@@ -402,7 +459,7 @@
 }
 
 camera_status_t ACameraManager::getCameraCharacteristics(
-        const char *cameraIdStr, ACameraMetadata **characteristics) {
+        const char* cameraIdStr, sp<ACameraMetadata>* characteristics) {
     Mutex::Autolock _l(mLock);
 
     sp<hardware::ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService();
@@ -437,18 +494,16 @@
         const char* cameraId,
         ACameraDevice_StateCallbacks* callback,
         /*out*/ACameraDevice** outDevice) {
-    ACameraMetadata* rawChars;
-    camera_status_t ret = getCameraCharacteristics(cameraId, &rawChars);
+    sp<ACameraMetadata> chars;
+    camera_status_t ret = getCameraCharacteristics(cameraId, &chars);
     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;
     }
-    std::unique_ptr<ACameraMetadata> chars(rawChars);
-    rawChars = nullptr;
 
-    ACameraDevice* device = new ACameraDevice(cameraId, callback, std::move(chars));
+    ACameraDevice* device = new ACameraDevice(cameraId, callback, chars);
 
     sp<hardware::ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService();
     if (cs == nullptr) {
diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h
index cc42f77..8c1da36 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
@@ -53,6 +54,11 @@
     void unregisterAvailabilityCallback(
             const ACameraManager_AvailabilityCallbacks *callback);
 
+    void registerExtendedAvailabilityCallback(
+            const ACameraManager_ExtendedAvailabilityCallbacks* callback);
+    void unregisterExtendedAvailabilityCallback(
+            const ACameraManager_ExtendedAvailabilityCallbacks* callback);
+
     /**
      * Return camera IDs that support camera2
      */
@@ -85,6 +91,8 @@
             return binder::Status::ok();
         }
 
+        virtual binder::Status onCameraAccessPrioritiesChanged();
+
       private:
         const wp<CameraManagerGlobal> mCameraManager;
     };
@@ -95,11 +103,19 @@
         explicit Callback(const ACameraManager_AvailabilityCallbacks *callback) :
             mAvailable(callback->onCameraAvailable),
             mUnavailable(callback->onCameraUnavailable),
+            mAccessPriorityChanged(nullptr),
             mContext(callback->context) {}
 
+        explicit Callback(const ACameraManager_ExtendedAvailabilityCallbacks *callback) :
+            mAvailable(callback->availabilityCallbacks.onCameraAvailable),
+            mUnavailable(callback->availabilityCallbacks.onCameraUnavailable),
+            mAccessPriorityChanged(callback->onCameraAccessPrioritiesChanged),
+            mContext(callback->availabilityCallbacks.context) {}
+
         bool operator == (const Callback& other) const {
             return (mAvailable == other.mAvailable &&
                     mUnavailable == other.mUnavailable &&
+                    mAccessPriorityChanged == other.mAccessPriorityChanged &&
                     mContext == other.mContext);
         }
         bool operator != (const Callback& other) const {
@@ -108,6 +124,9 @@
         bool operator < (const Callback& other) const {
             if (*this == other) return false;
             if (mContext != other.mContext) return mContext < other.mContext;
+            if (mAccessPriorityChanged != other.mAccessPriorityChanged) {
+                return mAccessPriorityChanged < other.mAccessPriorityChanged;
+            }
             if (mAvailable != other.mAvailable) return mAvailable < other.mAvailable;
             return mUnavailable < other.mUnavailable;
         }
@@ -116,13 +135,15 @@
         }
         ACameraManager_AvailabilityCallback mAvailable;
         ACameraManager_AvailabilityCallback mUnavailable;
+        ACameraManager_AccessPrioritiesChangedCallback mAccessPriorityChanged;
         void*                               mContext;
     };
     std::set<Callback> mCallbacks;
 
     // definition of handler and message
     enum {
-        kWhatSendSingleCallback
+        kWhatSendSingleCallback,
+        kWhatSendSingleAccessCallback,
     };
     static const char* kCameraIdKey;
     static const char* kCallbackFpKey;
@@ -135,6 +156,7 @@
     sp<CallbackHandler> mHandler;
     sp<ALooper>         mCbLooper; // Looper thread where callbacks actually happen on
 
+    void onCameraAccessPrioritiesChanged();
     void onStatusChanged(int32_t status, const String8& cameraId);
     void onStatusChangedLocked(int32_t status, const String8& cameraId);
     // Utils for status
@@ -172,6 +194,7 @@
     ~CameraManagerGlobal();
 };
 
+} // namespace acam;
 } // namespace android;
 
 /**
@@ -180,13 +203,13 @@
  */
 struct ACameraManager {
     ACameraManager() :
-            mGlobalManager(&(android::CameraManagerGlobal::getInstance())) {}
+            mGlobalManager(&(android::acam::CameraManagerGlobal::getInstance())) {}
     ~ACameraManager();
     camera_status_t getCameraIdList(ACameraIdList** cameraIdList);
     static void     deleteCameraIdList(ACameraIdList* cameraIdList);
 
     camera_status_t getCameraCharacteristics(
-            const char *cameraId, ACameraMetadata **characteristics);
+            const char* cameraId, android::sp<ACameraMetadata>* characteristics);
     camera_status_t openCamera(const char* cameraId,
                                ACameraDevice_StateCallbacks* callback,
                                /*out*/ACameraDevice** device);
@@ -196,7 +219,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.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index fc00a2d..77dcd48 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -32,6 +32,14 @@
     if (mType == ACM_CHARACTERISTICS) {
         filterUnsupportedFeatures();
         filterStreamConfigurations();
+        filterDurations(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+        filterDurations(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS);
+        filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS);
+        filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS);
+        filterDurations(ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS);
+        filterDurations(ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS);
+        filterDurations(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS);
+        filterDurations(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS);
     }
     // TODO: filter request/result keys
 }
@@ -39,23 +47,14 @@
 bool
 ACameraMetadata::isNdkSupportedCapability(int32_t capability) {
     switch (capability) {
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT:
-            return true;
         case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING:
         case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING:
         case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO:
             return false;
         default:
-            // Newly defined capabilities will be unsupported by default (blacklist)
-            // TODO: Should we do whitelist or blacklist here?
-            ALOGE("%s: Unknonwn capability %d", __FUNCTION__, capability);
-            return false;
+            // Assuming every capability passed to this function is actually a
+            // valid capability.
+            return true;
     }
 }
 
@@ -75,11 +74,128 @@
         uint8_t capability = entry.data.u8[i];
         if (isNdkSupportedCapability(capability)) {
             capabilities.push(capability);
+
+            if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
+                derivePhysicalCameraIds();
+            }
         }
     }
     mData.update(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, capabilities);
 }
 
+void
+ACameraMetadata::derivePhysicalCameraIds() {
+    ACameraMetadata_const_entry entry;
+    auto ret = getConstEntry(ACAMERA_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS, &entry);
+    if (ret != ACAMERA_OK) {
+        ALOGE("%s: Get ACAMERA_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS key failed. ret %d",
+                __FUNCTION__, ret);
+        return;
+    }
+
+    const uint8_t* ids = entry.data.u8;
+    size_t start = 0;
+    for (size_t i = 0; i < entry.count; ++i) {
+        if (ids[i] == '\0') {
+            if (start != i) {
+                mStaticPhysicalCameraIdValues.push_back(String8((const char *)ids+start));
+                mStaticPhysicalCameraIds.push_back(mStaticPhysicalCameraIdValues.back().string());
+            }
+            start = i+1;
+        }
+    }
+
+    if (mStaticPhysicalCameraIds.size() < 2) {
+        ALOGW("%s: Logical multi-camera device only has %zu physical cameras",
+                __FUNCTION__, mStaticPhysicalCameraIds.size());
+    }
+}
+
+void
+ACameraMetadata::filterDurations(uint32_t tag) {
+    const int STREAM_CONFIGURATION_SIZE = 4;
+    const int STREAM_FORMAT_OFFSET = 0;
+    const int STREAM_WIDTH_OFFSET = 1;
+    const int STREAM_HEIGHT_OFFSET = 2;
+    const int STREAM_DURATION_OFFSET = 3;
+    camera_metadata_entry entry = mData.find(tag);
+    if (entry.count == 0 || entry.count % 4 || entry.type != TYPE_INT64) {
+        ALOGE("%s: malformed duration key %d! count %zu, type %d",
+                __FUNCTION__, tag, entry.count, entry.type);
+        return;
+    }
+    Vector<int64_t> filteredDurations;
+    filteredDurations.setCapacity(entry.count * 2);
+
+    for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
+        int64_t format = entry.data.i64[i + STREAM_FORMAT_OFFSET];
+        int64_t width = entry.data.i64[i + STREAM_WIDTH_OFFSET];
+        int64_t height = entry.data.i64[i + STREAM_HEIGHT_OFFSET];
+        int64_t duration = entry.data.i32[i + STREAM_DURATION_OFFSET];
+
+        // Leave the unfiltered format in so apps depending on previous wrong
+        // filter behavior continue to work
+        filteredDurations.push_back(format);
+        filteredDurations.push_back(width);
+        filteredDurations.push_back(height);
+        filteredDurations.push_back(duration);
+
+        // Translate HAL formats to NDK format
+        switch (tag) {
+            case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS:
+            case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS:
+                if (format == HAL_PIXEL_FORMAT_BLOB) {
+                    format = AIMAGE_FORMAT_JPEG;
+                    filteredDurations.push_back(format);
+                    filteredDurations.push_back(width);
+                    filteredDurations.push_back(height);
+                    filteredDurations.push_back(duration);
+                }
+                break;
+            case ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:
+            case ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:
+                if (format == HAL_PIXEL_FORMAT_BLOB) {
+                    format = AIMAGE_FORMAT_DEPTH_POINT_CLOUD;
+                    filteredDurations.push_back(format);
+                    filteredDurations.push_back(width);
+                    filteredDurations.push_back(height);
+                    filteredDurations.push_back(duration);
+                } else if (format == HAL_PIXEL_FORMAT_Y16) {
+                    format = AIMAGE_FORMAT_DEPTH16;
+                    filteredDurations.push_back(format);
+                    filteredDurations.push_back(width);
+                    filteredDurations.push_back(height);
+                    filteredDurations.push_back(duration);
+                }
+                break;
+            case ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS:
+            case ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS:
+                if (format == HAL_PIXEL_FORMAT_BLOB) {
+                    format = AIMAGE_FORMAT_HEIC;
+                    filteredDurations.push_back(format);
+                    filteredDurations.push_back(width);
+                    filteredDurations.push_back(height);
+                    filteredDurations.push_back(duration);
+                }
+                break;
+            case ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS:
+            case ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS:
+                if (format == HAL_PIXEL_FORMAT_BLOB) {
+                    format = AIMAGE_FORMAT_DEPTH_JPEG;
+                    filteredDurations.push_back(format);
+                    filteredDurations.push_back(width);
+                    filteredDurations.push_back(height);
+                    filteredDurations.push_back(duration);
+                }
+                break;
+            default:
+                // Should not reach here
+                ALOGE("%s: Unkown tag 0x%x", __FUNCTION__, tag);
+        }
+    }
+
+    mData.update(tag, filteredDurations);
+}
 
 void
 ACameraMetadata::filterStreamConfigurations() {
@@ -89,7 +205,7 @@
     const int STREAM_HEIGHT_OFFSET = 2;
     const int STREAM_IS_INPUT_OFFSET = 3;
     camera_metadata_entry entry = mData.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
-    if (entry.count == 0 || entry.count % 4 || entry.type != TYPE_INT32) {
+    if (entry.count > 0 && (entry.count % 4 || entry.type != TYPE_INT32)) {
         ALOGE("%s: malformed available stream configuration key! count %zu, type %d",
                 __FUNCTION__, entry.count, entry.type);
         return;
@@ -117,9 +233,17 @@
         filteredStreamConfigs.push_back(isInput);
     }
 
-    mData.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, filteredStreamConfigs);
+    if (filteredStreamConfigs.size() > 0) {
+        mData.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, filteredStreamConfigs);
+    }
 
     entry = mData.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS);
+    if (entry.count > 0 && (entry.count % 4 || entry.type != TYPE_INT32)) {
+        ALOGE("%s: malformed available depth stream configuration key! count %zu, type %d",
+                __FUNCTION__, entry.count, entry.type);
+        return;
+    }
+
     Vector<int32_t> filteredDepthStreamConfigs;
     filteredDepthStreamConfigs.setCapacity(entry.count);
 
@@ -144,7 +268,62 @@
         filteredDepthStreamConfigs.push_back(height);
         filteredDepthStreamConfigs.push_back(isInput);
     }
-    mData.update(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, filteredDepthStreamConfigs);
+
+    if (filteredDepthStreamConfigs.size() > 0) {
+        mData.update(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS,
+                filteredDepthStreamConfigs);
+    }
+
+    entry = mData.find(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS);
+    Vector<int32_t> filteredHeicStreamConfigs;
+    filteredHeicStreamConfigs.setCapacity(entry.count);
+
+    for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
+        int32_t format = entry.data.i32[i + STREAM_FORMAT_OFFSET];
+        int32_t width = entry.data.i32[i + STREAM_WIDTH_OFFSET];
+        int32_t height = entry.data.i32[i + STREAM_HEIGHT_OFFSET];
+        int32_t isInput = entry.data.i32[i + STREAM_IS_INPUT_OFFSET];
+        if (isInput == ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_INPUT) {
+            // Hide input streams
+            continue;
+        }
+        // Translate HAL formats to NDK format
+        if (format == HAL_PIXEL_FORMAT_BLOB) {
+            format = AIMAGE_FORMAT_HEIC;
+        }
+
+        filteredHeicStreamConfigs.push_back(format);
+        filteredHeicStreamConfigs.push_back(width);
+        filteredHeicStreamConfigs.push_back(height);
+        filteredHeicStreamConfigs.push_back(isInput);
+    }
+    mData.update(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, filteredHeicStreamConfigs);
+
+    entry = mData.find(ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS);
+    Vector<int32_t> filteredDynamicDepthStreamConfigs;
+    filteredDynamicDepthStreamConfigs.setCapacity(entry.count);
+
+    for (size_t i = 0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
+        int32_t format = entry.data.i32[i + STREAM_FORMAT_OFFSET];
+        int32_t width = entry.data.i32[i + STREAM_WIDTH_OFFSET];
+        int32_t height = entry.data.i32[i + STREAM_HEIGHT_OFFSET];
+        int32_t isInput = entry.data.i32[i + STREAM_IS_INPUT_OFFSET];
+        if (isInput == ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_INPUT) {
+            // Hide input streams
+            continue;
+        }
+        // Translate HAL formats to NDK format
+        if (format == HAL_PIXEL_FORMAT_BLOB) {
+            format = AIMAGE_FORMAT_DEPTH_JPEG;
+        }
+
+        filteredDynamicDepthStreamConfigs.push_back(format);
+        filteredDynamicDepthStreamConfigs.push_back(width);
+        filteredDynamicDepthStreamConfigs.push_back(height);
+        filteredDynamicDepthStreamConfigs.push_back(isInput);
+    }
+    mData.update(ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS,
+            filteredDynamicDepthStreamConfigs);
 }
 
 bool
@@ -239,6 +418,27 @@
     return mData;
 }
 
+bool
+ACameraMetadata::isLogicalMultiCamera(size_t* count, const char*const** physicalCameraIds) const {
+    if (mType != ACM_CHARACTERISTICS) {
+        ALOGE("%s must be called for a static metadata!", __FUNCTION__);
+        return false;
+    }
+    if (count == nullptr || physicalCameraIds == nullptr) {
+        ALOGE("%s: Invalid input count: %p, physicalCameraIds: %p", __FUNCTION__,
+                count, physicalCameraIds);
+        return false;
+    }
+
+    if (mStaticPhysicalCameraIds.size() >= 2) {
+        *count = mStaticPhysicalCameraIds.size();
+        *physicalCameraIds = mStaticPhysicalCameraIds.data();
+        return true;
+    }
+
+    return false;
+}
+
 // TODO: some of key below should be hidden from user
 // ex: ACAMERA_REQUEST_ID and ACAMERA_REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
 /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
@@ -360,7 +560,10 @@
     ANDROID_STATISTICS_INFO_MAX_HISTOGRAM_COUNT,
     ANDROID_STATISTICS_INFO_MAX_SHARPNESS_MAP_VALUE,
     ANDROID_STATISTICS_INFO_SHARPNESS_MAP_SIZE,
+    ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION,
     ANDROID_DEPTH_MAX_DEPTH_SAMPLES,
+    ANDROID_HEIC_INFO_SUPPORTED,
+    ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT,
 });
 
 /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h
index 0fd7efa..97f7f48 100644
--- a/camera/ndk/impl/ACameraMetadata.h
+++ b/camera/ndk/impl/ACameraMetadata.h
@@ -17,12 +17,19 @@
 #define _ACAMERA_METADATA_H
 
 #include <unordered_set>
+#include <vector>
 
 #include <sys/types.h>
 #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>
 
@@ -58,13 +65,20 @@
     camera_status_t getTags(/*out*/int32_t* numTags,
                             /*out*/const uint32_t** tags) const;
 
+    const CameraMetadata& getInternalData() const;
+    bool isLogicalMultiCamera(size_t* count, const char* const** physicalCameraIds) const;
+
+  private:
+
+    // This function does not check whether the capability passed to it is valid.
+    // The caller must make sure that it is.
     bool isNdkSupportedCapability(const int32_t capability);
     static inline bool isVendorTag(const uint32_t tag);
     static bool isCaptureRequestTag(const uint32_t tag);
     void filterUnsupportedFeatures(); // Hide features not yet supported by NDK
     void filterStreamConfigurations(); // Hide input streams, translate hal format to NDK formats
-
-    const CameraMetadata& getInternalData() const;
+    void filterDurations(uint32_t tag); // translate hal format to NDK formats
+    void derivePhysicalCameraIds(); // Derive array of physical ids.
 
     template<typename INTERNAL_T, typename NDK_T>
     camera_status_t updateImpl(uint32_t tag, uint32_t count, const NDK_T* data) {
@@ -96,7 +110,6 @@
         }
     }
 
-  private:
     // guard access of public APIs: get/update/getTags
     mutable Mutex    mLock;
     CameraMetadata   mData;
@@ -104,6 +117,9 @@
     const ACAMERA_METADATA_TYPE mType;
 
     static std::unordered_set<uint32_t> sSystemTags;
+
+    std::vector<const char*> mStaticPhysicalCameraIds;
+    std::vector<String8> mStaticPhysicalCameraIdValues;
 };
 
 #endif // _ACAMERA_METADATA_H
diff --git a/camera/ndk/impl/ACaptureRequest.h b/camera/ndk/impl/ACaptureRequest.h
index 06b2cc3..2ffcafe 100644
--- a/camera/ndk/impl/ACaptureRequest.h
+++ b/camera/ndk/impl/ACaptureRequest.h
@@ -18,11 +18,15 @@
 
 #include <camera/NdkCaptureRequest.h>
 #include <set>
+#include <unordered_map>
 
 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 +41,9 @@
         return mWindow > other.mWindow;
     }
 
-    ANativeWindow* mWindow;
+    ACameraWindowType* mWindow;
 };
+#endif
 
 struct ACameraOutputTargets {
     std::set<ACameraOutputTarget> mOutputs;
@@ -55,7 +60,8 @@
         return ACAMERA_OK;
     }
 
-    ACameraMetadata*      settings;
+    sp<ACameraMetadata> settings;
+    std::unordered_map<std::string, sp<ACameraMetadata>> physicalSettings;
     ACameraOutputTargets* targets;
     void*                 context;
 };
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index 51cef8c..07176cf 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -35,9 +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
@@ -245,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.
@@ -433,7 +434,7 @@
  *
  */
 camera_status_t ACameraCaptureSession_getDevice(
-        ACameraCaptureSession* session, /*out*/ACameraDevice** device);
+        ACameraCaptureSession* session, /*out*/ACameraDevice** device) __INTRODUCED_IN(24);
 
 /**
  * Submit an array of requests to be captured in sequence as a burst in the minimum of time possible.
@@ -471,7 +472,7 @@
         ACameraCaptureSession* session,
         /*optional*/ACameraCaptureSession_captureCallbacks* callbacks,
         int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId);
+        /*optional*/int* captureSequenceId) __INTRODUCED_IN(24);
 
 /**
  * Request endlessly repeating capture of a sequence of images by this capture session.
@@ -525,7 +526,7 @@
         ACameraCaptureSession* session,
         /*optional*/ACameraCaptureSession_captureCallbacks* callbacks,
         int numRequests, ACaptureRequest** requests,
-        /*optional*/int* captureSequenceId);
+        /*optional*/int* captureSequenceId) __INTRODUCED_IN(24);
 
 /**
  * Cancel any ongoing repeating capture set by {@link ACameraCaptureSession_setRepeatingRequest}.
@@ -548,7 +549,8 @@
  *         <li>{@link ACAMERA_ERROR_CAMERA_SERVICE} if the camera service encounters fatal error</li>
  *         <li>{@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons</li></ul>
  */
-camera_status_t ACameraCaptureSession_stopRepeating(ACameraCaptureSession* session);
+camera_status_t ACameraCaptureSession_stopRepeating(ACameraCaptureSession* session)
+        __INTRODUCED_IN(24);
 
 /**
  * Discard all captures currently pending and in-progress as fast as possible.
@@ -588,7 +590,8 @@
  *         <li>{@link ACAMERA_ERROR_CAMERA_SERVICE} if the camera service encounters fatal error</li>
  *         <li>{@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons</li></ul>
  */
-camera_status_t ACameraCaptureSession_abortCaptures(ACameraCaptureSession* session);
+camera_status_t ACameraCaptureSession_abortCaptures(ACameraCaptureSession* session)
+        __INTRODUCED_IN(24);
 
 #endif /* __ANDROID_API__ >= 24 */
 
@@ -637,9 +640,156 @@
  *         <li>{@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons</li></ul>
  */
 camera_status_t ACameraCaptureSession_updateSharedOutput(ACameraCaptureSession* session,
-        ACaptureSessionOutput* output);
+        ACaptureSessionOutput* output) __INTRODUCED_IN(28);
 #endif /* __ANDROID_API__ >= 28 */
 
+#if __ANDROID_API__ >= 29
+/**
+ * The definition of final capture result callback with logical multi-camera support.
+ *
+ * This has the same functionality as final ACameraCaptureSession_captureCallback_result, with
+ * added ability to return physical camera result metadata within a logical multi-camera.
+ *
+ * For a logical multi-camera, this function will be called with the Id and result metadata
+ * of the underlying physical cameras, which the corresponding capture request contains targets for.
+ * If the capture request doesn't contain targets specific to any physical camera, or the current
+ * camera device isn't a logical multi-camera, physicalResultCount will be 0.
+ *
+ * @param context The optional application context provided by user in
+ *                {@link ACameraCaptureSession_captureCallbacks}.
+ * @param session The camera capture session of interest.
+ * @param request The capture request of interest. Note that this pointer points to a copy of
+ *                capture request sent by application, so the address is different to what
+ *                application sent but the content will match. This request will be freed by
+ *                framework immediately after this callback returns.
+ * @param result The capture result metadata reported by camera device. The memory is managed by
+ *                camera framework. Do not access this pointer after this callback returns.
+ * @param physicalResultCount The number of physical camera result metadata
+ * @param physicalCameraIds The array of physical camera IDs on which the
+ *                physical result metadata are reported.
+ * @param physicalResults The array of capture result metadata reported by the
+ *                physical camera devices.
+ */
+typedef void (*ACameraCaptureSession_logicalCamera_captureCallback_result)(
+        void* context, ACameraCaptureSession* session,
+        ACaptureRequest* request, const ACameraMetadata* result,
+        size_t physicalResultCount, const char** physicalCameraIds,
+        const ACameraMetadata** physicalResults);
+
+/// Struct to describe a logical camera capture failure
+typedef struct ALogicalCameraCaptureFailure {
+    /**
+     * The {@link ACameraCaptureFailure} contains information about regular logical device capture
+     * failure.
+     */
+    struct ACameraCaptureFailure captureFailure;
+
+    /**
+     * The physical camera device ID in case the capture failure comes from a capture request
+     * with configured physical camera streams for a logical camera. physicalCameraId will be set
+     * to NULL in case the capture request has no associated physical camera device.
+     *
+     */
+    const char*    physicalCameraId;
+} ALogicalCameraCaptureFailure;
+
+/**
+ * The definition of logical camera capture failure callback.
+ *
+ * @param context The optional application context provided by user in
+ *                {@link ACameraCaptureSession_captureCallbacks}.
+ * @param session The camera capture session of interest.
+ * @param request The capture request of interest. Note that this pointer points to a copy of
+ *                capture request sent by application, so the address is different to what
+ *                application sent but the content will match. This request will be freed by
+ *                framework immediately after this callback returns.
+ * @param failure The {@link ALogicalCameraCaptureFailure} desribes the capture failure. The memory
+ *                is managed by camera framework. Do not access this pointer after this callback
+ *                returns.
+ */
+typedef void (*ACameraCaptureSession_logicalCamera_captureCallback_failed)(
+        void* context, ACameraCaptureSession* session,
+        ACaptureRequest* request, ALogicalCameraCaptureFailure* failure);
+
+/**
+ * This has the same functionality as ACameraCaptureSession_captureCallbacks,
+ * with the exception that an onLogicalCameraCaptureCompleted callback is
+ * used, instead of onCaptureCompleted, to support logical multi-camera.
+ */
+typedef struct ACameraCaptureSession_logicalCamera_captureCallbacks {
+    /**
+     * Same as ACameraCaptureSession_captureCallbacks
+     */
+    void*                                               context;
+    ACameraCaptureSession_captureCallback_start         onCaptureStarted;
+    ACameraCaptureSession_captureCallback_result        onCaptureProgressed;
+
+    /**
+     * This callback is called when an image capture has fully completed and all the
+     * result metadata is available. For a logical multi-camera, this callback
+     * also returns the result metadata for all physical cameras being
+     * explicitly requested on.
+     *
+     * <p>This callback will always fire after the last {@link onCaptureProgressed};
+     * in other words, no more partial results will be delivered once the completed result
+     * is available.</p>
+     *
+     * <p>For performance-intensive use-cases where latency is a factor, consider
+     * using {@link onCaptureProgressed} instead.</p>
+     *
+     * <p>Note that the ACaptureRequest pointer in the callback will not match what application has
+     * submitted, but the contents the ACaptureRequest will match what application submitted.</p>
+     */
+    ACameraCaptureSession_logicalCamera_captureCallback_result onLogicalCameraCaptureCompleted;
+
+    /**
+     * This callback is called instead of {@link onLogicalCameraCaptureCompleted} when the
+     * camera device failed to produce a capture result for the
+     * request.
+     *
+     * <p>Other requests are unaffected, and some or all image buffers from
+     * the capture may have been pushed to their respective output
+     * streams.</p>
+     *
+     * <p>Note that the ACaptureRequest pointer in the callback will not match what application has
+     * submitted, but the contents the ACaptureRequest will match what application submitted.</p>
+     *
+     * @see ALogicalCameraCaptureFailure
+     */
+    ACameraCaptureSession_logicalCamera_captureCallback_failed onLogicalCameraCaptureFailed;
+
+    /**
+     * Same as ACameraCaptureSession_captureCallbacks
+     */
+    ACameraCaptureSession_captureCallback_sequenceEnd   onCaptureSequenceCompleted;
+    ACameraCaptureSession_captureCallback_sequenceAbort onCaptureSequenceAborted;
+    ACameraCaptureSession_captureCallback_bufferLost    onCaptureBufferLost;
+} ACameraCaptureSession_logicalCamera_captureCallbacks;
+
+/**
+ * This has the same functionality as ACameraCaptureSession_capture, with added
+ * support for logical multi-camera where the capture callbacks supports result metadata for
+ * physical cameras.
+ */
+camera_status_t ACameraCaptureSession_logicalCamera_capture(
+        ACameraCaptureSession* session,
+        /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* callbacks,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) __INTRODUCED_IN(29);
+
+/**
+ * This has the same functionality as ACameraCaptureSession_setRepeatingRequest, with added
+ * support for logical multi-camera where the capture callbacks supports result metadata for
+ * physical cameras.
+ */
+camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequest(
+        ACameraCaptureSession* session,
+        /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacks* callbacks,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
 __END_DECLS
 
 #endif /* _NDK_CAMERA_CAPTURE_SESSION_H */
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index 92dad1c..1537bde 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
@@ -53,6 +53,17 @@
  */
 typedef struct ACameraDevice ACameraDevice;
 
+/**
+ * Struct to hold list of camera device Ids. This can refer to either the Ids
+ * of connected camera devices returned from {@link ACameraManager_getCameraIdList},
+ * or the physical camera Ids passed into
+ * {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ */
+typedef struct ACameraIdList {
+    int numCameras;          ///< Number of camera device Ids
+    const char** cameraIds;  ///< list of camera device Ids
+} ACameraIdList;
+
 /// Enum for ACameraDevice_ErrorStateCallback error code
 enum {
     /**
@@ -176,7 +187,7 @@
  *         <li>{@link ACAMERA_OK} if the method call succeeds.</li>
  *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if device is NULL.</li></ul>
  */
-camera_status_t ACameraDevice_close(ACameraDevice* device);
+camera_status_t ACameraDevice_close(ACameraDevice* device) __INTRODUCED_IN(24);
 
 /**
  * Return the camera id associated with this camera device.
@@ -187,7 +198,7 @@
  * delete/free by the application. Also the returned string must not be used after the device
  * has been closed.
  */
-const char* ACameraDevice_getId(const ACameraDevice* device);
+const char* ACameraDevice_getId(const ACameraDevice* device) __INTRODUCED_IN(24);
 
 typedef enum {
     /**
@@ -290,7 +301,7 @@
  */
 camera_status_t ACameraDevice_createCaptureRequest(
         const ACameraDevice* device, ACameraDevice_request_template templateId,
-        /*out*/ACaptureRequest** request);
+        /*out*/ACaptureRequest** request) __INTRODUCED_IN(24);
 
 
 typedef struct ACaptureSessionOutputContainer ACaptureSessionOutputContainer;
@@ -313,7 +324,7 @@
  *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if container is NULL.</li></ul>
  */
 camera_status_t ACaptureSessionOutputContainer_create(
-        /*out*/ACaptureSessionOutputContainer** container);
+        /*out*/ACaptureSessionOutputContainer** container) __INTRODUCED_IN(24);
 
 /**
  * Free a capture session output container.
@@ -322,7 +333,8 @@
  *
  * @see ACaptureSessionOutputContainer_create
  */
-void            ACaptureSessionOutputContainer_free(ACaptureSessionOutputContainer* container);
+void            ACaptureSessionOutputContainer_free(ACaptureSessionOutputContainer* container)
+        __INTRODUCED_IN(24);
 
 /**
  * Create a ACaptureSessionOutput object.
@@ -344,7 +356,7 @@
  * @see ACaptureSessionOutputContainer_add
  */
 camera_status_t ACaptureSessionOutput_create(
-        ANativeWindow* anw, /*out*/ACaptureSessionOutput** output);
+        ACameraWindowType* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(24);
 
 /**
  * Free a ACaptureSessionOutput object.
@@ -353,7 +365,7 @@
  *
  * @see ACaptureSessionOutput_create
  */
-void            ACaptureSessionOutput_free(ACaptureSessionOutput* output);
+void            ACaptureSessionOutput_free(ACaptureSessionOutput* output) __INTRODUCED_IN(24);
 
 /**
  * Add an {@link ACaptureSessionOutput} object to {@link ACaptureSessionOutputContainer}.
@@ -366,7 +378,8 @@
  *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if container or output is NULL.</li></ul>
  */
 camera_status_t ACaptureSessionOutputContainer_add(
-        ACaptureSessionOutputContainer* container, const ACaptureSessionOutput* output);
+        ACaptureSessionOutputContainer* container, const ACaptureSessionOutput* output)
+        __INTRODUCED_IN(24);
 
 /**
  * Remove an {@link ACaptureSessionOutput} object from {@link ACaptureSessionOutputContainer}.
@@ -382,7 +395,8 @@
  *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if container or output is NULL.</li></ul>
  */
 camera_status_t ACaptureSessionOutputContainer_remove(
-        ACaptureSessionOutputContainer* container, const ACaptureSessionOutput* output);
+        ACaptureSessionOutputContainer* container, const ACaptureSessionOutput* output)
+        __INTRODUCED_IN(24);
 
 /**
  * Create a new camera capture session by providing the target output set of {@link ANativeWindow}
@@ -671,7 +685,7 @@
         ACameraDevice* device,
         const ACaptureSessionOutputContainer*       outputs,
         const ACameraCaptureSession_stateCallbacks* callbacks,
-        /*out*/ACameraCaptureSession** session);
+        /*out*/ACameraCaptureSession** session) __INTRODUCED_IN(24);
 
 #endif /* __ANDROID_API__ >= 24 */
 
@@ -699,7 +713,7 @@
  * @see ACaptureSessionOutputContainer_add
  */
 camera_status_t ACaptureSessionSharedOutput_create(
-        ANativeWindow* anw, /*out*/ACaptureSessionOutput** output);
+        ACameraWindowType* anw, /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(28);
 
 /**
  * Add a native window to shared ACaptureSessionOutput.
@@ -716,7 +730,8 @@
  *             window associated with ACaptureSessionOutput; or anw is already present inside
  *             ACaptureSessionOutput.</li></ul>
  */
-camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *output, ANativeWindow *anw);
+camera_status_t ACaptureSessionSharedOutput_add(ACaptureSessionOutput *output,
+        ACameraWindowType *anw) __INTRODUCED_IN(28);
 
 /**
  * Remove a native window from shared ACaptureSessionOutput.
@@ -732,7 +747,7 @@
  *             ACaptureSessionOutput.</li></ul>
  */
 camera_status_t ACaptureSessionSharedOutput_remove(ACaptureSessionOutput *output,
-        ANativeWindow* anw);
+        ACameraWindowType* anw) __INTRODUCED_IN(28);
 
 /**
  * Create a new camera capture session similar to {@link ACameraDevice_createCaptureSession}. This
@@ -765,10 +780,118 @@
         const ACaptureSessionOutputContainer* outputs,
         const ACaptureRequest* sessionParameters,
         const ACameraCaptureSession_stateCallbacks* callbacks,
-        /*out*/ACameraCaptureSession** session);
+        /*out*/ACameraCaptureSession** session) __INTRODUCED_IN(28);
 
 #endif /* __ANDROID_API__ >= 28 */
 
+#if __ANDROID_API__ >= 29
+
+/**
+ * Create a ACaptureSessionOutput object used for streaming from a physical
+ * camera as part of a logical camera device.
+ *
+ * <p>The ACaptureSessionOutput is used in {@link ACaptureSessionOutputContainer_add} method to add
+ * an output {@link ANativeWindow} to ACaptureSessionOutputContainer. Use
+ * {@link ACaptureSessionOutput_free} to free the object and its memory after application no longer
+ * needs the {@link ACaptureSessionOutput}.</p>
+ *
+ * @param anw the {@link ANativeWindow} to be associated with the {@link ACaptureSessionOutput}
+ * @param physicalId the Id of the physical camera this output is associated
+ *                  with.
+ * @param output the output {@link ACaptureSessionOutput} will be stored here if the
+ *                  method call succeeds.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds. The created container will be
+ *                                filled in the output argument.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if anw, physicalId or output is NULL.</li></ul>
+ *
+ * @see ACaptureSessionOutputContainer_add
+ */
+camera_status_t ACaptureSessionPhysicalOutput_create(
+        ACameraWindowType* anw, const char* physicalId,
+        /*out*/ACaptureSessionOutput** output) __INTRODUCED_IN(29);
+
+/**
+ * Create a logical multi-camera ACaptureRequest for capturing images, initialized with template
+ * for a target use case, with the ability to specify physical camera settings.
+ *
+ * <p>The settings are chosen to be the best options for this camera device,
+ * so it is not recommended to reuse the same request for a different camera device.</p>
+ *
+ * <p>Note that for all keys in physical camera settings, only the keys
+ * advertised in ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS are
+ * applicable. All other keys are ignored by the camera device.</p>
+ *
+ * @param device the camera device of interest
+ * @param templateId the type of capture request to be created.
+ *        See {@link ACameraDevice_request_template}.
+ * @param physicalIdList The list of physical camera Ids that can be used to
+ *        customize the request for a specific physical camera.
+ * @param request the output request will be stored here if the method call succeeds.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds. The created capture request will be
+ *                                filled in request argument.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if device, physicalIdList, or request is
+ *                                NULL, templateId is undefined or camera device does not support
+ *                                requested template, or if some Ids in physicalIdList isn't a
+ *                                valid physical camera backing the current camera device.</li>
+ *         <li>{@link ACAMERA_ERROR_CAMERA_DISCONNECTED} if the camera device is closed.</li>
+ *         <li>{@link ACAMERA_ERROR_CAMERA_DEVICE} if the camera device encounters fatal error.</li>
+ *         <li>{@link ACAMERA_ERROR_CAMERA_SERVICE} if the camera service encounters fatal error.</li>
+ *         <li>{@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons.</li></ul>
+ *
+ * @see TEMPLATE_PREVIEW
+ * @see TEMPLATE_RECORD
+ * @see TEMPLATE_STILL_CAPTURE
+ * @see TEMPLATE_VIDEO_SNAPSHOT
+ * @see TEMPLATE_MANUAL
+ */
+camera_status_t ACameraDevice_createCaptureRequest_withPhysicalIds(
+        const ACameraDevice* device, ACameraDevice_request_template templateId,
+        const ACameraIdList* physicalIdList,
+        /*out*/ACaptureRequest** request) __INTRODUCED_IN(29);
+
+/**
+ * Check whether a particular {@ACaptureSessionOutputContainer} is supported by
+ * the camera device.
+ *
+ * <p>This method performs a runtime check of a given {@link
+ * ACaptureSessionOutputContainer}. The result confirms whether or not the
+ * passed CaptureSession outputs can be successfully used to create a camera
+ * capture session using {@link ACameraDevice_createCaptureSession}.</p>
+ *
+ * <p>This method can be called at any point before, during and after active
+ * capture session. It must not impact normal camera behavior in any way and
+ * must complete significantly faster than creating a capture session.</p>
+ *
+ * <p>Although this method is faster than creating a new capture session, it is not intended
+ * to be used for exploring the entire space of supported stream combinations.</p>
+ *
+ * @param device the camera device of interest
+ * @param sessionOutputContainer the {@link ACaptureSessionOutputContainer} of
+ *                               interest.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the given {@link ACaptureSessionOutputContainer}
+ *                                is supported by the camera device.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if device, or sessionOutputContainer
+ *                                                     is NULL.</li>
+ *         <li>{@link ACAMERA_ERROR_STREAM_CONFIGURE_FAIL} if the given
+ *                                                         {@link ACaptureSessionOutputContainer}
+ *                                                         is not supported by
+ *                                                         the camera
+ *                                                         device.</li>
+ *        <li>{@link ACAMERA_ERROR_UNSUPPORTED_OPERATION} if the query operation is not
+ *                                                        supported by the camera device.</li>
+ */
+camera_status_t ACameraDevice_isSessionConfigurationSupported(
+        const ACameraDevice* device,
+        const ACaptureSessionOutputContainer* sessionOutputContainer) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
 __END_DECLS
 
 #endif /* _NDK_CAMERA_DEVICE_H */
diff --git a/camera/ndk/include/camera/NdkCameraError.h b/camera/ndk/include/camera/NdkCameraError.h
index 6b58155..fc618ee 100644
--- a/camera/ndk/include/camera/NdkCameraError.h
+++ b/camera/ndk/include/camera/NdkCameraError.h
@@ -106,7 +106,8 @@
 
     /**
      * Camera device does not support the stream configuration provided by application in
-     * {@link ACameraDevice_createCaptureSession}.
+     * {@link ACameraDevice_createCaptureSession} or {@link
+     * ACameraDevice_isSessionConfigurationSupported}.
      */
     ACAMERA_ERROR_STREAM_CONFIGURE_FAIL = ACAMERA_ERROR_BASE - 9,
 
@@ -130,6 +131,11 @@
      * The application does not have permission to open camera.
      */
     ACAMERA_ERROR_PERMISSION_DENIED     = ACAMERA_ERROR_BASE - 13,
+
+    /**
+     * The operation is not supported by the camera device.
+     */
+    ACAMERA_ERROR_UNSUPPORTED_OPERATION = ACAMERA_ERROR_BASE - 14,
 } camera_status_t;
 
 #endif /* __ANDROID_API__ >= 24 */
diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h
index e5b3ad8..2cc8a97 100644
--- a/camera/ndk/include/camera/NdkCameraManager.h
+++ b/camera/ndk/include/camera/NdkCameraManager.h
@@ -65,20 +65,14 @@
  * @return a {@link ACameraManager} instance.
  *
  */
-ACameraManager* ACameraManager_create();
+ACameraManager* ACameraManager_create() __INTRODUCED_IN(24);
 
 /**
  * <p>Delete the {@link ACameraManager} instance and free its resources. </p>
  *
  * @param manager the {@link ACameraManager} instance to be deleted.
  */
-void ACameraManager_delete(ACameraManager* manager);
-
-/// Struct to hold list of camera devices
-typedef struct ACameraIdList {
-    int numCameras;          ///< Number of connected camera devices
-    const char** cameraIds;  ///< list of identifier of connected camera devices
-} ACameraIdList;
+void ACameraManager_delete(ACameraManager* manager) __INTRODUCED_IN(24);
 
 /**
  * Create a list of currently connected camera devices, including
@@ -91,6 +85,11 @@
  * <p>ACameraManager_getCameraIdList will allocate and return an {@link ACameraIdList}.
  * The caller must call {@link ACameraManager_deleteCameraIdList} to free the memory</p>
  *
+ * <p>Note: the returned camera list might be a subset to the output of <a href=
+ * "https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraIdList()">
+ * SDK CameraManager#getCameraIdList API</a> as the NDK API does not support some legacy camera
+ * hardware.</p>
+ *
  * @param manager the {@link ACameraManager} of interest
  * @param cameraIdList the output {@link ACameraIdList} will be filled in here if the method call
  *        succeeds.
@@ -102,14 +101,14 @@
  *         <li>{@link ACAMERA_ERROR_NOT_ENOUGH_MEMORY} if allocating memory fails.</li></ul>
  */
 camera_status_t ACameraManager_getCameraIdList(ACameraManager* manager,
-                                              /*out*/ACameraIdList** cameraIdList);
+        /*out*/ACameraIdList** cameraIdList) __INTRODUCED_IN(24);
 
 /**
  * Delete a list of camera devices allocated via {@link ACameraManager_getCameraIdList}.
  *
  * @param cameraIdList the {@link ACameraIdList} to be deleted.
  */
-void ACameraManager_deleteCameraIdList(ACameraIdList* cameraIdList);
+void ACameraManager_deleteCameraIdList(ACameraIdList* cameraIdList) __INTRODUCED_IN(24);
 
 /**
  * Definition of camera availability callbacks.
@@ -120,7 +119,8 @@
  *                 argument is owned by camera framework and will become invalid immediately after
  *                 this callback returns.
  */
-typedef void (*ACameraManager_AvailabilityCallback)(void* context, const char* cameraId);
+typedef void (*ACameraManager_AvailabilityCallback)(void* context,
+        const char* cameraId);
 
 /**
  * A listener for camera devices becoming available or unavailable to open.
@@ -168,7 +168,8 @@
  *                  {ACameraManager_AvailabilityCallbacks#onCameraUnavailable} is NULL.</li></ul>
  */
 camera_status_t ACameraManager_registerAvailabilityCallback(
-        ACameraManager* manager, const ACameraManager_AvailabilityCallbacks* callback);
+        ACameraManager* manager,
+        const ACameraManager_AvailabilityCallbacks* callback) __INTRODUCED_IN(24);
 
 /**
  * Unregister camera availability callbacks.
@@ -185,7 +186,8 @@
  *                  {ACameraManager_AvailabilityCallbacks#onCameraUnavailable} is NULL.</li></ul>
  */
 camera_status_t ACameraManager_unregisterAvailabilityCallback(
-        ACameraManager* manager, const ACameraManager_AvailabilityCallbacks* callback);
+        ACameraManager* manager,
+        const ACameraManager_AvailabilityCallbacks* callback) __INTRODUCED_IN(24);
 
 /**
  * Query the capabilities of a camera device. These capabilities are
@@ -211,7 +213,7 @@
  */
 camera_status_t ACameraManager_getCameraCharacteristics(
         ACameraManager* manager, const char* cameraId,
-        /*out*/ACameraMetadata** characteristics);
+        /*out*/ACameraMetadata** characteristics) __INTRODUCED_IN(24);
 
 /**
  * Open a connection to a camera with the given ID. The opened camera device will be
@@ -271,10 +273,126 @@
 camera_status_t ACameraManager_openCamera(
         ACameraManager* manager, const char* cameraId,
         ACameraDevice_StateCallbacks* callback,
-        /*out*/ACameraDevice** device);
+        /*out*/ACameraDevice** device) __INTRODUCED_IN(24);
 
 #endif /* __ANDROID_API__ >= 24 */
 
+#if __ANDROID_API__ >= 29
+
+/**
+ * Definition of camera access permission change callback.
+ *
+ * <p>Notification that camera access priorities have changed and the camera may
+ * now be openable. An application that was previously denied camera access due to
+ * a higher-priority user already using the camera, or that was disconnected from an
+ * active camera session due to a higher-priority user trying to open the camera,
+ * should try to open the camera again if it still wants to use it.  Note that
+ * multiple applications may receive this callback at the same time, and only one of
+ * them will succeed in opening the camera in practice, depending on exact access
+ * priority levels and timing. This method is useful in cases where multiple
+ * applications may be in the resumed state at the same time, and the user switches
+ * focus between them, or if the current camera-using application moves between
+ * full-screen and Picture-in-Picture (PiP) states. In such cases, the camera
+ * available/unavailable callbacks will not be invoked, but another application may
+ * now have higher priority for camera access than the current camera-using
+ * application.</p>
+
+ * @param context The optional application context provided by user in
+ *                {@link ACameraManager_AvailabilityListener}.
+ */
+typedef void (*ACameraManager_AccessPrioritiesChangedCallback)(void* context);
+
+/**
+ * A listener for camera devices becoming available/unavailable to open or when
+ * the camera access permissions change.
+ *
+ * <p>Cameras become available when they are no longer in use, or when a new
+ * removable camera is connected. They become unavailable when some
+ * application or service starts using a camera, or when a removable camera
+ * is disconnected.</p>
+ *
+ * @see ACameraManager_registerExtendedAvailabilityCallback
+ */
+typedef struct ACameraManager_ExtendedAvailabilityListener {
+    ///
+    ACameraManager_AvailabilityCallbacks availabilityCallbacks;
+
+    /// Called when there is camera access permission change
+    ACameraManager_AccessPrioritiesChangedCallback onCameraAccessPrioritiesChanged;
+
+    /// Reserved for future use, please ensure that all entries are set to NULL
+    void *reserved[6];
+} ACameraManager_ExtendedAvailabilityCallbacks;
+
+/**
+ * Register camera extended availability callbacks.
+ *
+ * <p>onCameraUnavailable will be called whenever a camera device is opened by any camera API
+ * client. Other camera API clients may still be able to open such a camera device, evicting the
+ * existing client if they have higher priority than the existing client of a camera device.
+ * See {@link ACameraManager_openCamera} for more details.</p>
+ *
+ * <p>The callbacks will be called on a dedicated thread shared among all ACameraManager
+ * instances.</p>
+ *
+ * <p>Since this callback will be registered with the camera service, remember to unregister it
+ * once it is no longer needed; otherwise the callback will continue to receive events
+ * indefinitely and it may prevent other resources from being released. Specifically, the
+ * callbacks will be invoked independently of the general activity lifecycle and independently
+ * of the state of individual ACameraManager instances.</p>
+ *
+ * @param manager the {@link ACameraManager} of interest.
+ * @param callback the {@link ACameraManager_ExtendedAvailabilityCallbacks} to be registered.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if manager or callback is NULL, or
+ *                  {ACameraManager_ExtendedAvailabilityCallbacks#onCameraAccessPrioritiesChanged}
+ *                  or {ACameraManager_AvailabilityCallbacks#onCameraAvailable} or
+ *                  {ACameraManager_AvailabilityCallbacks#onCameraUnavailable} is NULL.</li></ul>
+ */
+camera_status_t ACameraManager_registerExtendedAvailabilityCallback(
+        ACameraManager* manager,
+        const ACameraManager_ExtendedAvailabilityCallbacks* callback) __INTRODUCED_IN(29);
+
+/**
+ * Unregister camera extended availability callbacks.
+ *
+ * <p>Removing a callback that isn't registered has no effect.</p>
+ *
+ * @param manager the {@link ACameraManager} of interest.
+ * @param callback the {@link ACameraManager_ExtendedAvailabilityCallbacks} to be unregistered.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if callback,
+ *                  {ACameraManager_ExtendedAvailabilityCallbacks#onCameraAccessPrioritiesChanged}
+ *                  or {ACameraManager_AvailabilityCallbacks#onCameraAvailable} or
+ *                  {ACameraManager_AvailabilityCallbacks#onCameraUnavailable} is NULL.</li></ul>
+ */
+camera_status_t ACameraManager_unregisterExtendedAvailabilityCallback(
+        ACameraManager* manager,
+        const ACameraManager_ExtendedAvailabilityCallbacks* callback) __INTRODUCED_IN(29);
+
+#ifdef __ANDROID_VNDK__
+/**
+ * Retrieve the tag value, given the tag name and camera id.
+ * This method is device specific since some metadata might be defined by device manufacturers
+ * and might only be accessible for specific cameras.
+ * @param manager The {@link ACameraManager} of interest.
+ * @param cameraId The cameraId, which is used to query camera characteristics.
+ * @param name The name of the tag being queried.
+ * @param tag The output tag assigned by this method.
+ *
+ * @return ACAMERA_OK only if the function call was successful.
+ */
+camera_status_t ACameraManager_getTagFromName(ACameraManager *manager, const char* cameraId,
+        const char *name, /*out*/uint32_t *tag)
+        __INTRODUCED_IN(29);
+#endif
+
+#endif /* __ANDROID_API__ >= 29 */
+
 __END_DECLS
 
 #endif /* _NDK_CAMERA_MANAGER_H */
diff --git a/camera/ndk/include/camera/NdkCameraMetadata.h b/camera/ndk/include/camera/NdkCameraMetadata.h
index f2aec98..9bbfb83 100644
--- a/camera/ndk/include/camera/NdkCameraMetadata.h
+++ b/camera/ndk/include/camera/NdkCameraMetadata.h
@@ -36,6 +36,7 @@
 #ifndef _NDK_CAMERA_METADATA_H
 #define _NDK_CAMERA_METADATA_H
 
+#include <stdint.h>
 #include <sys/cdefs.h>
 
 #include "NdkCameraError.h"
@@ -190,7 +191,8 @@
  *             of input tag value.</li></ul>
  */
 camera_status_t ACameraMetadata_getConstEntry(
-        const ACameraMetadata* metadata, uint32_t tag, /*out*/ACameraMetadata_const_entry* entry);
+        const ACameraMetadata* metadata,
+        uint32_t tag, /*out*/ACameraMetadata_const_entry* entry) __INTRODUCED_IN(24);
 
 /**
  * List all the entry tags in input {@link ACameraMetadata}.
@@ -207,7 +209,8 @@
  *         <li>{@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons.</li></ul>
  */
 camera_status_t ACameraMetadata_getAllTags(
-        const ACameraMetadata* metadata, /*out*/int32_t* numEntries, /*out*/const uint32_t** tags);
+        const ACameraMetadata* metadata,
+        /*out*/int32_t* numEntries, /*out*/const uint32_t** tags) __INTRODUCED_IN(24);
 
 /**
  * Create a copy of input {@link ACameraMetadata}.
@@ -219,17 +222,39 @@
  *
  * @return a valid ACameraMetadata pointer or NULL if the input metadata cannot be copied.
  */
-ACameraMetadata* ACameraMetadata_copy(const ACameraMetadata* src);
+ACameraMetadata* ACameraMetadata_copy(const ACameraMetadata* src) __INTRODUCED_IN(24);
 
 /**
  * Free a {@link ACameraMetadata} structure.
  *
  * @param metadata the {@link ACameraMetadata} to be freed.
  */
-void ACameraMetadata_free(ACameraMetadata* metadata);
+void ACameraMetadata_free(ACameraMetadata* metadata) __INTRODUCED_IN(24);
 
 #endif /* __ANDROID_API__ >= 24 */
 
+#if __ANDROID_API__ >= 29
+
+/**
+ * Helper function to check if a camera is logical multi-camera.
+ *
+ * <p> Check whether a camera device is a logical multi-camera based on its
+ * static metadata. If it is, also returns its physical sub camera Ids.</p>
+ *
+ * @param staticMetadata the static metadata of the camera being checked.
+ * @param numPhysicalCameras returns the number of physical cameras.
+ * @param physicalCameraIds returns the array of physical camera Ids backing this logical
+ *                          camera device. Note that this pointer is only valid
+ *                          during the lifetime of the staticMetadata object.
+ *
+ * @return true if this is a logical multi-camera, false otherwise.
+ */
+bool ACameraMetadata_isLogicalMultiCamera(const ACameraMetadata* staticMetadata,
+        /*out*/size_t* numPhysicalCameras, /*out*/const char* const** physicalCameraIds)
+        __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
 __END_DECLS
 
 #endif /* _NDK_CAMERA_METADATA_H */
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index bee1a46..8dd6e00 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -71,6 +71,8 @@
     ACAMERA_DEPTH,
     ACAMERA_LOGICAL_MULTI_CAMERA,
     ACAMERA_DISTORTION_CORRECTION,
+    ACAMERA_HEIC,
+    ACAMERA_HEIC_INFO,
     ACAMERA_SECTION_COUNT,
 
     ACAMERA_VENDOR = 0x8000
@@ -112,6 +114,8 @@
     ACAMERA_DISTORTION_CORRECTION_START
                                    = ACAMERA_DISTORTION_CORRECTION
                                                                 << 16,
+    ACAMERA_HEIC_START             = ACAMERA_HEIC              << 16,
+    ACAMERA_HEIC_INFO_START        = ACAMERA_HEIC_INFO         << 16,
     ACAMERA_VENDOR_START           = ACAMERA_VENDOR            << 16
 } acamera_metadata_section_start_t;
 
@@ -1912,6 +1916,7 @@
      *   <li>ACaptureRequest</li>
      * </ul></p>
      *
+     * <p>This tag is also used for HEIC image capture.</p>
      */
     ACAMERA_JPEG_GPS_COORDINATES =                              // double[3]
             ACAMERA_JPEG_START,
@@ -1927,6 +1932,7 @@
      *   <li>ACaptureRequest</li>
      * </ul></p>
      *
+     * <p>This tag is also used for HEIC image capture.</p>
      */
     ACAMERA_JPEG_GPS_PROCESSING_METHOD =                        // byte
             ACAMERA_JPEG_START + 1,
@@ -1942,6 +1948,7 @@
      *   <li>ACaptureRequest</li>
      * </ul></p>
      *
+     * <p>This tag is also used for HEIC image capture.</p>
      */
     ACAMERA_JPEG_GPS_TIMESTAMP =                                // int64
             ACAMERA_JPEG_START + 2,
@@ -1986,6 +1993,10 @@
      * </code></pre>
      * <p>For EXTERNAL cameras the sensor orientation will always be set to 0 and the facing will
      * also be set to EXTERNAL. The above code is not relevant in such case.</p>
+     * <p>This tag is also used to describe the orientation of the HEIC image capture, in which
+     * case the rotation is reflected by
+     * <a href="https://developer.android.com/reference/android/media/ExifInterface.html#TAG_ORIENTATION">EXIF orientation flag</a>, and not by
+     * rotating the image data itself.</p>
      *
      * @see ACAMERA_SENSOR_ORIENTATION
      */
@@ -2003,7 +2014,8 @@
      *   <li>ACaptureRequest</li>
      * </ul></p>
      *
-     * <p>85-95 is typical usage range.</p>
+     * <p>85-95 is typical usage range. This tag is also used to describe the quality
+     * of the HEIC image capture.</p>
      */
     ACAMERA_JPEG_QUALITY =                                      // byte
             ACAMERA_JPEG_START + 4,
@@ -2019,6 +2031,7 @@
      *   <li>ACaptureRequest</li>
      * </ul></p>
      *
+     * <p>This tag is also used to describe the quality of the HEIC image capture.</p>
      */
     ACAMERA_JPEG_THUMBNAIL_QUALITY =                            // byte
             ACAMERA_JPEG_START + 5,
@@ -2055,6 +2068,10 @@
      *   orientation is requested. LEGACY device will always report unrotated thumbnail
      *   size.</li>
      * </ul>
+     * <p>The tag is also used as thumbnail size for HEIC image format capture, in which case the
+     * the thumbnail rotation is reflected by
+     * <a href="https://developer.android.com/reference/android/media/ExifInterface.html#TAG_ORIENTATION">EXIF orientation flag</a>, and not by
+     * rotating the thumbnail data itself.</p>
      *
      * @see ACAMERA_JPEG_ORIENTATION
      */
@@ -2088,6 +2105,7 @@
      * and vice versa.</li>
      * <li>All non-<code>(0, 0)</code> sizes will have non-zero widths and heights.</li>
      * </ul>
+     * <p>This list is also used as supported thumbnail sizes for HEIC image format capture.</p>
      *
      * @see ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS
      */
@@ -2783,7 +2801,7 @@
      *   {@link AIMAGE_FORMAT_RAW12 RAW12}.</li>
      * <li>Processed (but not-stalling): any non-RAW format without a stall duration.  Typically
      *   {@link AIMAGE_FORMAT_YUV_420_888 YUV_420_888},
-     *   <a href="https://developer.android.com/reference/android/graphics/ImageFormat.html#NV21">NV21</a>, or <a href="https://developer.android.com/reference/android/graphics/ImageFormat.html#YV12">YV12</a>.</li>
+     *   <a href="https://developer.android.com/reference/android/graphics/ImageFormat.html#NV21">NV21</a>, <a href="https://developer.android.com/reference/android/graphics/ImageFormat.html#YV12">YV12</a>, or {@link AIMAGE_FORMAT_Y8 Y8} .</li>
      * </ul>
      *
      * @see ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS
@@ -3028,6 +3046,28 @@
      */
     ACAMERA_REQUEST_AVAILABLE_SESSION_KEYS =                    // int32[n]
             ACAMERA_REQUEST_START + 16,
+    /**
+     * <p>A subset of the available request keys that can be overridden for
+     * physical devices backing a logical multi-camera.</p>
+     *
+     * <p>Type: int32[n]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>This is a subset of ACAMERA_REQUEST_AVAILABLE_REQUEST_KEYS which contains a list
+     * of keys that can be overridden using <a href="https://developer.android.com/reference/CaptureRequest/Builder.html#setPhysicalCameraKey">Builder#setPhysicalCameraKey</a>.
+     * The respective value of such request key can be obtained by calling
+     * <a href="https://developer.android.com/reference/CaptureRequest/Builder.html#getPhysicalCameraKey">Builder#getPhysicalCameraKey</a>. Capture requests that contain
+     * individual physical device requests must be built via
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#createCaptureRequest(int,">Set)</a>.</p>
+     *
+     * @see ACAMERA_REQUEST_AVAILABLE_REQUEST_KEYS
+     */
+    ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS =    // int32[n]
+            ACAMERA_REQUEST_START + 17,
     ACAMERA_REQUEST_END,
 
     /**
@@ -3257,6 +3297,7 @@
      * <li>{@link AIMAGE_FORMAT_YUV_420_888 }</li>
      * <li>{@link AIMAGE_FORMAT_RAW10 }</li>
      * <li>{@link AIMAGE_FORMAT_RAW12 }</li>
+     * <li>{@link AIMAGE_FORMAT_Y8 }</li>
      * </ul>
      * <p>All other formats may or may not have an allowed stall duration on
      * a per-capability basis; refer to ACAMERA_REQUEST_AVAILABLE_CAPABILITIES
@@ -3294,6 +3335,81 @@
      */
     ACAMERA_SCALER_CROPPING_TYPE =                              // byte (acamera_metadata_enum_android_scaler_cropping_type_t)
             ACAMERA_SCALER_START + 13,
+    /**
+     * <p>Recommended stream configurations for common client use cases.</p>
+     *
+     * <p>Type: int32[n*5] (acamera_metadata_enum_android_scaler_available_recommended_stream_configurations_t)</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>Optional subset of the ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS that contains
+     * similar tuples listed as
+     * (i.e. width, height, format, output/input stream, usecase bit field).
+     * Camera devices will be able to suggest particular stream configurations which are
+     * power and performance efficient for specific use cases. For more information about
+     * retrieving the suggestions see
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#getRecommendedStreamConfigurationMap">CameraCharacteristics#getRecommendedStreamConfigurationMap</a>.</p>
+     * <p>The data representation is int[5], which maps to
+     * (width, height, format, output/input stream, usecase bit field). The array can be
+     * parsed using the following pseudo code:</p>
+     * <p>struct StreamConfiguration {
+     * int32_t format;
+     * int32_t width;
+     * int32_t height;
+     * int32_t isInput; };</p>
+     * <p>void getPreferredStreamConfigurations(
+     *     int32_t *array, size_t count, int32_t usecaseId,
+     *     Vector &lt; StreamConfiguration &gt; * scs) {
+     *     const size_t STREAM_CONFIGURATION_SIZE = 5;
+     *     const size_t STREAM_WIDTH_OFFSET = 0;
+     *     const size_t STREAM_HEIGHT_OFFSET = 1;
+     *     const size_t STREAM_FORMAT_OFFSET = 2;
+     *     const size_t STREAM_IS_INPUT_OFFSET = 3;
+     *     const size_t STREAM_USECASE_BITMAP_OFFSET = 4;</p>
+     * <pre><code>for (size_t i = 0; i &lt; count; i+= STREAM_CONFIGURATION_SIZE) {
+     *     int32_t width = array[i + STREAM_WIDTH_OFFSET];
+     *     int32_t height = array[i + STREAM_HEIGHT_OFFSET];
+     *     int32_t format = array[i + STREAM_FORMAT_OFFSET];
+     *     int32_t isInput = array[i + STREAM_IS_INPUT_OFFSET];
+     *     int32_t supportedUsecases = array[i + STREAM_USECASE_BITMAP_OFFSET];
+     *     if (supportedUsecases &amp; (1 &lt;&lt; usecaseId)) {
+     *         StreamConfiguration sc = {format, width, height, isInput};
+     *         scs-&gt;add(sc);
+     *     }
+     * }
+     * </code></pre>
+     * <p>}</p>
+     *
+     * @see ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS
+     */
+    ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS = 
+                                                                // int32[n*5] (acamera_metadata_enum_android_scaler_available_recommended_stream_configurations_t)
+            ACAMERA_SCALER_START + 14,
+    /**
+     * <p>Recommended mappings of image formats that are supported by this
+     * camera device for input streams, to their corresponding output formats.</p>
+     *
+     * <p>Type: int32</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>This is a recommended subset of the complete list of mappings found in
+     * android.scaler.availableInputOutputFormatsMap. The same requirements apply here as well.
+     * The list however doesn't need to contain all available and supported mappings. Instead of
+     * this developers must list only recommended and efficient entries.
+     * If set, the information will be available in the ZERO_SHUTTER_LAG recommended stream
+     * configuration see
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#getRecommendedStreamConfigurationMap">CameraCharacteristics#getRecommendedStreamConfigurationMap</a>.</p>
+     */
+    ACAMERA_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP = 
+                                                                // int32
+            ACAMERA_SCALER_START + 15,
     ACAMERA_SCALER_END,
 
     /**
@@ -3454,6 +3570,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
@@ -3483,6 +3601,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
@@ -3510,6 +3630,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
      */
@@ -3537,6 +3659,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
      */
@@ -3565,6 +3689,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
      */
@@ -3595,6 +3721,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
      */
@@ -3621,6 +3749,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
      */
@@ -3649,6 +3779,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
      */
@@ -3681,6 +3813,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
@@ -3775,6 +3908,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,
@@ -3805,6 +3940,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
      */
@@ -3850,6 +3987,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,
@@ -4002,6 +4141,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>
      *
@@ -4104,7 +4244,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>
      *
@@ -4573,13 +4714,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,
@@ -4599,6 +4740,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
@@ -4942,8 +5094,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>
@@ -5417,9 +5569,121 @@
      */
     ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE =                          // byte (acamera_metadata_enum_android_depth_depth_is_exclusive_t)
             ACAMERA_DEPTH_START + 4,
+    /**
+     * <p>Recommended depth stream configurations for common client use cases.</p>
+     *
+     * <p>Type: int32[n*5]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>Optional subset of the ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS that
+     * contains similar tuples listed as
+     * (i.e. width, height, format, output/input stream, usecase bit field).
+     * Camera devices will be able to suggest particular depth stream configurations which are
+     * power and performance efficient for specific use cases. For more information about
+     * retrieving the suggestions see
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#getRecommendedStreamConfigurationMap">CameraCharacteristics#getRecommendedStreamConfigurationMap</a>.</p>
+     * <p>For data representation please refer to
+     * ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS</p>
+     *
+     * @see ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS
+     * @see ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS
+     */
+    ACAMERA_DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS = 
+                                                                // int32[n*5]
+            ACAMERA_DEPTH_START + 5,
+    /**
+     * <p>The available dynamic depth dataspace stream
+     * configurations that this camera device supports
+     * (i.e. format, width, height, output/input stream).</p>
+     *
+     * <p>Type: int32[n*4] (acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_t)</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>These are output stream configurations for use with
+     * dataSpace DYNAMIC_DEPTH. The configurations are
+     * listed as <code>(format, width, height, input?)</code> tuples.</p>
+     * <p>Only devices that support depth output for at least
+     * the HAL_PIXEL_FORMAT_Y16 dense depth map along with
+     * HAL_PIXEL_FORMAT_BLOB with the same size or size with
+     * the same aspect ratio can have dynamic depth dataspace
+     * stream configuration. ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE also
+     * needs to be set to FALSE.</p>
+     *
+     * @see ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE
+     */
+    ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS = 
+                                                                // int32[n*4] (acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_t)
+            ACAMERA_DEPTH_START + 6,
+    /**
+     * <p>This lists the minimum frame duration for each
+     * format/size combination for dynamic depth output streams.</p>
+     *
+     * <p>Type: int64[4*n]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>This should correspond to the frame duration when only that
+     * stream is active, with all processing (typically in android.*.mode)
+     * set to either OFF or FAST.</p>
+     * <p>When multiple streams are used in a request, the minimum frame
+     * duration will be max(individual stream min durations).</p>
+     * <p>The minimum frame duration of a stream (of a particular format, size)
+     * is the same regardless of whether the stream is input or output.</p>
+     */
+    ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS = // int64[4*n]
+            ACAMERA_DEPTH_START + 7,
+    /**
+     * <p>This lists the maximum stall duration for each
+     * output format/size combination for dynamic depth streams.</p>
+     *
+     * <p>Type: int64[4*n]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>A stall duration is how much extra time would get added
+     * to the normal minimum frame duration for a repeating request
+     * that has streams with non-zero stall.</p>
+     * <p>All dynamic depth output streams may have a nonzero stall
+     * duration.</p>
+     */
+    ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS =     // int64[4*n]
+            ACAMERA_DEPTH_START + 8,
     ACAMERA_DEPTH_END,
 
     /**
+     * <p>String containing the ids of the underlying physical cameras.</p>
+     *
+     * <p>Type: byte[n]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>For a logical camera, this is concatenation of all underlying physical camera IDs.
+     * The null terminator for physical camera ID must be preserved so that the whole string
+     * can be tokenized using '\0' to generate list of physical camera IDs.</p>
+     * <p>For example, if the physical camera IDs of the logical camera are "2" and "3", the
+     * value of this tag will be ['2', '\0', '3', '\0'].</p>
+     * <p>The number of physical camera IDs must be no less than 2.</p>
+     */
+    ACAMERA_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS =                 // byte[n]
+            ACAMERA_LOGICAL_MULTI_CAMERA_START,
+    /**
      * <p>The accuracy of frame timestamp synchronization between physical cameras</p>
      *
      * <p>Type: byte (acamera_metadata_enum_android_logical_multi_camera_sensor_sync_type_t)</p>
@@ -5437,9 +5701,37 @@
      * <p>In both cases, all images generated for a particular capture request still carry the same
      * timestamps, so that they can be used to look up the matching frame number and
      * onCaptureStarted callback.</p>
+     * <p>This tag is only applicable if the logical camera device supports concurrent physical
+     * streams from different physical cameras.</p>
      */
     ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE =             // byte (acamera_metadata_enum_android_logical_multi_camera_sensor_sync_type_t)
             ACAMERA_LOGICAL_MULTI_CAMERA_START + 1,
+    /**
+     * <p>String containing the ID of the underlying active physical camera.</p>
+     *
+     * <p>Type: byte</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+     * </ul></p>
+     *
+     * <p>The ID of the active physical camera that's backing the logical camera. All camera
+     * streams and metadata that are not physical camera specific will be originating from this
+     * physical camera.</p>
+     * <p>For a logical camera made up of physical cameras where each camera's lenses have
+     * different characteristics, the camera device may choose to switch between the physical
+     * cameras when application changes FOCAL_LENGTH or SCALER_CROP_REGION.
+     * At the time of lens switch, this result metadata reflects the new active physical camera
+     * ID.</p>
+     * <p>This key will be available if the camera device advertises this key via {@link ACAMERA_REQUEST_AVAILABLE_RESULT_KEYS }.
+     * When available, this must be one of valid physical IDs backing this logical multi-camera.
+     * If this key is not available for a logical multi-camera, the camera device implementation
+     * may still switch between different active physical cameras based on use case, but the
+     * current active physical camera information won't be available to the application.</p>
+     */
+    ACAMERA_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID =           // byte
+            ACAMERA_LOGICAL_MULTI_CAMERA_START + 2,
     ACAMERA_LOGICAL_MULTI_CAMERA_END,
 
     /**
@@ -5463,8 +5755,8 @@
      * will not slow down capture rate when applying correction. FAST may be the same as OFF if
      * any correction at all would slow down capture rate.  Every output stream will have a
      * similar amount of enhancement applied.</p>
-     * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
-     * applied to any RAW output.</p>
+     * <p>The correction only applies to processed outputs such as YUV, Y8, JPEG, or DEPTH16; it is
+     * not applied to any RAW output.</p>
      * <p>This control will be on by default on devices that support this control. Applications
      * disabling distortion correction need to pay extra attention with the coordinate system of
      * metering regions, crop region, and face rectangles. When distortion correction is OFF,
@@ -5517,6 +5809,80 @@
             ACAMERA_DISTORTION_CORRECTION_START + 1,
     ACAMERA_DISTORTION_CORRECTION_END,
 
+    /**
+     * <p>The available HEIC (ISO/IEC 23008-12) stream
+     * configurations that this camera device supports
+     * (i.e. format, width, height, output/input stream).</p>
+     *
+     * <p>Type: int32[n*4] (acamera_metadata_enum_android_heic_available_heic_stream_configurations_t)</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>The configurations are listed as <code>(format, width, height, input?)</code> tuples.</p>
+     * <p>If the camera device supports HEIC image format, it will support identical set of stream
+     * combinations involving HEIC image format, compared to the combinations involving JPEG
+     * image format as required by the device's hardware level and capabilities.</p>
+     * <p>All the static, control, and dynamic metadata tags related to JPEG apply to HEIC formats.
+     * Configuring JPEG and HEIC streams at the same time is not supported.</p>
+     * <p>All the configuration tuples <code>(format, width, height, input?)</code> will contain
+     * AIMAGE_FORMAT_HEIC format as OUTPUT only.</p>
+     */
+    ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS =         // int32[n*4] (acamera_metadata_enum_android_heic_available_heic_stream_configurations_t)
+            ACAMERA_HEIC_START,
+    /**
+     * <p>This lists the minimum frame duration for each
+     * format/size combination for HEIC output formats.</p>
+     *
+     * <p>Type: int64[4*n]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>This should correspond to the frame duration when only that
+     * stream is active, with all processing (typically in android.*.mode)
+     * set to either OFF or FAST.</p>
+     * <p>When multiple streams are used in a request, the minimum frame
+     * duration will be max(individual stream min durations).</p>
+     * <p>See ACAMERA_SENSOR_FRAME_DURATION and
+     * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS for more details about
+     * calculating the max frame rate.</p>
+     *
+     * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS
+     * @see ACAMERA_SENSOR_FRAME_DURATION
+     */
+    ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS =           // int64[4*n]
+            ACAMERA_HEIC_START + 1,
+    /**
+     * <p>This lists the maximum stall duration for each
+     * output format/size combination for HEIC streams.</p>
+     *
+     * <p>Type: int64[4*n]</p>
+     *
+     * <p>This tag may appear in:
+     * <ul>
+     *   <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+     * </ul></p>
+     *
+     * <p>A stall duration is how much extra time would get added
+     * to the normal minimum frame duration for a repeating request
+     * that has streams with non-zero stall.</p>
+     * <p>This functions similarly to
+     * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS for HEIC
+     * streams.</p>
+     * <p>All HEIC output stream formats may have a nonzero stall
+     * duration.</p>
+     *
+     * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS
+     */
+    ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS =               // int64[4*n]
+            ACAMERA_HEIC_START + 2,
+    ACAMERA_HEIC_END,
+
 } acamera_metadata_tag_t;
 
 /**
@@ -6333,7 +6699,7 @@
 
     /**
      * <p>Optimized for dim settings where the main light source
-     * is a flame.</p>
+     * is a candle.</p>
      */
     ACAMERA_CONTROL_SCENE_MODE_CANDLELIGHT                           = 15,
 
@@ -7011,6 +7377,10 @@
      * <p>If this is supported, android.scaler.streamConfigurationMap will
      * additionally return a min frame duration that is greater than
      * zero for each supported size-format combination.</p>
+     * <p>For camera devices with LOGICAL_MULTI_CAMERA capability, when the underlying active
+     * physical camera switches, exposureTime, sensitivity, and lens properties may change
+     * even if AE/AF is locked. However, the overall auto exposure and auto focus experience
+     * for users will be consistent. Refer to LOGICAL_MULTI_CAMERA capability for details.</p>
      *
      * @see ACAMERA_BLACK_LEVEL_LOCK
      * @see ACAMERA_CONTROL_AE_LOCK
@@ -7066,6 +7436,10 @@
      * will accurately report the values applied by AWB in the result.</p>
      * <p>A given camera device may also support additional post-processing
      * controls, but this capability only covers the above list of controls.</p>
+     * <p>For camera devices with LOGICAL_MULTI_CAMERA capability, when underlying active
+     * physical camera switches, tonemap, white balance, and shading map may change even if
+     * awb is locked. However, the overall post-processing experience for users will be
+     * consistent. Refer to LOGICAL_MULTI_CAMERA capability for details.</p>
      *
      * @see ACAMERA_COLOR_CORRECTION_ABERRATION_MODE
      * @see ACAMERA_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES
@@ -7137,19 +7511,20 @@
 
     /**
      * <p>The camera device supports capturing high-resolution images at &gt;= 20 frames per
-     * second, in at least the uncompressed YUV format, when post-processing settings are set
-     * to FAST. Additionally, maximum-resolution images can be captured at &gt;= 10 frames
-     * per second.  Here, 'high resolution' means at least 8 megapixels, or the maximum
-     * resolution of the device, whichever is smaller.</p>
+     * second, in at least the uncompressed YUV format, when post-processing settings are
+     * set to FAST. Additionally, all image resolutions less than 24 megapixels can be
+     * captured at &gt;= 10 frames per second. Here, 'high resolution' means at least 8
+     * megapixels, or the maximum resolution of the device, whichever is smaller.</p>
      * <p>More specifically, this means that at least one output {@link AIMAGE_FORMAT_YUV_420_888 } size listed in
      * {@link ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS }
      * is larger or equal to the 'high resolution' defined above, and can be captured at at
      * least 20 fps.  For the largest {@link AIMAGE_FORMAT_YUV_420_888 } size listed in
      * {@link ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS },
-     * camera device can capture this size for at least 10 frames per second.  Also the
-     * ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES entry lists at least one FPS range where
-     * the minimum FPS is &gt;= 1 / minimumFrameDuration for the largest YUV_420_888 size.</p>
-     * <p>If the device supports the {@link AIMAGE_FORMAT_RAW10 }, {@link AIMAGE_FORMAT_RAW12 }, then those can also be
+     * camera device can capture this size for at least 10 frames per second if the size is
+     * less than 24 megapixels. Also the ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES entry
+     * lists at least one FPS range where the minimum FPS is &gt;= 1 / minimumFrameDuration
+     * for the largest YUV_420_888 size.</p>
+     * <p>If the device supports the {@link AIMAGE_FORMAT_RAW10 }, {@link AIMAGE_FORMAT_RAW12 }, {@link AIMAGE_FORMAT_Y8 }, then those can also be
      * captured at the same rate as the maximum-size YUV_420_888 resolution is.</p>
      * <p>In addition, the ACAMERA_SYNC_MAX_LATENCY field is guaranted to have a value between 0
      * and 4, inclusive. ACAMERA_CONTROL_AE_LOCK_AVAILABLE and ACAMERA_CONTROL_AWB_LOCK_AVAILABLE
@@ -7183,8 +7558,8 @@
      * <li>The ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE entry is listed by this device.</li>
      * <li>As of Android P, the ACAMERA_LENS_POSE_REFERENCE entry is listed by this device.</li>
      * <li>A LIMITED camera with only the DEPTH_OUTPUT capability does not have to support
-     *   normal YUV_420_888, JPEG, and PRIV-format outputs. It only has to support the DEPTH16
-     *   format.</li>
+     *   normal YUV_420_888, Y8, JPEG, and PRIV-format outputs. It only has to support the
+     *   DEPTH16 format.</li>
      * </ul>
      * <p>Generally, depth output operates at a slower frame rate than standard color capture,
      * so the DEPTH16 and DEPTH_POINT_CLOUD formats will commonly have a stall duration that
@@ -7215,8 +7590,23 @@
     ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING           = 10,
 
     /**
-     * <p>The camera device is a logical camera backed by two or more physical cameras that are
-     * also exposed to the application.</p>
+     * <p>The camera device is a logical camera backed by two or more physical cameras.</p>
+     * <p>In API level 28, the physical cameras must also be exposed to the application via
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraIdList">CameraManager#getCameraIdList</a>.</p>
+     * <p>Starting from API level 29, some or all physical cameras may not be independently
+     * exposed to the application, in which case the physical camera IDs will not be
+     * available in <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraIdList">CameraManager#getCameraIdList</a>. But the
+     * application can still query the physical cameras' characteristics by calling
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraCharacteristics">CameraManager#getCameraCharacteristics</a>. Additionally,
+     * if a physical camera is hidden from camera ID list, the mandatory stream combinations
+     * for that physical camera must be supported through the logical camera using physical
+     * streams.</p>
+     * <p>Combinations of logical and physical streams, or physical streams from different
+     * physical cameras are not guaranteed. However, if the camera device supports
+     * {@link ACameraDevice_isSessionConfigurationSupported },
+     * application must be able to query whether a stream combination involving physical
+     * streams is supported by calling
+     * {@link ACameraDevice_isSessionConfigurationSupported }.</p>
      * <p>Camera application shouldn't assume that there are at most 1 rear camera and 1 front
      * camera in the system. For an application that switches between front and back cameras,
      * the recommendation is to switch between the first rear camera and the first front
@@ -7239,42 +7629,128 @@
      * </li>
      * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be
      *   the same.</li>
-     * <li>The logical camera device must be LIMITED or higher device.</li>
+     * <li>The logical camera must be LIMITED or higher device.</li>
      * </ul>
-     * <p>Both the logical camera device and its underlying physical devices support the
-     * mandatory stream combinations required for their device levels.</p>
-     * <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p>
+     * <p>A logical camera device's dynamic metadata may contain
+     * ACAMERA_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID to notify the application of the current
+     * active physical camera Id. An active physical camera is the physical camera from which
+     * the logical camera's main image data outputs (YUV or RAW) and metadata come from.
+     * In addition, this serves as an indication which physical camera is used to output to
+     * a RAW stream, or in case only physical cameras support RAW, which physical RAW stream
+     * the application should request.</p>
+     * <p>Logical camera's static metadata tags below describe the default active physical
+     * camera. An active physical camera is default if it's used when application directly
+     * uses requests built from a template. All templates will default to the same active
+     * physical camera.</p>
      * <ul>
-     * <li>For each guaranteed stream combination, the logical camera supports replacing one
-     *   logical {@link AIMAGE_FORMAT_YUV_420_888 YUV_420_888}
-     *   or raw stream with two physical streams of the same size and format, each from a
-     *   separate physical camera, given that the size and format are supported by both
-     *   physical cameras.</li>
-     * <li>If the logical camera doesn't advertise RAW capability, but the underlying physical
-     *   cameras do, the logical camera will support guaranteed stream combinations for RAW
-     *   capability, except that the RAW streams will be physical streams, each from a separate
-     *   physical camera. This is usually the case when the physical cameras have different
-     *   sensor sizes.</li>
+     * <li>ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE</li>
+     * <li>ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT</li>
+     * <li>ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE</li>
+     * <li>ACAMERA_SENSOR_INFO_MAX_FRAME_DURATION</li>
+     * <li>ACAMERA_SENSOR_INFO_PHYSICAL_SIZE</li>
+     * <li>ACAMERA_SENSOR_INFO_WHITE_LEVEL</li>
+     * <li>ACAMERA_SENSOR_INFO_LENS_SHADING_APPLIED</li>
+     * <li>ACAMERA_SENSOR_REFERENCE_ILLUMINANT1</li>
+     * <li>ACAMERA_SENSOR_REFERENCE_ILLUMINANT2</li>
+     * <li>ACAMERA_SENSOR_CALIBRATION_TRANSFORM1</li>
+     * <li>ACAMERA_SENSOR_CALIBRATION_TRANSFORM2</li>
+     * <li>ACAMERA_SENSOR_COLOR_TRANSFORM1</li>
+     * <li>ACAMERA_SENSOR_COLOR_TRANSFORM2</li>
+     * <li>ACAMERA_SENSOR_FORWARD_MATRIX1</li>
+     * <li>ACAMERA_SENSOR_FORWARD_MATRIX2</li>
+     * <li>ACAMERA_SENSOR_BLACK_LEVEL_PATTERN</li>
+     * <li>ACAMERA_SENSOR_MAX_ANALOG_SENSITIVITY</li>
+     * <li>ACAMERA_SENSOR_OPTICAL_BLACK_REGIONS</li>
+     * <li>ACAMERA_SENSOR_AVAILABLE_TEST_PATTERN_MODES</li>
+     * <li>ACAMERA_LENS_INFO_HYPERFOCAL_DISTANCE</li>
+     * <li>ACAMERA_LENS_INFO_MINIMUM_FOCUS_DISTANCE</li>
+     * <li>ACAMERA_LENS_INFO_FOCUS_DISTANCE_CALIBRATION</li>
+     * <li>ACAMERA_LENS_POSE_ROTATION</li>
+     * <li>ACAMERA_LENS_POSE_TRANSLATION</li>
+     * <li>ACAMERA_LENS_INTRINSIC_CALIBRATION</li>
+     * <li>ACAMERA_LENS_POSE_REFERENCE</li>
+     * <li>ACAMERA_LENS_DISTORTION</li>
      * </ul>
-     * <p>Using physical streams in place of a logical stream of the same size and format will
-     * not slow down the frame rate of the capture, as long as the minimum frame duration
-     * of the physical and logical streams are the same.</p>
+     * <p>The field of view of all non-RAW physical streams must be the same or as close as
+     * possible to that of non-RAW logical streams. If the requested FOV is outside of the
+     * range supported by the physical camera, the physical stream for that physical camera
+     * will use either the maximum or minimum scaler crop region, depending on which one is
+     * closer to the requested FOV. For example, for a logical camera with wide-tele lens
+     * configuration where the wide lens is the default, if the logical camera's crop region
+     * is set to maximum, the physical stream for the tele lens will be configured to its
+     * maximum crop region. On the other hand, if the logical camera has a normal-wide lens
+     * configuration where the normal lens is the default, when the logical camera's crop
+     * region is set to maximum, the FOV of the logical streams will be that of the normal
+     * lens. The FOV of the physical streams for the wide lens will be the same as the
+     * logical stream, by making the crop region smaller than its active array size to
+     * compensate for the smaller focal length.</p>
+     * <p>Even if the underlying physical cameras have different RAW characteristics (such as
+     * size or CFA pattern), a logical camera can still advertise RAW capability. In this
+     * case, when the application configures a RAW stream, the camera device will make sure
+     * the active physical camera will remain active to ensure consistent RAW output
+     * behavior, and not switch to other physical cameras.</p>
+     * <p>The capture request and result metadata tags required for backward compatible camera
+     * functionalities will be solely based on the logical camera capabiltity. On the other
+     * hand, the use of manual capture controls (sensor or post-processing) with a
+     * logical camera may result in unexpected behavior when the HAL decides to switch
+     * between physical cameras with different characteristics under the hood. For example,
+     * when the application manually sets exposure time and sensitivity while zooming in,
+     * the brightness of the camera images may suddenly change because HAL switches from one
+     * physical camera to the other.</p>
      *
      * @see ACAMERA_LENS_DISTORTION
+     * @see ACAMERA_LENS_INFO_FOCUS_DISTANCE_CALIBRATION
+     * @see ACAMERA_LENS_INFO_HYPERFOCAL_DISTANCE
+     * @see ACAMERA_LENS_INFO_MINIMUM_FOCUS_DISTANCE
      * @see ACAMERA_LENS_INTRINSIC_CALIBRATION
      * @see ACAMERA_LENS_POSE_REFERENCE
      * @see ACAMERA_LENS_POSE_ROTATION
      * @see ACAMERA_LENS_POSE_TRANSLATION
+     * @see ACAMERA_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID
      * @see ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+     * @see ACAMERA_SENSOR_AVAILABLE_TEST_PATTERN_MODES
+     * @see ACAMERA_SENSOR_BLACK_LEVEL_PATTERN
+     * @see ACAMERA_SENSOR_CALIBRATION_TRANSFORM1
+     * @see ACAMERA_SENSOR_CALIBRATION_TRANSFORM2
+     * @see ACAMERA_SENSOR_COLOR_TRANSFORM1
+     * @see ACAMERA_SENSOR_COLOR_TRANSFORM2
+     * @see ACAMERA_SENSOR_FORWARD_MATRIX1
+     * @see ACAMERA_SENSOR_FORWARD_MATRIX2
+     * @see ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+     * @see ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE
+     * @see ACAMERA_SENSOR_INFO_LENS_SHADING_APPLIED
+     * @see ACAMERA_SENSOR_INFO_MAX_FRAME_DURATION
+     * @see ACAMERA_SENSOR_INFO_PHYSICAL_SIZE
+     * @see ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE
+     * @see ACAMERA_SENSOR_INFO_WHITE_LEVEL
+     * @see ACAMERA_SENSOR_MAX_ANALOG_SENSITIVITY
+     * @see ACAMERA_SENSOR_OPTICAL_BLACK_REGIONS
+     * @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
+     * @see ACAMERA_SENSOR_REFERENCE_ILLUMINANT2
      */
     ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA      = 11,
 
     /**
      * <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. 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,
 
+    /**
+     * <p>The camera device is capable of writing image data into a region of memory
+     * inaccessible to Android userspace or the Android kernel, and only accessible to
+     * trusted execution environments (TEE).</p>
+     */
+    ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA         = 13,
+
 } acamera_metadata_enum_android_request_available_capabilities_t;
 
 
@@ -7300,6 +7776,81 @@
 
 } acamera_metadata_enum_android_scaler_cropping_type_t;
 
+// ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS
+typedef enum acamera_metadata_enum_acamera_scaler_available_recommended_stream_configurations {
+    /**
+     * <p>Preview must only include non-stalling processed stream configurations with
+     * output formats like
+     * {@link AIMAGE_FORMAT_YUV_420_888 },
+     * {@link AIMAGE_FORMAT_PRIVATE }, etc.</p>
+     */
+    ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PREVIEW
+                                                                      = 0x0,
+
+    /**
+     * <p>Video record must include stream configurations that match the advertised
+     * supported media profiles <a href="https://developer.android.com/reference/android/media/CamcorderProfile.html">CamcorderProfile</a> with
+     * IMPLEMENTATION_DEFINED format.</p>
+     */
+    ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_RECORD
+                                                                      = 0x1,
+
+    /**
+     * <p>Video snapshot must include stream configurations at least as big as
+     * the maximum RECORD resolutions and only with
+     * {@link AIMAGE_FORMAT_JPEG JPEG output format}.
+     * Additionally the configurations shouldn't cause preview glitches and also be able to
+     * run at 30 fps.</p>
+     */
+    ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_VIDEO_SNAPSHOT
+                                                                      = 0x2,
+
+    /**
+     * <p>Recommended snapshot stream configurations must include at least one with
+     * size close to ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE and
+     * {@link AIMAGE_FORMAT_JPEG JPEG output format}.
+     * Taking into account restrictions on aspect ratio, alignment etc. the area of the
+     * maximum suggested size shouldn’t be less than 97% of the sensor array size area.</p>
+     *
+     * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+     */
+    ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_SNAPSHOT
+                                                                      = 0x3,
+
+    /**
+     * <p>If supported, recommended input stream configurations must only be advertised with
+     * ZSL along with other processed and/or stalling output formats.</p>
+     */
+    ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_ZSL   = 0x4,
+
+    /**
+     * <p>If supported, recommended raw stream configurations must only include RAW based
+     * output formats.</p>
+     */
+    ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_RAW   = 0x5,
+
+    /**
+     * <p>If supported, the recommended low latency stream configurations must have
+     * end-to-end latency that does not exceed 200 ms. under standard operating conditions
+     * (reasonable light levels, not loaded system) and using template
+     * TEMPLATE_STILL_CAPTURE. This is primarily for listing configurations for the
+     * {@link AIMAGE_FORMAT_JPEG JPEG output format}
+     * however other supported output formats can be added as well.</p>
+     */
+    ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_LOW_LATENCY_SNAPSHOT
+                                                                      = 0x6,
+
+    ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PUBLIC_END
+                                                                      = 0x7,
+
+    /**
+     * <p>Vendor defined use cases. These depend on the vendor implementation.</p>
+     */
+    ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_VENDOR_START
+                                                                      = 0x18,
+
+} acamera_metadata_enum_android_scaler_available_recommended_stream_configurations_t;
+
 
 // ACAMERA_SENSOR_REFERENCE_ILLUMINANT1
 typedef enum acamera_metadata_enum_acamera_sensor_reference_illuminant1 {
@@ -7476,6 +8027,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
@@ -7764,6 +8330,7 @@
      * fire the flash for flash power metering during precapture, and then fire the flash
      * for the final capture, if a flash is available on the device and the AE mode is set to
      * enable the flash.</p>
+     * <p>Devices that initially shipped with Android version <a href="https://developer.android.com/reference/android/os/Build/VERSION_CODES.html#Q">Q</a> or newer will not include any LEGACY-level devices.</p>
      *
      * @see ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER
      * @see ACAMERA_REQUEST_AVAILABLE_CAPABILITIES
@@ -7904,6 +8471,16 @@
 
 } acamera_metadata_enum_android_depth_depth_is_exclusive_t;
 
+// ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS
+typedef enum acamera_metadata_enum_acamera_depth_available_dynamic_depth_stream_configurations {
+    ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_OUTPUT
+                                                                      = 0,
+
+    ACAMERA_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_INPUT
+                                                                      = 1,
+
+} acamera_metadata_enum_android_depth_available_dynamic_depth_stream_configurations_t;
+
 
 // ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
 typedef enum acamera_metadata_enum_acamera_logical_multi_camera_sensor_sync_type {
@@ -7947,6 +8524,16 @@
 } acamera_metadata_enum_android_distortion_correction_mode_t;
 
 
+// ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS
+typedef enum acamera_metadata_enum_acamera_heic_available_heic_stream_configurations {
+    ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_OUTPUT         = 0,
+
+    ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_INPUT          = 1,
+
+} acamera_metadata_enum_android_heic_available_heic_stream_configurations_t;
+
+
+
 #endif /* __ANDROID_API__ >= 24 */
 
 __END_DECLS
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 4961ce3..d3f8826 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,8 @@
  *
  * @see ACaptureRequest_addTarget
  */
-camera_status_t ACameraOutputTarget_create(ANativeWindow* window, ACameraOutputTarget** output);
+camera_status_t ACameraOutputTarget_create(ACameraWindowType* window,
+        ACameraOutputTarget** output) __INTRODUCED_IN(24);
 
 /**
  * Free a ACameraOutputTarget object.
@@ -110,7 +111,7 @@
  *
  * @see ACameraOutputTarget_create
  */
-void ACameraOutputTarget_free(ACameraOutputTarget* output);
+void ACameraOutputTarget_free(ACameraOutputTarget* output) __INTRODUCED_IN(24);
 
 /**
  * Add an {@link ACameraOutputTarget} object to {@link ACaptureRequest}.
@@ -123,7 +124,7 @@
  *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if request or output is NULL.</li></ul>
  */
 camera_status_t ACaptureRequest_addTarget(ACaptureRequest* request,
-        const ACameraOutputTarget* output);
+        const ACameraOutputTarget* output) __INTRODUCED_IN(24);
 
 /**
  * Remove an {@link ACameraOutputTarget} object from {@link ACaptureRequest}.
@@ -138,7 +139,7 @@
  *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if request or output is NULL.</li></ul>
  */
 camera_status_t ACaptureRequest_removeTarget(ACaptureRequest* request,
-        const ACameraOutputTarget* output);
+        const ACameraOutputTarget* output) __INTRODUCED_IN(24);
 
 /**
  * Get a metadata entry from input {@link ACaptureRequest}.
@@ -158,7 +159,7 @@
  *             entry of input tag value.</li></ul>
  */
 camera_status_t ACaptureRequest_getConstEntry(
-        const ACaptureRequest* request, uint32_t tag, ACameraMetadata_const_entry* entry);
+        const ACaptureRequest* request, uint32_t tag, ACameraMetadata_const_entry* entry) __INTRODUCED_IN(24);
 
 /*
  * List all the entry tags in input {@link ACaptureRequest}.
@@ -179,7 +180,7 @@
  *         <li>{@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons.</li></ul>
  */
 camera_status_t ACaptureRequest_getAllTags(
-        const ACaptureRequest* request, /*out*/int32_t* numTags, /*out*/const uint32_t** tags);
+        const ACaptureRequest* request, /*out*/int32_t* numTags, /*out*/const uint32_t** tags) __INTRODUCED_IN(24);
 
 /**
  * Set/change a camera capture control entry with unsigned 8 bits data type.
@@ -198,7 +199,7 @@
  *             the tag is not controllable by application.</li></ul>
  */
 camera_status_t ACaptureRequest_setEntry_u8(
-        ACaptureRequest* request, uint32_t tag, uint32_t count, const uint8_t* data);
+        ACaptureRequest* request, uint32_t tag, uint32_t count, const uint8_t* data) __INTRODUCED_IN(24);
 
 /**
  * Set/change a camera capture control entry with signed 32 bits data type.
@@ -217,7 +218,7 @@
  *             the tag is not controllable by application.</li></ul>
  */
 camera_status_t ACaptureRequest_setEntry_i32(
-        ACaptureRequest* request, uint32_t tag, uint32_t count, const int32_t* data);
+        ACaptureRequest* request, uint32_t tag, uint32_t count, const int32_t* data) __INTRODUCED_IN(24);
 
 /**
  * Set/change a camera capture control entry with float data type.
@@ -236,7 +237,7 @@
  *             the tag is not controllable by application.</li></ul>
  */
 camera_status_t ACaptureRequest_setEntry_float(
-        ACaptureRequest* request, uint32_t tag, uint32_t count, const float* data);
+        ACaptureRequest* request, uint32_t tag, uint32_t count, const float* data) __INTRODUCED_IN(24);
 
 /**
  * Set/change a camera capture control entry with signed 64 bits data type.
@@ -255,7 +256,7 @@
  *             the tag is not controllable by application.</li></ul>
  */
 camera_status_t ACaptureRequest_setEntry_i64(
-        ACaptureRequest* request, uint32_t tag, uint32_t count, const int64_t* data);
+        ACaptureRequest* request, uint32_t tag, uint32_t count, const int64_t* data) __INTRODUCED_IN(24);
 
 /**
  * Set/change a camera capture control entry with double data type.
@@ -274,7 +275,7 @@
  *             the tag is not controllable by application.</li></ul>
  */
 camera_status_t ACaptureRequest_setEntry_double(
-        ACaptureRequest* request, uint32_t tag, uint32_t count, const double* data);
+        ACaptureRequest* request, uint32_t tag, uint32_t count, const double* data) __INTRODUCED_IN(24);
 
 /**
  * Set/change a camera capture control entry with rational data type.
@@ -294,14 +295,14 @@
  */
 camera_status_t ACaptureRequest_setEntry_rational(
         ACaptureRequest* request, uint32_t tag, uint32_t count,
-        const ACameraMetadata_rational* data);
+        const ACameraMetadata_rational* data) __INTRODUCED_IN(24);
 
 /**
  * Free a {@link ACaptureRequest} structure.
  *
  * @param request the {@link ACaptureRequest} to be freed.
  */
-void ACaptureRequest_free(ACaptureRequest* request);
+void ACaptureRequest_free(ACaptureRequest* request) __INTRODUCED_IN(24);
 
 #endif /* __ANDROID_API__ >= 24 */
 
@@ -325,7 +326,7 @@
  *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if request is NULL.</li></ul>
  */
 camera_status_t ACaptureRequest_setUserContext(
-        ACaptureRequest* request, void* context);
+        ACaptureRequest* request, void* context) __INTRODUCED_IN(28);
 
 /**
  * Get the user context pointer of the {@link ACaptureRequest}
@@ -341,7 +342,7 @@
  *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if request is NULL.</li></ul>
  */
 camera_status_t ACaptureRequest_getUserContext(
-        const ACaptureRequest* request, /*out*/void** context);
+        const ACaptureRequest* request, /*out*/void** context) __INTRODUCED_IN(28);
 
 /**
  * Create a copy of input {@link ACaptureRequest}.
@@ -353,10 +354,223 @@
  *
  * @return a valid ACaptureRequest pointer or NULL if the input request cannot be copied.
  */
-ACaptureRequest* ACaptureRequest_copy(const ACaptureRequest* src);
+ACaptureRequest* ACaptureRequest_copy(const ACaptureRequest* src) __INTRODUCED_IN(28);
 
 #endif /* __ANDROID_API__ >= 28 */
 
+#if __ANDROID_API__ >= 29
+
+/**
+ * Get a metadata entry from input {@link ACaptureRequest} for
+ * a physical camera backing a logical multi-camera device.
+ *
+ * <p>Same as ACaptureRequest_getConstEntry, except that if the key is contained
+ * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
+ * returns the entry set by ACaptureRequest_setEntry_physicalCamera_* class of
+ * functions on the particular physical camera.</p>
+ *
+ * @param request the {@link ACaptureRequest} of interest created by
+ *                {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param physicalId one of the physical Ids used when request is created with
+ *                   {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param tag the capture request metadata tag in
+ *            {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}
+ *            that is set by ACaptureRequest_setEntry_physicalCamera_* class of functions.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if metadata, physicalId, or entry is NULL,
+ *         physicalId is not one of the Ids used in creating the request, or if the capture
+ *         request is a regular request with no physical Ids at all.</li>
+ *         <li>{@link ACAMERA_ERROR_METADATA_NOT_FOUND} if the capture request does not contain an
+ *             entry of input tag value.</li></ul>
+ */
+camera_status_t ACaptureRequest_getConstEntry_physicalCamera(
+        const ACaptureRequest* request, const char* physicalId, uint32_t tag,
+        ACameraMetadata_const_entry* entry) __INTRODUCED_IN(29);
+
+/**
+ * Set/change a camera capture control entry with unsigned 8 bits data type for
+ * a physical camera backing a logical multi-camera device.
+ *
+ * <p>Same as ACaptureRequest_setEntry_u8, except that if {@link tag} is contained
+ * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
+ * sets the entry for a particular physical sub-camera backing the logical multi-camera.
+ * If {@link tag} is not contained in
+ * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
+ * by the camera device.</p>
+ *
+ * @param request the {@link ACaptureRequest} of interest created by
+ *                {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param physicalId one of the physical Ids used when request is created with
+ *                   {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param tag one of the capture request metadata tags in
+ *            {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is
+ *             larger than zero while data is NULL, the data type of the tag is not unsigned 8 bits,
+ *             the tag is not controllable by application, physicalId is not one of the Ids used
+ *             in creating the request, or if the capture request is a regular request with no
+ *             physical Ids at all.</li></ul>
+ */
+camera_status_t ACaptureRequest_setEntry_physicalCamera_u8(
+        ACaptureRequest* request, const char* physicalId, uint32_t tag,
+        uint32_t count, const uint8_t* data) __INTRODUCED_IN(29);
+
+/**
+ * Set/change a camera capture control entry with signed 32 bits data type for
+ * a physical camera of a logical multi-camera device.
+ *
+ * <p>Same as ACaptureRequest_setEntry_i32, except that if {@link tag} is contained
+ * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
+ * sets the entry for a particular physical sub-camera backing the logical multi-camera.
+ * If {@link tag} is not contained in
+ * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
+ * by the camera device.</p>
+ *
+ * @param request the {@link ACaptureRequest} of interest created by
+ *                {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param physicalId one of the physical Ids used when request is created with
+ *                   {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param tag one of the capture request metadata tags in
+ *            {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is
+ *             larger than zero while data is NULL, the data type of the tag is not signed 32 bits,
+ *             the tag is not controllable by application, physicalId is not one of the Ids used
+ *             in creating the request, or if the capture request is a regular request with no
+ *             physical Ids at all.</li></ul>
+ */
+camera_status_t ACaptureRequest_setEntry_physicalCamera_i32(
+        ACaptureRequest* request, const char* physicalId, uint32_t tag,
+        uint32_t count, const int32_t* data) __INTRODUCED_IN(29);
+
+/**
+ * Set/change a camera capture control entry with float data type for
+ * a physical camera of a logical multi-camera device.
+ *
+ * <p>Same as ACaptureRequest_setEntry_float, except that if {@link tag} is contained
+ * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
+ * sets the entry for a particular physical sub-camera backing the logical multi-camera.
+ * If {@link tag} is not contained in
+ * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
+ * by the camera device.</p>
+ *
+ * @param request the {@link ACaptureRequest} of interest created by
+ *                {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param physicalId one of the physical Ids used when request is created with
+ *                   {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param tag one of the capture request metadata tags in
+ *            {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is
+ *             larger than zero while data is NULL, the data type of the tag is not float,
+ *             the tag is not controllable by application, physicalId is not one of the Ids used
+ *             in creating the request, or if the capture request is a regular request with no
+ *             physical Ids at all.</li></ul>
+ */
+camera_status_t ACaptureRequest_setEntry_physicalCamera_float(
+        ACaptureRequest* request, const char* physicalId, uint32_t tag,
+        uint32_t count, const float* data) __INTRODUCED_IN(29);
+
+/**
+ * Set/change a camera capture control entry with signed 64 bits data type for
+ * a physical camera of a logical multi-camera device.
+ *
+ * <p>Same as ACaptureRequest_setEntry_i64, except that if {@link tag} is contained
+ * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
+ * sets the entry for a particular physical sub-camera backing the logical multi-camera.
+ * If {@link tag} is not contained in
+ * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
+ * by the camera device.</p>
+ *
+ * @param request the {@link ACaptureRequest} of interest created by
+ *                {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param physicalId one of the physical Ids used when request is created with
+ *                   {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param tag one of the capture request metadata tags in
+ *            {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is
+ *             larger than zero while data is NULL, the data type of the tag is not signed 64 bits,
+ *             the tag is not controllable by application, physicalId is not one of the Ids used
+ *             in creating the request, or if the capture request is a regular request with no
+ *             physical Ids at all.</li></ul>
+ */
+camera_status_t ACaptureRequest_setEntry_physicalCamera_i64(
+        ACaptureRequest* request, const char* physicalId, uint32_t tag,
+        uint32_t count, const int64_t* data) __INTRODUCED_IN(29);
+
+/**
+ * Set/change a camera capture control entry with double data type for
+ * a physical camera of a logical multi-camera device.
+ *
+ * <p>Same as ACaptureRequest_setEntry_double, except that if {@link tag} is contained
+ * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
+ * sets the entry for a particular physical sub-camera backing the logical multi-camera.
+ * If {@link tag} is not contained in
+ * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
+ * by the camera device.</p>
+ *
+ * @param request the {@link ACaptureRequest} of interest created by
+ *                {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param physicalId one of the physical Ids used when request is created with
+ *                   {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param tag one of the capture request metadata tags in
+ *            {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is
+ *             larger than zero while data is NULL, the data type of the tag is not double,
+ *             the tag is not controllable by application, physicalId is not one of the Ids used
+ *             in creating the request, or if the capture request is a regular request with no
+ *             physical Ids at all.</li></ul>
+ */
+camera_status_t ACaptureRequest_setEntry_physicalCamera_double(
+        ACaptureRequest* request, const char* physicalId, uint32_t tag,
+        uint32_t count, const double* data) __INTRODUCED_IN(29);
+
+/**
+ * Set/change a camera capture control entry with rational data type for
+ * a physical camera of a logical multi-camera device.
+ *
+ * <p>Same as ACaptureRequest_setEntry_rational, except that if {@link tag} is contained
+ * in {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, this function
+ * sets the entry for a particular physical sub-camera backing the logical multi-camera.
+ * If {@link tag} is not contained in
+ * {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}, the key will be ignored
+ * by the camera device.</p>
+ *
+ * @param request the {@link ACaptureRequest} of interest created by
+ *                {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param physicalId one of the physical Ids used when request is created with
+ *                   {@link ACameraDevice_createCaptureRequest_withPhysicalIds}.
+ * @param tag one of the capture request metadata tags in
+ *            {@link ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS}.
+ *
+ * @return <ul>
+ *         <li>{@link ACAMERA_OK} if the method call succeeds.</li>
+ *         <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if request or physicalId is NULL, count is
+ *             larger than zero while data is NULL, the data type of the tag is not rational,
+ *             the tag is not controllable by application, physicalId is not one of the Ids used
+ *             in creating the request, or if the capture request is a regular request with no
+ *             physical Ids at all.</li></ul>
+ */
+camera_status_t ACaptureRequest_setEntry_physicalCamera_rational(
+        ACaptureRequest* request, const char* physicalId, uint32_t tag,
+        uint32_t count, const ACameraMetadata_rational* data) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
 __END_DECLS
 
 #endif /* _NDK_CAPTURE_REQUEST_H */
diff --git a/camera/ndk/libcamera2ndk.map.txt b/camera/ndk/libcamera2ndk.map.txt
index d179aa0..b6f1553 100644
--- a/camera/ndk/libcamera2ndk.map.txt
+++ b/camera/ndk/libcamera2ndk.map.txt
@@ -2,15 +2,19 @@
   global:
     ACameraCaptureSession_abortCaptures;
     ACameraCaptureSession_capture;
+    ACameraCaptureSession_logicalCamera_capture; # introduced=29
     ACameraCaptureSession_close;
     ACameraCaptureSession_getDevice;
     ACameraCaptureSession_setRepeatingRequest;
+    ACameraCaptureSession_logicalCamera_setRepeatingRequest; # introduced=29
     ACameraCaptureSession_stopRepeating;
-    ACameraCaptureSession_updateSharedOutput;
+    ACameraCaptureSession_updateSharedOutput; # introduced=28
     ACameraDevice_close;
     ACameraDevice_createCaptureRequest;
+    ACameraDevice_createCaptureRequest_withPhysicalIds; # introduced=29
     ACameraDevice_createCaptureSession;
-    ACameraDevice_createCaptureSessionWithSessionParameters;
+    ACameraDevice_createCaptureSessionWithSessionParameters; # introduced=28
+    ACameraDevice_isSessionConfigurationSupported; # introduced=29
     ACameraDevice_getId;
     ACameraManager_create;
     ACameraManager_delete;
@@ -20,34 +24,45 @@
     ACameraManager_openCamera;
     ACameraManager_registerAvailabilityCallback;
     ACameraManager_unregisterAvailabilityCallback;
+    ACameraManager_registerExtendedAvailabilityCallback; # introduced=29
+    ACameraManager_unregisterExtendedAvailabilityCallback; # introduced=29
     ACameraMetadata_copy;
     ACameraMetadata_free;
     ACameraMetadata_getAllTags;
     ACameraMetadata_getConstEntry;
+    ACameraMetadata_isLogicalMultiCamera; # introduced=29
     ACameraOutputTarget_create;
     ACameraOutputTarget_free;
     ACaptureRequest_addTarget;
-    ACaptureRequest_copy;
+    ACaptureRequest_copy; # introduced=28
     ACaptureRequest_free;
     ACaptureRequest_getAllTags;
     ACaptureRequest_getConstEntry;
-    ACaptureRequest_getUserContext;
+    ACaptureRequest_getConstEntry_physicalCamera; # introduced=29
+    ACaptureRequest_getUserContext; # introduced=28
     ACaptureRequest_removeTarget;
     ACaptureRequest_setEntry_double;
+    ACaptureRequest_setEntry_physicalCamera_double; # introduced=29
     ACaptureRequest_setEntry_float;
+    ACaptureRequest_setEntry_physicalCamera_float; # introduced=29
     ACaptureRequest_setEntry_i32;
+    ACaptureRequest_setEntry_physicalCamera_i32; # introduced=29
     ACaptureRequest_setEntry_i64;
+    ACaptureRequest_setEntry_physicalCamera_i64; # introduced=29
     ACaptureRequest_setEntry_rational;
+    ACaptureRequest_setEntry_physicalCamera_rational; # introduced=29
     ACaptureRequest_setEntry_u8;
-    ACaptureRequest_setUserContext;
+    ACaptureRequest_setEntry_physicalCamera_u8; # introduced=29
+    ACaptureRequest_setUserContext; # introduced=28
     ACaptureSessionOutputContainer_add;
     ACaptureSessionOutputContainer_create;
     ACaptureSessionOutputContainer_free;
     ACaptureSessionOutputContainer_remove;
     ACaptureSessionOutput_create;
-    ACaptureSessionSharedOutput_create;
-    ACaptureSessionSharedOutput_add;
-    ACaptureSessionSharedOutput_remove;
+    ACaptureSessionSharedOutput_create; # introduced=28
+    ACaptureSessionSharedOutput_add; # introduced=28
+    ACaptureSessionSharedOutput_remove; # introduced=28
+    ACaptureSessionPhysicalOutput_create; # introduced=29
     ACaptureSessionOutput_free;
   local:
     *;
diff --git a/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h
new file mode 100644
index 0000000..e1af8c1
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraCaptureSessionVendor.h
@@ -0,0 +1,48 @@
+/*
+ * 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 <string>
+#include "utils.h"
+
+struct ACaptureSessionOutput {
+    explicit ACaptureSessionOutput(native_handle_t* window, bool isShared = false,
+            const char* physicalCameraId = "") :
+            mWindow(window), mIsShared(isShared), mPhysicalCameraId(physicalCameraId) {};
+
+    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;
+    std::string mPhysicalCameraId;
+};
+
+
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
new file mode 100644
index 0000000..35c8355
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
@@ -0,0 +1,1719 @@
+/*
+ * 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"
+
+#include "ACameraCaptureSession.inc"
+
+#define CHECK_TRANSACTION_AND_RET(remoteRet, status, callName) \
+    if (!remoteRet.isOk()) { \
+        ALOGE("%s: Transaction error during %s call %s", __FUNCTION__, callName, \
+                  remoteRet.description().c_str()); \
+        return ACAMERA_ERROR_UNKNOWN; \
+    } \
+    if (status != Status::NO_ERROR) { \
+        ALOGE("%s: %s call failed", __FUNCTION__, callName); \
+        return utils::convertFromHidl(status); \
+    }
+
+using namespace android;
+
+ACameraDevice::~ACameraDevice() {
+    mDevice->stopLooper();
+}
+
+namespace android {
+namespace acam {
+
+using HCameraMetadata = frameworks::cameraservice::device::V2_0::CameraMetadata;
+using OutputConfiguration = frameworks::cameraservice::device::V2_0::OutputConfiguration;
+using SessionConfiguration = frameworks::cameraservice::device::V2_0::SessionConfiguration;
+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::kPhysicalCaptureResultKey = "PhysicalCaptureResult";
+const char* CameraDevice::kCaptureFailureKey = "CaptureFailure";
+const char* CameraDevice::kSequenceIdKey     = "SequenceId";
+const char* CameraDevice::kFrameNumberKey    = "FrameNumber";
+const char* CameraDevice::kAnwKey            = "Anw";
+const char* CameraDevice::kFailingPhysicalCameraId= "FailingPhysicalCameraId";
+
+/**
+ * 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(id);
+    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;
+        LOG_ALWAYS_FATAL_IF(mCbLooper != nullptr,
+            "CameraDevice looper should've been stopped before ~CameraDevice");
+    }
+}
+
+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,
+        const ACameraIdList* physicalCameraIdList,
+        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::UNKNOWN_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__);
+            }
+        });
+    CHECK_TRANSACTION_AND_RET(remoteRet, status, "createDefaultRequest()")
+    ACaptureRequest* outReq = new ACaptureRequest();
+    outReq->settings = new ACameraMetadata(rawRequest.release(), ACameraMetadata::ACM_REQUEST);
+    if (physicalCameraIdList != nullptr) {
+        for (auto i = 0; i < physicalCameraIdList->numCameras; i++) {
+            outReq->physicalSettings.emplace(physicalCameraIdList->cameraIds[i],
+                    new ACameraMetadata(*(outReq->settings)));
+        }
+    }
+    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::isSessionConfigurationSupported(
+        const ACaptureSessionOutputContainer* sessionOutputContainer) const {
+    Mutex::Autolock _l(mDeviceLock);
+    camera_status_t ret = checkCameraClosedOrErrorLocked();
+    if (ret != ACAMERA_OK) {
+        return ret;
+    }
+
+    SessionConfiguration sessionConfig;
+    sessionConfig.inputWidth = 0;
+    sessionConfig.inputHeight = 0;
+    sessionConfig.inputFormat = -1;
+    sessionConfig.operationMode = StreamConfigurationMode::NORMAL_MODE;
+    sessionConfig.outputStreams.resize(sessionOutputContainer->mOutputs.size());
+    size_t index = 0;
+    for (const auto& output : sessionOutputContainer->mOutputs) {
+        sessionConfig.outputStreams[index].rotation = utils::convertToHidl(output.mRotation);
+        sessionConfig.outputStreams[index].windowGroupId = -1;
+        sessionConfig.outputStreams[index].windowHandles.resize(output.mSharedWindows.size() + 1);
+        sessionConfig.outputStreams[index].windowHandles[0] = output.mWindow;
+        sessionConfig.outputStreams[index].physicalCameraId = output.mPhysicalCameraId;
+        index++;
+    }
+
+    bool configSupported = false;
+    Status status = Status::UNKNOWN_ERROR;
+    auto remoteRet = mRemote->isSessionConfigurationSupported(sessionConfig,
+        [&status, &configSupported](auto s, auto supported) {
+            status = s;
+            configSupported = supported;
+        });
+
+    CHECK_TRANSACTION_AND_RET(remoteRet, status, "isSessionConfigurationSupported()");
+    return configSupported ? ACAMERA_OK : ACAMERA_ERROR_STREAM_CONFIGURE_FAIL;
+}
+
+static void addMetadataToPhysicalCameraSettings(const CameraMetadata *metadata,
+        const std::string &cameraId, PhysicalCameraSettings *physicalCameraSettings) {
+    CameraMetadata metadataCopy = *metadata;
+    camera_metadata_t *camera_metadata = metadataCopy.release();
+    HCameraMetadata hCameraMetadata;
+    utils::convertToHidl(camera_metadata, &hCameraMetadata, /*shouldOwn*/ true);
+    physicalCameraSettings->settings.metadata(std::move(hCameraMetadata));
+    physicalCameraSettings->id = cameraId;
+}
+
+void CameraDevice::addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest,
+        sp<CaptureRequest> &req) {
+    req->mPhysicalCameraSettings.resize(1 + aCaptureRequest->physicalSettings.size());
+    addMetadataToPhysicalCameraSettings(&(aCaptureRequest->settings->getInternalData()), getId(),
+                    &(req->mPhysicalCameraSettings[0]));
+    size_t i = 1;
+    for (auto &physicalSetting : aCaptureRequest->physicalSettings) {
+        addMetadataToPhysicalCameraSettings(&(physicalSetting.second->getInternalData()),
+                physicalSetting.first, &(req->mPhysicalCameraSettings[i]));
+        i++;
+    }
+}
+
+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.windowHandles.resize(output->mSharedWindows.size() + 1);
+    outConfig.windowHandles[0] = output->mWindow;
+    outConfig.physicalCameraId = output->mPhysicalCameraId;
+    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::allocateCaptureRequestLocked(
+        const ACaptureRequest* request, /*out*/sp<CaptureRequest> &outReq) {
+    sp<CaptureRequest> req(new CaptureRequest());
+    req->mCaptureRequest.physicalCameraSettings.resize(1 + request->physicalSettings.size());
+
+    size_t index = 0;
+    allocateOneCaptureRequestMetadata(
+            req->mCaptureRequest.physicalCameraSettings[index++], mCameraId, request->settings);
+
+    for (auto& physicalEntry : request->physicalSettings) {
+        allocateOneCaptureRequestMetadata(
+                req->mCaptureRequest.physicalCameraSettings[index++],
+                physicalEntry.first, physicalEntry.second);
+    }
+
+    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;
+}
+
+void CameraDevice::allocateOneCaptureRequestMetadata(
+        PhysicalCameraSettings& cameraSettings,
+        const std::string& id, const sp<ACameraMetadata>& metadata) {
+    cameraSettings.id = id;
+    // TODO: Do we really need to copy the metadata here ?
+    CameraMetadata metadataCopy = metadata->getInternalData();
+    camera_metadata_t *cameraMetadata = metadataCopy.release();
+    HCameraMetadata hCameraMetadata;
+    utils::convertToHidl(cameraMetadata, &hCameraMetadata, true);
+    if (metadata != 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.
+            cameraSettings.settings.fmqMetadataSize(hCameraMetadata.size());
+        } else {
+            ALOGE("Fmq write capture result failed, falling back to hwbinder");
+            cameraSettings.settings.metadata(std::move(hCameraMetadata));
+        }
+    }
+}
+
+
+ACaptureRequest*
+CameraDevice::allocateACaptureRequest(sp<CaptureRequest>& req, const char* deviceId) {
+    ACaptureRequest* pRequest = new ACaptureRequest();
+    for (size_t i = 0; i < req->mPhysicalCameraSettings.size(); i++) {
+        const std::string& id = req->mPhysicalCameraSettings[i].id;
+        CameraMetadata clone;
+        utils::convertFromHidlCloned(req->mPhysicalCameraSettings[i].settings.metadata(), &clone);
+        camera_metadata_t *clonep = clone.release();
+        if (id == deviceId) {
+            pRequest->settings = new ACameraMetadata(clonep, ACameraMetadata::ACM_REQUEST);
+        } else {
+            pRequest->physicalSettings[req->mPhysicalCameraSettings[i].id] =
+                    new ACameraMetadata(clonep, 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::UNKNOWN_ERROR;
+        auto remoteRet = mRemote->cancelRepeatingRequest(
+                [&status, &lastFrameNumber](Status s, auto frameNumber) {
+                    status = s;
+                    lastFrameNumber = frameNumber;
+                });
+        CHECK_TRANSACTION_AND_RET(remoteRet, status, "cancelRepeatingRequest()");
+        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 = Status::UNKNOWN_ERROR;
+    auto remoteRet = mRemote->flush([&status, &lastFrameNumber](auto s, auto frameNumber) {
+                                        status = s;
+                                        lastFrameNumber = frameNumber;
+                                    });
+    CHECK_TRANSACTION_AND_RET(remoteRet, status, "flush()")
+    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();
+    CHECK_TRANSACTION_AND_RET(remoteRet, remoteRet, "waitUntilIdle()")
+    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;
+        outConfigInsert.physicalCameraId = outConfig.mPhysicalCameraId;
+        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();
+    CHECK_TRANSACTION_AND_RET(remoteRet, remoteRet, "beginConfigure()")
+
+    // delete to-be-deleted streams
+    for (auto streamId : deleteList) {
+        remoteRet = mRemote->deleteStream(streamId);
+        CHECK_TRANSACTION_AND_RET(remoteRet, remoteRet, "deleteStream()")
+        mConfiguredOutputs.erase(streamId);
+    }
+
+    // add new streams
+    for (auto outputPair : addSet) {
+        int streamId;
+        Status status = Status::UNKNOWN_ERROR;
+        auto ret = mRemote->createStream(outputPair.second,
+                                         [&status, &streamId](Status s, auto stream_id) {
+                                             status = s;
+                                             streamId = stream_id;
+                                         });
+        CHECK_TRANSACTION_AND_RET(ret, status, "createStream()")
+        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);
+    CHECK_TRANSACTION_AND_RET(remoteRet, remoteRet, "endConfigure()")
+    return ACAMERA_OK;
+}
+
+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.mOnCaptureBufferLost;
+        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.mContext);
+                    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.mOnCaptureFailed;
+        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(cbh.mIsLogicalCameraCallback ? kWhatLogicalCaptureFail :
+                kWhatCaptureFail, mHandler);
+        msg->setPointer(kContextKey, cbh.mContext);
+        msg->setObject(kSessionSpKey, session);
+        if (cbh.mIsLogicalCameraCallback) {
+            if (resultExtras.errorPhysicalCameraId.size() > 0) {
+                msg->setString(kFailingPhysicalCameraId, resultExtras.errorPhysicalCameraId.c_str(),
+                        resultExtras.errorPhysicalCameraId.size());
+            }
+            msg->setPointer(kCallbackFpKey, (void*) cbh.mOnLogicalCameraCaptureFailed);
+        } else {
+            msg->setPointer(kCallbackFpKey, (void*) onError);
+        }
+        msg->setObject(kCaptureRequestKey, request);
+        msg->setObject(kCaptureFailureKey, failure);
+        postSessionMsgAndCleanup(msg);
+
+        // Update tracker
+        mFrameNumberTracker.updateTracker(frameNumber, /*isError*/true);
+        checkAndFireSequenceCompleteLocked();
+    }
+    return;
+}
+
+CameraDevice::CallbackHandler::CallbackHandler(const char *id) : mId(id) { }
+
+void CameraDevice::CallbackHandler::onMessageReceived(
+        const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatOnDisconnected:
+        case kWhatOnError:
+        case kWhatSessionStateCb:
+        case kWhatCaptureStart:
+        case kWhatCaptureResult:
+        case kWhatLogicalCaptureResult:
+        case kWhatCaptureFail:
+        case kWhatLogicalCaptureFail:
+        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 kWhatLogicalCaptureResult:
+        case kWhatCaptureFail:
+        case kWhatLogicalCaptureFail:
+        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;
+            const char *id_cstr = mId.c_str();
+            switch (msg->what()) {
+                case kWhatCaptureStart:
+                case kWhatCaptureResult:
+                case kWhatLogicalCaptureResult:
+                case kWhatCaptureFail:
+                case kWhatLogicalCaptureFail:
+                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, id_cstr);
+                    (*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, id_cstr);
+                    (*onResult)(context, session.get(), request, result.get());
+                    freeACaptureRequest(request);
+                    break;
+                }
+                case kWhatLogicalCaptureResult:
+                {
+                    ACameraCaptureSession_logicalCamera_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()));
+
+                    found = msg->findObject(kPhysicalCaptureResultKey, &obj);
+                    if (!found) {
+                        ALOGE("%s: Cannot find physical capture result!", __FUNCTION__);
+                        return;
+                    }
+                    sp<ACameraPhysicalCaptureResultInfo> physicalResult(
+                            static_cast<ACameraPhysicalCaptureResultInfo*>(obj.get()));
+                    std::vector<PhysicalCaptureResultInfoLocal>& physicalResultInfo =
+                            physicalResult->mPhysicalResultInfo;
+
+                    std::vector<std::string> physicalCameraIds;
+                    std::vector<sp<ACameraMetadata>> physicalMetadataCopy;
+                    for (size_t i = 0; i < physicalResultInfo.size(); i++) {
+                        physicalCameraIds.push_back(physicalResultInfo[i].physicalCameraId);
+
+                        CameraMetadata clone = physicalResultInfo[i].physicalMetadata;
+                        clone.update(ANDROID_SYNC_FRAME_NUMBER,
+                                &physicalResult->mFrameNumber, /*data_count*/1);
+                        sp<ACameraMetadata> metadata =
+                                new ACameraMetadata(clone.release(), ACameraMetadata::ACM_RESULT);
+                        physicalMetadataCopy.push_back(metadata);
+                    }
+                    std::vector<const char*> physicalCameraIdPtrs;
+                    std::vector<const ACameraMetadata*> physicalMetadataCopyPtrs;
+                    for (size_t i = 0; i < physicalResultInfo.size(); i++) {
+                        physicalCameraIdPtrs.push_back(physicalCameraIds[i].c_str());
+                        physicalMetadataCopyPtrs.push_back(physicalMetadataCopy[i].get());
+                    }
+
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp, id_cstr);
+                    (*onResult)(context, session.get(), request, result.get(),
+                            physicalResultInfo.size(), physicalCameraIdPtrs.data(),
+                            physicalMetadataCopyPtrs.data());
+                    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, id_cstr);
+                    (*onFail)(context, session.get(), request, failure);
+                    freeACaptureRequest(request);
+                    break;
+                }
+                case kWhatLogicalCaptureFail:
+                {
+                    ACameraCaptureSession_logicalCamera_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()));
+                    ALogicalCameraCaptureFailure failure;
+                    AString physicalCameraId;
+                    found = msg->findString(kFailingPhysicalCameraId, &physicalCameraId);
+                    if (found && !physicalCameraId.empty()) {
+                        failure.physicalCameraId = physicalCameraId.c_str();
+                    } else {
+                        failure.physicalCameraId = nullptr;
+                    }
+                    failure.captureFailure = *failureSp;
+                    ACaptureRequest* request = allocateACaptureRequest(requestSp, id_cstr);
+                    (*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, id_cstr);
+                    (*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),
+        mIsLogicalCameraCallback(false) {
+    initCaptureCallbacks(cbs);
+
+    if (cbs != nullptr) {
+        mOnCaptureCompleted = cbs->onCaptureCompleted;
+        mOnCaptureFailed = cbs->onCaptureFailed;
+    }
+}
+
+CameraDevice::CallbackHolder::CallbackHolder(
+        sp<ACameraCaptureSession>          session,
+        const Vector<sp<CaptureRequest> >& requests,
+        bool                               isRepeating,
+        ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs) :
+        mSession(session), mRequests(requests),
+        mIsRepeating(isRepeating),
+        mIsLogicalCameraCallback(true) {
+    initCaptureCallbacks(lcbs);
+
+    if (lcbs != nullptr) {
+        mOnLogicalCameraCaptureCompleted = lcbs->onLogicalCameraCaptureCompleted;
+        mOnLogicalCameraCaptureFailed = lcbs->onLogicalCameraCaptureFailed;
+    }
+}
+
+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.mContext);
+        msg->setObject(kSessionSpKey, cbh.mSession);
+        msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceAborted);
+        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.mContext);
+            msg->setObject(kSessionSpKey, cbh.mSession);
+            msg->setPointer(kCallbackFpKey, (void*) cbh.mOnCaptureSequenceCompleted);
+            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;
+        }
+    }
+}
+
+void CameraDevice::stopLooper() {
+    Mutex::Autolock _l(mDeviceLock);
+    if (mCbLooper != nullptr) {
+      mCbLooper->unregisterHandler(mHandler->id());
+      mCbLooper->stop();
+    }
+    mCbLooper.clear();
+    mHandler.clear();
+}
+
+/**
+  * 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"
+            " physical camera ID %s", errorCode, resultExtras.frameNumber, resultExtras.requestId,
+            resultExtras.burstId, resultExtras.errorPhysicalCameraId.c_str());
+    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.mOnCaptureStarted;
+        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.mContext);
+        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) {
+    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;
+    camera_status_t status = readOneResultMetadata(resultMetadata,
+            dev->mCaptureResultMetadataQueue.get(), &metadataCopy);
+    if (status != ACAMERA_OK) {
+        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;
+        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));
+
+        std::vector<PhysicalCaptureResultInfoLocal> localPhysicalResult;
+        localPhysicalResult.resize(physicalResultInfos.size());
+        for (size_t i = 0; i < physicalResultInfos.size(); i++) {
+            localPhysicalResult[i].physicalCameraId = physicalResultInfos[i].physicalCameraId;
+            status = readOneResultMetadata(physicalResultInfos[i].physicalCameraMetadata,
+                    dev->mCaptureResultMetadataQueue.get(),
+                    &localPhysicalResult[i].physicalMetadata);
+            if (status != ACAMERA_OK) {
+                ALOGE("%s: physical camera result metadata couldn't be converted", __FUNCTION__);
+                return ret;
+            }
+        }
+        sp<ACameraPhysicalCaptureResultInfo> physicalResult(
+                new ACameraPhysicalCaptureResultInfo(localPhysicalResult, frameNumber));
+
+        sp<AMessage> msg = new AMessage(
+                cbh.mIsLogicalCameraCallback ? kWhatLogicalCaptureResult : kWhatCaptureResult,
+                dev->mHandler);
+        msg->setPointer(kContextKey, cbh.mContext);
+        msg->setObject(kSessionSpKey, session);
+        msg->setObject(kCaptureRequestKey, request);
+        msg->setObject(kCaptureResultKey, result);
+        if (isPartialResult) {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnCaptureProgressed);
+        } else if (cbh.mIsLogicalCameraCallback) {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnLogicalCameraCaptureCompleted);
+            msg->setObject(kPhysicalCaptureResultKey, physicalResult);
+        } else {
+            msg->setPointer(kCallbackFpKey,
+                    (void *)cbh.mOnCaptureCompleted);
+        }
+        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;
+}
+
+camera_status_t CameraDevice::ServiceCallback::readOneResultMetadata(
+        const FmqSizeOrMetadata& fmqSizeOrMetadata, ResultMetadataQueue* metadataQueue,
+        CameraMetadata* metadata) {
+    if (metadataQueue == nullptr || metadata == nullptr) {
+        return ACAMERA_ERROR_INVALID_PARAMETER;
+    }
+    bool converted;
+    HCameraMetadata hCameraMetadata;
+    if (fmqSizeOrMetadata.getDiscriminator() ==
+            FmqSizeOrMetadata::hidl_discriminator::fmqMetadataSize) {
+        hCameraMetadata.resize(fmqSizeOrMetadata.fmqMetadataSize());
+        bool read = metadataQueue->read(
+                hCameraMetadata.data(), fmqSizeOrMetadata.fmqMetadataSize());
+        if (!read) {
+            ALOGE("%s capture request settings could't be read from fmq", __FUNCTION__);
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+        // TODO: Do we actually need to clone here ?
+        converted = utils::convertFromHidlCloned(hCameraMetadata, metadata);
+    } else {
+        converted = utils::convertFromHidlCloned(fmqSizeOrMetadata.metadata(), metadata);
+    }
+
+    return converted ? ACAMERA_OK : ACAMERA_ERROR_UNKNOWN;
+}
+
+} // 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..3328a85
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
@@ -0,0 +1,434 @@
+/*
+ * 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 <vector>
+#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 { };
+
+// Wrap PhysicalCaptureResultInfo so that it can be ref-counted
+struct PhysicalCaptureResultInfoLocal {
+    std::string physicalCameraId;
+    CameraMetadata physicalMetadata;
+};
+
+struct ACameraPhysicalCaptureResultInfo: public RefBase {
+    ACameraPhysicalCaptureResultInfo(const std::vector<PhysicalCaptureResultInfoLocal>& info,
+            int64_t frameNumber) :
+        mPhysicalResultInfo(info), mFrameNumber(frameNumber) {}
+
+    std::vector<PhysicalCaptureResultInfoLocal> mPhysicalResultInfo;
+    int64_t mFrameNumber;
+};
+
+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,
+            const ACameraIdList* physicalCameraIdList,
+            ACaptureRequest** request) const;
+
+    camera_status_t createCaptureSession(
+            const ACaptureSessionOutputContainer*       outputs,
+            const ACaptureRequest* sessionParameters,
+            const ACameraCaptureSession_stateCallbacks* callbacks,
+            /*out*/ACameraCaptureSession** session);
+
+    camera_status_t isSessionConfigurationSupported(
+            const ACaptureSessionOutputContainer* sessionOutputContainer) const;
+
+    // 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:
+        camera_status_t readOneResultMetadata(const FmqSizeOrMetadata& fmqSizeOrMetadata,
+                ResultMetadataQueue* metadataQueue, CameraMetadata* metadata);
+        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; };
+
+    // Stop the looper thread and unregister the handler
+    void stopLooper();
+
+  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();
+
+    template<class T>
+    camera_status_t captureLocked(sp<ACameraCaptureSession> session,
+            /*optional*/T* cbs,
+            int numRequests, ACaptureRequest** requests,
+            /*optional*/int* captureSequenceId);
+
+    template<class T>
+    camera_status_t setRepeatingRequestsLocked(sp<ACameraCaptureSession> session,
+            /*optional*/T* cbs,
+            int numRequests, ACaptureRequest** requests,
+            /*optional*/int* captureSequenceId);
+
+    template<class T>
+    camera_status_t submitRequestsLocked(
+            sp<ACameraCaptureSession> session,
+            /*optional*/T* cbs,
+            int numRequests, ACaptureRequest** requests,
+            /*out*/int* captureSequenceId,
+            bool isRepeating);
+
+    void addRequestSettingsMetadata(ACaptureRequest *aCaptureRequest, sp<CaptureRequest> &req);
+
+    camera_status_t updateOutputConfigurationLocked(ACaptureSessionOutput *output);
+
+    // Since this writes to ICameraDeviceUser's fmq, clients must take care that:
+    //   a) This function is called serially.
+    //   b) This function is called in accordance with ICameraDeviceUser.submitRequestList,
+    //      otherwise, the wrong capture request might have the wrong settings
+    //      metadata associated with it.
+    camera_status_t allocateCaptureRequestLocked(
+            const ACaptureRequest* request, sp<CaptureRequest>& outReq);
+    void allocateOneCaptureRequestMetadata(
+            PhysicalCameraSettings& cameraSettings,
+            const std::string& id, const sp<ACameraMetadata>& metadata);
+
+    static ACaptureRequest* allocateACaptureRequest(sp<CaptureRequest>& req, const char* deviceId);
+    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
+        kWhatLogicalCaptureResult, // onLogicalCameraCaptureCompleted
+        kWhatCaptureFail,      // onCaptureFailed
+        kWhatLogicalCaptureFail, // onLogicalCameraCaptureFailed
+        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* kPhysicalCaptureResultKey;
+    static const char* kCaptureFailureKey;
+    static const char* kSequenceIdKey;
+    static const char* kFrameNumberKey;
+    static const char* kAnwKey;
+    static const char* kFailingPhysicalCameraId;
+
+    class CallbackHandler : public AHandler {
+      public:
+        explicit CallbackHandler(const char *id);
+        void onMessageReceived(const sp<AMessage> &msg) override;
+
+      private:
+        std::string mId;
+        // 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);
+        CallbackHolder(sp<ACameraCaptureSession>          session,
+                       const Vector<sp<CaptureRequest>>&  requests,
+                       bool                               isRepeating,
+                       ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs);
+
+        template <class T>
+        void initCaptureCallbacks(T* cbs) {
+            mContext = nullptr;
+            mOnCaptureStarted = nullptr;
+            mOnCaptureProgressed = nullptr;
+            mOnCaptureCompleted = nullptr;
+            mOnLogicalCameraCaptureCompleted = nullptr;
+            mOnLogicalCameraCaptureFailed = nullptr;
+            mOnCaptureFailed = nullptr;
+            mOnCaptureSequenceCompleted = nullptr;
+            mOnCaptureSequenceAborted = nullptr;
+            mOnCaptureBufferLost = nullptr;
+            if (cbs != nullptr) {
+                mContext = cbs->context;
+                mOnCaptureStarted = cbs->onCaptureStarted;
+                mOnCaptureProgressed = cbs->onCaptureProgressed;
+                mOnCaptureSequenceCompleted = cbs->onCaptureSequenceCompleted;
+                mOnCaptureSequenceAborted = cbs->onCaptureSequenceAborted;
+                mOnCaptureBufferLost = cbs->onCaptureBufferLost;
+            }
+        }
+
+        sp<ACameraCaptureSession>   mSession;
+        Vector<sp<CaptureRequest>>  mRequests;
+        const bool                  mIsRepeating;
+        const bool                  mIsLogicalCameraCallback;
+
+        void*                       mContext;
+        ACameraCaptureSession_captureCallback_start mOnCaptureStarted;
+        ACameraCaptureSession_captureCallback_result mOnCaptureProgressed;
+        ACameraCaptureSession_captureCallback_result mOnCaptureCompleted;
+        ACameraCaptureSession_logicalCamera_captureCallback_result mOnLogicalCameraCaptureCompleted;
+        ACameraCaptureSession_logicalCamera_captureCallback_failed mOnLogicalCameraCaptureFailed;
+        ACameraCaptureSession_captureCallback_failed mOnCaptureFailed;
+        ACameraCaptureSession_captureCallback_sequenceEnd mOnCaptureSequenceCompleted;
+        ACameraCaptureSession_captureCallback_sequenceAbort mOnCaptureSequenceAborted;
+        ACameraCaptureSession_captureCallback_bufferLost mOnCaptureBufferLost;
+    };
+    // 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,
+            const ACameraIdList* physicalCameraIdList,
+            ACaptureRequest** request) const {
+        return mDevice->createCaptureRequest(templateId, physicalCameraIdList, 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);
+    }
+
+    camera_status_t isSessionConfigurationSupported(
+            const ACaptureSessionOutputContainer* sessionOutputContainer) const {
+        return mDevice->isSessionConfigurationSupported(sessionOutputContainer);
+    }
+
+    /***********************
+     * 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/ACameraDeviceVendor.inc b/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc
new file mode 100644
index 0000000..8bd5a52
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraDeviceVendor.inc
@@ -0,0 +1,152 @@
+/*
+ * 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 <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 {
+
+template<class T>
+camera_status_t
+CameraDevice::captureLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    return submitRequestsLocked(
+            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/false);
+}
+
+template<class T>
+camera_status_t
+CameraDevice::setRepeatingRequestsLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*optional*/int* captureSequenceId) {
+    return submitRequestsLocked(
+            session, cbs, numRequests, requests, captureSequenceId, /*isRepeating*/true);
+}
+
+template<class T>
+camera_status_t CameraDevice::submitRequestsLocked(
+        sp<ACameraCaptureSession> session,
+        /*optional*/T* cbs,
+        int numRequests, ACaptureRequest** requests,
+        /*out*/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 = allocateCaptureRequestLocked(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 retrieved 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;
+}
+
+} // namespace acam
+} // namespace android
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
new file mode 100644
index 0000000..70c887a
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
@@ -0,0 +1,614 @@
+/*
+ * 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 <CameraMetadata.h>
+#include <camera_metadata_hidden.h>
+
+#include <utils/Vector.h>
+#include <cutils/properties.h>
+#include <stdlib.h>
+
+#include <VendorTagDescriptor.h>
+
+using namespace android::acam;
+
+namespace android {
+namespace acam {
+
+using frameworks::cameraservice::service::V2_0::CameraStatusAndId;
+using frameworks::cameraservice::common::V2_0::ProviderIdAndVendorTagSections;
+using android::hardware::camera::common::V1_0::helper::VendorTagDescriptor;
+using android::hardware::camera::common::V1_0::helper::VendorTagDescriptorCache;
+
+// 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;
+
+/**
+ * The vendor tag descriptor class that takes HIDL vendor tag information as
+ * input. Not part of vendor available VendorTagDescriptor class because that class is used by
+ * default HAL implementation code as well.
+ */
+class HidlVendorTagDescriptor : public VendorTagDescriptor {
+public:
+    /**
+     * Create a VendorTagDescriptor object from the HIDL VendorTagSection
+     * vector.
+     *
+     * Returns OK on success, or a negative error code.
+     */
+    static status_t createDescriptorFromHidl(const hidl_vec<VendorTagSection>& vts,
+                                             /*out*/ sp<VendorTagDescriptor> *descriptor);
+};
+
+status_t HidlVendorTagDescriptor::createDescriptorFromHidl(const hidl_vec<VendorTagSection> &vts,
+                                                           sp<VendorTagDescriptor> *descriptor) {
+    int tagCount = 0;
+
+    for (size_t s = 0; s < vts.size(); s++) {
+        tagCount += vts[s].tags.size();
+    }
+
+    if (tagCount < 0 || tagCount > INT32_MAX) {
+        ALOGE("%s: tag count %d from vendor tag sections is invalid.", __FUNCTION__, tagCount);
+        return BAD_VALUE;
+    }
+
+    Vector<uint32_t> tagArray;
+    LOG_ALWAYS_FATAL_IF(tagArray.resize(tagCount) != tagCount,
+            "%s: too many (%u) vendor tags defined.", __FUNCTION__, tagCount);
+
+    sp<HidlVendorTagDescriptor> desc = new HidlVendorTagDescriptor();
+    desc->mTagCount = tagCount;
+
+    KeyedVector<uint32_t, String8> tagToSectionMap;
+
+    int idx = 0;
+    for (size_t s = 0; s < vts.size(); s++) {
+        const VendorTagSection& section = vts[s];
+        const char *sectionName = section.sectionName.c_str();
+        if (sectionName == NULL) {
+            ALOGE("%s: no section name defined for vendor tag section %zu.", __FUNCTION__, s);
+            return BAD_VALUE;
+        }
+        String8 sectionString(sectionName);
+        desc->mSections.add(sectionString);
+
+        for (size_t j = 0; j < section.tags.size(); j++) {
+            uint32_t tag = section.tags[j].tagId;
+            if (tag < CAMERA_METADATA_VENDOR_TAG_BOUNDARY) {
+                ALOGE("%s: vendor tag %d not in vendor tag section.", __FUNCTION__, tag);
+                return BAD_VALUE;
+            }
+
+            tagArray.editItemAt(idx++) = section.tags[j].tagId;
+
+            const char *tagName = section.tags[j].tagName.c_str();
+            if (tagName == NULL) {
+                ALOGE("%s: no tag name defined for vendor tag %d.", __FUNCTION__, tag);
+                return BAD_VALUE;
+            }
+            desc->mTagToNameMap.add(tag, String8(tagName));
+            tagToSectionMap.add(tag, sectionString);
+
+            int tagType = (int) section.tags[j].tagType;
+            if (tagType < 0 || tagType >= NUM_TYPES) {
+                ALOGE("%s: tag type %d from vendor ops does not exist.", __FUNCTION__, tagType);
+                return BAD_VALUE;
+            }
+            desc->mTagToTypeMap.emplace(tag, tagType);
+        }
+    }
+
+    for (size_t i = 0; i < tagArray.size(); ++i) {
+        uint32_t tag = tagArray[i];
+        String8 sectionString = tagToSectionMap.valueFor(tag);
+
+        // Set up tag to section index map
+        ssize_t index = desc->mSections.indexOf(sectionString);
+        LOG_ALWAYS_FATAL_IF(index < 0, "index %zd must be non-negative", index);
+        desc->mTagToSectionMap.add(tag, static_cast<uint32_t>(index));
+
+        // Set up reverse mapping
+        ssize_t reverseIndex = -1;
+        if ((reverseIndex = desc->mReverseMapping.indexOfKey(sectionString)) < 0) {
+            KeyedVector<String8, uint32_t>* nameMapper = new KeyedVector<String8, uint32_t>();
+            reverseIndex = desc->mReverseMapping.add(sectionString, nameMapper);
+        }
+        desc->mReverseMapping[reverseIndex]->add(desc->mTagToNameMap.valueFor(tag), tag);
+    }
+
+    *descriptor = std::move(desc);
+    return OK;
+}
+
+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);
+}
+
+bool CameraManagerGlobal::setupVendorTags() {
+    sp<VendorTagDescriptorCache> tagCache = new VendorTagDescriptorCache();
+    Status status = Status::NO_ERROR;
+    std::vector<ProviderIdAndVendorTagSections> providerIdsAndVts;
+    auto remoteRet = mCameraService->getCameraVendorTagSections([&status, &providerIdsAndVts]
+                                                                 (Status s,
+                                                                  auto &IdsAndVts) {
+                                                         status = s;
+                                                         providerIdsAndVts = IdsAndVts; });
+
+    if (!remoteRet.isOk() || status != Status::NO_ERROR) {
+        ALOGE("Failed to retrieve VendorTagSections %s", remoteRet.description().c_str());
+        return false;
+    }
+    // Convert each providers VendorTagSections into a VendorTagDescriptor and
+    // add it to the cache
+    for (auto &providerIdAndVts : providerIdsAndVts) {
+        sp<VendorTagDescriptor> vendorTagDescriptor;
+        if (HidlVendorTagDescriptor::createDescriptorFromHidl(providerIdAndVts.vendorTagSections,
+                                                              &vendorTagDescriptor) != OK) {
+            ALOGE("Failed to convert from Hidl: VendorTagDescriptor");
+            return false;
+        }
+        tagCache->addVendorDescriptor(providerIdAndVts.providerId, vendorTagDescriptor);
+    }
+    VendorTagDescriptorCache::setAsGlobalVendorTagCache(tagCache);
+    return true;
+}
+
+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());
+        }
+
+        // Setup vendor tags
+        if (!setupVendorTags()) {
+            ALOGE("Unable to set up vendor tags");
+            return nullptr;
+        }
+
+        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;
+}
+
+camera_status_t
+ACameraManager::getTagFromName(const char *cameraId, const char *name, uint32_t *tag) {
+    sp<ACameraMetadata> rawChars;
+    camera_status_t ret = getCameraCharacteristics(cameraId, &rawChars);
+    if (ret != ACAMERA_OK) {
+        ALOGE("%s, Cannot retrieve camera characteristics for camera id %s", __FUNCTION__,
+                cameraId);
+        return ACAMERA_ERROR_METADATA_NOT_FOUND;
+    }
+    const CameraMetadata& metadata = rawChars->getInternalData();
+    const camera_metadata_t *rawMetadata = metadata.getAndLock();
+    metadata_vendor_id_t vendorTagId = get_camera_metadata_vendor_id(rawMetadata);
+    metadata.unlock(rawMetadata);
+    sp<VendorTagDescriptorCache> vtCache = VendorTagDescriptorCache::getGlobalVendorTagCache();
+    sp<VendorTagDescriptor> vTags = nullptr;
+    vtCache->getVendorTagDescriptor(vendorTagId, &vTags);
+    status_t status= metadata.getTagFromName(name, vTags.get(), tag);
+    return status == OK ? ACAMERA_OK : ACAMERA_ERROR_METADATA_NOT_FOUND;
+}
+
+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..2c62d44
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.h
@@ -0,0 +1,217 @@
+/*
+ * 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);
+
+    void registerExtendedAvailabilityCallback(
+            const ACameraManager_ExtendedAvailabilityCallbacks* /*callback*/) {}
+    void unregisterExtendedAvailabilityCallback(
+            const ACameraManager_ExtendedAvailabilityCallbacks* /*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);
+    bool setupVendorTags();
+
+    // 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);
+    camera_status_t getTagFromName(const char *cameraId, const char *name, uint32_t *tag);
+
+  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..e4fb204
--- /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. dst owns memory if shouldOwn is set
+//       to true.
+void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst, bool shouldOwn) {
+    if (src == nullptr) {
+        return;
+    }
+    size_t size = get_camera_metadata_size(src);
+    dst->setToExternal((uint8_t *) src, size, shouldOwn);
+    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..f389f03
--- /dev/null
+++ b/camera/ndk/ndk_vendor/impl/utils.h
@@ -0,0 +1,182 @@
+/*
+ * 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() {
+        mOutputConfiguration.rotation = OutputConfiguration::Rotation::R0;
+        // The ndk currently doesn't support deferred surfaces
+        mOutputConfiguration.isDeferred = false;
+        mOutputConfiguration.width = 0;
+        mOutputConfiguration.height = 0;
+        // ndk doesn't support inter OutputConfiguration buffer sharing.
+        mOutputConfiguration.windowGroupId = -1;
+    };
+
+    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.
+void convertToHidl(const camera_metadata_t *src, HCameraMetadata* dst, bool shouldOwn = false);
+
+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..37de30a
--- /dev/null
+++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
@@ -0,0 +1,826 @@
+/*
+ * 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>
+#include <VendorTagDescriptor.h>
+
+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;
+
+using android::hardware::camera::common::V1_0::helper::VendorTagDescriptorCache;
+
+class CameraHelper {
+   public:
+    CameraHelper(const char* id, ACameraManager *manager) :
+            mImgReaderAnw(nullptr), mCameraId(id), mCameraManager(manager) {}
+    ~CameraHelper() { closeCamera(); }
+
+    struct PhysicalImgReaderInfo {
+        const char* physicalCameraId;
+        native_handle_t* anw;
+    };
+    int initCamera(native_handle_t* imgReaderAnw,
+            const std::vector<PhysicalImgReaderInfo>& physicalImgReaders,
+            bool usePhysicalSettings) {
+        if (imgReaderAnw == nullptr) {
+            ALOGE("Cannot initialize camera before image reader get initialized.");
+            return -1;
+        }
+        if (mIsCameraReady) {
+            ALOGE("initCamera should only be called once.");
+            return -1;
+        }
+
+        int ret;
+        mImgReaderAnw = imgReaderAnw;
+
+        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;
+        }
+
+        // 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;
+        }
+
+        std::vector<const char*> idPointerList;
+        for (auto& physicalStream : physicalImgReaders) {
+            ACaptureSessionOutput* sessionOutput = nullptr;
+            ret = ACaptureSessionPhysicalOutput_create(physicalStream.anw,
+                    physicalStream.physicalCameraId, &sessionOutput);
+            if (ret != ACAMERA_OK) {
+                ALOGE("ACaptureSessionPhysicalOutput_create failed, ret=%d", ret);
+                return ret;
+            }
+            ret = ACaptureSessionOutputContainer_add(mOutputs, sessionOutput);
+            if (ret != AMEDIA_OK) {
+                ALOGE("ACaptureSessionOutputContainer_add failed, ret=%d", ret);
+                return ret;
+            }
+            mExtraOutputs.push_back(sessionOutput);
+            // Assume that at most one physical stream per physical camera.
+            mPhysicalCameraIds.push_back(physicalStream.physicalCameraId);
+            idPointerList.push_back(physicalStream.physicalCameraId);
+        }
+        ACameraIdList cameraIdList;
+        cameraIdList.numCameras = idPointerList.size();
+        cameraIdList.cameraIds = idPointerList.data();
+
+        ret = ACameraDevice_isSessionConfigurationSupported(mDevice, mOutputs);
+        if (ret != ACAMERA_OK && ret != ACAMERA_ERROR_UNSUPPORTED_OPERATION) {
+            ALOGE("ACameraDevice_isSessionConfigurationSupported 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
+        if (usePhysicalSettings) {
+            ret = ACameraDevice_createCaptureRequest_withPhysicalIds(mDevice,
+                    TEMPLATE_STILL_CAPTURE, &cameraIdList, &mStillRequest);
+        } else {
+            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;
+        }
+
+        for (auto& physicalStream : physicalImgReaders) {
+            ACameraOutputTarget* outputTarget = nullptr;
+            ret = ACameraOutputTarget_create(physicalStream.anw, &outputTarget);
+            if (ret != AMEDIA_OK) {
+                ALOGE("ACameraOutputTarget_create failed, ret=%d", ret);
+                return ret;
+            }
+            ret = ACaptureRequest_addTarget(mStillRequest, outputTarget);
+            if (ret != AMEDIA_OK) {
+                ALOGE("ACaptureRequest_addTarget failed, ret=%d", ret);
+                return ret;
+            }
+            mReqExtraOutputs.push_back(outputTarget);
+        }
+
+        mIsCameraReady = true;
+        return 0;
+    }
+
+
+    bool isCameraReady() { return mIsCameraReady; }
+
+    void closeCamera() {
+        // Destroy capture request
+        if (mReqImgReaderOutput) {
+            ACameraOutputTarget_free(mReqImgReaderOutput);
+            mReqImgReaderOutput = nullptr;
+        }
+        for (auto& outputTarget : mReqExtraOutputs) {
+            ACameraOutputTarget_free(outputTarget);
+        }
+        mReqExtraOutputs.clear();
+        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;
+        }
+        for (auto& extraOutput : mExtraOutputs) {
+            ACaptureSessionOutput_free(extraOutput);
+        }
+        mExtraOutputs.clear();
+        if (mOutputs) {
+            ACaptureSessionOutputContainer_free(mOutputs);
+            mOutputs = nullptr;
+        }
+        // Destroy camera device
+        if (mDevice) {
+            ACameraDevice_close(mDevice);
+            mDevice = nullptr;
+        }
+        mIsCameraReady = false;
+    }
+
+    int takePicture() {
+        int seqId;
+        return ACameraCaptureSession_capture(mSession, &mCaptureCallbacks, 1, &mStillRequest,
+                                             &seqId);
+    }
+
+    int takeLogicalCameraPicture() {
+        int seqId;
+        return ACameraCaptureSession_logicalCamera_capture(mSession, &mLogicalCaptureCallbacks,
+                1, &mStillRequest, &seqId);
+    }
+
+    bool checkCallbacks(int pictureCount) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        if (mCompletedCaptureCallbackCount != pictureCount) {
+            ALOGE("Completed capture callaback count not as expected. expected %d actual %d",
+                  pictureCount, mCompletedCaptureCallbackCount);
+            return false;
+        }
+        return true;
+    }
+
+    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 device
+    ACameraDevice* mDevice = nullptr;
+    // Capture session
+    ACaptureSessionOutputContainer* mOutputs = nullptr;
+    ACaptureSessionOutput* mImgReaderOutput = nullptr;
+    std::vector<ACaptureSessionOutput*> mExtraOutputs;
+
+    ACameraCaptureSession* mSession = nullptr;
+    // Capture request
+    ACaptureRequest* mStillRequest = nullptr;
+    ACameraOutputTarget* mReqImgReaderOutput = nullptr;
+    std::vector<ACameraOutputTarget*> mReqExtraOutputs;
+
+    bool mIsCameraReady = false;
+    const char* mCameraId;
+    ACameraManager* mCameraManager;
+    int mCompletedCaptureCallbackCount = 0;
+    std::mutex mMutex;
+    ACameraCaptureSession_captureCallbacks mCaptureCallbacks = {
+        // TODO: Add tests for other callbacks
+        this, // context
+        nullptr, // onCaptureStarted
+        nullptr, // onCaptureProgressed
+        [](void* ctx , ACameraCaptureSession *, ACaptureRequest *,
+                                          const ACameraMetadata *) {
+            CameraHelper *ch = static_cast<CameraHelper *>(ctx);
+            std::lock_guard<std::mutex> lock(ch->mMutex);
+            ch->mCompletedCaptureCallbackCount++;
+        },
+        nullptr, // onCaptureFailed
+        nullptr, // onCaptureSequenceCompleted
+        nullptr, // onCaptureSequenceAborted
+        nullptr, // onCaptureBufferLost
+    };
+
+    std::vector<std::string> mPhysicalCameraIds;
+    ACameraCaptureSession_logicalCamera_captureCallbacks mLogicalCaptureCallbacks = {
+        // TODO: Add tests for other callbacks
+        this, // context
+        nullptr, // onCaptureStarted
+        nullptr, // onCaptureProgressed
+        [](void* ctx , ACameraCaptureSession *, ACaptureRequest *,
+                const ACameraMetadata *, size_t physicalResultCount,
+                const char** physicalCameraIds, const ACameraMetadata** physicalResults) {
+            CameraHelper *ch = static_cast<CameraHelper *>(ctx);
+            std::lock_guard<std::mutex> lock(ch->mMutex);
+            ASSERT_EQ(physicalResultCount, ch->mPhysicalCameraIds.size());
+            for (size_t i = 0; i < physicalResultCount; i++) {
+                ASSERT_TRUE(physicalCameraIds[i] != nullptr);
+                ASSERT_TRUE(physicalResults[i] != nullptr);
+                ASSERT_NE(std::find(ch->mPhysicalCameraIds.begin(),
+                        ch->mPhysicalCameraIds.end(), physicalCameraIds[i]),
+                        ch->mPhysicalCameraIds.end());
+
+                // Verify frameNumber and sensorTimestamp exist in physical
+                // result metadata
+                ACameraMetadata_const_entry entry;
+                ACameraMetadata_getConstEntry(
+                        physicalResults[i], ACAMERA_SYNC_FRAME_NUMBER, &entry);
+                ASSERT_EQ(entry.count, 1);
+                ACameraMetadata_getConstEntry(
+                        physicalResults[i], ACAMERA_SENSOR_TIMESTAMP, &entry);
+                ASSERT_EQ(entry.count, 1);
+            }
+            ch->mCompletedCaptureCallbackCount++;
+        },
+        [] (void * /*ctx*/, ACameraCaptureSession* /*session*/, ACaptureRequest* /*request*/,
+                ALogicalCameraCaptureFailure* failure) {
+            if (failure->physicalCameraId) {
+                ALOGD("%s: Physical camera id: %s result failure", __FUNCTION__,
+                        failure->physicalCameraId);
+            }
+        },
+        nullptr, // onCaptureSequenceCompleted
+        nullptr, // onCaptureSequenceAborted
+        nullptr, // onCaptureBufferLost
+    };
+};
+
+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};
+};
+
+
+class AImageReaderVendorTest : public ::testing::Test {
+  public:
+    void SetUp() override {
+        mCameraManager = ACameraManager_create();
+        if (mCameraManager == nullptr) {
+            ALOGE("Failed to create ACameraManager.");
+            return;
+        }
+
+        camera_status_t ret = ACameraManager_getCameraIdList(mCameraManager, &mCameraIdList);
+        if (ret != ACAMERA_OK) {
+            ALOGE("Failed to get cameraIdList: ret=%d", ret);
+            return;
+        }
+        // TODO: Add more rigorous tests for vendor tags
+        ASSERT_NE(VendorTagDescriptorCache::getGlobalVendorTagCache(), nullptr);
+        if (mCameraIdList->numCameras < 1) {
+            ALOGW("Device has no camera on board.");
+            return;
+        }
+    }
+    void TearDown() override {
+        // Destroy camera manager
+        if (mCameraIdList) {
+            ACameraManager_deleteCameraIdList(mCameraIdList);
+            mCameraIdList = nullptr;
+        }
+        if (mCameraManager) {
+            ACameraManager_delete(mCameraManager);
+            mCameraManager = nullptr;
+        }
+    }
+
+    bool takePictures(const char* id, 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) {
+            ALOGE("Unable to initialize ImageReader");
+            return false;
+        }
+
+        CameraHelper cameraHelper(id, mCameraManager);
+        ret = cameraHelper.initCamera(testCase.getNativeWindow(),
+                {}/*physicalImageReaders*/, false/*usePhysicalSettings*/);
+        if (ret < 0) {
+            ALOGE("Unable to initialize camera helper");
+            return false;
+        }
+
+        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 true;
+        }
+
+        for (int i = 0; i < pictureCount; i++) {
+            ret = cameraHelper.takePicture();
+            if (ret < 0) {
+                ALOGE("Unable to take picture");
+                return false;
+            }
+        }
+
+        // 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 &&
+                cameraHelper.checkCallbacks(pictureCount);
+    }
+
+    bool testTakePicturesNative(const char* id) {
+        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(id, 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;
+    }
+
+    // Camera manager
+    ACameraManager* mCameraManager = nullptr;
+    ACameraIdList* mCameraIdList = nullptr;
+
+    bool isCapabilitySupported(ACameraMetadata* staticInfo,
+            acamera_metadata_enum_android_request_available_capabilities_t cap) {
+        ACameraMetadata_const_entry entry;
+        ACameraMetadata_getConstEntry(
+                staticInfo, 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 isSizeSupportedForFormat(ACameraMetadata* staticInfo,
+            int32_t format, int32_t width, int32_t height) {
+        ACameraMetadata_const_entry entry;
+        ACameraMetadata_getConstEntry(staticInfo,
+                ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry);
+        for (uint32_t i = 0; i < entry.count; i += 4) {
+            if (entry.data.i32[i] == format &&
+                    entry.data.i32[i+3] == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+                    entry.data.i32[i+1] == width &&
+                    entry.data.i32[i+2] == height) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void findCandidateLogicalCamera(const char **cameraId,
+            ACameraMetadata** staticMetadata,
+            std::vector<const char*>* candidatePhysicalIds) {
+        // Find first available logical camera
+        for (int i = 0; i < mCameraIdList->numCameras; i++) {
+            camera_status_t ret;
+            ret = ACameraManager_getCameraCharacteristics(
+                    mCameraManager, mCameraIdList->cameraIds[i], staticMetadata);
+            ASSERT_EQ(ret, ACAMERA_OK);
+            ASSERT_NE(*staticMetadata, nullptr);
+
+            if (!isCapabilitySupported(*staticMetadata,
+                    ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
+                ACameraMetadata_free(*staticMetadata);
+                *staticMetadata = nullptr;
+                continue;
+            }
+
+            // Check returned physical camera Ids are valid
+            size_t physicalCameraIdCnt = 0;
+            const char*const* physicalCameraIds = nullptr;
+            bool isLogicalCamera = ACameraMetadata_isLogicalMultiCamera(*staticMetadata,
+                    &physicalCameraIdCnt, &physicalCameraIds);
+            ASSERT_TRUE(isLogicalCamera);
+            ASSERT_GE(physicalCameraIdCnt, 2);
+            ACameraMetadata* physicalCameraMetadata = nullptr;
+            candidatePhysicalIds->clear();
+            for (size_t j = 0; j < physicalCameraIdCnt && candidatePhysicalIds->size() < 2; j++) {
+                ASSERT_GT(strlen(physicalCameraIds[j]), 0);
+                ret = ACameraManager_getCameraCharacteristics(
+                        mCameraManager, physicalCameraIds[j], &physicalCameraMetadata);
+                ASSERT_EQ(ret, ACAMERA_OK);
+                ASSERT_NE(physicalCameraMetadata, nullptr);
+
+                if (isSizeSupportedForFormat(physicalCameraMetadata, kTestImageFormat,
+                        kTestImageWidth, kTestImageHeight)) {
+                    candidatePhysicalIds->push_back(physicalCameraIds[j]);
+                }
+                ACameraMetadata_free(physicalCameraMetadata);
+            }
+            if (candidatePhysicalIds->size() == 2) {
+                *cameraId = mCameraIdList->cameraIds[i];
+                return;
+            } else {
+                ACameraMetadata_free(*staticMetadata);
+                *staticMetadata = nullptr;
+            }
+        }
+        *cameraId = nullptr;
+        return;
+    }
+
+    void testLogicalCameraPhysicalStream(bool usePhysicalSettings) {
+        const char* cameraId = nullptr;
+        ACameraMetadata* staticMetadata = nullptr;
+        std::vector<const char*> physicalCameraIds;
+
+        findCandidateLogicalCamera(&cameraId, &staticMetadata, &physicalCameraIds);
+        if (cameraId == nullptr) {
+            // Couldn't find logical camera to test
+            return;
+        }
+
+        // Test streaming the logical multi-camera
+        uint64_t readerUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
+        int32_t readerMaxImages = 8;
+        bool readerAsync = false;
+        const int pictureCount = 6;
+        std::vector<ImageReaderTestCase*> testCases;
+        for (size_t i = 0; i < 3; i++) {
+            ImageReaderTestCase* testCase = new ImageReaderTestCase(
+                    kTestImageWidth, kTestImageHeight, kTestImageFormat, readerUsage,
+                    readerMaxImages, readerAsync);
+            ASSERT_EQ(testCase->initImageReader(), 0);
+            testCases.push_back(testCase);
+        }
+
+        CameraHelper cameraHelper(cameraId, mCameraManager);
+        std::vector<CameraHelper::PhysicalImgReaderInfo> physicalImgReaderInfo;
+        physicalImgReaderInfo.push_back({physicalCameraIds[0], testCases[1]->getNativeWindow()});
+        physicalImgReaderInfo.push_back({physicalCameraIds[1], testCases[2]->getNativeWindow()});
+
+        int ret = cameraHelper.initCamera(testCases[0]->getNativeWindow(),
+                physicalImgReaderInfo, usePhysicalSettings);
+        ASSERT_EQ(ret, 0);
+
+        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;
+        }
+
+        for (int i = 0; i < pictureCount; i++) {
+            ret = cameraHelper.takeLogicalCameraPicture();
+            ASSERT_EQ(ret, 0);
+        }
+
+        // Sleep until all capture finished
+        for (int i = 0; i < kCaptureWaitRetry * pictureCount; i++) {
+            usleep(kCaptureWaitUs);
+            if (testCases[0]->getAcquiredImageCount() == pictureCount) {
+                ALOGI("Session take ~%d ms to capture %d images", i * kCaptureWaitUs / 1000,
+                      pictureCount);
+                break;
+            }
+        }
+        ASSERT_EQ(testCases[0]->getAcquiredImageCount(), pictureCount);
+        ASSERT_EQ(testCases[1]->getAcquiredImageCount(), pictureCount);
+        ASSERT_EQ(testCases[2]->getAcquiredImageCount(), pictureCount);
+        ASSERT_TRUE(cameraHelper.checkCallbacks(pictureCount));
+
+        ACameraMetadata_free(staticMetadata);
+    }
+};
+
+TEST_F(AImageReaderVendorTest, CreateWindowNativeHandle) {
+    // We always use the first camera.
+    const char* cameraId = mCameraIdList->cameraIds[0];
+    ASSERT_TRUE(cameraId != nullptr);
+
+    ACameraMetadata* staticMetadata = nullptr;
+    camera_status_t ret = ACameraManager_getCameraCharacteristics(
+            mCameraManager, cameraId, &staticMetadata);
+    ASSERT_EQ(ret, ACAMERA_OK);
+    ASSERT_NE(staticMetadata, nullptr);
+
+    bool isBC = isCapabilitySupported(staticMetadata,
+            ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
+
+    uint32_t namedTag = 0;
+    // Test that ACameraMetadata_getTagFromName works as expected for public tag
+    // names
+    camera_status_t status = ACameraManager_getTagFromName(mCameraManager, cameraId,
+            "android.control.aeMode", &namedTag);
+
+    ASSERT_EQ(status, ACAMERA_OK);
+    ASSERT_EQ(namedTag, ACAMERA_CONTROL_AE_MODE);
+
+    ACameraMetadata_free(staticMetadata);
+
+    if (!isBC) {
+        ALOGW("Camera does not support BACKWARD_COMPATIBLE.");
+        return;
+    }
+
+    EXPECT_TRUE(testTakePicturesNative(cameraId));
+}
+
+TEST_F(AImageReaderVendorTest, LogicalCameraPhysicalStream) {
+    testLogicalCameraPhysicalStream(false/*usePhysicalSettings*/);
+    testLogicalCameraPhysicalStream(true/*usePhysicalSettings*/);
+}
+
+}  // namespace
diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk
index 659484f..e5c1631 100644
--- a/camera/tests/Android.mk
+++ b/camera/tests/Android.mk
@@ -19,7 +19,8 @@
 LOCAL_SRC_FILES:= \
 	VendorTagDescriptorTests.cpp \
 	CameraBinderTests.cpp \
-	CameraZSLTests.cpp
+	CameraZSLTests.cpp \
+	CameraCharacteristicsPermission.cpp
 
 LOCAL_SHARED_LIBRARIES := \
 	liblog \
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 1de7013..8fe029a 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -29,6 +29,7 @@
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
 #include <system/graphics.h>
+#include <hardware/camera3.h>
 #include <hardware/gralloc.h>
 
 #include <camera/CameraMetadata.h>
@@ -40,6 +41,7 @@
 #include <android/hardware/camera2/BnCameraDeviceCallbacks.h>
 #include <camera/camera2/CaptureRequest.h>
 #include <camera/camera2/OutputConfiguration.h>
+#include <camera/camera2/SessionConfiguration.h>
 #include <camera/camera2/SubmitInfo.h>
 
 #include <gui/BufferItemConsumer.h>
@@ -55,6 +57,8 @@
 #include <algorithm>
 
 using namespace android;
+using ::android::hardware::ICameraServiceDefault;
+using ::android::hardware::camera2::ICameraDeviceUser;
 
 #define ASSERT_NOT_NULL(x) \
     ASSERT_TRUE((x) != nullptr)
@@ -86,6 +90,11 @@
         return binder::Status::ok();
     };
 
+    virtual binder::Status onCameraAccessPrioritiesChanged() {
+        // No op
+        return binder::Status::ok();
+    }
+
     bool waitForNumCameras(size_t num) const {
         Mutex::Autolock l(mLock);
 
@@ -476,7 +485,8 @@
 
         sp<Surface> surface(new Surface(gbProducer, /*controlledByApp*/false));
 
-        OutputConfiguration output(gbProducer, /*rotation*/0);
+        String16 noPhysicalId;
+        OutputConfiguration output(gbProducer, /*rotation*/0, noPhysicalId);
 
         // Can we configure?
         res = device->beginConfigure();
@@ -490,6 +500,19 @@
         EXPECT_TRUE(res.isOk()) << res;
         EXPECT_FALSE(callbacks->hadError());
 
+        // Session configuration must also be supported in this case
+        SessionConfiguration sessionConfiguration = { /*inputWidth*/ 0, /*inputHeight*/0,
+                /*inputFormat*/ -1, CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE};
+        sessionConfiguration.addOutputConfiguration(output);
+        bool queryStatus;
+        res = device->isSessionConfigurationSupported(sessionConfiguration, &queryStatus);
+        EXPECT_TRUE(res.isOk() ||
+                (res.serviceSpecificErrorCode() == ICameraServiceDefault::ERROR_INVALID_OPERATION))
+                << res;
+        if (res.isOk()) {
+            EXPECT_TRUE(queryStatus);
+        }
+
         // Can we make requests?
         CameraMetadata requestTemplate;
         res = device->createDefaultRequest(/*preview template*/1,
diff --git a/camera/tests/CameraCharacteristicsPermission.cpp b/camera/tests/CameraCharacteristicsPermission.cpp
new file mode 100644
index 0000000..135d2a3
--- /dev/null
+++ b/camera/tests/CameraCharacteristicsPermission.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 "CameraCharacteristicsPermission"
+
+#include <gtest/gtest.h>
+
+#include <binder/ProcessState.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <camera/CameraMetadata.h>
+#include <camera/Camera.h>
+#include <android/hardware/ICameraService.h>
+
+using namespace android;
+using namespace android::hardware;
+
+class CameraCharacteristicsPermission : public ::testing::Test {
+protected:
+
+    CameraCharacteristicsPermission() : numCameras(0){}
+    //Gtest interface
+    void SetUp() override;
+    void TearDown() override;
+
+    int32_t numCameras;
+    sp<ICameraService> mCameraService;
+};
+
+void CameraCharacteristicsPermission::SetUp() {
+    ::android::binder::Status rc;
+    ProcessState::self()->startThreadPool();
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IBinder> binder = sm->getService(String16("media.camera"));
+    mCameraService = interface_cast<ICameraService>(binder);
+    rc = mCameraService->getNumberOfCameras(
+            hardware::ICameraService::CAMERA_TYPE_ALL, &numCameras);
+    EXPECT_TRUE(rc.isOk());
+}
+
+void CameraCharacteristicsPermission::TearDown() {
+    mCameraService.clear();
+}
+
+// Revoking and acquiring permissions automatically might not be possible.
+// Test the functionality for removal of camera characteristics needing
+// a camera permission.
+TEST_F(CameraCharacteristicsPermission, TestCameraPermission) {
+    for (int32_t cameraId = 0; cameraId < numCameras; cameraId++) {
+
+        String16 cameraIdStr = String16(String8::format("%d", cameraId));
+        bool isSupported = false;
+        auto rc = mCameraService->supportsCameraApi(cameraIdStr,
+                hardware::ICameraService::API_VERSION_2, &isSupported);
+        EXPECT_TRUE(rc.isOk());
+        if (!isSupported) {
+            continue;
+        }
+
+        CameraMetadata metadata;
+        std::vector<int32_t> tagsNeedingPermission;
+        rc = mCameraService->getCameraCharacteristics(cameraIdStr, &metadata);
+        ASSERT_TRUE(rc.isOk());
+        EXPECT_FALSE(metadata.isEmpty());
+        EXPECT_EQ(metadata.removePermissionEntries(CAMERA_METADATA_INVALID_VENDOR_ID,
+                    &tagsNeedingPermission), NO_ERROR);
+        camera_metadata_entry_t availableCharacteristics =
+                metadata.find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+        EXPECT_TRUE(0 < availableCharacteristics.count);
+
+        std::vector<uint32_t> availableKeys;
+        availableKeys.reserve(availableCharacteristics.count);
+        availableKeys.insert(availableKeys.begin(), availableCharacteristics.data.i32,
+                availableCharacteristics.data.i32 + availableCharacteristics.count);
+
+        for (const auto &key : tagsNeedingPermission) {
+            ASSERT_FALSE(metadata.exists(key));
+            auto it = std::find(availableKeys.begin(), availableKeys.end(), key);
+            ASSERT_TRUE(it == availableKeys.end());
+        }
+    }
+}
diff --git a/cmds/screenrecord/Android.bp b/cmds/screenrecord/Android.bp
new file mode 100644
index 0000000..86476cd
--- /dev/null
+++ b/cmds/screenrecord/Android.bp
@@ -0,0 +1,55 @@
+// 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_binary {
+    name: "screenrecord",
+
+    srcs: [
+        "screenrecord.cpp",
+        "EglWindow.cpp",
+        "FrameOutput.cpp",
+        "TextRenderer.cpp",
+        "Overlay.cpp",
+        "Program.cpp",
+    ],
+
+    shared_libs: [
+        "libstagefright",
+        "libmedia",
+        "libmedia_omx",
+        "libutils",
+        "libbinder",
+        "libstagefright_foundation",
+        "libjpeg",
+        "libui",
+        "libgui",
+        "libcutils",
+        "liblog",
+        "libEGL",
+        "libGLESv2",
+    ],
+
+    include_dirs: [
+        "frameworks/av/media/libstagefright",
+        "frameworks/av/media/libstagefright/include",
+        "frameworks/native/include/media/openmax",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wno-multichar",
+        //"-UNDEBUG",
+    ]
+}
diff --git a/cmds/screenrecord/Android.mk b/cmds/screenrecord/Android.mk
deleted file mode 100644
index 5e83ed6..0000000
--- a/cmds/screenrecord/Android.mk
+++ /dev/null
@@ -1,45 +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_SRC_FILES := \
-	screenrecord.cpp \
-	EglWindow.cpp \
-	FrameOutput.cpp \
-	TextRenderer.cpp \
-	Overlay.cpp \
-	Program.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	libstagefright libmedia libmedia_omx libutils libbinder libstagefright_foundation \
-	libjpeg libui libgui libcutils liblog libEGL libGLESv2
-
-LOCAL_C_INCLUDES := \
-	frameworks/av/media/libstagefright \
-	frameworks/av/media/libstagefright/include \
-	frameworks/native/include/media/openmax \
-	external/jpeg
-
-LOCAL_CFLAGS := -Werror -Wall
-LOCAL_CFLAGS += -Wno-multichar
-#LOCAL_CFLAGS += -UNDEBUG
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE:= screenrecord
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index d1859d1..7aa655f 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -48,6 +48,7 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecConstants.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaMuxer.h>
 #include <media/stagefright/PersistentSurface.h>
@@ -58,7 +59,35 @@
 #include "Overlay.h"
 #include "FrameOutput.h"
 
-using namespace android;
+using android::ABuffer;
+using android::ALooper;
+using android::AMessage;
+using android::AString;
+using android::DisplayInfo;
+using android::FrameOutput;
+using android::IBinder;
+using android::IGraphicBufferProducer;
+using android::ISurfaceComposer;
+using android::MediaCodec;
+using android::MediaCodecBuffer;
+using android::MediaMuxer;
+using android::Overlay;
+using android::PersistentSurface;
+using android::ProcessState;
+using android::Rect;
+using android::String8;
+using android::SurfaceComposerClient;
+using android::Vector;
+using android::sp;
+using android::status_t;
+
+using android::DISPLAY_ORIENTATION_0;
+using android::DISPLAY_ORIENTATION_180;
+using android::DISPLAY_ORIENTATION_90;
+using android::INVALID_OPERATION;
+using android::NAME_NOT_FOUND;
+using android::NO_ERROR;
+using android::UNKNOWN_ERROR;
 
 static const uint32_t kMinBitRate = 100000;         // 0.1Mbps
 static const uint32_t kMaxBitRate = 200 * 1000000;  // 200Mbps
@@ -73,7 +102,7 @@
 static bool gMonotonicTime = false;     // use system monotonic time for timestamps
 static bool gPersistentSurface = false; // use persistent surface
 static enum {
-    FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES, FORMAT_RAW_FRAMES
+    FORMAT_MP4, FORMAT_H264, FORMAT_WEBM, FORMAT_3GPP, FORMAT_FRAMES, FORMAT_RAW_FRAMES
 } gOutputFormat = FORMAT_MP4;           // data format for output
 static AString gCodecName = "";         // codec name override
 static bool gSizeSpecified = false;     // was size explicitly requested?
@@ -83,6 +112,7 @@
 static uint32_t gVideoHeight = 0;
 static uint32_t gBitRate = 20000000;     // 20Mbps
 static uint32_t gTimeLimitSec = kMaxTimeLimitSec;
+static uint32_t gBframes = 0;
 
 // Set by signal handler to stop recording.
 static volatile bool gStopRequested = false;
@@ -154,15 +184,20 @@
     }
 
     sp<AMessage> format = new AMessage;
-    format->setInt32("width", gVideoWidth);
-    format->setInt32("height", gVideoHeight);
-    format->setString("mime", kMimeTypeAvc);
-    format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
-    format->setInt32("bitrate", gBitRate);
-    format->setFloat("frame-rate", displayFps);
-    format->setInt32("i-frame-interval", 10);
+    format->setInt32(KEY_WIDTH, gVideoWidth);
+    format->setInt32(KEY_HEIGHT, gVideoHeight);
+    format->setString(KEY_MIME, kMimeTypeAvc);
+    format->setInt32(KEY_COLOR_FORMAT, OMX_COLOR_FormatAndroidOpaque);
+    format->setInt32(KEY_BIT_RATE, gBitRate);
+    format->setFloat(KEY_FRAME_RATE, displayFps);
+    format->setInt32(KEY_I_FRAME_INTERVAL, 10);
+    format->setInt32(KEY_MAX_B_FRAMES, gBframes);
+    if (gBframes > 0) {
+        format->setInt32(KEY_PROFILE, AVCProfileMain);
+        format->setInt32(KEY_LEVEL, AVCLevel41);
+    }
 
-    sp<ALooper> looper = new ALooper;
+    sp<android::ALooper> looper = new android::ALooper;
     looper->setName("screenrecord_looper");
     looper->start();
     ALOGV("Creating codec");
@@ -235,10 +270,10 @@
 
     // Set the region of the layer stack we're interested in, which in our
     // case is "all of it".
-    Rect layerStackRect(mainDpyInfo.w, mainDpyInfo.h);
+    Rect layerStackRect(mainDpyInfo.viewportW, mainDpyInfo.viewportH);
 
     // We need to preserve the aspect ratio of the display.
-    float displayAspect = (float) mainDpyInfo.h / (float) mainDpyInfo.w;
+    float displayAspect = (float) mainDpyInfo.viewportH / (float) mainDpyInfo.viewportW;
 
 
     // Set the way we map the output onto the display surface (which will
@@ -315,22 +350,6 @@
 }
 
 /*
- * Set the main display width and height to the actual width and height
- */
-static status_t getActualDisplaySize(const sp<IBinder>& mainDpy, DisplayInfo* mainDpyInfo) {
-    Rect viewport;
-    status_t err = SurfaceComposerClient::getDisplayViewport(mainDpy, &viewport);
-    if (err != NO_ERROR) {
-        fprintf(stderr, "ERROR: unable to get display viewport\n");
-        return err;
-    }
-    mainDpyInfo->w = viewport.width();
-    mainDpyInfo->h = viewport.height();
-
-    return NO_ERROR;
-}
-
-/*
  * Runs the MediaCodec encoder, sending the output to the MediaMuxer.  The
  * input frames are coming from the virtual display as fast as SurfaceFlinger
  * wants to send them.
@@ -400,22 +419,14 @@
                     // useful stuff is hard to get at without a Dalvik VM.
                     err = SurfaceComposerClient::getDisplayInfo(mainDpy,
                             &mainDpyInfo);
-                    if (err == NO_ERROR) {
-                        err = getActualDisplaySize(mainDpy, &mainDpyInfo);
-                        if (err != NO_ERROR) {
-                            fprintf(stderr, "ERROR: unable to set actual display size\n");
-                            return err;
-                        }
-
-                        if (orientation != mainDpyInfo.orientation) {
-                            ALOGD("orientation changed, now %d", mainDpyInfo.orientation);
-                            SurfaceComposerClient::Transaction t;
-                            setDisplayProjection(t, virtualDpy, mainDpyInfo);
-                            t.apply();
-                            orientation = mainDpyInfo.orientation;
-                        }
-                    } else {
+                    if (err != NO_ERROR) {
                         ALOGW("getDisplayInfo(main) failed: %d", err);
+                    } else if (orientation != mainDpyInfo.orientation) {
+                        ALOGD("orientation changed, now %d", mainDpyInfo.orientation);
+                        SurfaceComposerClient::Transaction t;
+                        setDisplayProjection(t, virtualDpy, mainDpyInfo);
+                        t.apply();
+                        orientation = mainDpyInfo.orientation;
                     }
                 }
 
@@ -472,7 +483,7 @@
         case -EAGAIN:                       // INFO_TRY_AGAIN_LATER
             ALOGV("Got -EAGAIN, looping");
             break;
-        case INFO_FORMAT_CHANGED:           // INFO_OUTPUT_FORMAT_CHANGED
+        case android::INFO_FORMAT_CHANGED:    // INFO_OUTPUT_FORMAT_CHANGED
             {
                 // Format includes CSD, which we must provide to muxer.
                 ALOGV("Encoder format changed");
@@ -489,7 +500,7 @@
                 }
             }
             break;
-        case INFO_OUTPUT_BUFFERS_CHANGED:   // INFO_OUTPUT_BUFFERS_CHANGED
+        case android::INFO_OUTPUT_BUFFERS_CHANGED:   // INFO_OUTPUT_BUFFERS_CHANGED
             // Not expected for an encoder; handle it anyway.
             ALOGV("Encoder buffers changed");
             err = encoder->getOutputBuffers(&buffers);
@@ -580,8 +591,12 @@
     self->startThreadPool();
 
     // Get main display parameters.
-    sp<IBinder> mainDpy = SurfaceComposerClient::getBuiltInDisplay(
-            ISurfaceComposer::eDisplayIdMain);
+    const sp<IBinder> mainDpy = SurfaceComposerClient::getInternalDisplayToken();
+    if (mainDpy == nullptr) {
+        fprintf(stderr, "ERROR: no display\n");
+        return NAME_NOT_FOUND;
+    }
+
     DisplayInfo mainDpyInfo;
     err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
     if (err != NO_ERROR) {
@@ -589,25 +604,19 @@
         return err;
     }
 
-    err = getActualDisplaySize(mainDpy, &mainDpyInfo);
-    if (err != NO_ERROR) {
-        fprintf(stderr, "ERROR: unable to set actual display size\n");
-        return err;
-    }
-
     if (gVerbose) {
         printf("Main display is %dx%d @%.2ffps (orientation=%u)\n",
-                mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps,
+                mainDpyInfo.viewportW, mainDpyInfo.viewportH, mainDpyInfo.fps,
                 mainDpyInfo.orientation);
         fflush(stdout);
     }
 
     // Encoder can't take odd number as config
     if (gVideoWidth == 0) {
-        gVideoWidth = floorToEven(mainDpyInfo.w);
+        gVideoWidth = floorToEven(mainDpyInfo.viewportW);
     }
     if (gVideoHeight == 0) {
-        gVideoHeight = floorToEven(mainDpyInfo.h);
+        gVideoHeight = floorToEven(mainDpyInfo.viewportH);
     }
 
     // Configure and start the encoder.
@@ -685,7 +694,9 @@
     sp<MediaMuxer> muxer = NULL;
     FILE* rawFp = NULL;
     switch (gOutputFormat) {
-        case FORMAT_MP4: {
+        case FORMAT_MP4:
+        case FORMAT_WEBM:
+        case FORMAT_3GPP: {
             // Configure muxer.  We have to wait for the CSD blob from the encoder
             // before we can start it.
             err = unlink(fileName);
@@ -698,7 +709,13 @@
                 fprintf(stderr, "ERROR: couldn't open file\n");
                 abort();
             }
-            muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_MPEG_4);
+            if (gOutputFormat == FORMAT_MP4) {
+                muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_MPEG_4);
+            } else if (gOutputFormat == FORMAT_WEBM) {
+                muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_WEBM);
+            } else {
+                muxer = new MediaMuxer(fd, MediaMuxer::OUTPUT_FORMAT_THREE_GPP);
+            }
             close(fd);
             if (gRotate) {
                 muxer->setOrientationHint(90);  // TODO: does this do anything?
@@ -948,6 +965,7 @@
         { "codec-name",         required_argument,  NULL, 'N' },
         { "monotonic-time",     no_argument,        NULL, 'm' },
         { "persistent-surface", no_argument,        NULL, 'p' },
+        { "bframes",            required_argument,  NULL, 'B' },
         { NULL,                 0,                  NULL, 0 }
     };
 
@@ -1018,6 +1036,10 @@
                 gOutputFormat = FORMAT_MP4;
             } else if (strcmp(optarg, "h264") == 0) {
                 gOutputFormat = FORMAT_H264;
+            } else if (strcmp(optarg, "webm") == 0) {
+                gOutputFormat = FORMAT_WEBM;
+            } else if (strcmp(optarg, "3gpp") == 0) {
+                gOutputFormat = FORMAT_3GPP;
             } else if (strcmp(optarg, "frames") == 0) {
                 gOutputFormat = FORMAT_FRAMES;
             } else if (strcmp(optarg, "raw-frames") == 0) {
@@ -1036,6 +1058,11 @@
         case 'p':
             gPersistentSurface = true;
             break;
+        case 'B':
+            if (parseValueWithUnit(optarg, &gBframes) != NO_ERROR) {
+                return 2;
+            }
+            break;
         default:
             if (ic != '?') {
                 fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic);
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index c7619af..6eb2e9f 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -8,7 +8,7 @@
         SineSource.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright libmedia libmedia_omx libmediaextractor libutils libbinder \
+        libstagefright libmedia libmedia_omx libutils libbinder \
         libstagefright_foundation libjpeg libui libgui libcutils liblog \
         libhidlbase \
         android.hardware.media.omx@1.0 \
@@ -36,7 +36,7 @@
         record.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright libmedia libmediaextractor liblog libutils libbinder \
+        libstagefright libmedia liblog libutils libbinder \
         libstagefright_foundation
 
 LOCAL_C_INCLUDES:= \
@@ -61,7 +61,7 @@
         recordvideo.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright libmedia libmediaextractor liblog libutils libbinder \
+        libstagefright libmedia liblog libutils libbinder \
         libstagefright_foundation
 
 LOCAL_C_INCLUDES:= \
@@ -87,7 +87,7 @@
         audioloop.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright libmedia libmediaextractor liblog libutils libbinder \
+        libstagefright libmedia liblog libutils libbinder \
         libstagefright_foundation
 
 LOCAL_C_INCLUDES:= \
@@ -111,7 +111,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libstagefright liblog libutils libbinder libui libgui \
-        libstagefright_foundation libmedia libcutils libmediaextractor
+        libstagefright_foundation libmedia libcutils
 
 LOCAL_C_INCLUDES:= \
         frameworks/av/media/libstagefright \
@@ -191,7 +191,6 @@
 LOCAL_MODULE:= mediafilter
 
 LOCAL_SANITIZE := cfi
-LOCAL_SANITIZE_DIAG := cfi
 
 include $(BUILD_EXECUTABLE)
 
@@ -204,7 +203,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libstagefright liblog libutils libbinder libstagefright_foundation \
-        libcutils libc libmediaextractor
+        libcutils libc
 
 LOCAL_C_INCLUDES:= \
         frameworks/av/media/libstagefright \
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index 6a58467..e5a4337 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);
@@ -366,13 +366,13 @@
             case 'T':
             {
                 useTimestamp = true;
+                FALLTHROUGH_INTENDED;
             }
-            // fall through
             case 'R':
             {
                 renderSurface = true;
+                FALLTHROUGH_INTENDED;
             }
-            // fall through
             case 'S':
             {
                 useSurface = true;
@@ -400,7 +400,7 @@
 
     ProcessState::self()->startThreadPool();
 
-    sp<ALooper> looper = new ALooper;
+    sp<android::ALooper> looper = new android::ALooper;
     looper->start();
 
     sp<SurfaceComposerClient> composerClient;
@@ -411,10 +411,12 @@
         composerClient = new SurfaceComposerClient;
         CHECK_EQ(composerClient->initCheck(), (status_t)OK);
 
-        sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
-                ISurfaceComposer::eDisplayIdMain));
+        const sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
+        CHECK(display != nullptr);
+
         DisplayInfo info;
-        SurfaceComposerClient::getDisplayInfo(display, &info);
+        CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR);
+
         ssize_t displayWidth = info.w;
         ssize_t displayHeight = info.h;
 
diff --git a/cmds/stagefright/mediafilter.cpp b/cmds/stagefright/mediafilter.cpp
index f24d2dd..2cf6955 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);
@@ -706,13 +706,13 @@
             case 'T':
             {
                 useTimestamp = true;
+                FALLTHROUGH_INTENDED;
             }
-            // fall through
             case 'R':
             {
                 renderSurface = true;
+                FALLTHROUGH_INTENDED;
             }
-            // fall through
             case 'S':
             {
                 useSurface = true;
@@ -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;
@@ -748,10 +748,12 @@
         composerClient = new SurfaceComposerClient;
         CHECK_EQ((status_t)OK, composerClient->initCheck());
 
-        android::sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
-                ISurfaceComposer::eDisplayIdMain));
+        const android::sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
+        CHECK(display != nullptr);
+
         DisplayInfo info;
-        SurfaceComposerClient::getDisplayInfo(display, &info);
+        CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR);
+
         ssize_t displayWidth = info.w;
         ssize_t displayHeight = info.h;
 
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 44b0015..95a16f3 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -17,7 +17,6 @@
 #include "SineSource.h"
 
 #include <binder/ProcessState.h>
-#include <media/MediaExtractor.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -28,6 +27,7 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaCodecSource.h>
 #include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MediaExtractorFactory.h>
 #include <media/stagefright/MPEG4Writer.h>
 #include <media/stagefright/SimpleDecodingSource.h>
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 61fc897..bf36be0 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -32,7 +32,6 @@
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <media/DataSource.h>
-#include <media/MediaExtractor.h>
 #include <media/MediaSource.h>
 #include <media/ICrypto.h>
 #include <media/IMediaHTTPService.h>
@@ -47,9 +46,11 @@
 #include <media/stagefright/JPEGSource.h>
 #include <media/stagefright/InterfaceUtils.h>
 #include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecConstants.h>
 #include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MediaExtractorFactory.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/SimpleDecodingSource.h>
@@ -78,6 +79,7 @@
 static bool gPlaybackAudio;
 static bool gWriteMP4;
 static bool gDisplayHistogram;
+static bool gVerbose = false;
 static bool showProgress = true;
 static String8 gWriteMP4Filename;
 static String8 gComponentNameOverride;
@@ -159,6 +161,11 @@
             break;
         }
 
+        if (gVerbose) {
+            MetaDataBase &meta = mbuf->meta_data();
+            fprintf(stdout, "sample format: %s\n", meta.toString().c_str());
+        }
+
         CHECK_EQ(
                 fwrite((const uint8_t *)mbuf->data() + mbuf->range_offset(),
                        1,
@@ -218,11 +225,15 @@
         player->setSource(rawSource);
         rawSource.clear();
 
-        player->start(true /* sourceAlreadyStarted */);
+        err = player->start(true /* sourceAlreadyStarted */);
 
-        status_t finalStatus;
-        while (!player->reachedEOS(&finalStatus)) {
-            usleep(100000ll);
+        if (err == OK) {
+            status_t finalStatus;
+            while (!player->reachedEOS(&finalStatus)) {
+                usleep(100000ll);
+            }
+        } else {
+            fprintf(stderr, "unable to start playback err=%d (0x%08x)\n", err, err);
         }
 
         delete player;
@@ -348,7 +359,10 @@
                     decodeTimesUs.push(delayDecodeUs);
                 }
 
-                if (showProgress && (n++ % 16) == 0) {
+                if (gVerbose) {
+                    MetaDataBase &meta = buffer->meta_data();
+                    fprintf(stdout, "%ld sample format: %s\n", numFrames, meta.toString().c_str());
+                } else if (showProgress && (n++ % 16) == 0) {
                     printf(".");
                     fflush(stdout);
                 }
@@ -579,12 +593,12 @@
                 break;
             }
 
+            CHECK(buffer != NULL);
+
             if (buffer->range_length() > 0) {
                 break;
             }
 
-            CHECK(buffer != NULL);
-
             buffer->release();
             buffer = NULL;
         }
@@ -615,7 +629,7 @@
     fprintf(stderr, "       -l(ist) components\n");
     fprintf(stderr, "       -m max-number-of-frames-to-decode in each pass\n");
     fprintf(stderr, "       -b bug to reproduce\n");
-    fprintf(stderr, "       -p(rofiles) dump decoder profiles supported\n");
+    fprintf(stderr, "       -i(nfo) dump codec info (profiles and color formats supported, details)\n");
     fprintf(stderr, "       -t(humbnail) extract video thumbnail or album art\n");
     fprintf(stderr, "       -s(oftware) prefer software codec\n");
     fprintf(stderr, "       -r(hardware) force to use hardware codec\n");
@@ -630,55 +644,134 @@
     fprintf(stderr, "       -T allocate buffers from a surface texture\n");
     fprintf(stderr, "       -d(ump) output_filename (raw stream data to a file)\n");
     fprintf(stderr, "       -D(ump) output_filename (decoded PCM data to a file)\n");
+    fprintf(stderr, "       -v be more verbose\n");
 }
 
-static void dumpCodecProfiles(bool queryDecoders) {
-    const char *kMimeTypes[] = {
-        MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4,
-        MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC,
-        MEDIA_MIMETYPE_AUDIO_AMR_NB, MEDIA_MIMETYPE_AUDIO_AMR_WB,
-        MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW,
-        MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS,
-        MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9,
-        MEDIA_MIMETYPE_VIDEO_DOLBY_VISION
-    };
-
-    const char *codecType = queryDecoders? "decoder" : "encoder";
-    printf("%s profiles:\n", codecType);
+static void dumpCodecDetails(bool queryDecoders) {
+    const char *codecType = queryDecoders? "Decoder" : "Encoder";
+    printf("\n%s infos by media types:\n"
+           "=============================\n", codecType);
 
     sp<IMediaCodecList> list = MediaCodecList::getInstance();
     size_t numCodecs = list->countCodecs();
 
-    for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); ++k) {
-        printf("type '%s':\n", kMimeTypes[k]);
+    // gather all media types supported by codec class, and link to codecs that support them
+    KeyedVector<AString, Vector<sp<MediaCodecInfo>>> allMediaTypes;
+    for (size_t codec_ix = 0; codec_ix < numCodecs; ++codec_ix) {
+        sp<MediaCodecInfo> info = list->getCodecInfo(codec_ix);
+        if (info->isEncoder() == !queryDecoders) {
+            Vector<AString> supportedMediaTypes;
+            info->getSupportedMediaTypes(&supportedMediaTypes);
+            if (!supportedMediaTypes.size()) {
+                printf("warning: %s does not support any media types\n",
+                        info->getCodecName());
+            } else {
+                for (const AString &mediaType : supportedMediaTypes) {
+                    if (allMediaTypes.indexOfKey(mediaType) < 0) {
+                        allMediaTypes.add(mediaType, Vector<sp<MediaCodecInfo>>());
+                    }
+                    allMediaTypes.editValueFor(mediaType).add(info);
+                }
+            }
+        }
+    }
 
-        for (size_t index = 0; index < numCodecs; ++index) {
-            sp<MediaCodecInfo> info = list->getCodecInfo(index);
-            if (info == NULL || info->isEncoder() != !queryDecoders) {
-                continue;
-            }
-            sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(kMimeTypes[k]);
+    KeyedVector<AString, bool> visitedCodecs;
+    for (size_t type_ix = 0; type_ix < allMediaTypes.size(); ++type_ix) {
+        const AString &mediaType = allMediaTypes.keyAt(type_ix);
+        printf("\nMedia type '%s':\n", mediaType.c_str());
+
+        for (const sp<MediaCodecInfo> &info : allMediaTypes.valueAt(type_ix)) {
+            sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(mediaType.c_str());
             if (caps == NULL) {
+                printf("warning: %s does not have capabilities for type %s\n",
+                        info->getCodecName(), mediaType.c_str());
                 continue;
             }
-            printf("  %s '%s' supports ",
+            printf("  %s \"%s\" supports\n",
                        codecType, info->getCodecName());
 
-            Vector<MediaCodecInfo::ProfileLevel> profileLevels;
-            caps->getSupportedProfileLevels(&profileLevels);
-            if (profileLevels.size() == 0) {
-                printf("NOTHING.\n");
-                continue;
+            auto printList = [](const char *type, const Vector<AString> &values){
+                printf("    %s: [", type);
+                for (size_t j = 0; j < values.size(); ++j) {
+                    printf("\n      %s%s", values[j].c_str(),
+                            j == values.size() - 1 ? " " : ",");
+                }
+                printf("]\n");
+            };
+
+            if (visitedCodecs.indexOfKey(info->getCodecName()) < 0) {
+                visitedCodecs.add(info->getCodecName(), true);
+                {
+                    Vector<AString> aliases;
+                    info->getAliases(&aliases);
+                    // quote alias
+                    for (AString &alias : aliases) {
+                        alias.insert("\"", 1, 0);
+                        alias.append('"');
+                    }
+                    printList("aliases", aliases);
+                }
+                {
+                    uint32_t attrs = info->getAttributes();
+                    Vector<AString> list;
+                    list.add(AStringPrintf("encoder: %d", !!(attrs & MediaCodecInfo::kFlagIsEncoder)));
+                    list.add(AStringPrintf("vendor: %d", !!(attrs & MediaCodecInfo::kFlagIsVendor)));
+                    list.add(AStringPrintf("software-only: %d", !!(attrs & MediaCodecInfo::kFlagIsSoftwareOnly)));
+                    list.add(AStringPrintf("hw-accelerated: %d", !!(attrs & MediaCodecInfo::kFlagIsHardwareAccelerated)));
+                    printList(AStringPrintf("attributes: %#x", attrs).c_str(), list);
+                }
+
+                printf("    owner: \"%s\"\n", info->getOwnerName());
+                printf("    rank: %u\n", info->getRank());
+            } else {
+                printf("    aliases, attributes, owner, rank: see above\n");
             }
 
-            for (size_t j = 0; j < profileLevels.size(); ++j) {
-                const MediaCodecInfo::ProfileLevel &profileLevel = profileLevels[j];
+            {
+                Vector<AString> list;
+                Vector<MediaCodecInfo::ProfileLevel> profileLevels;
+                caps->getSupportedProfileLevels(&profileLevels);
+                for (const MediaCodecInfo::ProfileLevel &pl : profileLevels) {
+                    const char *niceProfile =
+                        mediaType.equalsIgnoreCase(MIMETYPE_AUDIO_AAC)   ? asString_AACObject(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2) ? asString_MPEG2Profile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263)  ? asString_H263Profile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4) ? asString_MPEG4Profile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC)   ? asString_AVCProfile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8)   ? asString_VP8Profile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC)  ? asString_HEVCProfile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9)   ? asString_VP9Profile(pl.mProfile) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1)   ? asString_AV1Profile(pl.mProfile) :"??";
+                    const char *niceLevel =
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG2) ? asString_MPEG2Level(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_H263)  ? asString_H263Level(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_MPEG4) ? asString_MPEG4Level(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AVC)   ? asString_AVCLevel(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP8)   ? asString_VP8Level(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_HEVC)  ? asString_HEVCTierLevel(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_VP9)   ? asString_VP9Level(pl.mLevel) :
+                        mediaType.equalsIgnoreCase(MIMETYPE_VIDEO_AV1)   ? asString_AV1Level(pl.mLevel) :
+                        "??";
 
-                printf("%s%u/%u", j > 0 ? ", " : "",
-                        profileLevel.mProfile, profileLevel.mLevel);
+                    list.add(AStringPrintf("% 5u/% 5u (%s/%s)",
+                            pl.mProfile, pl.mLevel, niceProfile, niceLevel));
+                }
+                printList("profile/levels", list);
             }
 
-            printf("\n");
+            {
+                Vector<AString> list;
+                Vector<uint32_t> colors;
+                caps->getSupportedColorFormats(&colors);
+                for (uint32_t color : colors) {
+                    list.add(AStringPrintf("%#x (%s)", color,
+                            asString_ColorFormat((int32_t)color)));
+                }
+                printList("colors", list);
+            }
+
+            printf("    details: %s\n", caps->getDetails()->debugString(6).c_str());
         }
     }
 }
@@ -688,7 +781,7 @@
 
     bool audioOnly = false;
     bool listComponents = false;
-    bool dumpProfiles = false;
+    bool dumpCodecInfo = false;
     bool extractThumbnail = false;
     bool seekTest = false;
     bool useSurfaceAlloc = false;
@@ -705,10 +798,10 @@
     gWriteMP4 = false;
     gDisplayHistogram = false;
 
-    sp<ALooper> looper;
+    sp<android::ALooper> looper;
 
     int res;
-    while ((res = getopt(argc, argv, "haqn:lm:b:ptsrow:kN:xSTd:D:")) >= 0) {
+    while ((res = getopt(argc, argv, "vhaqn:lm:b:itsrow:kN:xSTd:D:")) >= 0) {
         switch (res) {
             case 'a':
             {
@@ -778,9 +871,9 @@
                 break;
             }
 
-            case 'p':
+            case 'i':
             {
-                dumpProfiles = true;
+                dumpCodecInfo = true;
                 break;
             }
 
@@ -832,6 +925,12 @@
                 break;
             }
 
+            case 'v':
+            {
+                gVerbose = true;
+                break;
+            }
+
             case '?':
             case 'h':
             default:
@@ -915,9 +1014,9 @@
         return 0;
     }
 
-    if (dumpProfiles) {
-        dumpCodecProfiles(true /* queryDecoders */);
-        dumpCodecProfiles(false /* queryDecoders */);
+    if (dumpCodecInfo) {
+        dumpCodecDetails(true /* queryDecoders */);
+        dumpCodecDetails(false /* queryDecoders */);
     }
 
     if (listComponents) {
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index b0199d8..35bdbc0 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -24,7 +24,6 @@
 #include <media/DataSource.h>
 #include <media/IMediaHTTPService.h>
 #include <media/IStreamSource.h>
-#include <media/MediaExtractor.h>
 #include <media/mediaplayer.h>
 #include <media/MediaSource.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -32,6 +31,7 @@
 #include <media/stagefright/DataSourceFactory.h>
 #include <media/stagefright/InterfaceUtils.h>
 #include <media/stagefright/MPEG2TSWriter.h>
+#include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MediaExtractorFactory.h>
 #include <media/stagefright/MetaData.h>
 
@@ -318,10 +318,12 @@
     sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient;
     CHECK_EQ(composerClient->initCheck(), (status_t)OK);
 
-    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
-            ISurfaceComposer::eDisplayIdMain));
+    const sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
+    CHECK(display != nullptr);
+
     DisplayInfo info;
-    SurfaceComposerClient::getDisplayInfo(display, &info);
+    CHECK_EQ(SurfaceComposerClient::getDisplayInfo(display, &info), NO_ERROR);
+
     ssize_t displayWidth = info.w;
     ssize_t displayHeight = info.h;
 
diff --git a/drm/common/Android.bp b/drm/common/Android.bp
index 1552c3f..272684c 100644
--- a/drm/common/Android.bp
+++ b/drm/common/Android.bp
@@ -35,7 +35,7 @@
 
     cflags: ["-Wall", "-Werror"],
 
-    static_libs: ["libbinder"],
+    shared_libs: ["libbinder"],
 
     export_include_dirs: ["include"],
 }
diff --git a/drm/common/DrmEngineBase.cpp b/drm/common/DrmEngineBase.cpp
index f734905..aec5959 100644
--- a/drm/common/DrmEngineBase.cpp
+++ b/drm/common/DrmEngineBase.cpp
@@ -79,12 +79,12 @@
 }
 
 status_t DrmEngineBase::consumeRights(
-    int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+    int uniqueId, sp<DecryptHandle>& decryptHandle, int action, bool reserve) {
     return onConsumeRights(uniqueId, decryptHandle, action, reserve);
 }
 
 status_t DrmEngineBase::setPlaybackStatus(
-    int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
+    int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus, int64_t position) {
     return onSetPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
 }
 
@@ -120,7 +120,7 @@
 }
 
 status_t DrmEngineBase::openDecryptSession(
-    int uniqueId, DecryptHandle* decryptHandle,
+    int uniqueId, sp<DecryptHandle>& decryptHandle,
     int fd, off64_t offset, off64_t length, const char* mime) {
 
     if (!mime || mime[0] == '\0') {
@@ -131,7 +131,7 @@
 }
 
 status_t DrmEngineBase::openDecryptSession(
-    int uniqueId, DecryptHandle* decryptHandle,
+    int uniqueId, sp<DecryptHandle>& decryptHandle,
     const char* uri, const char* mime) {
     if (!mime || mime[0] == '\0') {
         return onOpenDecryptSession(uniqueId, decryptHandle, uri);
@@ -139,33 +139,33 @@
     return onOpenDecryptSession(uniqueId, decryptHandle, uri, mime);
 }
 
-status_t DrmEngineBase::openDecryptSession(int uniqueId, DecryptHandle* decryptHandle,
+status_t DrmEngineBase::openDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle,
         const DrmBuffer& buf, const String8& mimeType) {
     return onOpenDecryptSession(uniqueId, decryptHandle, buf, mimeType);
 }
 
-status_t DrmEngineBase::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+status_t DrmEngineBase::closeDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle) {
     return onCloseDecryptSession(uniqueId, decryptHandle);
 }
 
 status_t DrmEngineBase::initializeDecryptUnit(
-    int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
+    int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
     return onInitializeDecryptUnit(uniqueId, decryptHandle, decryptUnitId, headerInfo);
 }
 
 status_t DrmEngineBase::decrypt(
-    int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+    int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
     const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
     return onDecrypt(uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
 }
 
 status_t DrmEngineBase::finalizeDecryptUnit(
-    int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+    int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId) {
     return onFinalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
 }
 
 ssize_t DrmEngineBase::pread(
-    int uniqueId, DecryptHandle* decryptHandle, void* buffer, ssize_t numBytes, off64_t offset) {
+    int uniqueId, sp<DecryptHandle>& decryptHandle, void* buffer, ssize_t numBytes, off64_t offset) {
     return onPread(uniqueId, decryptHandle, buffer, numBytes, offset);
 }
 
diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp
index 44f98dd..a6d33b0 100644
--- a/drm/common/IDrmManagerService.cpp
+++ b/drm/common/IDrmManagerService.cpp
@@ -39,7 +39,7 @@
 using namespace android;
 
 static void writeDecryptHandleToParcelData(
-        const DecryptHandle* handle, Parcel* data) {
+        const sp<DecryptHandle>& handle, Parcel* data) {
     data->writeInt32(handle->decryptId);
     data->writeString8(handle->mimeType);
     data->writeInt32(handle->decryptApiType);
@@ -67,7 +67,7 @@
 }
 
 static void readDecryptHandleFromParcelData(
-        DecryptHandle* handle, const Parcel& data) {
+        sp<DecryptHandle>& handle, const Parcel& data) {
     if (0 == data.dataAvail()) {
         return;
     }
@@ -99,7 +99,7 @@
     }
 }
 
-static void clearDecryptHandle(DecryptHandle* handle) {
+static void clearDecryptHandle(sp<DecryptHandle> &handle) {
     if (handle == NULL) {
         return;
     }
@@ -414,7 +414,7 @@
 }
 
 status_t BpDrmManagerService::consumeRights(
-            int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int action, bool reserve) {
     ALOGV("consumeRights");
     Parcel data, reply;
 
@@ -431,7 +431,7 @@
 }
 
 status_t BpDrmManagerService::setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus, int64_t position) {
     ALOGV("setPlaybackStatus");
     Parcel data, reply;
 
@@ -603,7 +603,7 @@
     return reply.readInt32();
 }
 
-DecryptHandle* BpDrmManagerService::openDecryptSession(
+sp<DecryptHandle> BpDrmManagerService::openDecryptSession(
             int uniqueId, int fd, off64_t offset, off64_t length, const char* mime) {
     ALOGV("Entering BpDrmManagerService::openDecryptSession");
     Parcel data, reply;
@@ -621,7 +621,7 @@
 
     remote()->transact(OPEN_DECRYPT_SESSION, data, &reply);
 
-    DecryptHandle* handle = NULL;
+    sp<DecryptHandle> handle;
     if (0 != reply.dataAvail()) {
         handle = new DecryptHandle();
         readDecryptHandleFromParcelData(handle, reply);
@@ -629,7 +629,7 @@
     return handle;
 }
 
-DecryptHandle* BpDrmManagerService::openDecryptSession(
+sp<DecryptHandle> BpDrmManagerService::openDecryptSession(
         int uniqueId, const char* uri, const char* mime) {
 
     ALOGV("Entering BpDrmManagerService::openDecryptSession: mime=%s", mime? mime: "NULL");
@@ -646,7 +646,7 @@
 
     remote()->transact(OPEN_DECRYPT_SESSION_FROM_URI, data, &reply);
 
-    DecryptHandle* handle = NULL;
+    sp<DecryptHandle> handle;
     if (0 != reply.dataAvail()) {
         handle = new DecryptHandle();
         readDecryptHandleFromParcelData(handle, reply);
@@ -656,7 +656,7 @@
     return handle;
 }
 
-DecryptHandle* BpDrmManagerService::openDecryptSession(
+sp<DecryptHandle> BpDrmManagerService::openDecryptSession(
             int uniqueId, const DrmBuffer& buf, const String8& mimeType) {
     ALOGV("Entering BpDrmManagerService::openDecryptSession");
     Parcel data, reply;
@@ -673,7 +673,7 @@
 
     remote()->transact(OPEN_DECRYPT_SESSION_FOR_STREAMING, data, &reply);
 
-    DecryptHandle* handle = NULL;
+    sp<DecryptHandle> handle;
     if (0 != reply.dataAvail()) {
         handle = new DecryptHandle();
         readDecryptHandleFromParcelData(handle, reply);
@@ -683,7 +683,7 @@
     return handle;
 }
 
-status_t BpDrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+status_t BpDrmManagerService::closeDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle) {
     ALOGV("closeDecryptSession");
     Parcel data, reply;
 
@@ -698,7 +698,7 @@
 }
 
 status_t BpDrmManagerService::initializeDecryptUnit(
-            int uniqueId, DecryptHandle* decryptHandle,
+            int uniqueId, sp<DecryptHandle>& decryptHandle,
             int decryptUnitId, const DrmBuffer* headerInfo) {
     ALOGV("initializeDecryptUnit");
     Parcel data, reply;
@@ -718,7 +718,7 @@
 }
 
 status_t BpDrmManagerService::decrypt(
-            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
     ALOGV("decrypt");
     Parcel data, reply;
@@ -754,7 +754,7 @@
 }
 
 status_t BpDrmManagerService::finalizeDecryptUnit(
-            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId) {
     ALOGV("finalizeDecryptUnit");
     Parcel data, reply;
 
@@ -770,7 +770,7 @@
 }
 
 ssize_t BpDrmManagerService::pread(
-            int uniqueId, DecryptHandle* decryptHandle, void* buffer,
+            int uniqueId, sp<DecryptHandle>& decryptHandle, void* buffer,
             ssize_t numBytes, off64_t offset) {
     ALOGV("read");
     Parcel data, reply;
@@ -1128,16 +1128,16 @@
 
         const int uniqueId = data.readInt32();
 
-        DecryptHandle handle;
-        readDecryptHandleFromParcelData(&handle, data);
+        sp<DecryptHandle> handle = new DecryptHandle();
+        readDecryptHandleFromParcelData(handle, data);
 
         const int action = data.readInt32();
         const bool reserve = static_cast<bool>(data.readInt32());
         const status_t status
-            = consumeRights(uniqueId, &handle, action, reserve);
+            = consumeRights(uniqueId, handle, action, reserve);
         reply->writeInt32(status);
 
-        clearDecryptHandle(&handle);
+        clearDecryptHandle(handle);
         return DRM_NO_ERROR;
     }
 
@@ -1148,16 +1148,16 @@
 
         const int uniqueId = data.readInt32();
 
-        DecryptHandle handle;
-        readDecryptHandleFromParcelData(&handle, data);
+        sp<DecryptHandle> handle = new DecryptHandle();
+        readDecryptHandleFromParcelData(handle, data);
 
         const int playbackStatus = data.readInt32();
         const int64_t position = data.readInt64();
         const status_t status
-            = setPlaybackStatus(uniqueId, &handle, playbackStatus, position);
+            = setPlaybackStatus(uniqueId, handle, playbackStatus, position);
         reply->writeInt32(status);
 
-        clearDecryptHandle(&handle);
+        clearDecryptHandle(handle);
         return DRM_NO_ERROR;
     }
 
@@ -1329,13 +1329,13 @@
         const off64_t length = data.readInt64();
         const String8 mime = data.readString8();
 
-        DecryptHandle* handle
+        sp<DecryptHandle> handle
             = openDecryptSession(uniqueId, fd, offset, length, mime.string());
 
-        if (NULL != handle) {
-            writeDecryptHandleToParcelData(handle, reply);
+        if (NULL != handle.get()) {
+            writeDecryptHandleToParcelData(handle.get(), reply);
             clearDecryptHandle(handle);
-            delete handle; handle = NULL;
+            handle.clear();
         }
         return DRM_NO_ERROR;
     }
@@ -1349,13 +1349,13 @@
         const String8 uri = data.readString8();
         const String8 mime = data.readString8();
 
-        DecryptHandle* handle = openDecryptSession(uniqueId, uri.string(), mime.string());
+        sp<DecryptHandle> handle = openDecryptSession(uniqueId, uri.string(), mime.string());
 
-        if (NULL != handle) {
-            writeDecryptHandleToParcelData(handle, reply);
+        if (NULL != handle.get()) {
+            writeDecryptHandleToParcelData(handle.get(), reply);
 
             clearDecryptHandle(handle);
-            delete handle; handle = NULL;
+            handle.clear();
         } else {
             ALOGV("NULL decryptHandle is returned");
         }
@@ -1373,13 +1373,12 @@
                 bufferSize);
         const String8 mimeType(data.readString8());
 
-        DecryptHandle* handle = openDecryptSession(uniqueId, buf, mimeType);
+        sp<DecryptHandle> handle = openDecryptSession(uniqueId, buf, mimeType);
 
         if (handle != NULL) {
             writeDecryptHandleToParcelData(handle, reply);
             clearDecryptHandle(handle);
-            delete handle;
-            handle = NULL;
+            handle.clear();
         } else {
             ALOGV("NULL decryptHandle is returned");
         }
@@ -1393,7 +1392,7 @@
 
         const int uniqueId = data.readInt32();
 
-        DecryptHandle* handle = new DecryptHandle();
+        sp<DecryptHandle> handle = new DecryptHandle();
         readDecryptHandleFromParcelData(handle, data);
 
         const status_t status = closeDecryptSession(uniqueId, handle);
@@ -1408,8 +1407,8 @@
 
         const int uniqueId = data.readInt32();
 
-        DecryptHandle handle;
-        readDecryptHandleFromParcelData(&handle, data);
+        sp<DecryptHandle> handle = new DecryptHandle();
+        readDecryptHandleFromParcelData(handle, data);
 
         const int decryptUnitId = data.readInt32();
 
@@ -1417,17 +1416,17 @@
         const uint32_t bufferSize = data.readInt32();
         if (bufferSize > data.dataAvail()) {
             reply->writeInt32(BAD_VALUE);
-            clearDecryptHandle(&handle);
+            clearDecryptHandle(handle);
             return DRM_NO_ERROR;
         }
         DrmBuffer* headerInfo = NULL;
         headerInfo = new DrmBuffer((char *)data.readInplace(bufferSize), bufferSize);
 
         const status_t status
-            = initializeDecryptUnit(uniqueId, &handle, decryptUnitId, headerInfo);
+            = initializeDecryptUnit(uniqueId, handle, decryptUnitId, headerInfo);
         reply->writeInt32(status);
 
-        clearDecryptHandle(&handle);
+        clearDecryptHandle(handle);
         delete headerInfo; headerInfo = NULL;
         return DRM_NO_ERROR;
     }
@@ -1439,8 +1438,8 @@
 
         const int uniqueId = data.readInt32();
 
-        DecryptHandle handle;
-        readDecryptHandleFromParcelData(&handle, data);
+        sp<DecryptHandle> handle = new DecryptHandle;
+        readDecryptHandleFromParcelData(handle, data);
 
         const int decryptUnitId = data.readInt32();
         const uint32_t decBufferSize = data.readInt32();
@@ -1450,7 +1449,7 @@
             decBufferSize > MAX_BINDER_TRANSACTION_SIZE) {
             reply->writeInt32(BAD_VALUE);
             reply->writeInt32(0);
-            clearDecryptHandle(&handle);
+            clearDecryptHandle(handle);
             return DRM_NO_ERROR;
         }
 
@@ -1470,7 +1469,7 @@
         }
 
         const status_t status
-            = decrypt(uniqueId, &handle, decryptUnitId, encBuffer, &decBuffer, IV);
+            = decrypt(uniqueId, handle, decryptUnitId, encBuffer, &decBuffer, IV);
 
         reply->writeInt32(status);
 
@@ -1480,7 +1479,7 @@
             reply->write(decBuffer->data, size);
         }
 
-        clearDecryptHandle(&handle);
+        clearDecryptHandle(handle);
         delete encBuffer; encBuffer = NULL;
         delete decBuffer; decBuffer = NULL;
         delete [] buffer; buffer = NULL;
@@ -1495,13 +1494,13 @@
 
         const int uniqueId = data.readInt32();
 
-        DecryptHandle handle;
-        readDecryptHandleFromParcelData(&handle, data);
+        sp<DecryptHandle> handle = new DecryptHandle();
+        readDecryptHandleFromParcelData(handle, data);
 
-        const status_t status = finalizeDecryptUnit(uniqueId, &handle, data.readInt32());
+        const status_t status = finalizeDecryptUnit(uniqueId, handle, data.readInt32());
         reply->writeInt32(status);
 
-        clearDecryptHandle(&handle);
+        clearDecryptHandle(handle);
         return DRM_NO_ERROR;
     }
 
@@ -1512,8 +1511,8 @@
 
         const int uniqueId = data.readInt32();
 
-        DecryptHandle handle;
-        readDecryptHandleFromParcelData(&handle, data);
+        sp<DecryptHandle> handle = new DecryptHandle();
+        readDecryptHandleFromParcelData(handle, data);
 
         const uint32_t numBytes = data.readInt32();
         if (numBytes > MAX_BINDER_TRANSACTION_SIZE) {
@@ -1524,13 +1523,13 @@
 
         const off64_t offset = data.readInt64();
 
-        ssize_t result = pread(uniqueId, &handle, buffer, numBytes, offset);
+        ssize_t result = pread(uniqueId, handle, buffer, numBytes, offset);
         reply->writeInt32(result);
         if (0 < result) {
             reply->write(buffer, result);
         }
 
-        clearDecryptHandle(&handle);
+        clearDecryptHandle(handle);
         delete [] buffer, buffer = NULL;
         return DRM_NO_ERROR;
     }
diff --git a/drm/common/include/DrmEngineBase.h b/drm/common/include/DrmEngineBase.h
index 417107f..73f11a4 100644
--- a/drm/common/include/DrmEngineBase.h
+++ b/drm/common/include/DrmEngineBase.h
@@ -59,10 +59,11 @@
 
     int checkRightsStatus(int uniqueId, const String8& path, int action);
 
-    status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+    status_t consumeRights(int uniqueId, sp<DecryptHandle>& decryptHandle, int action,
+            bool reserve);
 
     status_t setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus, int64_t position);
 
     bool validateAction(
             int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -80,27 +81,28 @@
     DrmSupportInfo* getSupportInfo(int uniqueId);
 
     status_t openDecryptSession(
-            int uniqueId, DecryptHandle* decryptHandle,
+            int uniqueId, sp<DecryptHandle>& decryptHandle,
             int fd, off64_t offset, off64_t length, const char* mime);
 
     status_t openDecryptSession(
-            int uniqueId, DecryptHandle* decryptHandle,
+            int uniqueId, sp<DecryptHandle>& decryptHandle,
             const char* uri, const char* mime);
 
-    status_t openDecryptSession(int uniqueId, DecryptHandle* decryptHandle,
+    status_t openDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle,
             const DrmBuffer& buf, const String8& mimeType);
 
-    status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+    status_t closeDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle);
 
-    status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+    status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
             int decryptUnitId, const DrmBuffer* headerInfo);
 
-    status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+    status_t decrypt(int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
 
-    status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+    status_t finalizeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
+            int decryptUnitId);
 
-    ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+    ssize_t pread(int uniqueId, sp<DecryptHandle>& decryptHandle,
             void* buffer, ssize_t numBytes, off64_t offset);
 
 protected:
@@ -265,7 +267,7 @@
      * @return status_t
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
-    virtual status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle,
+    virtual status_t onConsumeRights(int uniqueId, sp<DecryptHandle>& decryptHandle,
             int action, bool reserve) = 0;
 
     /**
@@ -280,7 +282,8 @@
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
     virtual status_t onSetPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) = 0;
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus,
+            int64_t position) = 0;
 
     /**
      * Validates whether an action on the DRM content is allowed or not.
@@ -381,7 +384,7 @@
      *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
      */
     virtual status_t onOpenDecryptSession(
-            int uniqueId, DecryptHandle* decryptHandle,
+            int uniqueId, sp<DecryptHandle>& decryptHandle,
             int fd, off64_t offset, off64_t length) = 0;
 
     /**
@@ -398,7 +401,7 @@
      *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
      */
     virtual status_t onOpenDecryptSession(
-            int /* uniqueId */, DecryptHandle* /* decryptHandle */,
+            int /* uniqueId */, sp<DecryptHandle>& /* decryptHandle */,
             int /* fd */, off64_t /* offset */, off64_t /* length */,
             const char* /* mime */) {
 
@@ -415,7 +418,7 @@
      *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
      */
     virtual status_t onOpenDecryptSession(
-            int uniqueId, DecryptHandle* decryptHandle,
+            int uniqueId, sp<DecryptHandle>& decryptHandle,
             const char* uri) = 0;
 
     /**
@@ -430,7 +433,7 @@
      *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
      */
     virtual status_t onOpenDecryptSession(
-            int /* uniqueId */, DecryptHandle* /* decryptHandle */,
+            int /* uniqueId */, sp<DecryptHandle>& /* decryptHandle */,
             const char* /* uri */, const char* /* mime */) {
 
         return DRM_ERROR_CANNOT_HANDLE;
@@ -447,7 +450,7 @@
      *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
      */
     virtual status_t onOpenDecryptSession(int /* uniqueId */,
-            DecryptHandle* /* decryptHandle */,
+            sp<DecryptHandle>& /* decryptHandle */,
             const DrmBuffer& /* buf */,
             const String8& /* mimeType */) {
         return DRM_ERROR_CANNOT_HANDLE;
@@ -461,7 +464,7 @@
      * @return status_t
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
-    virtual status_t onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0;
+    virtual status_t onCloseDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle) = 0;
 
     /**
      * Initialize decryption for the given unit of the protected content
@@ -473,7 +476,7 @@
      * @return status_t
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
-    virtual status_t onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+    virtual status_t onInitializeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
             int decryptUnitId, const DrmBuffer* headerInfo) = 0;
 
     /**
@@ -493,7 +496,7 @@
      *     DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
      *     DRM_ERROR_DECRYPT for failure.
      */
-    virtual status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+    virtual status_t onDecrypt(int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0;
 
     /**
@@ -506,7 +509,7 @@
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
     virtual status_t onFinalizeDecryptUnit(
-            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0;
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId) = 0;
 
     /**
      * Reads the specified number of bytes from an open DRM file.
@@ -519,7 +522,7 @@
      *
      * @return Number of bytes read. Returns -1 for Failure.
      */
-    virtual ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle,
+    virtual ssize_t onPread(int uniqueId, sp<DecryptHandle>& decryptHandle,
             void* buffer, ssize_t numBytes, off64_t offset) = 0;
 };
 
diff --git a/drm/common/include/IDrmEngine.h b/drm/common/include/IDrmEngine.h
index acc8ed9..1837a11 100644
--- a/drm/common/include/IDrmEngine.h
+++ b/drm/common/include/IDrmEngine.h
@@ -210,7 +210,7 @@
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
     virtual status_t consumeRights(
-            int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) = 0;
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int action, bool reserve) = 0;
 
     /**
      * Informs the DRM Engine about the playback actions performed on the DRM files.
@@ -223,7 +223,7 @@
      * @return status_t
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
-    virtual status_t setPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle,
+    virtual status_t setPlaybackStatus(int uniqueId, sp<DecryptHandle>& decryptHandle,
             int playbackStatus, int64_t position) = 0;
 
     /**
@@ -327,7 +327,7 @@
      *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
      */
     virtual status_t openDecryptSession(
-        int uniqueId, DecryptHandle* decryptHandle,
+        int uniqueId, sp<DecryptHandle>& decryptHandle,
         int fd, off64_t offset, off64_t length, const char* mime) = 0;
 
     /**
@@ -342,7 +342,7 @@
      *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
      */
     virtual status_t openDecryptSession(
-        int uniqueId, DecryptHandle* decryptHandle,
+        int uniqueId, sp<DecryptHandle>& decryptHandle,
         const char* uri, const char* mime) = 0;
 
     /**
@@ -355,7 +355,7 @@
      * @return
      *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
      */
-    virtual status_t openDecryptSession(int uniqueId, DecryptHandle* decryptHandle,
+    virtual status_t openDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle,
             const DrmBuffer& buf, const String8& mimeType) = 0;
 
     /**
@@ -366,7 +366,7 @@
      * @return status_t
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
-    virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0;
+    virtual status_t closeDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle) = 0;
 
     /**
      * Initialize decryption for the given unit of the protected content
@@ -378,7 +378,7 @@
      * @return status_t
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
-    virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+    virtual status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
             int decryptUnitId, const DrmBuffer* headerInfo) = 0;
 
     /**
@@ -398,7 +398,7 @@
      *     DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
      *     DRM_ERROR_DECRYPT for failure.
      */
-    virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+    virtual status_t decrypt(int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0;
 
     /**
@@ -411,7 +411,7 @@
      *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
      */
     virtual status_t finalizeDecryptUnit(
-            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0;
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId) = 0;
 
     /**
      * Reads the specified number of bytes from an open DRM file.
@@ -424,7 +424,7 @@
      *
      * @return Number of bytes read. Returns -1 for Failure.
      */
-    virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+    virtual ssize_t pread(int uniqueId, sp<DecryptHandle>& decryptHandle,
             void* buffer, ssize_t numBytes, off64_t offset) = 0;
 };
 
diff --git a/drm/common/include/IDrmManagerService.h b/drm/common/include/IDrmManagerService.h
index 0376b49..836ae0a 100644
--- a/drm/common/include/IDrmManagerService.h
+++ b/drm/common/include/IDrmManagerService.h
@@ -115,10 +115,11 @@
     virtual int checkRightsStatus(int uniqueId, const String8& path, int action) = 0;
 
     virtual status_t consumeRights(
-            int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) = 0;
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int action, bool reserve) = 0;
 
     virtual status_t setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) = 0;
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus,
+            int64_t position) = 0;
 
     virtual bool validateAction(
             int uniqueId, const String8& path,
@@ -138,28 +139,28 @@
     virtual status_t getAllSupportInfo(
             int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) = 0;
 
-    virtual DecryptHandle* openDecryptSession(
+    virtual sp<DecryptHandle> openDecryptSession(
                 int uniqueId, int fd, off64_t offset,
                 off64_t length, const char* mime) = 0;
 
-    virtual DecryptHandle* openDecryptSession(
+    virtual sp<DecryptHandle> openDecryptSession(
                 int uniqueId, const char* uri, const char* mime) = 0;
 
-    virtual DecryptHandle* openDecryptSession(
+    virtual sp<DecryptHandle> openDecryptSession(
             int uniqueId, const DrmBuffer& buf, const String8& mimeType) = 0;
 
-    virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) = 0;
+    virtual status_t closeDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle) = 0;
 
-    virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+    virtual status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
             int decryptUnitId, const DrmBuffer* headerInfo) = 0;
 
-    virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+    virtual status_t decrypt(int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) = 0;
 
     virtual status_t finalizeDecryptUnit(
-            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) = 0;
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId) = 0;
 
-    virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+    virtual ssize_t pread(int uniqueId, sp<DecryptHandle>& decryptHandle,
             void* buffer, ssize_t numBytes,off64_t offset) = 0;
 };
 
@@ -203,10 +204,10 @@
     virtual int checkRightsStatus(int uniqueId, const String8& path, int action);
 
     virtual status_t consumeRights(
-            int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int action, bool reserve);
 
     virtual status_t setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus, int64_t position);
 
     virtual bool validateAction(
             int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -225,28 +226,28 @@
     virtual status_t getAllSupportInfo(
             int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
 
-    virtual DecryptHandle* openDecryptSession(
+    virtual sp<DecryptHandle> openDecryptSession(
                 int uniqueId, int fd, off64_t offset, off64_t length,
                 const char* mime);
 
-    virtual DecryptHandle* openDecryptSession(
+    virtual sp<DecryptHandle> openDecryptSession(
                 int uniqueId, const char* uri, const char* mime);
 
-    virtual DecryptHandle* openDecryptSession(
+    virtual sp<DecryptHandle> openDecryptSession(
             int uniqueId, const DrmBuffer& buf, const String8& mimeType);
 
-    virtual status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+    virtual status_t closeDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle);
 
-    virtual status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+    virtual status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
             int decryptUnitId, const DrmBuffer* headerInfo);
 
-    virtual status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+    virtual status_t decrypt(int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
 
     virtual status_t finalizeDecryptUnit(
-            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId);
 
-    virtual ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+    virtual ssize_t pread(int uniqueId, sp<DecryptHandle>& decryptHandle,
             void* buffer, ssize_t numBytes, off64_t offset);
 };
 
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index bf04a89..afbcb39 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -267,7 +267,7 @@
 }
 
 status_t DrmManager::consumeRights(
-    int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+    int uniqueId, sp<DecryptHandle>& decryptHandle, int action, bool reserve) {
     status_t result = DRM_ERROR_UNKNOWN;
     Mutex::Autolock _l(mDecryptLock);
     if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
@@ -278,7 +278,7 @@
 }
 
 status_t DrmManager::setPlaybackStatus(
-    int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
+    int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus, int64_t position) {
     status_t result = DRM_ERROR_UNKNOWN;
     Mutex::Autolock _l(mDecryptLock);
     if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
@@ -396,15 +396,15 @@
     return DRM_NO_ERROR;
 }
 
-DecryptHandle* DrmManager::openDecryptSession(
+sp<DecryptHandle> DrmManager::openDecryptSession(
         int uniqueId, int fd, off64_t offset, off64_t length, const char* mime) {
 
     Mutex::Autolock _l(mDecryptLock);
     status_t result = DRM_ERROR_CANNOT_HANDLE;
     Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
 
-    DecryptHandle* handle = new DecryptHandle();
-    if (NULL != handle) {
+    sp<DecryptHandle> handle = new DecryptHandle();
+    if (NULL != handle.get()) {
         handle->decryptId = mDecryptSessionId + 1;
 
         for (size_t index = 0; index < plugInIdList.size(); index++) {
@@ -420,19 +420,19 @@
         }
     }
     if (DRM_NO_ERROR != result) {
-        delete handle; handle = NULL;
+        handle.clear();
     }
     return handle;
 }
 
-DecryptHandle* DrmManager::openDecryptSession(
+sp<DecryptHandle> DrmManager::openDecryptSession(
         int uniqueId, const char* uri, const char* mime) {
     Mutex::Autolock _l(mDecryptLock);
     status_t result = DRM_ERROR_CANNOT_HANDLE;
     Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
 
-    DecryptHandle* handle = new DecryptHandle();
-    if (NULL != handle) {
+    sp<DecryptHandle> handle = new DecryptHandle();
+    if (NULL != handle.get()) {
         handle->decryptId = mDecryptSessionId + 1;
 
         for (size_t index = 0; index < plugInIdList.size(); index++) {
@@ -448,20 +448,20 @@
         }
     }
     if (DRM_NO_ERROR != result) {
-        delete handle; handle = NULL;
+        handle.clear();
         ALOGV("DrmManager::openDecryptSession: no capable plug-in found");
     }
     return handle;
 }
 
-DecryptHandle* DrmManager::openDecryptSession(
+sp<DecryptHandle> DrmManager::openDecryptSession(
         int uniqueId, const DrmBuffer& buf, const String8& mimeType) {
     Mutex::Autolock _l(mDecryptLock);
     status_t result = DRM_ERROR_CANNOT_HANDLE;
     Vector<String8> plugInIdList = mPlugInManager.getPlugInIdList();
 
-    DecryptHandle* handle = new DecryptHandle();
-    if (NULL != handle) {
+    sp<DecryptHandle> handle = new DecryptHandle();
+    if (NULL != handle.get()) {
         handle->decryptId = mDecryptSessionId + 1;
 
         for (size_t index = 0; index < plugInIdList.size(); index++) {
@@ -477,20 +477,19 @@
         }
     }
     if (DRM_NO_ERROR != result) {
-        delete handle;
-        handle = NULL;
+        handle.clear();
         ALOGV("DrmManager::openDecryptSession: no capable plug-in found");
     }
     return handle;
 }
 
-status_t DrmManager::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+status_t DrmManager::closeDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle) {
     Mutex::Autolock _l(mDecryptLock);
     status_t result = DRM_ERROR_UNKNOWN;
     if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
         IDrmEngine* drmEngine = mDecryptSessionMap.valueFor(decryptHandle->decryptId);
         result = drmEngine->closeDecryptSession(uniqueId, decryptHandle);
-        if (DRM_NO_ERROR == result) {
+        if (DRM_NO_ERROR == result && NULL != decryptHandle.get()) {
             mDecryptSessionMap.removeItem(decryptHandle->decryptId);
         }
     }
@@ -498,7 +497,8 @@
 }
 
 status_t DrmManager::initializeDecryptUnit(
-    int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) {
+        int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
+        const DrmBuffer* headerInfo) {
     status_t result = DRM_ERROR_UNKNOWN;
     Mutex::Autolock _l(mDecryptLock);
     if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
@@ -508,7 +508,7 @@
     return result;
 }
 
-status_t DrmManager::decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+status_t DrmManager::decrypt(int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
     status_t result = DRM_ERROR_UNKNOWN;
 
@@ -522,7 +522,7 @@
 }
 
 status_t DrmManager::finalizeDecryptUnit(
-            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId) {
     status_t result = DRM_ERROR_UNKNOWN;
     Mutex::Autolock _l(mDecryptLock);
     if (mDecryptSessionMap.indexOfKey(decryptHandle->decryptId) != NAME_NOT_FOUND) {
@@ -532,7 +532,7 @@
     return result;
 }
 
-ssize_t DrmManager::pread(int uniqueId, DecryptHandle* decryptHandle,
+ssize_t DrmManager::pread(int uniqueId, sp<DecryptHandle>& decryptHandle,
             void* buffer, ssize_t numBytes, off64_t offset) {
     ssize_t result = DECRYPT_FILE_ERROR;
 
diff --git a/drm/drmserver/DrmManager.h b/drm/drmserver/DrmManager.h
index e7cdd36..26222bc 100644
--- a/drm/drmserver/DrmManager.h
+++ b/drm/drmserver/DrmManager.h
@@ -89,10 +89,11 @@
 
     int checkRightsStatus(int uniqueId, const String8& path, int action);
 
-    status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+    status_t consumeRights(int uniqueId, sp<DecryptHandle>& decryptHandle, int action,
+            bool reserve);
 
     status_t setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus, int64_t position);
 
     bool validateAction(
             int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -109,25 +110,26 @@
 
     status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
 
-    DecryptHandle* openDecryptSession(
+    sp<DecryptHandle> openDecryptSession(
             int uniqueId, int fd, off64_t offset, off64_t length, const char* mime);
 
-    DecryptHandle* openDecryptSession(int uniqueId, const char* uri, const char* mime);
+    sp<DecryptHandle> openDecryptSession(int uniqueId, const char* uri, const char* mime);
 
-    DecryptHandle* openDecryptSession(int uniqueId, const DrmBuffer& buf,
+    sp<DecryptHandle> openDecryptSession(int uniqueId, const DrmBuffer& buf,
             const String8& mimeType);
 
-    status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+    status_t closeDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle);
 
-    status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+    status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
             int decryptUnitId, const DrmBuffer* headerInfo);
 
-    status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+    status_t decrypt(int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
 
-    status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+    status_t finalizeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
+            int decryptUnitId);
 
-    ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+    ssize_t pread(int uniqueId, sp<DecryptHandle>& decryptHandle,
             void* buffer, ssize_t numBytes, off64_t offset);
 
     void onInfo(const DrmInfoEvent& event);
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
index dad599b..2600a2c 100644
--- a/drm/drmserver/DrmManagerService.cpp
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -58,22 +58,26 @@
     return drm_perm_labels[index];
 }
 
-bool DrmManagerService::selinuxIsProtectedCallAllowed(pid_t spid, drm_perm_t perm) {
+bool DrmManagerService::selinuxIsProtectedCallAllowed(pid_t spid, const char* ssid, drm_perm_t perm) {
     if (selinux_enabled <= 0) {
         return true;
     }
 
-    char *sctx;
+    char *sctx = NULL;
     const char *selinux_class = "drmservice";
     const char *str_perm = get_perm_label(perm);
 
-    if (getpidcon(spid, &sctx) != 0) {
-        ALOGE("SELinux: getpidcon(pid=%d) failed.\n", spid);
-        return false;
+    if (ssid == NULL) {
+        android_errorWriteLog(0x534e4554, "121035042");
+
+        if (getpidcon(spid, &sctx) != 0) {
+            ALOGE("SELinux: getpidcon(pid=%d) failed.\n", spid);
+            return false;
+        }
     }
 
-    bool allowed = (selinux_check_access(sctx, drmserver_context, selinux_class,
-            str_perm, NULL) == 0);
+    bool allowed = (selinux_check_access(ssid ? ssid : sctx, drmserver_context,
+            selinux_class, str_perm, NULL) == 0);
     freecon(sctx);
 
     return allowed;
@@ -86,10 +90,11 @@
     IPCThreadState* ipcState = IPCThreadState::self();
     uid_t uid = ipcState->getCallingUid();
     pid_t spid = ipcState->getCallingPid();
+    const char* ssid = ipcState->getCallingSid();
 
     for (unsigned int i = 0; i < trustedUids.size(); ++i) {
         if (trustedUids[i] == uid) {
-            return selinuxIsProtectedCallAllowed(spid, perm);
+            return selinuxIsProtectedCallAllowed(spid, ssid, perm);
         }
     }
     return false;
@@ -97,7 +102,9 @@
 
 void DrmManagerService::instantiate() {
     ALOGV("instantiate");
-    defaultServiceManager()->addService(String16("drm.drmManager"), new DrmManagerService());
+    sp<DrmManagerService> service = new DrmManagerService();
+    service->setRequestingSid(true);
+    defaultServiceManager()->addService(String16("drm.drmManager"), service);
 
     if (0 >= trustedUids.size()) {
         // TODO
@@ -206,7 +213,7 @@
 }
 
 status_t DrmManagerService::consumeRights(
-            int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int action, bool reserve) {
     ALOGV("Entering consumeRights");
     if (!isProtectedCallAllowed(CONSUME_RIGHTS)) {
         return DRM_ERROR_NO_PERMISSION;
@@ -215,7 +222,7 @@
 }
 
 status_t DrmManagerService::setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus, int64_t position) {
     ALOGV("Entering setPlaybackStatus");
     if (!isProtectedCallAllowed(SET_PLAYBACK_STATUS)) {
         return DRM_ERROR_NO_PERMISSION;
@@ -262,7 +269,7 @@
     return mDrmManager->getAllSupportInfo(uniqueId, length, drmSupportInfoArray);
 }
 
-DecryptHandle* DrmManagerService::openDecryptSession(
+sp<DecryptHandle> DrmManagerService::openDecryptSession(
             int uniqueId, int fd, off64_t offset, off64_t length, const char* mime) {
     ALOGV("Entering DrmManagerService::openDecryptSession");
     if (isProtectedCallAllowed(OPEN_DECRYPT_SESSION)) {
@@ -272,7 +279,7 @@
     return NULL;
 }
 
-DecryptHandle* DrmManagerService::openDecryptSession(
+sp<DecryptHandle> DrmManagerService::openDecryptSession(
             int uniqueId, const char* uri, const char* mime) {
     ALOGV("Entering DrmManagerService::openDecryptSession with uri");
     if (isProtectedCallAllowed(OPEN_DECRYPT_SESSION)) {
@@ -282,7 +289,7 @@
     return NULL;
 }
 
-DecryptHandle* DrmManagerService::openDecryptSession(
+sp<DecryptHandle> DrmManagerService::openDecryptSession(
             int uniqueId, const DrmBuffer& buf, const String8& mimeType) {
     ALOGV("Entering DrmManagerService::openDecryptSession for streaming");
     if (isProtectedCallAllowed(OPEN_DECRYPT_SESSION)) {
@@ -292,7 +299,7 @@
     return NULL;
 }
 
-status_t DrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+status_t DrmManagerService::closeDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle) {
     ALOGV("Entering closeDecryptSession");
     if (!isProtectedCallAllowed(CLOSE_DECRYPT_SESSION)) {
         return DRM_ERROR_NO_PERMISSION;
@@ -300,7 +307,7 @@
     return mDrmManager->closeDecryptSession(uniqueId, decryptHandle);
 }
 
-status_t DrmManagerService::initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+status_t DrmManagerService::initializeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
             int decryptUnitId, const DrmBuffer* headerInfo) {
     ALOGV("Entering initializeDecryptUnit");
     if (!isProtectedCallAllowed(INITIALIZE_DECRYPT_UNIT)) {
@@ -310,7 +317,7 @@
 }
 
 status_t DrmManagerService::decrypt(
-            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
     ALOGV("Entering decrypt");
     if (!isProtectedCallAllowed(DECRYPT)) {
@@ -320,7 +327,7 @@
 }
 
 status_t DrmManagerService::finalizeDecryptUnit(
-            int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId) {
     ALOGV("Entering finalizeDecryptUnit");
     if (!isProtectedCallAllowed(FINALIZE_DECRYPT_UNIT)) {
         return DRM_ERROR_NO_PERMISSION;
@@ -328,7 +335,7 @@
     return mDrmManager->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
 }
 
-ssize_t DrmManagerService::pread(int uniqueId, DecryptHandle* decryptHandle,
+ssize_t DrmManagerService::pread(int uniqueId, sp<DecryptHandle>& decryptHandle,
             void* buffer, ssize_t numBytes, off64_t offset) {
     ALOGV("Entering pread");
     if (!isProtectedCallAllowed(PREAD)) {
diff --git a/drm/drmserver/DrmManagerService.h b/drm/drmserver/DrmManagerService.h
index 45cee2e..2e27a3c 100644
--- a/drm/drmserver/DrmManagerService.h
+++ b/drm/drmserver/DrmManagerService.h
@@ -60,7 +60,7 @@
 
     static const char *get_perm_label(drm_perm_t perm);
 
-    static bool selinuxIsProtectedCallAllowed(pid_t spid, drm_perm_t perm);
+    static bool selinuxIsProtectedCallAllowed(pid_t spid, const char* ssid, drm_perm_t perm);
 
     static bool isProtectedCallAllowed(drm_perm_t perm);
 
@@ -95,10 +95,11 @@
 
     int checkRightsStatus(int uniqueId, const String8& path,int action);
 
-    status_t consumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+    status_t consumeRights(int uniqueId, sp<DecryptHandle>& decryptHandle, int action,
+            bool reserve);
 
     status_t setPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus, int64_t position);
 
     bool validateAction(int uniqueId, const String8& path,
             int action, const ActionDescription& description);
@@ -115,26 +116,27 @@
 
     status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray);
 
-    DecryptHandle* openDecryptSession(
+    sp<DecryptHandle> openDecryptSession(
         int uniqueId, int fd, off64_t offset, off64_t length, const char *mime);
 
-    DecryptHandle* openDecryptSession(
+    sp<DecryptHandle> openDecryptSession(
         int uniqueId, const char* uri, const char* mime);
 
-    DecryptHandle* openDecryptSession(int uniqueId, const DrmBuffer& buf,
+    sp<DecryptHandle> openDecryptSession(int uniqueId, const DrmBuffer& buf,
             const String8& mimeType);
 
-    status_t closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+    status_t closeDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle);
 
-    status_t initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+    status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
             int decryptUnitId, const DrmBuffer* headerInfo);
 
-    status_t decrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+    status_t decrypt(int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
 
-    status_t finalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+    status_t finalizeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
+            int decryptUnitId);
 
-    ssize_t pread(int uniqueId, DecryptHandle* decryptHandle,
+    ssize_t pread(int uniqueId, sp<DecryptHandle>& decryptHandle,
             void* buffer, ssize_t numBytes, off64_t offset);
 
     virtual status_t dump(int fd, const Vector<String16>& args);
diff --git a/drm/libdrmframework/Android.bp b/drm/libdrmframework/Android.bp
index 43ba72b..940c17d 100644
--- a/drm/libdrmframework/Android.bp
+++ b/drm/libdrmframework/Android.bp
@@ -39,4 +39,3 @@
     cflags: ["-Werror"],
 }
 
-subdirs = ["plugins/*"]
diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp
index c047eb1..b0a441b 100644
--- a/drm/libdrmframework/DrmManagerClientImpl.cpp
+++ b/drm/libdrmframework/DrmManagerClientImpl.cpp
@@ -183,7 +183,7 @@
     status_t status = DRM_ERROR_UNKNOWN;
     if (NULL != decryptHandle.get()) {
         status = getDrmManagerService()->consumeRights(
-                uniqueId, decryptHandle.get(), action, reserve);
+                uniqueId, decryptHandle, action, reserve);
     }
     return status;
 }
@@ -194,7 +194,7 @@
     status_t status = DRM_ERROR_UNKNOWN;
     if (NULL != decryptHandle.get()) {
         status = getDrmManagerService()->setPlaybackStatus(
-                uniqueId, decryptHandle.get(), playbackStatus, position);
+                uniqueId, decryptHandle, playbackStatus, position);
     }
     return status;
 }
@@ -267,7 +267,7 @@
 sp<DecryptHandle> DrmManagerClientImpl::openDecryptSession(
         int uniqueId, const char* uri, const char* mime) {
 
-    DecryptHandle* handle = NULL;
+    sp<DecryptHandle> handle;
     if (NULL != uri) {
         handle = getDrmManagerService()->openDecryptSession(uniqueId, uri, mime);
     }
@@ -284,7 +284,7 @@
     status_t status = DRM_ERROR_UNKNOWN;
     if (NULL != decryptHandle.get()) {
         status = getDrmManagerService()->closeDecryptSession(
-                uniqueId, decryptHandle.get());
+                uniqueId, decryptHandle);
     }
     return status;
 }
@@ -295,7 +295,7 @@
     status_t status = DRM_ERROR_UNKNOWN;
     if ((NULL != decryptHandle.get()) && (NULL != headerInfo)) {
         status = getDrmManagerService()->initializeDecryptUnit(
-                uniqueId, decryptHandle.get(), decryptUnitId, headerInfo);
+                uniqueId, decryptHandle, decryptUnitId, headerInfo);
     }
     return status;
 }
@@ -308,7 +308,7 @@
     if ((NULL != decryptHandle.get()) && (NULL != encBuffer)
         && (NULL != decBuffer) && (NULL != *decBuffer)) {
         status = getDrmManagerService()->decrypt(
-                uniqueId, decryptHandle.get(), decryptUnitId,
+                uniqueId, decryptHandle, decryptUnitId,
                 encBuffer, decBuffer, IV);
     }
     return status;
@@ -319,7 +319,7 @@
     status_t status = DRM_ERROR_UNKNOWN;
     if (NULL != decryptHandle.get()) {
         status = getDrmManagerService()->finalizeDecryptUnit(
-                    uniqueId, decryptHandle.get(), decryptUnitId);
+                    uniqueId, decryptHandle, decryptUnitId);
     }
     return status;
 }
@@ -329,7 +329,7 @@
     ssize_t retCode = INVALID_VALUE;
     if ((NULL != decryptHandle.get()) && (NULL != buffer) && (0 < numBytes)) {
         retCode = getDrmManagerService()->pread(
-                uniqueId, decryptHandle.get(), buffer, numBytes, offset);
+                uniqueId, decryptHandle, buffer, numBytes, offset);
     }
     return retCode;
 }
diff --git a/drm/libdrmframework/plugins/common/Android.bp b/drm/libdrmframework/plugins/common/Android.bp
deleted file mode 100644
index 213e57f..0000000
--- a/drm/libdrmframework/plugins/common/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["util"]
diff --git a/drm/libdrmframework/plugins/forward-lock/Android.bp b/drm/libdrmframework/plugins/forward-lock/Android.bp
deleted file mode 100644
index f884c14..0000000
--- a/drm/libdrmframework/plugins/forward-lock/Android.bp
+++ /dev/null
@@ -1,4 +0,0 @@
-subdirs = [
-    "FwdLockEngine",
-    "internal-format",
-]
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp
index 28a78aa..bb9d7ec 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp
@@ -29,8 +29,7 @@
     srcs: ["src/FwdLockEngine.cpp"],
 
     shared_libs: [
-        "libicui18n",
-        "libicuuc",
+        "libandroidicu",
         "libutils",
         "liblog",
         "libdl",
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
index a571b3a..b62ddb9 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
@@ -198,7 +198,7 @@
  *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
  */
 status_t onConsumeRights(int uniqueId,
-                         DecryptHandle* decryptHandle,
+                         sp<DecryptHandle>& decryptHandle,
                          int action,
                          bool reserve);
 
@@ -215,12 +215,12 @@
  */
 #ifdef USE_64BIT_DRM_API
 status_t onSetPlaybackStatus(int uniqueId,
-                             DecryptHandle* decryptHandle,
+                             sp<DecryptHandle>& decryptHandle,
                              int playbackStatus,
                              int64_t position);
 #else
 status_t onSetPlaybackStatus(int uniqueId,
-                             DecryptHandle* decryptHandle,
+                             sp<DecryptHandle>& decryptHandle,
                              int playbackStatus,
                              int position);
 #endif
@@ -330,11 +330,11 @@
  */
 #ifdef USE_64BIT_DRM_API
 status_t onOpenDecryptSession(int uniqueId,
-                              DecryptHandle* decryptHandle,
+                              sp<DecryptHandle>& decryptHandle,
                               int fd, off64_t offset, off64_t length);
 #else
 status_t onOpenDecryptSession(int uniqueId,
-                              DecryptHandle* decryptHandle,
+                              sp<DecryptHandle>& decryptHandle,
                               int fd, int offset, int length);
 #endif
 
@@ -348,7 +348,7 @@
  *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
  */
 status_t onOpenDecryptSession(int uniqueId,
-                              DecryptHandle* decryptHandle,
+                              sp<DecryptHandle>& decryptHandle,
                               const char* uri);
 
 /**
@@ -360,7 +360,7 @@
  *     Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
  */
 status_t onCloseDecryptSession(int uniqueId,
-                               DecryptHandle* decryptHandle);
+                               sp<DecryptHandle>& decryptHandle);
 
 /**
  * Initialize decryption for the given unit of the protected content.
@@ -373,7 +373,7 @@
  *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
  */
 status_t onInitializeDecryptUnit(int uniqueId,
-                                 DecryptHandle* decryptHandle,
+                                 sp<DecryptHandle>& decryptHandle,
                                  int decryptUnitId,
                                  const DrmBuffer* headerInfo);
 
@@ -394,7 +394,7 @@
  *     DRM_ERROR_DECRYPT for failure.
  */
 status_t onDecrypt(int uniqueId,
-                   DecryptHandle* decryptHandle,
+                   sp<DecryptHandle>& decryptHandle,
                    int decryptUnitId,
                    const DrmBuffer* encBuffer,
                    DrmBuffer** decBuffer);
@@ -416,7 +416,7 @@
  *     DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED,
  *     DRM_ERROR_DECRYPT for failure.
  */
-status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle,
+status_t onDecrypt(int uniqueId, sp<DecryptHandle>& decryptHandle,
                    int decryptUnitId, const DrmBuffer* encBuffer,
                    DrmBuffer** decBuffer, DrmBuffer* IV);
 
@@ -430,7 +430,7 @@
  *     DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success
  */
 status_t onFinalizeDecryptUnit(int uniqueId,
-                               DecryptHandle* decryptHandle,
+                               sp<DecryptHandle>& decryptHandle,
                                int decryptUnitId);
 
 /**
@@ -445,7 +445,7 @@
  * @retval -1 Failure.
  */
 ssize_t onRead(int uniqueId,
-               DecryptHandle* decryptHandle,
+               sp<DecryptHandle>& decryptHandle,
                void* pBuffer,
                int numBytes);
 
@@ -463,12 +463,12 @@
  */
 #ifdef USE_64BIT_DRM_API
 off64_t onLseek(int uniqueId,
-                DecryptHandle* decryptHandle,
+                sp<DecryptHandle>& decryptHandle,
                 off64_t offset,
                 int whence);
 #else
 off_t onLseek(int uniqueId,
-              DecryptHandle* decryptHandle,
+              sp<DecryptHandle>& decryptHandle,
               off_t offset,
               int whence);
 #endif
@@ -486,13 +486,13 @@
  */
 #ifdef USE_64BIT_DRM_API
 ssize_t onPread(int uniqueId,
-                DecryptHandle* decryptHandle,
+                sp<DecryptHandle>& decryptHandle,
                 void* buffer,
                 ssize_t numBytes,
                 off64_t offset);
 #else
 ssize_t onPread(int uniqueId,
-                DecryptHandle* decryptHandle,
+                sp<DecryptHandle>& decryptHandle,
                 void* buffer,
                 ssize_t numBytes,
                 off_t offset);
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
index 73eea89..769de0c 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
@@ -294,7 +294,7 @@
 }
 
 status_t FwdLockEngine::onConsumeRights(int /* uniqueId */,
-                                        DecryptHandle* /* decryptHandle */,
+                                        sp<DecryptHandle>& /* decryptHandle */,
                                         int /* action */,
                                         bool /* reserve */) {
     // No rights consumption
@@ -372,11 +372,13 @@
 }
 
 #ifdef USE_64BIT_DRM_API
-status_t FwdLockEngine::onSetPlaybackStatus(int /* uniqueId */, DecryptHandle* /* decryptHandle */,
-                                            int /* playbackStatus */, int64_t /* position */) {
+status_t FwdLockEngine::onSetPlaybackStatus(int /* uniqueId */,
+        sp<DecryptHandle>& /* decryptHandle */, int /* playbackStatus */,
+        int64_t /* position */) {
 #else
-status_t FwdLockEngine::onSetPlaybackStatus(int /* uniqueId */, DecryptHandle* /* decryptHandle */,
-                                            int /* playbackStatus */, int /* position */) {
+status_t FwdLockEngine::onSetPlaybackStatus(int /* uniqueId */,
+        sp<DecryptHandle>& /* decryptHandle */,
+        int /* playbackStatus */, int /* position */) {
 #endif
     // Not used
     LOG_VERBOSE("FwdLockEngine::onSetPlaybackStatus");
@@ -470,13 +472,13 @@
 
 #ifdef USE_64BIT_DRM_API
 status_t FwdLockEngine::onOpenDecryptSession(int /* uniqueId */,
-                                             DecryptHandle* decryptHandle,
+                                             sp<DecryptHandle>& decryptHandle,
                                              int fd,
                                              off64_t offset,
                                              off64_t /* length */) {
 #else
 status_t FwdLockEngine::onOpenDecryptSession(int /* uniqueId */,
-                                             DecryptHandle* decryptHandle,
+                                             sp<DecryptHandle>& decryptHandle,
                                              int fd,
                                              int offset,
                                              int /* length */) {
@@ -487,7 +489,7 @@
     LOG_VERBOSE("FwdLockEngine::onOpenDecryptSession");
 
     if ((-1 < fd) &&
-        (NULL != decryptHandle) &&
+        (NULL != decryptHandle.get()) &&
         (!decodeSessionMap.isCreated(decryptHandle->decryptId))) {
         fileDesc = dup(fd);
     } else {
@@ -533,12 +535,12 @@
 }
 
 status_t FwdLockEngine::onOpenDecryptSession(int uniqueId,
-                                             DecryptHandle* decryptHandle,
+                                             sp<DecryptHandle>& decryptHandle,
                                              const char* uri) {
     status_t result = DRM_ERROR_CANNOT_HANDLE;
     const char fileTag [] = "file://";
 
-    if (NULL != decryptHandle && NULL != uri && strlen(uri) > sizeof(fileTag)) {
+    if (NULL != decryptHandle.get() && NULL != uri && strlen(uri) > sizeof(fileTag)) {
         String8 uriTag = String8(uri);
         uriTag.toLower();
 
@@ -562,11 +564,11 @@
 }
 
 status_t FwdLockEngine::onCloseDecryptSession(int /* uniqueId */,
-                                              DecryptHandle* decryptHandle) {
+                                              sp<DecryptHandle>& decryptHandle) {
     status_t result = DRM_ERROR_UNKNOWN;
     LOG_VERBOSE("FwdLockEngine::onCloseDecryptSession");
 
-    if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
+    if (NULL != decryptHandle.get() && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
         DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
         if (NULL != session && session->fileDesc > -1) {
             FwdLockFile_detach(session->fileDesc);
@@ -576,7 +578,7 @@
         }
     }
 
-    if (NULL != decryptHandle) {
+    if (NULL != decryptHandle.get()) {
         if (NULL != decryptHandle->decryptInfo) {
             delete decryptHandle->decryptInfo;
             decryptHandle->decryptInfo = NULL;
@@ -584,9 +586,7 @@
 
         decryptHandle->copyControlVector.clear();
         decryptHandle->extendedData.clear();
-
-        delete decryptHandle;
-        decryptHandle = NULL;
+        decryptHandle.clear();
     }
 
     LOG_VERBOSE("FwdLockEngine::onCloseDecryptSession Exit");
@@ -594,7 +594,7 @@
 }
 
 status_t FwdLockEngine::onInitializeDecryptUnit(int /* uniqueId */,
-                                                DecryptHandle* /* decryptHandle */,
+                                                sp<DecryptHandle>& /* decryptHandle */,
                                                 int /* decryptUnitId */,
                                                 const DrmBuffer* /* headerInfo */) {
     ALOGE("FwdLockEngine::onInitializeDecryptUnit is not supported for this DRM scheme");
@@ -603,7 +603,7 @@
 
 status_t FwdLockEngine::onDecrypt(
             int /* uniqueId */,
-            DecryptHandle* /* decryptHandle */,
+            sp<DecryptHandle>& /* decryptHandle */,
             int /* decryptUnitId */,
             const DrmBuffer* /* encBuffer */,
             DrmBuffer** /* decBuffer */,
@@ -613,7 +613,7 @@
 }
 
 status_t FwdLockEngine::onDecrypt(int /* uniqueId */,
-                                  DecryptHandle* /* decryptHandle */,
+                                  sp<DecryptHandle>& /* decryptHandle */,
                                   int /* decryptUnitId */,
                                   const DrmBuffer* /* encBuffer */,
                                   DrmBuffer** /* decBuffer */) {
@@ -622,19 +622,19 @@
 }
 
 status_t FwdLockEngine::onFinalizeDecryptUnit(int /* uniqueId */,
-                                              DecryptHandle* /* decryptHandle */,
+                                              sp<DecryptHandle>& /* decryptHandle */,
                                               int /* decryptUnitId */) {
     ALOGE("FwdLockEngine::onFinalizeDecryptUnit is not supported for this DRM scheme");
     return DRM_ERROR_UNKNOWN;
 }
 
 ssize_t FwdLockEngine::onRead(int /* uniqueId */,
-                              DecryptHandle* decryptHandle,
+                              sp<DecryptHandle>& decryptHandle,
                               void* buffer,
                               int numBytes) {
     ssize_t size = -1;
 
-    if (NULL != decryptHandle &&
+    if (NULL != decryptHandle.get() &&
         decodeSessionMap.isCreated(decryptHandle->decryptId) &&
         NULL != buffer &&
         numBytes > -1) {
@@ -654,15 +654,15 @@
 }
 
 #ifdef USE_64BIT_DRM_API
-off64_t FwdLockEngine::onLseek(int /* uniqueId */, DecryptHandle* decryptHandle,
+off64_t FwdLockEngine::onLseek(int /* uniqueId */, sp<DecryptHandle>& decryptHandle,
                                off64_t offset, int whence) {
 #else
-off_t FwdLockEngine::onLseek(int /* uniqueId */, DecryptHandle* decryptHandle,
+off_t FwdLockEngine::onLseek(int /* uniqueId */, sp<DecryptHandle>& decryptHandle,
                              off_t offset, int whence) {
 #endif
     off_t offval = -1;
 
-    if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
+    if (NULL != decryptHandle.get() && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
         DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
         if (NULL != session && session->fileDesc > -1) {
             offval = FwdLockFile_lseek(session->fileDesc, offset, whence);
@@ -675,13 +675,13 @@
 
 #ifdef USE_64BIT_DRM_API
 ssize_t FwdLockEngine::onPread(int uniqueId,
-                               DecryptHandle* decryptHandle,
+                               sp<DecryptHandle>& decryptHandle,
                                void* buffer,
                                ssize_t numBytes,
                                off64_t offset) {
 #else
 ssize_t FwdLockEngine::onPread(int uniqueId,
-                               DecryptHandle* decryptHandle,
+                               sp<DecryptHandle>& decryptHandle,
                                void* buffer,
                                ssize_t numBytes,
                                off_t offset) {
@@ -690,7 +690,7 @@
 
     DecodeSession* decoderSession = NULL;
 
-    if ((NULL != decryptHandle) &&
+    if ((NULL != decryptHandle.get()) &&
         (NULL != (decoderSession = decodeSessionMap.getValue(decryptHandle->decryptId))) &&
         (NULL != buffer) &&
         (numBytes > -1) &&
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/Android.bp b/drm/libdrmframework/plugins/forward-lock/internal-format/Android.bp
deleted file mode 100644
index 9f58e26..0000000
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/Android.bp
+++ /dev/null
@@ -1,5 +0,0 @@
-subdirs = [
-    "common",
-    "converter",
-    "decoder",
-]
diff --git a/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
index 7b66dc7..4ab5272 100644
--- a/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
+++ b/drm/libdrmframework/plugins/passthru/include/DrmPassthruPlugIn.h
@@ -53,10 +53,11 @@
 
     int onCheckRightsStatus(int uniqueId, const String8& path, int action);
 
-    status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve);
+    status_t onConsumeRights(int uniqueId, sp<DecryptHandle>& decryptHandle,
+            int action, bool reserve);
 
     status_t onSetPlaybackStatus(
-            int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position);
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int playbackStatus, int64_t position);
 
     bool onValidateAction(
             int uniqueId, const String8& path, int action, const ActionDescription& description);
@@ -74,26 +75,25 @@
     DrmSupportInfo* onGetSupportInfo(int uniqueId);
 
     status_t onOpenDecryptSession(
-            int uniqueId, DecryptHandle* decryptHandle, int fd, off64_t offset, off64_t length);
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int fd, off64_t offset,
+            off64_t length);
 
     status_t onOpenDecryptSession(
-            int uniqueId, DecryptHandle* decryptHandle, const char* uri);
+            int uniqueId, sp<DecryptHandle>& decryptHandle, const char* uri);
 
-    status_t onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle);
+    status_t onCloseDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle);
 
-    status_t onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
+    status_t onInitializeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
             int decryptUnitId, const DrmBuffer* headerInfo);
 
-    status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
+    status_t onDecrypt(int uniqueId, sp<DecryptHandle>& decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV);
 
-    status_t onFinalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId);
+    status_t onFinalizeDecryptUnit(int uniqueId, sp<DecryptHandle>& decryptHandle,
+            int decryptUnitId);
 
-    ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle,
+    ssize_t onPread(int uniqueId, sp<DecryptHandle>& decryptHandle,
             void* buffer, ssize_t numBytes, off64_t offset);
-
-private:
-    DecryptHandle* openDecryptSessionImpl();
 };
 
 };
diff --git a/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
index d7f2d28..0fa3478 100644
--- a/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
+++ b/drm/libdrmframework/plugins/passthru/src/DrmPassthruPlugIn.cpp
@@ -183,13 +183,13 @@
 }
 
 status_t DrmPassthruPlugIn::onConsumeRights(int uniqueId,
-            DecryptHandle* /*decryptHandle*/, int /*action*/, bool /*reserve*/) {
+            sp<DecryptHandle>& /*decryptHandle*/, int /*action*/, bool /*reserve*/) {
     ALOGV("DrmPassthruPlugIn::onConsumeRights() : %d", uniqueId);
     return DRM_NO_ERROR;
 }
 
 status_t DrmPassthruPlugIn::onSetPlaybackStatus(int uniqueId,
-            DecryptHandle* /*decryptHandle*/, int /*playbackStatus*/, int64_t /*position*/) {
+            sp<DecryptHandle>& /*decryptHandle*/, int /*playbackStatus*/, int64_t /*position*/) {
     ALOGV("DrmPassthruPlugIn::onSetPlaybackStatus() : %d", uniqueId);
     return DRM_NO_ERROR;
 }
@@ -236,7 +236,8 @@
 }
 
 status_t DrmPassthruPlugIn::onOpenDecryptSession(
-            int uniqueId, DecryptHandle* decryptHandle, int /*fd*/, off64_t /*offset*/, off64_t /*length*/) {
+            int uniqueId, sp<DecryptHandle>& decryptHandle, int /*fd*/, off64_t /*offset*/,
+            off64_t /*length*/) {
     ALOGV("DrmPassthruPlugIn::onOpenDecryptSession() : %d", uniqueId);
 
 #ifdef ENABLE_PASSTHRU_DECRYPTION
@@ -246,36 +247,38 @@
     decryptHandle->decryptInfo = NULL;
     return DRM_NO_ERROR;
 #else
-    (void)(decryptHandle); // unused
+    (void)(decryptHandle.get()); // unused
 #endif
 
     return DRM_ERROR_CANNOT_HANDLE;
 }
 
 status_t DrmPassthruPlugIn::onOpenDecryptSession(
-            int /*uniqueId*/, DecryptHandle* /*decryptHandle*/, const char* /*uri*/) {
+            int /*uniqueId*/, sp<DecryptHandle>& /*decryptHandle*/, const char* /*uri*/) {
     return DRM_ERROR_CANNOT_HANDLE;
 }
 
-status_t DrmPassthruPlugIn::onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
+status_t DrmPassthruPlugIn::onCloseDecryptSession(int uniqueId, sp<DecryptHandle>& decryptHandle) {
     ALOGV("DrmPassthruPlugIn::onCloseDecryptSession() : %d", uniqueId);
-    if (NULL != decryptHandle) {
+    if (NULL != decryptHandle.get()) {
         if (NULL != decryptHandle->decryptInfo) {
             delete decryptHandle->decryptInfo; decryptHandle->decryptInfo = NULL;
         }
-        delete decryptHandle; decryptHandle = NULL;
+        decryptHandle.clear();
     }
     return DRM_NO_ERROR;
 }
 
-status_t DrmPassthruPlugIn::onInitializeDecryptUnit(int uniqueId, DecryptHandle* /*decryptHandle*/,
-            int /*decryptUnitId*/, const DrmBuffer* /*headerInfo*/) {
+status_t DrmPassthruPlugIn::onInitializeDecryptUnit(int uniqueId,
+        sp<DecryptHandle>& /*decryptHandle*/,
+        int /*decryptUnitId*/, const DrmBuffer* /*headerInfo*/) {
     ALOGV("DrmPassthruPlugIn::onInitializeDecryptUnit() : %d", uniqueId);
     return DRM_NO_ERROR;
 }
 
-status_t DrmPassthruPlugIn::onDecrypt(int uniqueId, DecryptHandle* /*decryptHandle*/,
-            int /*decryptUnitId*/, const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* /*IV*/) {
+status_t DrmPassthruPlugIn::onDecrypt(int uniqueId, sp<DecryptHandle>& /*decryptHandle*/,
+        int /*decryptUnitId*/, const DrmBuffer* encBuffer, DrmBuffer** decBuffer,
+        DrmBuffer* /*IV*/) {
     ALOGV("DrmPassthruPlugIn::onDecrypt() : %d", uniqueId);
     /**
      * As a workaround implementation passthru would copy the given
@@ -296,12 +299,12 @@
 }
 
 status_t DrmPassthruPlugIn::onFinalizeDecryptUnit(
-            int uniqueId, DecryptHandle* /*decryptHandle*/, int /*decryptUnitId*/) {
+            int uniqueId, sp<DecryptHandle>& /*decryptHandle*/, int /*decryptUnitId*/) {
     ALOGV("DrmPassthruPlugIn::onFinalizeDecryptUnit() : %d", uniqueId);
     return DRM_NO_ERROR;
 }
 
-ssize_t DrmPassthruPlugIn::onPread(int uniqueId, DecryptHandle* /*decryptHandle*/,
+ssize_t DrmPassthruPlugIn::onPread(int uniqueId, sp<DecryptHandle>& /*decryptHandle*/,
             void* /*buffer*/, ssize_t /*numBytes*/, off64_t /*offset*/) {
     ALOGV("DrmPassthruPlugIn::onPread() : %d", uniqueId);
     return 0;
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 4991e50..d6db1d4 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -27,11 +27,11 @@
         "libmediadrmmetrics_lite",
         "libmediametrics",
         "libmediautils",
-        "libprotobuf-cpp-lite",
         "libstagefright_foundation",
         "libutils",
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
         "libhidlallocatorutils",
         "libhidlbase",
         "libhidltransport",
@@ -59,20 +59,18 @@
     shared_libs: [
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
-        "libbase",
+        "android.hardware.drm@1.2",
         "libbinder",
         "libhidlbase",
         "liblog",
         "libmediametrics",
         "libprotobuf-cpp-lite",
-        "libstagefright_foundation",
         "libutils",
     ],
     cflags: [
         // Suppress unused parameter and no error options. These cause problems
         // with the when using the map type in a proto definition.
         "-Wno-unused-parameter",
-        "-Wno-error",
     ],
 }
 
@@ -92,6 +90,7 @@
     shared_libs: [
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
         "libbase",
         "libbinder",
         "libhidlbase",
@@ -105,7 +104,6 @@
         // Suppress unused parameter and no error options. These cause problems
         // when using the map type in a proto definition.
         "-Wno-unused-parameter",
-        "-Wno-error",
     ],
 }
 
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index 3035c5a..d62ccd6 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -30,16 +30,16 @@
 #include <media/stagefright/MediaErrors.h>
 #include <mediadrm/CryptoHal.h>
 
+using drm::V1_0::BufferType;
+using drm::V1_0::DestinationBuffer;
+using drm::V1_0::ICryptoFactory;
+using drm::V1_0::ICryptoPlugin;
+using drm::V1_0::Mode;
+using drm::V1_0::Pattern;
+using drm::V1_0::SharedBuffer;
+using drm::V1_0::Status;
+using drm::V1_0::SubSample;
 
-using ::android::hardware::drm::V1_0::BufferType;
-using ::android::hardware::drm::V1_0::DestinationBuffer;
-using ::android::hardware::drm::V1_0::ICryptoFactory;
-using ::android::hardware::drm::V1_0::ICryptoPlugin;
-using ::android::hardware::drm::V1_0::Mode;
-using ::android::hardware::drm::V1_0::Pattern;
-using ::android::hardware::drm::V1_0::SharedBuffer;
-using ::android::hardware::drm::V1_0::Status;
-using ::android::hardware::drm::V1_0::SubSample;
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_handle;
 using ::android::hardware::hidl_memory;
@@ -50,6 +50,7 @@
 using ::android::hidl::manager::V1_0::IServiceManager;
 using ::android::sp;
 
+typedef drm::V1_2::Status Status_V1_2;
 
 namespace android {
 
@@ -76,6 +77,18 @@
     }
 }
 
+static status_t toStatusT_1_2(Status_V1_2 status) {
+    switch (status) {
+    case Status_V1_2::ERROR_DRM_SESSION_LOST_STATE:
+        return ERROR_DRM_SESSION_LOST_STATE;;
+    case Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE:
+        return ERROR_DRM_FRAME_TOO_LARGE;
+    case Status_V1_2::ERROR_DRM_INSUFFICIENT_SECURITY:
+        return ERROR_DRM_INSUFFICIENT_SECURITY;
+    default:
+        return toStatusT(static_cast<Status>(status));
+    }
+}
 
 static hidl_vec<uint8_t> toHidlVec(const Vector<uint8_t> &vector) {
     hidl_vec<uint8_t> vec;
@@ -196,6 +209,9 @@
     for (size_t i = 0; i < mFactories.size(); i++) {
         if (mFactories[i]->isCryptoSchemeSupported(uuid)) {
             mPlugin = makeCryptoPlugin(mFactories[i], uuid, data, size);
+            if (mPlugin != NULL) {
+                mPluginV1_2 = drm::V1_2::ICryptoPlugin::castFrom(mPlugin);
+            }
         }
     }
 
@@ -216,6 +232,7 @@
     }
 
     mPlugin.clear();
+    mPluginV1_2.clear();
     return OK;
 }
 
@@ -284,7 +301,7 @@
     ssize_t offset;
     size_t size;
 
-    if (memory == NULL && buffer == NULL) {
+    if (memory == NULL || buffer == NULL) {
         return UNEXPECTED_NULL;
     }
 
@@ -389,21 +406,33 @@
     status_t err = UNKNOWN_ERROR;
     uint32_t bytesWritten = 0;
 
-    Return<void> hResult = mPlugin->decrypt(secure, toHidlArray16(keyId), toHidlArray16(iv), hMode,
-            hPattern, hSubSamples, hSource, offset, hDestination,
-            [&](Status status, uint32_t hBytesWritten, hidl_string hDetailedError) {
-                if (status == Status::OK) {
-                    bytesWritten = hBytesWritten;
-                    *errorDetailMsg = toString8(hDetailedError);
-                }
-                err = toStatusT(status);
-            }
-        );
+    Return<void> hResult;
 
-    if (!hResult.isOk()) {
-        err = DEAD_OBJECT;
+    if (mPluginV1_2 != NULL) {
+        hResult = mPluginV1_2->decrypt_1_2(secure, toHidlArray16(keyId), toHidlArray16(iv),
+                hMode, hPattern, hSubSamples, hSource, offset, hDestination,
+                [&](Status_V1_2 status, uint32_t hBytesWritten, hidl_string hDetailedError) {
+                    if (status == Status_V1_2::OK) {
+                        bytesWritten = hBytesWritten;
+                        *errorDetailMsg = toString8(hDetailedError);
+                    }
+                    err = toStatusT_1_2(status);
+                }
+            );
+    } else {
+        hResult = mPlugin->decrypt(secure, toHidlArray16(keyId), toHidlArray16(iv),
+                hMode, hPattern, hSubSamples, hSource, offset, hDestination,
+                [&](Status status, uint32_t hBytesWritten, hidl_string hDetailedError) {
+                    if (status == Status::OK) {
+                        bytesWritten = hBytesWritten;
+                        *errorDetailMsg = toString8(hDetailedError);
+                    }
+                    err = toStatusT(status);
+                }
+            );
     }
 
+    err = hResult.isOk() ? err : DEAD_OBJECT;
     if (err == OK) {
         return bytesWritten;
     }
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index cf08610..919f4ee 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -23,8 +23,8 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 
-#include <android/hardware/drm/1.0/types.h>
-#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android/hardware/drm/1.2/types.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
 #include <hidl/ServiceManagement.h>
 
 #include <media/EventMetric.h>
@@ -40,15 +40,17 @@
 #include <mediadrm/DrmSessionManager.h>
 
 using drm::V1_0::KeyedVector;
-using drm::V1_0::KeyStatusType;
+using drm::V1_0::KeyRequestType;
 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 drm::V1_2::KeyStatusType;
 using ::android::hardware::drm::V1_1::DrmMetricGroup;
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_string;
@@ -59,6 +61,10 @@
 using ::android::os::PersistableBundle;
 using ::android::sp;
 
+typedef drm::V1_1::KeyRequestType KeyRequestType_V1_1;
+typedef drm::V1_2::Status Status_V1_2;
+typedef drm::V1_2::HdcpLevel HdcpLevel_V1_2;
+
 namespace {
 
 // This constant corresponds to the PROPERTY_DEVICE_UNIQUE_ID constant
@@ -139,26 +145,55 @@
     }
 }
 
-static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel level) {
+static SecurityLevel toHidlSecurityLevel(DrmPlugin::SecurityLevel level) {
     switch(level) {
-    case HdcpLevel::HDCP_NONE:
+    case DrmPlugin::kSecurityLevelSwSecureCrypto:
+        return SecurityLevel::SW_SECURE_CRYPTO;
+    case DrmPlugin::kSecurityLevelSwSecureDecode:
+        return SecurityLevel::SW_SECURE_DECODE;
+    case DrmPlugin::kSecurityLevelHwSecureCrypto:
+        return SecurityLevel::HW_SECURE_CRYPTO;
+    case DrmPlugin::kSecurityLevelHwSecureDecode:
+        return SecurityLevel::HW_SECURE_DECODE;
+    case DrmPlugin::kSecurityLevelHwSecureAll:
+        return SecurityLevel::HW_SECURE_ALL;
+    default:
+        return SecurityLevel::UNKNOWN;
+    }
+}
+
+static DrmPlugin::OfflineLicenseState toOfflineLicenseState(
+        OfflineLicenseState licenseState) {
+    switch(licenseState) {
+    case OfflineLicenseState::USABLE:
+        return DrmPlugin::kOfflineLicenseStateUsable;
+    case OfflineLicenseState::INACTIVE:
+        return DrmPlugin::kOfflineLicenseStateReleased;
+    default:
+        return DrmPlugin::kOfflineLicenseStateUnknown;
+    }
+}
+
+static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel_V1_2 level) {
+    switch(level) {
+    case HdcpLevel_V1_2::HDCP_NONE:
         return DrmPlugin::kHdcpNone;
-    case HdcpLevel::HDCP_V1:
+    case HdcpLevel_V1_2::HDCP_V1:
         return DrmPlugin::kHdcpV1;
-    case HdcpLevel::HDCP_V2:
+    case HdcpLevel_V1_2::HDCP_V2:
         return DrmPlugin::kHdcpV2;
-    case HdcpLevel::HDCP_V2_1:
+    case HdcpLevel_V1_2::HDCP_V2_1:
         return DrmPlugin::kHdcpV2_1;
-    case HdcpLevel::HDCP_V2_2:
+    case HdcpLevel_V1_2::HDCP_V2_2:
         return DrmPlugin::kHdcpV2_2;
-    case HdcpLevel::HDCP_NO_OUTPUT:
+    case HdcpLevel_V1_2::HDCP_V2_3:
+        return DrmPlugin::kHdcpV2_3;
+    case HdcpLevel_V1_2::HDCP_NO_OUTPUT:
         return DrmPlugin::kHdcpNoOutput;
     default:
         return DrmPlugin::kHdcpLevelUnknown;
     }
 }
-
-
 static ::KeyedVector toHidlKeyedVector(const KeyedVector<String8, String8>&
         keyedVector) {
     std::vector<KeyValue> stdKeyedVector;
@@ -199,6 +234,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:
@@ -217,7 +261,7 @@
         return ERROR_DRM_CANNOT_HANDLE;
         break;
     case Status::ERROR_DRM_INVALID_STATE:
-        return ERROR_DRM_TAMPER_DETECTED;
+        return ERROR_DRM_INVALID_STATE;
         break;
     case Status::BAD_VALUE:
         return BAD_VALUE;
@@ -238,6 +282,19 @@
     }
 }
 
+static status_t toStatusT_1_2(Status_V1_2 status) {
+    switch (status) {
+    case Status_V1_2::ERROR_DRM_RESOURCE_CONTENTION:
+        return ERROR_DRM_RESOURCE_CONTENTION;
+    case Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE:
+        return ERROR_DRM_FRAME_TOO_LARGE;
+    case Status_V1_2::ERROR_DRM_INSUFFICIENT_SECURITY:
+        return ERROR_DRM_INSUFFICIENT_SECURITY;
+    default:
+        return toStatusT(static_cast<Status>(status));
+    }
+}
+
 
 Mutex DrmHal::mLock;
 
@@ -297,39 +354,51 @@
 
     setListener(NULL);
     mInitCheck = NO_INIT;
-
-    if (mPlugin != NULL) {
+    if (mPluginV1_2 != NULL) {
+        if (!mPluginV1_2->setListener(NULL).isOk()) {
+            mInitCheck = DEAD_OBJECT;
+        }
+    } else if (mPlugin != NULL) {
         if (!mPlugin->setListener(NULL).isOk()) {
             mInitCheck = DEAD_OBJECT;
         }
     }
     mPlugin.clear();
     mPluginV1_1.clear();
+    mPluginV1_2.clear();
 }
 
 Vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() {
     Vector<sp<IDrmFactory>> factories;
 
-    auto manager = hardware::defaultServiceManager();
+    auto manager = hardware::defaultServiceManager1_2();
 
     if (manager != NULL) {
-        manager->listByInterface(drm::V1_0::IDrmFactory::descriptor,
+        manager->listManifestByInterface(drm::V1_0::IDrmFactory::descriptor,
                 [&factories](const hidl_vec<hidl_string> &registered) {
                     for (const auto &instance : registered) {
                         auto factory = drm::V1_0::IDrmFactory::getService(instance);
                         if (factory != NULL) {
-                            ALOGD("found drm@1.0 IDrmFactory %s", instance.c_str());
                             factories.push_back(factory);
                         }
                     }
                 }
             );
-        manager->listByInterface(drm::V1_1::IDrmFactory::descriptor,
+        manager->listManifestByInterface(drm::V1_1::IDrmFactory::descriptor,
                 [&factories](const hidl_vec<hidl_string> &registered) {
                     for (const auto &instance : registered) {
                         auto factory = drm::V1_1::IDrmFactory::getService(instance);
                         if (factory != NULL) {
-                            ALOGD("found drm@1.1 IDrmFactory %s", instance.c_str());
+                            factories.push_back(factory);
+                        }
+                    }
+                }
+            );
+        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);
                         }
                     }
@@ -448,6 +517,17 @@
 }
 
 Return<void> DrmHal::sendKeysChange(const hidl_vec<uint8_t>& sessionId,
+        const hidl_vec<KeyStatus_V1_0>& keyStatusList_V1_0, bool hasNewUsableKey) {
+    std::vector<KeyStatus> keyStatusVec;
+    for (const auto &keyStatus_V1_0 : keyStatusList_V1_0) {
+        keyStatusVec.push_back({keyStatus_V1_0.keyId,
+                static_cast<KeyStatusType>(keyStatus_V1_0.type)});
+    }
+    hidl_vec<KeyStatus> keyStatusList_V1_2(keyStatusVec);
+    return sendKeysChange_1_2(sessionId, keyStatusList_V1_2, hasNewUsableKey);
+}
+
+Return<void> DrmHal::sendKeysChange_1_2(const hidl_vec<uint8_t>& sessionId,
         const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) {
 
     mEventLock.lock();
@@ -477,6 +557,9 @@
             case KeyStatusType::STATUSPENDING:
                 type = DrmPlugin::kKeyStatusType_StatusPending;
                 break;
+            case KeyStatusType::USABLEINFUTURE:
+                type = DrmPlugin::kKeyStatusType_UsableInFuture;
+                break;
             case KeyStatusType::INTERNALERROR:
             default:
                 type = DrmPlugin::kKeyStatusType_InternalError;
@@ -501,32 +584,80 @@
     return Void();
 }
 
-bool DrmHal::isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) {
-    Mutex::Autolock autoLock(mLock);
+Return<void> DrmHal::sendSessionLostState(
+        const hidl_vec<uint8_t>& sessionId) {
 
-    for (size_t i = 0; i < mFactories.size(); i++) {
+    mEventLock.lock();
+    sp<IDrmClient> listener = mListener;
+    mEventLock.unlock();
+
+    if (listener != NULL) {
+        Parcel obj;
+        writeByteArray(obj, sessionId);
+        Mutex::Autolock lock(mNotifyLock);
+        listener->notify(DrmPlugin::kDrmPluginEventSessionLostState, 0, &obj);
+    }
+    return Void();
+}
+
+status_t DrmHal::matchMimeTypeAndSecurityLevel(const sp<IDrmFactory> &factory,
+                                               const uint8_t uuid[16],
+                                               const String8 &mimeType,
+                                               DrmPlugin::SecurityLevel level,
+                                               bool *isSupported) {
+    *isSupported = false;
+
+    // handle default value cases
+    if (level == DrmPlugin::kSecurityLevelUnknown) {
+        if (mimeType == "") {
+            // isCryptoSchemeSupported(uuid)
+            *isSupported = true;
+        } else {
+            // isCryptoSchemeSupported(uuid, mimeType)
+            *isSupported = factory->isContentTypeSupported(mimeType.string());
+        }
+        return OK;
+    } else if (mimeType == "") {
+        return BAD_VALUE;
+    }
+
+    sp<drm::V1_2::IDrmFactory> factoryV1_2 = drm::V1_2::IDrmFactory::castFrom(factory);
+    if (factoryV1_2 == NULL) {
+        return ERROR_UNSUPPORTED;
+    } else {
+        *isSupported = factoryV1_2->isCryptoSchemeSupported_1_2(uuid,
+                mimeType.string(), toHidlSecurityLevel(level));
+        return OK;
+    }
+}
+
+status_t DrmHal::isCryptoSchemeSupported(const uint8_t uuid[16],
+                                         const String8 &mimeType,
+                                         DrmPlugin::SecurityLevel level,
+                                         bool *isSupported) {
+    Mutex::Autolock autoLock(mLock);
+    *isSupported = false;
+    for (ssize_t i = mFactories.size() - 1; i >= 0; i--) {
         if (mFactories[i]->isCryptoSchemeSupported(uuid)) {
-            if (mimeType != "") {
-                if (mFactories[i]->isContentTypeSupported(mimeType.string())) {
-                    return true;
-                }
-            } else {
-                return true;
-            }
+            return matchMimeTypeAndSecurityLevel(mFactories[i],
+                    uuid, mimeType, level, isSupported);
         }
     }
-    return false;
+    return OK;
 }
 
 status_t DrmHal::createPlugin(const uint8_t uuid[16],
         const String8& appPackageName) {
     Mutex::Autolock autoLock(mLock);
 
-    for (size_t i = 0; i < mFactories.size(); i++) {
+    for (ssize_t i = mFactories.size() - 1; i >= 0; i--) {
         if (mFactories[i]->isCryptoSchemeSupported(uuid)) {
-            mPlugin = makeDrmPlugin(mFactories[i], uuid, appPackageName);
-            if (mPlugin != NULL) {
+            auto plugin = makeDrmPlugin(mFactories[i], uuid, appPackageName);
+            if (plugin != NULL) {
+                mPlugin = plugin;
                 mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin);
+                mPluginV1_2 = drm::V1_2::IDrmPlugin::castFrom(mPlugin);
+                break;
             }
         }
     }
@@ -534,13 +665,22 @@
     if (mPlugin == NULL) {
         mInitCheck = ERROR_UNSUPPORTED;
     } else {
-        if (!mPlugin->setListener(this).isOk()) {
+        mInitCheck = OK;
+        if (mPluginV1_2 != NULL) {
+            if (!mPluginV1_2->setListener(this).isOk()) {
+                mInitCheck = DEAD_OBJECT;
+            }
+        } else if (!mPlugin->setListener(this).isOk()) {
             mInitCheck = DEAD_OBJECT;
-        } else {
-            mInitCheck = OK;
+        }
+        if (mInitCheck != OK) {
+            mPlugin.clear();
+            mPluginV1_1.clear();
+            mPluginV1_2.clear();
         }
     }
 
+
     return mInitCheck;
 }
 
@@ -554,30 +694,15 @@
     Mutex::Autolock autoLock(mLock);
     INIT_CHECK();
 
-    SecurityLevel hSecurityLevel;
+    SecurityLevel hSecurityLevel = toHidlSecurityLevel(level);
     bool setSecurityLevel = true;
 
-    switch(level) {
-    case DrmPlugin::kSecurityLevelSwSecureCrypto:
-        hSecurityLevel = SecurityLevel::SW_SECURE_CRYPTO;
-        break;
-    case DrmPlugin::kSecurityLevelSwSecureDecode:
-        hSecurityLevel = SecurityLevel::SW_SECURE_DECODE;
-        break;
-    case DrmPlugin::kSecurityLevelHwSecureCrypto:
-        hSecurityLevel = SecurityLevel::HW_SECURE_CRYPTO;
-        break;
-    case DrmPlugin::kSecurityLevelHwSecureDecode:
-        hSecurityLevel = SecurityLevel::HW_SECURE_DECODE;
-        break;
-    case DrmPlugin::kSecurityLevelHwSecureAll:
-        hSecurityLevel = SecurityLevel::HW_SECURE_ALL;
-        break;
-    case DrmPlugin::kSecurityLevelMax:
+    if (level == DrmPlugin::kSecurityLevelMax) {
         setSecurityLevel = false;
-        break;
-    default:
-        return ERROR_DRM_CANNOT_HANDLE;
+    } else {
+        if (hSecurityLevel == SecurityLevel::UNKNOWN) {
+            return ERROR_DRM_CANNOT_HANDLE;
+        }
     }
 
     status_t  err = UNKNOWN_ERROR;
@@ -657,6 +782,39 @@
     return DEAD_OBJECT;
 }
 
+static DrmPlugin::KeyRequestType toKeyRequestType(
+        KeyRequestType keyRequestType) {
+    switch (keyRequestType) {
+        case KeyRequestType::INITIAL:
+            return DrmPlugin::kKeyRequestType_Initial;
+            break;
+        case KeyRequestType::RENEWAL:
+            return DrmPlugin::kKeyRequestType_Renewal;
+            break;
+        case KeyRequestType::RELEASE:
+            return DrmPlugin::kKeyRequestType_Release;
+            break;
+        default:
+            return DrmPlugin::kKeyRequestType_Unknown;
+            break;
+    }
+}
+
+static DrmPlugin::KeyRequestType toKeyRequestType_1_1(
+        KeyRequestType_V1_1 keyRequestType) {
+    switch (keyRequestType) {
+        case KeyRequestType_V1_1::NONE:
+            return DrmPlugin::kKeyRequestType_None;
+            break;
+        case KeyRequestType_V1_1::UPDATE:
+            return DrmPlugin::kKeyRequestType_Update;
+            break;
+        default:
+            return toKeyRequestType(static_cast<KeyRequestType>(keyRequestType));
+            break;
+    }
+}
+
 status_t DrmHal::getKeyRequest(Vector<uint8_t> const &sessionId,
         Vector<uint8_t> const &initData, String8 const &mimeType,
         DrmPlugin::KeyType keyType, KeyedVector<String8,
@@ -683,73 +841,51 @@
     ::KeyedVector hOptionalParameters = toHidlKeyedVector(optionalParameters);
 
     status_t err = UNKNOWN_ERROR;
+    Return<void> hResult;
 
-    if (mPluginV1_1 != NULL) {
-        Return<void> hResult =
-            mPluginV1_1->getKeyRequest_1_1(
+    if (mPluginV1_2 != NULL) {
+        hResult = mPluginV1_2->getKeyRequest_1_2(
+                toHidlVec(sessionId), toHidlVec(initData),
+                toHidlString(mimeType), hKeyType, hOptionalParameters,
+                [&](Status_V1_2 status, const hidl_vec<uint8_t>& hRequest,
+                        KeyRequestType_V1_1 hKeyRequestType,
+                        const hidl_string& hDefaultUrl) {
+                    if (status == Status_V1_2::OK) {
+                        request = toVector(hRequest);
+                        defaultUrl = toString8(hDefaultUrl);
+                        *keyRequestType = toKeyRequestType_1_1(hKeyRequestType);
+                    }
+                    err = toStatusT_1_2(status);
+                });
+    } else if (mPluginV1_1 != NULL) {
+        hResult = mPluginV1_1->getKeyRequest_1_1(
                 toHidlVec(sessionId), toHidlVec(initData),
                 toHidlString(mimeType), hKeyType, hOptionalParameters,
                 [&](Status status, const hidl_vec<uint8_t>& hRequest,
-                    drm::V1_1::KeyRequestType hKeyRequestType,
-                    const hidl_string& hDefaultUrl) {
-
-            if (status == Status::OK) {
-                request = toVector(hRequest);
-                defaultUrl = toString8(hDefaultUrl);
-
-                switch (hKeyRequestType) {
-                    case drm::V1_1::KeyRequestType::INITIAL:
-                        *keyRequestType = DrmPlugin::kKeyRequestType_Initial;
-                        break;
-                    case drm::V1_1::KeyRequestType::RENEWAL:
-                        *keyRequestType = DrmPlugin::kKeyRequestType_Renewal;
-                        break;
-                    case drm::V1_1::KeyRequestType::RELEASE:
-                        *keyRequestType = DrmPlugin::kKeyRequestType_Release;
-                        break;
-                    case drm::V1_1::KeyRequestType::NONE:
-                        *keyRequestType = DrmPlugin::kKeyRequestType_None;
-                        break;
-                    case drm::V1_1::KeyRequestType::UPDATE:
-                        *keyRequestType = DrmPlugin::kKeyRequestType_Update;
-                        break;
-                    default:
-                        *keyRequestType = DrmPlugin::kKeyRequestType_Unknown;
-                        break;
-                }
-                err = toStatusT(status);
-            }
-        });
-        return hResult.isOk() ? err : DEAD_OBJECT;
-    }
-
-    Return<void> hResult = mPlugin->getKeyRequest(toHidlVec(sessionId),
-            toHidlVec(initData), toHidlString(mimeType), hKeyType, hOptionalParameters,
-            [&](Status status, const hidl_vec<uint8_t>& hRequest,
-                    drm::V1_0::KeyRequestType hKeyRequestType,
-                    const hidl_string& hDefaultUrl) {
-
-                if (status == Status::OK) {
-                    request = toVector(hRequest);
-                    defaultUrl = toString8(hDefaultUrl);
-
-                    switch (hKeyRequestType) {
-                    case drm::V1_0::KeyRequestType::INITIAL:
-                        *keyRequestType = DrmPlugin::kKeyRequestType_Initial;
-                        break;
-                    case drm::V1_0::KeyRequestType::RENEWAL:
-                        *keyRequestType = DrmPlugin::kKeyRequestType_Renewal;
-                        break;
-                    case drm::V1_0::KeyRequestType::RELEASE:
-                        *keyRequestType = DrmPlugin::kKeyRequestType_Release;
-                        break;
-                    default:
-                        *keyRequestType = DrmPlugin::kKeyRequestType_Unknown;
-                        break;
+                        KeyRequestType_V1_1 hKeyRequestType,
+                        const hidl_string& hDefaultUrl) {
+                    if (status == Status::OK) {
+                        request = toVector(hRequest);
+                        defaultUrl = toString8(hDefaultUrl);
+                        *keyRequestType = toKeyRequestType_1_1(hKeyRequestType);
                     }
                     err = toStatusT(status);
-                }
-            });
+                });
+    } else {
+        hResult = mPlugin->getKeyRequest(
+                toHidlVec(sessionId), toHidlVec(initData),
+                toHidlString(mimeType), hKeyType, hOptionalParameters,
+                [&](Status status, const hidl_vec<uint8_t>& hRequest,
+                        KeyRequestType hKeyRequestType,
+                        const hidl_string& hDefaultUrl) {
+                    if (status == Status::OK) {
+                        request = toVector(hRequest);
+                        defaultUrl = toString8(hDefaultUrl);
+                        *keyRequestType = toKeyRequestType(hKeyRequestType);
+                    }
+                    err = toStatusT(status);
+                });
+    }
 
     err = hResult.isOk() ? err : DEAD_OBJECT;
     keyRequestTimer.SetAttribute(err);
@@ -831,18 +967,33 @@
     INIT_CHECK();
 
     status_t err = UNKNOWN_ERROR;
+    Return<void> hResult;
 
-    Return<void> hResult = mPlugin->getProvisionRequest(
-            toHidlString(certType), toHidlString(certAuthority),
-            [&](Status status, const hidl_vec<uint8_t>& hRequest,
-                    const hidl_string& hDefaultUrl) {
-                if (status == Status::OK) {
-                    request = toVector(hRequest);
-                    defaultUrl = toString8(hDefaultUrl);
+    if (mPluginV1_2 != NULL) {
+        Return<void> hResult = mPluginV1_2->getProvisionRequest_1_2(
+                toHidlString(certType), toHidlString(certAuthority),
+                [&](Status_V1_2 status, const hidl_vec<uint8_t>& hRequest,
+                        const hidl_string& hDefaultUrl) {
+                    if (status == Status_V1_2::OK) {
+                        request = toVector(hRequest);
+                        defaultUrl = toString8(hDefaultUrl);
+                    }
+                    err = toStatusT_1_2(status);
                 }
-                err = toStatusT(status);
-            }
-        );
+            );
+    } else {
+        Return<void> hResult = mPlugin->getProvisionRequest(
+                toHidlString(certType), toHidlString(certAuthority),
+                [&](Status status, const hidl_vec<uint8_t>& hRequest,
+                        const hidl_string& hDefaultUrl) {
+                    if (status == Status::OK) {
+                        request = toVector(hRequest);
+                        defaultUrl = toString8(hDefaultUrl);
+                    }
+                    err = toStatusT(status);
+                }
+            );
+    }
 
     err = hResult.isOk() ? err : DEAD_OBJECT;
     mMetrics.mGetProvisionRequestCounter.Increment(err);
@@ -988,22 +1139,31 @@
     }
     status_t err = UNKNOWN_ERROR;
 
-    if (mPluginV1_1 == NULL) {
-        return ERROR_DRM_CANNOT_HANDLE;
-    }
-
     *connected = DrmPlugin::kHdcpLevelUnknown;
     *max = DrmPlugin::kHdcpLevelUnknown;
 
-    Return<void> hResult = mPluginV1_1->getHdcpLevels(
-            [&](Status status, const HdcpLevel& hConnected, const HdcpLevel& hMax) {
-                if (status == Status::OK) {
-                    *connected = toHdcpLevel(hConnected);
-                    *max = toHdcpLevel(hMax);
-                }
-                err = toStatusT(status);
-            }
-    );
+    Return<void> hResult;
+    if (mPluginV1_2 != NULL) {
+        hResult = mPluginV1_2->getHdcpLevels_1_2(
+                [&](Status_V1_2 status, const HdcpLevel_V1_2& hConnected, const HdcpLevel_V1_2& hMax) {
+                    if (status == Status_V1_2::OK) {
+                        *connected = toHdcpLevel(hConnected);
+                        *max = toHdcpLevel(hMax);
+                    }
+                    err = toStatusT_1_2(status);
+                });
+    } else if (mPluginV1_1 != NULL) {
+        hResult = mPluginV1_1->getHdcpLevels(
+                [&](Status status, const HdcpLevel& hConnected, const HdcpLevel& hMax) {
+                    if (status == Status::OK) {
+                        *connected = toHdcpLevel(static_cast<HdcpLevel_V1_2>(hConnected));
+                        *max = toHdcpLevel(static_cast<HdcpLevel_V1_2>(hMax));
+                    }
+                    err = toStatusT(status);
+                });
+    } else {
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
 
     return hResult.isOk() ? err : DEAD_OBJECT;
 }
@@ -1065,6 +1225,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_UNSUPPORTED;
+    }
+
+    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_UNSUPPORTED;
+    }
+
+    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_UNSUPPORTED;
+    }
+    *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);
@@ -1343,22 +1570,22 @@
 
 void DrmHal::reportFrameworkMetrics() const
 {
-    MediaAnalyticsItem item("mediadrm");
-    item.generateSessionID();
-    item.setPkgName(mMetrics.GetAppPackageName().c_str());
+    std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("mediadrm"));
+    item->generateSessionID();
+    item->setPkgName(mMetrics.GetAppPackageName().c_str());
     String8 vendor;
     String8 description;
     status_t result = getPropertyStringInternal(String8("vendor"), vendor);
     if (result != OK) {
         ALOGE("Failed to get vendor from drm plugin: %d", result);
     } else {
-        item.setCString("vendor", vendor.c_str());
+        item->setCString("vendor", vendor.c_str());
     }
     result = getPropertyStringInternal(String8("description"), description);
     if (result != OK) {
         ALOGE("Failed to get description from drm plugin: %d", result);
     } else {
-        item.setCString("description", description.c_str());
+        item->setCString("description", description.c_str());
     }
 
     std::string serializedMetrics;
@@ -1369,9 +1596,9 @@
     std::string b64EncodedMetrics = toBase64StringNoPad(serializedMetrics.data(),
                                                         serializedMetrics.size());
     if (!b64EncodedMetrics.empty()) {
-        item.setCString("serialized_metrics", b64EncodedMetrics.c_str());
+        item->setCString("serialized_metrics", b64EncodedMetrics.c_str());
     }
-    if (!item.selfrecord()) {
+    if (!item->selfrecord()) {
         ALOGE("Failed to self record framework metrics");
     }
 }
diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp
index 4fed707..3080802 100644
--- a/drm/libmediadrm/DrmMetrics.cpp
+++ b/drm/libmediadrm/DrmMetrics.cpp
@@ -32,7 +32,7 @@
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::drm::V1_0::EventType;
-using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::hardware::drm::V1_2::KeyStatusType;
 using ::android::hardware::drm::V1_1::DrmMetricGroup;
 using ::android::os::PersistableBundle;
 
diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp
index 509961f..51274d1 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> {
@@ -80,18 +83,22 @@
         return reply.readInt32();
     }
 
-    virtual bool isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) {
+    virtual status_t isCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType,
+            DrmPlugin::SecurityLevel level, bool *isSupported) {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
         data.write(uuid, 16);
         data.writeString8(mimeType);
+        data.writeInt32(level);
+
         status_t status = remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply);
         if (status != OK) {
             ALOGE("isCryptoSchemeSupported: binder call failed: %d", status);
-            return false;
+            return status;
         }
+        *isSupported = static_cast<bool>(reply.readInt32());
 
-        return reply.readInt32() != 0;
+        return reply.readInt32();
     }
 
     virtual status_t createPlugin(const uint8_t uuid[16],
@@ -120,11 +127,11 @@
         return reply.readInt32();
     }
 
-    virtual status_t openSession(DrmPlugin::SecurityLevel securityLevel,
+    virtual status_t openSession(DrmPlugin::SecurityLevel level,
             Vector<uint8_t> &sessionId) {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-        data.writeInt32(securityLevel);
+        data.writeInt32(level);
 
         status_t status = remote()->transact(OPEN_SESSION, data, &reply);
         if (status != OK) {
@@ -376,6 +383,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());
@@ -719,7 +772,12 @@
             uint8_t uuid[16];
             data.read(uuid, sizeof(uuid));
             String8 mimeType = data.readString8();
-            reply->writeInt32(isCryptoSchemeSupported(uuid, mimeType));
+            DrmPlugin::SecurityLevel level =
+                    static_cast<DrmPlugin::SecurityLevel>(data.readInt32());
+            bool isSupported = false;
+            status_t result = isCryptoSchemeSupported(uuid, mimeType, level, &isSupported);
+            reply->writeInt32(isSupported);
+            reply->writeInt32(result);
             return OK;
         }
 
@@ -980,6 +1038,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/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index 5cb48bf..8cd6f96 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -34,17 +34,17 @@
 status_t reportVendorMetrics(const std::string& metrics,
                              const String8& name,
                              const String8& appPackageName) {
-    MediaAnalyticsItem analyticsItem(name.c_str());
-    analyticsItem.generateSessionID();
+    std::unique_ptr<MediaAnalyticsItem> analyticsItem(MediaAnalyticsItem::create(name.c_str()));
+    analyticsItem->generateSessionID();
 
     std::string app_package_name(appPackageName.c_str(), appPackageName.size());
-    analyticsItem.setPkgName(app_package_name);
+    analyticsItem->setPkgName(app_package_name);
     if (metrics.size() > 0) {
-        analyticsItem.setCString(kSerializedMetricsField, metrics.c_str());
+        analyticsItem->setCString(kSerializedMetricsField, metrics.c_str());
     }
 
-    if (!analyticsItem.selfrecord()) {
-      ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem.getSessionID());
+    if (!analyticsItem->selfrecord()) {
+      ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem->getSessionID());
     }
 
     return OK;
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
index 66c906f..9e0115e 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",
@@ -33,7 +34,6 @@
         // Suppress unused parameter and no error options. These cause problems
         // when using the map type in a proto definition.
         "-Wno-unused-parameter",
-        "-Wno-error",
     ]
 }
 
diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp
index 64aa9d0..5c8a1b0 100644
--- a/drm/libmediadrm/tests/DrmMetrics_test.cpp
+++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp
@@ -30,7 +30,7 @@
 using ::android::drm_metrics::DrmFrameworkMetrics;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::drm::V1_0::EventType;
-using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::hardware::drm::V1_2::KeyStatusType;
 using ::android::hardware::drm::V1_0::Status;
 using ::android::hardware::drm::V1_1::DrmMetricGroup;
 using ::android::os::PersistableBundle;
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
index 1558e8b..bf35224 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
@@ -65,7 +65,20 @@
     *plugin = new ClearKeyCasPlugin(appData, callback);
     return OK;
 }
-///////////////////////////////////////////////////////////////////////////////
+
+status_t ClearKeyCasFactory::createPlugin(
+        int32_t CA_system_id,
+        void *appData,
+        CasPluginCallbackExt callback,
+        CasPlugin **plugin) {
+    if (!isSystemIdSupported(CA_system_id)) {
+        return BAD_VALUE;
+    }
+
+    *plugin = new ClearKeyCasPlugin(appData, callback);
+    return OK;
+}
+////////////////////////////////////////////////////////////////////////////////
 bool ClearKeyDescramblerFactory::isSystemIdSupported(
         int32_t CA_system_id) const {
     return CA_system_id == sClearKeySystemId;
@@ -84,7 +97,13 @@
 ///////////////////////////////////////////////////////////////////////////////
 ClearKeyCasPlugin::ClearKeyCasPlugin(
         void *appData, CasPluginCallback callback)
-    : mCallback(callback), mAppData(appData) {
+    : mCallback(callback), mCallbackExt(NULL), mAppData(appData) {
+    ALOGV("CTOR");
+}
+
+ClearKeyCasPlugin::ClearKeyCasPlugin(
+        void *appData, CasPluginCallbackExt callback)
+    : mCallback(NULL), mCallbackExt(callback), mAppData(appData) {
     ALOGV("CTOR");
 }
 
@@ -167,11 +186,30 @@
     // Echo the received event to the callback.
     // Clear key plugin doesn't use any event, echo'ing for testing only.
     if (mCallback != NULL) {
-        mCallback((void*)mAppData, event, arg, (uint8_t*)eventData.data(), eventData.size());
+        mCallback((void*)mAppData, event, arg, (uint8_t*)eventData.data(),
+                    eventData.size());
+    } else if (mCallbackExt != NULL) {
+        mCallbackExt((void*)mAppData, event, arg, (uint8_t*)eventData.data(),
+                    eventData.size(), NULL);
     }
     return OK;
 }
 
+status_t ClearKeyCasPlugin::sendSessionEvent(
+        const CasSessionId &sessionId, int32_t event,
+        int arg, const CasData &eventData) {
+    ALOGV("sendSessionEvent: sessionId=%s, event=%d, arg=%d",
+          sessionIdToString(sessionId).string(), event, arg);
+    // Echo the received event to the callback.
+    // Clear key plugin doesn't use any event, echo'ing for testing only.
+    if (mCallbackExt != NULL) {
+        mCallbackExt((void*)mAppData, event, arg, (uint8_t*)eventData.data(),
+                    eventData.size(), &sessionId);
+    }
+
+    return OK;
+}
+
 status_t ClearKeyCasPlugin::provision(const String8 &str) {
     ALOGV("provision: provisionString=%s", str.string());
     Mutex::Autolock lock(mKeyFetcherLock);
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
index 389e172..f48d5b1 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
@@ -47,6 +47,11 @@
             void *appData,
             CasPluginCallback callback,
             CasPlugin **plugin) override;
+    virtual status_t createPlugin(
+            int32_t CA_system_id,
+            void *appData,
+            CasPluginCallbackExt callback,
+            CasPlugin **plugin) override;
 };
 
 class ClearKeyDescramblerFactory : public DescramblerFactory {
@@ -63,6 +68,7 @@
 class ClearKeyCasPlugin : public CasPlugin {
 public:
     ClearKeyCasPlugin(void *appData, CasPluginCallback callback);
+    ClearKeyCasPlugin(void *appData, CasPluginCallbackExt callback);
     virtual ~ClearKeyCasPlugin();
 
     virtual status_t setPrivateData(
@@ -85,6 +91,10 @@
     virtual status_t sendEvent(
             int32_t event, int32_t arg, const CasData &eventData) override;
 
+    virtual status_t sendSessionEvent(
+            const CasSessionId &sessionId,
+            int32_t event, int32_t arg, const CasData &eventData) override;
+
     virtual status_t provision(const String8 &str) override;
 
     virtual status_t refreshEntitlements(
@@ -94,6 +104,7 @@
     Mutex mKeyFetcherLock;
     std::unique_ptr<KeyFetcher> mKeyFetcher;
     CasPluginCallback mCallback;
+    CasPluginCallbackExt mCallbackExt;
     void* mAppData;
 };
 
diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.cpp b/drm/mediacas/plugins/mock/MockCasPlugin.cpp
index 8404a83..2964791 100644
--- a/drm/mediacas/plugins/mock/MockCasPlugin.cpp
+++ b/drm/mediacas/plugins/mock/MockCasPlugin.cpp
@@ -60,6 +60,19 @@
     return OK;
 }
 
+status_t MockCasFactory::createPlugin(
+        int32_t CA_system_id,
+        void* /*appData*/,
+        CasPluginCallbackExt /*callback*/,
+        CasPlugin **plugin) {
+    if (!isSystemIdSupported(CA_system_id)) {
+        return BAD_VALUE;
+    }
+
+    *plugin = new MockCasPlugin();
+    return OK;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 bool MockDescramblerFactory::isSystemIdSupported(int32_t CA_system_id) const {
@@ -170,6 +183,16 @@
     return OK;
 }
 
+status_t MockCasPlugin::sendSessionEvent(
+        const CasSessionId &sessionId, int32_t event,
+        int /*arg*/, const CasData& /*eventData*/) {
+    ALOGV("sendSessionEvent: sessionId=%s, event=%d",
+          arrayToString(sessionId).string(), event);
+    Mutex::Autolock lock(mLock);
+
+    return OK;
+}
+
 status_t MockCasPlugin::provision(const String8 &str) {
     ALOGV("provision: provisionString=%s", str.string());
     Mutex::Autolock lock(mLock);
diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.h b/drm/mediacas/plugins/mock/MockCasPlugin.h
index 8106990..74b540c 100644
--- a/drm/mediacas/plugins/mock/MockCasPlugin.h
+++ b/drm/mediacas/plugins/mock/MockCasPlugin.h
@@ -42,6 +42,11 @@
             void *appData,
             CasPluginCallback callback,
             CasPlugin **plugin) override;
+    virtual status_t createPlugin(
+            int32_t CA_system_id,
+            void *appData,
+            CasPluginCallbackExt callback,
+            CasPlugin **plugin) override;
 };
 
 class MockDescramblerFactory : public DescramblerFactory {
@@ -80,7 +85,11 @@
     virtual status_t sendEvent(
             int32_t event, int32_t arg, const CasData &eventData) override;
 
-    virtual status_t provision(const String8 &str) override;
+    virtual status_t sendSessionEvent(
+            const CasSessionId &sessionId,
+            int32_t event, int32_t arg, const CasData &eventData) override;
+
+     virtual status_t provision(const String8 &str) override;
 
     virtual status_t refreshEntitlements(
             int32_t refreshType, const CasData &refreshData) override;
diff --git a/drm/mediadrm/Android.bp b/drm/mediadrm/Android.bp
deleted file mode 100644
index b9f07f1..0000000
--- a/drm/mediadrm/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["plugins/*"]
diff --git a/drm/mediadrm/plugins/clearkey/common/Utils.cpp b/drm/mediadrm/plugins/clearkey/common/Utils.cpp
index 93c643b..d48b0a8 100644
--- a/drm/mediadrm/plugins/clearkey/common/Utils.cpp
+++ b/drm/mediadrm/plugins/clearkey/common/Utils.cpp
@@ -27,4 +27,18 @@
     return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0;
 }
 
+std::string ByteArrayToHexString(const uint8_t* in_buffer, size_t length) {
+    static const char kHexChars[] = "0123456789ABCDEF";
+
+    // Each input byte creates two output hex characters.
+    std::string out_buffer(length * 2, '\0');
+
+    for (size_t i = 0; i < length; ++i) {
+        char byte = in_buffer[i];
+        out_buffer[(i * 2)] = kHexChars[(byte >> 4) & 0xf];
+        out_buffer[(i * 2) + 1] = kHexChars[byte & 0xf];
+    }
+    return out_buffer;
+}
+
 } // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/common/include/Utils.h b/drm/mediadrm/plugins/clearkey/common/include/Utils.h
index 2543124..aa571c0 100644
--- a/drm/mediadrm/plugins/clearkey/common/include/Utils.h
+++ b/drm/mediadrm/plugins/clearkey/common/include/Utils.h
@@ -17,14 +17,16 @@
 #ifndef CLEARKEY_UTILS_H_
 #define CLEARKEY_UTILS_H_
 
+#include <string>
 #include <utils/Vector.h>
 
+namespace android {
 // Add a comparison operator for this Vector specialization so that it can be
 // used as a key in a KeyedVector.
-namespace android {
-
 bool operator<(const Vector<uint8_t> &lhs, const Vector<uint8_t> &rhs);
 
+std::string ByteArrayToHexString(const uint8_t* in_buffer, size_t length);
+
 } // namespace android
 
 #define UNUSED(x) (void)(x);
diff --git a/drm/mediadrm/plugins/clearkey/default/Android.bp b/drm/mediadrm/plugins/clearkey/default/Android.bp
index 7ba5708..9803d32 100644
--- a/drm/mediadrm/plugins/clearkey/default/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/default/Android.bp
@@ -61,7 +61,3 @@
     },
 }
 
-//########################################################################
-// Build unit tests
-
-subdirs = ["tests"]
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 341d4f6..e91e918 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
@@ -14,8 +14,8 @@
 // limitations under the License.
 //
 
-cc_binary {
-    name: "android.hardware.drm@1.1-service.clearkey",
+cc_defaults {
+    name: "clearkey_service_defaults",
     vendor: true,
 
     srcs: [
@@ -25,23 +25,24 @@
         "CreatePluginFactories.cpp",
         "CryptoFactory.cpp",
         "CryptoPlugin.cpp",
+        "DeviceFiles.cpp",
         "DrmFactory.cpp",
         "DrmPlugin.cpp",
         "InitDataParser.cpp",
         "JsonWebKey.cpp",
+        "MemoryFileSystem.cpp",
         "Session.cpp",
         "SessionLibrary.cpp",
-        "service.cpp",
     ],
 
     relative_install_path: "hw",
 
     cflags: ["-Wall", "-Werror"],
-    init_rc: ["android.hardware.drm@1.1-service.clearkey.rc"],
 
     shared_libs: [
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
         "libbase",
         "libbinder",
         "libcrypto",
@@ -49,11 +50,13 @@
         "libhidlmemory",
         "libhidltransport",
         "liblog",
+        "libprotobuf-cpp-lite",
         "libutils",
     ],
 
     static_libs: [
         "libclearkeycommon",
+        "libclearkeydevicefiles-protos",
         "libjsmn",
     ],
 
@@ -65,4 +68,26 @@
         integer_overflow: true,
     },
 }
+cc_library_static {
+    name: "libclearkeydevicefiles-protos",
+    vendor: true,
 
+    proto: {
+        export_proto_headers: true,
+        type: "lite",
+    },
+    srcs: ["protos/DeviceFiles.proto"],
+}
+cc_binary {
+    name: "android.hardware.drm@1.2-service.clearkey",
+    defaults: ["clearkey_service_defaults"],
+    srcs: ["service.cpp"],
+    init_rc: ["android.hardware.drm@1.2-service.clearkey.rc"],
+}
+cc_binary {
+    name: "android.hardware.drm@1.2-service-lazy.clearkey",
+    overrides: ["android.hardware.drm@1.2-service.clearkey"],
+    defaults: ["clearkey_service_defaults"],
+    srcs: ["serviceLazy.cpp"],
+    init_rc: ["android.hardware.drm@1.2-service-lazy.clearkey.rc"],
+}
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 cd2224d..3ecf6d5 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;
@@ -42,10 +42,40 @@
     return Void();
 }
 
+Return<void> CryptoPlugin::decrypt(
+    bool secure,
+    const hidl_array<uint8_t, 16>& keyId,
+    const hidl_array<uint8_t, 16>& iv,
+    Mode mode,
+    const Pattern& pattern,
+    const hidl_vec<SubSample>& subSamples,
+    const SharedBuffer& source,
+    uint64_t offset,
+    const DestinationBuffer& destination,
+    decrypt_cb _hidl_cb) {
+
+  Status status = Status::ERROR_DRM_UNKNOWN;
+  hidl_string detailedError;
+  uint32_t bytesWritten = 0;
+
+  Return<void> hResult = decrypt_1_2(
+      secure, keyId, iv, mode, pattern, subSamples, source, offset, destination,
+      [&](Status_V1_2 hStatus, uint32_t hBytesWritten, hidl_string hDetailedError) {
+        status = toStatus_1_0(hStatus);
+        bytesWritten = hBytesWritten;
+        detailedError = hDetailedError;
+      }
+    );
+
+  status = hResult.isOk() ? status : Status::ERROR_DRM_CANNOT_HANDLE;
+  _hidl_cb(status, bytesWritten, detailedError);
+  return Void();
+}
+
 // Returns negative values for error code and positive values for the size of
 // decrypted data.  In theory, the output size can be larger than the input
 // size, but in practice this will never happen for AES-CTR.
-Return<void> CryptoPlugin::decrypt(
+Return<void> CryptoPlugin::decrypt_1_2(
         bool secure,
         const hidl_array<uint8_t, KEY_ID_SIZE>& keyId,
         const hidl_array<uint8_t, KEY_IV_SIZE>& iv,
@@ -55,17 +85,17 @@
         const SharedBuffer& source,
         uint64_t offset,
         const DestinationBuffer& destination,
-        decrypt_cb _hidl_cb) {
+        decrypt_1_2_cb _hidl_cb) {
     UNUSED(pattern);
 
     if (secure) {
-        _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0,
+        _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0,
             "Secure decryption is not supported with ClearKey.");
         return Void();
     }
 
     if (mSharedBufferMap.find(source.bufferId) == mSharedBufferMap.end()) {
-      _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0,
+      _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0,
                "source decrypt buffer base not set");
       return Void();
     }
@@ -73,24 +103,24 @@
     if (destination.type == BufferType::SHARED_MEMORY) {
       const SharedBuffer& dest = destination.nonsecureMemory;
       if (mSharedBufferMap.find(dest.bufferId) == mSharedBufferMap.end()) {
-        _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0,
+        _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0,
                  "destination decrypt buffer base not set");
         return Void();
       }
     } else {
-        _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0,
+        _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0,
                  "destination type not supported");
         return Void();
     }
 
     sp<IMemory> sourceBase = mSharedBufferMap[source.bufferId];
     if (sourceBase == nullptr) {
-        _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "source is a nullptr");
+        _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "source is a nullptr");
         return Void();
     }
 
     if (source.offset + offset + source.size > sourceBase->getSize()) {
-        _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size");
+        _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size");
         return Void();
     }
 
@@ -102,18 +132,19 @@
     const SharedBuffer& destBuffer = destination.nonsecureMemory;
     sp<IMemory> destBase = mSharedBufferMap[destBuffer.bufferId];
     if (destBase == nullptr) {
-        _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "destination is a nullptr");
+        _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "destination is a nullptr");
         return Void();
     }
 
     base = static_cast<uint8_t *>(static_cast<void *>(destBase->getPointer()));
 
     if (destBuffer.offset + destBuffer.size > destBase->getSize()) {
-        _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size");
+        _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "invalid buffer size");
         return Void();
     }
     destPtr = static_cast<void *>(base + destination.nonsecureMemory.offset);
 
+
     // Calculate the output buffer size and determine if any subsamples are
     // encrypted.
     size_t destSize = 0;
@@ -121,11 +152,11 @@
     for (size_t i = 0; i < subSamples.size(); i++) {
         const SubSample &subSample = subSamples[i];
         if (__builtin_add_overflow(destSize, subSample.numBytesOfClearData, &destSize)) {
-            _hidl_cb(Status::BAD_VALUE, 0, "subsample clear size overflow");
+            _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample clear size overflow");
             return Void();
         }
         if (__builtin_add_overflow(destSize, subSample.numBytesOfEncryptedData, &destSize)) {
-            _hidl_cb(Status::BAD_VALUE, 0, "subsample encrypted size overflow");
+            _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample encrypted size overflow");
             return Void();
         }
         if (subSample.numBytesOfEncryptedData > 0) {
@@ -134,13 +165,13 @@
     }
 
     if (destSize > destBuffer.size) {
-        _hidl_cb(Status::BAD_VALUE, 0, "subsample sum too large");
+        _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample sum too large");
         return Void();
     }
 
     if (mode == Mode::UNENCRYPTED) {
         if (haveEncryptedSubsamples) {
-            _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0,
+            _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0,
                     "Encrypted subsamples found in allegedly unencrypted data.");
             return Void();
         }
@@ -156,22 +187,21 @@
             }
         }
 
-        _hidl_cb(Status::OK, static_cast<ssize_t>(offset), "");
+        _hidl_cb(Status_V1_2::OK, static_cast<ssize_t>(offset), "");
         return Void();
     } else if (mode == Mode::AES_CTR) {
         size_t bytesDecrypted;
-        Status res = mSession->decrypt(keyId.data(), iv.data(), srcPtr,
+        Status_V1_2 res = mSession->decrypt(keyId.data(), iv.data(), srcPtr,
                 static_cast<uint8_t*>(destPtr), toVector(subSamples), &bytesDecrypted);
-        if (res == Status::OK) {
-            _hidl_cb(Status::OK, static_cast<ssize_t>(bytesDecrypted), "");
+        if (res == Status_V1_2::OK) {
+            _hidl_cb(Status_V1_2::OK, static_cast<ssize_t>(bytesDecrypted), "");
             return Void();
         } else {
-            _hidl_cb(Status::ERROR_DRM_DECRYPT, static_cast<ssize_t>(res),
-                    "Decryption Error");
+            _hidl_cb(res, 0, "Decryption Error");
             return Void();
         }
     } else {
-        _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0,
+        _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0,
                 "Selected encryption mode is not supported by the ClearKey DRM Plugin.");
         return Void();
     }
@@ -191,7 +221,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
new file mode 100644
index 0000000..2415b6f
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/DeviceFiles.cpp
@@ -0,0 +1,252 @@
+// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
+// source code may only be used and distributed under the Widevine Master
+// License Agreement.
+
+#include <utils/Log.h>
+
+#include <string>
+#include <sys/stat.h>
+
+#include "DeviceFiles.h"
+#include "Utils.h"
+
+#include <openssl/sha.h>
+
+// Protobuf generated classes.
+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";
+
+bool Hash(const std::string& data, std::string* hash) {
+    if (!hash) return false;
+
+    hash->resize(SHA256_DIGEST_LENGTH);
+
+    const unsigned char* input = reinterpret_cast<const unsigned char*>(data.data());
+    unsigned char* output = reinterpret_cast<unsigned char*>(&(*hash)[0]);
+    SHA256(input, data.size(), output);
+    return true;
+}
+
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace clearkey {
+
+bool DeviceFiles::StoreLicense(
+        const std::string& keySetId, LicenseState state,
+        const std::string& licenseResponse) {
+
+    OfflineFile file;
+    file.set_type(OfflineFile::LICENSE);
+    file.set_version(OfflineFile::VERSION_1);
+
+    License* license = file.mutable_license();
+    switch (state) {
+        case kLicenseStateActive:
+            license->set_state(License_LicenseState_ACTIVE);
+            license->set_license(licenseResponse);
+            break;
+        case kLicenseStateReleasing:
+            license->set_state(License_LicenseState_RELEASING);
+            license->set_license(licenseResponse);
+            break;
+        default:
+            ALOGW("StoreLicense: Unknown license state: %u", state);
+            return false;
+    }
+
+    std::string serializedFile;
+    file.SerializeToString(&serializedFile);
+
+    return StoreFileWithHash(keySetId + kLicenseFileNameExt, serializedFile);
+}
+
+bool DeviceFiles::StoreFileWithHash(const std::string& fileName,
+        const std::string& serializedFile) {
+    std::string hash;
+    if (!Hash(serializedFile, &hash)) {
+        ALOGE("StoreFileWithHash: Failed to compute hash");
+        return false;
+    }
+
+    HashedFile hashFile;
+    hashFile.set_file(serializedFile);
+    hashFile.set_hash(hash);
+
+    std::string serializedHashFile;
+    hashFile.SerializeToString(&serializedHashFile);
+
+    return StoreFileRaw(fileName, serializedHashFile);
+}
+
+bool DeviceFiles::StoreFileRaw(const std::string& fileName, const std::string& serializedHashFile) {
+    MemoryFileSystem::MemoryFile memFile;
+    memFile.setFileName(fileName);
+    memFile.setContent(serializedHashFile);
+    memFile.setFileSize(serializedHashFile.size());
+    size_t len = mFileHandle.Write(fileName, memFile);
+
+    if (len != static_cast<size_t>(serializedHashFile.size())) {
+        ALOGE("StoreFileRaw: Failed to write %s", fileName.c_str());
+        ALOGD("StoreFileRaw: expected=%zd, actual=%zu", serializedHashFile.size(), len);
+        return false;
+    }
+
+    ALOGD("StoreFileRaw: wrote %zu bytes to %s", serializedHashFile.size(), fileName.c_str());
+    return true;
+}
+
+bool DeviceFiles::RetrieveLicense(
+    const std::string& keySetId, LicenseState* state, std::string* offlineLicense) {
+
+    OfflineFile file;
+    if (!RetrieveHashedFile(keySetId + kLicenseFileNameExt, &file)) {
+        return false;
+    }
+
+    if (file.type() != OfflineFile::LICENSE) {
+        ALOGE("RetrieveLicense: Invalid file type");
+        return false;
+    }
+
+    if (file.version() != OfflineFile::VERSION_1) {
+        ALOGE("RetrieveLicense: Invalid file version");
+        return false;
+    }
+
+    if (!file.has_license()) {
+        ALOGE("RetrieveLicense: License not present");
+        return false;
+    }
+
+    License license = file.license();
+    switch (license.state()) {
+        case License_LicenseState_ACTIVE:
+            *state = kLicenseStateActive;
+            break;
+        case License_LicenseState_RELEASING:
+            *state = kLicenseStateReleasing;
+            break;
+        default:
+            ALOGW("RetrieveLicense: Unrecognized license state: %u",
+                    kLicenseStateUnknown);
+            *state = kLicenseStateUnknown;
+            break;
+    }
+    *offlineLicense = license.license();
+    return true;
+}
+
+bool DeviceFiles::DeleteLicense(const std::string& keySetId) {
+    return mFileHandle.RemoveFile(keySetId + kLicenseFileNameExt);
+}
+
+bool DeviceFiles::DeleteAllLicenses() {
+    return mFileHandle.RemoveAllFiles();
+}
+
+bool DeviceFiles::LicenseExists(const std::string& keySetId) {
+    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");
+        return false;
+    }
+
+    if (!FileExists(fileName)) {
+        ALOGE("RetrieveHashedFile: %s does not exist", fileName.c_str());
+        return false;
+    }
+
+    ssize_t bytes = GetFileSize(fileName);
+    if (bytes <= 0) {
+        ALOGE("RetrieveHashedFile: invalid file size: %s", fileName.c_str());
+        // Remove the corrupted file so the caller will not get the same error
+        // when trying to access the file repeatedly, causing the system to stall.
+        RemoveFile(fileName);
+        return false;
+    }
+
+    std::string serializedHashFile;
+    serializedHashFile.resize(bytes);
+    bytes = mFileHandle.Read(fileName, &serializedHashFile);
+
+    if (bytes != static_cast<ssize_t>(serializedHashFile.size())) {
+        ALOGE("RetrieveHashedFile: Failed to read from %s", fileName.c_str());
+        ALOGV("RetrieveHashedFile: expected: %zd, actual: %zd", serializedHashFile.size(), bytes);
+        // Remove the corrupted file so the caller will not get the same error
+        // when trying to access the file repeatedly, causing the system to stall.
+        RemoveFile(fileName);
+        return false;
+    }
+
+    ALOGV("RetrieveHashedFile: read %zd from %s", bytes, fileName.c_str());
+
+    HashedFile hashFile;
+    if (!hashFile.ParseFromString(serializedHashFile)) {
+        ALOGE("RetrieveHashedFile: Unable to parse hash file");
+        // Remove corrupt file.
+        RemoveFile(fileName);
+        return false;
+    }
+
+    std::string hash;
+    if (!Hash(hashFile.file(), &hash)) {
+        ALOGE("RetrieveHashedFile: Hash computation failed");
+        return false;
+    }
+
+    if (hash != hashFile.hash()) {
+        ALOGE("RetrieveHashedFile: Hash mismatch");
+        // Remove corrupt file.
+        RemoveFile(fileName);
+        return false;
+    }
+
+    if (!deSerializedFile->ParseFromString(hashFile.file())) {
+        ALOGE("RetrieveHashedFile: Unable to parse file");
+        // Remove corrupt file.
+        RemoveFile(fileName);
+        return false;
+    }
+
+    return true;
+}
+
+bool DeviceFiles::FileExists(const std::string& fileName) const {
+    return mFileHandle.FileExists(fileName);
+}
+
+bool DeviceFiles::RemoveFile(const std::string& fileName) {
+    return mFileHandle.RemoveFile(fileName);
+}
+
+ssize_t DeviceFiles::GetFileSize(const std::string& fileName) const {
+    return mFileHandle.GetFileSize(fileName);
+}
+
+} // namespace clearkey
+} // 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..9fb5bbe 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
@@ -30,10 +30,11 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::Status;
+using ::android::hardware::drm::V1_1::SecurityLevel;
 using ::android::hardware::Void;
 
 Return<bool> DrmFactory::isCryptoSchemeSupported(
@@ -41,6 +42,13 @@
     return clearkeydrm::isClearKeyUUID(uuid.data());
 }
 
+Return<bool> DrmFactory::isCryptoSchemeSupported_1_2(const hidl_array<uint8_t, 16>& uuid,
+                                                     const hidl_string &mimeType,
+                                                     SecurityLevel level) {
+    return isCryptoSchemeSupported(uuid) && isContentTypeSupported(mimeType) &&
+            level == SecurityLevel::SW_SECURE_CRYPTO;
+}
+
 Return<bool> DrmFactory::isContentTypeSupported(const hidl_string &mimeType) {
     // This should match the mimeTypes handed by InitDataParser.
     return mimeType == kIsoBmffVideoMimeType ||
@@ -71,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/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index 30f7459..7cb5a38 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -25,11 +25,15 @@
 #include "ClearKeyDrmProperties.h"
 #include "Session.h"
 #include "TypeConvert.h"
+#include "Utils.h"
 
 namespace {
+const std::string kKeySetIdPrefix("ckid");
+const int kKeySetIdLength = 16;
 const int kSecureStopIdStart = 100;
+const std::string kOfflineLicense("\"type\":\"persistent-license\"");
 const std::string kStreaming("Streaming");
-const std::string kOffline("Offline");
+const std::string kTemporaryLicense("\"type\":\"temporary\"");
 const std::string kTrue("True");
 
 const std::string kQueryKeyLicenseType("LicenseType");
@@ -54,18 +58,31 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
+KeyRequestType toKeyRequestType_V1_0(KeyRequestType_V1_1 keyRequestType) {
+  switch (keyRequestType) {
+    case KeyRequestType_V1_1::NONE:
+    case KeyRequestType_V1_1::UPDATE:
+      return KeyRequestType::UNKNOWN;
+    default:
+      return static_cast<KeyRequestType>(keyRequestType);
+  }
+}
+
 DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary)
         : mSessionLibrary(sessionLibrary),
           mOpenSessionOkCount(0),
           mCloseSessionOkCount(0),
           mCloseSessionNotOpenedCount(0),
-          mNextSecureStopId(kSecureStopIdStart) {
+          mNextSecureStopId(kSecureStopIdStart),
+          mMockError(Status_V1_2::OK) {
     mPlayPolicy.clear();
     initProperties();
     mSecureStops.clear();
+    mReleaseKeysMap.clear();
+    std::srand(std::time(nullptr));
 }
 
 void DrmPlugin::initProperties() {
@@ -75,6 +92,7 @@
     mStringProperties[kPluginDescriptionKey] = kPluginDescriptionValue;
     mStringProperties[kAlgorithmsKey] = kAlgorithmsValue;
     mStringProperties[kListenerTestSupportKey] = kListenerTestSupportValue;
+    mStringProperties[kDrmErrorTestKey] = kDrmErrorTestValue;
 
     std::vector<uint8_t> valueVector;
     valueVector.clear();
@@ -103,6 +121,7 @@
 
 Return<void> DrmPlugin::openSession(openSession_cb _hidl_cb) {
     sp<Session> session = mSessionLibrary->createSession();
+    processMockError(session);
     std::vector<uint8_t> sessionId = session->sessionId();
 
     Status status = setSecurityLevel(sessionId, SecurityLevel::SW_SECURE_CRYPTO);
@@ -114,6 +133,7 @@
 Return<void> DrmPlugin::openSession_1_1(SecurityLevel securityLevel,
         openSession_1_1_cb _hidl_cb) {
     sp<Session> session = mSessionLibrary->createSession();
+    processMockError(session);
     std::vector<uint8_t> sessionId = session->sessionId();
 
     Status status = setSecurityLevel(sessionId, securityLevel);
@@ -129,6 +149,10 @@
 
     sp<Session> session = mSessionLibrary->findSession(toVector(sessionId));
     if (session.get()) {
+        if (session->getMockError() != Status_V1_2::OK) {
+            sendSessionLostState(sessionId);
+            return Status::ERROR_DRM_INVALID_STATE;
+        }
         mCloseSessionOkCount++;
         mSessionLibrary->destroySession(session);
         return Status::OK;
@@ -137,35 +161,75 @@
     return Status::ERROR_DRM_SESSION_NOT_OPENED;
 }
 
-Status DrmPlugin::getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
+Status_V1_2 DrmPlugin::getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
         const hidl_vec<uint8_t>& initData,
         const hidl_string& mimeType,
         KeyType keyType,
         const hidl_vec<KeyValue>& optionalParameters,
         std::vector<uint8_t> *request,
-        KeyRequestType *keyRequestType,
+        KeyRequestType_V1_1 *keyRequestType,
         std::string *defaultUrl) {
         UNUSED(optionalParameters);
 
+    // GetKeyRequestOfflineKeyTypeNotSupported() in vts 1.0 and 1.1 expects
+    // KeyType::OFFLINE to return ERROR_DRM_CANNOT_HANDLE in clearkey plugin.
+    // Those tests pass in an empty initData, we use the empty initData to
+    // signal such specific use case.
+    if (keyType == KeyType::OFFLINE && 0 == initData.size()) {
+        return Status_V1_2::ERROR_DRM_CANNOT_HANDLE;
+    }
+
     *defaultUrl = "";
-    *keyRequestType = KeyRequestType::UNKNOWN;
+    *keyRequestType = KeyRequestType_V1_1::UNKNOWN;
     *request = std::vector<uint8_t>();
 
-    if (scope.size() == 0) {
-        return Status::BAD_VALUE;
+    if (scope.size() == 0 ||
+            (keyType != KeyType::STREAMING &&
+            keyType != KeyType::OFFLINE &&
+            keyType != KeyType::RELEASE)) {
+        return Status_V1_2::BAD_VALUE;
     }
 
-    if (keyType != KeyType::STREAMING) {
-        return Status::ERROR_DRM_CANNOT_HANDLE;
+    const std::vector<uint8_t> scopeId = toVector(scope);
+    sp<Session> session;
+    if (keyType == KeyType::STREAMING || keyType == KeyType::OFFLINE) {
+        std::vector<uint8_t> sessionId(scopeId.begin(), scopeId.end());
+        session = mSessionLibrary->findSession(sessionId);
+        if (!session.get()) {
+            return Status_V1_2::ERROR_DRM_SESSION_NOT_OPENED;
+        } else if (session->getMockError() != Status_V1_2::OK) {
+            return session->getMockError();
+        }
+
+        *keyRequestType = KeyRequestType_V1_1::INITIAL;
     }
 
-    sp<Session> session = mSessionLibrary->findSession(toVector(scope));
-    if (!session.get()) {
-        return Status::ERROR_DRM_SESSION_NOT_OPENED;
-    }
+    Status_V1_2 status = static_cast<Status_V1_2>(
+            session->getKeyRequest(initData, mimeType, keyType, request));
 
-    Status status = session->getKeyRequest(initData, mimeType, request);
-    *keyRequestType = KeyRequestType::INITIAL;
+    if (keyType == KeyType::RELEASE) {
+        std::vector<uint8_t> keySetId(scopeId.begin(), scopeId.end());
+        std::string requestString(request->begin(), request->end());
+        if (requestString.find(kOfflineLicense) != std::string::npos) {
+            std::string emptyResponse;
+            std::string keySetIdString(keySetId.begin(), keySetId.end());
+            if (!mFileHandle.StoreLicense(keySetIdString,
+                    DeviceFiles::kLicenseStateReleasing,
+                    emptyResponse)) {
+                ALOGE("Problem releasing offline license");
+                return Status_V1_2::ERROR_DRM_UNKNOWN;
+            }
+            if (mReleaseKeysMap.find(keySetIdString) == mReleaseKeysMap.end()) {
+                sp<Session> session = mSessionLibrary->createSession();
+                mReleaseKeysMap[keySetIdString] = session->sessionId();
+            } else {
+                ALOGI("key is in use, ignore release request");
+            }
+        } else {
+            ALOGE("Offline license not found, nothing to release");
+        }
+        *keyRequestType = KeyRequestType_V1_1::RELEASE;
+    }
     return status;
 }
 
@@ -178,15 +242,15 @@
         getKeyRequest_cb _hidl_cb) {
     UNUSED(optionalParameters);
 
-    KeyRequestType keyRequestType = KeyRequestType::UNKNOWN;
+    KeyRequestType_V1_1 keyRequestType = KeyRequestType_V1_1::UNKNOWN;
     std::string defaultUrl("");
     std::vector<uint8_t> request;
-    Status status = getKeyRequestCommon(
+    Status_V1_2 status = getKeyRequestCommon(
             scope, initData, mimeType, keyType, optionalParameters,
             &request, &keyRequestType, &defaultUrl);
 
-    _hidl_cb(status, toHidlVec(request),
-            static_cast<drm::V1_0::KeyRequestType>(keyRequestType),
+    _hidl_cb(toStatus_1_0(status), toHidlVec(request),
+            toKeyRequestType_V1_0(keyRequestType),
             hidl_string(defaultUrl));
     return Void();
 }
@@ -200,10 +264,31 @@
         getKeyRequest_1_1_cb _hidl_cb) {
     UNUSED(optionalParameters);
 
-    KeyRequestType keyRequestType = KeyRequestType::UNKNOWN;
+    KeyRequestType_V1_1 keyRequestType = KeyRequestType_V1_1::UNKNOWN;
     std::string defaultUrl("");
     std::vector<uint8_t> request;
-    Status status = getKeyRequestCommon(
+    Status_V1_2 status = getKeyRequestCommon(
+            scope, initData, mimeType, keyType, optionalParameters,
+            &request, &keyRequestType, &defaultUrl);
+
+    _hidl_cb(toStatus_1_0(status), toHidlVec(request),
+            keyRequestType, hidl_string(defaultUrl));
+    return Void();
+}
+
+Return<void> DrmPlugin::getKeyRequest_1_2(
+        const hidl_vec<uint8_t>& scope,
+        const hidl_vec<uint8_t>& initData,
+        const hidl_string& mimeType,
+        KeyType keyType,
+        const hidl_vec<KeyValue>& optionalParameters,
+        getKeyRequest_1_2_cb _hidl_cb) {
+    UNUSED(optionalParameters);
+
+    KeyRequestType_V1_1 keyRequestType = KeyRequestType_V1_1::UNKNOWN;
+    std::string defaultUrl("");
+    std::vector<uint8_t> request;
+    Status_V1_2 status = getKeyRequestCommon(
             scope, initData, mimeType, keyType, optionalParameters,
             &request, &keyRequestType, &defaultUrl);
 
@@ -227,6 +312,30 @@
     mPlayPolicy.push_back(policy);
 }
 
+bool DrmPlugin::makeKeySetId(std::string* keySetId) {
+    if (!keySetId) {
+        ALOGE("keySetId destination not provided");
+        return false;
+    }
+    std::vector<uint8_t> ksid(kKeySetIdPrefix.begin(), kKeySetIdPrefix.end());
+    ksid.resize(kKeySetIdLength);
+    std::vector<uint8_t> randomData((kKeySetIdLength - kKeySetIdPrefix.size()) / 2, 0);
+
+    while (keySetId->empty()) {
+        for (auto itr = randomData.begin(); itr != randomData.end(); ++itr) {
+            *itr = std::rand() % 0xff;
+        }
+        *keySetId = kKeySetIdPrefix + ByteArrayToHexString(
+                reinterpret_cast<const uint8_t*>(randomData.data()), randomData.size());
+        if (mFileHandle.LicenseExists(*keySetId)) {
+            // collision, regenerate
+            ALOGV("Retry generating KeySetId");
+            keySetId->clear();
+        }
+    }
+    return true;
+}
+
 Return<void> DrmPlugin::provideKeyResponse(
         const hidl_vec<uint8_t>& scope,
         const hidl_vec<uint8_t>& response,
@@ -237,28 +346,124 @@
         return Void();
     }
 
-    sp<Session> session = mSessionLibrary->findSession(toVector(scope));
+    std::string responseString(
+            reinterpret_cast<const char*>(response.data()), response.size());
+    const std::vector<uint8_t> scopeId = toVector(scope);
+    std::vector<uint8_t> sessionId;
+    std::string keySetId;
+
+    Status status = Status::OK;
+    bool isOfflineLicense = responseString.find(kOfflineLicense) != std::string::npos;
+    bool isRelease = (memcmp(scopeId.data(), kKeySetIdPrefix.data(), kKeySetIdPrefix.size()) == 0);
+    if (isRelease) {
+        keySetId.assign(scopeId.begin(), scopeId.end());
+
+        auto iter = mReleaseKeysMap.find(std::string(keySetId.begin(), keySetId.end()));
+        if (iter != mReleaseKeysMap.end()) {
+            sessionId.assign(iter->second.begin(), iter->second.end());
+        }
+    } else {
+        sessionId.assign(scopeId.begin(), scopeId.end());
+        // non offline license returns empty keySetId
+        keySetId.clear();
+    }
+
+    sp<Session> session = mSessionLibrary->findSession(sessionId);
     if (!session.get()) {
         _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>());
         return Void();
     }
-
     setPlayPolicy();
-    std::vector<uint8_t> keySetId;
-    Status status = session->provideKeyResponse(response);
+
+    status = session->provideKeyResponse(response);
     if (status == Status::OK) {
-        // This is for testing AMediaDrm_setOnEventListener only.
-        sendEvent(EventType::VENDOR_DEFINED, 0, scope);
-        keySetId.clear();
+        if (isOfflineLicense) {
+            if (isRelease) {
+                mFileHandle.DeleteLicense(keySetId);
+            } else {
+                if (!makeKeySetId(&keySetId)) {
+                    _hidl_cb(Status::ERROR_DRM_UNKNOWN, hidl_vec<uint8_t>());
+                    return Void();
+                }
+
+                bool ok = mFileHandle.StoreLicense(
+                        keySetId,
+                        DeviceFiles::kLicenseStateActive,
+                        std::string(response.begin(), response.end()));
+                if (!ok) {
+                    ALOGE("Failed to store offline license");
+                }
+            }
+        }
+
+        // Test calling AMediaDrm listeners.
+        sendEvent(EventType::VENDOR_DEFINED, sessionId, sessionId);
+
+        sendExpirationUpdate(sessionId, 100);
+
+        std::vector<KeyStatus_V1_2> keysStatus;
+        KeyStatus_V1_2 keyStatus;
+
+        std::vector<uint8_t> keyId1 = { 0xA, 0xB, 0xC };
+        keyStatus.keyId = keyId1;
+        keyStatus.type = V1_2::KeyStatusType::USABLE;
+        keysStatus.push_back(keyStatus);
+
+        std::vector<uint8_t> keyId2 = { 0xD, 0xE, 0xF };
+        keyStatus.keyId = keyId2;
+        keyStatus.type = V1_2::KeyStatusType::EXPIRED;
+        keysStatus.push_back(keyStatus);
+
+        std::vector<uint8_t> keyId3 = { 0x0, 0x1, 0x2 };
+        keyStatus.keyId = keyId3;
+        keyStatus.type = V1_2::KeyStatusType::USABLEINFUTURE;
+        keysStatus.push_back(keyStatus);
+
+        sendKeysChange_1_2(sessionId, keysStatus, true);
+
+        installSecureStop(sessionId);
+    } else {
+        ALOGE("provideKeyResponse returns error=%d", status);
     }
 
-    installSecureStop(scope);
-
-    // Returns status and empty keySetId
-    _hidl_cb(status, toHidlVec(keySetId));
+    std::vector<uint8_t> keySetIdVec(keySetId.begin(), keySetId.end());
+    _hidl_cb(status, toHidlVec(keySetIdVec));
     return Void();
 }
 
+Return<Status> DrmPlugin::restoreKeys(
+        const hidl_vec<uint8_t>& sessionId, const hidl_vec<uint8_t>& keySetId) {
+        if (sessionId.size() == 0 || keySetId.size() == 0) {
+            return Status::BAD_VALUE;
+        }
+
+        DeviceFiles::LicenseState licenseState;
+        std::string offlineLicense;
+        Status status = Status::OK;
+        if (!mFileHandle.RetrieveLicense(std::string(keySetId.begin(), keySetId.end()),
+                &licenseState, &offlineLicense)) {
+            ALOGE("Failed to restore offline license");
+            return Status::ERROR_DRM_NO_LICENSE;
+        }
+
+        if (DeviceFiles::kLicenseStateUnknown == licenseState ||
+                DeviceFiles::kLicenseStateReleasing == licenseState) {
+            ALOGE("Invalid license state=%d", licenseState);
+            return Status::ERROR_DRM_NO_LICENSE;
+        }
+
+        sp<Session> session = mSessionLibrary->findSession(toVector(sessionId));
+        if (!session.get()) {
+            return Status::ERROR_DRM_SESSION_NOT_OPENED;
+        }
+        status = session->provideKeyResponse(std::vector<uint8_t>(offlineLicense.begin(),
+                offlineLicense.end()));
+        if (status != Status::OK) {
+            ALOGE("Failed to restore keys");
+        }
+        return status;
+}
+
 Return<void> DrmPlugin::getPropertyString(
         const hidl_string& propertyName, getPropertyString_cb _hidl_cb) {
     std::string name(propertyName.c_str());
@@ -274,6 +479,8 @@
         value = mStringProperties[kAlgorithmsKey];
     } else if (name == kListenerTestSupportKey) {
         value = mStringProperties[kListenerTestSupportKey];
+    } else if (name == kDrmErrorTestKey) {
+        value = mStringProperties[kDrmErrorTestKey];
     } else {
         ALOGE("App requested unknown string property %s", name.c_str());
         _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, "");
@@ -318,6 +525,20 @@
         return Status::BAD_VALUE;
     }
 
+    if (name == kDrmErrorTestKey) {
+        if (value == kResourceContentionValue) {
+            mMockError = Status_V1_2::ERROR_DRM_RESOURCE_CONTENTION;
+        } else if (value == kLostStateValue) {
+            mMockError = Status_V1_2::ERROR_DRM_SESSION_LOST_STATE;
+        } else if (value == kFrameTooLargeValue) {
+            mMockError = Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE;
+        } else if (value == kInvalidStateValue)  {
+            mMockError = Status_V1_2::ERROR_DRM_INVALID_STATE;
+        } else {
+            mMockError = Status_V1_2::ERROR_DRM_UNKNOWN;
+        }
+    }
+
     mStringProperties[key] = std::string(value.c_str());
     return Status::OK;
 }
@@ -328,6 +549,9 @@
    if (name == kDeviceIdKey) {
       ALOGD("Cannot set immutable property: %s", name.c_str());
       return Status::BAD_VALUE;
+   } else if (name == kClientIdKey) {
+       mByteArrayProperties[kClientIdKey] = toVector(value);
+       return Status::OK;
    }
 
    // Setting of undefined properties is not supported
@@ -465,6 +689,60 @@
     return Void();
 }
 
+Return<void> DrmPlugin::getOfflineLicenseKeySetIds(getOfflineLicenseKeySetIds_cb _hidl_cb) {
+    std::vector<std::string> licenseNames = mFileHandle.ListLicenses();
+    std::vector<KeySetId> keySetIds;
+    if (mMockError != Status_V1_2::OK) {
+        _hidl_cb(toStatus_1_0(mMockError), keySetIds);
+        return Void();
+    }
+    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) {
+    if (mMockError != Status_V1_2::OK) {
+        return toStatus_1_0(mMockError);
+    }
+    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 (mMockError != Status_V1_2::OK) {
+        _hidl_cb(toStatus_1_0(mMockError), OfflineLicenseState::UNKNOWN);
+    } else 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) {
@@ -572,7 +850,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 e2bb651..b988ce0 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 {
@@ -45,21 +45,22 @@
 }
 
 Status InitDataParser::parse(const std::vector<uint8_t>& initData,
-        const std::string& type,
+        const std::string& mimeType,
+        V1_0::KeyType keyType,
         std::vector<uint8_t>* licenseRequest) {
     // Build a list of the key IDs
     std::vector<const uint8_t*> keyIds;
 
-    if (type == kIsoBmffVideoMimeType ||
-        type == kIsoBmffAudioMimeType ||
-        type == kCencInitDataFormat) {
+    if (mimeType == kIsoBmffVideoMimeType.c_str() ||
+        mimeType == kIsoBmffAudioMimeType.c_str() ||
+        mimeType == kCencInitDataFormat.c_str()) {
         Status res = parsePssh(initData, &keyIds);
         if (res != Status::OK) {
             return res;
         }
-    } else if (type == kWebmVideoMimeType ||
-        type == kWebmAudioMimeType ||
-        type == kWebmInitDataFormat) {
+    } else if (mimeType == kWebmVideoMimeType.c_str() ||
+        mimeType == kWebmAudioMimeType.c_str() ||
+        mimeType == kWebmInitDataFormat.c_str()) {
         // WebM "init data" is just a single key ID
         if (initData.size() != kKeyIdSize) {
             return Status::ERROR_DRM_CANNOT_HANDLE;
@@ -69,8 +70,12 @@
         return Status::ERROR_DRM_CANNOT_HANDLE;
     }
 
+    if (keyType == V1_0::KeyType::RELEASE) {
+        // restore key
+    }
+
     // Build the request
-    std::string requestJson = generateRequest(keyIds);
+    std::string requestJson = generateRequest(keyType, keyIds);
     std::vector<uint8_t> requestJsonVec = StrToVector(requestJson);
 
     licenseRequest->clear();
@@ -131,9 +136,11 @@
     return Status::OK;
 }
 
-std::string InitDataParser::generateRequest(const std::vector<const uint8_t*>& keyIds) {
+std::string InitDataParser::generateRequest(V1_0::KeyType keyType,
+        const std::vector<const uint8_t*>& keyIds) {
     const std::string kRequestPrefix("{\"kids\":[");
-    const std::string kRequestSuffix("],\"type\":\"temporary\"}");
+    const std::string kTemporarySession("],\"type\":\"temporary\"}");
+    const std::string kPersistentSession("],\"type\":\"persistent-license\"}");
 
     std::string request(kRequestPrefix);
     std::string encodedId;
@@ -147,7 +154,12 @@
         request.append(encodedId);
         request.push_back('\"');
     }
-    request.append(kRequestSuffix);
+    if (keyType == V1_0::KeyType::STREAMING) {
+        request.append(kTemporarySession);
+    } else if (keyType == V1_0::KeyType::OFFLINE ||
+                   keyType == V1_0::KeyType::RELEASE) {
+            request.append(kPersistentSession);
+    }
 
     // Android's Base64 encoder produces padding. EME forbids padding.
     const char kBase64Padding = '=';
@@ -157,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 cccb41e..d93777d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp
@@ -22,18 +22,21 @@
 #include "Base64.h"
 
 namespace {
+const std::string kBase64Padding("=");
 const std::string kKeysTag("keys");
 const std::string kKeyTypeTag("kty");
-const std::string kSymmetricKeyValue("oct");
 const std::string kKeyTag("k");
 const std::string kKeyIdTag("kid");
-const std::string kBase64Padding("=");
+const std::string kMediaSessionType("type");
+const std::string kPersistentLicenseSession("persistent-license");
+const std::string kSymmetricKeyValue("oct");
+const std::string kTemporaryLicenseSession("temporary");
 }
 
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 JsonWebKey::JsonWebKey() {
@@ -268,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
new file mode 100644
index 0000000..2dcd00f
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp
@@ -0,0 +1,92 @@
+// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
+// source code may only be used and distributed under the Widevine Master
+// License Agreement.
+
+#include <utils/Log.h>
+#include <string>
+
+#include "MemoryFileSystem.h"
+#include "Utils.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace clearkey {
+
+std::string MemoryFileSystem::GetFileName(const std::string& path) {
+    size_t index = path.find_last_of("/");
+    if (index != std::string::npos) {
+        return path.substr(index+1);
+    } else {
+        return path;
+    }
+}
+
+bool MemoryFileSystem::FileExists(const std::string& fileName) const {
+    auto result = mMemoryFileSystem.find(fileName);
+    return result != mMemoryFileSystem.end();
+}
+
+ssize_t MemoryFileSystem::GetFileSize(const std::string& fileName) const {
+    auto result = mMemoryFileSystem.find(fileName);
+    if (result != mMemoryFileSystem.end()) {
+        return static_cast<ssize_t>(result->second.getFileSize());
+    } else {
+        ALOGE("Failed to get size for %s", fileName.c_str());
+        return -1;
+    }
+}
+
+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);
+    if (result != mMemoryFileSystem.end()) {
+        std::string serializedHashFile = result->second.getContent();
+        buffer->assign(serializedHashFile);
+        return buffer->size();
+    } else {
+        ALOGE("Failed to read from %s", path.c_str());
+        return -1;
+    }
+}
+
+size_t MemoryFileSystem::Write(const std::string& path, const MemoryFile& memoryFile) {
+    std::string key = GetFileName(path);
+    auto result = mMemoryFileSystem.find(key);
+    if (result != mMemoryFileSystem.end()) {
+        mMemoryFileSystem.erase(key);
+    }
+    mMemoryFileSystem.insert(std::pair<std::string, MemoryFile>(key, memoryFile));
+    return memoryFile.getFileSize();
+}
+
+bool MemoryFileSystem::RemoveFile(const std::string& fileName) {
+    auto result = mMemoryFileSystem.find(fileName);
+    if (result != mMemoryFileSystem.end()) {
+        mMemoryFileSystem.erase(result);
+        return true;
+    } else {
+        ALOGE("Cannot find license to remove: %s", fileName.c_str());
+        return false;
+    }
+}
+
+bool MemoryFileSystem::RemoveAllFiles() {
+    mMemoryFileSystem.clear();
+    return mMemoryFileSystem.empty();
+}
+
+} // namespace clearkey
+} // 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 07c9269..a9d7016 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;
@@ -42,9 +42,10 @@
 Status Session::getKeyRequest(
         const std::vector<uint8_t>& initData,
         const std::string& mimeType,
+        V1_0::KeyType keyType,
         std::vector<uint8_t>* keyRequest) const {
     InitDataParser parser;
-    return parser.parse(initData, mimeType, keyRequest);
+    return parser.parse(initData, mimeType, keyType, keyRequest);
 }
 
 Status Session::provideKeyResponse(const std::vector<uint8_t>& response) {
@@ -67,29 +68,34 @@
     }
 }
 
-Status Session::decrypt(
+Status_V1_2 Session::decrypt(
         const KeyId keyId, const Iv iv, const uint8_t* srcPtr,
         uint8_t* destPtr, const std::vector<SubSample> subSamples,
         size_t* bytesDecryptedOut) {
     Mutex::Autolock lock(mMapLock);
 
+    if (getMockError() != Status_V1_2::OK) {
+        return getMockError();
+    }
+
     std::vector<uint8_t> keyIdVector;
     keyIdVector.clear();
     keyIdVector.insert(keyIdVector.end(), keyId, keyId + kBlockSize);
     std::map<std::vector<uint8_t>, std::vector<uint8_t> >::iterator itr;
     itr = mKeyMap.find(keyIdVector);
     if (itr == mKeyMap.end()) {
-        return Status::ERROR_DRM_NO_LICENSE;
+        return Status_V1_2::ERROR_DRM_NO_LICENSE;
     }
 
     AesCtrDecryptor decryptor;
-    return decryptor.decrypt(
+    Status status = decryptor.decrypt(
             itr->second /*key*/, iv, srcPtr, destPtr, subSamples,
             subSamples.size(), bytesDecryptedOut);
+    return static_cast<Status_V1_2>(status);
 }
 
 } // 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 b4319e6..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;
@@ -59,7 +59,8 @@
 
     mSessions.insert(std::pair<std::vector<uint8_t>,
             sp<Session> >(sessionId, new Session(sessionId)));
-    std::map<std::vector<uint8_t>, sp<Session> >::iterator itr = mSessions.find(sessionId);
+    std::map<std::vector<uint8_t>, sp<Session> >::iterator itr =
+            mSessions.find(sessionId);
     if (itr != mSessions.end()) {
         return itr->second;
     } else {
@@ -70,7 +71,8 @@
 sp<Session> SessionLibrary::findSession(
         const std::vector<uint8_t>& sessionId) {
     Mutex::Autolock lock(mSessionsLock);
-    std::map<std::vector<uint8_t>, sp<Session> >::iterator itr = mSessions.find(sessionId);
+    std::map<std::vector<uint8_t>, sp<Session> >::iterator itr =
+            mSessions.find(sessionId);
     if (itr != mSessions.end()) {
         return itr->second;
     } else {
@@ -84,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-lazy.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service-lazy.clearkey.rc
new file mode 100644
index 0000000..9afd3d7
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service-lazy.clearkey.rc
@@ -0,0 +1,14 @@
+service vendor.drm-clearkey-hal-1-2 /vendor/bin/hw/android.hardware.drm@1.2-service-lazy.clearkey
+    interface android.hardware.drm@1.0::ICryptoFactory clearkey
+    interface android.hardware.drm@1.0::IDrmFactory clearkey
+    interface android.hardware.drm@1.1::ICryptoFactory clearkey
+    interface android.hardware.drm@1.1::IDrmFactory clearkey
+    interface android.hardware.drm@1.2::ICryptoFactory clearkey
+    interface android.hardware.drm@1.2::IDrmFactory clearkey
+    disabled
+    oneshot
+    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..5ba669d
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.2-service.clearkey.rc
@@ -0,0 +1,12 @@
+service vendor.drm-clearkey-hal-1-2 /vendor/bin/hw/android.hardware.drm@1.2-service.clearkey
+    interface android.hardware.drm@1.0::ICryptoFactory clearkey
+    interface android.hardware.drm@1.0::IDrmFactory clearkey
+    interface android.hardware.drm@1.1::ICryptoFactory clearkey
+    interface android.hardware.drm@1.1::IDrmFactory clearkey
+    interface android.hardware.drm@1.2::ICryptoFactory clearkey
+    interface android.hardware.drm@1.2::IDrmFactory 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 d65b25c..b83ce69 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
@@ -22,30 +22,40 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 static const std::string kVendorKey("vendor");
 static const std::string kVendorValue("Google");
 static const std::string kVersionKey("version");
-static const std::string kVersionValue("1.1");
+static const std::string kVersionValue("1.2");
 static const std::string kPluginDescriptionKey("description");
 static const std::string kPluginDescriptionValue("ClearKey CDM");
 static const std::string kAlgorithmsKey("algorithms");
 static const std::string kAlgorithmsValue("");
 static const std::string kListenerTestSupportKey("listenerTestSupport");
 static const std::string kListenerTestSupportValue("true");
+static const std::string kDrmErrorTestKey("drmErrorTest");
+static const std::string kDrmErrorTestValue("");
+static const std::string kResourceContentionValue("resourceContention");
+static const std::string kLostStateValue("lostState");
+static const std::string kFrameTooLargeValue("frameTooLarge");
+static const std::string kInvalidStateValue("invalidState");
 
 static const std::string kDeviceIdKey("deviceId");
 static const uint8_t kTestDeviceIdData[] =
         {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
          0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+
+// settable byte array property
+static const std::string kClientIdKey("clientId");
+
 // TODO stub out metrics for nw
 static const std::string kMetricsKey("metrics");
 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..03c434e 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h
@@ -17,17 +17,18 @@
 #ifndef CLEARKEY_MACROS_H_
 #define CLEARKEY_MACROS_H_
 
-#include <android/hardware/drm/1.0/types.h>
+#include <android/hardware/drm/1.2/types.h>
 
 #include <map>
 
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
 using ::android::hardware::drm::V1_0::KeyValue;
+using ::android::hardware::drm::V1_1::SecurityLevel;
 using ::android::hardware::hidl_vec;
 
 const uint8_t kBlockSize = 16; //AES_BLOCK_SIZE;
@@ -47,7 +48,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..8680f0c 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h
@@ -17,7 +17,7 @@
 #ifndef CLEARKEY_CRYPTO_PLUGIN_H_
 #define CLEARKEY_CRYPTO_PLUGIN_H_
 
-#include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hardware/drm/1.2/ICryptoPlugin.h>
 #include <android/hidl/memory/1.0/IMemory.h>
 
 #include "ClearKeyTypes.h"
@@ -32,16 +32,17 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
-using ::android::hardware::drm::V1_0::DestinationBuffer;
-using ::android::hardware::drm::V1_0::ICryptoPlugin;
-using ::android::hardware::drm::V1_0::Mode;
-using ::android::hardware::drm::V1_0::Pattern;
-using ::android::hardware::drm::V1_0::SharedBuffer;
-using ::android::hardware::drm::V1_0::Status;
-using ::android::hardware::drm::V1_0::SubSample;
+namespace drm = ::android::hardware::drm;
+using drm::V1_0::DestinationBuffer;
+using drm::V1_0::Mode;
+using drm::V1_0::Pattern;
+using drm::V1_0::SharedBuffer;
+using drm::V1_0::Status;
+using drm::V1_0::SubSample;
+
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_memory;
 using ::android::hardware::hidl_string;
@@ -51,7 +52,9 @@
 using ::android::hidl::memory::V1_0::IMemory;
 using ::android::sp;
 
-struct CryptoPlugin : public ICryptoPlugin {
+typedef drm::V1_2::Status Status_V1_2;
+
+struct CryptoPlugin : public drm::V1_2::ICryptoPlugin {
     explicit CryptoPlugin(const hidl_vec<uint8_t>& sessionId) {
         mInitStatus = setMediaDrmSession(sessionId);
     }
@@ -80,6 +83,18 @@
             const DestinationBuffer& destination,
             decrypt_cb _hidl_cb);
 
+    Return<void> decrypt_1_2(
+            bool secure,
+            const hidl_array<uint8_t, KEY_ID_SIZE>& keyId,
+            const hidl_array<uint8_t, KEY_IV_SIZE>& iv,
+            Mode mode,
+            const Pattern& pattern,
+            const hidl_vec<SubSample>& subSamples,
+            const SharedBuffer& source,
+            uint64_t offset,
+            const DestinationBuffer& destination,
+            decrypt_1_2_cb _hidl_cb);
+
     Return<void> setSharedBufferBase(const hidl_memory& base,
             uint32_t bufferId);
 
@@ -96,7 +111,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
new file mode 100644
index 0000000..554ae59
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DeviceFiles.h
@@ -0,0 +1,71 @@
+// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
+// source code may only be used and distributed under the Widevine Master
+// License Agreement.
+//
+#ifndef CLEARKEY_DEVICE_FILES_H_
+#define CLEARKEY_DEVICE_FILES_H_
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "protos/DeviceFiles.pb.h"
+#include "ClearKeyTypes.h"
+#include "MemoryFileSystem.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace clearkey {
+
+class DeviceFiles {
+ public:
+    typedef enum {
+        kLicenseStateUnknown,
+        kLicenseStateActive,
+        kLicenseStateReleasing,
+    } LicenseState;
+
+    DeviceFiles() {};
+    virtual ~DeviceFiles() {};
+
+    virtual bool StoreLicense(const std::string& keySetId, LicenseState state,
+            const std::string& keyResponse);
+
+    virtual bool RetrieveLicense(
+            const std::string& key_set_id, LicenseState* state, std::string* offlineLicense);
+
+    virtual bool LicenseExists(const std::string& keySetId);
+
+    virtual std::vector<std::string> ListLicenses() const;
+
+    virtual bool DeleteLicense(const std::string& keySetId);
+
+    virtual bool DeleteAllLicenses();
+
+ private:
+    bool FileExists(const std::string& path) const;
+    ssize_t GetFileSize(const std::string& fileName) const;
+    bool RemoveFile(const std::string& fileName);
+
+    bool RetrieveHashedFile(const std::string& fileName, OfflineFile* deSerializedFile);
+    bool StoreFileRaw(const std::string& fileName, const std::string& serializedFile);
+    bool StoreFileWithHash(const std::string& fileName, const std::string& serializedFile);
+
+    MemoryFileSystem mFileHandle;
+
+    CLEARKEY_DISALLOW_COPY_AND_ASSIGN(DeviceFiles);
+};
+
+} // namespace clearkey
+} // namespace V1_2
+} // namespace drm
+} // namespace hardware
+} // namespace android
+
+#endif  // CLEARKEY_DEVICE_FILES_H_
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
index 6f58195..4ca856d 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;
@@ -39,6 +39,10 @@
     Return<bool> isCryptoSchemeSupported(const hidl_array<uint8_t, 16>& uuid)
             override;
 
+    Return<bool> isCryptoSchemeSupported_1_2(const hidl_array<uint8_t, 16>& uuid,
+                                             const hidl_string& mimeType,
+                                             SecurityLevel level) override;
+
     Return<bool> isContentTypeSupported(const hidl_string &mimeType)
             override;
 
@@ -52,7 +56,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 fb0695a..f294d4d 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -17,32 +17,42 @@
 #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 <android/hardware/drm/1.2/IDrmPluginListener.h>
 
-#include <stdio.h>
 #include <map>
+#include <stdio.h>
 
+#include <utils/List.h>
+
+#include "DeviceFiles.h"
 #include "SessionLibrary.h"
 #include "Utils.h"
 
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
-using ::android::hardware::drm::V1_0::EventType;
-using ::android::hardware::drm::V1_0::IDrmPluginListener;
-using ::android::hardware::drm::V1_0::KeyStatus;
-using ::android::hardware::drm::V1_0::KeyType;
-using ::android::hardware::drm::V1_0::KeyValue;
-using ::android::hardware::drm::V1_0::SecureStop;
-using ::android::hardware::drm::V1_0::SecureStopId;
-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::KeyRequestType;
+namespace drm = ::android::hardware::drm;
+using drm::V1_0::EventType;
+using drm::V1_0::IDrmPluginListener;
+using drm::V1_0::KeyRequestType;
+using drm::V1_0::KeyStatus;
+using drm::V1_0::KeyType;
+using drm::V1_0::KeyValue;
+using drm::V1_0::SecureStop;
+using drm::V1_0::SecureStopId;
+using drm::V1_0::SessionId;
+using drm::V1_0::Status;
+using drm::V1_1::DrmMetricGroup;
+using drm::V1_1::HdcpLevel;
+using drm::V1_1::SecureStopRelease;
+using drm::V1_1::SecurityLevel;
+using drm::V1_2::IDrmPlugin;
+using drm::V1_2::KeySetId;
+using drm::V1_2::OfflineLicenseState;
 
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
@@ -50,10 +60,16 @@
 using ::android::hardware::Void;
 using ::android::sp;
 
+typedef drm::V1_1::KeyRequestType KeyRequestType_V1_1;
+typedef drm::V1_2::IDrmPluginListener IDrmPluginListener_V1_2;
+typedef drm::V1_2::KeyStatus KeyStatus_V1_2;
+typedef drm::V1_2::Status Status_V1_2;
+typedef drm::V1_2::HdcpLevel HdcpLevel_V1_2;
+
 struct DrmPlugin : public IDrmPlugin {
     explicit DrmPlugin(SessionLibrary* sessionLibrary);
 
-    virtual ~DrmPlugin() {}
+    virtual ~DrmPlugin() { mFileHandle.DeleteAllLicenses(); }
 
     Return<void> openSession(openSession_cb _hidl_cb) override;
     Return<void> openSession_1_1(SecurityLevel securityLevel,
@@ -77,6 +93,14 @@
         const hidl_vec<KeyValue>& optionalParameters,
         getKeyRequest_1_1_cb _hidl_cb) override;
 
+    Return<void> getKeyRequest_1_2(
+        const hidl_vec<uint8_t>& scope,
+        const hidl_vec<uint8_t>& initData,
+        const hidl_string& mimeType,
+        KeyType keyType,
+        const hidl_vec<KeyValue>& optionalParameters,
+        getKeyRequest_1_2_cb _hidl_cb) override;
+
     Return<void> provideKeyResponse(
         const hidl_vec<uint8_t>& scope,
         const hidl_vec<uint8_t>& response,
@@ -91,13 +115,7 @@
 
     Return<Status> restoreKeys(
         const hidl_vec<uint8_t>& sessionId,
-        const hidl_vec<uint8_t>& keySetId) {
-
-        if (sessionId.size() == 0 || keySetId.size() == 0) {
-            return Status::BAD_VALUE;
-        }
-        return Status::ERROR_DRM_CANNOT_HANDLE;
-    }
+        const hidl_vec<uint8_t>& keySetId) override;
 
     Return<void> queryKeyStatus(
         const hidl_vec<uint8_t>& sessionId,
@@ -115,6 +133,18 @@
         return Void();
     }
 
+    Return<void> getProvisionRequest_1_2(
+        const hidl_string& certificateType,
+        const hidl_string& certificateAuthority,
+        getProvisionRequest_1_2_cb _hidl_cb) {
+        UNUSED(certificateType);
+        UNUSED(certificateAuthority);
+
+        hidl_string defaultUrl;
+        _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>(), defaultUrl);
+        return Void();
+    }
+
     Return<void> provideProvisionResponse(
         const hidl_vec<uint8_t>& response,
         provideProvisionResponse_cb _hidl_cb) {
@@ -134,6 +164,13 @@
         return Void();
     }
 
+    Return<void> getHdcpLevels_1_2(getHdcpLevels_1_2_cb _hidl_cb) {
+        HdcpLevel_V1_2 connectedLevel = HdcpLevel_V1_2::HDCP_NONE;
+        HdcpLevel_V1_2 maxLevel = HdcpLevel_V1_2::HDCP_NO_OUTPUT;
+        _hidl_cb(Status_V1_2::OK, connectedLevel, maxLevel);
+        return Void();
+    }
+
     Return<void> getNumberOfSessions(getNumberOfSessions_cb _hidl_cb) override;
 
     Return<void> getSecurityLevel(const hidl_vec<uint8_t>& sessionId,
@@ -141,6 +178,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;
@@ -248,12 +292,17 @@
 
     Return<void> setListener(const sp<IDrmPluginListener>& listener) {
         mListener = listener;
+        mListenerV1_2 = IDrmPluginListener_V1_2::castFrom(listener);
         return Void();
     };
 
-    Return<void> sendEvent(EventType eventType, const hidl_vec<uint8_t>& sessionId,
+    Return<void> sendEvent(
+            EventType eventType,
+            const hidl_vec<uint8_t>& sessionId,
             const hidl_vec<uint8_t>& data) {
-        if (mListener != NULL) {
+        if (mListenerV1_2 != NULL) {
+            mListenerV1_2->sendEvent(eventType, sessionId, data);
+        } else if (mListener != NULL) {
             mListener->sendEvent(eventType, sessionId, data);
         } else {
             ALOGE("Null event listener, event not sent");
@@ -261,8 +310,12 @@
         return Void();
     }
 
-    Return<void> sendExpirationUpdate(const hidl_vec<uint8_t>& sessionId, int64_t expiryTimeInMS) {
-        if (mListener != NULL) {
+    Return<void> sendExpirationUpdate(
+            const hidl_vec<uint8_t>& sessionId,
+            int64_t expiryTimeInMS) {
+        if (mListenerV1_2 != NULL) {
+            mListenerV1_2->sendExpirationUpdate(sessionId, expiryTimeInMS);
+        } else if (mListener != NULL) {
             mListener->sendExpirationUpdate(sessionId, expiryTimeInMS);
         } else {
             ALOGE("Null event listener, event not sent");
@@ -270,9 +323,12 @@
         return Void();
     }
 
-    Return<void> sendKeysChange(const hidl_vec<uint8_t>& sessionId,
+    Return<void> sendKeysChange(
+            const hidl_vec<uint8_t>& sessionId,
             const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) {
-        if (mListener != NULL) {
+        if (mListenerV1_2 != NULL) {
+            mListenerV1_2->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
+        } else if (mListener != NULL) {
             mListener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
         } else {
             ALOGE("Null event listener, event not sent");
@@ -280,6 +336,23 @@
         return Void();
     }
 
+    Return<void> sendKeysChange_1_2(
+            const hidl_vec<uint8_t>& sessionId,
+            const hidl_vec<KeyStatus_V1_2>& keyStatusList, bool hasNewUsableKey) {
+        if (mListenerV1_2 != NULL) {
+            mListenerV1_2->sendKeysChange_1_2(sessionId, keyStatusList, hasNewUsableKey);
+        }
+        return Void();
+    }
+
+    Return<void> sendSessionLostState(
+            const hidl_vec<uint8_t>& sessionId) {
+        if (mListenerV1_2 != NULL) {
+            mListenerV1_2->sendSessionLostState(sessionId);
+        }
+        return Void();
+    }
+
     Return<void> getSecureStops(getSecureStops_cb _hidl_cb);
 
     Return<void> getSecureStop(const hidl_vec<uint8_t>& secureStopId,
@@ -300,18 +373,19 @@
 private:
     void initProperties();
     void installSecureStop(const hidl_vec<uint8_t>& sessionId);
+    bool makeKeySetId(std::string* keySetId);
     void setPlayPolicy();
 
     Return<Status> setSecurityLevel(const hidl_vec<uint8_t>& sessionId,
             SecurityLevel level);
 
-    Status getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
+    Status_V1_2 getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
             const hidl_vec<uint8_t>& initData,
             const hidl_string& mimeType,
             KeyType keyType,
             const hidl_vec<KeyValue>& optionalParameters,
             std::vector<uint8_t> *request,
-            KeyRequestType *getKeyRequestType,
+            KeyRequestType_V1_1 *getKeyRequestType,
             std::string *defaultUrl);
 
     struct ClearkeySecureStop {
@@ -323,19 +397,31 @@
     std::vector<KeyValue> mPlayPolicy;
     std::map<std::string, std::string> mStringProperties;
     std::map<std::string, std::vector<uint8_t> > mByteArrayProperties;
+    std::map<std::string, std::vector<uint8_t> > mReleaseKeysMap;
     std::map<std::vector<uint8_t>, SecurityLevel> mSecurityLevel;
     sp<IDrmPluginListener> mListener;
+    sp<IDrmPluginListener_V1_2> mListenerV1_2;
     SessionLibrary *mSessionLibrary;
     int64_t mOpenSessionOkCount;
     int64_t mCloseSessionOkCount;
     int64_t mCloseSessionNotOpenedCount;
     uint32_t mNextSecureStopId;
 
+    // set by property to mock error scenarios
+    Status_V1_2 mMockError;
+
+    void processMockError(const sp<Session> &session) {
+        session->setMockError(mMockError);
+        mMockError = Status_V1_2::OK;
+    }
+
+    DeviceFiles mFileHandle;
+
     CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin);
 };
 
 } // 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 3189c4a..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;
@@ -34,7 +34,8 @@
     InitDataParser() {}
 
     Status parse(const std::vector<uint8_t>& initData,
-            const std::string& type,
+            const std::string& mimeType,
+            V1_0::KeyType keyType,
             std::vector<uint8_t>* licenseRequest);
 
 private:
@@ -43,12 +44,12 @@
     Status parsePssh(const std::vector<uint8_t>& initData,
             std::vector<const uint8_t*>* keyIds);
 
-    std::string generateRequest(
+    std::string generateRequest(V1_0::KeyType keyType,
             const std::vector<const uint8_t*>& keyIds);
 };
 
 } // 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
new file mode 100644
index 0000000..bcd9fd6
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h
@@ -0,0 +1,68 @@
+// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
+// source code may only be used and distributed under the Widevine Master
+// License Agreement.
+//
+#ifndef CLEARKEY_MEMORY_FILE_SYSTEM_H_
+#define CLEARKEY_MEMORY_FILE_SYSTEM_H_
+
+#include <map>
+#include <string>
+
+#include "ClearKeyTypes.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace clearkey {
+
+// Using android file system requires clearkey plugin to update
+// its sepolicy. However, we are unable to update sepolicy for
+// older vendor partitions. To provide backward compatibility,
+// clearkey plugin implements a very simple file system in memory.
+// This memory file system does not support directory structure.
+class MemoryFileSystem {
+ public:
+    struct MemoryFile {
+        std::string fileName;  // excludes path
+        std::string content;
+        size_t fileSize;
+
+        std::string getContent() const { return content; }
+        size_t getFileSize() const { return fileSize; }
+        void setContent(const std::string& file) { content = file; }
+        void setFileName(const std::string& name) { fileName = name; }
+        void setFileSize(size_t size) {
+            content.resize(size); fileSize = size;
+        }
+    };
+
+    MemoryFileSystem() {};
+    virtual ~MemoryFileSystem() {};
+
+    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);
+    size_t Write(const std::string& pathName, const MemoryFile& memoryFile);
+
+ private:
+    // License file name is made up of a unique keySetId, therefore,
+    // the filename can be used as the key to locate licenses in the
+    // memory file system.
+    std::map<std::string, MemoryFile> mMemoryFileSystem;
+
+    std::string GetFileName(const std::string& path);
+
+    CLEARKEY_DISALLOW_COPY_AND_ASSIGN(MemoryFileSystem);
+};
+
+} // namespace clearkey
+} // namespace V1_2
+} // namespace drm
+} // namespace hardware
+} // namespace android
+
+#endif  // CLEARKEY_MEMORY_FILE_SYSTEM_H_
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
index cddfca5..a159e5a 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h
@@ -27,43 +27,53 @@
 namespace android {
 namespace hardware {
 namespace drm {
-namespace V1_1 {
+namespace V1_2 {
 namespace clearkey {
 
-using ::android::hardware::drm::V1_0::Status;
-using ::android::hardware::drm::V1_0::SubSample;
+namespace drm = ::android::hardware::drm;
+using drm::V1_0::Status;
+using drm::V1_0::SubSample;
+
+typedef drm::V1_2::Status Status_V1_2;
 
 class Session : public RefBase {
 public:
     explicit Session(const std::vector<uint8_t>& sessionId)
-            : mSessionId(sessionId) {}
+        : mSessionId(sessionId), mMockError(Status_V1_2::OK) {}
     virtual ~Session() {}
 
     const std::vector<uint8_t>& sessionId() const { return mSessionId; }
 
     Status getKeyRequest(
-            const std::vector<uint8_t>& mimeType,
-            const std::string& initDataType,
+            const std::vector<uint8_t>& initDataType,
+            const std::string& mimeType,
+            V1_0::KeyType keyType,
             std::vector<uint8_t>* keyRequest) const;
 
     Status provideKeyResponse(
             const std::vector<uint8_t>& response);
 
-    Status decrypt(
+    Status_V1_2 decrypt(
             const KeyId keyId, const Iv iv, const uint8_t* srcPtr,
             uint8_t* dstPtr, const std::vector<SubSample> subSamples,
             size_t* bytesDecryptedOut);
 
+    void setMockError(Status_V1_2 error) {mMockError = error;}
+    Status_V1_2 getMockError() const {return mMockError;}
+
 private:
     CLEARKEY_DISALLOW_COPY_AND_ASSIGN(Session);
 
     const std::vector<uint8_t> mSessionId;
     KeyMap mKeyMap;
     Mutex mMapLock;
+
+    // For mocking error return scenarios
+    Status_V1_2 mMockError;
 };
 
 } // 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..b0f8607 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;
@@ -68,8 +68,19 @@
     return vec;
 }
 
+inline Status toStatus_1_0(Status_V1_2 status) {
+  switch (status) {
+    case Status_V1_2::ERROR_DRM_INSUFFICIENT_SECURITY:
+    case Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE:
+    case Status_V1_2::ERROR_DRM_SESSION_LOST_STATE:
+      return Status::ERROR_DRM_UNKNOWN;
+    default:
+      return static_cast<Status>(status);
+  }
+}
+
 }  // 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
new file mode 100644
index 0000000..3e11f0b
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/protos/DeviceFiles.proto
@@ -0,0 +1,47 @@
+// ----------------------------------------------------------------------------
+// device_files.proto
+// ----------------------------------------------------------------------------
+// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
+// source code may only be used and distributed under the Widevine Master
+// License Agreement.
+//
+// Description:
+//   Format of various files stored at the device.
+//
+syntax = "proto2";
+
+package android.hardware.drm.V1_2.clearkey;
+
+// need this if we are using libprotobuf-cpp-2.3.0-lite
+option optimize_for = LITE_RUNTIME;
+
+message License {
+  enum LicenseState {
+    ACTIVE = 1;
+    RELEASING = 2;
+  }
+
+  optional LicenseState state = 1;
+  optional bytes license = 2;
+}
+
+message OfflineFile {
+  enum FileType {
+    LICENSE = 1;
+  }
+
+  enum FileVersion {
+    VERSION_1 = 1;
+  }
+
+  optional FileType type = 1;
+  optional FileVersion version = 2 [default = VERSION_1];
+  optional License license = 3;
+
+}
+
+message HashedFile {
+  optional bytes file = 1;
+  // A raw (not hex-encoded) SHA256, taken over the bytes of 'file'.
+  optional bytes hash = 2;
+}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/service.cpp b/drm/mediadrm/plugins/clearkey/hidl/service.cpp
index 6a97b72..b39ea01 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/service.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/service.cpp
@@ -13,32 +13,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#define LOG_TAG "android.hardware.drm@1.1-service.clearkey"
-
 #include <CryptoFactory.h>
 #include <DrmFactory.h>
 
 #include <android-base/logging.h>
 #include <binder/ProcessState.h>
+#include <hidl/HidlLazyUtils.h>
 #include <hidl/HidlTransportSupport.h>
 
 using ::android::hardware::configureRpcThreadpool;
 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...");
-
-    // The DRM HAL may communicate to other vendor components via
-    // /dev/vndbinder
-    android::ProcessState::initWithDriver("/dev/vndbinder");
-
     sp<IDrmFactory> drmFactory = new DrmFactory;
     sp<ICryptoFactory> cryptoFactory = new CryptoFactory;
 
diff --git a/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp b/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp
new file mode 100644
index 0000000..99fd883
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/serviceLazy.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <CryptoFactory.h>
+#include <DrmFactory.h>
+
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
+#include <hidl/HidlLazyUtils.h>
+#include <hidl/HidlTransportSupport.h>
+
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::joinRpcThreadpool;
+using ::android::sp;
+
+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;
+using android::hardware::LazyServiceRegistrar;
+
+int main(int /* argc */, char** /* argv */) {
+    sp<IDrmFactory> drmFactory = new DrmFactory;
+    sp<ICryptoFactory> cryptoFactory = new CryptoFactory;
+
+    configureRpcThreadpool(8, true /* callerWillJoin */);
+
+    // Setup hwbinder service
+    LazyServiceRegistrar serviceRegistrar;
+
+    // Setup hwbinder service
+    CHECK_EQ(serviceRegistrar.registerService(drmFactory, "clearkey"), android::NO_ERROR)
+        << "Failed to register Clearkey Factory HAL";
+    CHECK_EQ(serviceRegistrar.registerService(cryptoFactory, "clearkey"), android::NO_ERROR)
+        << "Failed to register Clearkey Crypto  HAL";
+
+    joinRpcThreadpool();
+}
diff --git a/include/common_time/ICommonClock.h b/include/common_time/ICommonClock.h
deleted file mode 100644
index d7073f1..0000000
--- a/include/common_time/ICommonClock.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_ICOMMONCLOCK_H
-#define ANDROID_ICOMMONCLOCK_H
-
-#include <stdint.h>
-#include <linux/socket.h>
-
-#include <binder/IInterface.h>
-#include <binder/IServiceManager.h>
-
-namespace android {
-
-class ICommonClockListener : public IInterface {
-  public:
-    DECLARE_META_INTERFACE(CommonClockListener);
-
-    virtual void onTimelineChanged(uint64_t timelineID) = 0;
-};
-
-class BnCommonClockListener : public BnInterface<ICommonClockListener> {
-  public:
-    virtual status_t onTransact(uint32_t code, const Parcel& data,
-                                Parcel* reply, uint32_t flags = 0);
-};
-
-class ICommonClock : public IInterface {
-  public:
-    DECLARE_META_INTERFACE(CommonClock);
-
-    // Name of the ICommonClock service registered with the service manager.
-    static const String16 kServiceName;
-
-    // a reserved invalid timeline ID
-    static const uint64_t kInvalidTimelineID;
-
-    // a reserved invalid error estimate
-    static const int32_t kErrorEstimateUnknown;
-
-    enum State {
-        // the device just came up and is trying to discover the master
-        STATE_INITIAL,
-
-        // the device is a client of a master
-        STATE_CLIENT,
-
-        // the device is acting as master
-        STATE_MASTER,
-
-        // the device has lost contact with its master and needs to participate
-        // in the election of a new master
-        STATE_RONIN,
-
-        // the device is waiting for announcement of the newly elected master
-        STATE_WAIT_FOR_ELECTION,
-    };
-
-    virtual status_t isCommonTimeValid(bool* valid, uint32_t* timelineID) = 0;
-    virtual status_t commonTimeToLocalTime(int64_t commonTime,
-                                           int64_t* localTime) = 0;
-    virtual status_t localTimeToCommonTime(int64_t localTime,
-                                           int64_t* commonTime) = 0;
-    virtual status_t getCommonTime(int64_t* commonTime) = 0;
-    virtual status_t getCommonFreq(uint64_t* freq) = 0;
-    virtual status_t getLocalTime(int64_t* localTime) = 0;
-    virtual status_t getLocalFreq(uint64_t* freq) = 0;
-    virtual status_t getEstimatedError(int32_t* estimate) = 0;
-    virtual status_t getTimelineID(uint64_t* id) = 0;
-    virtual status_t getState(State* state) = 0;
-    virtual status_t getMasterAddr(struct sockaddr_storage* addr) = 0;
-
-    virtual status_t registerListener(
-            const sp<ICommonClockListener>& listener) = 0;
-    virtual status_t unregisterListener(
-            const sp<ICommonClockListener>& listener) = 0;
-
-    // Simple helper to make it easier to connect to the CommonClock service.
-    static inline sp<ICommonClock> getInstance() {
-        sp<IBinder> binder = defaultServiceManager()->checkService(
-                ICommonClock::kServiceName);
-        sp<ICommonClock> clk = interface_cast<ICommonClock>(binder);
-        return clk;
-    }
-};
-
-class BnCommonClock : public BnInterface<ICommonClock> {
-  public:
-    virtual status_t onTransact(uint32_t code, const Parcel& data,
-                                Parcel* reply, uint32_t flags = 0);
-};
-
-};  // namespace android
-
-#endif  // ANDROID_ICOMMONCLOCK_H
diff --git a/include/common_time/ICommonTimeConfig.h b/include/common_time/ICommonTimeConfig.h
deleted file mode 100644
index 497b666..0000000
--- a/include/common_time/ICommonTimeConfig.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ICOMMONTIMECONFIG_H
-#define ANDROID_ICOMMONTIMECONFIG_H
-
-#include <stdint.h>
-#include <linux/socket.h>
-
-#include <binder/IInterface.h>
-#include <binder/IServiceManager.h>
-
-namespace android {
-
-class String16;
-
-class ICommonTimeConfig : public IInterface {
-  public:
-    DECLARE_META_INTERFACE(CommonTimeConfig);
-
-    // Name of the ICommonTimeConfig service registered with the service
-    // manager.
-    static const String16 kServiceName;
-
-    virtual status_t getMasterElectionPriority(uint8_t *priority) = 0;
-    virtual status_t setMasterElectionPriority(uint8_t priority) = 0;
-    virtual status_t getMasterElectionEndpoint(struct sockaddr_storage *addr) = 0;
-    virtual status_t setMasterElectionEndpoint(const struct sockaddr_storage *addr) = 0;
-    virtual status_t getMasterElectionGroupId(uint64_t *id) = 0;
-    virtual status_t setMasterElectionGroupId(uint64_t id) = 0;
-    virtual status_t getInterfaceBinding(String16& ifaceName) = 0;
-    virtual status_t setInterfaceBinding(const String16& ifaceName) = 0;
-    virtual status_t getMasterAnnounceInterval(int *interval) = 0;
-    virtual status_t setMasterAnnounceInterval(int interval) = 0;
-    virtual status_t getClientSyncInterval(int *interval) = 0;
-    virtual status_t setClientSyncInterval(int interval) = 0;
-    virtual status_t getPanicThreshold(int *threshold) = 0;
-    virtual status_t setPanicThreshold(int threshold) = 0;
-    virtual status_t getAutoDisable(bool *autoDisable) = 0;
-    virtual status_t setAutoDisable(bool autoDisable) = 0;
-    virtual status_t forceNetworklessMasterMode() = 0;
-
-    // Simple helper to make it easier to connect to the CommonTimeConfig service.
-    static inline sp<ICommonTimeConfig> getInstance() {
-        sp<IBinder> binder = defaultServiceManager()->checkService(
-                ICommonTimeConfig::kServiceName);
-        sp<ICommonTimeConfig> clk = interface_cast<ICommonTimeConfig>(binder);
-        return clk;
-    }
-};
-
-class BnCommonTimeConfig : public BnInterface<ICommonTimeConfig> {
-  public:
-    virtual status_t onTransact(uint32_t code, const Parcel& data,
-                                Parcel* reply, uint32_t flags = 0);
-};
-
-};  // namespace android
-
-#endif  // ANDROID_ICOMMONTIMECONFIG_H
diff --git a/include/common_time/cc_helper.h b/include/common_time/cc_helper.h
deleted file mode 100644
index 8c4d5c0..0000000
--- a/include/common_time/cc_helper.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 __CC_HELPER_H__
-#define __CC_HELPER_H__
-
-#include <stdint.h>
-#include <common_time/ICommonClock.h>
-#include <utils/threads.h>
-
-namespace android {
-
-// CCHelper is a simple wrapper class to help with centralizing access to the
-// Common Clock service and implementing lifetime managment, as well as to
-// implement a simple policy of making a basic attempt to reconnect to the
-// common clock service when things go wrong.
-//
-// On platforms which run the native common_time service in auto-disable mode,
-// the service will go into networkless mode whenever it has no active clients.
-// It tracks active clients using registered CommonClockListeners (the callback
-// interface for onTimelineChanged) since this provides a convienent death
-// handler notification for when the service's clients die unexpectedly.  This
-// means that users of the common time service should really always have a
-// CommonClockListener, unless they know that the time service is not running in
-// auto disabled mode, or that there is at least one other registered listener
-// active in the system.  The CCHelper makes this a little easier by sharing a
-// ref counted ICommonClock interface across all clients and automatically
-// registering and unregistering a listener whenever there are CCHelper
-// instances active in the process.
-class CCHelper {
-  public:
-    CCHelper();
-    ~CCHelper();
-
-    status_t isCommonTimeValid(bool* valid, uint32_t* timelineID);
-    status_t commonTimeToLocalTime(int64_t commonTime, int64_t* localTime);
-    status_t localTimeToCommonTime(int64_t localTime, int64_t* commonTime);
-    status_t getCommonTime(int64_t* commonTime);
-    status_t getCommonFreq(uint64_t* freq);
-    status_t getLocalTime(int64_t* localTime);
-    status_t getLocalFreq(uint64_t* freq);
-
-  private:
-    class CommonClockListener : public BnCommonClockListener {
-      public:
-        void onTimelineChanged(uint64_t timelineID);
-    };
-
-    static bool verifyClock_l();
-
-    static Mutex lock_;
-    static sp<ICommonClock> common_clock_;
-    static sp<ICommonClockListener> common_clock_listener_;
-    static uint32_t ref_count_;
-};
-
-
-}  // namespace android
-#endif  // __CC_HELPER_H__
diff --git a/include/common_time/local_clock.h b/include/common_time/local_clock.h
deleted file mode 100644
index 384c3de..0000000
--- a/include/common_time/local_clock.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 __LOCAL_CLOCK_H__
-#define __LOCAL_CLOCK_H__
-
-#include <stdint.h>
-
-#include <hardware/local_time_hal.h>
-#include <utils/Errors.h>
-#include <utils/threads.h>
-
-namespace android {
-
-class LocalClock {
-  public:
-    LocalClock();
-
-    bool initCheck();
-
-    int64_t  getLocalTime();
-    uint64_t getLocalFreq();
-    status_t setLocalSlew(int16_t rate);
-    int32_t  getDebugLog(struct local_time_debug_event* records,
-                         int max_records);
-
-  private:
-    static Mutex dev_lock_;
-    static local_time_hw_device_t* dev_;
-};
-
-}  // namespace android
-#endif  // __LOCAL_CLOCK_H__
diff --git a/include/media/AudioAttributes.h b/include/media/AudioAttributes.h
new file mode 120000
index 0000000..27ba471
--- /dev/null
+++ b/include/media/AudioAttributes.h
@@ -0,0 +1 @@
+../../media/libaudioclient/include/media/AudioAttributes.h
\ No newline at end of file
diff --git a/include/media/AudioCommonTypes.h b/include/media/AudioCommonTypes.h
new file mode 120000
index 0000000..ae7c99a
--- /dev/null
+++ b/include/media/AudioCommonTypes.h
@@ -0,0 +1 @@
+../../media/libaudioclient/include/media/AudioCommonTypes.h
\ No newline at end of file
diff --git a/include/media/AudioPolicyHelper.h b/include/media/AudioPolicyHelper.h
deleted file mode 120000
index 558657e..0000000
--- a/include/media/AudioPolicyHelper.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libaudioclient/include/media/AudioPolicyHelper.h
\ No newline at end of file
diff --git a/include/media/AudioPresentationInfo.h b/include/media/AudioPresentationInfo.h
deleted file mode 100644
index e91a992..0000000
--- a/include/media/AudioPresentationInfo.h
+++ /dev/null
@@ -1,79 +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.
- */
-
-#ifndef AUDIO_PRESENTATION_INFO_H_
-#define AUDIO_PRESENTATION_INFO_H_
-
-#include <sstream>
-#include <stdint.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-enum mastering_indication {
-    MASTERING_NOT_INDICATED,
-    MASTERED_FOR_STEREO,
-    MASTERED_FOR_SURROUND,
-    MASTERED_FOR_3D,
-    MASTERED_FOR_HEADPHONE,
-};
-
-struct AudioPresentation : public RefBase {
-    int32_t mPresentationId;
-    int32_t mProgramId;
-    KeyedVector<String8, String8> mLabels;
-    String8 mLanguage;
-    int32_t mMasteringIndication;
-    bool mAudioDescriptionAvailable;
-    bool mSpokenSubtitlesAvailable;
-    bool mDialogueEnhancementAvailable;
-
-    AudioPresentation() {
-        mPresentationId = -1;
-        mProgramId = -1;
-        mLanguage = "";
-        mMasteringIndication = MASTERING_NOT_INDICATED;
-        mAudioDescriptionAvailable = false;
-        mSpokenSubtitlesAvailable = false;
-        mDialogueEnhancementAvailable = false;
-    }
-};
-
-typedef Vector<sp<AudioPresentation>> AudioPresentations;
-
-class AudioPresentationInfo : public RefBase {
- public:
-    AudioPresentationInfo();
-
-    ~AudioPresentationInfo();
-
-    void addPresentation(sp<AudioPresentation> presentation);
-
-    size_t countPresentations() const;
-
-    const sp<AudioPresentation> getPresentation(size_t index) const;
-
- private:
-    AudioPresentations mPresentations;
-};
-
-}  // namespace android
-
-#endif  // AUDIO_PRESENTATION_INFO_H_
diff --git a/include/media/AudioProductStrategy.h b/include/media/AudioProductStrategy.h
new file mode 120000
index 0000000..6bfaf11
--- /dev/null
+++ b/include/media/AudioProductStrategy.h
@@ -0,0 +1 @@
+../../media/libaudioclient/include/media/AudioProductStrategy.h
\ No newline at end of file
diff --git a/include/media/AudioVolumeGroup.h b/include/media/AudioVolumeGroup.h
new file mode 120000
index 0000000..d6f1c99
--- /dev/null
+++ b/include/media/AudioVolumeGroup.h
@@ -0,0 +1 @@
+../../media/libaudioclient/include/media/AudioVolumeGroup.h
\ No newline at end of file
diff --git a/include/media/DataSource.h b/include/media/DataSource.h
index 905bec1..198b27e 120000
--- a/include/media/DataSource.h
+++ b/include/media/DataSource.h
@@ -1 +1 @@
-../../media/libmediaextractor/include/media/DataSource.h
\ No newline at end of file
+stagefright/DataSource.h
\ No newline at end of file
diff --git a/include/media/DataSourceBase.h b/include/media/DataSourceBase.h
index 54c8047..d2ab2f1 120000
--- a/include/media/DataSourceBase.h
+++ b/include/media/DataSourceBase.h
@@ -1 +1 @@
-../../media/libmediaextractor/include/media/DataSourceBase.h
\ No newline at end of file
+stagefright/DataSourceBase.h
\ No newline at end of file
diff --git a/include/media/EventLog.h b/include/media/EventLog.h
new file mode 120000
index 0000000..9b2c4bf
--- /dev/null
+++ b/include/media/EventLog.h
@@ -0,0 +1 @@
+../../media/utils/include/mediautils/EventLog.h
\ No newline at end of file
diff --git a/include/media/ExtractorUtils.h b/include/media/ExtractorUtils.h
deleted file mode 120000
index e2dd082..0000000
--- a/include/media/ExtractorUtils.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmediaextractor/include/media/ExtractorUtils.h
\ No newline at end of file
diff --git a/media/libmediaextractor/include/media/ExtractorUtils.h b/include/media/ExtractorUtils.h
similarity index 100%
rename from media/libmediaextractor/include/media/ExtractorUtils.h
rename to include/media/ExtractorUtils.h
diff --git a/include/media/MediaExtractor.h b/include/media/MediaExtractor.h
deleted file mode 120000
index 4b35fe1..0000000
--- a/include/media/MediaExtractor.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmediaextractor/include/media/MediaExtractor.h
\ No newline at end of file
diff --git a/include/media/MediaExtractorPluginApi.h b/include/media/MediaExtractorPluginApi.h
new file mode 100644
index 0000000..916472c
--- /dev/null
+++ b/include/media/MediaExtractorPluginApi.h
@@ -0,0 +1,168 @@
+/*
+ * 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 MEDIA_EXTRACTOR_PLUGIN_API_H_
+#define MEDIA_EXTRACTOR_PLUGIN_API_H_
+
+#include <utils/Errors.h> // for status_t
+#include <media/NdkMediaError.h>
+struct AMediaFormat;
+
+namespace android {
+
+struct MediaTrack;
+class MetaDataBase;
+class MediaBufferBase;
+
+extern "C" {
+
+struct CDataSource {
+    ssize_t (*readAt)(void *handle, off64_t offset, void *data, size_t size);
+    status_t (*getSize)(void *handle, off64_t *size);
+    uint32_t (*flags)(void *handle );
+    bool (*getUri)(void *handle, char *uriString, size_t bufferSize);
+    void *handle;
+};
+
+enum CMediaTrackReadOptions : uint32_t {
+    SEEK_PREVIOUS_SYNC = 0,
+    SEEK_NEXT_SYNC = 1,
+    SEEK_CLOSEST_SYNC = 2,
+    SEEK_CLOSEST = 3,
+    SEEK_FRAME_INDEX = 4,
+    SEEK = 8,
+    NONBLOCKING = 16
+};
+
+/**
+ * only use CMediaBuffer allocated from the CMediaBufferGroup that is
+ * provided to CMediaTrack::start()
+ */
+struct CMediaBuffer {
+    void *handle;
+    void (*release)(void *handle);
+    void* (*data)(void *handle);
+    size_t (*size)(void *handle);
+    size_t (*range_offset)(void *handle);
+    size_t (*range_length)(void *handle);
+    void (*set_range)(void *handle, size_t offset, size_t length);
+    AMediaFormat* (*meta_data)(void *handle);
+};
+
+struct CMediaBufferGroup {
+    void *handle;
+    bool (*init)(void *handle, size_t buffers, size_t buffer_size, size_t growthLimit);
+    void (*add_buffer)(void *handle, size_t size);
+    media_status_t (*acquire_buffer)(void *handle,
+            CMediaBuffer **buffer, bool nonBlocking, size_t requestedSize);
+    bool (*has_buffers)(void *handle);
+};
+
+struct CMediaTrack {
+    void *data;
+    void (*free)(void *data);
+
+    media_status_t (*start)(void *data, CMediaBufferGroup *bufferGroup);
+    media_status_t (*stop)(void *data);
+    media_status_t (*getFormat)(void *data, AMediaFormat *format);
+    media_status_t (*read)(void *data, CMediaBuffer **buffer, uint32_t options, int64_t seekPosUs);
+    bool     (*supportsNonBlockingRead)(void *data);
+};
+
+struct CMediaExtractor {
+    void *data;
+
+    void (*free)(void *data);
+    size_t (*countTracks)(void *data);
+    CMediaTrack* (*getTrack)(void *data, size_t index);
+    media_status_t (*getTrackMetaData)(
+            void *data,
+            AMediaFormat *meta,
+            size_t index, uint32_t flags);
+
+    media_status_t (*getMetaData)(void *data, AMediaFormat *meta);
+    uint32_t (*flags)(void *data);
+    media_status_t (*setMediaCas)(void *data, const uint8_t* casToken, size_t size);
+    const char * (*name)(void *data);
+};
+
+typedef CMediaExtractor* (*CreatorFunc)(CDataSource *source, void *meta);
+typedef void (*FreeMetaFunc)(void *meta);
+
+// The sniffer can optionally fill in an opaque object, "meta", that helps
+// the corresponding extractor initialize its state without duplicating
+// effort already exerted by the sniffer. If "freeMeta" is given, it will be
+// called against the opaque object when it is no longer used.
+typedef CreatorFunc (*SnifferFunc)(
+        CDataSource *source, float *confidence,
+        void **meta, FreeMetaFunc *freeMeta);
+
+typedef CMediaExtractor CMediaExtractor;
+typedef CreatorFunc CreatorFunc;
+
+
+typedef struct {
+    const uint8_t b[16];
+} media_uuid_t;
+
+struct ExtractorDef {
+    // version number of this structure
+    const uint32_t def_version;
+
+    // A unique identifier for this extractor.
+    // See below for a convenience macro to create this from a string.
+    media_uuid_t extractor_uuid;
+
+    // Version number of this extractor. When two extractors with the same
+    // uuid are encountered, the one with the largest version number will
+    // be used.
+    const uint32_t extractor_version;
+
+    // a human readable name
+    const char *extractor_name;
+
+    union {
+        struct {
+            SnifferFunc sniff;
+        } v2;
+        struct {
+            SnifferFunc sniff;
+            // a NULL terminated list of container mime types and/or file extensions
+            // that this extractor supports
+            const char **supported_types;
+        } v3;
+    } u;
+};
+
+// the C++ based API which first shipped in P and is no longer supported
+const uint32_t EXTRACTORDEF_VERSION_LEGACY = 1;
+
+// the first C/NDK based API
+const uint32_t EXTRACTORDEF_VERSION_NDK_V1 = 2;
+
+// the second C/NDK based API
+const uint32_t EXTRACTORDEF_VERSION_NDK_V2 = 3;
+
+const uint32_t EXTRACTORDEF_VERSION = EXTRACTORDEF_VERSION_NDK_V2;
+
+// each plugin library exports one function of this type
+typedef ExtractorDef (*GetExtractorDef)();
+
+} // extern "C"
+
+}  // namespace android
+
+#endif  // MEDIA_EXTRACTOR_PLUGIN_API_H_
diff --git a/include/media/MediaExtractorPluginHelper.h b/include/media/MediaExtractorPluginHelper.h
new file mode 100644
index 0000000..b86f177
--- /dev/null
+++ b/include/media/MediaExtractorPluginHelper.h
@@ -0,0 +1,494 @@
+/*
+ * 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 MEDIA_EXTRACTOR_PLUGIN_HELPER_H_
+
+#define MEDIA_EXTRACTOR_PLUGIN_HELPER_H_
+
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <map>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+#include <media/MediaExtractorPluginApi.h>
+#include <media/NdkMediaFormat.h>
+
+namespace android {
+
+class DataSourceBase;
+class MetaDataBase;
+struct MediaTrack;
+
+
+class MediaTrackHelper;
+
+class MediaBufferHelper {
+private:
+    friend CMediaTrack *wrap(MediaTrackHelper *);
+    CMediaBuffer *mBuffer;
+public:
+    MediaBufferHelper(CMediaBuffer *buf) {
+        mBuffer = buf;
+    }
+
+    virtual ~MediaBufferHelper() {}
+
+    virtual void release() {
+        mBuffer->release(mBuffer->handle);
+    }
+
+    virtual void* data() {
+        return mBuffer->data(mBuffer->handle);
+    }
+
+    virtual size_t size() {
+        return mBuffer->size(mBuffer->handle);
+    }
+
+    virtual size_t range_offset() {
+        return mBuffer->range_offset(mBuffer->handle);
+    }
+
+    virtual size_t range_length() {
+        return mBuffer->range_length(mBuffer->handle);
+    }
+
+    virtual void set_range(size_t offset, size_t length) {
+        mBuffer->set_range(mBuffer->handle, offset, length);
+    }
+    virtual AMediaFormat *meta_data() {
+        return mBuffer->meta_data(mBuffer->handle);
+    }
+};
+
+class MediaBufferGroupHelper {
+private:
+    CMediaBufferGroup *mGroup;
+    std::map<CMediaBuffer*, MediaBufferHelper*> mBufferHelpers;
+public:
+    MediaBufferGroupHelper(CMediaBufferGroup *group) {
+        mGroup = group;
+    }
+    ~MediaBufferGroupHelper() {
+        // delete all entries in map
+        ALOGV("buffergroup %p map has %zu entries", this, mBufferHelpers.size());
+        for (auto it = mBufferHelpers.begin(); it != mBufferHelpers.end(); ++it) {
+            delete it->second;
+        }
+    }
+    bool init(size_t buffers, size_t buffer_size, size_t growthLimit = 0) {
+        return mGroup->init(mGroup->handle, buffers, buffer_size, growthLimit);
+    }
+    void add_buffer(size_t size) {
+        mGroup->add_buffer(mGroup->handle, size);
+    }
+    media_status_t acquire_buffer(
+            MediaBufferHelper **buffer, bool nonBlocking = false, size_t requestedSize = 0) {
+        CMediaBuffer *buf = nullptr;
+        media_status_t ret =
+                mGroup->acquire_buffer(mGroup->handle, &buf, nonBlocking, requestedSize);
+        if (ret == AMEDIA_OK && buf != nullptr) {
+            auto helper = mBufferHelpers.find(buf);
+            if (helper == mBufferHelpers.end()) {
+                MediaBufferHelper* newHelper = new MediaBufferHelper(buf);
+                mBufferHelpers.insert(std::make_pair(buf, newHelper));
+                *buffer = newHelper;
+            } else {
+                *buffer = helper->second;
+            }
+        } else {
+            *buffer = nullptr;
+        }
+        return ret;
+    }
+    bool has_buffers() {
+        return mGroup->has_buffers(mGroup->handle);
+    }
+};
+
+class MediaTrackHelper {
+public:
+    MediaTrackHelper() : mBufferGroup(nullptr) {
+    }
+    virtual ~MediaTrackHelper() {
+        delete mBufferGroup;
+    }
+    virtual media_status_t start() = 0;
+    virtual media_status_t stop() = 0;
+    virtual media_status_t getFormat(AMediaFormat *format) = 0;
+
+    class ReadOptions {
+    public:
+        enum SeekMode : int32_t {
+            SEEK_PREVIOUS_SYNC,
+            SEEK_NEXT_SYNC,
+            SEEK_CLOSEST_SYNC,
+            SEEK_CLOSEST,
+            SEEK_FRAME_INDEX,
+        };
+
+        ReadOptions(uint32_t options, int64_t seekPosUs) {
+            mOptions = options;
+            mSeekPosUs = seekPosUs;
+        }
+        bool getSeekTo(int64_t *time_us, SeekMode *mode) const {
+            if ((mOptions & CMediaTrackReadOptions::SEEK) == 0) {
+                return false;
+            }
+            *time_us = mSeekPosUs;
+            *mode = (SeekMode) (mOptions & 7);
+            return true;
+        }
+        bool getNonBlocking() const {
+            return mOptions & CMediaTrackReadOptions::NONBLOCKING;
+        }
+    private:
+        uint32_t mOptions;
+        int64_t mSeekPosUs;
+    };
+
+    virtual media_status_t read(
+            MediaBufferHelper **buffer, const ReadOptions *options = NULL) = 0;
+    virtual bool supportsNonBlockingRead() { return false; }
+protected:
+    friend CMediaTrack *wrap(MediaTrackHelper *track);
+    MediaBufferGroupHelper *mBufferGroup;
+};
+
+inline CMediaTrack *wrap(MediaTrackHelper *track) {
+    if (track == nullptr) {
+        return nullptr;
+    }
+    CMediaTrack *wrapper = (CMediaTrack*) malloc(sizeof(CMediaTrack));
+    wrapper->data = track;
+    wrapper->free = [](void *data) -> void {
+        delete (MediaTrackHelper*)(data);
+    };
+    wrapper->start = [](void *data, CMediaBufferGroup *bufferGroup) -> media_status_t {
+        if (((MediaTrackHelper*)data)->mBufferGroup) {
+            // this shouldn't happen, but handle it anyway
+            delete ((MediaTrackHelper*)data)->mBufferGroup;
+        }
+        ((MediaTrackHelper*)data)->mBufferGroup = new MediaBufferGroupHelper(bufferGroup);
+        return ((MediaTrackHelper*)data)->start();
+    };
+    wrapper->stop = [](void *data) -> media_status_t {
+        return ((MediaTrackHelper*)data)->stop();
+    };
+    wrapper->getFormat = [](void *data, AMediaFormat *meta) -> media_status_t {
+        return ((MediaTrackHelper*)data)->getFormat(meta);
+    };
+    wrapper->read = [](void *data, CMediaBuffer **buffer,  uint32_t options, int64_t seekPosUs)
+            -> media_status_t {
+        MediaTrackHelper::ReadOptions opts(options, seekPosUs);
+        MediaBufferHelper *buf = NULL;
+        media_status_t ret = ((MediaTrackHelper*)data)->read(&buf, &opts);
+        if (ret == AMEDIA_OK && buf != nullptr) {
+            *buffer = buf->mBuffer;
+        }
+        return ret;
+    };
+    wrapper->supportsNonBlockingRead = [](void *data) -> bool {
+                return ((MediaTrackHelper*)data)->supportsNonBlockingRead();
+    };
+    return wrapper;
+}
+
+
+// extractor plugins can derive from this class which looks remarkably
+// like MediaExtractor and can be easily wrapped in the required C API
+class MediaExtractorPluginHelper
+{
+public:
+    virtual ~MediaExtractorPluginHelper() {}
+    virtual size_t countTracks() = 0;
+    virtual MediaTrackHelper *getTrack(size_t index) = 0;
+
+    enum GetTrackMetaDataFlags {
+        kIncludeExtensiveMetaData = 1
+    };
+    virtual media_status_t getTrackMetaData(
+            AMediaFormat *meta,
+            size_t index, uint32_t flags = 0) = 0;
+
+    // Return container specific meta-data. The default implementation
+    // returns an empty metadata object.
+    virtual media_status_t getMetaData(AMediaFormat *meta) = 0;
+
+    enum Flags {
+        CAN_SEEK_BACKWARD  = 1,  // the "seek 10secs back button"
+        CAN_SEEK_FORWARD   = 2,  // the "seek 10secs forward button"
+        CAN_PAUSE          = 4,
+        CAN_SEEK           = 8,  // the "seek bar"
+    };
+
+    // If subclasses do _not_ override this, the default is
+    // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
+    virtual uint32_t flags() const {
+        return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE;
+    };
+
+    virtual media_status_t setMediaCas(const uint8_t* /*casToken*/, size_t /*size*/) {
+        return AMEDIA_ERROR_INVALID_OPERATION;
+    }
+
+    virtual const char * name() { return "<unspecified>"; }
+
+protected:
+    MediaExtractorPluginHelper() {}
+
+private:
+    MediaExtractorPluginHelper(const MediaExtractorPluginHelper &);
+    MediaExtractorPluginHelper &operator=(const MediaExtractorPluginHelper &);
+};
+
+inline CMediaExtractor *wrap(MediaExtractorPluginHelper *extractor) {
+    CMediaExtractor *wrapper = (CMediaExtractor*) malloc(sizeof(CMediaExtractor));
+    wrapper->data = extractor;
+    wrapper->free = [](void *data) -> void {
+        delete (MediaExtractorPluginHelper*)(data);
+    };
+    wrapper->countTracks = [](void *data) -> size_t {
+        return ((MediaExtractorPluginHelper*)data)->countTracks();
+    };
+    wrapper->getTrack = [](void *data, size_t index) -> CMediaTrack* {
+        return wrap(((MediaExtractorPluginHelper*)data)->getTrack(index));
+    };
+    wrapper->getTrackMetaData = [](
+            void *data,
+            AMediaFormat *meta,
+            size_t index, uint32_t flags) -> media_status_t {
+        return ((MediaExtractorPluginHelper*)data)->getTrackMetaData(meta, index, flags);
+    };
+    wrapper->getMetaData = [](
+            void *data,
+            AMediaFormat *meta) -> media_status_t {
+        return ((MediaExtractorPluginHelper*)data)->getMetaData(meta);
+    };
+    wrapper->flags = [](
+            void *data) -> uint32_t {
+        return ((MediaExtractorPluginHelper*)data)->flags();
+    };
+    wrapper->setMediaCas = [](
+            void *data, const uint8_t *casToken, size_t size) -> media_status_t {
+        return ((MediaExtractorPluginHelper*)data)->setMediaCas(casToken, size);
+    };
+    wrapper->name = [](
+            void *data) -> const char * {
+        return ((MediaExtractorPluginHelper*)data)->name();
+    };
+    return wrapper;
+}
+
+/* adds some convience methods */
+class DataSourceHelper {
+public:
+    explicit DataSourceHelper(CDataSource *csource) {
+        mSource = csource;
+    }
+
+    explicit DataSourceHelper(DataSourceHelper *source) {
+        mSource = source->mSource;
+    }
+
+    virtual ~DataSourceHelper() {}
+
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size) {
+        return mSource->readAt(mSource->handle, offset, data, size);
+    }
+
+    virtual status_t getSize(off64_t *size) {
+        return mSource->getSize(mSource->handle, size);
+    }
+
+    bool getUri(char *uriString, size_t bufferSize) {
+        return mSource->getUri(mSource->handle, uriString, bufferSize);
+    }
+
+    virtual uint32_t flags() {
+        return mSource->flags(mSource->handle);
+    }
+
+    // Convenience methods:
+    bool getUInt16(off64_t offset, uint16_t *x) {
+        *x = 0;
+
+        uint8_t byte[2];
+        if (readAt(offset, byte, 2) != 2) {
+            return false;
+        }
+
+        *x = (byte[0] << 8) | byte[1];
+
+        return true;
+    }
+
+    // 3 byte int, returned as a 32-bit int
+    bool getUInt24(off64_t offset, uint32_t *x) {
+        *x = 0;
+
+        uint8_t byte[3];
+        if (readAt(offset, byte, 3) != 3) {
+            return false;
+        }
+
+        *x = (byte[0] << 16) | (byte[1] << 8) | byte[2];
+
+        return true;
+    }
+
+    bool getUInt32(off64_t offset, uint32_t *x) {
+        *x = 0;
+
+        uint32_t tmp;
+        if (readAt(offset, &tmp, 4) != 4) {
+            return false;
+        }
+
+        *x = ntohl(tmp);
+
+        return true;
+    }
+
+    bool getUInt64(off64_t offset, uint64_t *x) {
+        *x = 0;
+
+        uint64_t tmp;
+        if (readAt(offset, &tmp, 8) != 8) {
+            return false;
+        }
+
+        *x = ((uint64_t)ntohl(tmp & 0xffffffff) << 32) | ntohl(tmp >> 32);
+
+        return true;
+    }
+
+    // read either int<N> or int<2N> into a uint<2N>_t, size is the int size in bytes.
+    bool getUInt16Var(off64_t offset, uint16_t *x, size_t size) {
+        if (size == 2) {
+            return getUInt16(offset, x);
+        }
+        if (size == 1) {
+            uint8_t tmp;
+            if (readAt(offset, &tmp, 1) == 1) {
+                *x = tmp;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool getUInt32Var(off64_t offset, uint32_t *x, size_t size) {
+        if (size == 4) {
+            return getUInt32(offset, x);
+        }
+        if (size == 2) {
+            uint16_t tmp;
+            if (getUInt16(offset, &tmp)) {
+                *x = tmp;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool getUInt64Var(off64_t offset, uint64_t *x, size_t size) {
+        if (size == 8) {
+            return getUInt64(offset, x);
+        }
+        if (size == 4) {
+            uint32_t tmp;
+            if (getUInt32(offset, &tmp)) {
+                *x = tmp;
+                return true;
+            }
+        }
+        return false;
+    }
+
+protected:
+    CDataSource *mSource;
+};
+
+
+
+// helpers to create a media_uuid_t from a string literal
+
+// purposely not defined anywhere so that this will fail to link if
+// expressions below are not evaluated at compile time
+int invalid_uuid_string(const char *);
+
+template <typename T, size_t N>
+constexpr uint8_t _digitAt_(const T (&s)[N], const size_t n) {
+    return s[n] >= '0' && s[n] <= '9' ? s[n] - '0'
+            : s[n] >= 'a' && s[n] <= 'f' ? s[n] - 'a' + 10
+                    : s[n] >= 'A' && s[n] <= 'F' ? s[n] - 'A' + 10
+                            : invalid_uuid_string("uuid: bad digits");
+}
+
+template <typename T, size_t N>
+constexpr uint8_t _hexByteAt_(const T (&s)[N], size_t n) {
+    return (_digitAt_(s, n) << 4) + _digitAt_(s, n + 1);
+}
+
+constexpr bool _assertIsDash_(char c) {
+    return c == '-' ? true : invalid_uuid_string("Wrong format");
+}
+
+template <size_t N>
+constexpr media_uuid_t constUUID(const char (&s) [N]) {
+    static_assert(N == 37, "uuid: wrong length");
+    return
+            _assertIsDash_(s[8]),
+            _assertIsDash_(s[13]),
+            _assertIsDash_(s[18]),
+            _assertIsDash_(s[23]),
+            media_uuid_t {{
+                _hexByteAt_(s, 0),
+                _hexByteAt_(s, 2),
+                _hexByteAt_(s, 4),
+                _hexByteAt_(s, 6),
+                _hexByteAt_(s, 9),
+                _hexByteAt_(s, 11),
+                _hexByteAt_(s, 14),
+                _hexByteAt_(s, 16),
+                _hexByteAt_(s, 19),
+                _hexByteAt_(s, 21),
+                _hexByteAt_(s, 24),
+                _hexByteAt_(s, 26),
+                _hexByteAt_(s, 28),
+                _hexByteAt_(s, 30),
+                _hexByteAt_(s, 32),
+                _hexByteAt_(s, 34),
+            }};
+}
+// Convenience macro to create a media_uuid_t from a string literal, which should
+// be formatted as "12345678-1234-1234-1234-123456789abc", as generated by
+// e.g. https://www.uuidgenerator.net/ or the 'uuidgen' linux command.
+// Hex digits may be upper or lower case.
+//
+// The macro call is otherwise equivalent to specifying the structure directly
+// (e.g. UUID("7d613858-5837-4a38-84c5-332d1cddee27") is the same as
+//       {{0x7d, 0x61, 0x38, 0x58, 0x58, 0x37, 0x4a, 0x38,
+//         0x84, 0xc5, 0x33, 0x2d, 0x1c, 0xdd, 0xee, 0x27}})
+
+#define UUID(str) []{ constexpr media_uuid_t uuid = constUUID(str); return uuid; }()
+
+}  // namespace android
+
+#endif  // MEDIA_EXTRACTOR_PLUGIN_HELPER_H_
diff --git a/include/media/MediaMetrics.h b/include/media/MediaMetrics.h
new file mode 120000
index 0000000..5f757e4
--- /dev/null
+++ b/include/media/MediaMetrics.h
@@ -0,0 +1 @@
+../../media/libmediametrics/include/MediaMetrics.h
\ No newline at end of file
diff --git a/include/media/MediaSource.h b/include/media/MediaSource.h
index 2e147c4..34bf65d 120000
--- a/include/media/MediaSource.h
+++ b/include/media/MediaSource.h
@@ -1 +1 @@
-../../media/libmediaextractor/include/media/MediaSource.h
\ No newline at end of file
+../../media/libstagefright/include/media/stagefright/MediaSource.h
\ No newline at end of file
diff --git a/include/media/MediaTrack.h b/include/media/MediaTrack.h
deleted file mode 120000
index 5a63287a..0000000
--- a/include/media/MediaTrack.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmediaextractor/include/media/MediaTrack.h
\ No newline at end of file
diff --git a/include/media/MediaTrack.h b/include/media/MediaTrack.h
new file mode 100644
index 0000000..493eba3
--- /dev/null
+++ b/include/media/MediaTrack.h
@@ -0,0 +1,165 @@
+/*
+ * 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 MEDIA_SOURCE_BASE_H_
+
+#define MEDIA_SOURCE_BASE_H_
+
+#include <sys/types.h>
+
+#include <binder/IMemory.h>
+#include <binder/MemoryDealer.h>
+#include <media/MediaExtractorPluginApi.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/MediaExtractorPluginApi.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class MediaBufferBase;
+struct CMediaTrack;
+
+class SourceBaseAllocTracker {
+public:
+    SourceBaseAllocTracker() {
+        ALOGD("sourcebase allocated: %p", this);
+    }
+    virtual ~SourceBaseAllocTracker() {
+        ALOGD("sourcebase freed: %p", this);
+    }
+};
+
+struct MediaTrack
+//    : public SourceBaseAllocTracker
+{
+    MediaTrack();
+
+    // To be called before any other methods on this object, except
+    // getFormat().
+    virtual status_t start() = 0;
+
+    // Any blocking read call returns immediately with a result of NO_INIT.
+    // It is an error to call any methods other than start after this call
+    // returns. Any buffers the object may be holding onto at the time of
+    // the stop() call are released.
+    // Also, it is imperative that any buffers output by this object and
+    // held onto by callers be released before a call to stop() !!!
+    virtual status_t stop() = 0;
+
+    // Returns the format of the data output by this media track.
+    virtual status_t getFormat(MetaDataBase& format) = 0;
+
+    // Options that modify read() behaviour. The default is to
+    // a) not request a seek
+    // b) not be late, i.e. lateness_us = 0
+    struct ReadOptions {
+        enum SeekMode : int32_t {
+            SEEK_PREVIOUS_SYNC = CMediaTrackReadOptions::SEEK_PREVIOUS_SYNC,
+            SEEK_NEXT_SYNC = CMediaTrackReadOptions::SEEK_NEXT_SYNC,
+            SEEK_CLOSEST_SYNC = CMediaTrackReadOptions::SEEK_CLOSEST_SYNC,
+            SEEK_CLOSEST = CMediaTrackReadOptions::SEEK_CLOSEST,
+            SEEK_FRAME_INDEX = CMediaTrackReadOptions::SEEK_FRAME_INDEX,
+        };
+
+        ReadOptions() {
+            reset();
+        }
+
+        // Reset everything back to defaults.
+        void reset() {
+            mOptions = 0;
+            mSeekTimeUs = 0;
+            mNonBlocking = false;
+        }
+
+        void setSeekTo(int64_t time_us, SeekMode mode = SEEK_CLOSEST_SYNC);
+        void clearSeekTo() {
+            mOptions &= ~kSeekTo_Option;
+            mSeekTimeUs = 0;
+            mSeekMode = SEEK_CLOSEST_SYNC;
+        }
+        bool getSeekTo(int64_t *time_us, SeekMode *mode) const;
+
+        void setNonBlocking();
+        void clearNonBlocking();
+        bool getNonBlocking() const;
+
+        // Used to clear all non-persistent options for multiple buffer reads.
+        void clearNonPersistent() {
+            clearSeekTo();
+        }
+
+    private:
+        enum Options {
+            kSeekTo_Option      = 1,
+        };
+
+        uint32_t mOptions;
+        int64_t mSeekTimeUs;
+        SeekMode mSeekMode;
+        bool mNonBlocking;
+    } __attribute__((packed)); // sent through Binder
+
+    // Returns a new buffer of data. Call blocks until a
+    // buffer is available, an error is encountered of the end of the stream
+    // is reached.
+    // End of stream is signalled by a result of ERROR_END_OF_STREAM.
+    // A result of INFO_FORMAT_CHANGED indicates that the format of this
+    // MediaSource has changed mid-stream, the client can continue reading
+    // but should be prepared for buffers of the new configuration.
+    virtual status_t read(
+            MediaBufferBase **buffer, const ReadOptions *options = NULL) = 0;
+
+    // Returns true if |read| supports nonblocking option, otherwise false.
+    // |readMultiple| if supported, always allows the nonblocking option.
+    virtual bool supportNonblockingRead() {
+        return false;
+    }
+
+    virtual ~MediaTrack();
+
+private:
+    MediaTrack(const MediaTrack &);
+    MediaTrack &operator=(const MediaTrack &);
+};
+
+class MediaTrackCUnwrapper : public MediaTrack {
+public:
+    static MediaTrackCUnwrapper *create(CMediaTrack *wrapper);
+
+    virtual status_t start();
+    virtual status_t stop();
+    virtual status_t getFormat(MetaDataBase& format);
+    virtual status_t read(MediaBufferBase **buffer, const ReadOptions *options = NULL);
+
+    virtual bool supportNonblockingRead();
+
+protected:
+    virtual ~MediaTrackCUnwrapper();
+
+private:
+    explicit MediaTrackCUnwrapper(CMediaTrack *wrapper);
+    CMediaTrack *wrapper;
+    MediaBufferGroup *bufferGroup;
+};
+
+}  // namespace android
+
+#endif  // MEDIA_SOURCE_BASE_H_
diff --git a/include/media/NdkMediaErrorPriv.h b/include/media/NdkMediaErrorPriv.h
new file mode 100644
index 0000000..3bbba79
--- /dev/null
+++ b/include/media/NdkMediaErrorPriv.h
@@ -0,0 +1,27 @@
+/*
+ * 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_MEDIA_ERROR_PRIV_H
+#define _NDK_MEDIA_ERROR_PRIV_H
+
+#include <media/NdkMediaError.h>
+#include <utils/Errors.h>
+
+media_status_t translate_error(android::status_t);
+
+android::status_t reverse_translate_error(media_status_t);
+
+#endif // _NDK_MEDIA_ERROR_PRIV_H
diff --git a/include/media/NdkMediaFormatPriv.h b/include/media/NdkMediaFormatPriv.h
new file mode 100644
index 0000000..1fda4a8
--- /dev/null
+++ b/include/media/NdkMediaFormatPriv.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef _NDK_MEDIA_FORMAT_PRIV_H
+#define _NDK_MEDIA_FORMAT_PRIV_H
+
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AMediaFormat {
+    android::sp<android::AMessage> mFormat;
+    android::String8 mDebug;
+    android::KeyedVector<android::String8, android::String8> mStringCache;
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+namespace android {
+
+AMediaFormat* AMediaFormat_fromMsg(sp<AMessage> *);
+void AMediaFormat_getFormat(const AMediaFormat* mData, sp<AMessage> *dest);
+
+} // namespace android
+
+#endif // _NDK_MEDIA_FORMAT_PRIV_H
+
diff --git a/include/media/TimeCheck.h b/include/media/TimeCheck.h
index e3ef134..85e17f9 120000
--- a/include/media/TimeCheck.h
+++ b/include/media/TimeCheck.h
@@ -1 +1 @@
-../../media/libmedia/include/media/TimeCheck.h
\ No newline at end of file
+../../media/utils/include/mediautils/TimeCheck.h
\ No newline at end of file
diff --git a/include/media/VolumeShaper.h b/include/media/VolumeShaper.h
index a3aaece..79afd6c 100644
--- a/include/media/VolumeShaper.h
+++ b/include/media/VolumeShaper.h
@@ -551,7 +551,7 @@
 
     static int64_t convertTimespecToUs(const struct timespec &tv)
     {
-        return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
+        return tv.tv_sec * 1000000LL + tv.tv_nsec / 1000;
     }
 
     // current monotonic time in microseconds.
diff --git a/include/media/VorbisComment.h b/include/media/VorbisComment.h
deleted file mode 120000
index adaa489..0000000
--- a/include/media/VorbisComment.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmediaextractor/include/media/VorbisComment.h
\ No newline at end of file
diff --git a/include/mediadrm/Crypto.h b/include/mediadrm/Crypto.h
deleted file mode 120000
index 9af6495..0000000
--- a/include/mediadrm/Crypto.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmedia/include/media/Crypto.h
\ No newline at end of file
diff --git a/include/mediadrm/Drm.h b/include/mediadrm/Drm.h
deleted file mode 120000
index ac60003..0000000
--- a/include/mediadrm/Drm.h
+++ /dev/null
@@ -1 +0,0 @@
-../../media/libmedia/include/media/Drm.h
\ No newline at end of file
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index ca119d5..5f19f74 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -538,6 +538,10 @@
         mTimestampMutator.push(timestamp);
     }
 
+    virtual ExtendedTimestamp getTimestamp() const {
+        return mTimestampMutator.last();
+    }
+
     // Flushes the shared ring buffer if the client had requested it using mStreaming.mFlush.
     // If flush occurs then:
     //   cblk->u.mStreaming.mFront, ServerProxy::mFlush and ServerProxy::mFlushed will be modified
@@ -551,6 +555,9 @@
     // Total count of the number of flushed frames since creation (never reset).
     virtual int64_t     framesFlushed() const { return mFlushed; }
 
+    // Safe frames ready query with no side effects.
+    virtual size_t      framesReadySafe() const = 0;
+
     // Get dynamic buffer size from the shared control block.
     uint32_t            getBufferSizeInFrames() const {
         return android_atomic_acquire_load((int32_t *)&mCblk->mBufferSizeInFrames);
@@ -588,8 +595,7 @@
     // which may include non-contiguous frames
     virtual size_t      framesReady();
 
-    // Safe frames ready query used by dump() - this has no side effects.
-    virtual size_t      framesReadySafe() const;
+    size_t              framesReadySafe() const override; // frames available to read by server.
 
     // Currently AudioFlinger will call framesReady() for a fast track from two threads:
     // FastMixer thread, and normal mixer thread.  This is dangerous, as the proxy is intended
@@ -693,6 +699,8 @@
         return mCblk->u.mStreaming.mRear; // For completeness only; mRear written by server.
     }
 
+    size_t framesReadySafe() const override; // frames available to read by client.
+
 protected:
     virtual ~AudioRecordServerProxy() { }
 };
diff --git a/include/soundtrigger/ISoundTrigger.h b/include/soundtrigger/ISoundTrigger.h
index 5fd8eb2..c357caa 100644
--- a/include/soundtrigger/ISoundTrigger.h
+++ b/include/soundtrigger/ISoundTrigger.h
@@ -40,6 +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) = 0;
 
 };
 
diff --git a/include/soundtrigger/ISoundTriggerHwService.h b/include/soundtrigger/ISoundTriggerHwService.h
index ae0cb01..1faeb0f 100644
--- a/include/soundtrigger/ISoundTriggerHwService.h
+++ b/include/soundtrigger/ISoundTriggerHwService.h
@@ -33,12 +33,14 @@
 
     DECLARE_META_INTERFACE(SoundTriggerHwService);
 
-    virtual status_t listModules(struct sound_trigger_module_descriptor *modules,
+    virtual status_t listModules(const String16& opPackageName,
+                                 struct sound_trigger_module_descriptor *modules,
                                  uint32_t *numModules) = 0;
 
-    virtual status_t attach(const sound_trigger_module_handle_t handle,
-                                      const sp<ISoundTriggerClient>& client,
-                                      sp<ISoundTrigger>& module) = 0;
+    virtual status_t attach(const String16& opPackageName,
+                            const sound_trigger_module_handle_t handle,
+                            const sp<ISoundTriggerClient>& client,
+                            sp<ISoundTrigger>& module) = 0;
 
     virtual status_t setCaptureState(bool active) = 0;
 };
diff --git a/include/soundtrigger/SoundTrigger.h b/include/soundtrigger/SoundTrigger.h
index 7a29e31..ccc61dc 100644
--- a/include/soundtrigger/SoundTrigger.h
+++ b/include/soundtrigger/SoundTrigger.h
@@ -36,10 +36,12 @@
 
     virtual ~SoundTrigger();
 
-    static  status_t listModules(struct sound_trigger_module_descriptor *modules,
+    static  status_t listModules(const String16& opPackageName,
+                                 struct sound_trigger_module_descriptor *modules,
                                  uint32_t *numModules);
-    static  sp<SoundTrigger> attach(const sound_trigger_module_handle_t module,
-                                       const sp<SoundTriggerCallback>& callback);
+    static  sp<SoundTrigger> attach(const String16& opPackageName,
+                                    const sound_trigger_module_handle_t module,
+                                    const sp<SoundTriggerCallback>& callback);
 
     static  status_t setCaptureState(bool active);
 
@@ -52,6 +54,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);
 
             // BpSoundTriggerClient
             virtual void onRecognitionEvent(const sp<IMemory>& eventMemory);
diff --git a/media/OWNERS b/media/OWNERS
index d49eb8d..1afc253 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,9 +1,12 @@
+andrewlewis@google.com
 chz@google.com
 dwkang@google.com
 elaurent@google.com
 essick@google.com
+gkasten@google.com
 hkuang@google.com
 hunga@google.com
+jiabin@google.com
 jmtrivi@google.com
 krocard@google.com
 lajos@google.com
@@ -15,5 +18,6 @@
 rachad@google.com
 rago@google.com
 robertshih@google.com
+taklee@google.com
 wjia@google.com
 wonsik@google.com
diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk
index 70c281a..33b36b8 100644
--- a/media/audioserver/Android.mk
+++ b/media/audioserver/Android.mk
@@ -4,7 +4,6 @@
 
 LOCAL_SRC_FILES := \
 	main_audioserver.cpp \
-	../libaudioclient/aidl/android/media/IAudioRecord.aidl
 
 LOCAL_SHARED_LIBRARIES := \
 	libaaudioservice \
@@ -13,13 +12,17 @@
 	libbinder \
 	libcutils \
 	liblog \
+	libhidlbase \
 	libhidltransport \
 	libhwbinder \
 	libmedia \
 	libmedialogservice \
+	libmediautils \
 	libnbaio \
+	libnblog \
 	libsoundtriggerservice \
-	libutils
+	libutils \
+	libvibrator
 
 # TODO oboeservice is the old folder name for aaudioservice. It will be changed.
 LOCAL_C_INCLUDES := \
@@ -40,9 +43,6 @@
 	$(call include-path-for, audio-utils) \
 	external/sonic \
 
-LOCAL_AIDL_INCLUDES := \
-        frameworks/av/media/libaudioclient/aidl
-
 # If AUDIOSERVER_MULTILIB in device.mk is non-empty then it is used to control
 # the LOCAL_MULTILIB for all audioserver exclusive libraries.
 # This is relevant for 64 bit architectures where either or both
@@ -54,13 +54,9 @@
 #   both    to build both 32 bit and 64 bit libraries,
 #           and use primary target architecture (32 or 64) for audioserver.
 #   first   to build libraries and audioserver for the primary target architecture only.
-#   <empty> to build both 32 and 64 bit libraries and 32 bit audioserver.
+#   <empty> to build both 32 and 64 bit libraries and primary target audioserver.
 
-ifeq ($(strip $(AUDIOSERVER_MULTILIB)),)
-LOCAL_MULTILIB := 32
-else
 LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
-endif
 
 LOCAL_MODULE := audioserver
 
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 75675a9..dfb1a3f 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -2,10 +2,12 @@
     class core
     user audioserver
     # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
-    group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct
+    group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock
+    capabilities BLOCK_SUSPEND
     ioprio rt 4
     writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
     onrestart restart vendor.audio-hal-2-0
+    onrestart restart vendor.audio-hal-4-0-msd
     # Keep the original service name for backward compatibility when upgrading
     # O-MR1 devices with framework-only.
     onrestart restart audio-hal-2-0
@@ -14,3 +16,6 @@
     stop audioserver
 on property:vts.native_server.on=0
     start audioserver
+
+on init
+    mkdir /dev/socket/audioserver 0775 audioserver audioserver
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..d712398
--- /dev/null
+++ b/media/bufferpool/1.0/BufferPoolClient.cpp
@@ -0,0 +1,712 @@
+/*
+ * 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;
+                    });
+            if(!transResult.isOk()) {
+                ALOGD("sync from client %lld failed: bufferpool process died.",
+                      (long long)mConnectionId);
+            }
+        }
+        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..be89701
--- /dev/null
+++ b/media/bufferpool/1.0/Connection.cpp
@@ -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.
+ */
+
+#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) {
+                Buffer buffer = {};
+                buffer.id = bufferId;
+                buffer.buffer = handle;
+                _hidl_cb(status, buffer);
+                return Void();
+            }
+        } else {
+            mAccessor->cleanUp(false);
+        }
+    }
+
+    Buffer buffer = {};
+    buffer.id = 0;
+    buffer.buffer = nullptr;
+
+    _hidl_cb(status, buffer);
+    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/bufferpool/2.0/Accessor.cpp b/media/bufferpool/2.0/Accessor.cpp
new file mode 100644
index 0000000..57b4609
--- /dev/null
+++ b/media/bufferpool/2.0/Accessor.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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 V2_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;
+}
+
+void Accessor::createInvalidator() {
+    Accessor::Impl::createInvalidator();
+}
+
+// Methods from ::android::hardware::media::bufferpool::V2_0::IAccessor follow.
+Return<void> Accessor::connect(
+        const sp<::android::hardware::media::bufferpool::V2_0::IObserver>& observer,
+        connect_cb _hidl_cb) {
+    sp<Connection> connection;
+    ConnectionId connectionId;
+    uint32_t msgId;
+    const StatusDescriptor* fmqDesc;
+    const InvalidationDescriptor* invDesc;
+
+    ResultStatus status = connect(
+            observer, false, &connection, &connectionId, &msgId, &fmqDesc, &invDesc);
+    if (status == ResultStatus::OK) {
+        _hidl_cb(status, connection, connectionId, msgId, *fmqDesc, *invDesc);
+    } else {
+        _hidl_cb(status, nullptr, -1LL, 0,
+                 android::hardware::MQDescriptorSync<BufferStatusMessage>(
+                         std::vector<android::hardware::GrantorDescriptor>(),
+                         nullptr /* nhandle */, 0 /* size */),
+                 android::hardware::MQDescriptorUnsync<BufferInvalidationMessage>(
+                         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 && mImpl->isValid();
+}
+
+ResultStatus Accessor::flush() {
+    if (mImpl) {
+        mImpl->flush();
+        return ResultStatus::OK;
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+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(
+        const sp<IObserver> &observer, bool local,
+        sp<Connection> *connection, ConnectionId *pConnectionId,
+        uint32_t *pMsgId,
+        const StatusDescriptor** statusDescPtr,
+        const InvalidationDescriptor** invDescPtr) {
+    if (mImpl) {
+        ResultStatus status = mImpl->connect(
+                this, observer, connection, pConnectionId, pMsgId,
+                statusDescPtr, invDescPtr);
+        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 V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/Accessor.h b/media/bufferpool/2.0/Accessor.h
new file mode 100644
index 0000000..8d02519
--- /dev/null
+++ b/media/bufferpool/2.0/Accessor.h
@@ -0,0 +1,202 @@
+/*
+ * 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_V2_0_ACCESSOR_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSOR_H
+
+#include <android/hardware/media/bufferpool/2.0/IAccessor.h>
+#include <android/hardware/media/bufferpool/2.0/IObserver.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 V2_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::V2_0::IAccessor follow.
+    Return<void> connect(const sp<::android::hardware::media::bufferpool::V2_0::IObserver>& observer, 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();
+
+    /** Invalidates all buffers which are owned by bufferpool */
+    ResultStatus flush();
+
+    /** 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 observer      client observer for buffer invalidation
+     * @param local         true when a connection request comes from local process,
+     *                      false otherwise.
+     * @param connection    created connection
+     * @param pConnectionId the id of the created connection
+     * @param pMsgId        the id of the recent buffer pool message
+     * @param statusDescPtr FMQ descriptor for shared buffer status message
+     *                      queue between a buffer pool and the client.
+     * @param invDescPtr    FMQ descriptor for buffer invalidation message
+     *                      queue from a buffer pool to the client.
+     *
+     * @return OK when a connection is successfully made.
+     *         NO_MEMORY when there is no memory.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus connect(
+            const sp<IObserver>& observer,
+            bool local,
+            sp<Connection> *connection, ConnectionId *pConnectionId,
+            uint32_t *pMsgId,
+            const StatusDescriptor** statusDescPtr,
+            const InvalidationDescriptor** invDescPtr);
+
+    /**
+     * 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();
+
+    static void createInvalidator();
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSOR_H
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
new file mode 100644
index 0000000..94cf006
--- /dev/null
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -0,0 +1,861 @@
+/*
+ * 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 <thread>
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_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;
+    bool mInvalidated;
+
+    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),
+            mInvalidated(false) {}
+
+    const native_handle_t *handle() {
+        return mAllocation->handle();
+    }
+
+    void invalidate() {
+        mInvalidated = true;
+    }
+};
+
+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, const sp<IObserver> &observer,
+        sp<Connection> *connection,
+        ConnectionId *pConnectionId,
+        uint32_t *pMsgId,
+        const StatusDescriptor** statusDescPtr,
+        const InvalidationDescriptor** invDescPtr) {
+    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, statusDescPtr);
+            if (status == ResultStatus::OK) {
+                newConnection->initialize(accessor, id);
+                *connection = newConnection;
+                *pConnectionId = id;
+                *pMsgId = mBufferPool.mInvalidation.mInvalidationId;
+                mBufferPool.mInvalidationChannel.getDesc(invDescPtr);
+                mBufferPool.mInvalidation.onConnect(id, observer);
+                ++sSeqId;
+            }
+
+        }
+        mBufferPool.processStatusMessages();
+        mBufferPool.cleanUp();
+    }
+    return status;
+}
+
+ResultStatus Accessor::Impl::close(ConnectionId connectionId) {
+    std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+    ALOGV("connection close %lld: %u", (long long)connectionId, mBufferPool.mInvalidation.mId);
+    mBufferPool.processStatusMessages();
+    mBufferPool.handleClose(connectionId);
+    mBufferPool.mObserver.close(connectionId);
+    mBufferPool.mInvalidation.onClose(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);
+}
+
+void Accessor::Impl::flush() {
+    std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+    mBufferPool.processStatusMessages();
+    mBufferPool.flush(shared_from_this());
+}
+
+void Accessor::Impl::handleInvalidateAck() {
+    std::map<ConnectionId, const sp<IObserver>> observers;
+    uint32_t invalidationId;
+    {
+        std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+        mBufferPool.processStatusMessages();
+        mBufferPool.mInvalidation.onHandleAck(&observers, &invalidationId);
+    }
+    // Do not hold lock for send invalidations
+    size_t deadClients = 0;
+    for (auto it = observers.begin(); it != observers.end(); ++it) {
+        const sp<IObserver> observer = it->second;
+        if (observer) {
+            Return<void> transResult = observer->onMessage(it->first, invalidationId);
+            if (!transResult.isOk()) {
+                ++deadClients;
+            }
+        }
+    }
+    if (deadClients > 0) {
+        ALOGD("During invalidation found %zu dead clients", deadClients);
+    }
+}
+
+bool Accessor::Impl::isValid() {
+    return mBufferPool.isValid();
+}
+
+Accessor::Impl::Impl::BufferPool::BufferPool()
+    : mTimestampUs(getTimestampNow()),
+      mLastCleanUpUs(mTimestampUs),
+      mLastLogUs(mTimestampUs),
+      mSeq(0),
+      mStartSeq(0) {
+    mValid = mInvalidationChannel.isValid();
+}
+
+
+// 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);
+}
+
+std::atomic<std::uint32_t> Accessor::Impl::BufferPool::Invalidation::sInvSeqId(0);
+
+Accessor::Impl::Impl::BufferPool::~BufferPool() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    ALOGD("Destruction - bufferpool2 %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));
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onConnect(
+        ConnectionId conId, const sp<IObserver>& observer) {
+    mAcks[conId] = mInvalidationId; // starts from current invalidationId
+    mObservers.insert(std::make_pair(conId, observer));
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onClose(ConnectionId conId) {
+    mAcks.erase(conId);
+    mObservers.erase(conId);
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onAck(
+        ConnectionId conId,
+        uint32_t msgId) {
+    auto it = mAcks.find(conId);
+    if (it == mAcks.end()) {
+        ALOGW("ACK from inconsistent connection! %lld", (long long)conId);
+        return;
+    }
+    if (isMessageLater(msgId, it->second)) {
+        mAcks[conId] = msgId;
+    }
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onBufferInvalidated(
+        BufferId bufferId,
+        BufferInvalidationChannel &channel) {
+    for (auto it = mPendings.begin(); it != mPendings.end();) {
+        if (it->isInvalidated(bufferId)) {
+            uint32_t msgId = 0;
+            if (it->mNeedsAck) {
+                msgId = ++mInvalidationId;
+                if (msgId == 0) {
+                    // wrap happens
+                    msgId = ++mInvalidationId;
+                }
+            }
+            channel.postInvalidation(msgId, it->mFrom, it->mTo);
+            it = mPendings.erase(it);
+            continue;
+        }
+        ++it;
+    }
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onInvalidationRequest(
+        bool needsAck,
+        uint32_t from,
+        uint32_t to,
+        size_t left,
+        BufferInvalidationChannel &channel,
+        const std::shared_ptr<Accessor::Impl> &impl) {
+        uint32_t msgId = 0;
+    if (needsAck) {
+        msgId = ++mInvalidationId;
+        if (msgId == 0) {
+            // wrap happens
+            msgId = ++mInvalidationId;
+        }
+    }
+    ALOGV("bufferpool2 invalidation requested and queued");
+    if (left == 0) {
+        channel.postInvalidation(msgId, from, to);
+    } else {
+        // TODO: sending hint message?
+        ALOGV("bufferpoo2 invalidation requested and pending");
+        Pending pending(needsAck, from, to, left, impl);
+        mPendings.push_back(pending);
+    }
+    sInvalidator->addAccessor(mId, impl);
+}
+
+void Accessor::Impl::BufferPool::Invalidation::onHandleAck(
+        std::map<ConnectionId, const sp<IObserver>> *observers,
+        uint32_t *invalidationId) {
+    if (mInvalidationId != 0) {
+        *invalidationId = mInvalidationId;
+        std::set<int> deads;
+        for (auto it = mAcks.begin(); it != mAcks.end(); ++it) {
+            if (it->second != mInvalidationId) {
+                const sp<IObserver> observer = mObservers[it->first];
+                if (observer) {
+                    observers->emplace(it->first, observer);
+                    ALOGV("connection %lld will call observer (%u: %u)",
+                          (long long)it->first, it->second, mInvalidationId);
+                    // N.B: onMessage will be called later. ignore possibility of
+                    // onMessage# oneway call being lost.
+                    it->second = mInvalidationId;
+                } else {
+                    ALOGV("bufferpool2 observer died %lld", (long long)it->first);
+                    deads.insert(it->first);
+                }
+            }
+        }
+        if (deads.size() > 0) {
+            for (auto it = deads.begin(); it != deads.end(); ++it) {
+                onClose(*it);
+            }
+        }
+    }
+    if (mPendings.size() == 0) {
+        // All invalidation Ids are synced and no more pending invalidations.
+        sInvalidator->delAccessor(mId);
+    }
+}
+
+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) {
+            if (!iter->second->mInvalidated) {
+                mStats.onBufferUnused(iter->second->mAllocSize);
+                mFreeBuffers.insert(bufferId);
+            } else {
+                mStats.onBufferUnused(iter->second->mAllocSize);
+                mStats.onBufferEvicted(iter->second->mAllocSize);
+                mBuffers.erase(iter);
+                mInvalidation.onBufferInvalidated(bufferId, mInvalidationChannel);
+            }
+        }
+    }
+    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) {
+                if (!bufferIter->second->mInvalidated) {
+                    mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                    mFreeBuffers.insert(message.bufferId);
+                } else {
+                    mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                    mStats.onBufferEvicted(bufferIter->second->mAllocSize);
+                    mBuffers.erase(bufferIter);
+                    mInvalidation.onBufferInvalidated(message.bufferId, mInvalidationChannel);
+                }
+            }
+            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;
+            case BufferStatus::INVALIDATION_ACK:
+                mInvalidation.onAck(message.connectionId, message.bufferId);
+                ret = true;
+                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
+                    if (!bufferIter->second->mInvalidated) {
+                        mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                        mFreeBuffers.insert(bufferId);
+                    } else {
+                        mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                        mStats.onBufferEvicted(bufferIter->second->mAllocSize);
+                        mBuffers.erase(bufferIter);
+                        mInvalidation.onBufferInvalidated(bufferId, mInvalidationChannel);
+                    }
+                }
+            }
+        }
+        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
+                    if (!bufferIter->second->mInvalidated) {
+                        mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                        mFreeBuffers.insert(bufferId);
+                    } else {
+                        mStats.onBufferUnused(bufferIter->second->mAllocSize);
+                        mStats.onBufferEvicted(bufferIter->second->mAllocSize);
+                        mBuffers.erase(bufferIter);
+                        mInvalidation.onBufferInvalidated(bufferId, mInvalidationChannel);
+                    }
+                }
+                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("bufferpool2 %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("bufferpool2 inconsistent!");
+            }
+        }
+    }
+}
+
+void Accessor::Impl::BufferPool::invalidate(
+        bool needsAck, BufferId from, BufferId to,
+        const std::shared_ptr<Accessor::Impl> &impl) {
+    for (auto freeIt = mFreeBuffers.begin(); freeIt != mFreeBuffers.end();) {
+        if (isBufferInRange(from, to, *freeIt)) {
+            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);
+                continue;
+            } else {
+                ALOGW("bufferpool2 inconsistent!");
+            }
+        }
+        ++freeIt;
+    }
+
+    size_t left = 0;
+    for (auto it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+        if (isBufferInRange(from, to, it->first)) {
+            it->second->invalidate();
+            ++left;
+        }
+    }
+    mInvalidation.onInvalidationRequest(needsAck, from, to, left, mInvalidationChannel, impl);
+}
+
+void Accessor::Impl::BufferPool::flush(const std::shared_ptr<Accessor::Impl> &impl) {
+    BufferId from = mStartSeq;
+    BufferId to = mSeq;
+    mStartSeq = mSeq;
+    // TODO: needsAck params 
+    ALOGV("buffer invalidation request bp:%u %u %u", mInvalidation.mId, from, to);
+    if (from != to) {
+        invalidate(true, from, to, impl);
+    }
+}
+
+void Accessor::Impl::invalidatorThread(
+            std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> &accessors,
+            std::mutex &mutex,
+            std::condition_variable &cv,
+            bool &ready) {
+    while(true) {
+        std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> copied;
+        {
+            std::unique_lock<std::mutex> lock(mutex);
+            if (!ready) {
+                cv.wait(lock);
+            }
+            copied.insert(accessors.begin(), accessors.end());
+        }
+        std::list<ConnectionId> erased;
+        for (auto it = copied.begin(); it != copied.end(); ++it) {
+            const std::shared_ptr<Accessor::Impl> impl = it->second.lock();
+            if (!impl) {
+                erased.push_back(it->first);
+            } else {
+                impl->handleInvalidateAck();
+            }
+        }
+        {
+            std::unique_lock<std::mutex> lock(mutex);
+            for (auto it = erased.begin(); it != erased.end(); ++it) {
+                accessors.erase(*it);
+            }
+            if (accessors.size() == 0) {
+                ready = false;
+            } else {
+                // prevent draining cpu.
+                lock.unlock();
+                std::this_thread::yield();
+            }
+        }
+    }
+}
+
+Accessor::Impl::AccessorInvalidator::AccessorInvalidator() : mReady(false) {
+    std::thread invalidator(
+            invalidatorThread,
+            std::ref(mAccessors),
+            std::ref(mMutex),
+            std::ref(mCv),
+            std::ref(mReady));
+    invalidator.detach();
+}
+
+void Accessor::Impl::AccessorInvalidator::addAccessor(
+        uint32_t accessorId, const std::weak_ptr<Accessor::Impl> &impl) {
+    bool notify = false;
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (mAccessors.find(accessorId) == mAccessors.end()) {
+        if (!mReady) {
+            mReady = true;
+            notify = true;
+        }
+        mAccessors.insert(std::make_pair(accessorId, impl));
+        ALOGV("buffer invalidation added bp:%u %d", accessorId, notify);
+    }
+    lock.unlock();
+    if (notify) {
+        mCv.notify_one();
+    }
+}
+
+void Accessor::Impl::AccessorInvalidator::delAccessor(uint32_t accessorId) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mAccessors.erase(accessorId);
+    ALOGV("buffer invalidation deleted bp:%u", accessorId);
+    if (mAccessors.size() == 0) {
+        mReady = false;
+    }
+}
+
+std::unique_ptr<Accessor::Impl::AccessorInvalidator> Accessor::Impl::sInvalidator;
+
+void Accessor::Impl::createInvalidator() {
+    if (!sInvalidator) {
+        sInvalidator = std::make_unique<Accessor::Impl::AccessorInvalidator>();
+    }
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h
new file mode 100644
index 0000000..eea72b9
--- /dev/null
+++ b/media/bufferpool/2.0/AccessorImpl.h
@@ -0,0 +1,401 @@
+/*
+ * 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_V2_0_ACCESSORIMPL_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSORIMPL_H
+
+#include <map>
+#include <set>
+#include <condition_variable>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+struct InternalBuffer;
+struct TransactionStatus;
+
+/**
+ * An implementation of a buffer pool accessor(or a buffer pool implementation.) */
+class Accessor::Impl
+    : public std::enable_shared_from_this<Accessor::Impl> {
+public:
+    Impl(const std::shared_ptr<BufferPoolAllocator> &allocator);
+
+    ~Impl();
+
+    ResultStatus connect(
+            const sp<Accessor> &accessor, const sp<IObserver> &observer,
+            sp<Connection> *connection,
+            ConnectionId *pConnectionId,
+            uint32_t *pMsgId,
+            const StatusDescriptor** statusDescPtr,
+            const InvalidationDescriptor** invDescPtr);
+
+    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 flush();
+
+    void cleanUp(bool clearCache);
+
+    bool isValid();
+
+    void handleInvalidateAck();
+
+    static void createInvalidator();
+
+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;
+        BufferId mStartSeq;
+        bool mValid;
+        BufferStatusObserver mObserver;
+        BufferInvalidationChannel mInvalidationChannel;
+
+        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;
+
+        struct Invalidation {
+            static std::atomic<std::uint32_t> sInvSeqId;
+
+            struct Pending {
+                bool mNeedsAck;
+                uint32_t mFrom;
+                uint32_t mTo;
+                size_t mLeft;
+                const std::weak_ptr<Accessor::Impl> mImpl;
+                Pending(bool needsAck, uint32_t from, uint32_t to, size_t left,
+                        const std::shared_ptr<Accessor::Impl> &impl)
+                        : mNeedsAck(needsAck),
+                          mFrom(from),
+                          mTo(to),
+                          mLeft(left),
+                          mImpl(impl)
+                {}
+
+                bool isInvalidated(uint32_t bufferId) {
+                    return isBufferInRange(mFrom, mTo, bufferId) && --mLeft == 0;
+                }
+            };
+
+            std::list<Pending> mPendings;
+            std::map<ConnectionId, uint32_t> mAcks;
+            std::map<ConnectionId, const sp<IObserver>> mObservers;
+            uint32_t mInvalidationId;
+            uint32_t mId;
+
+            Invalidation() : mInvalidationId(0), mId(sInvSeqId.fetch_add(1)) {}
+
+            void onConnect(ConnectionId conId, const sp<IObserver> &observer);
+
+            void onClose(ConnectionId conId);
+
+            void onAck(ConnectionId conId, uint32_t msgId);
+
+            void onBufferInvalidated(
+                    BufferId bufferId,
+                    BufferInvalidationChannel &channel);
+
+            void onInvalidationRequest(
+                    bool needsAck, uint32_t from, uint32_t to, size_t left,
+                    BufferInvalidationChannel &channel,
+                    const std::shared_ptr<Accessor::Impl> &impl);
+
+            void onHandleAck(
+                    std::map<ConnectionId, const sp<IObserver>> *observers,
+                    uint32_t *invalidationId);
+        } mInvalidation;
+        /// 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;
+
+        bool isValid() {
+            return mValid;
+        }
+
+        void invalidate(bool needsAck, BufferId from, BufferId to,
+                        const std::shared_ptr<Accessor::Impl> &impl);
+
+        static void createInvalidator();
+
+    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);
+
+        /**
+         * Processes pending buffer status messages and invalidate all current
+         * free buffers. Active buffers are invalidated after being inactive.
+         */
+        void flush(const std::shared_ptr<Accessor::Impl> &impl);
+
+        friend class Accessor::Impl;
+    } mBufferPool;
+
+    struct  AccessorInvalidator {
+        std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> mAccessors;
+        std::mutex mMutex;
+        std::condition_variable mCv;
+        bool mReady;
+
+        AccessorInvalidator();
+        void addAccessor(uint32_t accessorId, const std::weak_ptr<Accessor::Impl> &impl);
+        void delAccessor(uint32_t accessorId);
+    };
+
+    static std::unique_ptr<AccessorInvalidator> sInvalidator;
+
+    static void invalidatorThread(
+        std::map<uint32_t, const std::weak_ptr<Accessor::Impl>> &accessors,
+        std::mutex &mutex,
+        std::condition_variable &cv,
+        bool &ready);
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace ufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSORIMPL_H
diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp
new file mode 100644
index 0000000..c71ac17
--- /dev/null
+++ b/media/bufferpool/2.0/Android.bp
@@ -0,0 +1,33 @@
+cc_library {
+    name: "libstagefright_bufferpool@2.0",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "Accessor.cpp",
+        "AccessorImpl.cpp",
+        "BufferPoolClient.cpp",
+        "BufferStatus.cpp",
+        "ClientManager.cpp",
+        "Connection.cpp",
+        "Observer.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhwbinder",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+        "android.hardware.media.bufferpool@2.0",
+    ],
+    export_shared_lib_headers: [
+        "libfmq",
+        "android.hardware.media.bufferpool@2.0",
+    ],
+}
diff --git a/media/bufferpool/2.0/BufferPoolClient.cpp b/media/bufferpool/2.0/BufferPoolClient.cpp
new file mode 100644
index 0000000..342fef6
--- /dev/null
+++ b/media/bufferpool/2.0/BufferPoolClient.cpp
@@ -0,0 +1,868 @@
+/*
+ * 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 V2_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, const sp<IObserver> &observer);
+
+    explicit Impl(const sp<IAccessor> &accessor, const sp<IObserver> &observer);
+
+    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);
+
+    void receiveInvalidation(uint32_t msgID);
+
+    ResultStatus flush();
+
+    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(uint32_t msgId = 0);
+
+    void evictCaches(bool clearCache = false);
+
+    void invalidateBuffer(BufferId id);
+
+    void invalidateRange(BufferId from, BufferId to);
+
+    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;
+    std::unique_ptr<BufferInvalidationListener> mInvalidationListener;
+
+    // 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 ReleaseCache {
+        std::mutex mLock;
+        // TODO: use only one list?(using one list may dealy sending messages?)
+        std::list<BufferId> mReleasingIds;
+        std::list<BufferId> mReleasedIds;
+        uint32_t mInvalidateId; // TODO: invalidation ACK to bufferpool
+        bool mInvalidateAck;
+        std::unique_ptr<BufferStatusChannel> mStatusChannel;
+
+        ReleaseCache() : mInvalidateId(0), mInvalidateAck(true) {}
+    } 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:
+    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)
+            : mHasCache(false), mConnectionId(connectionId),
+              mId(id), mHandle(handle) {
+        mExpireUs = getTimestampNow() + kCacheTtlUs;
+    }
+
+    ~ClientBuffer() {
+        if (mHandle) {
+            native_handle_close(mHandle);
+            native_handle_delete(mHandle);
+        }
+    }
+
+    BufferId id() const {
+        return mId;
+    }
+
+    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, const sp<IObserver> &observer)
+    : mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0),
+      mLastEvictCacheUs(getTimestampNow()) {
+    const StatusDescriptor *statusDesc;
+    const InvalidationDescriptor *invDesc;
+    ResultStatus status = accessor->connect(
+            observer, true,
+            &mLocalConnection, &mConnectionId, &mReleasing.mInvalidateId,
+            &statusDesc, &invDesc);
+    if (status == ResultStatus::OK) {
+        mReleasing.mStatusChannel =
+                std::make_unique<BufferStatusChannel>(*statusDesc);
+        mInvalidationListener =
+                std::make_unique<BufferInvalidationListener>(*invDesc);
+        mValid = mReleasing.mStatusChannel &&
+                mReleasing.mStatusChannel->isValid() &&
+                mInvalidationListener &&
+                mInvalidationListener->isValid();
+    }
+}
+
+BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor, const sp<IObserver> &observer)
+    : mLocal(false), mValid(false), mAccessor(accessor), mSeqId(0),
+      mLastEvictCacheUs(getTimestampNow()) {
+    bool valid = false;
+    sp<IConnection>& outConnection = mRemoteConnection;
+    ConnectionId& id = mConnectionId;
+    uint32_t& outMsgId = mReleasing.mInvalidateId;
+    std::unique_ptr<BufferStatusChannel>& outChannel =
+            mReleasing.mStatusChannel;
+    std::unique_ptr<BufferInvalidationListener>& outObserver =
+            mInvalidationListener;
+    Return<void> transResult = accessor->connect(
+            observer,
+            [&valid, &outConnection, &id, &outMsgId, &outChannel, &outObserver]
+            (ResultStatus status, sp<IConnection> connection,
+             ConnectionId connectionId, uint32_t msgId,
+             const StatusDescriptor& statusDesc,
+             const InvalidationDescriptor& invDesc) {
+                if (status == ResultStatus::OK) {
+                    outConnection = connection;
+                    id = connectionId;
+                    outMsgId = msgId;
+                    outChannel = std::make_unique<BufferStatusChannel>(statusDesc);
+                    outObserver = std::make_unique<BufferInvalidationListener>(invDesc);
+                    if (outChannel && outChannel->isValid() &&
+                        outObserver && outObserver->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;
+}
+
+void BufferPoolClient::Impl::receiveInvalidation(uint32_t messageId) {
+    std::lock_guard<std::mutex> lock(mCache.mLock);
+    syncReleased(messageId);
+    // TODO: evict cache required?
+}
+
+ResultStatus BufferPoolClient::Impl::flush() {
+    if (!mLocal || !mLocalConnection || !mValid) {
+        return ResultStatus::CRITICAL_ERROR;
+    }
+    {
+        std::unique_lock<std::mutex> lock(mCache.mLock);
+        syncReleased();
+        evictCaches();
+        return mLocalConnection->flush();
+    }
+}
+
+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) {
+    {
+        // TODO: don't need to call syncReleased every time
+        std::lock_guard<std::mutex> lock(mCache.mLock);
+        syncReleased();
+    }
+    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;
+                    });
+            if (!transResult.isOk()) {
+                ALOGD("sync from client %lld failed: bufferpool process died.",
+                      (long long)mConnectionId);
+            }
+        }
+        mRemoteSyncLock.unlock();
+    }
+}
+
+// should have mCache.mLock
+bool BufferPoolClient::Impl::syncReleased(uint32_t messageId) {
+    bool cleared = false;
+    {
+        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();
+            cleared = true;
+        }
+    }
+    std::vector<BufferInvalidationMessage> invalidations;
+    mInvalidationListener->getInvalidations(invalidations);
+    uint32_t lastMsgId = 0;
+    if (invalidations.size() > 0) {
+        for (auto it = invalidations.begin(); it != invalidations.end(); ++it) {
+            if (it->messageId != 0) {
+                lastMsgId = it->messageId;
+            }
+            if (it->fromBufferId == it->toBufferId) {
+                // TODO: handle fromBufferId = UINT32_MAX
+                invalidateBuffer(it->fromBufferId);
+            } else {
+                invalidateRange(it->fromBufferId, it->toBufferId);
+            }
+        }
+    }
+    {
+        std::lock_guard<std::mutex> lock(mReleasing.mLock);
+        if (lastMsgId != 0) {
+            if (isMessageLater(lastMsgId, mReleasing.mInvalidateId)) {
+                mReleasing.mInvalidateId = lastMsgId;
+                mReleasing.mInvalidateAck = false;
+            }
+        } else if (messageId != 0) {
+            // messages are drained.
+            if (isMessageLater(messageId, mReleasing.mInvalidateId)) {
+                mReleasing.mInvalidateId = messageId;
+                mReleasing.mInvalidateAck = true;
+            }
+        }
+        if (!mReleasing.mInvalidateAck) {
+            // post ACK
+            mReleasing.mStatusChannel->postBufferInvalidateAck(
+                    mConnectionId,
+                    mReleasing.mInvalidateId, &mReleasing.mInvalidateAck);
+            ALOGV("client %lld invalidateion ack (%d) %u",
+                (long long)mConnectionId,
+                mReleasing.mInvalidateAck, mReleasing.mInvalidateId);
+        }
+    }
+    return cleared;
+}
+
+// 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;
+    }
+}
+
+// should have mCache.mLock
+void BufferPoolClient::Impl::invalidateBuffer(BufferId id) {
+    for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end(); ++it) {
+        if (id == it->second->id()) {
+            if (!it->second->hasCache()) {
+                mCache.mBuffers.erase(it);
+                ALOGV("cache invalidated %lld : buffer %u",
+                      (long long)mConnectionId, id);
+            } else {
+                ALOGW("Inconsitent invalidation %lld : activer buffer!! %u",
+                      (long long)mConnectionId, (unsigned int)id);
+            }
+            break;
+        }
+    }
+}
+
+// should have mCache.mLock
+void BufferPoolClient::Impl::invalidateRange(BufferId from, BufferId to) {
+    size_t invalidated = 0;
+    for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) {
+        if (!it->second->hasCache()) {
+            BufferId bid = it->second->id();
+            if (from < to) {
+                if (from <= bid && bid < to) {
+                    ++invalidated;
+                    it = mCache.mBuffers.erase(it);
+                    continue;
+                }
+            } else {
+                if (from <= bid || bid < to) {
+                    ++invalidated;
+                    it = mCache.mBuffers.erase(it);
+                    continue;
+                }
+            }
+        }
+        ++it;
+    }
+    ALOGV("cache invalidated %lld : # of invalidated %zu",
+          (long long)mConnectionId, invalidated);
+}
+
+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,
+                                   const sp<IObserver> &observer) {
+    mImpl = std::make_shared<Impl>(accessor, observer);
+}
+
+BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor,
+                                   const sp<IObserver> &observer) {
+    mImpl = std::make_shared<Impl>(accessor, observer);
+}
+
+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;
+}
+
+void BufferPoolClient::receiveInvalidation(uint32_t msgId) {
+    ALOGV("bufferpool2 client recv inv %u", msgId);
+    if (isValid()) {
+        mImpl->receiveInvalidation(msgId);
+    }
+}
+
+ResultStatus BufferPoolClient::flush() {
+    if (isValid()) {
+        return mImpl->flush();
+    }
+    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 V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/BufferPoolClient.h b/media/bufferpool/2.0/BufferPoolClient.h
new file mode 100644
index 0000000..e8d9ae6
--- /dev/null
+++ b/media/bufferpool/2.0/BufferPoolClient.h
@@ -0,0 +1,111 @@
+/*
+ * 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_V2_0_BUFFERPOOLCLIENT_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLCLIENT_H
+
+#include <memory>
+#include <android/hardware/media/bufferpool/2.0/IAccessor.h>
+#include <android/hardware/media/bufferpool/2.0/IConnection.h>
+#include <android/hardware/media/bufferpool/2.0/IObserver.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <cutils/native_handle.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::media::bufferpool::V2_0::IAccessor;
+using ::android::hardware::media::bufferpool::V2_0::IConnection;
+using ::android::hardware::media::bufferpool::V2_0::IObserver;
+using ::android::hardware::media::bufferpool::V2_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,
+                              const sp<IObserver> &observer);
+
+    /**
+     * 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,
+                              const sp<IObserver> &observer);
+
+    /** 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);
+
+    void receiveInvalidation(uint32_t msgId);
+
+    ResultStatus flush();
+
+    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;
+    friend struct Observer;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLCLIENT_H
diff --git a/media/bufferpool/2.0/BufferStatus.cpp b/media/bufferpool/2.0/BufferStatus.cpp
new file mode 100644
index 0000000..6937260
--- /dev/null
+++ b/media/bufferpool/2.0/BufferStatus.cpp
@@ -0,0 +1,304 @@
+/*
+ * 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 <thread>
+#include <time.h>
+#include "BufferStatus.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_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;
+}
+
+bool isMessageLater(uint32_t curMsgId, uint32_t prevMsgId) {
+    return curMsgId != prevMsgId && curMsgId - prevMsgId < prevMsgId - curMsgId;
+}
+
+bool isBufferInRange(BufferId from, BufferId to, BufferId bufferId) {
+    if (from < to) {
+        return from <= bufferId && bufferId < to;
+    } else { // wrap happens
+        return from <= bufferId || bufferId < to;
+    }
+}
+
+static constexpr int kNumElementsInQueue = 1024*16;
+static constexpr int kMinElementsToSyncInQueue = 128;
+
+ResultStatus BufferStatusObserver::open(
+        ConnectionId id, const StatusDescriptor** 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 StatusDescriptor &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);
+        }
+    }
+}
+
+void BufferStatusChannel::postBufferInvalidateAck(
+        ConnectionId connectionId,
+        uint32_t invalidateId,
+        bool *invalidated) {
+    if (mValid && !*invalidated) {
+        size_t avail = mBufferStatusQueue->availableToWrite();
+        if (avail > 0) {
+            BufferStatusMessage message;
+            message.newStatus = BufferStatus::INVALIDATION_ACK;
+            message.bufferId = invalidateId;
+            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;
+            }
+            *invalidated = true;
+        }
+    }
+}
+
+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;
+}
+
+BufferInvalidationListener::BufferInvalidationListener(
+        const InvalidationDescriptor &fmqDesc) {
+    std::unique_ptr<BufferInvalidationQueue> queue =
+            std::make_unique<BufferInvalidationQueue>(fmqDesc);
+    if (!queue || queue->isValid() == false) {
+        mValid = false;
+        return;
+    }
+    mValid  = true;
+    mBufferInvalidationQueue = std::move(queue);
+    // drain previous messages
+    size_t avail = std::min(
+            mBufferInvalidationQueue->availableToRead(), (size_t) kNumElementsInQueue);
+    std::vector<BufferInvalidationMessage> temp(avail);
+    if (avail > 0) {
+        mBufferInvalidationQueue->read(temp.data(), avail);
+    }
+}
+
+void BufferInvalidationListener::getInvalidations(
+        std::vector<BufferInvalidationMessage> &messages) {
+    // Try twice in case of overflow.
+    // TODO: handling overflow though it may not happen.
+    for (int i = 0; i < 2; ++i) {
+        size_t avail = std::min(
+                mBufferInvalidationQueue->availableToRead(), (size_t) kNumElementsInQueue);
+        if (avail > 0) {
+            std::vector<BufferInvalidationMessage> temp(avail);
+            if (mBufferInvalidationQueue->read(temp.data(), avail)) {
+                messages.reserve(messages.size() + avail);
+                for (auto it = temp.begin(); it != temp.end(); ++it) {
+                    messages.push_back(*it);
+                }
+                break;
+            }
+        } else {
+            return;
+        }
+    }
+}
+
+bool BufferInvalidationListener::isValid() {
+    return mValid;
+}
+
+BufferInvalidationChannel::BufferInvalidationChannel()
+    : mValid(true),
+      mBufferInvalidationQueue(
+              std::make_unique<BufferInvalidationQueue>(kNumElementsInQueue, true)) {
+    if (!mBufferInvalidationQueue || mBufferInvalidationQueue->isValid() == false) {
+        mValid = false;
+    }
+}
+
+bool BufferInvalidationChannel::isValid() {
+    return mValid;
+}
+
+void BufferInvalidationChannel::getDesc(const InvalidationDescriptor **fmqDescPtr) {
+    if (mValid) {
+        *fmqDescPtr = mBufferInvalidationQueue->getDesc();
+    } else {
+        *fmqDescPtr = nullptr;
+    }
+}
+
+void BufferInvalidationChannel::postInvalidation(
+        uint32_t msgId, BufferId fromId, BufferId toId) {
+    BufferInvalidationMessage message;
+
+    message.messageId = msgId;
+    message.fromBufferId = fromId;
+    message.toBufferId = toId;
+    // TODO: handle failure (it does not happen normally.)
+    mBufferInvalidationQueue->write(&message);
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
diff --git a/media/bufferpool/2.0/BufferStatus.h b/media/bufferpool/2.0/BufferStatus.h
new file mode 100644
index 0000000..fa65838
--- /dev/null
+++ b/media/bufferpool/2.0/BufferStatus.h
@@ -0,0 +1,230 @@
+/*
+ * 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_V2_0_BUFFERSTATUS_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERSTATUS_H
+
+#include <android/hardware/media/bufferpool/2.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 V2_0 {
+namespace implementation {
+
+/** Returns monotonic timestamp in Us since fixed point in time. */
+int64_t getTimestampNow();
+
+bool isMessageLater(uint32_t curMsgId, uint32_t prevMsgId);
+
+bool isBufferInRange(BufferId from, BufferId to, BufferId bufferId);
+
+/**
+ * A collection of buffer status message 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 a buffer status message 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 StatusDescriptor** fmqDescPtr);
+
+    /** Closes a buffer status message 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);
+};
+
+/**
+ * A buffer status message 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 a buffer status message FMQ from a descriptor of
+     * the created FMQ.
+     *
+     * @param fmqDesc   Descriptor of the created FMQ.
+     */
+    BufferStatusChannel(const StatusDescriptor &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);
+
+    /**
+     * Posts a buffer invaliadation messge to the buffer pool.
+     *
+     * @param connectionId  connection Id of the client.
+     * @param invalidateId  invalidation ack to the buffer pool.
+     *                      if invalidation id is zero, the ack will not be
+     *                      posted.
+     * @param invalidated   sets {@code true} only when the invalidation ack is
+     *                      posted.
+     */
+    void postBufferInvalidateAck(
+            ConnectionId connectionId,
+            uint32_t invalidateId,
+            bool *invalidated);
+};
+
+/**
+ * A buffer invalidation FMQ for a buffer pool client. Buffer invalidation
+ * messages are received via the fmq from the buffer pool. Buffer invalidation
+ * messages are handled as soon as possible.
+ */
+class BufferInvalidationListener {
+private:
+    bool mValid;
+    std::unique_ptr<BufferInvalidationQueue> mBufferInvalidationQueue;
+
+public:
+    /**
+     * Connects to a buffer invalidation FMQ from a descriptor of the created FMQ.
+     *
+     * @param fmqDesc   Descriptor of the created FMQ.
+     */
+    BufferInvalidationListener(const InvalidationDescriptor &fmqDesc);
+
+    /** Retrieves all pending buffer invalidation messages from the buffer pool.
+     *
+     * @param messages  retrieved pending messages.
+     */
+    void getInvalidations(std::vector<BufferInvalidationMessage> &messages);
+
+    /** Returns whether the FMQ is connected succesfully. */
+    bool isValid();
+};
+
+/**
+ * A buffer invalidation FMQ for a buffer pool. A buffer pool will send buffer
+ * invalidation messages to the clients via the FMQ. The FMQ is shared among
+ * buffer pool clients.
+ */
+class BufferInvalidationChannel {
+private:
+    bool mValid;
+    std::unique_ptr<BufferInvalidationQueue> mBufferInvalidationQueue;
+
+public:
+    /**
+     * Creates a buffer invalidation FMQ for a buffer pool.
+     */
+    BufferInvalidationChannel();
+
+    /** Returns whether the FMQ is connected succesfully. */
+    bool isValid();
+
+    /**
+     * Retrieves the descriptor of a buffer invalidation FMQ. the descriptor may
+     * be passed to the client for buffer invalidation handling.
+     *
+     * @param fmqDescPtr    double ptr of created FMQ's descriptor.
+     */
+    void getDesc(const InvalidationDescriptor **fmqDescPtr);
+
+    /** Posts a buffer invalidation for invalidated buffers.
+     *
+     * @param msgId     Invalidation message id which is used when clients send
+     *                  acks back via BufferStatusMessage
+     * @param fromId    The start bufferid of the invalidated buffers(inclusive)
+     * @param toId      The end bufferId of the invalidated buffers(inclusive)
+     */
+    void postInvalidation(uint32_t msgId, BufferId fromId, BufferId toId);
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERSTATUS_H
diff --git a/media/bufferpool/2.0/ClientManager.cpp b/media/bufferpool/2.0/ClientManager.cpp
new file mode 100644
index 0000000..c31d313
--- /dev/null
+++ b/media/bufferpool/2.0/ClientManager.cpp
@@ -0,0 +1,539 @@
+/*
+ * 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"
+#include "Observer.h"
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_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 flush(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;
+
+    sp<Observer> mObserver;
+
+    ClientManagerCookieHolder mRemoteClientCookies;
+};
+
+ClientManager::Impl::Impl()
+    : mObserver(new Observer()) {}
+
+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, mObserver);
+            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();
+                mObserver->addClient(conId, wclient);
+                {
+                    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;
+    }
+    // TODO: observer is local. use direct call instead of hidl call.
+    std::shared_ptr<BufferPoolClient> client =
+            std::make_shared<BufferPoolClient>(accessor, mObserver);
+    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();
+        mObserver->addClient(conId, wclient);
+        {
+            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::unique_lock<std::mutex> lock1(mCache.mMutex);
+    std::unique_lock<std::mutex> lock2(mActive.mMutex);
+    auto it = mActive.mClients.find(connectionId);
+    if (it != mActive.mClients.end()) {
+        sp<IAccessor> accessor;
+        it->second->getAccessor(&accessor);
+        std::shared_ptr<BufferPoolClient> closing = it->second;
+        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++;
+            }
+        }
+        lock2.unlock();
+        lock1.unlock();
+        closing->flush();
+        return ResultStatus::OK;
+    }
+    return ResultStatus::NOT_FOUND;
+}
+
+ResultStatus ClientManager::Impl::flush(ConnectionId connectionId) {
+    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->flush();
+}
+
+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::V2_0::IClientManager follow.
+Return<void> ClientManager::registerSender(const sp<::android::hardware::media::bufferpool::V2_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();
+    }
+    Accessor::createInvalidator();
+    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::flush(ConnectionId connectionId) {
+    if (mImpl) {
+        return mImpl->flush(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 V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/Connection.cpp b/media/bufferpool/2.0/Connection.cpp
new file mode 100644
index 0000000..57d0c7e
--- /dev/null
+++ b/media/bufferpool/2.0/Connection.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 V2_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::bufferpool::V2_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) {
+                Buffer buffer = {};
+                buffer.id = bufferId;
+                buffer.buffer = handle;
+                _hidl_cb(status, buffer);
+                return Void();
+            }
+        } else {
+            mAccessor->cleanUp(false);
+        }
+    }
+
+    Buffer buffer = {};
+    buffer.id = 0;
+    buffer.buffer = nullptr;
+
+    _hidl_cb(status, buffer);
+    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::flush() {
+    if (mInitialized && mAccessor) {
+        return mAccessor->flush();
+    }
+    return ResultStatus::CRITICAL_ERROR;
+}
+
+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 V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/Connection.h b/media/bufferpool/2.0/Connection.h
new file mode 100644
index 0000000..8507749
--- /dev/null
+++ b/media/bufferpool/2.0/Connection.h
@@ -0,0 +1,108 @@
+/*
+ * 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_V2_0_CONNECTION_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CONNECTION_H
+
+#include <android/hardware/media/bufferpool/2.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 V2_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::V2_0::implementation::Accessor;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Connection : public IConnection {
+    // Methods from ::android::hardware::media::bufferpool::V2_0::IConnection follow.
+    Return<void> fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override;
+
+    /**
+     * Invalidates all buffers which are active and/or are ready to be recycled.
+     */
+    ResultStatus flush();
+
+    /**
+     * 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 V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CONNECTION_H
diff --git a/media/bufferpool/2.0/Observer.cpp b/media/bufferpool/2.0/Observer.cpp
new file mode 100644
index 0000000..5b23160
--- /dev/null
+++ b/media/bufferpool/2.0/Observer.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Observer.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+Observer::Observer() {
+}
+
+Observer::~Observer() {
+}
+
+// Methods from ::android::hardware::media::bufferpool::V2_0::IObserver follow.
+Return<void> Observer::onMessage(int64_t connectionId, uint32_t msgId) {
+    std::unique_lock<std::mutex> lock(mLock);
+    auto it = mClients.find(connectionId);
+    if (it != mClients.end()) {
+        const std::shared_ptr<BufferPoolClient> client = it->second.lock();
+        if (!client) {
+            mClients.erase(it);
+        } else {
+            lock.unlock();
+            client->receiveInvalidation(msgId);
+        }
+    }
+    return Void();
+}
+
+void Observer::addClient(ConnectionId connectionId,
+                         const std::weak_ptr<BufferPoolClient> &wclient) {
+    std::lock_guard<std::mutex> lock(mLock);
+    for (auto it = mClients.begin(); it != mClients.end();) {
+        if (!it->second.lock() || it->first == connectionId) {
+            it = mClients.erase(it);
+        } else {
+            ++it;
+        }
+    }
+    mClients.insert(std::make_pair(connectionId, wclient));
+
+}
+
+void Observer::delClient(ConnectionId connectionId) {
+    std::lock_guard<std::mutex> lock(mLock);
+    mClients.erase(connectionId);
+}
+
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/bufferpool/2.0/Observer.h b/media/bufferpool/2.0/Observer.h
new file mode 100644
index 0000000..42bd7c1
--- /dev/null
+++ b/media/bufferpool/2.0/Observer.h
@@ -0,0 +1,67 @@
+/*
+ * 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_V2_0_OBSERVER_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_OBSERVER_H
+
+#include <android/hardware/media/bufferpool/2.0/IObserver.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "BufferPoolClient.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_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 Observer : public IObserver {
+    // Methods from ::android::hardware::media::bufferpool::V2_0::IObserver follow.
+    Return<void> onMessage(int64_t connectionId, uint32_t msgId) override;
+
+    ~Observer();
+
+    void addClient(ConnectionId connectionId,
+                   const std::weak_ptr<BufferPoolClient> &wclient);
+
+    void delClient(ConnectionId connectionId);
+
+private:
+    Observer();
+
+    friend struct ClientManager;
+
+    std::mutex mLock;
+    std::map<ConnectionId, const std::weak_ptr<BufferPoolClient>> mClients;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_OBSERVER_H
diff --git a/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h b/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h
new file mode 100644
index 0000000..7c906cb
--- /dev/null
+++ b/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h
@@ -0,0 +1,123 @@
+/*
+ * 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_V2_0_BUFFERPOOLTYPES_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLTYPES_H
+
+#include <android/hardware/media/bufferpool/2.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@2.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 V2_0 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::kUnsynchronizedWrite;
+
+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 StatusDescriptor;
+
+typedef android::hardware::MessageQueue<BufferInvalidationMessage, kUnsynchronizedWrite>
+        BufferInvalidationQueue;
+typedef BufferInvalidationQueue::Descriptor InvalidationDescriptor;
+
+/**
+ * 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 V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLTYPES_H
diff --git a/media/bufferpool/2.0/include/bufferpool/ClientManager.h b/media/bufferpool/2.0/include/bufferpool/ClientManager.h
new file mode 100644
index 0000000..953c304
--- /dev/null
+++ b/media/bufferpool/2.0/include/bufferpool/ClientManager.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CLIENTMANAGER_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CLIENTMANAGER_H
+
+#include <android/hardware/media/bufferpool/2.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 V2_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::V2_0::IAccessor;
+using ::android::hardware::media::bufferpool::V2_0::ResultStatus;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ClientManager : public IClientManager {
+    // Methods from ::android::hardware::media::bufferpool::V2_0::IClientManager follow.
+    Return<void> registerSender(const sp<::android::hardware::media::bufferpool::V2_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);
+
+    /**
+     * Evicts cached allocations. If it's local connection, release the
+     * previous allocations and do not recycle current active allocations.
+     *
+     * @param connectionId The id of the connection.
+     *
+     * @return OK when the connection is resetted.
+     *         NOT_FOUND when the specified connection was not found.
+     *         CRITICAL_ERROR otherwise.
+     */
+    ResultStatus flush(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 V2_0
+}  // namespace bufferpool
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CLIENTMANAGER_H
diff --git a/media/bufferpool/2.0/tests/Android.bp b/media/bufferpool/2.0/tests/Android.bp
new file mode 100644
index 0000000..8b44f61
--- /dev/null
+++ b/media/bufferpool/2.0/tests/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: "VtsVndkHidlBufferpoolV2_0TargetSingleTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "allocator.cpp",
+        "single.cpp",
+    ],
+    static_libs: [
+        "android.hardware.media.bufferpool@2.0",
+        "libcutils",
+        "libstagefright_bufferpool@2.0",
+    ],
+    shared_libs: [
+        "libfmq",
+    ],
+    compile_multilib: "both",
+}
+
+cc_test {
+    name: "VtsVndkHidlBufferpoolV2_0TargetMultiTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "allocator.cpp",
+        "multi.cpp",
+    ],
+    static_libs: [
+        "android.hardware.media.bufferpool@2.0",
+        "libcutils",
+        "libstagefright_bufferpool@2.0",
+    ],
+    shared_libs: [
+        "libfmq",
+    ],
+    compile_multilib: "both",
+}
diff --git a/media/bufferpool/2.0/tests/OWNERS b/media/bufferpool/2.0/tests/OWNERS
new file mode 100644
index 0000000..6733e0c
--- /dev/null
+++ b/media/bufferpool/2.0/tests/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/2.0/tests/allocator.cpp b/media/bufferpool/2.0/tests/allocator.cpp
new file mode 100644
index 0000000..843f7ea
--- /dev/null
+++ b/media/bufferpool/2.0/tests/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/2.0/tests/allocator.h b/media/bufferpool/2.0/tests/allocator.h
new file mode 100644
index 0000000..5281dc3
--- /dev/null
+++ b/media/bufferpool/2.0/tests/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_V2_0_ALLOCATOR_H
+#define VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H
+
+#include <bufferpool/BufferPoolTypes.h>
+
+using android::hardware::media::bufferpool::V2_0::ResultStatus;
+using android::hardware::media::bufferpool::V2_0::implementation::
+    BufferPoolAllocation;
+using android::hardware::media::bufferpool::V2_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_V2_0_ALLOCATOR_H
diff --git a/media/bufferpool/2.0/tests/multi.cpp b/media/bufferpool/2.0/tests/multi.cpp
new file mode 100644
index 0000000..68b6992
--- /dev/null
+++ b/media/bufferpool/2.0/tests/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::V2_0::IClientManager;
+using android::hardware::media::bufferpool::V2_0::ResultStatus;
+using android::hardware::media::bufferpool::V2_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V2_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V2_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/2.0/tests/single.cpp b/media/bufferpool/2.0/tests/single.cpp
new file mode 100644
index 0000000..777edcf
--- /dev/null
+++ b/media/bufferpool/2.0/tests/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::V2_0::ResultStatus;
+using android::hardware::media::bufferpool::V2_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V2_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V2_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/codec2/Android.mk b/media/codec2/Android.mk
new file mode 100644
index 0000000..82d739f
--- /dev/null
+++ b/media/codec2/Android.mk
@@ -0,0 +1,48 @@
+# =============================================================================
+# DOCUMENTATION GENERATION
+# =============================================================================
+C2_ROOT := $(call my-dir)
+
+C2_DOCS_ROOT := $(OUT_DIR)/target/common/docs/codec2
+
+C2_OUT_TEMP := $(PRODUCT_OUT)/gen/ETC/Codec2-docs_intermediates
+
+C2_DOXY := $(or $(shell command -v doxygen),\
+		$(shell command -v /Applications/Doxygen.app/Contents/Resources/doxygen))
+
+.PHONY: check-doxygen
+check-doxygen:
+ifndef C2_DOXY
+	$(error 'doxygen is not available')
+endif
+
+$(C2_OUT_TEMP)/doxy-api.config: $(C2_ROOT)/docs/doxygen.config
+	# only document include directory, no internal sections
+	sed 's/\(^INPUT *=.*\)/\1include\//; \
+	s/\(^INTERNAL_DOCS *= *\).*/\1NO/; \
+	s/\(^ENABLED_SECTIONS *=.*\)INTERNAL\(.*\).*/\1\2/; \
+	s:\(^OUTPUT_DIRECTORY *= \)out:\1'$(OUT_DIR)':;' \
+		$(C2_ROOT)/docs/doxygen.config > $@
+
+$(C2_OUT_TEMP)/doxy-internal.config: $(C2_ROOT)/docs/doxygen.config
+	sed 's:\(^OUTPUT_DIRECTORY *= \)out\(.*\)api:\1'$(OUT_DIR)'\2internal:;' \
+		$(C2_ROOT)/docs/doxygen.config > $@
+
+.PHONY: docs-api
+docs-api: $(C2_OUT_TEMP)/doxy-api.config check-doxygen
+	echo API docs are building in $(C2_DOCS_ROOT)/api
+	rm -rf $(C2_DOCS_ROOT)/api
+	mkdir -p $(C2_DOCS_ROOT)/api
+	$(C2_DOXY) $(C2_OUT_TEMP)/doxy-api.config
+
+.PHONY: docs-internal
+docs-internal: $(C2_OUT_TEMP)/doxy-internal.config check-doxygen
+	echo Internal docs are building in $(C2_DOCS_ROOT)/internal
+	rm -rf $(C2_DOCS_ROOT)/internal
+	mkdir -p $(C2_DOCS_ROOT)/internal
+	$(C2_DOXY) $(C2_OUT_TEMP)/doxy-internal.config
+
+.PHONY: docs-all
+docs-all: docs-api docs-internal
+
+include $(call all-makefiles-under,$(call my-dir))
diff --git a/media/codec2/components/aac/Android.bp b/media/codec2/components/aac/Android.bp
new file mode 100644
index 0000000..9eca585
--- /dev/null
+++ b/media/codec2/components/aac/Android.bp
@@ -0,0 +1,30 @@
+cc_library_shared {
+    name: "libcodec2_soft_aacdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: [
+        "C2SoftAacDec.cpp",
+        "DrcPresModeWrap.cpp",
+    ],
+
+    static_libs: [
+        "libFraunhoferAAC",
+    ],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_aacenc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftAacEnc.cpp"],
+
+    static_libs: [
+        "libFraunhoferAAC",
+    ],
+}
diff --git a/media/codec2/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp
new file mode 100644
index 0000000..2d4e126
--- /dev/null
+++ b/media/codec2/components/aac/C2SoftAacDec.cpp
@@ -0,0 +1,928 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftAacDec"
+#include <log/log.h>
+
+#include <inttypes.h>
+#include <math.h>
+#include <numeric>
+
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/misc.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftAacDec.h"
+
+#define FILEREAD_MAX_LAYERS 2
+
+#define DRC_DEFAULT_MOBILE_REF_LEVEL -16.0  /* 64*-0.25dB = -16 dB below full scale for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_CUT   1.0 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_BOOST 1.0 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_HEAVY C2Config::DRC_COMPRESSION_HEAVY   /* switch for heavy compression for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_EFFECT 3  /* MPEG-D DRC effect type; 3 => Limited playback range */
+#define DRC_DEFAULT_MOBILE_ENC_LEVEL (0.25) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
+#define MAX_CHANNEL_COUNT            8  /* maximum number of audio channels that can be decoded */
+// names of properties that can be used to override the default DRC settings
+#define PROP_DRC_OVERRIDE_REF_LEVEL  "aac_drc_reference_level"
+#define PROP_DRC_OVERRIDE_CUT        "aac_drc_cut"
+#define PROP_DRC_OVERRIDE_BOOST      "aac_drc_boost"
+#define PROP_DRC_OVERRIDE_HEAVY      "aac_drc_heavy"
+#define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level"
+#define PROP_DRC_OVERRIDE_EFFECT     "ro.aac_drc_effect_type"
+
+namespace android {
+
+constexpr char COMPONENT_NAME[] = "c2.android.aac.decoder";
+
+class C2SoftAacDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_AAC) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+
+        addParameter(
+                DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+                .withConstValue(new C2PortActualDelayTuning::output(2u))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::output(0u, 44100))
+                .withFields({C2F(mSampleRate, value).oneOf({
+                    7350, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+                })})
+                .withSetter(Setter<decltype(*mSampleRate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::output(0u, 1))
+                .withFields({C2F(mChannelCount, value).inRange(1, 8)})
+                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::input(0u, 64000))
+                .withFields({C2F(mBitrate, value).inRange(8000, 960000)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 8192))
+                .build());
+
+        addParameter(
+                DefineParam(mAacFormat, C2_PARAMKEY_AAC_PACKAGING)
+                .withDefault(new C2StreamAacFormatInfo::input(0u, C2Config::AAC_PACKAGING_RAW))
+                .withFields({C2F(mAacFormat, value).oneOf({
+                    C2Config::AAC_PACKAGING_RAW, C2Config::AAC_PACKAGING_ADTS
+                })})
+                .withSetter(Setter<decltype(*mAacFormat)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::input(0u,
+                        C2Config::PROFILE_AAC_LC, C2Config::LEVEL_UNUSED))
+                .withFields({
+                    C2F(mProfileLevel, profile).oneOf({
+                            C2Config::PROFILE_AAC_LC,
+                            C2Config::PROFILE_AAC_HE,
+                            C2Config::PROFILE_AAC_HE_PS,
+                            C2Config::PROFILE_AAC_LD,
+                            C2Config::PROFILE_AAC_ELD,
+                            C2Config::PROFILE_AAC_ER_SCALABLE,
+                            C2Config::PROFILE_AAC_XHE}),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_UNUSED
+                    })
+                })
+                .withSetter(ProfileLevelSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcCompressMode, C2_PARAMKEY_DRC_COMPRESSION_MODE)
+                .withDefault(new C2StreamDrcCompressionModeTuning::input(0u, C2Config::DRC_COMPRESSION_HEAVY))
+                .withFields({
+                    C2F(mDrcCompressMode, value).oneOf({
+                            C2Config::DRC_COMPRESSION_ODM_DEFAULT,
+                            C2Config::DRC_COMPRESSION_NONE,
+                            C2Config::DRC_COMPRESSION_LIGHT,
+                            C2Config::DRC_COMPRESSION_HEAVY})
+                })
+                .withSetter(Setter<decltype(*mDrcCompressMode)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcTargetRefLevel, C2_PARAMKEY_DRC_TARGET_REFERENCE_LEVEL)
+                .withDefault(new C2StreamDrcTargetReferenceLevelTuning::input(0u, DRC_DEFAULT_MOBILE_REF_LEVEL))
+                .withFields({C2F(mDrcTargetRefLevel, value).inRange(-31.75, 0.25)})
+                .withSetter(Setter<decltype(*mDrcTargetRefLevel)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcEncTargetLevel, C2_PARAMKEY_DRC_ENCODED_TARGET_LEVEL)
+                .withDefault(new C2StreamDrcEncodedTargetLevelTuning::input(0u, DRC_DEFAULT_MOBILE_ENC_LEVEL))
+                .withFields({C2F(mDrcEncTargetLevel, value).inRange(-31.75, 0.25)})
+                .withSetter(Setter<decltype(*mDrcEncTargetLevel)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcBoostFactor, C2_PARAMKEY_DRC_BOOST_FACTOR)
+                .withDefault(new C2StreamDrcBoostFactorTuning::input(0u, DRC_DEFAULT_MOBILE_DRC_BOOST))
+                .withFields({C2F(mDrcBoostFactor, value).inRange(0, 1.)})
+                .withSetter(Setter<decltype(*mDrcBoostFactor)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcAttenuationFactor, C2_PARAMKEY_DRC_ATTENUATION_FACTOR)
+                .withDefault(new C2StreamDrcAttenuationFactorTuning::input(0u, DRC_DEFAULT_MOBILE_DRC_CUT))
+                .withFields({C2F(mDrcAttenuationFactor, value).inRange(0, 1.)})
+                .withSetter(Setter<decltype(*mDrcAttenuationFactor)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcEffectType, C2_PARAMKEY_DRC_EFFECT_TYPE)
+                .withDefault(new C2StreamDrcEffectTypeTuning::input(0u, C2Config::DRC_EFFECT_LIMITED_PLAYBACK_RANGE))
+                .withFields({
+                    C2F(mDrcEffectType, value).oneOf({
+                            C2Config::DRC_EFFECT_ODM_DEFAULT,
+                            C2Config::DRC_EFFECT_OFF,
+                            C2Config::DRC_EFFECT_NONE,
+                            C2Config::DRC_EFFECT_LATE_NIGHT,
+                            C2Config::DRC_EFFECT_NOISY_ENVIRONMENT,
+                            C2Config::DRC_EFFECT_LIMITED_PLAYBACK_RANGE,
+                            C2Config::DRC_EFFECT_LOW_PLAYBACK_LEVEL,
+                            C2Config::DRC_EFFECT_DIALOG_ENHANCEMENT,
+                            C2Config::DRC_EFFECT_GENERAL_COMPRESSION})
+                })
+                .withSetter(Setter<decltype(*mDrcEffectType)>::StrictValueWithNoDeps)
+                .build());
+    }
+
+    bool isAdts() const { return mAacFormat->value == C2Config::AAC_PACKAGING_ADTS; }
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input> &me) {
+        (void)mayBlock;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+    int32_t getDrcCompressMode() const { return mDrcCompressMode->value == C2Config::DRC_COMPRESSION_HEAVY ? 1 : 0; }
+    int32_t getDrcTargetRefLevel() const { return (mDrcTargetRefLevel->value <= 0 ? -mDrcTargetRefLevel->value * 4. + 0.5 : -1); }
+    int32_t getDrcEncTargetLevel() const { return (mDrcEncTargetLevel->value <= 0 ? -mDrcEncTargetLevel->value * 4. + 0.5 : -1); }
+    int32_t getDrcBoostFactor() const { return mDrcBoostFactor->value * 127. + 0.5; }
+    int32_t getDrcAttenuationFactor() const { return mDrcAttenuationFactor->value * 127. + 0.5; }
+    int32_t getDrcEffectType() const { return mDrcEffectType->value; }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+    std::shared_ptr<C2StreamAacFormatInfo::input> mAacFormat;
+    std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+    std::shared_ptr<C2StreamDrcCompressionModeTuning::input> mDrcCompressMode;
+    std::shared_ptr<C2StreamDrcTargetReferenceLevelTuning::input> mDrcTargetRefLevel;
+    std::shared_ptr<C2StreamDrcEncodedTargetLevelTuning::input> mDrcEncTargetLevel;
+    std::shared_ptr<C2StreamDrcBoostFactorTuning::input> mDrcBoostFactor;
+    std::shared_ptr<C2StreamDrcAttenuationFactorTuning::input> mDrcAttenuationFactor;
+    std::shared_ptr<C2StreamDrcEffectTypeTuning::input> mDrcEffectType;
+    // TODO Add : C2StreamAacSbrModeTuning
+};
+
+C2SoftAacDec::C2SoftAacDec(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mAACDecoder(nullptr),
+      mStreamInfo(nullptr),
+      mSignalledError(false),
+      mOutputDelayRingBuffer(nullptr) {
+}
+
+C2SoftAacDec::~C2SoftAacDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftAacDec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftAacDec::onStop() {
+    drainDecoder();
+    // reset the "configured" state
+    mOutputDelayCompensated = 0;
+    mOutputDelayRingBufferWritePos = 0;
+    mOutputDelayRingBufferReadPos = 0;
+    mOutputDelayRingBufferFilled = 0;
+    mBuffersInfo.clear();
+
+    // To make the codec behave the same before and after a reset, we need to invalidate the
+    // streaminfo struct. This does that:
+    mStreamInfo->sampleRate = 0; // TODO: mStreamInfo is read only
+
+    mSignalledError = false;
+
+    return C2_OK;
+}
+
+void C2SoftAacDec::onReset() {
+    (void)onStop();
+}
+
+void C2SoftAacDec::onRelease() {
+    if (mAACDecoder) {
+        aacDecoder_Close(mAACDecoder);
+        mAACDecoder = nullptr;
+    }
+    if (mOutputDelayRingBuffer) {
+        delete[] mOutputDelayRingBuffer;
+        mOutputDelayRingBuffer = nullptr;
+    }
+}
+
+status_t C2SoftAacDec::initDecoder() {
+    ALOGV("initDecoder()");
+    status_t status = UNKNOWN_ERROR;
+    mAACDecoder = aacDecoder_Open(TT_MP4_ADIF, /* num layers */ 1);
+    if (mAACDecoder != nullptr) {
+        mStreamInfo = aacDecoder_GetStreamInfo(mAACDecoder);
+        if (mStreamInfo != nullptr) {
+            status = OK;
+        }
+    }
+
+    mOutputDelayCompensated = 0;
+    mOutputDelayRingBufferSize = 2048 * MAX_CHANNEL_COUNT * kNumDelayBlocksMax;
+    mOutputDelayRingBuffer = new short[mOutputDelayRingBufferSize];
+    mOutputDelayRingBufferWritePos = 0;
+    mOutputDelayRingBufferReadPos = 0;
+    mOutputDelayRingBufferFilled = 0;
+
+    if (mAACDecoder == nullptr) {
+        ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code");
+    }
+
+    //aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE, 0);
+
+    //init DRC wrapper
+    mDrcWrap.setDecoderHandle(mAACDecoder);
+    mDrcWrap.submitStreamData(mStreamInfo);
+
+    // for streams that contain metadata, use the mobile profile DRC settings unless overridden by platform properties
+    // TODO: change the DRC settings depending on audio output device type (HDMI, loadspeaker, headphone)
+
+    //  DRC_PRES_MODE_WRAP_DESIRED_TARGET
+    int32_t targetRefLevel = mIntf->getDrcTargetRefLevel();
+    ALOGV("AAC decoder using desired DRC target reference level of %d", targetRefLevel);
+    mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, (unsigned)targetRefLevel);
+
+    //  DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR
+
+    int32_t attenuationFactor = mIntf->getDrcAttenuationFactor();
+    ALOGV("AAC decoder using desired DRC attenuation factor of %d", attenuationFactor);
+    mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, (unsigned)attenuationFactor);
+
+    //  DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR
+    int32_t boostFactor = mIntf->getDrcBoostFactor();
+    ALOGV("AAC decoder using desired DRC boost factor of %d", boostFactor);
+    mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, (unsigned)boostFactor);
+
+    //  DRC_PRES_MODE_WRAP_DESIRED_HEAVY
+    int32_t compressMode = mIntf->getDrcCompressMode();
+    ALOGV("AAC decoder using desried DRC heavy compression switch of %d", compressMode);
+    mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, (unsigned)compressMode);
+
+    // DRC_PRES_MODE_WRAP_ENCODER_TARGET
+    int32_t encTargetLevel = mIntf->getDrcEncTargetLevel();
+    ALOGV("AAC decoder using encoder-side DRC reference level of %d", encTargetLevel);
+    mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, (unsigned)encTargetLevel);
+
+    // AAC_UNIDRC_SET_EFFECT
+    int32_t effectType = mIntf->getDrcEffectType();
+    ALOGV("AAC decoder using MPEG-D DRC effect type %d", effectType);
+    aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_SET_EFFECT, effectType);
+
+    // By default, the decoder creates a 5.1 channel downmix signal.
+    // For seven and eight channel input streams, enable 6.1 and 7.1 channel output
+    aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1);
+
+    return status;
+}
+
+bool C2SoftAacDec::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) {
+    if (numSamples == 0) {
+        return true;
+    }
+    if (outputDelayRingBufferSpaceLeft() < numSamples) {
+        ALOGE("RING BUFFER WOULD OVERFLOW");
+        return false;
+    }
+    if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize
+            && (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos
+                    || mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) {
+        // faster memcopy loop without checks, if the preconditions allow this
+        for (int32_t i = 0; i < numSamples; i++) {
+            mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos++] = samples[i];
+        }
+
+        if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
+            mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
+        }
+    } else {
+        ALOGV("slow C2SoftAacDec::outputDelayRingBufferPutSamples()");
+
+        for (int32_t i = 0; i < numSamples; i++) {
+            mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos] = samples[i];
+            mOutputDelayRingBufferWritePos++;
+            if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
+                mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
+            }
+        }
+    }
+    mOutputDelayRingBufferFilled += numSamples;
+    return true;
+}
+
+int32_t C2SoftAacDec::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t numSamples) {
+
+    if (numSamples > mOutputDelayRingBufferFilled) {
+        ALOGE("RING BUFFER WOULD UNDERRUN");
+        return -1;
+    }
+
+    if (mOutputDelayRingBufferReadPos + numSamples <= mOutputDelayRingBufferSize
+            && (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos
+                    || mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) {
+        // faster memcopy loop without checks, if the preconditions allow this
+        if (samples != nullptr) {
+            for (int32_t i = 0; i < numSamples; i++) {
+                samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos++];
+            }
+        } else {
+            mOutputDelayRingBufferReadPos += numSamples;
+        }
+        if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
+            mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
+        }
+    } else {
+        ALOGV("slow C2SoftAacDec::outputDelayRingBufferGetSamples()");
+
+        for (int32_t i = 0; i < numSamples; i++) {
+            if (samples != nullptr) {
+                samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos];
+            }
+            mOutputDelayRingBufferReadPos++;
+            if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
+                mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
+            }
+        }
+    }
+    mOutputDelayRingBufferFilled -= numSamples;
+    return numSamples;
+}
+
+int32_t C2SoftAacDec::outputDelayRingBufferSamplesAvailable() {
+    return mOutputDelayRingBufferFilled;
+}
+
+int32_t C2SoftAacDec::outputDelayRingBufferSpaceLeft() {
+    return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable();
+}
+
+void C2SoftAacDec::drainRingBuffer(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool,
+        bool eos) {
+    while (!mBuffersInfo.empty() && outputDelayRingBufferSamplesAvailable()
+            >= mStreamInfo->frameSize * mStreamInfo->numChannels) {
+        Info &outInfo = mBuffersInfo.front();
+        ALOGV("outInfo.frameIndex = %" PRIu64, outInfo.frameIndex);
+        int samplesize __unused = mStreamInfo->numChannels * sizeof(int16_t);
+
+        int available = outputDelayRingBufferSamplesAvailable();
+        int numFrames = outInfo.decodedSizes.size();
+        int numSamples = numFrames * (mStreamInfo->frameSize * mStreamInfo->numChannels);
+        if (available < numSamples) {
+            if (eos) {
+                numSamples = available;
+            } else {
+                break;
+            }
+        }
+        ALOGV("%d samples available (%d), or %d frames",
+                numSamples, available, numFrames);
+        ALOGV("getting %d from ringbuffer", numSamples);
+
+        std::shared_ptr<C2LinearBlock> block;
+        std::function<void(const std::unique_ptr<C2Work>&)> fillWork =
+            [&block, numSamples, pool, this]()
+                    -> std::function<void(const std::unique_ptr<C2Work>&)> {
+                auto fillEmptyWork = [](
+                        const std::unique_ptr<C2Work> &work, c2_status_t err) {
+                    work->result = err;
+                    C2FrameData &output = work->worklets.front()->output;
+                    output.flags = work->input.flags;
+                    output.buffers.clear();
+                    output.ordinal = work->input.ordinal;
+
+                    work->workletsProcessed = 1u;
+                };
+
+                using namespace std::placeholders;
+                if (numSamples == 0) {
+                    return std::bind(fillEmptyWork, _1, C2_OK);
+                }
+
+                // TODO: error handling, proper usage, etc.
+                C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+                c2_status_t err = pool->fetchLinearBlock(
+                        numSamples * sizeof(int16_t), usage, &block);
+                if (err != C2_OK) {
+                    ALOGD("failed to fetch a linear block (%d)", err);
+                    return std::bind(fillEmptyWork, _1, C2_NO_MEMORY);
+                }
+                C2WriteView wView = block->map().get();
+                // TODO
+                INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(wView.data());
+                int32_t ns = outputDelayRingBufferGetSamples(outBuffer, numSamples);
+                if (ns != numSamples) {
+                    ALOGE("not a complete frame of samples available");
+                    mSignalledError = true;
+                    return std::bind(fillEmptyWork, _1, C2_CORRUPTED);
+                }
+                return [buffer = createLinearBuffer(block)](
+                        const std::unique_ptr<C2Work> &work) {
+                    work->result = C2_OK;
+                    C2FrameData &output = work->worklets.front()->output;
+                    output.flags = work->input.flags;
+                    output.buffers.clear();
+                    output.buffers.push_back(buffer);
+                    output.ordinal = work->input.ordinal;
+                    work->workletsProcessed = 1u;
+                };
+            }();
+
+        if (work && work->input.ordinal.frameIndex == c2_cntr64_t(outInfo.frameIndex)) {
+            fillWork(work);
+        } else {
+            finish(outInfo.frameIndex, fillWork);
+        }
+
+        ALOGV("out timestamp %" PRIu64 " / %u", outInfo.timestamp, block ? block->capacity() : 0);
+        mBuffersInfo.pop_front();
+    }
+}
+
+void C2SoftAacDec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError) {
+        return;
+    }
+
+    UCHAR* inBuffer[FILEREAD_MAX_LAYERS];
+    UINT inBufferLength[FILEREAD_MAX_LAYERS] = {0};
+    UINT bytesValid[FILEREAD_MAX_LAYERS] = {0};
+
+    INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+    C2ReadView view = mDummyReadView;
+    size_t offset = 0u;
+    size_t size = 0u;
+    if (!work->input.buffers.empty()) {
+        view = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        size = view.capacity();
+    }
+
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+    bool codecConfig = (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+
+    //TODO
+#if 0
+    if (mInputBufferCount == 0 && !codecConfig) {
+        ALOGW("first buffer should have FLAG_CODEC_CONFIG set");
+        codecConfig = true;
+    }
+#endif
+    if (codecConfig && size > 0u) {
+        // const_cast because of libAACdec method signature.
+        inBuffer[0] = const_cast<UCHAR *>(view.data() + offset);
+        inBufferLength[0] = size;
+
+        AAC_DECODER_ERROR decoderErr =
+            aacDecoder_ConfigRaw(mAACDecoder,
+                                 inBuffer,
+                                 inBufferLength);
+
+        if (decoderErr != AAC_DEC_OK) {
+            ALOGE("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        work->worklets.front()->output.flags = work->input.flags;
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->worklets.front()->output.buffers.clear();
+        return;
+    }
+
+    Info inInfo;
+    inInfo.frameIndex = work->input.ordinal.frameIndex.peeku();
+    inInfo.timestamp = work->input.ordinal.timestamp.peeku();
+    inInfo.bufferSize = size;
+    inInfo.decodedSizes.clear();
+    while (size > 0u) {
+        ALOGV("size = %zu", size);
+        if (mIntf->isAdts()) {
+            size_t adtsHeaderSize = 0;
+            // skip 30 bits, aac_frame_length follows.
+            // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
+
+            const uint8_t *adtsHeader = view.data() + offset;
+
+            bool signalError = false;
+            if (size < 7) {
+                ALOGE("Audio data too short to contain even the ADTS header. "
+                        "Got %zu bytes.", size);
+                hexdump(adtsHeader, size);
+                signalError = true;
+            } else {
+                bool protectionAbsent = (adtsHeader[1] & 1);
+
+                unsigned aac_frame_length =
+                    ((adtsHeader[3] & 3) << 11)
+                    | (adtsHeader[4] << 3)
+                    | (adtsHeader[5] >> 5);
+
+                if (size < aac_frame_length) {
+                    ALOGE("Not enough audio data for the complete frame. "
+                            "Got %zu bytes, frame size according to the ADTS "
+                            "header is %u bytes.",
+                            size, aac_frame_length);
+                    hexdump(adtsHeader, size);
+                    signalError = true;
+                } else {
+                    adtsHeaderSize = (protectionAbsent ? 7 : 9);
+                    if (aac_frame_length < adtsHeaderSize) {
+                        signalError = true;
+                    } else {
+                        // const_cast because of libAACdec method signature.
+                        inBuffer[0] = const_cast<UCHAR *>(adtsHeader + adtsHeaderSize);
+                        inBufferLength[0] = aac_frame_length - adtsHeaderSize;
+
+                        offset += adtsHeaderSize;
+                        size -= adtsHeaderSize;
+                    }
+                }
+            }
+
+            if (signalError) {
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+        } else {
+            // const_cast because of libAACdec method signature.
+            inBuffer[0] = const_cast<UCHAR *>(view.data() + offset);
+            inBufferLength[0] = size;
+        }
+
+        // Fill and decode
+        bytesValid[0] = inBufferLength[0];
+
+        INT prevSampleRate = mStreamInfo->sampleRate;
+        INT prevNumChannels = mStreamInfo->numChannels;
+
+        aacDecoder_Fill(mAACDecoder,
+                        inBuffer,
+                        inBufferLength,
+                        bytesValid);
+
+        // run DRC check
+        mDrcWrap.submitStreamData(mStreamInfo);
+        mDrcWrap.update();
+
+        UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
+        size -= inBufferUsedLength;
+        offset += inBufferUsedLength;
+
+        AAC_DECODER_ERROR decoderErr;
+        do {
+            if (outputDelayRingBufferSpaceLeft() <
+                    (mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+                ALOGV("skipping decode: not enough space left in ringbuffer");
+                // discard buffer
+                size = 0;
+                break;
+            }
+
+            int numConsumed = mStreamInfo->numTotalBytes;
+            decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
+                                       tmpOutBuffer,
+                                       2048 * MAX_CHANNEL_COUNT,
+                                       0 /* flags */);
+
+            numConsumed = mStreamInfo->numTotalBytes - numConsumed;
+
+            if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
+                break;
+            }
+            inInfo.decodedSizes.push_back(numConsumed);
+
+            if (decoderErr != AAC_DEC_OK) {
+                ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+            }
+
+            if (bytesValid[0] != 0) {
+                ALOGE("bytesValid[0] != 0 should never happen");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+
+            size_t numOutBytes =
+                mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
+
+            if (decoderErr == AAC_DEC_OK) {
+                if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+                        mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+                    mSignalledError = true;
+                    work->result = C2_CORRUPTED;
+                    return;
+                }
+            } else {
+                ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
+
+                memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
+
+                if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+                        mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+                    mSignalledError = true;
+                    work->result = C2_CORRUPTED;
+                    return;
+                }
+
+                // Discard input buffer.
+                size = 0;
+
+                aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
+
+                // After an error, replace bufferSize with the sum of the
+                // decodedSizes to resynchronize the in/out lists.
+                inInfo.decodedSizes.pop_back();
+                inInfo.bufferSize = std::accumulate(
+                        inInfo.decodedSizes.begin(), inInfo.decodedSizes.end(), 0);
+
+                // fall through
+            }
+
+            /*
+             * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+             * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+             * rate system and the sampling rate in the final output is actually
+             * doubled compared with the core AAC decoder sampling rate.
+             *
+             * Explicit signalling is done by explicitly defining SBR audio object
+             * type in the bitstream. Implicit signalling is done by embedding
+             * SBR content in AAC extension payload specific to SBR, and hence
+             * requires an AAC decoder to perform pre-checks on actual audio frames.
+             *
+             * Thus, we could not say for sure whether a stream is
+             * AAC+/eAAC+ until the first data frame is decoded.
+             */
+            if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
+                // if ((mInputBufferCount > 2) && (mOutputBufferCount <= 1)) {
+                    ALOGD("Invalid AAC stream");
+                    // TODO: notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                    // mSignalledError = true;
+                // }
+            } else if ((mStreamInfo->sampleRate != prevSampleRate) ||
+                       (mStreamInfo->numChannels != prevNumChannels)) {
+                ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+                      prevSampleRate, mStreamInfo->sampleRate,
+                      prevNumChannels, mStreamInfo->numChannels);
+
+                C2StreamSampleRateInfo::output sampleRateInfo(0u, mStreamInfo->sampleRate);
+                C2StreamChannelCountInfo::output channelCountInfo(0u, mStreamInfo->numChannels);
+                std::vector<std::unique_ptr<C2SettingResult>> failures;
+                c2_status_t err = mIntf->config(
+                        { &sampleRateInfo, &channelCountInfo },
+                        C2_MAY_BLOCK,
+                        &failures);
+                if (err == OK) {
+                    // TODO: this does not handle the case where the values are
+                    //       altered during config.
+                    C2FrameData &output = work->worklets.front()->output;
+                    output.configUpdate.push_back(C2Param::Copy(sampleRateInfo));
+                    output.configUpdate.push_back(C2Param::Copy(channelCountInfo));
+                } else {
+                    ALOGE("Config Update failed");
+                    mSignalledError = true;
+                    work->result = C2_CORRUPTED;
+                    return;
+                }
+            }
+            ALOGV("size = %zu", size);
+        } while (decoderErr == AAC_DEC_OK);
+    }
+
+    int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
+
+    mBuffersInfo.push_back(std::move(inInfo));
+    work->workletsProcessed = 0u;
+    if (!eos && mOutputDelayCompensated < outputDelay) {
+        // discard outputDelay at the beginning
+        int32_t toCompensate = outputDelay - mOutputDelayCompensated;
+        int32_t discard = outputDelayRingBufferSamplesAvailable();
+        if (discard > toCompensate) {
+            discard = toCompensate;
+        }
+        int32_t discarded = outputDelayRingBufferGetSamples(nullptr, discard);
+        mOutputDelayCompensated += discarded;
+        return;
+    }
+
+    if (eos) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+    } else {
+        drainRingBuffer(work, pool, false /* not EOS */);
+    }
+}
+
+c2_status_t C2SoftAacDec::drainInternal(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work) {
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    bool eos = (drainMode == DRAIN_COMPONENT_WITH_EOS);
+
+    drainDecoder();
+    drainRingBuffer(work, pool, eos);
+
+    if (eos) {
+        auto fillEmptyWork = [](const std::unique_ptr<C2Work> &work) {
+            work->worklets.front()->output.flags = work->input.flags;
+            work->worklets.front()->output.buffers.clear();
+            work->worklets.front()->output.ordinal = work->input.ordinal;
+            work->workletsProcessed = 1u;
+        };
+        while (mBuffersInfo.size() > 1u) {
+            finish(mBuffersInfo.front().frameIndex, fillEmptyWork);
+            mBuffersInfo.pop_front();
+        }
+        if (work && work->workletsProcessed == 0u) {
+            fillEmptyWork(work);
+        }
+        mBuffersInfo.clear();
+    }
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftAacDec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+c2_status_t C2SoftAacDec::onFlush_sm() {
+    drainDecoder();
+    mBuffersInfo.clear();
+
+    int avail;
+    while ((avail = outputDelayRingBufferSamplesAvailable()) > 0) {
+        if (avail > mStreamInfo->frameSize * mStreamInfo->numChannels) {
+            avail = mStreamInfo->frameSize * mStreamInfo->numChannels;
+        }
+        int32_t ns = outputDelayRingBufferGetSamples(nullptr, avail);
+        if (ns != avail) {
+            ALOGW("not a complete frame of samples available");
+            break;
+        }
+    }
+    mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos;
+
+    return C2_OK;
+}
+
+void C2SoftAacDec::drainDecoder() {
+    // flush decoder until outputDelay is compensated
+    while (mOutputDelayCompensated > 0) {
+        // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC
+        INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+
+        // run DRC check
+        mDrcWrap.submitStreamData(mStreamInfo);
+        mDrcWrap.update();
+
+        AAC_DECODER_ERROR decoderErr =
+            aacDecoder_DecodeFrame(mAACDecoder,
+                                   tmpOutBuffer,
+                                   2048 * MAX_CHANNEL_COUNT,
+                                   AACDEC_FLUSH);
+        if (decoderErr != AAC_DEC_OK) {
+            ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+        }
+
+        int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
+        if (tmpOutBufferSamples > mOutputDelayCompensated) {
+            tmpOutBufferSamples = mOutputDelayCompensated;
+        }
+        outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);
+
+        mOutputDelayCompensated -= tmpOutBufferSamples;
+    }
+}
+
+class C2SoftAacDecFactory : public C2ComponentFactory {
+public:
+    C2SoftAacDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftAacDec(COMPONENT_NAME,
+                              id,
+                              std::make_shared<C2SoftAacDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftAacDec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftAacDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftAacDecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftAacDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/aac/C2SoftAacDec.h b/media/codec2/components/aac/C2SoftAacDec.h
new file mode 100644
index 0000000..965c29e
--- /dev/null
+++ b/media/codec2/components/aac/C2SoftAacDec.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_C2_SOFT_AAC_DEC_H_
+#define ANDROID_C2_SOFT_AAC_DEC_H_
+
+#include <SimpleC2Component.h>
+
+
+#include "aacdecoder_lib.h"
+#include "DrcPresModeWrap.h"
+
+namespace android {
+
+struct C2SoftAacDec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftAacDec(const char *name, c2_node_id_t id, const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftAacDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    enum {
+        kNumDelayBlocksMax      = 8,
+    };
+
+    std::shared_ptr<IntfImpl> mIntf;
+
+    HANDLE_AACDECODER mAACDecoder;
+    CStreamInfo *mStreamInfo;
+    bool mIsFirst;
+    size_t mInputBufferCount;
+    size_t mOutputBufferCount;
+    bool mSignalledError;
+    struct Info {
+        uint64_t frameIndex;
+        size_t bufferSize;
+        uint64_t timestamp;
+        std::vector<int32_t> decodedSizes;
+    };
+    std::list<Info> mBuffersInfo;
+
+    CDrcPresModeWrapper mDrcWrap;
+
+    enum {
+        NONE,
+        AWAITING_DISABLED,
+        AWAITING_ENABLED
+    } mOutputPortSettingsChange;
+
+    void initPorts();
+    status_t initDecoder();
+    bool isConfigured() const;
+    void drainDecoder();
+
+    void drainRingBuffer(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool,
+            bool eos);
+    c2_status_t drainInternal(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool,
+            const std::unique_ptr<C2Work> &work);
+
+//      delay compensation
+    bool mEndOfInput;
+    bool mEndOfOutput;
+    int32_t mOutputDelayCompensated;
+    int32_t mOutputDelayRingBufferSize;
+    short *mOutputDelayRingBuffer;
+    int32_t mOutputDelayRingBufferWritePos;
+    int32_t mOutputDelayRingBufferReadPos;
+    int32_t mOutputDelayRingBufferFilled;
+    bool outputDelayRingBufferPutSamples(INT_PCM *samples, int numSamples);
+    int32_t outputDelayRingBufferGetSamples(INT_PCM *samples, int numSamples);
+    int32_t outputDelayRingBufferSamplesAvailable();
+    int32_t outputDelayRingBufferSpaceLeft();
+
+    C2_DO_NOT_COPY(C2SoftAacDec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_AAC_DEC_H_
diff --git a/media/codec2/components/aac/C2SoftAacEnc.cpp b/media/codec2/components/aac/C2SoftAacEnc.cpp
new file mode 100644
index 0000000..8e3852c
--- /dev/null
+++ b/media/codec2/components/aac/C2SoftAacEnc.cpp
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftAacEnc"
+#include <utils/Log.h>
+
+#include <inttypes.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+#include "C2SoftAacEnc.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.aac.encoder";
+
+}  // namespace
+
+class C2SoftAacEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_AAC) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::input(0u, 44100))
+                .withFields({C2F(mSampleRate, value).oneOf({
+                    8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+                })})
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::input(0u, 1))
+                .withFields({C2F(mChannelCount, value).inRange(1, 6)})
+                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::output(0u, 64000))
+                .withFields({C2F(mBitrate, value).inRange(8000, 960000)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 8192))
+                .calculatedAs(MaxBufSizeCalculator, mChannelCount)
+                .build());
+
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::output(0u,
+                        C2Config::PROFILE_AAC_LC, C2Config::LEVEL_UNUSED))
+                .withFields({
+                    C2F(mProfileLevel, profile).oneOf({
+                            C2Config::PROFILE_AAC_LC,
+                            C2Config::PROFILE_AAC_HE,
+                            C2Config::PROFILE_AAC_HE_PS,
+                            C2Config::PROFILE_AAC_LD,
+                            C2Config::PROFILE_AAC_ELD}),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_UNUSED
+                    })
+                })
+                .withSetter(ProfileLevelSetter)
+                .build());
+
+       addParameter(
+                DefineParam(mSBRMode, C2_PARAMKEY_AAC_SBR_MODE)
+                .withDefault(new C2StreamAacSbrModeTuning::input(0u, AAC_SBR_AUTO))
+                .withFields({C2F(mSBRMode, value).oneOf({
+                            C2Config::AAC_SBR_OFF,
+                            C2Config::AAC_SBR_SINGLE_RATE,
+                            C2Config::AAC_SBR_DUAL_RATE,
+                            C2Config::AAC_SBR_AUTO })})
+                .withSetter(Setter<decltype(*mSBRMode)>::NonStrictValueWithNoDeps)
+                .build());
+    }
+
+    uint32_t getSampleRate() const { return mSampleRate->value; }
+    uint32_t getChannelCount() const { return mChannelCount->value; }
+    uint32_t getBitrate() const { return mBitrate->value; }
+    uint32_t getSBRMode() const { return mSBRMode->value; }
+    uint32_t getProfile() const { return mProfileLevel->profile; }
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::output> &me) {
+        (void)mayBlock;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+    static C2R MaxBufSizeCalculator(
+            bool mayBlock,
+            C2P<C2StreamMaxBufferSizeInfo::input> &me,
+            const C2P<C2StreamChannelCountInfo::input> &channelCount) {
+        (void)mayBlock;
+        me.set().value = 1024 * sizeof(short) * channelCount.v.value;
+        return C2R::Ok();
+    }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+    std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
+    std::shared_ptr<C2StreamAacSbrModeTuning::input> mSBRMode;
+};
+
+C2SoftAacEnc::C2SoftAacEnc(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mAACEncoder(nullptr),
+      mNumBytesPerInputFrame(0u),
+      mOutBufferSize(0u),
+      mSentCodecSpecificData(false),
+      mInputTimeSet(false),
+      mInputSize(0),
+      mInputTimeUs(0),
+      mSignalledError(false),
+      mOutIndex(0u) {
+}
+
+C2SoftAacEnc::~C2SoftAacEnc() {
+    onReset();
+}
+
+c2_status_t C2SoftAacEnc::onInit() {
+    status_t err = initEncoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+status_t C2SoftAacEnc::initEncoder() {
+    if (AACENC_OK != aacEncOpen(&mAACEncoder, 0, 0)) {
+        ALOGE("Failed to init AAC encoder");
+        return UNKNOWN_ERROR;
+    }
+    return setAudioParams();
+}
+
+c2_status_t C2SoftAacEnc::onStop() {
+    mSentCodecSpecificData = false;
+    mInputTimeSet = false;
+    mInputSize = 0u;
+    mInputTimeUs = 0;
+    mSignalledError = false;
+    return C2_OK;
+}
+
+void C2SoftAacEnc::onReset() {
+    (void)onStop();
+    aacEncClose(&mAACEncoder);
+}
+
+void C2SoftAacEnc::onRelease() {
+    // no-op
+}
+
+c2_status_t C2SoftAacEnc::onFlush_sm() {
+    mSentCodecSpecificData = false;
+    mInputTimeSet = false;
+    mInputSize = 0u;
+    mInputTimeUs = 0;
+    return C2_OK;
+}
+
+static CHANNEL_MODE getChannelMode(uint32_t nChannels) {
+    CHANNEL_MODE chMode = MODE_INVALID;
+    switch (nChannels) {
+        case 1: chMode = MODE_1; break;
+        case 2: chMode = MODE_2; break;
+        case 3: chMode = MODE_1_2; break;
+        case 4: chMode = MODE_1_2_1; break;
+        case 5: chMode = MODE_1_2_2; break;
+        case 6: chMode = MODE_1_2_2_1; break;
+        default: chMode = MODE_INVALID;
+    }
+    return chMode;
+}
+
+static AUDIO_OBJECT_TYPE getAOTFromProfile(uint32_t profile) {
+   if (profile == C2Config::PROFILE_AAC_LC) {
+       return AOT_AAC_LC;
+   } else if (profile == C2Config::PROFILE_AAC_HE) {
+       return AOT_SBR;
+   } else if (profile == C2Config::PROFILE_AAC_HE_PS) {
+       return AOT_PS;
+   } else if (profile == C2Config::PROFILE_AAC_LD) {
+       return AOT_ER_AAC_LD;
+   } else if (profile == C2Config::PROFILE_AAC_ELD) {
+       return AOT_ER_AAC_ELD;
+   } else {
+       ALOGW("Unsupported AAC profile - defaulting to AAC-LC");
+       return AOT_AAC_LC;
+   }
+}
+
+status_t C2SoftAacEnc::setAudioParams() {
+    // We call this whenever sample rate, number of channels, bitrate or SBR mode change
+    // in reponse to setParameter calls.
+    int32_t sbrRatio = 0;
+    uint32_t sbrMode = mIntf->getSBRMode();
+    if (sbrMode == AAC_SBR_SINGLE_RATE) sbrRatio = 1;
+    else if (sbrMode == AAC_SBR_DUAL_RATE) sbrRatio = 2;
+
+    ALOGV("setAudioParams: %u Hz, %u channels, %u bps, %i sbr mode, %i sbr ratio",
+         mIntf->getSampleRate(), mIntf->getChannelCount(), mIntf->getBitrate(),
+         sbrMode, sbrRatio);
+
+    uint32_t aacProfile = mIntf->getProfile();
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_AOT, getAOTFromProfile(aacProfile))) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SAMPLERATE, mIntf->getSampleRate())) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_BITRATE, mIntf->getBitrate())) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_CHANNELMODE,
+            getChannelMode(mIntf->getChannelCount()))) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_TRANSMUX, TT_MP4_RAW)) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+
+    if (sbrMode != -1 && aacProfile == C2Config::PROFILE_AAC_ELD) {
+        if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_MODE, sbrMode)) {
+            ALOGE("Failed to set AAC encoder parameters");
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    /* SBR ratio parameter configurations:
+       0: Default configuration wherein SBR ratio is configured depending on audio object type by
+          the FDK.
+       1: Downsampled SBR (default for ELD)
+       2: Dualrate SBR (default for HE-AAC)
+     */
+    if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_RATIO, sbrRatio)) {
+        ALOGE("Failed to set AAC encoder parameters");
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+void C2SoftAacEnc::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError) {
+        return;
+    }
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+
+    uint32_t sampleRate = mIntf->getSampleRate();
+    uint32_t channelCount = mIntf->getChannelCount();
+
+    if (!mSentCodecSpecificData) {
+        // The very first thing we want to output is the codec specific
+        // data.
+
+        if (AACENC_OK != aacEncEncode(mAACEncoder, nullptr, nullptr, nullptr, nullptr)) {
+            ALOGE("Unable to initialize encoder for profile / sample-rate / bit-rate / channels");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        uint32_t bitrate = mIntf->getBitrate();
+        uint32_t actualBitRate = aacEncoder_GetParam(mAACEncoder, AACENC_BITRATE);
+        if (bitrate != actualBitRate) {
+            ALOGW("Requested bitrate %u unsupported, using %u", bitrate, actualBitRate);
+        }
+
+        AACENC_InfoStruct encInfo;
+        if (AACENC_OK != aacEncInfo(mAACEncoder, &encInfo)) {
+            ALOGE("Failed to get AAC encoder info");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        std::unique_ptr<C2StreamInitDataInfo::output> csd =
+            C2StreamInitDataInfo::output::AllocUnique(encInfo.confSize, 0u);
+        if (!csd) {
+            ALOGE("CSD allocation failed");
+            mSignalledError = true;
+            work->result = C2_NO_MEMORY;
+            return;
+        }
+        memcpy(csd->m.value, encInfo.confBuf, encInfo.confSize);
+        ALOGV("put csd");
+#if defined(LOG_NDEBUG) && !LOG_NDEBUG
+        hexdump(csd->m.value, csd->flexCount());
+#endif
+        work->worklets.front()->output.configUpdate.push_back(std::move(csd));
+
+        mOutBufferSize = encInfo.maxOutBufBytes;
+        mNumBytesPerInputFrame = encInfo.frameLength * channelCount * sizeof(int16_t);
+
+        mSentCodecSpecificData = true;
+    }
+
+    uint8_t temp[1];
+    C2ReadView view = mDummyReadView;
+    const uint8_t *data = temp;
+    size_t capacity = 0u;
+    if (!work->input.buffers.empty()) {
+        view = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        data = view.data();
+        capacity = view.capacity();
+    }
+    if (!mInputTimeSet && capacity > 0) {
+        mInputTimeUs = work->input.ordinal.timestamp;
+        mInputTimeSet = true;
+    }
+
+    size_t numFrames = (capacity + mInputSize + (eos ? mNumBytesPerInputFrame - 1 : 0))
+            / mNumBytesPerInputFrame;
+    ALOGV("capacity = %zu; mInputSize = %zu; numFrames = %zu mNumBytesPerInputFrame = %u",
+          capacity, mInputSize, numFrames, mNumBytesPerInputFrame);
+
+    std::shared_ptr<C2LinearBlock> block;
+    std::shared_ptr<C2Buffer> buffer;
+    std::unique_ptr<C2WriteView> wView;
+    uint8_t *outPtr = temp;
+    size_t outAvailable = 0u;
+    uint64_t inputIndex = work->input.ordinal.frameIndex.peeku();
+
+    AACENC_InArgs inargs;
+    AACENC_OutArgs outargs;
+    memset(&inargs, 0, sizeof(inargs));
+    memset(&outargs, 0, sizeof(outargs));
+    inargs.numInSamples = capacity / sizeof(int16_t);
+
+    void* inBuffer[]        = { (unsigned char *)data };
+    INT   inBufferIds[]     = { IN_AUDIO_DATA };
+    INT   inBufferSize[]    = { (INT)capacity };
+    INT   inBufferElSize[]  = { sizeof(int16_t) };
+
+    AACENC_BufDesc inBufDesc;
+    inBufDesc.numBufs           = sizeof(inBuffer) / sizeof(void*);
+    inBufDesc.bufs              = (void**)&inBuffer;
+    inBufDesc.bufferIdentifiers = inBufferIds;
+    inBufDesc.bufSizes          = inBufferSize;
+    inBufDesc.bufElSizes        = inBufferElSize;
+
+    void* outBuffer[]       = { outPtr };
+    INT   outBufferIds[]    = { OUT_BITSTREAM_DATA };
+    INT   outBufferSize[]   = { 0 };
+    INT   outBufferElSize[] = { sizeof(UCHAR) };
+
+    AACENC_BufDesc outBufDesc;
+    outBufDesc.numBufs           = sizeof(outBuffer) / sizeof(void*);
+    outBufDesc.bufs              = (void**)&outBuffer;
+    outBufDesc.bufferIdentifiers = outBufferIds;
+    outBufDesc.bufSizes          = outBufferSize;
+    outBufDesc.bufElSizes        = outBufferElSize;
+
+    AACENC_ERROR encoderErr = AACENC_OK;
+
+    class FillWork {
+    public:
+        FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+                 const std::shared_ptr<C2Buffer> &buffer)
+            : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {
+        }
+        ~FillWork() = default;
+
+        void operator()(const std::unique_ptr<C2Work> &work) {
+            work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+            work->worklets.front()->output.buffers.clear();
+            work->worklets.front()->output.ordinal = mOrdinal;
+            work->workletsProcessed = 1u;
+            work->result = C2_OK;
+            if (mBuffer) {
+                work->worklets.front()->output.buffers.push_back(mBuffer);
+            }
+            ALOGV("timestamp = %lld, index = %lld, w/%s buffer",
+                  mOrdinal.timestamp.peekll(),
+                  mOrdinal.frameIndex.peekll(),
+                  mBuffer ? "" : "o");
+        }
+
+    private:
+        const uint32_t mFlags;
+        const C2WorkOrdinalStruct mOrdinal;
+        const std::shared_ptr<C2Buffer> mBuffer;
+    };
+
+    C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+
+    while (encoderErr == AACENC_OK && inargs.numInSamples > 0) {
+        if (numFrames && !block) {
+            C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+            // TODO: error handling, proper usage, etc.
+            c2_status_t err = pool->fetchLinearBlock(mOutBufferSize, usage, &block);
+            if (err != C2_OK) {
+                ALOGE("fetchLinearBlock failed : err = %d", err);
+                work->result = C2_NO_MEMORY;
+                return;
+            }
+
+            wView.reset(new C2WriteView(block->map().get()));
+            outPtr = wView->data();
+            outAvailable = wView->size();
+            --numFrames;
+        }
+
+        memset(&outargs, 0, sizeof(outargs));
+
+        outBuffer[0] = outPtr;
+        outBufferSize[0] = outAvailable;
+
+        encoderErr = aacEncEncode(mAACEncoder,
+                                  &inBufDesc,
+                                  &outBufDesc,
+                                  &inargs,
+                                  &outargs);
+
+        if (encoderErr == AACENC_OK) {
+            if (buffer) {
+                outOrdinal.frameIndex = mOutIndex++;
+                outOrdinal.timestamp = mInputTimeUs;
+                cloneAndSend(
+                        inputIndex,
+                        work,
+                        FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+                buffer.reset();
+            }
+
+            if (outargs.numOutBytes > 0) {
+                mInputSize = 0;
+                int consumed = (capacity / sizeof(int16_t)) - inargs.numInSamples
+                        + outargs.numInSamples;
+                mInputTimeUs = work->input.ordinal.timestamp
+                        + (consumed * 1000000ll / channelCount / sampleRate);
+                buffer = createLinearBuffer(block, 0, outargs.numOutBytes);
+#if defined(LOG_NDEBUG) && !LOG_NDEBUG
+                hexdump(outPtr, std::min(outargs.numOutBytes, 256));
+#endif
+                outPtr = temp;
+                outAvailable = 0;
+                block.reset();
+            } else {
+                mInputSize += outargs.numInSamples * sizeof(int16_t);
+            }
+
+            if (outargs.numInSamples > 0) {
+                inBuffer[0] = (int16_t *)inBuffer[0] + outargs.numInSamples;
+                inBufferSize[0] -= outargs.numInSamples * sizeof(int16_t);
+                inargs.numInSamples -= outargs.numInSamples;
+            }
+        }
+        ALOGV("encoderErr = %d mInputSize = %zu inargs.numInSamples = %d, mInputTimeUs = %lld",
+              encoderErr, mInputSize, inargs.numInSamples, mInputTimeUs.peekll());
+    }
+
+    if (eos && inBufferSize[0] > 0) {
+        if (numFrames && !block) {
+            C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+            // TODO: error handling, proper usage, etc.
+            c2_status_t err = pool->fetchLinearBlock(mOutBufferSize, usage, &block);
+            if (err != C2_OK) {
+                ALOGE("fetchLinearBlock failed : err = %d", err);
+                work->result = C2_NO_MEMORY;
+                return;
+            }
+
+            wView.reset(new C2WriteView(block->map().get()));
+            outPtr = wView->data();
+            outAvailable = wView->size();
+            --numFrames;
+        }
+
+        memset(&outargs, 0, sizeof(outargs));
+
+        outBuffer[0] = outPtr;
+        outBufferSize[0] = outAvailable;
+
+        // Flush
+        inargs.numInSamples = -1;
+
+        (void)aacEncEncode(mAACEncoder,
+                           &inBufDesc,
+                           &outBufDesc,
+                           &inargs,
+                           &outargs);
+    }
+
+    outOrdinal.frameIndex = mOutIndex++;
+    outOrdinal.timestamp = mInputTimeUs;
+    FillWork((C2FrameData::flags_t)(eos ? C2FrameData::FLAG_END_OF_STREAM : 0),
+             outOrdinal, buffer)(work);
+}
+
+c2_status_t C2SoftAacEnc::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    switch (drainMode) {
+        case DRAIN_COMPONENT_NO_EOS:
+            [[fallthrough]];
+        case NO_DRAIN:
+            // no-op
+            return C2_OK;
+        case DRAIN_CHAIN:
+            return C2_OMITTED;
+        case DRAIN_COMPONENT_WITH_EOS:
+            break;
+        default:
+            return C2_BAD_VALUE;
+    }
+
+    (void)pool;
+    mSentCodecSpecificData = false;
+    mInputTimeSet = false;
+    mInputSize = 0u;
+    mInputTimeUs = 0;
+
+    // TODO: we don't have any pending work at this time to drain.
+    return C2_OK;
+}
+
+class C2SoftAacEncFactory : public C2ComponentFactory {
+public:
+    C2SoftAacEncFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftAacEnc(COMPONENT_NAME,
+                                 id,
+                                 std::make_shared<C2SoftAacEnc::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftAacEnc::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftAacEnc::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftAacEncFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftAacEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/aac/C2SoftAacEnc.h b/media/codec2/components/aac/C2SoftAacEnc.h
new file mode 100644
index 0000000..a38be19
--- /dev/null
+++ b/media/codec2/components/aac/C2SoftAacEnc.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_C2_SOFT_AAC_ENC_H_
+#define ANDROID_C2_SOFT_AAC_ENC_H_
+
+#include <atomic>
+
+#include <SimpleC2Component.h>
+
+#include "aacenc_lib.h"
+
+namespace android {
+
+class C2SoftAacEnc : public SimpleC2Component {
+public:
+    class IntfImpl;
+
+    C2SoftAacEnc(const char *name, c2_node_id_t id, const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftAacEnc();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    std::shared_ptr<IntfImpl> mIntf;
+
+    HANDLE_AACENCODER mAACEncoder;
+
+    UINT mNumBytesPerInputFrame;
+    UINT mOutBufferSize;
+
+    bool mSentCodecSpecificData;
+    bool mInputTimeSet;
+    size_t mInputSize;
+    c2_cntr64_t mInputTimeUs;
+
+    bool mSignalledError;
+    std::atomic_uint64_t mOutIndex;
+
+    status_t initEncoder();
+
+    status_t setAudioParams();
+
+    C2_DO_NOT_COPY(C2SoftAacEnc);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_AAC_ENC_H_
diff --git a/media/codec2/components/aac/DrcPresModeWrap.cpp b/media/codec2/components/aac/DrcPresModeWrap.cpp
new file mode 100644
index 0000000..5b9aebc
--- /dev/null
+++ b/media/codec2/components/aac/DrcPresModeWrap.cpp
@@ -0,0 +1,372 @@
+/*
+ * 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.
+ */
+#include "DrcPresModeWrap.h"
+
+#include <assert.h>
+
+#define LOG_TAG "C2SoftAacDrcWrapper"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+//#define DRC_PRES_MODE_WRAP_DEBUG
+
+#define GPM_ENCODER_TARGET_LEVEL 64
+#define MAX_TARGET_LEVEL 40
+
+CDrcPresModeWrapper::CDrcPresModeWrapper()
+{
+    mDataUpdate = true;
+
+    /* Data from streamInfo. */
+    /* Initialized to the same values as in the aac decoder */
+    mStreamPRL = -1;
+    mStreamDRCPresMode = -1;
+    mStreamNrAACChan = 0;
+    mStreamNrOutChan = 0;
+
+    /* Desired values (set by user). */
+    /* Initialized to the same values as in the aac decoder */
+    mDesTarget = -1;
+    mDesAttFactor = 0;
+    mDesBoostFactor = 0;
+    mDesHeavy = 0;
+
+    mEncoderTarget = -1;
+
+    /* Values from last time. */
+    /* Initialized to the same values as the desired values */
+    mLastTarget = -1;
+    mLastAttFactor = 0;
+    mLastBoostFactor = 0;
+    mLastHeavy = 0;
+}
+
+CDrcPresModeWrapper::~CDrcPresModeWrapper()
+{
+}
+
+void
+CDrcPresModeWrapper::setDecoderHandle(const HANDLE_AACDECODER handle)
+{
+    mHandleDecoder = handle;
+}
+
+void
+CDrcPresModeWrapper::submitStreamData(CStreamInfo* pStreamInfo)
+{
+    assert(pStreamInfo);
+
+    if (mStreamPRL != pStreamInfo->drcProgRefLev) {
+        mStreamPRL = pStreamInfo->drcProgRefLev;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: drcProgRefLev is %d\n", mStreamPRL);
+#endif
+    }
+
+    if (mStreamDRCPresMode != pStreamInfo->drcPresMode) {
+        mStreamDRCPresMode = pStreamInfo->drcPresMode;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: drcPresMode is %d\n", mStreamDRCPresMode);
+#endif
+    }
+
+    if (mStreamNrAACChan != pStreamInfo->aacNumChannels) {
+        mStreamNrAACChan = pStreamInfo->aacNumChannels;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: aacNumChannels is %d\n", mStreamNrAACChan);
+#endif
+    }
+
+    if (mStreamNrOutChan != pStreamInfo->numChannels) {
+        mStreamNrOutChan = pStreamInfo->numChannels;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: numChannels is %d\n", mStreamNrOutChan);
+#endif
+    }
+
+
+
+    if (mStreamNrOutChan<mStreamNrAACChan) {
+        mIsDownmix = true;
+    } else {
+        mIsDownmix = false;
+    }
+
+    if (mIsDownmix && (mStreamNrOutChan == 1)) {
+        mIsMonoDownmix = true;
+    } else {
+        mIsMonoDownmix = false;
+    }
+
+    if (mIsDownmix && mStreamNrOutChan == 2){
+        mIsStereoDownmix = true;
+    } else {
+        mIsStereoDownmix = false;
+    }
+
+}
+
+void
+CDrcPresModeWrapper::setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value)
+{
+    switch (param) {
+    case DRC_PRES_MODE_WRAP_DESIRED_TARGET:
+        mDesTarget = value;
+        break;
+    case DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR:
+        mDesAttFactor = value;
+        break;
+    case DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR:
+        mDesBoostFactor = value;
+        break;
+    case DRC_PRES_MODE_WRAP_DESIRED_HEAVY:
+        mDesHeavy = value;
+        break;
+    case DRC_PRES_MODE_WRAP_ENCODER_TARGET:
+        mEncoderTarget = value;
+        break;
+    default:
+        break;
+    }
+    mDataUpdate = true;
+}
+
+void
+CDrcPresModeWrapper::update()
+{
+    // Get Data from Decoder
+    int progRefLevel = mStreamPRL;
+    int drcPresMode = mStreamDRCPresMode;
+
+    // by default, do as desired
+    int newTarget         = mDesTarget;
+    int newAttFactor      = mDesAttFactor;
+    int newBoostFactor    = mDesBoostFactor;
+    int newHeavy          = mDesHeavy;
+
+    if (mDataUpdate) {
+        // sanity check
+        if (mDesTarget < MAX_TARGET_LEVEL){
+            mDesTarget = MAX_TARGET_LEVEL;  // limit target level to -10 dB or below
+            newTarget = MAX_TARGET_LEVEL;
+        }
+
+        if (mEncoderTarget != -1) {
+            if (mDesTarget<124) { // if target level > -31 dB
+                if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) {
+                    // no stereo or mono downmixing, calculated scaling of light DRC
+                    /* use as little compression as possible */
+                    newAttFactor = 0;
+                    newBoostFactor = 0;
+                    if (mDesTarget<progRefLevel) { // if target level > PRL
+                        if (mEncoderTarget < mDesTarget) { // if mEncoderTarget > target level
+                            // mEncoderTarget > target level > PRL
+                            int calcFactor;
+                            float calcFactor_norm;
+                            // 0.0f < calcFactor_norm < 1.0f
+                            calcFactor_norm = (float)(mDesTarget - progRefLevel) /
+                                    (float)(mEncoderTarget - progRefLevel);
+                            calcFactor = (int)(calcFactor_norm*127.0f); // 0 <= calcFactor < 127
+                            // calcFactor is the lower limit
+                            newAttFactor = (calcFactor>newAttFactor) ? calcFactor : newAttFactor;
+                            // new AttFactor will be always = calcFactor, as it is set to 0 before.
+                            newBoostFactor = newAttFactor;
+                        } else {
+                            /* target level > mEncoderTarget > PRL */
+                            // newTDLimiterEnable = 1;
+                            // the time domain limiter must always be active in this case.
+                            //     It is assumed that the framework activates it by default
+                            newAttFactor = 127;
+                            newBoostFactor = 127;
+                        }
+                    } else { // target level <= PRL
+                        // no restrictions required
+                        // newAttFactor = newAttFactor;
+                    }
+                } else { // downmixing
+                    // if target level > -23 dB or mono downmix
+                    if ( (mDesTarget<92) || mIsMonoDownmix ) {
+                        newHeavy = 1;
+                    } else {
+                        // we perform a downmix, so, we need at least full light DRC
+                        newAttFactor = 127;
+                    }
+                }
+            } else { // target level <= -31 dB
+                // playback -31 dB: light DRC only needed if we perform downmixing
+                if (mIsDownmix) {   // we do downmixing
+                    newAttFactor = 127;
+                }
+            }
+        }
+        else { // handle other used encoder target levels
+
+            // Sanity check: DRC presentation mode is only specified for max. 5.1 channels
+            if (mStreamNrAACChan > 6) {
+                drcPresMode = 0;
+            }
+
+            switch (drcPresMode) {
+            case 0:
+            default: // presentation mode not indicated
+            {
+
+                if (mDesTarget<124) { // if target level > -31 dB
+                    // no stereo or mono downmixing
+                    if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) {
+                        if (mDesTarget<progRefLevel) { // if target level > PRL
+                            // newTDLimiterEnable = 1;
+                            // the time domain limiter must always be active in this case.
+                            //    It is assumed that the framework activates it by default
+                            newAttFactor = 127; // at least, use light compression
+                        } else { // target level <= PRL
+                            // no restrictions required
+                            // newAttFactor = newAttFactor;
+                        }
+                    } else { // downmixing
+                        // newTDLimiterEnable = 1;
+                        // the time domain limiter must always be active in this case.
+                        //    It is assumed that the framework activates it by default
+
+                        // if target level > -23 dB or mono downmix
+                        if ( (mDesTarget < 92) || mIsMonoDownmix ) {
+                            newHeavy = 1;
+                        } else{
+                            // we perform a downmix, so, we need at least full light DRC
+                            newAttFactor = 127;
+                        }
+                    }
+                } else { // target level <= -31 dB
+                    if (mIsDownmix) {   // we do downmixing.
+                        // newTDLimiterEnable = 1;
+                        // the time domain limiter must always be active in this case.
+                        //    It is assumed that the framework activates it by default
+                        newAttFactor = 127;
+                    }
+                }
+            }
+            break;
+
+            // Presentation mode 1 and 2 according to ETSI TS 101 154:
+            // Digital Video Broadcasting (DVB); Specification for the use of Video and Audio Coding
+            // in Broadcasting Applications based on the MPEG-2 Transport Stream,
+            // section C.5.4., "Decoding", and Table C.33
+            // ISO DRC            -> newHeavy = 0  (Use light compression, MPEG-style)
+            // Compression_value  -> newHeavy = 1  (Use heavy compression, DVB-style)
+            // scaling restricted -> newAttFactor = 127
+
+            case 1: // presentation mode 1, Light:-31/Heavy:-23
+            {
+                if (mDesTarget < 124) { // if target level > -31 dB
+                    // playback up to -23 dB
+                    newHeavy = 1;
+                } else { // target level <= -31 dB
+                    // playback -31 dB
+                    if (mIsDownmix) {   // we do downmixing.
+                        newAttFactor = 127;
+                    }
+                }
+            }
+            break;
+
+            case 2: // presentation mode 2, Light:-23/Heavy:-23
+            {
+                if (mDesTarget < 124) { // if target level > -31 dB
+                    // playback up to -23 dB
+                    if (mIsMonoDownmix) { // if mono downmix
+                        newHeavy = 1;
+                    } else {
+                        newHeavy = 0;
+                        newAttFactor = 127;
+                    }
+                } else { // target level <= -31 dB
+                    // playback -31 dB
+                    newHeavy = 0;
+                    if (mIsDownmix) {   // we do downmixing.
+                        newAttFactor = 127;
+                    }
+                }
+            }
+            break;
+
+            } // switch()
+        } // if (mEncoderTarget  == GPM_ENCODER_TARGET_LEVEL)
+
+        // sanity again
+        if (newHeavy == 1) {
+            newBoostFactor=127; // not really needed as the same would be done by the decoder anyway
+            newAttFactor = 127;
+        }
+
+        // update the decoder
+        if (newTarget != mLastTarget) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_REFERENCE_LEVEL, newTarget);
+            mLastTarget = newTarget;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newTarget != mDesTarget)
+                ALOGV("DRC presentation mode wrapper: forced target level to %d (from %d)\n", newTarget, mDesTarget);
+            else
+                ALOGV("DRC presentation mode wrapper: set target level to %d\n", newTarget);
+#endif
+        }
+
+        if (newAttFactor != mLastAttFactor) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_ATTENUATION_FACTOR, newAttFactor);
+            mLastAttFactor = newAttFactor;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newAttFactor != mDesAttFactor)
+                ALOGV("DRC presentation mode wrapper: forced attenuation factor to %d (from %d)\n", newAttFactor, mDesAttFactor);
+            else
+                ALOGV("DRC presentation mode wrapper: set attenuation factor to %d\n", newAttFactor);
+#endif
+        }
+
+        if (newBoostFactor != mLastBoostFactor) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_BOOST_FACTOR, newBoostFactor);
+            mLastBoostFactor = newBoostFactor;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newBoostFactor != mDesBoostFactor)
+                ALOGV("DRC presentation mode wrapper: forced boost factor to %d (from %d)\n",
+                        newBoostFactor, mDesBoostFactor);
+            else
+                ALOGV("DRC presentation mode wrapper: set boost factor to %d\n", newBoostFactor);
+#endif
+        }
+
+        if (newHeavy != mLastHeavy) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_HEAVY_COMPRESSION, newHeavy);
+            mLastHeavy = newHeavy;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newHeavy != mDesHeavy)
+                ALOGV("DRC presentation mode wrapper: forced heavy compression to %d (from %d)\n",
+                        newHeavy, mDesHeavy);
+            else
+                ALOGV("DRC presentation mode wrapper: set heavy compression to %d\n", newHeavy);
+#endif
+        }
+
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC config: tgt_lev: %3d, cut: %3d, boost: %3d, heavy: %d\n", newTarget,
+                newAttFactor, newBoostFactor, newHeavy);
+#endif
+        mDataUpdate = false;
+
+    } // if (mDataUpdate)
+}
diff --git a/media/codec2/components/aac/DrcPresModeWrap.h b/media/codec2/components/aac/DrcPresModeWrap.h
new file mode 100644
index 0000000..f0b6cf2
--- /dev/null
+++ b/media/codec2/components/aac/DrcPresModeWrap.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+#pragma once
+#include "aacdecoder_lib.h"
+
+typedef enum
+{
+    DRC_PRES_MODE_WRAP_DESIRED_TARGET         = 0x0000,
+    DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR     = 0x0001,
+    DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR   = 0x0002,
+    DRC_PRES_MODE_WRAP_DESIRED_HEAVY          = 0x0003,
+    DRC_PRES_MODE_WRAP_ENCODER_TARGET         = 0x0004
+} DRC_PRES_MODE_WRAP_PARAM;
+
+
+class CDrcPresModeWrapper {
+public:
+    CDrcPresModeWrapper();
+    ~CDrcPresModeWrapper();
+    void setDecoderHandle(const HANDLE_AACDECODER handle);
+    void setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value);
+    void submitStreamData(CStreamInfo*);
+    void update();
+
+protected:
+    HANDLE_AACDECODER mHandleDecoder;
+    int mDesTarget;
+    int mDesAttFactor;
+    int mDesBoostFactor;
+    int mDesHeavy;
+
+    int mEncoderTarget;
+
+    int mLastTarget;
+    int mLastAttFactor;
+    int mLastBoostFactor;
+    int mLastHeavy;
+
+    SCHAR mStreamPRL;
+    SCHAR mStreamDRCPresMode;
+    INT mStreamNrAACChan;
+    INT mStreamNrOutChan;
+
+    bool mIsDownmix;
+    bool mIsMonoDownmix;
+    bool mIsStereoDownmix;
+
+    bool mDataUpdate;
+};
diff --git a/media/common_time/MODULE_LICENSE_APACHE2 b/media/codec2/components/aac/MODULE_LICENSE_APACHE2
similarity index 100%
rename from media/common_time/MODULE_LICENSE_APACHE2
rename to media/codec2/components/aac/MODULE_LICENSE_APACHE2
diff --git a/media/common_time/NOTICE b/media/codec2/components/aac/NOTICE
similarity index 100%
rename from media/common_time/NOTICE
rename to media/codec2/components/aac/NOTICE
diff --git a/media/codec2/components/aac/patent_disclaimer.txt b/media/codec2/components/aac/patent_disclaimer.txt
new file mode 100644
index 0000000..b4bf11d
--- /dev/null
+++ b/media/codec2/components/aac/patent_disclaimer.txt
@@ -0,0 +1,9 @@
+
+THIS IS NOT A GRANT OF PATENT RIGHTS.
+
+Google makes no representation or warranty that the codecs for which
+source code is made available hereunder are unencumbered by
+third-party patents.  Those intending to use this source code in
+hardware or software products are advised that implementations of
+these codecs, including in open source software or shareware, may
+require patent licenses from the relevant patent holders.
diff --git a/media/codec2/components/amr_nb_wb/Android.bp b/media/codec2/components/amr_nb_wb/Android.bp
new file mode 100644
index 0000000..ce25bc9
--- /dev/null
+++ b/media/codec2/components/amr_nb_wb/Android.bp
@@ -0,0 +1,77 @@
+cc_library_shared {
+    name: "libcodec2_soft_amrnbdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftAmrDec.cpp"],
+
+    cflags: [
+        "-DAMRNB",
+    ],
+
+    static_libs: [
+        "libstagefright_amrnbdec",
+        "libstagefright_amrwbdec",
+    ],
+
+    shared_libs: [
+        "libstagefright_amrnb_common",
+    ],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_amrwbdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftAmrDec.cpp"],
+
+    static_libs: [
+        "libstagefright_amrnbdec",
+        "libstagefright_amrwbdec",
+    ],
+
+    shared_libs: [
+        "libstagefright_amrnb_common",
+    ],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_amrnbenc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftAmrNbEnc.cpp"],
+
+    static_libs: [
+        "libstagefright_amrnbenc",
+    ],
+
+    shared_libs: [
+        "libstagefright_amrnb_common",
+    ],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_amrwbenc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftAmrWbEnc.cpp"],
+
+    static_libs: [
+        "libstagefright_amrwbenc",
+    ],
+
+    shared_libs: [
+        "libstagefright_enc_common",
+    ],
+}
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp
new file mode 100644
index 0000000..6578ad2
--- /dev/null
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp
@@ -0,0 +1,432 @@
+/*
+ * 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
+#ifdef AMRNB
+#define LOG_TAG "C2SoftAmrNbDec"
+#else
+#define LOG_TAG "C2SoftAmrWbDec"
+#endif
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftAmrDec.h"
+#include "gsmamr_dec.h"
+#include "pvamrwbdecoder.h"
+
+namespace android {
+
+namespace {
+
+#ifdef AMRNB
+  constexpr char COMPONENT_NAME[] = "c2.android.amrnb.decoder";
+#else
+  constexpr char COMPONENT_NAME[] = "c2.android.amrwb.decoder";
+#endif
+
+}  // namespace
+
+class C2SoftAmrDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+#ifdef AMRNB
+                MEDIA_MIMETYPE_AUDIO_AMR_NB
+#else
+                MEDIA_MIMETYPE_AUDIO_AMR_WB
+#endif
+                ) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+#ifdef AMRNB
+                .withDefault(new C2StreamSampleRateInfo::output(0u, 8000))
+                .withFields({C2F(mSampleRate, value).equalTo(8000)})
+#else
+                .withDefault(new C2StreamSampleRateInfo::output(0u, 16000))
+                .withFields({C2F(mSampleRate, value).equalTo(16000)})
+#endif
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::output(0u, 1))
+                .withFields({C2F(mChannelCount, value).equalTo(1)})
+                .withSetter((Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+#ifdef AMRNB
+                .withDefault(new C2StreamBitrateInfo::input(0u, 4750))
+                .withFields({C2F(mBitrate, value).inRange(4750, 12200)})
+#else
+                .withDefault(new C2StreamBitrateInfo::input(0u, 6600))
+                .withFields({C2F(mBitrate, value).inRange(6600, 23850)})
+#endif
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 8192))
+                .build());
+    }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+};
+
+C2SoftAmrDec::C2SoftAmrDec(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mAmrHandle(nullptr),
+      mDecoderBuf(nullptr),
+      mDecoderCookie(nullptr) {
+#ifdef AMRNB
+    mIsWide = false;
+#else
+    mIsWide = true;
+#endif
+}
+
+C2SoftAmrDec::~C2SoftAmrDec() {
+    (void)onRelease();
+}
+
+c2_status_t C2SoftAmrDec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+c2_status_t C2SoftAmrDec::onStop() {
+    if (!mIsWide) {
+        Speech_Decode_Frame_reset(mAmrHandle);
+    } else {
+        pvDecoder_AmrWb_Reset(mAmrHandle, 0 /* reset_all */);
+    }
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+
+    return C2_OK;
+}
+
+void C2SoftAmrDec::onReset() {
+    (void)onStop();
+}
+
+void C2SoftAmrDec::onRelease() {
+    if (!mIsWide) {
+        if (mAmrHandle) {
+            GSMDecodeFrameExit(&mAmrHandle);
+        }
+        mAmrHandle = nullptr;
+    } else {
+        if (mDecoderBuf) {
+            free(mDecoderBuf);
+        }
+        mDecoderBuf = nullptr;
+        mAmrHandle = nullptr;
+        mDecoderCookie = nullptr;
+    }
+}
+
+c2_status_t C2SoftAmrDec::onFlush_sm() {
+    return onStop();
+}
+
+status_t C2SoftAmrDec::initDecoder() {
+    if (!mIsWide) {
+        if (GSMInitDecode(&mAmrHandle, (int8_t *)"AMRNBDecoder"))
+            return UNKNOWN_ERROR;
+    } else {
+        uint32_t memReq = pvDecoder_AmrWbMemRequirements();
+        mDecoderBuf = malloc(memReq);
+        if (mDecoderBuf) {
+            pvDecoder_AmrWb_Init(&mAmrHandle, mDecoderBuf, &mDecoderCookie);
+        }
+        else {
+            return NO_MEMORY;
+        }
+    }
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+
+    return OK;
+}
+
+static size_t getFrameSize(bool isWide, unsigned FM) {
+    static const size_t kFrameSizeNB[16] = {
+        12, 13, 15, 17, 19, 20, 26, 31,
+        5, 6, 5, 5, // SID
+        0, 0, 0, // future use
+        0 // no data
+    };
+    static const size_t kFrameSizeWB[16] = {
+        17, 23, 32, 36, 40, 46, 50, 58, 60,
+        5, // SID
+        0, 0, 0, 0, // future use
+        0, // speech lost
+        0 // no data
+    };
+
+    if (FM > 15 || (isWide && FM > 9 && FM < 14) || (!isWide && FM > 11 && FM < 15)) {
+        ALOGE("illegal AMR frame mode %d", FM);
+        return 0;
+    }
+    // add 1 for header byte
+    return (isWide ? kFrameSizeWB[FM] : kFrameSizeNB[FM]) + 1;
+}
+
+static status_t calculateNumFrames(const uint8 *input, size_t inSize,
+                                   std::vector<size_t> *frameSizeList, bool isWide) {
+    for (size_t k = 0; k < inSize;) {
+        int16_t FM = ((input[0] >> 3) & 0x0f);
+        size_t frameSize = getFrameSize(isWide, FM);
+        if (frameSize == 0) {
+            return UNKNOWN_ERROR;
+        }
+        if ((inSize - k) >= frameSize) {
+            input += frameSize;
+            k += frameSize;
+        }
+        else break;
+        frameSizeList->push_back(frameSize);
+    }
+    return OK;
+}
+
+void C2SoftAmrDec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    C2ReadView rView = mDummyReadView;
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = rView.error();
+            return;
+        }
+    }
+
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+    if (inSize == 0) {
+        work->worklets.front()->output.flags = work->input.flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+          (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+
+    std::vector<size_t> frameSizeList;
+    if (OK != calculateNumFrames(rView.data() + inOffset, inSize, &frameSizeList,
+                                 mIsWide)) {
+        work->result = C2_CORRUPTED;
+        mSignalledError = true;
+        return;
+    }
+    if (frameSizeList.empty()) {
+        ALOGE("input size smaller than expected");
+        work->result = C2_CORRUPTED;
+        mSignalledError = true;
+        return;
+    }
+
+    int16_t outSamples = mIsWide ? kNumSamplesPerFrameWB : kNumSamplesPerFrameNB;
+    size_t calOutSize = outSamples * frameSizeList.size() * sizeof(int16_t);
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(calOutSize, usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = wView.error();
+        return;
+    }
+
+    int16_t *output = reinterpret_cast<int16_t *>(wView.data());
+    auto it = frameSizeList.begin();
+    const uint8_t *inPtr = rView.data() + inOffset;
+    size_t inPos = 0;
+    while (inPos < inSize) {
+        if (it == frameSizeList.end()) {
+            ALOGD("unexpected trailing bytes, ignoring them");
+            break;
+        }
+        uint8_t *input = const_cast<uint8_t *>(inPtr + inPos);
+        int16_t FM = ((*input >> 3) & 0x0f);
+        if (!mIsWide) {
+            int32_t numBytesRead = AMRDecode(mAmrHandle,
+                                             (Frame_Type_3GPP) FM,
+                                             input + 1, output, MIME_IETF);
+            if (static_cast<size_t>(numBytesRead + 1) != *it) {
+                ALOGE("panic, parsed size does not match decoded size");
+                work->result = C2_CORRUPTED;
+                mSignalledError = true;
+                return;
+            }
+        } else {
+            if (FM >= 9) {
+                // Produce silence instead of comfort noise and for
+                // speech lost/no data.
+                memset(output, 0, outSamples * sizeof(int16_t));
+            } else {
+                int16_t FT;
+                RX_State_wb rx_state;
+                int16_t numRecSamples;
+
+                mime_unsorting(const_cast<uint8_t *>(&input[1]),
+                               mInputSampleBuffer, &FT, &FM, 1, &rx_state);
+                pvDecoder_AmrWb(FM, mInputSampleBuffer, output, &numRecSamples,
+                                mDecoderBuf, FT, mDecoderCookie);
+                if (numRecSamples != outSamples) {
+                    ALOGE("Sample output per frame incorrect");
+                    work->result = C2_CORRUPTED;
+                    mSignalledError = true;
+                    return;
+                }
+                /* Delete the 2 LSBs (14-bit output) */
+                for (int i = 0; i < numRecSamples; ++i) {
+                    output[i] &= 0xfffC;
+                }
+            }
+        }
+        inPos += *it;
+        output += outSamples;
+        ++it;
+    }
+
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.buffers.push_back(createLinearBuffer(block));
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+    }
+}
+
+c2_status_t C2SoftAmrDec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void)pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+    return C2_OK;
+}
+
+class C2SoftAMRDecFactory : public C2ComponentFactory {
+public:
+    C2SoftAMRDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftAmrDec(COMPONENT_NAME, id,
+                              std::make_shared<C2SoftAmrDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftAmrDec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftAmrDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftAMRDecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftAMRDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.h b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.h
new file mode 100644
index 0000000..6384450
--- /dev/null
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.h
@@ -0,0 +1,68 @@
+/*
+ * 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_C2_SOFT_AMR_DEC_H_
+#define ANDROID_C2_SOFT_AMR_DEC_H_
+
+#include <SimpleC2Component.h>
+
+
+namespace android {
+
+struct C2SoftAmrDec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftAmrDec(const char *name, c2_node_id_t id,
+              const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftAmrDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+private:
+    enum {
+        kNumSamplesPerFrameNB   = 160,
+        kNumSamplesPerFrameWB   = 320,
+    };
+
+    std::shared_ptr<IntfImpl> mIntf;
+    void *mAmrHandle;
+    void *mDecoderBuf;
+    int16_t *mDecoderCookie;
+
+    int16_t mInputSampleBuffer[477];
+
+    bool mIsWide;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+
+    status_t initDecoder();
+
+    C2_DO_NOT_COPY(C2SoftAmrDec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_AMR_DEC_H_
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp
new file mode 100644
index 0000000..e2d8cb6
--- /dev/null
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp
@@ -0,0 +1,348 @@
+/*
+ * 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 "C2SoftAmrNbEnc"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftAmrNbEnc.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.amrnb.encoder";
+
+}  // namespace
+
+
+class C2SoftAmrNbEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_AMR_NB) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::input(0u, 1))
+                .withFields({C2F(mChannelCount, value).equalTo(1)})
+                .withSetter((Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+            DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::input(0u, 8000))
+                .withFields({C2F(mSampleRate, value).equalTo(8000)})
+                .withSetter(
+                    (Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::output(0u, 4750))
+                .withFields({C2F(mBitrate, value).inRange(4750, 12200)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 8192))
+                .build());
+    }
+
+    uint32_t getSampleRate() const { return mSampleRate->value; }
+    uint32_t getChannelCount() const { return mChannelCount->value; }
+    uint32_t getBitrate() const { return mBitrate->value; }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+};
+
+C2SoftAmrNbEnc::C2SoftAmrNbEnc(const char* name, c2_node_id_t id,
+                               const std::shared_ptr<IntfImpl>& intfImpl)
+    : SimpleC2Component(
+          std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mEncState(nullptr),
+      mSidState(nullptr) {
+}
+
+C2SoftAmrNbEnc::~C2SoftAmrNbEnc() {
+    onRelease();
+}
+
+c2_status_t C2SoftAmrNbEnc::onInit() {
+    bool dtx_enable = false;
+
+    if (AMREncodeInit(&mEncState, &mSidState, dtx_enable) != 0)
+        return C2_CORRUPTED;
+    // TODO: get mode directly from config
+    switch(mIntf->getBitrate()) {
+        case 4750: mMode = MR475;
+            break;
+        case 5150: mMode = MR515;
+            break;
+        case 5900: mMode = MR59;
+            break;
+        case 6700: mMode = MR67;
+            break;
+        case 7400: mMode = MR74;
+            break;
+        case 7950: mMode = MR795;
+            break;
+        case 10200: mMode = MR102;
+            break;
+        case 12200: mMode = MR122;
+            break;
+        default: mMode = MR795;
+    }
+    mIsFirst = true;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mAnchorTimeStamp = 0;
+    mProcessedSamples = 0;
+    mFilledLen = 0;
+
+    return C2_OK;
+}
+
+void C2SoftAmrNbEnc::onRelease() {
+    if (mEncState) {
+        AMREncodeExit(&mEncState, &mSidState);
+        mEncState = mSidState = nullptr;
+    }
+}
+
+c2_status_t C2SoftAmrNbEnc::onStop() {
+    if (AMREncodeReset(mEncState, mSidState) != 0)
+        return C2_CORRUPTED;
+    mIsFirst = true;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mAnchorTimeStamp = 0;
+    mProcessedSamples = 0;
+    mFilledLen = 0;
+
+    return C2_OK;
+}
+
+void C2SoftAmrNbEnc::onReset() {
+    (void) onStop();
+}
+
+c2_status_t C2SoftAmrNbEnc::onFlush_sm() {
+    return onStop();
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftAmrNbEnc::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+
+    size_t outCapacity = kNumBytesPerInputFrame;
+    outCapacity += mFilledLen + inSize;
+    std::shared_ptr<C2LinearBlock> outputBlock;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &outputBlock);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = outputBlock->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+    uint64_t outTimeStamp =
+        mProcessedSamples * 1000000ll / mIntf->getSampleRate();
+    size_t inPos = 0;
+    size_t outPos = 0;
+    while (inPos < inSize) {
+        const uint8_t *inPtr = rView.data() + inOffset;
+        int validSamples = mFilledLen / sizeof(int16_t);
+        if ((inPos + (kNumBytesPerInputFrame - mFilledLen)) <= inSize) {
+            memcpy(mInputFrame + validSamples, inPtr + inPos,
+                   (kNumBytesPerInputFrame - mFilledLen));
+            inPos += (kNumBytesPerInputFrame - mFilledLen);
+        } else {
+            memcpy(mInputFrame + validSamples, inPtr + inPos, (inSize - inPos));
+            mFilledLen += (inSize - inPos);
+            inPos += (inSize - inPos);
+            if (eos) {
+                validSamples = mFilledLen / sizeof(int16_t);
+                memset(mInputFrame + validSamples, 0, (kNumBytesPerInputFrame - mFilledLen));
+            } else break;
+
+        }
+        Frame_Type_3GPP frameType;
+        int numEncBytes = AMREncode(mEncState, mSidState, mMode, mInputFrame,
+                                    wView.data() + outPos, &frameType,
+                                    AMR_TX_WMF);
+        if (numEncBytes < 0 || numEncBytes > ((int)outCapacity - (int)outPos)) {
+            ALOGE("encodeFrame call failed, state [%d %zu %zu]", numEncBytes, outPos, outCapacity);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        // Convert header byte from WMF to IETF format.
+        if (numEncBytes > 0)
+            wView.data()[outPos] = ((wView.data()[outPos] << 3) | 4) & 0x7c;
+        outPos += numEncBytes;
+        mProcessedSamples += kNumSamplesPerFrame;
+        mFilledLen = 0;
+    }
+    ALOGV("causal sample size %d", mFilledLen);
+    if (mIsFirst && outPos != 0) {
+        mIsFirst = false;
+        mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+    }
+    fillEmptyWork(work);
+    if (outPos != 0) {
+        work->worklets.front()->output.buffers.push_back(
+                createLinearBuffer(std::move(outputBlock), 0, outPos));
+        work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+
+    }
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+        if (mFilledLen) ALOGV("Discarding trailing %d bytes", mFilledLen);
+    }
+}
+
+c2_status_t C2SoftAmrNbEnc::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    onFlush_sm();
+    return C2_OK;
+}
+
+class C2SoftAmrNbEncFactory : public C2ComponentFactory {
+public:
+    C2SoftAmrNbEncFactory()
+        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+              GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+            new C2SoftAmrNbEnc(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftAmrNbEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+            new SimpleInterface<C2SoftAmrNbEnc::IntfImpl>(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftAmrNbEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftAmrNbEncFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftAmrNbEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.h b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.h
new file mode 100644
index 0000000..6ab14db
--- /dev/null
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.h
@@ -0,0 +1,67 @@
+/*
+ * 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_C2_SOFT_AMR_NB_ENC_H_
+#define ANDROID_C2_SOFT_AMR_NB_ENC_H_
+
+#include <SimpleC2Component.h>
+
+#include "gsmamr_enc.h"
+
+namespace android {
+
+class C2SoftAmrNbEnc : public SimpleC2Component {
+   public:
+    class IntfImpl;
+    C2SoftAmrNbEnc(const char* name, c2_node_id_t id,
+                   const std::shared_ptr<IntfImpl>& intfImpl);
+    virtual ~C2SoftAmrNbEnc();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    std::shared_ptr<IntfImpl> mIntf;
+    static const int32_t kNumSamplesPerFrame = L_FRAME;
+    static const int32_t kNumBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t);
+
+    void *mEncState;
+    void *mSidState;
+    Mode mMode;
+    bool mIsFirst;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+    uint64_t mAnchorTimeStamp;
+    uint64_t mProcessedSamples;
+    int32_t mFilledLen;
+    int16_t mInputFrame[kNumSamplesPerFrame];
+
+    C2_DO_NOT_COPY(C2SoftAmrNbEnc);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_AMR_NB_ENC_H_
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp
new file mode 100644
index 0000000..84ae4b7
--- /dev/null
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.cpp
@@ -0,0 +1,422 @@
+/*
+ * 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 "C2SoftAmrWbEnc"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftAmrWbEnc.h"
+#include "cmnMemory.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.amrwb.encoder";
+
+}  // namespace
+
+class C2SoftAmrWbEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_AMR_WB) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::input(0u, 1))
+                .withFields({C2F(mChannelCount, value).equalTo(1)})
+                .withSetter((Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+            DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::input(0u, 16000))
+                .withFields({C2F(mSampleRate, value).equalTo(16000)})
+                .withSetter(
+                    (Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::output(0u, 6600))
+                .withFields({C2F(mBitrate, value).inRange(6600, 23850)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 8192))
+                .build());
+    }
+
+    uint32_t getSampleRate() const { return mSampleRate->value; }
+    uint32_t getChannelCount() const { return mChannelCount->value; }
+    uint32_t getBitrate() const { return mBitrate->value; }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+};
+
+C2SoftAmrWbEnc::C2SoftAmrWbEnc(const char* name, c2_node_id_t id,
+                               const std::shared_ptr<IntfImpl>& intfImpl)
+    : SimpleC2Component(
+          std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mEncoderHandle(nullptr),
+      mApiHandle(nullptr),
+      mMemOperator(nullptr) {
+}
+
+C2SoftAmrWbEnc::~C2SoftAmrWbEnc() {
+    onRelease();
+}
+
+c2_status_t C2SoftAmrWbEnc::onInit() {
+    // TODO: get mode directly from config
+    switch(mIntf->getBitrate()) {
+        case 6600: mMode = VOAMRWB_MD66;
+            break;
+        case 8850: mMode = VOAMRWB_MD885;
+            break;
+        case 12650: mMode = VOAMRWB_MD1265;
+            break;
+        case 14250: mMode = VOAMRWB_MD1425;
+            break;
+        case 15850: mMode = VOAMRWB_MD1585;
+            break;
+        case 18250: mMode = VOAMRWB_MD1825;
+            break;
+        case 19850: mMode = VOAMRWB_MD1985;
+            break;
+        case 23050: mMode = VOAMRWB_MD2305;
+            break;
+        case 23850: mMode = VOAMRWB_MD2385;
+            break;
+        default: mMode = VOAMRWB_MD2305;
+    }
+    status_t err = initEncoder();
+    mIsFirst = true;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mAnchorTimeStamp = 0;
+    mProcessedSamples = 0;
+    mFilledLen = 0;
+
+    return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+void C2SoftAmrWbEnc::onRelease() {
+    if (mEncoderHandle) {
+        CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
+        mEncoderHandle = nullptr;
+    }
+    if (mApiHandle) {
+        delete mApiHandle;
+        mApiHandle = nullptr;
+    }
+    if (mMemOperator) {
+        delete mMemOperator;
+        mMemOperator = nullptr;
+    }
+}
+
+c2_status_t C2SoftAmrWbEnc::onStop() {
+    for (int i = 0; i < kNumSamplesPerFrame; i++) {
+        mInputFrame[i] = 0x0008; /* EHF_MASK */
+    }
+    uint8_t outBuffer[kNumBytesPerInputFrame];
+    (void) encodeInput(outBuffer, kNumBytesPerInputFrame);
+    mIsFirst = true;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mAnchorTimeStamp = 0;
+    mProcessedSamples = 0;
+    mFilledLen = 0;
+
+    return C2_OK;
+}
+
+void C2SoftAmrWbEnc::onReset() {
+    (void) onStop();
+}
+
+c2_status_t C2SoftAmrWbEnc::onFlush_sm() {
+    return onStop();
+}
+
+status_t C2SoftAmrWbEnc::initEncoder() {
+    mApiHandle = new VO_AUDIO_CODECAPI;
+    if (!mApiHandle) return NO_MEMORY;
+
+    if (VO_ERR_NONE != voGetAMRWBEncAPI(mApiHandle)) {
+        ALOGE("Failed to get api handle");
+        return UNKNOWN_ERROR;
+    }
+
+    mMemOperator = new VO_MEM_OPERATOR;
+    if (!mMemOperator) return NO_MEMORY;
+
+    mMemOperator->Alloc = cmnMemAlloc;
+    mMemOperator->Copy = cmnMemCopy;
+    mMemOperator->Free = cmnMemFree;
+    mMemOperator->Set = cmnMemSet;
+    mMemOperator->Check = cmnMemCheck;
+
+    VO_CODEC_INIT_USERDATA userData;
+    memset(&userData, 0, sizeof(userData));
+    userData.memflag = VO_IMF_USERMEMOPERATOR;
+    userData.memData = (VO_PTR) mMemOperator;
+
+    if (VO_ERR_NONE != mApiHandle->Init(
+                &mEncoderHandle, VO_AUDIO_CodingAMRWB, &userData)) {
+        ALOGE("Failed to init AMRWB encoder");
+        return UNKNOWN_ERROR;
+    }
+
+    VOAMRWBFRAMETYPE type = VOAMRWB_RFC3267;
+    if (VO_ERR_NONE != mApiHandle->SetParam(
+                mEncoderHandle, VO_PID_AMRWB_FRAMETYPE, &type)) {
+        ALOGE("Failed to set AMRWB encoder frame type to %d", type);
+        return UNKNOWN_ERROR;
+    }
+
+    if (VO_ERR_NONE !=
+            mApiHandle->SetParam(
+                    mEncoderHandle, VO_PID_AMRWB_MODE,  &mMode)) {
+        ALOGE("Failed to set AMRWB encoder mode to %d", mMode);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+int C2SoftAmrWbEnc::encodeInput(uint8_t *buffer, uint32_t length) {
+    VO_CODECBUFFER inputData;
+    memset(&inputData, 0, sizeof(inputData));
+    inputData.Buffer = (unsigned char *) mInputFrame;
+    inputData.Length = kNumBytesPerInputFrame;
+
+    CHECK_EQ((VO_U32)VO_ERR_NONE,
+             mApiHandle->SetInputData(mEncoderHandle, &inputData));
+
+    VO_AUDIO_OUTPUTINFO outputInfo;
+    memset(&outputInfo, 0, sizeof(outputInfo));
+    VO_CODECBUFFER outputData;
+    memset(&outputData, 0, sizeof(outputData));
+    outputData.Buffer = buffer;
+    outputData.Length = length;
+    VO_U32 ret = mApiHandle->GetOutputData(
+            mEncoderHandle, &outputData, &outputInfo);
+    if (ret != VO_ERR_NONE && ret != VO_ERR_INPUT_BUFFER_SMALL) {
+        ALOGD("encountered error during encode call");
+        return -1;
+    }
+    return outputData.Length;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftAmrWbEnc::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = rView.error();
+            return;
+        }
+    }
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+
+    size_t outCapacity = kNumBytesPerInputFrame;
+    outCapacity += mFilledLen + inSize;
+    std::shared_ptr<C2LinearBlock> outputBlock;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &outputBlock);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = outputBlock->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = wView.error();
+        return;
+    }
+    uint64_t outTimeStamp =
+        mProcessedSamples * 1000000ll / mIntf->getSampleRate();
+    size_t inPos = 0;
+    size_t outPos = 0;
+    while (inPos < inSize) {
+        const uint8_t *inPtr = rView.data() + inOffset;
+        int validSamples = mFilledLen / sizeof(int16_t);
+        if ((inPos + (kNumBytesPerInputFrame - mFilledLen)) <= inSize) {
+            memcpy(mInputFrame + validSamples, inPtr + inPos,
+                   (kNumBytesPerInputFrame - mFilledLen));
+            inPos += (kNumBytesPerInputFrame - mFilledLen);
+        } else {
+            memcpy(mInputFrame + validSamples, inPtr + inPos, (inSize - inPos));
+            mFilledLen += (inSize - inPos);
+            inPos += (inSize - inPos);
+            if (eos) {
+                validSamples = mFilledLen / sizeof(int16_t);
+                memset(mInputFrame + validSamples, 0, (kNumBytesPerInputFrame - mFilledLen));
+            } else break;
+        }
+        int numEncBytes = encodeInput((wView.data() + outPos), outCapacity - outPos);
+        if (numEncBytes < 0) {
+            ALOGE("encodeFrame call failed, state [%d %zu %zu]", numEncBytes, outPos, outCapacity);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        outPos += numEncBytes;
+        mProcessedSamples += kNumSamplesPerFrame;
+        mFilledLen = 0;
+    }
+    ALOGV("causal sample size %d", mFilledLen);
+    if (mIsFirst && outPos != 0) {
+        mIsFirst = false;
+        mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+    }
+    fillEmptyWork(work);
+    if (outPos != 0) {
+        work->worklets.front()->output.buffers.push_back(
+                createLinearBuffer(std::move(outputBlock), 0, outPos));
+        work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+    }
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+        if (mFilledLen) ALOGV("Discarding trailing %d bytes", mFilledLen);
+    }
+}
+
+c2_status_t C2SoftAmrWbEnc::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    onFlush_sm();
+    return C2_OK;
+}
+
+class C2SoftAmrWbEncFactory : public C2ComponentFactory {
+public:
+    C2SoftAmrWbEncFactory()
+        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+              GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+            new C2SoftAmrWbEnc(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftAmrWbEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+            new SimpleInterface<C2SoftAmrWbEnc::IntfImpl>(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftAmrWbEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftAmrWbEncFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftAmrWbEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.h b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.h
new file mode 100644
index 0000000..0cc9e9f
--- /dev/null
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrWbEnc.h
@@ -0,0 +1,71 @@
+/*
+ * 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_C2_SOFT_AMR_WB_ENC_H_
+#define ANDROID_C2_SOFT_AMR_WB_ENC_H_
+
+#include <SimpleC2Component.h>
+
+#include "voAMRWB.h"
+
+namespace android {
+
+class C2SoftAmrWbEnc : public SimpleC2Component {
+public:
+    class IntfImpl;
+    C2SoftAmrWbEnc(const char* name, c2_node_id_t id,
+                   const std::shared_ptr<IntfImpl>& intfImpl);
+    virtual ~C2SoftAmrWbEnc();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    std::shared_ptr<IntfImpl> mIntf;
+    static const int32_t kNumSamplesPerFrame = 320;
+    static const int32_t kNumBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t);
+
+    void *mEncoderHandle;
+    VO_AUDIO_CODECAPI *mApiHandle;
+    VO_MEM_OPERATOR *mMemOperator;
+    VOAMRWBMODE mMode;
+    bool mIsFirst;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+    uint64_t mAnchorTimeStamp;
+    uint64_t mProcessedSamples;
+    int32_t mFilledLen;
+    int16_t mInputFrame[kNumSamplesPerFrame];
+
+    status_t initEncoder();
+    int encodeInput(uint8_t *buffer, uint32_t length);
+
+    C2_DO_NOT_COPY(C2SoftAmrWbEnc);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_AMR_WB_ENC_H_
diff --git a/media/common_time/MODULE_LICENSE_APACHE2 b/media/codec2/components/amr_nb_wb/MODULE_LICENSE_APACHE2
similarity index 100%
copy from media/common_time/MODULE_LICENSE_APACHE2
copy to media/codec2/components/amr_nb_wb/MODULE_LICENSE_APACHE2
diff --git a/media/common_time/NOTICE b/media/codec2/components/amr_nb_wb/NOTICE
similarity index 100%
copy from media/common_time/NOTICE
copy to media/codec2/components/amr_nb_wb/NOTICE
diff --git a/media/codec2/components/amr_nb_wb/patent_disclaimer.txt b/media/codec2/components/amr_nb_wb/patent_disclaimer.txt
new file mode 100644
index 0000000..b4bf11d
--- /dev/null
+++ b/media/codec2/components/amr_nb_wb/patent_disclaimer.txt
@@ -0,0 +1,9 @@
+
+THIS IS NOT A GRANT OF PATENT RIGHTS.
+
+Google makes no representation or warranty that the codecs for which
+source code is made available hereunder are unencumbered by
+third-party patents.  Those intending to use this source code in
+hardware or software products are advised that implementations of
+these codecs, including in open source software or shareware, may
+require patent licenses from the relevant patent holders.
diff --git a/media/codec2/components/aom/Android.bp b/media/codec2/components/aom/Android.bp
new file mode 100644
index 0000000..0fabf5c
--- /dev/null
+++ b/media/codec2/components/aom/Android.bp
@@ -0,0 +1,14 @@
+cc_library_shared {
+    name: "libcodec2_soft_av1dec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftAomDec.cpp"],
+    static_libs: ["libaom"],
+
+    include_dirs: [
+        "external/libaom/",
+    ],
+}
diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp
new file mode 100644
index 0000000..769895c
--- /dev/null
+++ b/media/codec2/components/aom/C2SoftAomDec.cpp
@@ -0,0 +1,810 @@
+/*
+ * 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 "C2SoftAomDec"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftAomDec.h"
+
+namespace android {
+
+constexpr char COMPONENT_NAME[] = "c2.android.av1.decoder";
+
+class C2SoftAomDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+  public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
+        : SimpleInterface<void>::BaseParams(
+              helper, COMPONENT_NAME, C2Component::KIND_DECODER,
+              C2Component::DOMAIN_VIDEO, MEDIA_MIMETYPE_VIDEO_AV1) {
+        noPrivateBuffers();  // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+
+        addParameter(DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                         .withConstValue(new C2ComponentAttributesSetting(
+                             C2Component::ATTRIB_IS_TEMPORAL))
+                         .build());
+
+        addParameter(
+            DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 2048, 2),
+                    C2F(mSize, height).inRange(2, 2048, 2),
+                })
+                .withSetter(SizeSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::input(0u,
+                        C2Config::PROFILE_AV1_0, C2Config::LEVEL_AV1_2_1))
+                .withFields({
+                    C2F(mProfileLevel, profile).oneOf({
+                            C2Config::PROFILE_AV1_0,
+                            C2Config::PROFILE_AV1_1}),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_AV1_2,
+                            C2Config::LEVEL_AV1_2_1,
+                            C2Config::LEVEL_AV1_2_2,
+                            C2Config::LEVEL_AV1_3,
+                            C2Config::LEVEL_AV1_3_1,
+                            C2Config::LEVEL_AV1_3_2,
+                    })
+                })
+                .withSetter(ProfileLevelSetter, mSize)
+                .build());
+
+        mHdr10PlusInfoInput = C2StreamHdr10PlusInfo::input::AllocShared(0);
+        addParameter(
+                DefineParam(mHdr10PlusInfoInput, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO)
+                .withDefault(mHdr10PlusInfoInput)
+                .withFields({
+                    C2F(mHdr10PlusInfoInput, m.value).any(),
+                })
+                .withSetter(Hdr10PlusInfoInputSetter)
+                .build());
+
+        mHdr10PlusInfoOutput = C2StreamHdr10PlusInfo::output::AllocShared(0);
+        addParameter(
+                DefineParam(mHdr10PlusInfoOutput, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO)
+                .withDefault(mHdr10PlusInfoOutput)
+                .withFields({
+                    C2F(mHdr10PlusInfoOutput, m.value).any(),
+                })
+                .withSetter(Hdr10PlusInfoOutputSetter)
+                .build());
+
+        addParameter(DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
+                         .withDefault(new C2StreamMaxPictureSizeTuning::output(
+                             0u, 320, 240))
+                         .withFields({
+                             C2F(mSize, width).inRange(2, 2048, 2),
+                             C2F(mSize, height).inRange(2, 2048, 2),
+                         })
+                         .withSetter(MaxPictureSizeSetter, mSize)
+                         .build());
+
+        addParameter(
+            DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withDefault(
+                    new C2StreamMaxBufferSizeInfo::input(0u, 320 * 240 * 3 / 4))
+                .withFields({
+                    C2F(mMaxInputSize, value).any(),
+                })
+                .calculatedAs(MaxInputSizeSetter, mMaxSize)
+                .build());
+
+        C2ChromaOffsetStruct locations[1] = {
+            C2ChromaOffsetStruct::ITU_YUV_420_0()};
+        std::shared_ptr<C2StreamColorInfo::output> defaultColorInfo =
+            C2StreamColorInfo::output::AllocShared(1u, 0u, 8u /* bitDepth */,
+                                                   C2Color::YUV_420);
+        memcpy(defaultColorInfo->m.locations, locations, sizeof(locations));
+
+        defaultColorInfo = C2StreamColorInfo::output::AllocShared(
+            {C2ChromaOffsetStruct::ITU_YUV_420_0()}, 0u, 8u /* bitDepth */,
+            C2Color::YUV_420);
+        helper->addStructDescriptors<C2ChromaOffsetStruct>();
+
+        addParameter(DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
+                         .withConstValue(defaultColorInfo)
+                         .build());
+
+        addParameter(
+                DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsTuning::output(
+                        0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+                        C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({
+                    C2F(mDefaultColorAspects, range).inRange(
+                                C2Color::RANGE_UNSPECIFIED,     C2Color::RANGE_OTHER),
+                    C2F(mDefaultColorAspects, primaries).inRange(
+                                C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+                    C2F(mDefaultColorAspects, transfer).inRange(
+                                C2Color::TRANSFER_UNSPECIFIED,  C2Color::TRANSFER_OTHER),
+                    C2F(mDefaultColorAspects, matrix).inRange(
+                                C2Color::MATRIX_UNSPECIFIED,    C2Color::MATRIX_OTHER)
+                })
+                .withSetter(DefaultColorAspectsSetter)
+                .build());
+
+        // TODO: support more formats?
+        addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+                         .withConstValue(new C2StreamPixelFormatInfo::output(
+                             0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+                         .build());
+    }
+
+    static C2R SizeSetter(bool mayBlock,
+                          const C2P<C2StreamPictureSizeInfo::output>& oldMe,
+                          C2P<C2StreamPictureSizeInfo::output>& me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R MaxPictureSizeSetter(
+        bool mayBlock, C2P<C2StreamMaxPictureSizeTuning::output>& me,
+        const C2P<C2StreamPictureSizeInfo::output>& size) {
+        (void)mayBlock;
+        // TODO: get max width/height from the size's field helpers vs.
+        // hardcoding
+        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 4096u);
+        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 4096u);
+        return C2R::Ok();
+    }
+
+    static C2R MaxInputSizeSetter(
+        bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input>& me,
+        const C2P<C2StreamMaxPictureSizeTuning::output>& maxSize) {
+        (void)mayBlock;
+        // assume compression ratio of 2
+        me.set().value = (((maxSize.v.width + 63) / 64) *
+                          ((maxSize.v.height + 63) / 64) * 3072);
+        return C2R::Ok();
+    }
+    static C2R DefaultColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsTuning::output> &me) {
+        (void)mayBlock;
+        if (me.v.range > C2Color::RANGE_OTHER) {
+            me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+            me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+            me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+            me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input> &me,
+                                  const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        (void)size;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+    std::shared_ptr<C2StreamColorAspectsTuning::output> getDefaultColorAspects_l() {
+        return mDefaultColorAspects;
+    }
+
+    static C2R Hdr10PlusInfoInputSetter(bool mayBlock, C2P<C2StreamHdr10PlusInfo::input> &me) {
+        (void)mayBlock;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+    static C2R Hdr10PlusInfoOutputSetter(bool mayBlock, C2P<C2StreamHdr10PlusInfo::output> &me) {
+        (void)mayBlock;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+  private:
+    std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+    std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
+    std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mMaxInputSize;
+    std::shared_ptr<C2StreamColorInfo::output> mColorInfo;
+    std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
+    std::shared_ptr<C2StreamColorAspectsTuning::output> mDefaultColorAspects;
+    std::shared_ptr<C2StreamHdr10PlusInfo::input> mHdr10PlusInfoInput;
+    std::shared_ptr<C2StreamHdr10PlusInfo::output> mHdr10PlusInfoOutput;
+};
+
+C2SoftAomDec::C2SoftAomDec(const char* name, c2_node_id_t id,
+                           const std::shared_ptr<IntfImpl>& intfImpl)
+    : SimpleC2Component(
+          std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mCodecCtx(nullptr){
+
+    GENERATE_FILE_NAMES();
+    CREATE_DUMP_FILE(mInFile);
+    CREATE_DUMP_FILE(mOutFile);
+
+    gettimeofday(&mTimeStart, nullptr);
+    gettimeofday(&mTimeEnd, nullptr);
+}
+
+C2SoftAomDec::~C2SoftAomDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftAomDec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftAomDec::onStop() {
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    return C2_OK;
+}
+
+void C2SoftAomDec::onReset() {
+    (void)onStop();
+    c2_status_t err = onFlush_sm();
+    if (err != C2_OK) {
+        ALOGW("Failed to flush decoder. Try to hard reset decoder.");
+        destroyDecoder();
+        (void)initDecoder();
+    }
+}
+
+void C2SoftAomDec::onRelease() {
+    destroyDecoder();
+}
+
+c2_status_t C2SoftAomDec::onFlush_sm() {
+    if (aom_codec_decode(mCodecCtx, nullptr, 0, nullptr)) {
+        ALOGE("Failed to flush av1 decoder.");
+        return C2_CORRUPTED;
+    }
+
+    aom_codec_iter_t iter = nullptr;
+    while (aom_codec_get_frame(mCodecCtx, &iter)) {
+    }
+
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+
+    return C2_OK;
+}
+
+static int GetCPUCoreCount() {
+    int cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGV("Number of CPU cores: %d", cpuCoreCount);
+    return cpuCoreCount;
+}
+
+status_t C2SoftAomDec::initDecoder() {
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    if (!mCodecCtx) {
+        mCodecCtx = new aom_codec_ctx_t;
+    }
+
+    if (!mCodecCtx) {
+        ALOGE("mCodecCtx is null");
+        return NO_MEMORY;
+    }
+
+    aom_codec_dec_cfg_t cfg;
+    memset(&cfg, 0, sizeof(aom_codec_dec_cfg_t));
+    cfg.threads = GetCPUCoreCount();
+    cfg.allow_lowbitdepth = 1;
+
+    aom_codec_flags_t flags;
+    memset(&flags, 0, sizeof(aom_codec_flags_t));
+
+    aom_codec_err_t err;
+    if ((err = aom_codec_dec_init(mCodecCtx, aom_codec_av1_dx(), &cfg, 0))) {
+        ALOGE("av1 decoder failed to initialize. (%d)", err);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftAomDec::destroyDecoder() {
+    if (mCodecCtx) {
+        aom_codec_destroy(mCodecCtx);
+        delete mCodecCtx;
+        mCodecCtx = nullptr;
+    }
+    return OK;
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work>& work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftAomDec::finishWork(uint64_t index,
+                              const std::unique_ptr<C2Work>& work,
+                              const std::shared_ptr<C2GraphicBlock>& block) {
+    std::shared_ptr<C2Buffer> buffer =
+        createGraphicBuffer(block, C2Rect(mWidth, mHeight));
+    auto fillWork = [buffer, index, intf = this->mIntf](
+            const std::unique_ptr<C2Work>& work) {
+        uint32_t flags = 0;
+        if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
+            (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
+            flags |= C2FrameData::FLAG_END_OF_STREAM;
+            ALOGV("signalling eos");
+        }
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+
+        for (const std::unique_ptr<C2Param> &param: work->input.configUpdate) {
+            if (param) {
+                C2StreamHdr10PlusInfo::input *hdr10PlusInfo =
+                        C2StreamHdr10PlusInfo::input::From(param.get());
+
+                if (hdr10PlusInfo != nullptr) {
+                    std::vector<std::unique_ptr<C2SettingResult>> failures;
+                    std::unique_ptr<C2Param> outParam = C2Param::CopyAsStream(
+                            *param.get(), true /*output*/, param->stream());
+                    c2_status_t err = intf->config(
+                            { outParam.get() }, C2_MAY_BLOCK, &failures);
+                    if (err == C2_OK) {
+                        work->worklets.front()->output.configUpdate.push_back(
+                                C2Param::Copy(*outParam.get()));
+                    } else {
+                        ALOGE("finishWork: Config update size failed");
+                    }
+                    break;
+                }
+            }
+        }
+    };
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        fillWork(work);
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+void C2SoftAomDec::process(const std::unique_ptr<C2Work>& work,
+                           const std::shared_ptr<C2BlockPool>& pool) {
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView =
+            work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+
+    bool codecConfig =
+        ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0);
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+
+    if (codecConfig) {
+        fillEmptyWork(work);
+        return;
+    }
+
+    int64_t frameIndex = work->input.ordinal.frameIndex.peekll();
+    if (inSize) {
+        uint8_t* bitstream = const_cast<uint8_t*>(rView.data() + inOffset);
+        int32_t decodeTime = 0;
+        int32_t delay = 0;
+
+        DUMP_TO_FILE(mOutFile, bitstream, inSize);
+        GETTIME(&mTimeStart, nullptr);
+        TIME_DIFF(mTimeEnd, mTimeStart, delay);
+
+        aom_codec_err_t err =
+            aom_codec_decode(mCodecCtx, bitstream, inSize, &frameIndex);
+
+        GETTIME(&mTimeEnd, nullptr);
+        TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
+        ALOGV("decodeTime=%4d delay=%4d\n", decodeTime, delay);
+
+        if (err != AOM_CODEC_OK) {
+            ALOGE("av1 decoder failed to decode frame err: %d", err);
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            mSignalledError = true;
+            return;
+        }
+
+    } else {
+        if (aom_codec_decode(mCodecCtx, nullptr, 0, nullptr)) {
+            ALOGE("Failed to flush av1 decoder.");
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            mSignalledError = true;
+            return;
+        }
+    }
+
+    (void)outputBuffer(pool, work);
+
+    if (eos) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+        mSignalledOutputEos = true;
+    } else if (!inSize) {
+        fillEmptyWork(work);
+    }
+}
+
+static void copyOutputBufferToYuvPlanarFrame(
+        uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
+        size_t srcYStride, size_t srcUStride, size_t srcVStride,
+        size_t dstYStride, size_t dstUVStride,
+        uint32_t width, uint32_t height) {
+    uint8_t* dstStart = dst;
+
+    for (size_t i = 0; i < height; ++i) {
+        memcpy(dst, srcY, width);
+        srcY += srcYStride;
+        dst += dstYStride;
+    }
+
+    dst = dstStart + dstYStride * height;
+    for (size_t i = 0; i < height / 2; ++i) {
+         memcpy(dst, srcV, width / 2);
+        srcV += srcVStride;
+        dst += dstUVStride;
+    }
+
+    dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
+    for (size_t i = 0; i < height / 2; ++i) {
+         memcpy(dst, srcU, width / 2);
+        srcU += srcUStride;
+        dst += dstUVStride;
+    }
+}
+
+static void convertYUV420Planar16ToY410(uint32_t *dst,
+        const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV,
+        size_t srcYStride, size_t srcUStride, size_t srcVStride,
+        size_t dstStride, size_t width, size_t height) {
+
+    // Converting two lines at a time, slightly faster
+    for (size_t y = 0; y < height; y += 2) {
+        uint32_t *dstTop = (uint32_t *) dst;
+        uint32_t *dstBot = (uint32_t *) (dst + dstStride);
+        uint16_t *ySrcTop = (uint16_t*) srcY;
+        uint16_t *ySrcBot = (uint16_t*) (srcY + srcYStride);
+        uint16_t *uSrc = (uint16_t*) srcU;
+        uint16_t *vSrc = (uint16_t*) srcV;
+
+        uint32_t u01, v01, y01, y23, y45, y67, uv0, uv1;
+        size_t x = 0;
+        for (; x < width - 3; x += 4) {
+
+            u01 = *((uint32_t*)uSrc); uSrc += 2;
+            v01 = *((uint32_t*)vSrc); vSrc += 2;
+
+            y01 = *((uint32_t*)ySrcTop); ySrcTop += 2;
+            y23 = *((uint32_t*)ySrcTop); ySrcTop += 2;
+            y45 = *((uint32_t*)ySrcBot); ySrcBot += 2;
+            y67 = *((uint32_t*)ySrcBot); ySrcBot += 2;
+
+            uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
+            uv1 = (u01 >> 16) | ((v01 >> 16) << 20);
+
+            *dstTop++ = 3 << 30 | ((y01 & 0x3FF) << 10) | uv0;
+            *dstTop++ = 3 << 30 | ((y01 >> 16) << 10) | uv0;
+            *dstTop++ = 3 << 30 | ((y23 & 0x3FF) << 10) | uv1;
+            *dstTop++ = 3 << 30 | ((y23 >> 16) << 10) | uv1;
+
+            *dstBot++ = 3 << 30 | ((y45 & 0x3FF) << 10) | uv0;
+            *dstBot++ = 3 << 30 | ((y45 >> 16) << 10) | uv0;
+            *dstBot++ = 3 << 30 | ((y67 & 0x3FF) << 10) | uv1;
+            *dstBot++ = 3 << 30 | ((y67 >> 16) << 10) | uv1;
+        }
+
+        // There should be at most 2 more pixels to process. Note that we don't
+        // need to consider odd case as the buffer is always aligned to even.
+        if (x < width) {
+            u01 = *uSrc;
+            v01 = *vSrc;
+            y01 = *((uint32_t*)ySrcTop);
+            y45 = *((uint32_t*)ySrcBot);
+            uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
+            *dstTop++ = ((y01 & 0x3FF) << 10) | uv0;
+            *dstTop++ = ((y01 >> 16) << 10) | uv0;
+            *dstBot++ = ((y45 & 0x3FF) << 10) | uv0;
+            *dstBot++ = ((y45 >> 16) << 10) | uv0;
+        }
+
+        srcY += srcYStride * 2;
+        srcU += srcUStride;
+        srcV += srcVStride;
+        dst += dstStride * 2;
+    }
+
+    return;
+}
+
+static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst,
+        const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV,
+        size_t srcYStride, size_t srcUStride, size_t srcVStride,
+        size_t dstYStride, size_t dstUVStride, size_t width, size_t height) {
+
+    uint8_t *dstY = (uint8_t *)dst;
+    size_t dstYSize = dstYStride * height;
+    size_t dstUVSize = dstUVStride * height / 2;
+    uint8_t *dstV = dstY + dstYSize;
+    uint8_t *dstU = dstV + dstUVSize;
+
+    for (size_t y = 0; y < height; ++y) {
+        for (size_t x = 0; x < width; ++x) {
+            dstY[x] = (uint8_t)(srcY[x] >> 2);
+        }
+
+        srcY += srcYStride;
+        dstY += dstYStride;
+    }
+
+    for (size_t y = 0; y < (height + 1) / 2; ++y) {
+        for (size_t x = 0; x < (width + 1) / 2; ++x) {
+            dstU[x] = (uint8_t)(srcU[x] >> 2);
+            dstV[x] = (uint8_t)(srcV[x] >> 2);
+        }
+
+        srcU += srcUStride;
+        srcV += srcVStride;
+        dstU += dstUVStride;
+        dstV += dstUVStride;
+    }
+    return;
+}
+bool C2SoftAomDec::outputBuffer(
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work)
+{
+    if (!(work && pool)) return false;
+
+    aom_codec_iter_t iter = nullptr;
+    aom_image_t* img = aom_codec_get_frame(mCodecCtx, &iter);
+
+    if (!img) return false;
+
+    if (img->d_w != mWidth || img->d_h != mHeight) {
+        mWidth = img->d_w;
+        mHeight = img->d_h;
+
+        C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+        std::vector<std::unique_ptr<C2SettingResult>> failures;
+        c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+        if (err == C2_OK) {
+            work->worklets.front()->output.configUpdate.push_back(
+                C2Param::Copy(size));
+        } else {
+            ALOGE("Config update size failed");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return false;
+        }
+    }
+
+    CHECK(img->fmt == AOM_IMG_FMT_I420 || img->fmt == AOM_IMG_FMT_I42016);
+
+    std::shared_ptr<C2GraphicBlock> block;
+    uint32_t format = HAL_PIXEL_FORMAT_YV12;
+    if (img->fmt == AOM_IMG_FMT_I42016) {
+        IntfImpl::Lock lock = mIntf->lock();
+        std::shared_ptr<C2StreamColorAspectsTuning::output> defaultColorAspects = mIntf->getDefaultColorAspects_l();
+
+        if (defaultColorAspects->primaries == C2Color::PRIMARIES_BT2020 &&
+            defaultColorAspects->matrix == C2Color::MATRIX_BT2020 &&
+            defaultColorAspects->transfer == C2Color::TRANSFER_ST2084) {
+            format = HAL_PIXEL_FORMAT_RGBA_1010102;
+        }
+    }
+    C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+
+    c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight,
+                                              format, usage, &block);
+
+    if (err != C2_OK) {
+        ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+        work->result = err;
+        return false;
+    }
+
+    C2GraphicView wView = block->map().get();
+
+    if (wView.error()) {
+        ALOGE("graphic view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return false;
+    }
+
+    ALOGV("provided (%dx%d) required (%dx%d), out frameindex %d",
+          block->width(), block->height(), mWidth, mHeight,
+          (int)*(int64_t*)img->user_priv);
+
+    uint8_t* dst = const_cast<uint8_t*>(wView.data()[C2PlanarLayout::PLANE_Y]);
+    size_t srcYStride = img->stride[AOM_PLANE_Y];
+    size_t srcUStride = img->stride[AOM_PLANE_U];
+    size_t srcVStride = img->stride[AOM_PLANE_V];
+    C2PlanarLayout layout = wView.layout();
+    size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+    size_t dstUVStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+
+    if (img->fmt == AOM_IMG_FMT_I42016) {
+        const uint16_t *srcY = (const uint16_t *)img->planes[AOM_PLANE_Y];
+        const uint16_t *srcU = (const uint16_t *)img->planes[AOM_PLANE_U];
+        const uint16_t *srcV = (const uint16_t *)img->planes[AOM_PLANE_V];
+
+        if (format == HAL_PIXEL_FORMAT_RGBA_1010102) {
+            convertYUV420Planar16ToY410((uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2,
+                                    srcUStride / 2, srcVStride / 2,
+                                    dstYStride / sizeof(uint32_t),
+                                    mWidth, mHeight);
+        } else {
+            convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2,
+                                    srcUStride / 2, srcVStride / 2,
+                                    dstYStride, dstUVStride,
+                                    mWidth, mHeight);
+        }
+    } else {
+        const uint8_t *srcY = (const uint8_t *)img->planes[AOM_PLANE_Y];
+        const uint8_t *srcU = (const uint8_t *)img->planes[AOM_PLANE_U];
+        const uint8_t *srcV = (const uint8_t *)img->planes[AOM_PLANE_V];
+        copyOutputBufferToYuvPlanarFrame(
+                dst, srcY, srcU, srcV,
+                srcYStride, srcUStride, srcVStride,
+                dstYStride, dstUVStride,
+                mWidth, mHeight);
+    }
+    finishWork(*(int64_t*)img->user_priv, work, std::move(block));
+    block = nullptr;
+    return true;
+}
+
+c2_status_t C2SoftAomDec::drainInternal(
+    uint32_t drainMode, const std::shared_ptr<C2BlockPool>& pool,
+    const std::unique_ptr<C2Work>& work) {
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    if (aom_codec_decode(mCodecCtx, nullptr, 0, nullptr)) {
+        ALOGE("Failed to flush av1 decoder.");
+        return C2_CORRUPTED;
+    }
+
+    while ((outputBuffer(pool, work))) {
+    }
+
+    if (drainMode == DRAIN_COMPONENT_WITH_EOS && work &&
+        work->workletsProcessed == 0u) {
+        fillEmptyWork(work);
+    }
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftAomDec::drain(uint32_t drainMode,
+                                const std::shared_ptr<C2BlockPool>& pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftAomFactory : public C2ComponentFactory {
+   public:
+    C2SoftAomFactory()
+        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+              GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+    virtual c2_status_t createComponent(
+        c2_node_id_t id, std::shared_ptr<C2Component>* const component,
+        std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+            new C2SoftAomDec(COMPONENT_NAME, id,
+                             std::make_shared<C2SoftAomDec::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+        c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+        std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+            new SimpleInterface<C2SoftAomDec::IntfImpl>(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftAomDec::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftAomFactory() override = default;
+
+   private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftAomFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/aom/C2SoftAomDec.h b/media/codec2/components/aom/C2SoftAomDec.h
new file mode 100644
index 0000000..4c82647
--- /dev/null
+++ b/media/codec2/components/aom/C2SoftAomDec.h
@@ -0,0 +1,137 @@
+/*
+ * 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_C2_SOFT_AV1_DEC_H_
+#define ANDROID_C2_SOFT_AV1_DEC_H_
+
+#include <SimpleC2Component.h>
+#include "aom/aom_decoder.h"
+#include "aom/aomdx.h"
+
+#define GETTIME(a, b) gettimeofday(a, b);
+#define TIME_DIFF(start, end, diff)     \
+    diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+            ((end).tv_usec - (start).tv_usec);
+
+namespace android {
+
+struct C2SoftAomDec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftAomDec(const char* name, c2_node_id_t id,
+                 const std::shared_ptr<IntfImpl>& intfImpl);
+    virtual ~C2SoftAomDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(const std::unique_ptr<C2Work>& work,
+                 const std::shared_ptr<C2BlockPool>& pool) override;
+    c2_status_t drain(uint32_t drainMode,
+                      const std::shared_ptr<C2BlockPool>& pool) override;
+
+   private:
+    std::shared_ptr<IntfImpl> mIntf;
+    aom_codec_ctx_t* mCodecCtx;
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+
+    #ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+    char mOutFile[200];
+    #endif /* FILE_DUMP_ENABLE */
+
+    struct timeval mTimeStart;   // Time at the start of decode()
+    struct timeval mTimeEnd;     // Time at the end of decode()
+
+    status_t initDecoder();
+    status_t destroyDecoder();
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work>& work,
+                    const std::shared_ptr<C2GraphicBlock>& block);
+    bool outputBuffer(const std::shared_ptr<C2BlockPool>& pool,
+                      const std::unique_ptr<C2Work>& work);
+
+    c2_status_t drainInternal(uint32_t drainMode,
+                              const std::shared_ptr<C2BlockPool>& pool,
+                              const std::unique_ptr<C2Work>& work);
+
+    C2_DO_NOT_COPY(C2SoftAomDec);
+};
+
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH "/data/local/tmp/temp/av1"
+#define INPUT_DUMP_EXT "webm"
+#define OUTPUT_DUMP_PATH "/data/local/tmp/temp/av1"
+#define OUTPUT_DUMP_EXT "av1"
+#define GENERATE_FILE_NAMES()                                                 \
+    {                                                                         \
+        GETTIME(&mTimeStart, NULL);                                           \
+        strcpy(mInFile, "");                                                  \
+        ALOGD("GENERATE_FILE_NAMES");                                         \
+        sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, mTimeStart.tv_sec, \
+                mTimeStart.tv_usec, INPUT_DUMP_EXT);                          \
+        strcpy(mOutFile, "");                                                 \
+        sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH,                  \
+                mTimeStart.tv_sec, mTimeStart.tv_usec, OUTPUT_DUMP_EXT);      \
+    }
+
+#define CREATE_DUMP_FILE(m_filename)                     \
+    {                                                    \
+        FILE* fp = fopen(m_filename, "wb");              \
+        if (fp != NULL) {                                \
+            ALOGD("Opened file %s", m_filename);         \
+            fclose(fp);                                  \
+        } else {                                         \
+            ALOGD("Could not open file %s", m_filename); \
+        }                                                \
+    }
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)              \
+    {                                                        \
+        FILE* fp = fopen(m_filename, "ab");                  \
+        if (fp != NULL && m_buf != NULL) {                   \
+            int i;                                           \
+            ALOGD("Dump to file!");                          \
+            i = fwrite(m_buf, 1, m_size, fp);                \
+            if (i != (int)m_size) {                          \
+                ALOGD("Error in fwrite, returned %d", i);    \
+                perror("Error in write to file");            \
+            }                                                \
+            fclose(fp);                                      \
+        } else {                                             \
+            ALOGD("Could not write to file %s", m_filename); \
+            if (fp != NULL) fclose(fp);                      \
+        }                                                    \
+    }
+#else /* FILE_DUMP_ENABLE */
+#define INPUT_DUMP_PATH
+#define INPUT_DUMP_EXT
+#define OUTPUT_DUMP_PATH
+#define OUTPUT_DUMP_EXT
+#define GENERATE_FILE_NAMES()
+#define CREATE_DUMP_FILE(m_filename)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_AV1_DEC_H_
diff --git a/media/codec2/components/avc/Android.bp b/media/codec2/components/avc/Android.bp
new file mode 100644
index 0000000..4021444
--- /dev/null
+++ b/media/codec2/components/avc/Android.bp
@@ -0,0 +1,37 @@
+cc_library_shared {
+    name: "libcodec2_soft_avcdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_signed-defaults",
+   ],
+
+    static_libs: ["libavcdec"],
+
+    srcs: ["C2SoftAvcDec.cpp"],
+
+    include_dirs: [
+        "external/libavc/decoder",
+        "external/libavc/common",
+    ],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_avcenc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_signed-defaults",
+   ],
+
+    static_libs: ["libavcenc"],
+
+    srcs: ["C2SoftAvcEnc.cpp"],
+
+    include_dirs: [
+        "external/libavc/encoder",
+        "external/libavc/common",
+    ],
+
+    cflags: [
+        "-Wno-unused-variable",
+    ],
+}
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
new file mode 100644
index 0000000..3f015b4
--- /dev/null
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -0,0 +1,1033 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftAvcDec"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <Codec2Mapper.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftAvcDec.h"
+#include "ih264d.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.avc.decoder";
+
+}  // namespace
+
+class C2SoftAvcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_VIDEO,
+                MEDIA_MIMETYPE_VIDEO_AVC) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+
+        // TODO: Proper support for reorder depth.
+        addParameter(
+                DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+                .withConstValue(new C2PortActualDelayTuning::output(8u))
+                .build());
+
+        // TODO: output latency and reordering
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        // coded and output picture size is the same for this codec
+        addParameter(
+                DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 4080, 2),
+                    C2F(mSize, height).inRange(2, 4080, 2),
+                })
+                .withSetter(SizeSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
+                .withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 4080, 2),
+                    C2F(mSize, height).inRange(2, 4080, 2),
+                })
+                .withSetter(MaxPictureSizeSetter, mSize)
+                .build());
+
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::input(0u,
+                        C2Config::PROFILE_AVC_CONSTRAINED_BASELINE, C2Config::LEVEL_AVC_5_2))
+                .withFields({
+                    C2F(mProfileLevel, profile).oneOf({
+                            C2Config::PROFILE_AVC_CONSTRAINED_BASELINE,
+                            C2Config::PROFILE_AVC_BASELINE,
+                            C2Config::PROFILE_AVC_MAIN,
+                            C2Config::PROFILE_AVC_CONSTRAINED_HIGH,
+                            C2Config::PROFILE_AVC_PROGRESSIVE_HIGH,
+                            C2Config::PROFILE_AVC_HIGH}),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_AVC_1, C2Config::LEVEL_AVC_1B, C2Config::LEVEL_AVC_1_1,
+                            C2Config::LEVEL_AVC_1_2, C2Config::LEVEL_AVC_1_3,
+                            C2Config::LEVEL_AVC_2, C2Config::LEVEL_AVC_2_1, C2Config::LEVEL_AVC_2_2,
+                            C2Config::LEVEL_AVC_3, C2Config::LEVEL_AVC_3_1, C2Config::LEVEL_AVC_3_2,
+                            C2Config::LEVEL_AVC_4, C2Config::LEVEL_AVC_4_1, C2Config::LEVEL_AVC_4_2,
+                            C2Config::LEVEL_AVC_5, C2Config::LEVEL_AVC_5_1, C2Config::LEVEL_AVC_5_2
+                    })
+                })
+                .withSetter(ProfileLevelSetter, mSize)
+                .build());
+
+        addParameter(
+                DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 320 * 240 * 3 / 4))
+                .withFields({
+                    C2F(mMaxInputSize, value).any(),
+                })
+                .calculatedAs(MaxInputSizeSetter, mMaxSize)
+                .build());
+
+        C2ChromaOffsetStruct locations[1] = { C2ChromaOffsetStruct::ITU_YUV_420_0() };
+        std::shared_ptr<C2StreamColorInfo::output> defaultColorInfo =
+            C2StreamColorInfo::output::AllocShared(
+                    1u, 0u, 8u /* bitDepth */, C2Color::YUV_420);
+        memcpy(defaultColorInfo->m.locations, locations, sizeof(locations));
+
+        defaultColorInfo =
+            C2StreamColorInfo::output::AllocShared(
+                    { C2ChromaOffsetStruct::ITU_YUV_420_0() },
+                    0u, 8u /* bitDepth */, C2Color::YUV_420);
+        helper->addStructDescriptors<C2ChromaOffsetStruct>();
+
+        addParameter(
+                DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
+                .withConstValue(defaultColorInfo)
+                .build());
+
+        addParameter(
+                DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsTuning::output(
+                        0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+                        C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({
+                    C2F(mDefaultColorAspects, range).inRange(
+                                C2Color::RANGE_UNSPECIFIED,     C2Color::RANGE_OTHER),
+                    C2F(mDefaultColorAspects, primaries).inRange(
+                                C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+                    C2F(mDefaultColorAspects, transfer).inRange(
+                                C2Color::TRANSFER_UNSPECIFIED,  C2Color::TRANSFER_OTHER),
+                    C2F(mDefaultColorAspects, matrix).inRange(
+                                C2Color::MATRIX_UNSPECIFIED,    C2Color::MATRIX_OTHER)
+                })
+                .withSetter(DefaultColorAspectsSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsInfo::input(
+                        0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
+                        C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({
+                    C2F(mCodedColorAspects, range).inRange(
+                                C2Color::RANGE_UNSPECIFIED,     C2Color::RANGE_OTHER),
+                    C2F(mCodedColorAspects, primaries).inRange(
+                                C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+                    C2F(mCodedColorAspects, transfer).inRange(
+                                C2Color::TRANSFER_UNSPECIFIED,  C2Color::TRANSFER_OTHER),
+                    C2F(mCodedColorAspects, matrix).inRange(
+                                C2Color::MATRIX_UNSPECIFIED,    C2Color::MATRIX_OTHER)
+                })
+                .withSetter(CodedColorAspectsSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsInfo::output(
+                        0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+                        C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({
+                    C2F(mColorAspects, range).inRange(
+                                C2Color::RANGE_UNSPECIFIED,     C2Color::RANGE_OTHER),
+                    C2F(mColorAspects, primaries).inRange(
+                                C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+                    C2F(mColorAspects, transfer).inRange(
+                                C2Color::TRANSFER_UNSPECIFIED,  C2Color::TRANSFER_OTHER),
+                    C2F(mColorAspects, matrix).inRange(
+                                C2Color::MATRIX_UNSPECIFIED,    C2Color::MATRIX_OTHER)
+                })
+                .withSetter(ColorAspectsSetter, mDefaultColorAspects, mCodedColorAspects)
+                .build());
+
+        // TODO: support more formats?
+        addParameter(
+                DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+                .withConstValue(new C2StreamPixelFormatInfo::output(
+                                     0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+                .build());
+    }
+
+    static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output> &oldMe,
+                          C2P<C2StreamPictureSizeInfo::output> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R MaxPictureSizeSetter(bool mayBlock, C2P<C2StreamMaxPictureSizeTuning::output> &me,
+                                    const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        // TODO: get max width/height from the size's field helpers vs. hardcoding
+        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 4080u);
+        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 4080u);
+        return C2R::Ok();
+    }
+
+    static C2R MaxInputSizeSetter(bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input> &me,
+                                  const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
+        (void)mayBlock;
+        // assume compression ratio of 2
+        me.set().value = (((maxSize.v.width + 15) / 16) * ((maxSize.v.height + 15) / 16) * 192);
+        return C2R::Ok();
+    }
+
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input> &me,
+                                  const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        (void)size;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+    static C2R DefaultColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsTuning::output> &me) {
+        (void)mayBlock;
+        if (me.v.range > C2Color::RANGE_OTHER) {
+                me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+                me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+                me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+                me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R CodedColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input> &me) {
+        (void)mayBlock;
+        if (me.v.range > C2Color::RANGE_OTHER) {
+                me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+                me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+                me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+                me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output> &me,
+                                  const C2P<C2StreamColorAspectsTuning::output> &def,
+                                  const C2P<C2StreamColorAspectsInfo::input> &coded) {
+        (void)mayBlock;
+        // take default values for all unspecified fields, and coded values for specified ones
+        me.set().range = coded.v.range == RANGE_UNSPECIFIED ? def.v.range : coded.v.range;
+        me.set().primaries = coded.v.primaries == PRIMARIES_UNSPECIFIED
+                ? def.v.primaries : coded.v.primaries;
+        me.set().transfer = coded.v.transfer == TRANSFER_UNSPECIFIED
+                ? def.v.transfer : coded.v.transfer;
+        me.set().matrix = coded.v.matrix == MATRIX_UNSPECIFIED ? def.v.matrix : coded.v.matrix;
+        return C2R::Ok();
+    }
+
+    std::shared_ptr<C2StreamColorAspectsInfo::output> getColorAspects_l() {
+        return mColorAspects;
+    }
+
+private:
+    std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+    std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
+    std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mMaxInputSize;
+    std::shared_ptr<C2StreamColorInfo::output> mColorInfo;
+    std::shared_ptr<C2StreamColorAspectsInfo::input> mCodedColorAspects;
+    std::shared_ptr<C2StreamColorAspectsTuning::output> mDefaultColorAspects;
+    std::shared_ptr<C2StreamColorAspectsInfo::output> mColorAspects;
+    std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
+};
+
+static size_t getCpuCoreCount() {
+    long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGV("Number of CPU cores: %ld", cpuCoreCount);
+    return (size_t)cpuCoreCount;
+}
+
+static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
+    (void) ctxt;
+    return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *ctxt, void *mem) {
+    (void) ctxt;
+    free(mem);
+}
+
+C2SoftAvcDec::C2SoftAvcDec(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mDecHandle(nullptr),
+      mOutBufferFlush(nullptr),
+      mIvColorFormat(IV_YUV_420P),
+      mWidth(320),
+      mHeight(240),
+      mHeaderDecoded(false),
+      mOutIndex(0u) {
+    GENERATE_FILE_NAMES();
+    CREATE_DUMP_FILE(mInFile);
+}
+
+C2SoftAvcDec::~C2SoftAvcDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftAvcDec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftAvcDec::onStop() {
+    if (OK != resetDecoder()) return C2_CORRUPTED;
+    resetPlugin();
+    return C2_OK;
+}
+
+void C2SoftAvcDec::onReset() {
+    (void) onStop();
+}
+
+void C2SoftAvcDec::onRelease() {
+    (void) deleteDecoder();
+    if (mOutBufferFlush) {
+        ivd_aligned_free(nullptr, mOutBufferFlush);
+        mOutBufferFlush = nullptr;
+    }
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
+}
+
+c2_status_t C2SoftAvcDec::onFlush_sm() {
+    if (OK != setFlushMode()) return C2_CORRUPTED;
+
+    uint32_t bufferSize = mStride * mHeight * 3 / 2;
+    mOutBufferFlush = (uint8_t *)ivd_aligned_malloc(nullptr, 128, bufferSize);
+    if (!mOutBufferFlush) {
+        ALOGE("could not allocate tmp output buffer (for flush) of size %u ", bufferSize);
+        return C2_NO_MEMORY;
+    }
+
+    while (true) {
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+
+        setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, nullptr, 0, 0, 0);
+        (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        if (0 == s_decode_op.u4_output_present) {
+            resetPlugin();
+            break;
+        }
+    }
+
+    if (mOutBufferFlush) {
+        ivd_aligned_free(nullptr, mOutBufferFlush);
+        mOutBufferFlush = nullptr;
+    }
+
+    return C2_OK;
+}
+
+status_t C2SoftAvcDec::createDecoder() {
+    ivdext_create_ip_t s_create_ip;
+    ivdext_create_op_t s_create_op;
+
+    s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
+    s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
+    s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
+    s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorFormat;
+    s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc;
+    s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free;
+    s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = nullptr;
+    s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(nullptr,
+                                                     &s_create_ip,
+                                                     &s_create_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__,
+              s_create_op.s_ivd_create_op_t.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+    mDecHandle = (iv_obj_t*)s_create_op.s_ivd_create_op_t.pv_handle;
+    mDecHandle->pv_fxns = (void *)ivdec_api_function;
+    mDecHandle->u4_size = sizeof(iv_obj_t);
+
+    return OK;
+}
+
+status_t C2SoftAvcDec::setNumCores() {
+    ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip;
+    ivdext_ctl_set_num_cores_op_t s_set_num_cores_op;
+
+    s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+    s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+    s_set_num_cores_ip.u4_num_cores = mNumCores;
+    s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_num_cores_ip,
+                                                     &s_set_num_cores_op);
+    if (IV_SUCCESS != status) {
+        ALOGD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftAvcDec::setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode) {
+    ivd_ctl_set_config_ip_t s_set_dyn_params_ip;
+    ivd_ctl_set_config_op_t s_set_dyn_params_op;
+
+    s_set_dyn_params_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
+    s_set_dyn_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_dyn_params_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+    s_set_dyn_params_ip.u4_disp_wd = (UWORD32) stride;
+    s_set_dyn_params_ip.e_frm_skip_mode = IVD_SKIP_NONE;
+    s_set_dyn_params_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+    s_set_dyn_params_ip.e_vid_dec_mode = dec_mode;
+    s_set_dyn_params_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_dyn_params_ip,
+                                                     &s_set_dyn_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__, s_set_dyn_params_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+void C2SoftAvcDec::getVersion() {
+    ivd_ctl_getversioninfo_ip_t s_get_versioninfo_ip;
+    ivd_ctl_getversioninfo_op_t s_get_versioninfo_op;
+    UWORD8 au1_buf[512];
+
+    s_get_versioninfo_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
+    s_get_versioninfo_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_get_versioninfo_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
+    s_get_versioninfo_ip.pv_version_buffer = au1_buf;
+    s_get_versioninfo_ip.u4_version_buffer_size = sizeof(au1_buf);
+    s_get_versioninfo_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_get_versioninfo_ip,
+                                                     &s_get_versioninfo_op);
+    if (status != IV_SUCCESS) {
+        ALOGD("error in %s: 0x%x", __func__,
+              s_get_versioninfo_op.u4_error_code);
+    } else {
+        ALOGV("ittiam decoder version number: %s",
+              (char *) s_get_versioninfo_ip.pv_version_buffer);
+    }
+}
+
+status_t C2SoftAvcDec::initDecoder() {
+    if (OK != createDecoder()) return UNKNOWN_ERROR;
+    mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
+    mStride = ALIGN64(mWidth);
+    mSignalledError = false;
+    resetPlugin();
+    (void) setNumCores();
+    if (OK != setParams(mStride, IVD_DECODE_FRAME)) return UNKNOWN_ERROR;
+    (void) getVersion();
+
+    return OK;
+}
+
+bool C2SoftAvcDec::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                                 ivd_video_decode_op_t *ps_decode_op,
+                                 C2ReadView *inBuffer,
+                                 C2GraphicView *outBuffer,
+                                 size_t inOffset,
+                                 size_t inSize,
+                                 uint32_t tsMarker) {
+    uint32_t displayStride = mStride;
+    uint32_t displayHeight = mHeight;
+    size_t lumaSize = displayStride * displayHeight;
+    size_t chromaSize = lumaSize >> 2;
+
+    ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+    ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+    if (inBuffer) {
+        ps_decode_ip->u4_ts = tsMarker;
+        ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer->data() + inOffset);
+        ps_decode_ip->u4_num_Bytes = inSize;
+    } else {
+        ps_decode_ip->u4_ts = 0;
+        ps_decode_ip->pv_stream_buffer = nullptr;
+        ps_decode_ip->u4_num_Bytes = 0;
+    }
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
+    if (outBuffer) {
+        if (outBuffer->width() < displayStride || outBuffer->height() < displayHeight) {
+            ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
+                  outBuffer->width(), outBuffer->height(), displayStride, displayHeight);
+            return false;
+        }
+        ps_decode_ip->s_out_buffer.pu1_bufs[0] = outBuffer->data()[C2PlanarLayout::PLANE_Y];
+        ps_decode_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[C2PlanarLayout::PLANE_U];
+        ps_decode_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[C2PlanarLayout::PLANE_V];
+    } else {
+        ps_decode_ip->s_out_buffer.pu1_bufs[0] = mOutBufferFlush;
+        ps_decode_ip->s_out_buffer.pu1_bufs[1] = mOutBufferFlush + lumaSize;
+        ps_decode_ip->s_out_buffer.pu1_bufs[2] = mOutBufferFlush + lumaSize + chromaSize;
+    }
+    ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
+    ps_decode_op->u4_size = sizeof(ivd_video_decode_op_t);
+
+    return true;
+}
+
+bool C2SoftAvcDec::getVuiParams() {
+    ivdext_ctl_get_vui_params_ip_t s_get_vui_params_ip;
+    ivdext_ctl_get_vui_params_op_t s_get_vui_params_op;
+
+    s_get_vui_params_ip.u4_size = sizeof(ivdext_ctl_get_vui_params_ip_t);
+    s_get_vui_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_get_vui_params_ip.e_sub_cmd =
+            (IVD_CONTROL_API_COMMAND_TYPE_T) IH264D_CMD_CTL_GET_VUI_PARAMS;
+    s_get_vui_params_op.u4_size = sizeof(ivdext_ctl_get_vui_params_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_get_vui_params_ip,
+                                                     &s_get_vui_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGD("error in %s: 0x%x", __func__, s_get_vui_params_op.u4_error_code);
+        return false;
+    }
+
+    VuiColorAspects vuiColorAspects;
+    vuiColorAspects.primaries = s_get_vui_params_op.u1_colour_primaries;
+    vuiColorAspects.transfer = s_get_vui_params_op.u1_tfr_chars;
+    vuiColorAspects.coeffs = s_get_vui_params_op.u1_matrix_coeffs;
+    vuiColorAspects.fullRange = s_get_vui_params_op.u1_video_full_range_flag;
+
+    // convert vui aspects to C2 values if changed
+    if (!(vuiColorAspects == mBitstreamColorAspects)) {
+        mBitstreamColorAspects = vuiColorAspects;
+        ColorAspects sfAspects;
+        C2StreamColorAspectsInfo::input codedAspects = { 0u };
+        ColorUtils::convertIsoColorAspectsToCodecAspects(
+                vuiColorAspects.primaries, vuiColorAspects.transfer, vuiColorAspects.coeffs,
+                vuiColorAspects.fullRange, sfAspects);
+        if (!C2Mapper::map(sfAspects.mPrimaries, &codedAspects.primaries)) {
+            codedAspects.primaries = C2Color::PRIMARIES_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mRange, &codedAspects.range)) {
+            codedAspects.range = C2Color::RANGE_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mMatrixCoeffs, &codedAspects.matrix)) {
+            codedAspects.matrix = C2Color::MATRIX_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mTransfer, &codedAspects.transfer)) {
+            codedAspects.transfer = C2Color::TRANSFER_UNSPECIFIED;
+        }
+        std::vector<std::unique_ptr<C2SettingResult>> failures;
+        (void)mIntf->config({&codedAspects}, C2_MAY_BLOCK, &failures);
+    }
+    return true;
+}
+
+status_t C2SoftAvcDec::setFlushMode() {
+    ivd_ctl_flush_ip_t s_set_flush_ip;
+    ivd_ctl_flush_op_t s_set_flush_op;
+
+    s_set_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
+    s_set_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
+    s_set_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_flush_ip,
+                                                     &s_set_flush_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__, s_set_flush_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftAvcDec::resetDecoder() {
+    ivd_ctl_reset_ip_t s_reset_ip;
+    ivd_ctl_reset_op_t s_reset_op;
+
+    s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+    s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+    s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_reset_ip,
+                                                     &s_reset_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+    mStride = 0;
+    (void) setNumCores();
+    mSignalledError = false;
+    mHeaderDecoded = false;
+
+    return OK;
+}
+
+void C2SoftAvcDec::resetPlugin() {
+    mSignalledOutputEos = false;
+    gettimeofday(&mTimeStart, nullptr);
+    gettimeofday(&mTimeEnd, nullptr);
+}
+
+status_t C2SoftAvcDec::deleteDecoder() {
+    if (mDecHandle) {
+        ivdext_delete_ip_t s_delete_ip;
+        ivdext_delete_op_t s_delete_op;
+
+        s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
+        s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;
+        s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t);
+        IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                         &s_delete_ip,
+                                                         &s_delete_op);
+        if (status != IV_SUCCESS) {
+            ALOGE("error in %s: 0x%x", __func__,
+                  s_delete_op.s_ivd_delete_op_t.u4_error_code);
+            return UNKNOWN_ERROR;
+        }
+        mDecHandle = nullptr;
+    }
+
+    return OK;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftAvcDec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work) {
+    std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mOutBlock),
+                                                           C2Rect(mWidth, mHeight));
+    mOutBlock = nullptr;
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        buffer->setInfo(mIntf->getColorAspects_l());
+    }
+
+    class FillWork {
+       public:
+        FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+                 const std::shared_ptr<C2Buffer>& buffer)
+            : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {}
+        ~FillWork() = default;
+
+        void operator()(const std::unique_ptr<C2Work>& work) {
+            work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+            work->worklets.front()->output.buffers.clear();
+            work->worklets.front()->output.ordinal = mOrdinal;
+            work->workletsProcessed = 1u;
+            work->result = C2_OK;
+            if (mBuffer) {
+                work->worklets.front()->output.buffers.push_back(mBuffer);
+            }
+            ALOGV("timestamp = %lld, index = %lld, w/%s buffer",
+                  mOrdinal.timestamp.peekll(), mOrdinal.frameIndex.peekll(),
+                  mBuffer ? "" : "o");
+        }
+
+       private:
+        const uint32_t mFlags;
+        const C2WorkOrdinalStruct mOrdinal;
+        const std::shared_ptr<C2Buffer> mBuffer;
+    };
+
+    auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+        // TODO: Check if cloneAndSend can be avoided by tracking number of frames remaining
+        if (eos) {
+            if (buffer) {
+                mOutIndex = index;
+                C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+                cloneAndSend(
+                    mOutIndex, work,
+                    FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+                buffer.reset();
+            }
+        } else {
+            fillWork(work);
+        }
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+c2_status_t C2SoftAvcDec::ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool) {
+    if (!mDecHandle) {
+        ALOGE("not supposed to be here, invalid decoder context");
+        return C2_CORRUPTED;
+    }
+    if (mStride != ALIGN64(mWidth)) {
+        mStride = ALIGN64(mWidth);
+        if (OK != setParams(mStride, IVD_DECODE_FRAME)) return C2_CORRUPTED;
+    }
+    if (mOutBlock &&
+            (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+        mOutBlock.reset();
+    }
+    if (!mOutBlock) {
+        uint32_t format = HAL_PIXEL_FORMAT_YV12;
+        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+        if (err != C2_OK) {
+            ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+            return err;
+        }
+        ALOGV("provided (%dx%d) required (%dx%d)",
+              mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+    }
+
+    return C2_OK;
+}
+
+// TODO: can overall error checking be improved?
+// TODO: allow configuration of color format and usage for graphic buffers instead
+//       of hard coding them to HAL_PIXEL_FORMAT_YV12
+// TODO: pass coloraspects information to surface
+// TODO: test support for dynamic change in resolution
+// TODO: verify if the decoder sent back all frames
+void C2SoftAvcDec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    work->worklets.front()->output.flags = work->input.flags;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = rView.error();
+            return;
+        }
+    }
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    bool hasPicture = false;
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+    size_t inPos = 0;
+    while (inPos < inSize) {
+        if (C2_OK != ensureDecoderState(pool)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+        {
+            C2GraphicView wView = mOutBlock->map().get();
+            if (wView.error()) {
+                ALOGE("graphic view map failed %d", wView.error());
+                work->result = wView.error();
+                return;
+            }
+            if (!setDecodeArgs(&s_decode_ip, &s_decode_op, &rView, &wView,
+                               inOffset + inPos, inSize - inPos, workIndex)) {
+                mSignalledError = true;
+                work->workletsProcessed = 1u;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+
+            if (false == mHeaderDecoded) {
+                /* Decode header and get dimensions */
+                setParams(mStride, IVD_DECODE_HEADER);
+            }
+
+            WORD32 delay;
+            GETTIME(&mTimeStart, nullptr);
+            TIME_DIFF(mTimeEnd, mTimeStart, delay);
+            (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+            WORD32 decodeTime;
+            GETTIME(&mTimeEnd, nullptr);
+            TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
+            ALOGV("decodeTime=%6d delay=%6d numBytes=%6d", decodeTime, delay,
+                  s_decode_op.u4_num_bytes_consumed);
+        }
+        if (IVD_MEM_ALLOC_FAILED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+            ALOGE("allocation failure in decoder");
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        } else if (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+            ALOGE("unsupported resolution : %dx%d", mWidth, mHeight);
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        } else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+            ALOGV("resolution changed");
+            drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+            resetDecoder();
+            resetPlugin();
+            work->workletsProcessed = 0u;
+
+            /* Decode header and get new dimensions */
+            setParams(mStride, IVD_DECODE_HEADER);
+            (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        } else if (IS_IVD_FATAL_ERROR(s_decode_op.u4_error_code)) {
+            ALOGE("Fatal error in decoder 0x%x", s_decode_op.u4_error_code);
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
+            if (mHeaderDecoded == false) {
+                mHeaderDecoded = true;
+                setParams(ALIGN64(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+            }
+            if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
+                mWidth = s_decode_op.u4_pic_wd;
+                mHeight = s_decode_op.u4_pic_ht;
+                CHECK_EQ(0u, s_decode_op.u4_output_present);
+
+                C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+                std::vector<std::unique_ptr<C2SettingResult>> failures;
+                c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+                if (err == OK) {
+                    work->worklets.front()->output.configUpdate.push_back(
+                        C2Param::Copy(size));
+                } else {
+                    ALOGE("Cannot set width and height");
+                    mSignalledError = true;
+                    work->workletsProcessed = 1u;
+                    work->result = C2_CORRUPTED;
+                    return;
+                }
+                continue;
+            }
+        }
+        (void)getVuiParams();
+        hasPicture |= (1 == s_decode_op.u4_frame_decoded_flag);
+        if (s_decode_op.u4_output_present) {
+            finishWork(s_decode_op.u4_ts, work);
+        }
+        if (0 == s_decode_op.u4_num_bytes_consumed) {
+            ALOGD("Bytes consumed is zero. Ignoring remaining bytes");
+            break;
+        }
+        inPos += s_decode_op.u4_num_bytes_consumed;
+        if (hasPicture && (inSize - inPos)) {
+            ALOGD("decoded frame in current access nal, ignoring further trailing bytes %d",
+                  (int)inSize - (int)inPos);
+            break;
+        }
+    }
+    if (eos) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+        mSignalledOutputEos = true;
+    } else if (!hasPicture) {
+        fillEmptyWork(work);
+    }
+
+    work->input.buffers.clear();
+}
+
+c2_status_t C2SoftAvcDec::drainInternal(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work) {
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    if (OK != setFlushMode()) return C2_CORRUPTED;
+    while (true) {
+        if (C2_OK != ensureDecoderState(pool)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return C2_CORRUPTED;
+        }
+        C2GraphicView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            return C2_CORRUPTED;
+        }
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+        if (!setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, &wView, 0, 0, 0)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            return C2_CORRUPTED;
+        }
+        (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        if (s_decode_op.u4_output_present) {
+            finishWork(s_decode_op.u4_ts, work);
+        } else {
+            fillEmptyWork(work);
+            break;
+        }
+    }
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcDec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftAvcDecFactory : public C2ComponentFactory {
+public:
+    C2SoftAvcDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+        GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftAvcDec(COMPONENT_NAME,
+                                 id,
+                                 std::make_shared<C2SoftAvcDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftAvcDec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftAvcDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftAvcDecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftAvcDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/avc/C2SoftAvcDec.h b/media/codec2/components/avc/C2SoftAvcDec.h
new file mode 100644
index 0000000..72ee583
--- /dev/null
+++ b/media/codec2/components/avc/C2SoftAvcDec.h
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_C2_SOFT_AVC_DEC_H_
+#define ANDROID_C2_SOFT_AVC_DEC_H_
+
+#include <sys/time.h>
+
+#include <media/stagefright/foundation/ColorUtils.h>
+
+#include <atomic>
+#include <SimpleC2Component.h>
+
+#include "ih264_typedefs.h"
+#include "iv.h"
+#include "ivd.h"
+
+namespace android {
+
+#define ivdec_api_function              ih264d_api_function
+#define ivdext_create_ip_t              ih264d_create_ip_t
+#define ivdext_create_op_t              ih264d_create_op_t
+#define ivdext_delete_ip_t              ih264d_delete_ip_t
+#define ivdext_delete_op_t              ih264d_delete_op_t
+#define ivdext_ctl_set_num_cores_ip_t   ih264d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t   ih264d_ctl_set_num_cores_op_t
+#define ivdext_ctl_get_vui_params_ip_t  ih264d_ctl_get_vui_params_ip_t
+#define ivdext_ctl_get_vui_params_op_t  ih264d_ctl_get_vui_params_op_t
+#define ALIGN64(x)                      ((((x) + 63) >> 6) << 6)
+#define MAX_NUM_CORES                   4
+#define IVDEXT_CMD_CTL_SET_NUM_CORES    \
+        (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
+#define MIN(a, b)                       (((a) < (b)) ? (a) : (b))
+#define GETTIME(a, b)                   gettimeofday(a, b);
+#define TIME_DIFF(start, end, diff)     \
+    diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+            ((end).tv_usec - (start).tv_usec);
+
+#ifdef FILE_DUMP_ENABLE
+    #define INPUT_DUMP_PATH     "/sdcard/clips/avcd_input"
+    #define INPUT_DUMP_EXT      "h264"
+    #define GENERATE_FILE_NAMES() {                         \
+        GETTIME(&mTimeStart, NULL);                         \
+        strcpy(mInFile, "");                                \
+        sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH,  \
+                mTimeStart.tv_sec, mTimeStart.tv_usec,      \
+                INPUT_DUMP_EXT);                            \
+    }
+    #define CREATE_DUMP_FILE(m_filename) {                  \
+        FILE *fp = fopen(m_filename, "wb");                 \
+        if (fp != NULL) {                                   \
+            fclose(fp);                                     \
+        } else {                                            \
+            ALOGD("Could not open file %s", m_filename);    \
+        }                                                   \
+    }
+    #define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset)\
+    {                                                       \
+        FILE *fp = fopen(m_filename, "ab");                 \
+        if (fp != NULL && m_buf != NULL && m_offset == 0) { \
+            int i;                                          \
+            i = fwrite(m_buf, 1, m_size, fp);               \
+            ALOGD("fwrite ret %d to write %d", i, m_size);  \
+            if (i != (int) m_size) {                        \
+                ALOGD("Error in fwrite, returned %d", i);   \
+                perror("Error in write to file");           \
+            }                                               \
+        } else if (fp == NULL) {                            \
+            ALOGD("Could not write to file %s", m_filename);\
+        }                                                   \
+        if (fp) {                                           \
+            fclose(fp);                                     \
+        }                                                   \
+    }
+#else /* FILE_DUMP_ENABLE */
+    #define INPUT_DUMP_PATH
+    #define INPUT_DUMP_EXT
+    #define OUTPUT_DUMP_PATH
+    #define OUTPUT_DUMP_EXT
+    #define GENERATE_FILE_NAMES()
+    #define CREATE_DUMP_FILE(m_filename)
+    #define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset)
+#endif /* FILE_DUMP_ENABLE */
+
+
+class C2SoftAvcDec : public SimpleC2Component {
+public:
+    class IntfImpl;
+    C2SoftAvcDec(const char *name, c2_node_id_t id, const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftAvcDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    status_t createDecoder();
+    status_t setNumCores();
+    status_t setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode);
+    void getVersion();
+    status_t initDecoder();
+    bool setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                       ivd_video_decode_op_t *ps_decode_op,
+                       C2ReadView *inBuffer,
+                       C2GraphicView *outBuffer,
+                       size_t inOffset,
+                       size_t inSize,
+                       uint32_t tsMarker);
+    bool getVuiParams();
+    c2_status_t ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool);
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work);
+    status_t setFlushMode();
+    c2_status_t drainInternal(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool,
+            const std::unique_ptr<C2Work> &work);
+    status_t resetDecoder();
+    void resetPlugin();
+    status_t deleteDecoder();
+
+    std::shared_ptr<IntfImpl> mIntf;
+
+    // TODO:This is not the right place for this enum. These should
+    // be part of c2-vndk so that they can be accessed by all video plugins
+    // until then, make them feel at home
+    enum {
+        kNotSupported,
+        kPreferBitstream,
+        kPreferContainer,
+    };
+
+    iv_obj_t *mDecHandle;
+    std::shared_ptr<C2GraphicBlock> mOutBlock;
+    uint8_t *mOutBufferFlush;
+
+    size_t mNumCores;
+    IV_COLOR_FORMAT_T mIvColorFormat;
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mStride;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+    bool mHeaderDecoded;
+    std::atomic_uint64_t mOutIndex;
+    // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
+    // converting them to C2 values for each frame
+    struct VuiColorAspects {
+        uint8_t primaries;
+        uint8_t transfer;
+        uint8_t coeffs;
+        uint8_t fullRange;
+
+        // default color aspects
+        VuiColorAspects()
+            : primaries(2), transfer(2), coeffs(2), fullRange(0) { }
+
+        bool operator==(const VuiColorAspects &o) {
+            return primaries == o.primaries && transfer == o.transfer && coeffs == o.coeffs
+                    && fullRange == o.fullRange;
+        }
+    } mBitstreamColorAspects;
+
+    // profile
+    struct timeval mTimeStart;
+    struct timeval mTimeEnd;
+#ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+    C2_DO_NOT_COPY(C2SoftAvcDec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_AVC_DEC_H_
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
new file mode 100644
index 0000000..b41c271
--- /dev/null
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -0,0 +1,1765 @@
+/*
+ * 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 "C2SoftAvcEnc"
+#include <log/log.h>
+#include <utils/misc.h>
+
+#include <media/hardware/VideoAPI.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/foundation/AUtils.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <Codec2BufferUtils.h>
+#include <SimpleC2Interface.h>
+#include <util/C2InterfaceHelper.h>
+
+#include "C2SoftAvcEnc.h"
+#include "ih264e.h"
+#include "ih264e_error.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.avc.encoder";
+
+void ParseGop(
+        const C2StreamGopTuning::output &gop,
+        uint32_t *syncInterval, uint32_t *iInterval, uint32_t *maxBframes) {
+    uint32_t syncInt = 1;
+    uint32_t iInt = 1;
+    for (size_t i = 0; i < gop.flexCount(); ++i) {
+        const C2GopLayerStruct &layer = gop.m.values[i];
+        if (layer.count == UINT32_MAX) {
+            syncInt = 0;
+        } else if (syncInt <= UINT32_MAX / (layer.count + 1)) {
+            syncInt *= (layer.count + 1);
+        }
+        if ((layer.type_ & I_FRAME) == 0) {
+            if (layer.count == UINT32_MAX) {
+                iInt = 0;
+            } else if (iInt <= UINT32_MAX / (layer.count + 1)) {
+                iInt *= (layer.count + 1);
+            }
+        }
+        if (layer.type_ == C2Config::picture_type_t(P_FRAME | B_FRAME) && maxBframes) {
+            *maxBframes = layer.count;
+        }
+    }
+    if (syncInterval) {
+        *syncInterval = syncInt;
+    }
+    if (iInterval) {
+        *iInterval = iInt;
+    }
+}
+
+}  // namespace
+
+class C2SoftAvcEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_VIDEO,
+                MEDIA_MIMETYPE_VIDEO_AVC) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
+                .withConstValue(new C2StreamUsageTuning::input(
+                        0u, (uint64_t)C2MemoryUsage::CPU_READ))
+                .build());
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 2560, 2),
+                    C2F(mSize, height).inRange(2, 2560, 2),
+                })
+                .withSetter(SizeSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mGop, C2_PARAMKEY_GOP)
+                .withDefault(C2StreamGopTuning::output::AllocShared(
+                        0 /* flexCount */, 0u /* stream */))
+                .withFields({C2F(mGop, m.values[0].type_).any(),
+                             C2F(mGop, m.values[0].count).any()})
+                .withSetter(GopSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY)
+                .withDefault(new C2PortActualDelayTuning::input(DEFAULT_B_FRAMES))
+                .withFields({C2F(mActualInputDelay, value).inRange(0, MAX_B_FRAMES)})
+                .calculatedAs(InputDelaySetter, mGop)
+                .build());
+
+        addParameter(
+                DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
+                .withDefault(new C2StreamFrameRateInfo::output(0u, 30.))
+                // TODO: More restriction?
+                .withFields({C2F(mFrameRate, value).greaterThan(0.)})
+                .withSetter(Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::output(0u, 64000))
+                .withFields({C2F(mBitrate, value).inRange(4096, 12000000)})
+                .withSetter(BitrateSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mIntraRefresh, C2_PARAMKEY_INTRA_REFRESH)
+                .withDefault(new C2StreamIntraRefreshTuning::output(
+                        0u, C2Config::INTRA_REFRESH_DISABLED, 0.))
+                .withFields({
+                    C2F(mIntraRefresh, mode).oneOf({
+                        C2Config::INTRA_REFRESH_DISABLED, C2Config::INTRA_REFRESH_ARBITRARY }),
+                    C2F(mIntraRefresh, period).any()
+                })
+                .withSetter(IntraRefreshSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::output(
+                        0u, PROFILE_AVC_CONSTRAINED_BASELINE, LEVEL_AVC_4_1))
+                .withFields({
+                    C2F(mProfileLevel, profile).oneOf({
+                        PROFILE_AVC_BASELINE,
+                        PROFILE_AVC_CONSTRAINED_BASELINE,
+                        PROFILE_AVC_MAIN,
+                    }),
+                    C2F(mProfileLevel, level).oneOf({
+                        LEVEL_AVC_1,
+                        LEVEL_AVC_1B,
+                        LEVEL_AVC_1_1,
+                        LEVEL_AVC_1_2,
+                        LEVEL_AVC_1_3,
+                        LEVEL_AVC_2,
+                        LEVEL_AVC_2_1,
+                        LEVEL_AVC_2_2,
+                        LEVEL_AVC_3,
+                        LEVEL_AVC_3_1,
+                        LEVEL_AVC_3_2,
+                        LEVEL_AVC_4,
+                        LEVEL_AVC_4_1,
+                        LEVEL_AVC_4_2,
+                        LEVEL_AVC_5,
+                    }),
+                })
+                .withSetter(ProfileLevelSetter, mSize, mFrameRate, mBitrate)
+                .build());
+
+        addParameter(
+                DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
+                .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
+                .withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) })
+                .withSetter(Setter<decltype(*mRequestSync)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL)
+                .withDefault(new C2StreamSyncFrameIntervalTuning::output(0u, 1000000))
+                .withFields({C2F(mSyncFramePeriod, value).any()})
+                .withSetter(Setter<decltype(*mSyncFramePeriod)>::StrictValueWithNoDeps)
+                .build());
+    }
+
+    static C2R InputDelaySetter(
+            bool mayBlock,
+            C2P<C2PortActualDelayTuning::input> &me,
+            const C2P<C2StreamGopTuning::output> &gop) {
+        (void)mayBlock;
+        uint32_t maxBframes = 0;
+        ParseGop(gop.v, nullptr, nullptr, &maxBframes);
+        me.set().value = maxBframes;
+        return C2R::Ok();
+    }
+
+    static C2R BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (me.v.value <= 4096) {
+            me.set().value = 4096;
+        }
+        return res;
+    }
+
+    static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input> &oldMe,
+                          C2P<C2StreamPictureSizeInfo::input> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R ProfileLevelSetter(
+            bool mayBlock,
+            C2P<C2StreamProfileLevelInfo::output> &me,
+            const C2P<C2StreamPictureSizeInfo::input> &size,
+            const C2P<C2StreamFrameRateInfo::output> &frameRate,
+            const C2P<C2StreamBitrateInfo::output> &bitrate) {
+        (void)mayBlock;
+        if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
+            me.set().profile = PROFILE_AVC_CONSTRAINED_BASELINE;
+        }
+
+        struct LevelLimits {
+            C2Config::level_t level;
+            float mbsPerSec;
+            uint64_t mbs;
+            uint32_t bitrate;
+        };
+        constexpr LevelLimits kLimits[] = {
+            { LEVEL_AVC_1,     1485,    99,     64000 },
+            // Decoder does not properly handle level 1b.
+            // { LEVEL_AVC_1B,    1485,   99,   128000 },
+            { LEVEL_AVC_1_1,   3000,   396,    192000 },
+            { LEVEL_AVC_1_2,   6000,   396,    384000 },
+            { LEVEL_AVC_1_3,  11880,   396,    768000 },
+            { LEVEL_AVC_2,    11880,   396,   2000000 },
+            { LEVEL_AVC_2_1,  19800,   792,   4000000 },
+            { LEVEL_AVC_2_2,  20250,  1620,   4000000 },
+            { LEVEL_AVC_3,    40500,  1620,  10000000 },
+            { LEVEL_AVC_3_1, 108000,  3600,  14000000 },
+            { LEVEL_AVC_3_2, 216000,  5120,  20000000 },
+            { LEVEL_AVC_4,   245760,  8192,  20000000 },
+            { LEVEL_AVC_4_1, 245760,  8192,  50000000 },
+            { LEVEL_AVC_4_2, 522240,  8704,  50000000 },
+            { LEVEL_AVC_5,   589824, 22080, 135000000 },
+        };
+
+        uint64_t mbs = uint64_t((size.v.width + 15) / 16) * ((size.v.height + 15) / 16);
+        float mbsPerSec = float(mbs) * frameRate.v.value;
+
+        // Check if the supplied level meets the MB / bitrate requirements. If
+        // not, update the level with the lowest level meeting the requirements.
+
+        bool found = false;
+        // By default needsUpdate = false in case the supplied level does meet
+        // the requirements. For Level 1b, we want to update the level anyway,
+        // so we set it to true in that case.
+        bool needsUpdate = (me.v.level == LEVEL_AVC_1B);
+        for (const LevelLimits &limit : kLimits) {
+            if (mbs <= limit.mbs && mbsPerSec <= limit.mbsPerSec &&
+                    bitrate.v.value <= limit.bitrate) {
+                // This is the lowest level that meets the requirements, and if
+                // we haven't seen the supplied level yet, that means we don't
+                // need the update.
+                if (needsUpdate) {
+                    ALOGD("Given level %x does not cover current configuration: "
+                          "adjusting to %x", me.v.level, limit.level);
+                    me.set().level = limit.level;
+                }
+                found = true;
+                break;
+            }
+            if (me.v.level == limit.level) {
+                // We break out of the loop when the lowest feasible level is
+                // found. The fact that we're here means that our level doesn't
+                // meet the requirement and needs to be updated.
+                needsUpdate = true;
+            }
+        }
+        if (!found) {
+            // We set to the highest supported level.
+            me.set().level = LEVEL_AVC_5;
+        }
+
+        return C2R::Ok();
+    }
+
+    static C2R IntraRefreshSetter(bool mayBlock, C2P<C2StreamIntraRefreshTuning::output> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (me.v.period < 1) {
+            me.set().mode = C2Config::INTRA_REFRESH_DISABLED;
+            me.set().period = 0;
+        } else {
+            // only support arbitrary mode (cyclic in our case)
+            me.set().mode = C2Config::INTRA_REFRESH_ARBITRARY;
+        }
+        return res;
+    }
+
+    static C2R GopSetter(bool mayBlock, C2P<C2StreamGopTuning::output> &me) {
+        (void)mayBlock;
+        for (size_t i = 0; i < me.v.flexCount(); ++i) {
+            const C2GopLayerStruct &layer = me.v.m.values[0];
+            if (layer.type_ == C2Config::picture_type_t(P_FRAME | B_FRAME)
+                    && layer.count > MAX_B_FRAMES) {
+                me.set().m.values[i].count = MAX_B_FRAMES;
+            }
+        }
+        return C2R::Ok();
+    }
+
+    IV_PROFILE_T getProfile_l() const {
+        switch (mProfileLevel->profile) {
+        case PROFILE_AVC_CONSTRAINED_BASELINE:  [[fallthrough]];
+        case PROFILE_AVC_BASELINE: return IV_PROFILE_BASE;
+        case PROFILE_AVC_MAIN:     return IV_PROFILE_MAIN;
+        default:
+            ALOGD("Unrecognized profile: %x", mProfileLevel->profile);
+            return IV_PROFILE_DEFAULT;
+        }
+    }
+
+    UWORD32 getLevel_l() const {
+        struct Level {
+            C2Config::level_t c2Level;
+            UWORD32 avcLevel;
+        };
+        constexpr Level levels[] = {
+            { LEVEL_AVC_1,   10 },
+            { LEVEL_AVC_1B,   9 },
+            { LEVEL_AVC_1_1, 11 },
+            { LEVEL_AVC_1_2, 12 },
+            { LEVEL_AVC_1_3, 13 },
+            { LEVEL_AVC_2,   20 },
+            { LEVEL_AVC_2_1, 21 },
+            { LEVEL_AVC_2_2, 22 },
+            { LEVEL_AVC_3,   30 },
+            { LEVEL_AVC_3_1, 31 },
+            { LEVEL_AVC_3_2, 32 },
+            { LEVEL_AVC_4,   40 },
+            { LEVEL_AVC_4_1, 41 },
+            { LEVEL_AVC_4_2, 42 },
+            { LEVEL_AVC_5,   50 },
+        };
+        for (const Level &level : levels) {
+            if (mProfileLevel->level == level.c2Level) {
+                return level.avcLevel;
+            }
+        }
+        ALOGD("Unrecognized level: %x", mProfileLevel->level);
+        return 41;
+    }
+
+    uint32_t getSyncFramePeriod_l() const {
+        if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) {
+            return 0;
+        }
+        double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
+        return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
+    }
+
+    // unsafe getters
+    std::shared_ptr<C2StreamPictureSizeInfo::input> getSize_l() const { return mSize; }
+    std::shared_ptr<C2StreamIntraRefreshTuning::output> getIntraRefresh_l() const { return mIntraRefresh; }
+    std::shared_ptr<C2StreamFrameRateInfo::output> getFrameRate_l() const { return mFrameRate; }
+    std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const { return mBitrate; }
+    std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const { return mRequestSync; }
+    std::shared_ptr<C2StreamGopTuning::output> getGop_l() const { return mGop; }
+
+private:
+    std::shared_ptr<C2StreamUsageTuning::input> mUsage;
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+    std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
+    std::shared_ptr<C2StreamIntraRefreshTuning::output> mIntraRefresh;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
+    std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
+    std::shared_ptr<C2StreamGopTuning::output> mGop;
+};
+
+#define ive_api_function  ih264e_api_function
+
+namespace {
+
+// From external/libavc/encoder/ih264e_bitstream.h
+constexpr uint32_t MIN_STREAM_SIZE = 0x800;
+
+static size_t GetCPUCoreCount() {
+    long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGV("Number of CPU cores: %ld", cpuCoreCount);
+    return (size_t)cpuCoreCount;
+}
+
+}  // namespace
+
+C2SoftAvcEnc::C2SoftAvcEnc(
+        const char *name, c2_node_id_t id, const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mIvVideoColorFormat(IV_YUV_420P),
+      mAVCEncProfile(IV_PROFILE_BASE),
+      mAVCEncLevel(41),
+      mStarted(false),
+      mSawInputEOS(false),
+      mSignalledError(false),
+      mCodecCtx(nullptr),
+      mOutBlock(nullptr),
+      // TODO: output buffer size
+      mOutBufferSize(524288) {
+
+    // If dump is enabled, then open create an empty file
+    GENERATE_FILE_NAMES();
+    CREATE_DUMP_FILE(mInFile);
+    CREATE_DUMP_FILE(mOutFile);
+
+    initEncParams();
+}
+
+C2SoftAvcEnc::~C2SoftAvcEnc() {
+    onRelease();
+}
+
+c2_status_t C2SoftAvcEnc::onInit() {
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::onStop() {
+    return C2_OK;
+}
+
+void C2SoftAvcEnc::onReset() {
+    // TODO: use IVE_CMD_CTL_RESET?
+    releaseEncoder();
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
+    initEncParams();
+}
+
+void C2SoftAvcEnc::onRelease() {
+    releaseEncoder();
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
+}
+
+c2_status_t C2SoftAvcEnc::onFlush_sm() {
+    // TODO: use IVE_CMD_CTL_FLUSH?
+    return C2_OK;
+}
+
+void  C2SoftAvcEnc::initEncParams() {
+    mCodecCtx = nullptr;
+    mMemRecords = nullptr;
+    mNumMemRecords = DEFAULT_MEM_REC_CNT;
+    mHeaderGenerated = 0;
+    mNumCores = GetCPUCoreCount();
+    mArch = DEFAULT_ARCH;
+    mSliceMode = DEFAULT_SLICE_MODE;
+    mSliceParam = DEFAULT_SLICE_PARAM;
+    mHalfPelEnable = DEFAULT_HPEL;
+    mIInterval = DEFAULT_I_INTERVAL;
+    mIDRInterval = DEFAULT_IDR_INTERVAL;
+    mDisableDeblkLevel = DEFAULT_DISABLE_DEBLK_LEVEL;
+    mEnableFastSad = DEFAULT_ENABLE_FAST_SAD;
+    mEnableAltRef = DEFAULT_ENABLE_ALT_REF;
+    mEncSpeed = DEFAULT_ENC_SPEED;
+    mIntra4x4 = DEFAULT_INTRA4x4;
+    mConstrainedIntraFlag = DEFAULT_CONSTRAINED_INTRA;
+    mPSNREnable = DEFAULT_PSNR_ENABLE;
+    mReconEnable = DEFAULT_RECON_ENABLE;
+    mEntropyMode = DEFAULT_ENTROPY_MODE;
+    mBframes = DEFAULT_B_FRAMES;
+
+    gettimeofday(&mTimeStart, nullptr);
+    gettimeofday(&mTimeEnd, nullptr);
+}
+
+c2_status_t C2SoftAvcEnc::setDimensions() {
+    ive_ctl_set_dimensions_ip_t s_dimensions_ip;
+    ive_ctl_set_dimensions_op_t s_dimensions_op;
+    IV_STATUS_T status;
+
+    s_dimensions_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_dimensions_ip.e_sub_cmd = IVE_CMD_CTL_SET_DIMENSIONS;
+    s_dimensions_ip.u4_ht = mSize->height;
+    s_dimensions_ip.u4_wd = mSize->width;
+
+    s_dimensions_ip.u4_timestamp_high = -1;
+    s_dimensions_ip.u4_timestamp_low = -1;
+
+    s_dimensions_ip.u4_size = sizeof(ive_ctl_set_dimensions_ip_t);
+    s_dimensions_op.u4_size = sizeof(ive_ctl_set_dimensions_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_dimensions_ip, &s_dimensions_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set frame dimensions = 0x%x\n",
+                s_dimensions_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setNumCores() {
+    IV_STATUS_T status;
+    ive_ctl_set_num_cores_ip_t s_num_cores_ip;
+    ive_ctl_set_num_cores_op_t s_num_cores_op;
+    s_num_cores_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_num_cores_ip.e_sub_cmd = IVE_CMD_CTL_SET_NUM_CORES;
+    s_num_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_CORES);
+    s_num_cores_ip.u4_timestamp_high = -1;
+    s_num_cores_ip.u4_timestamp_low = -1;
+    s_num_cores_ip.u4_size = sizeof(ive_ctl_set_num_cores_ip_t);
+
+    s_num_cores_op.u4_size = sizeof(ive_ctl_set_num_cores_op_t);
+
+    status = ive_api_function(
+            mCodecCtx, (void *) &s_num_cores_ip, (void *) &s_num_cores_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set processor params = 0x%x\n",
+                s_num_cores_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setFrameRate() {
+    ive_ctl_set_frame_rate_ip_t s_frame_rate_ip;
+    ive_ctl_set_frame_rate_op_t s_frame_rate_op;
+    IV_STATUS_T status;
+
+    s_frame_rate_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_frame_rate_ip.e_sub_cmd = IVE_CMD_CTL_SET_FRAMERATE;
+
+    s_frame_rate_ip.u4_src_frame_rate = mFrameRate->value + 0.5;
+    s_frame_rate_ip.u4_tgt_frame_rate = mFrameRate->value + 0.5;
+
+    s_frame_rate_ip.u4_timestamp_high = -1;
+    s_frame_rate_ip.u4_timestamp_low = -1;
+
+    s_frame_rate_ip.u4_size = sizeof(ive_ctl_set_frame_rate_ip_t);
+    s_frame_rate_op.u4_size = sizeof(ive_ctl_set_frame_rate_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_frame_rate_ip, &s_frame_rate_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set frame rate = 0x%x\n",
+                s_frame_rate_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setIpeParams() {
+    ive_ctl_set_ipe_params_ip_t s_ipe_params_ip;
+    ive_ctl_set_ipe_params_op_t s_ipe_params_op;
+    IV_STATUS_T status;
+
+    s_ipe_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_ipe_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_IPE_PARAMS;
+
+    s_ipe_params_ip.u4_enable_intra_4x4 = mIntra4x4;
+    s_ipe_params_ip.u4_enc_speed_preset = mEncSpeed;
+    s_ipe_params_ip.u4_constrained_intra_pred = mConstrainedIntraFlag;
+
+    s_ipe_params_ip.u4_timestamp_high = -1;
+    s_ipe_params_ip.u4_timestamp_low = -1;
+
+    s_ipe_params_ip.u4_size = sizeof(ive_ctl_set_ipe_params_ip_t);
+    s_ipe_params_op.u4_size = sizeof(ive_ctl_set_ipe_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_ipe_params_ip, &s_ipe_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set ipe params = 0x%x\n",
+                s_ipe_params_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setBitRate() {
+    ive_ctl_set_bitrate_ip_t s_bitrate_ip;
+    ive_ctl_set_bitrate_op_t s_bitrate_op;
+    IV_STATUS_T status;
+
+    s_bitrate_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_bitrate_ip.e_sub_cmd = IVE_CMD_CTL_SET_BITRATE;
+
+    s_bitrate_ip.u4_target_bitrate = mBitrate->value;
+
+    s_bitrate_ip.u4_timestamp_high = -1;
+    s_bitrate_ip.u4_timestamp_low = -1;
+
+    s_bitrate_ip.u4_size = sizeof(ive_ctl_set_bitrate_ip_t);
+    s_bitrate_op.u4_size = sizeof(ive_ctl_set_bitrate_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_bitrate_ip, &s_bitrate_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set bit rate = 0x%x\n", s_bitrate_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setFrameType(IV_PICTURE_CODING_TYPE_T e_frame_type) {
+    ive_ctl_set_frame_type_ip_t s_frame_type_ip;
+    ive_ctl_set_frame_type_op_t s_frame_type_op;
+    IV_STATUS_T status;
+    s_frame_type_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_frame_type_ip.e_sub_cmd = IVE_CMD_CTL_SET_FRAMETYPE;
+
+    s_frame_type_ip.e_frame_type = e_frame_type;
+
+    s_frame_type_ip.u4_timestamp_high = -1;
+    s_frame_type_ip.u4_timestamp_low = -1;
+
+    s_frame_type_ip.u4_size = sizeof(ive_ctl_set_frame_type_ip_t);
+    s_frame_type_op.u4_size = sizeof(ive_ctl_set_frame_type_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_frame_type_ip, &s_frame_type_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set frame type = 0x%x\n",
+                s_frame_type_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setQp() {
+    ive_ctl_set_qp_ip_t s_qp_ip;
+    ive_ctl_set_qp_op_t s_qp_op;
+    IV_STATUS_T status;
+
+    s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP;
+
+    s_qp_ip.u4_i_qp = DEFAULT_I_QP;
+    s_qp_ip.u4_i_qp_max = DEFAULT_QP_MAX;
+    s_qp_ip.u4_i_qp_min = DEFAULT_QP_MIN;
+
+    s_qp_ip.u4_p_qp = DEFAULT_P_QP;
+    s_qp_ip.u4_p_qp_max = DEFAULT_QP_MAX;
+    s_qp_ip.u4_p_qp_min = DEFAULT_QP_MIN;
+
+    s_qp_ip.u4_b_qp = DEFAULT_P_QP;
+    s_qp_ip.u4_b_qp_max = DEFAULT_QP_MAX;
+    s_qp_ip.u4_b_qp_min = DEFAULT_QP_MIN;
+
+    s_qp_ip.u4_timestamp_high = -1;
+    s_qp_ip.u4_timestamp_low = -1;
+
+    s_qp_ip.u4_size = sizeof(ive_ctl_set_qp_ip_t);
+    s_qp_op.u4_size = sizeof(ive_ctl_set_qp_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_qp_ip, &s_qp_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set qp 0x%x\n", s_qp_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setEncMode(IVE_ENC_MODE_T e_enc_mode) {
+    IV_STATUS_T status;
+    ive_ctl_set_enc_mode_ip_t s_enc_mode_ip;
+    ive_ctl_set_enc_mode_op_t s_enc_mode_op;
+
+    s_enc_mode_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_enc_mode_ip.e_sub_cmd = IVE_CMD_CTL_SET_ENC_MODE;
+
+    s_enc_mode_ip.e_enc_mode = e_enc_mode;
+
+    s_enc_mode_ip.u4_timestamp_high = -1;
+    s_enc_mode_ip.u4_timestamp_low = -1;
+
+    s_enc_mode_ip.u4_size = sizeof(ive_ctl_set_enc_mode_ip_t);
+    s_enc_mode_op.u4_size = sizeof(ive_ctl_set_enc_mode_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_enc_mode_ip, &s_enc_mode_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set in header encode mode = 0x%x\n",
+                s_enc_mode_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setVbvParams() {
+    ive_ctl_set_vbv_params_ip_t s_vbv_ip;
+    ive_ctl_set_vbv_params_op_t s_vbv_op;
+    IV_STATUS_T status;
+
+    s_vbv_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_vbv_ip.e_sub_cmd = IVE_CMD_CTL_SET_VBV_PARAMS;
+
+    s_vbv_ip.u4_vbv_buf_size = 0;
+    s_vbv_ip.u4_vbv_buffer_delay = 1000;
+
+    s_vbv_ip.u4_timestamp_high = -1;
+    s_vbv_ip.u4_timestamp_low = -1;
+
+    s_vbv_ip.u4_size = sizeof(ive_ctl_set_vbv_params_ip_t);
+    s_vbv_op.u4_size = sizeof(ive_ctl_set_vbv_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_vbv_ip, &s_vbv_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set VBV params = 0x%x\n", s_vbv_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setAirParams() {
+    ive_ctl_set_air_params_ip_t s_air_ip;
+    ive_ctl_set_air_params_op_t s_air_op;
+    IV_STATUS_T status;
+
+    s_air_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_air_ip.e_sub_cmd = IVE_CMD_CTL_SET_AIR_PARAMS;
+
+    s_air_ip.e_air_mode =
+        (mIntraRefresh->mode == C2Config::INTRA_REFRESH_DISABLED || mIntraRefresh->period < 1)
+            ? IVE_AIR_MODE_NONE : IVE_AIR_MODE_CYCLIC;
+    s_air_ip.u4_air_refresh_period = mIntraRefresh->period;
+
+    s_air_ip.u4_timestamp_high = -1;
+    s_air_ip.u4_timestamp_low = -1;
+
+    s_air_ip.u4_size = sizeof(ive_ctl_set_air_params_ip_t);
+    s_air_op.u4_size = sizeof(ive_ctl_set_air_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_air_ip, &s_air_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set air params = 0x%x\n", s_air_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setMeParams() {
+    IV_STATUS_T status;
+    ive_ctl_set_me_params_ip_t s_me_params_ip;
+    ive_ctl_set_me_params_op_t s_me_params_op;
+
+    s_me_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_me_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_ME_PARAMS;
+
+    s_me_params_ip.u4_enable_fast_sad = mEnableFastSad;
+    s_me_params_ip.u4_enable_alt_ref = mEnableAltRef;
+
+    s_me_params_ip.u4_enable_hpel = mHalfPelEnable;
+    s_me_params_ip.u4_enable_qpel = DEFAULT_QPEL;
+    s_me_params_ip.u4_me_speed_preset = DEFAULT_ME_SPEED;
+    s_me_params_ip.u4_srch_rng_x = DEFAULT_SRCH_RNG_X;
+    s_me_params_ip.u4_srch_rng_y = DEFAULT_SRCH_RNG_Y;
+
+    s_me_params_ip.u4_timestamp_high = -1;
+    s_me_params_ip.u4_timestamp_low = -1;
+
+    s_me_params_ip.u4_size = sizeof(ive_ctl_set_me_params_ip_t);
+    s_me_params_op.u4_size = sizeof(ive_ctl_set_me_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_me_params_ip, &s_me_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set me params = 0x%x\n", s_me_params_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setGopParams() {
+    IV_STATUS_T status;
+    ive_ctl_set_gop_params_ip_t s_gop_params_ip;
+    ive_ctl_set_gop_params_op_t s_gop_params_op;
+
+    s_gop_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_gop_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_GOP_PARAMS;
+
+    s_gop_params_ip.u4_i_frm_interval = mIInterval;
+    s_gop_params_ip.u4_idr_frm_interval = mIDRInterval;
+
+    s_gop_params_ip.u4_timestamp_high = -1;
+    s_gop_params_ip.u4_timestamp_low = -1;
+
+    s_gop_params_ip.u4_size = sizeof(ive_ctl_set_gop_params_ip_t);
+    s_gop_params_op.u4_size = sizeof(ive_ctl_set_gop_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_gop_params_ip, &s_gop_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set GOP params = 0x%x\n",
+                s_gop_params_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setProfileParams() {
+    IntfImpl::Lock lock = mIntf->lock();
+
+    IV_STATUS_T status;
+    ive_ctl_set_profile_params_ip_t s_profile_params_ip;
+    ive_ctl_set_profile_params_op_t s_profile_params_op;
+
+    s_profile_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_profile_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_PROFILE_PARAMS;
+
+    s_profile_params_ip.e_profile = mIntf->getProfile_l();
+    if (s_profile_params_ip.e_profile == IV_PROFILE_BASE) {
+        s_profile_params_ip.u4_entropy_coding_mode = 0;
+    } else {
+        s_profile_params_ip.u4_entropy_coding_mode = 1;
+    }
+    s_profile_params_ip.u4_timestamp_high = -1;
+    s_profile_params_ip.u4_timestamp_low = -1;
+
+    s_profile_params_ip.u4_size = sizeof(ive_ctl_set_profile_params_ip_t);
+    s_profile_params_op.u4_size = sizeof(ive_ctl_set_profile_params_op_t);
+    lock.unlock();
+
+    status = ive_api_function(mCodecCtx, &s_profile_params_ip, &s_profile_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set profile params = 0x%x\n",
+                s_profile_params_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setDeblockParams() {
+    IV_STATUS_T status;
+    ive_ctl_set_deblock_params_ip_t s_deblock_params_ip;
+    ive_ctl_set_deblock_params_op_t s_deblock_params_op;
+
+    s_deblock_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_deblock_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_DEBLOCK_PARAMS;
+
+    s_deblock_params_ip.u4_disable_deblock_level = mDisableDeblkLevel;
+
+    s_deblock_params_ip.u4_timestamp_high = -1;
+    s_deblock_params_ip.u4_timestamp_low = -1;
+
+    s_deblock_params_ip.u4_size = sizeof(ive_ctl_set_deblock_params_ip_t);
+    s_deblock_params_op.u4_size = sizeof(ive_ctl_set_deblock_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_deblock_params_ip, &s_deblock_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to enable/disable deblock params = 0x%x\n",
+                s_deblock_params_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+void C2SoftAvcEnc::logVersion() {
+    ive_ctl_getversioninfo_ip_t s_ctl_ip;
+    ive_ctl_getversioninfo_op_t s_ctl_op;
+    UWORD8 au1_buf[512];
+    IV_STATUS_T status;
+
+    s_ctl_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_ctl_ip.e_sub_cmd = IVE_CMD_CTL_GETVERSION;
+    s_ctl_ip.u4_size = sizeof(ive_ctl_getversioninfo_ip_t);
+    s_ctl_op.u4_size = sizeof(ive_ctl_getversioninfo_op_t);
+    s_ctl_ip.pu1_version = au1_buf;
+    s_ctl_ip.u4_version_bufsize = sizeof(au1_buf);
+
+    status = ive_api_function(mCodecCtx, (void *) &s_ctl_ip, (void *) &s_ctl_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Error in getting version: 0x%x", s_ctl_op.u4_error_code);
+    } else {
+        ALOGV("Ittiam encoder version: %s", (char *)s_ctl_ip.pu1_version);
+    }
+    return;
+}
+
+c2_status_t C2SoftAvcEnc::initEncoder() {
+    IV_STATUS_T status;
+    WORD32 level;
+
+    CHECK(!mStarted);
+
+    c2_status_t errType = C2_OK;
+
+    std::shared_ptr<C2StreamGopTuning::output> gop;
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        mSize = mIntf->getSize_l();
+        mBitrate = mIntf->getBitrate_l();
+        mFrameRate = mIntf->getFrameRate_l();
+        mIntraRefresh = mIntf->getIntraRefresh_l();
+        mAVCEncLevel = mIntf->getLevel_l();
+        mIInterval = mIntf->getSyncFramePeriod_l();
+        mIDRInterval = mIntf->getSyncFramePeriod_l();
+        gop = mIntf->getGop_l();
+    }
+    if (gop && gop->flexCount() > 0) {
+        uint32_t syncInterval = 1;
+        uint32_t iInterval = 1;
+        uint32_t maxBframes = 0;
+        ParseGop(*gop, &syncInterval, &iInterval, &maxBframes);
+        if (syncInterval > 0) {
+            ALOGD("Updating IDR interval from GOP: old %u new %u", mIDRInterval, syncInterval);
+            mIDRInterval = syncInterval;
+        }
+        if (iInterval > 0) {
+            ALOGD("Updating I interval from GOP: old %u new %u", mIInterval, iInterval);
+            mIInterval = iInterval;
+        }
+        if (mBframes != maxBframes) {
+            ALOGD("Updating max B frames from GOP: old %u new %u", mBframes, maxBframes);
+            mBframes = maxBframes;
+        }
+    }
+    uint32_t width = mSize->width;
+    uint32_t height = mSize->height;
+
+    mStride = width;
+
+    // TODO
+    mIvVideoColorFormat = IV_YUV_420P;
+
+    ALOGD("Params width %d height %d level %d colorFormat %d bframes %d", width,
+            height, mAVCEncLevel, mIvVideoColorFormat, mBframes);
+
+    /* Getting Number of MemRecords */
+    {
+        iv_num_mem_rec_ip_t s_num_mem_rec_ip;
+        iv_num_mem_rec_op_t s_num_mem_rec_op;
+
+        s_num_mem_rec_ip.u4_size = sizeof(iv_num_mem_rec_ip_t);
+        s_num_mem_rec_op.u4_size = sizeof(iv_num_mem_rec_op_t);
+
+        s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+
+        status = ive_api_function(nullptr, &s_num_mem_rec_ip, &s_num_mem_rec_op);
+
+        if (status != IV_SUCCESS) {
+            ALOGE("Get number of memory records failed = 0x%x\n",
+                    s_num_mem_rec_op.u4_error_code);
+            return C2_CORRUPTED;
+        }
+
+        mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+    }
+
+    /* Allocate array to hold memory records */
+    if (mNumMemRecords > SIZE_MAX / sizeof(iv_mem_rec_t)) {
+        ALOGE("requested memory size is too big.");
+        return C2_CORRUPTED;
+    }
+    mMemRecords = (iv_mem_rec_t *)malloc(mNumMemRecords * sizeof(iv_mem_rec_t));
+    if (nullptr == mMemRecords) {
+        ALOGE("Unable to allocate memory for hold memory records: Size %zu",
+                mNumMemRecords * sizeof(iv_mem_rec_t));
+        mSignalledError = true;
+        return C2_CORRUPTED;
+    }
+
+    {
+        iv_mem_rec_t *ps_mem_rec;
+        ps_mem_rec = mMemRecords;
+        for (size_t i = 0; i < mNumMemRecords; i++) {
+            ps_mem_rec->u4_size = sizeof(iv_mem_rec_t);
+            ps_mem_rec->pv_base = nullptr;
+            ps_mem_rec->u4_mem_size = 0;
+            ps_mem_rec->u4_mem_alignment = 0;
+            ps_mem_rec->e_mem_type = IV_NA_MEM_TYPE;
+
+            ps_mem_rec++;
+        }
+    }
+
+    /* Getting MemRecords Attributes */
+    {
+        iv_fill_mem_rec_ip_t s_fill_mem_rec_ip;
+        iv_fill_mem_rec_op_t s_fill_mem_rec_op;
+
+        s_fill_mem_rec_ip.u4_size = sizeof(iv_fill_mem_rec_ip_t);
+        s_fill_mem_rec_op.u4_size = sizeof(iv_fill_mem_rec_op_t);
+
+        s_fill_mem_rec_ip.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+        s_fill_mem_rec_ip.ps_mem_rec = mMemRecords;
+        s_fill_mem_rec_ip.u4_num_mem_rec = mNumMemRecords;
+        s_fill_mem_rec_ip.u4_max_wd = width;
+        s_fill_mem_rec_ip.u4_max_ht = height;
+        s_fill_mem_rec_ip.u4_max_level = mAVCEncLevel;
+        s_fill_mem_rec_ip.e_color_format = DEFAULT_INP_COLOR_FORMAT;
+        s_fill_mem_rec_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
+        s_fill_mem_rec_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
+        s_fill_mem_rec_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
+        s_fill_mem_rec_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
+
+        status = ive_api_function(nullptr, &s_fill_mem_rec_ip, &s_fill_mem_rec_op);
+
+        if (status != IV_SUCCESS) {
+            ALOGE("Fill memory records failed = 0x%x\n",
+                    s_fill_mem_rec_op.u4_error_code);
+            return C2_CORRUPTED;
+        }
+    }
+
+    /* Allocating Memory for Mem Records */
+    {
+        WORD32 total_size;
+        iv_mem_rec_t *ps_mem_rec;
+        total_size = 0;
+        ps_mem_rec = mMemRecords;
+
+        for (size_t i = 0; i < mNumMemRecords; i++) {
+            ps_mem_rec->pv_base = ive_aligned_malloc(
+                    ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+            if (ps_mem_rec->pv_base == nullptr) {
+                ALOGE("Allocation failure for mem record id %zu size %u\n", i,
+                        ps_mem_rec->u4_mem_size);
+                return C2_CORRUPTED;
+
+            }
+            total_size += ps_mem_rec->u4_mem_size;
+
+            ps_mem_rec++;
+        }
+    }
+
+    /* Codec Instance Creation */
+    {
+        ive_init_ip_t s_init_ip;
+        ive_init_op_t s_init_op;
+
+        mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base;
+        mCodecCtx->u4_size = sizeof(iv_obj_t);
+        mCodecCtx->pv_fxns = (void *)ive_api_function;
+
+        s_init_ip.u4_size = sizeof(ive_init_ip_t);
+        s_init_op.u4_size = sizeof(ive_init_op_t);
+
+        s_init_ip.e_cmd = IV_CMD_INIT;
+        s_init_ip.u4_num_mem_rec = mNumMemRecords;
+        s_init_ip.ps_mem_rec = mMemRecords;
+        s_init_ip.u4_max_wd = width;
+        s_init_ip.u4_max_ht = height;
+        s_init_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
+        s_init_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
+        s_init_ip.u4_max_level = mAVCEncLevel;
+        s_init_ip.e_inp_color_fmt = mIvVideoColorFormat;
+
+        if (mReconEnable || mPSNREnable) {
+            s_init_ip.u4_enable_recon = 1;
+        } else {
+            s_init_ip.u4_enable_recon = 0;
+        }
+        s_init_ip.e_recon_color_fmt = DEFAULT_RECON_COLOR_FORMAT;
+        s_init_ip.e_rc_mode = DEFAULT_RC_MODE;
+        s_init_ip.u4_max_framerate = DEFAULT_MAX_FRAMERATE;
+        s_init_ip.u4_max_bitrate = DEFAULT_MAX_BITRATE;
+        s_init_ip.u4_num_bframes = mBframes;
+        s_init_ip.e_content_type = IV_PROGRESSIVE;
+        s_init_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
+        s_init_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
+        s_init_ip.e_slice_mode = mSliceMode;
+        s_init_ip.u4_slice_param = mSliceParam;
+        s_init_ip.e_arch = mArch;
+        s_init_ip.e_soc = DEFAULT_SOC;
+
+        status = ive_api_function(mCodecCtx, &s_init_ip, &s_init_op);
+
+        if (status != IV_SUCCESS) {
+            ALOGE("Init encoder failed = 0x%x\n", s_init_op.u4_error_code);
+            return C2_CORRUPTED;
+        }
+    }
+
+    /* Get Codec Version */
+    logVersion();
+
+    /* set processor details */
+    setNumCores();
+
+    /* Video control Set Frame dimensions */
+    setDimensions();
+
+    /* Video control Set Frame rates */
+    setFrameRate();
+
+    /* Video control Set IPE Params */
+    setIpeParams();
+
+    /* Video control Set Bitrate */
+    setBitRate();
+
+    /* Video control Set QP */
+    setQp();
+
+    /* Video control Set AIR params */
+    setAirParams();
+
+    /* Video control Set VBV params */
+    setVbvParams();
+
+    /* Video control Set Motion estimation params */
+    setMeParams();
+
+    /* Video control Set GOP params */
+    setGopParams();
+
+    /* Video control Set Deblock params */
+    setDeblockParams();
+
+    /* Video control Set Profile params */
+    setProfileParams();
+
+    /* Video control Set in Encode header mode */
+    setEncMode(IVE_ENC_MODE_HEADER);
+
+    ALOGV("init_codec successfull");
+
+    mSpsPpsHeaderReceived = false;
+    mStarted = true;
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::releaseEncoder() {
+    IV_STATUS_T status = IV_SUCCESS;
+    iv_retrieve_mem_rec_ip_t s_retrieve_mem_ip;
+    iv_retrieve_mem_rec_op_t s_retrieve_mem_op;
+    iv_mem_rec_t *ps_mem_rec;
+
+    if (!mStarted) {
+        return C2_OK;
+    }
+
+    s_retrieve_mem_ip.u4_size = sizeof(iv_retrieve_mem_rec_ip_t);
+    s_retrieve_mem_op.u4_size = sizeof(iv_retrieve_mem_rec_op_t);
+    s_retrieve_mem_ip.e_cmd = IV_CMD_RETRIEVE_MEMREC;
+    s_retrieve_mem_ip.ps_mem_rec = mMemRecords;
+
+    status = ive_api_function(mCodecCtx, &s_retrieve_mem_ip, &s_retrieve_mem_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to retrieve memory records = 0x%x\n",
+                s_retrieve_mem_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+
+    /* Free memory records */
+    ps_mem_rec = mMemRecords;
+    for (size_t i = 0; i < s_retrieve_mem_op.u4_num_mem_rec_filled; i++) {
+        if (ps_mem_rec) ive_aligned_free(ps_mem_rec->pv_base);
+        else {
+            ALOGE("memory record is null.");
+            return C2_CORRUPTED;
+        }
+        ps_mem_rec++;
+    }
+
+    if (mMemRecords) free(mMemRecords);
+
+    // clear other pointers into the space being free()d
+    mCodecCtx = nullptr;
+
+    mStarted = false;
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setEncodeArgs(
+        ive_video_encode_ip_t *ps_encode_ip,
+        ive_video_encode_op_t *ps_encode_op,
+        const C2GraphicView *const input,
+        uint8_t *base,
+        uint32_t capacity,
+        uint64_t workIndex) {
+    iv_raw_buf_t *ps_inp_raw_buf;
+    memset(ps_encode_ip, 0, sizeof(*ps_encode_ip));
+    memset(ps_encode_op, 0, sizeof(*ps_encode_op));
+
+    ps_inp_raw_buf = &ps_encode_ip->s_inp_buf;
+    ps_encode_ip->s_out_buf.pv_buf = base;
+    ps_encode_ip->s_out_buf.u4_bytes = 0;
+    ps_encode_ip->s_out_buf.u4_bufsize = capacity;
+    ps_encode_ip->u4_size = sizeof(ive_video_encode_ip_t);
+    ps_encode_op->u4_size = sizeof(ive_video_encode_op_t);
+
+    ps_encode_ip->e_cmd = IVE_CMD_VIDEO_ENCODE;
+    ps_encode_ip->pv_bufs = nullptr;
+    ps_encode_ip->pv_mb_info = nullptr;
+    ps_encode_ip->pv_pic_info = nullptr;
+    ps_encode_ip->u4_mb_info_type = 0;
+    ps_encode_ip->u4_pic_info_type = 0;
+    ps_encode_ip->u4_is_last = 0;
+    ps_encode_ip->u4_timestamp_high = workIndex >> 32;
+    ps_encode_ip->u4_timestamp_low = workIndex & 0xFFFFFFFF;
+    ps_encode_op->s_out_buf.pv_buf = nullptr;
+
+    /* Initialize color formats */
+    memset(ps_inp_raw_buf, 0, sizeof(iv_raw_buf_t));
+    ps_inp_raw_buf->u4_size = sizeof(iv_raw_buf_t);
+    ps_inp_raw_buf->e_color_fmt = mIvVideoColorFormat;
+    if (input == nullptr) {
+        if (mSawInputEOS) {
+            ps_encode_ip->u4_is_last = 1;
+        }
+        return C2_OK;
+    }
+
+    if (input->width() < mSize->width ||
+        input->height() < mSize->height) {
+        /* Expect width height to be configured */
+        ALOGW("unexpected Capacity Aspect %d(%d) x %d(%d)", input->width(),
+              mSize->width, input->height(), mSize->height);
+        return C2_BAD_VALUE;
+    }
+    ALOGV("width = %d, height = %d", input->width(), input->height());
+    const C2PlanarLayout &layout = input->layout();
+    uint8_t *yPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_Y]);
+    uint8_t *uPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_U]);
+    uint8_t *vPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_V]);
+    int32_t yStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+    int32_t uStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+    int32_t vStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc;
+
+    uint32_t width = mSize->width;
+    uint32_t height = mSize->height;
+    // width and height are always even (as block size is 16x16)
+    CHECK_EQ((width & 1u), 0u);
+    CHECK_EQ((height & 1u), 0u);
+    size_t yPlaneSize = width * height;
+
+    switch (layout.type) {
+        case C2PlanarLayout::TYPE_RGB:
+            [[fallthrough]];
+        case C2PlanarLayout::TYPE_RGBA: {
+            ALOGV("yPlaneSize = %zu", yPlaneSize);
+            MemoryBlock conversionBuffer = mConversionBuffers.fetch(yPlaneSize * 3 / 2);
+            mConversionBuffersInUse.emplace(conversionBuffer.data(), conversionBuffer);
+            yPlane = conversionBuffer.data();
+            uPlane = yPlane + yPlaneSize;
+            vPlane = uPlane + yPlaneSize / 4;
+            yStride = width;
+            uStride = vStride = yStride / 2;
+            ConvertRGBToPlanarYUV(yPlane, yStride, height, conversionBuffer.size(), *input);
+            break;
+        }
+        case C2PlanarLayout::TYPE_YUV: {
+            if (!IsYUV420(*input)) {
+                ALOGE("input is not YUV420");
+                return C2_BAD_VALUE;
+            }
+
+            if (layout.planes[layout.PLANE_Y].colInc == 1
+                    && layout.planes[layout.PLANE_U].colInc == 1
+                    && layout.planes[layout.PLANE_V].colInc == 1
+                    && uStride == vStride
+                    && yStride == 2 * vStride) {
+                // I420 compatible - already set up above
+                break;
+            }
+
+            // copy to I420
+            yStride = width;
+            uStride = vStride = yStride / 2;
+            MemoryBlock conversionBuffer = mConversionBuffers.fetch(yPlaneSize * 3 / 2);
+            mConversionBuffersInUse.emplace(conversionBuffer.data(), conversionBuffer);
+            MediaImage2 img = CreateYUV420PlanarMediaImage2(width, height, yStride, height);
+            status_t err = ImageCopy(conversionBuffer.data(), &img, *input);
+            if (err != OK) {
+                ALOGE("Buffer conversion failed: %d", err);
+                return C2_BAD_VALUE;
+            }
+            yPlane = conversionBuffer.data();
+            uPlane = yPlane + yPlaneSize;
+            vPlane = uPlane + yPlaneSize / 4;
+            break;
+
+        }
+
+        case C2PlanarLayout::TYPE_YUVA:
+            ALOGE("YUVA plane type is not supported");
+            return C2_BAD_VALUE;
+
+        default:
+            ALOGE("Unrecognized plane type: %d", layout.type);
+            return C2_BAD_VALUE;
+    }
+
+    switch (mIvVideoColorFormat) {
+        case IV_YUV_420P:
+        {
+            // input buffer is supposed to be const but Ittiam API wants bare pointer.
+            ps_inp_raw_buf->apv_bufs[0] = yPlane;
+            ps_inp_raw_buf->apv_bufs[1] = uPlane;
+            ps_inp_raw_buf->apv_bufs[2] = vPlane;
+
+            ps_inp_raw_buf->au4_wd[0] = input->width();
+            ps_inp_raw_buf->au4_wd[1] = input->width() / 2;
+            ps_inp_raw_buf->au4_wd[2] = input->width() / 2;
+
+            ps_inp_raw_buf->au4_ht[0] = input->height();
+            ps_inp_raw_buf->au4_ht[1] = input->height() / 2;
+            ps_inp_raw_buf->au4_ht[2] = input->height() / 2;
+
+            ps_inp_raw_buf->au4_strd[0] = yStride;
+            ps_inp_raw_buf->au4_strd[1] = uStride;
+            ps_inp_raw_buf->au4_strd[2] = vStride;
+            break;
+        }
+
+        case IV_YUV_422ILE:
+        {
+            // TODO
+            // ps_inp_raw_buf->apv_bufs[0] = pu1_buf;
+            // ps_inp_raw_buf->au4_wd[0] = mWidth * 2;
+            // ps_inp_raw_buf->au4_ht[0] = mHeight;
+            // ps_inp_raw_buf->au4_strd[0] = mStride * 2;
+            break;
+        }
+
+        case IV_YUV_420SP_UV:
+        case IV_YUV_420SP_VU:
+        default:
+        {
+            ps_inp_raw_buf->apv_bufs[0] = yPlane;
+            ps_inp_raw_buf->apv_bufs[1] = uPlane;
+
+            ps_inp_raw_buf->au4_wd[0] = input->width();
+            ps_inp_raw_buf->au4_wd[1] = input->width();
+
+            ps_inp_raw_buf->au4_ht[0] = input->height();
+            ps_inp_raw_buf->au4_ht[1] = input->height() / 2;
+
+            ps_inp_raw_buf->au4_strd[0] = yStride;
+            ps_inp_raw_buf->au4_strd[1] = uStride;
+            break;
+        }
+    }
+    return C2_OK;
+}
+
+void C2SoftAvcEnc::finishWork(uint64_t workIndex, const std::unique_ptr<C2Work> &work,
+                              ive_video_encode_op_t *ps_encode_op) {
+    std::shared_ptr<C2Buffer> buffer =
+            createLinearBuffer(mOutBlock, 0, ps_encode_op->s_out_buf.u4_bytes);
+    if (IV_IDR_FRAME == ps_encode_op->u4_encoded_frame_type) {
+        ALOGV("IDR frame produced");
+        buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
+                0u /* stream id */, C2Config::SYNC_FRAME));
+    }
+    mOutBlock = nullptr;
+
+    auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+    if (work && c2_cntr64_t(workIndex) == work->input.ordinal.frameIndex) {
+        fillWork(work);
+        if (mSawInputEOS) {
+            work->worklets.front()->output.flags = C2FrameData::FLAG_END_OF_STREAM;
+        }
+    } else {
+        finish(workIndex, fillWork);
+    }
+}
+
+void C2SoftAvcEnc::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    IV_STATUS_T status;
+    WORD32 timeDelay = 0;
+    WORD32 timeTaken = 0;
+    uint64_t workIndex = work->input.ordinal.frameIndex.peekull();
+
+    // Initialize encoder if not already initialized
+    if (mCodecCtx == nullptr) {
+        if (C2_OK != initEncoder()) {
+            ALOGE("Failed to initialize encoder");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return;
+        }
+    }
+    if (mSignalledError) {
+        return;
+    }
+    // while (!mSawOutputEOS && !outQueue.empty()) {
+    c2_status_t error;
+    ive_video_encode_ip_t s_encode_ip;
+    ive_video_encode_op_t s_encode_op;
+    memset(&s_encode_op, 0, sizeof(s_encode_op));
+
+    if (!mSpsPpsHeaderReceived) {
+        constexpr uint32_t kHeaderLength = MIN_STREAM_SIZE;
+        uint8_t header[kHeaderLength];
+        error = setEncodeArgs(
+                &s_encode_ip, &s_encode_op, nullptr, header, kHeaderLength, workIndex);
+        if (error != C2_OK) {
+            ALOGE("setEncodeArgs failed: %d", error);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return;
+        }
+        status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+
+        if (IV_SUCCESS != status) {
+            ALOGE("Encode header failed = 0x%x\n",
+                    s_encode_op.u4_error_code);
+            work->workletsProcessed = 1u;
+            return;
+        } else {
+            ALOGV("Bytes Generated in header %d\n",
+                    s_encode_op.s_out_buf.u4_bytes);
+        }
+
+        mSpsPpsHeaderReceived = true;
+
+        std::unique_ptr<C2StreamInitDataInfo::output> csd =
+            C2StreamInitDataInfo::output::AllocUnique(s_encode_op.s_out_buf.u4_bytes, 0u);
+        if (!csd) {
+            ALOGE("CSD allocation failed");
+            mSignalledError = true;
+            work->result = C2_NO_MEMORY;
+            work->workletsProcessed = 1u;
+            return;
+        }
+        memcpy(csd->m.value, header, s_encode_op.s_out_buf.u4_bytes);
+        work->worklets.front()->output.configUpdate.push_back(std::move(csd));
+
+        DUMP_TO_FILE(
+                mOutFile, csd->m.value, csd->flexCount());
+        if (work->input.buffers.empty()) {
+            work->workletsProcessed = 1u;
+            return;
+        }
+    }
+
+    // handle dynamic config parameters
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        std::shared_ptr<C2StreamIntraRefreshTuning::output> intraRefresh = mIntf->getIntraRefresh_l();
+        std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
+        std::shared_ptr<C2StreamRequestSyncFrameTuning::output> requestSync = mIntf->getRequestSync_l();
+        lock.unlock();
+
+        if (bitrate != mBitrate) {
+            mBitrate = bitrate;
+            setBitRate();
+        }
+
+        if (intraRefresh != mIntraRefresh) {
+            mIntraRefresh = intraRefresh;
+            setAirParams();
+        }
+
+        if (requestSync != mRequestSync) {
+            // we can handle IDR immediately
+            if (requestSync->value) {
+                // unset request
+                C2StreamRequestSyncFrameTuning::output clearSync(0u, C2_FALSE);
+                std::vector<std::unique_ptr<C2SettingResult>> failures;
+                mIntf->config({ &clearSync }, C2_MAY_BLOCK, &failures);
+                ALOGV("Got sync request");
+                setFrameType(IV_IDR_FRAME);
+            }
+            mRequestSync = requestSync;
+        }
+    }
+
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        mSawInputEOS = true;
+    }
+
+    /* In normal mode, store inputBufferInfo and this will be returned
+       when encoder consumes this input */
+    // if (!mInputDataIsMeta && (inputBufferInfo != NULL)) {
+    //     for (size_t i = 0; i < MAX_INPUT_BUFFER_HEADERS; i++) {
+    //         if (NULL == mInputBufferInfo[i]) {
+    //             mInputBufferInfo[i] = inputBufferInfo;
+    //             break;
+    //         }
+    //     }
+    // }
+    std::shared_ptr<const C2GraphicView> view;
+    std::shared_ptr<C2Buffer> inputBuffer;
+    if (!work->input.buffers.empty()) {
+        inputBuffer = work->input.buffers[0];
+        view = std::make_shared<const C2GraphicView>(
+                inputBuffer->data().graphicBlocks().front().map().get());
+        if (view->error() != C2_OK) {
+            ALOGE("graphic view map err = %d", view->error());
+            work->workletsProcessed = 1u;
+            return;
+        }
+    }
+
+    do {
+        if (mSawInputEOS && work->input.buffers.empty()) break;
+        if (!mOutBlock) {
+            C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
+                                   C2MemoryUsage::CPU_WRITE};
+            // TODO: error handling, proper usage, etc.
+            c2_status_t err =
+                pool->fetchLinearBlock(mOutBufferSize, usage, &mOutBlock);
+            if (err != C2_OK) {
+                ALOGE("fetch linear block err = %d", err);
+                work->result = err;
+                work->workletsProcessed = 1u;
+                return;
+            }
+        }
+        C2WriteView wView = mOutBlock->map().get();
+        if (wView.error() != C2_OK) {
+            ALOGE("write view map err = %d", wView.error());
+            work->result = wView.error();
+            work->workletsProcessed = 1u;
+            return;
+        }
+
+        error = setEncodeArgs(
+                &s_encode_ip, &s_encode_op, view.get(), wView.base(), wView.capacity(), workIndex);
+        if (error != C2_OK) {
+            ALOGE("setEncodeArgs failed : %d", error);
+            mSignalledError = true;
+            work->result = error;
+            work->workletsProcessed = 1u;
+            return;
+        }
+
+        // DUMP_TO_FILE(
+        //         mInFile, s_encode_ip.s_inp_buf.apv_bufs[0],
+        //         (mHeight * mStride * 3 / 2));
+
+        GETTIME(&mTimeStart, nullptr);
+        /* Compute time elapsed between end of previous decode()
+         * to start of current decode() */
+        TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+        status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+
+        if (IV_SUCCESS != status) {
+            if ((s_encode_op.u4_error_code & 0xFF) == IH264E_BITSTREAM_BUFFER_OVERFLOW) {
+                // TODO: use IVE_CMD_CTL_GETBUFINFO for proper max input size?
+                mOutBufferSize *= 2;
+                mOutBlock.reset();
+                continue;
+            }
+            ALOGE("Encode Frame failed = 0x%x\n",
+                    s_encode_op.u4_error_code);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return;
+        }
+    } while (IV_SUCCESS != status);
+
+    // Hold input buffer reference
+    if (inputBuffer) {
+        mBuffers[s_encode_ip.s_inp_buf.apv_bufs[0]] = inputBuffer;
+    }
+
+    GETTIME(&mTimeEnd, nullptr);
+    /* Compute time taken for decode() */
+    TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
+
+    ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
+            s_encode_op.s_out_buf.u4_bytes);
+
+    void *freed = s_encode_op.s_inp_buf.apv_bufs[0];
+    /* If encoder frees up an input buffer, mark it as free */
+    if (freed != nullptr) {
+        if (mBuffers.count(freed) == 0u) {
+            ALOGD("buffer not tracked");
+        } else {
+            // Release input buffer reference
+            mBuffers.erase(freed);
+            mConversionBuffersInUse.erase(freed);
+        }
+    }
+
+    if (s_encode_op.output_present) {
+        if (!s_encode_op.s_out_buf.u4_bytes) {
+            ALOGE("Error: Output present but bytes generated is zero");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return;
+        }
+        uint64_t workId = ((uint64_t)s_encode_op.u4_timestamp_high << 32) |
+                      s_encode_op.u4_timestamp_low;
+        finishWork(workId, work, &s_encode_op);
+    }
+    if (mSawInputEOS) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+    }
+}
+
+c2_status_t C2SoftAvcEnc::drainInternal(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work) {
+
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    while (true) {
+        if (!mOutBlock) {
+            C2MemoryUsage usage = {C2MemoryUsage::CPU_READ,
+                                   C2MemoryUsage::CPU_WRITE};
+            // TODO: error handling, proper usage, etc.
+            c2_status_t err =
+                pool->fetchLinearBlock(mOutBufferSize, usage, &mOutBlock);
+            if (err != C2_OK) {
+                ALOGE("fetch linear block err = %d", err);
+                work->result = err;
+                work->workletsProcessed = 1u;
+                return err;
+            }
+        }
+        C2WriteView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            return C2_CORRUPTED;
+        }
+        ive_video_encode_ip_t s_encode_ip;
+        ive_video_encode_op_t s_encode_op;
+        if (C2_OK != setEncodeArgs(&s_encode_ip, &s_encode_op, nullptr,
+                                   wView.base(), wView.capacity(), 0)) {
+            ALOGE("setEncodeArgs failed for drainInternal");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return C2_CORRUPTED;
+        }
+        (void)ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+
+        void *freed = s_encode_op.s_inp_buf.apv_bufs[0];
+        /* If encoder frees up an input buffer, mark it as free */
+        if (freed != nullptr) {
+            if (mBuffers.count(freed) == 0u) {
+                ALOGD("buffer not tracked");
+            } else {
+                // Release input buffer reference
+                mBuffers.erase(freed);
+                mConversionBuffersInUse.erase(freed);
+            }
+        }
+
+        if (s_encode_op.output_present) {
+            uint64_t workId = ((uint64_t)s_encode_op.u4_timestamp_high << 32) |
+                          s_encode_op.u4_timestamp_low;
+            finishWork(workId, work, &s_encode_op);
+        } else {
+            if (work->workletsProcessed != 1u) {
+                work->worklets.front()->output.flags = work->input.flags;
+                work->worklets.front()->output.ordinal = work->input.ordinal;
+                work->worklets.front()->output.buffers.clear();
+                work->workletsProcessed = 1u;
+            }
+            break;
+        }
+    }
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftAvcEncFactory : public C2ComponentFactory {
+public:
+    C2SoftAvcEncFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+        GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftAvcEnc(COMPONENT_NAME,
+                                 id,
+                                 std::make_shared<C2SoftAvcEnc::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftAvcEnc::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftAvcEnc::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftAvcEncFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftAvcEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h
new file mode 100644
index 0000000..555055b
--- /dev/null
+++ b/media/codec2/components/avc/C2SoftAvcEnc.h
@@ -0,0 +1,304 @@
+/*
+ * 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 ANDROID_C2_SOFT_AVC_ENC_H__
+#define ANDROID_C2_SOFT_AVC_ENC_H__
+
+#include <map>
+
+#include <utils/Vector.h>
+
+#include <SimpleC2Component.h>
+
+#include "ih264_typedefs.h"
+#include "iv2.h"
+#include "ive2.h"
+
+namespace android {
+
+#define CODEC_MAX_CORES          4
+#define LEN_STATUS_BUFFER        (10  * 1024)
+#define MAX_VBV_BUFF_SIZE        (120 * 16384)
+#define MAX_NUM_IO_BUFS           3
+#define MAX_B_FRAMES              1
+
+#define DEFAULT_MAX_REF_FRM         2
+#define DEFAULT_MAX_REORDER_FRM     0
+#define DEFAULT_QP_MIN              10
+#define DEFAULT_QP_MAX              40
+#define DEFAULT_MAX_BITRATE         240000000
+#define DEFAULT_MAX_SRCH_RANGE_X    256
+#define DEFAULT_MAX_SRCH_RANGE_Y    256
+#define DEFAULT_MAX_FRAMERATE       120000
+#define DEFAULT_NUM_CORES           1
+#define DEFAULT_NUM_CORES_PRE_ENC   0
+#define DEFAULT_FPS                 30
+#define DEFAULT_ENC_SPEED           IVE_NORMAL
+
+#define DEFAULT_MEM_REC_CNT         0
+#define DEFAULT_RECON_ENABLE        0
+#define DEFAULT_CHKSUM_ENABLE       0
+#define DEFAULT_START_FRM           0
+#define DEFAULT_NUM_FRMS            0xFFFFFFFF
+#define DEFAULT_INP_COLOR_FORMAT       IV_YUV_420SP_VU
+#define DEFAULT_RECON_COLOR_FORMAT     IV_YUV_420P
+#define DEFAULT_LOOPBACK            0
+#define DEFAULT_SRC_FRAME_RATE      30
+#define DEFAULT_TGT_FRAME_RATE      30
+#define DEFAULT_MAX_WD              1920
+#define DEFAULT_MAX_HT              1920
+#define DEFAULT_MAX_LEVEL           41
+#define DEFAULT_STRIDE              0
+#define DEFAULT_WD                  1280
+#define DEFAULT_HT                  720
+#define DEFAULT_PSNR_ENABLE         0
+#define DEFAULT_ME_SPEED            100
+#define DEFAULT_ENABLE_FAST_SAD     0
+#define DEFAULT_ENABLE_ALT_REF      0
+#define DEFAULT_RC_MODE             IVE_RC_STORAGE
+#define DEFAULT_BITRATE             6000000
+#define DEFAULT_I_QP                22
+#define DEFAULT_I_QP_MAX            DEFAULT_QP_MAX
+#define DEFAULT_I_QP_MIN            DEFAULT_QP_MIN
+#define DEFAULT_P_QP                28
+#define DEFAULT_P_QP_MAX            DEFAULT_QP_MAX
+#define DEFAULT_P_QP_MIN            DEFAULT_QP_MIN
+#define DEFAULT_B_QP                22
+#define DEFAULT_B_QP_MAX            DEFAULT_QP_MAX
+#define DEFAULT_B_QP_MIN            DEFAULT_QP_MIN
+#define DEFAULT_AIR                 IVE_AIR_MODE_NONE
+#define DEFAULT_AIR_REFRESH_PERIOD  30
+#define DEFAULT_SRCH_RNG_X          64
+#define DEFAULT_SRCH_RNG_Y          48
+#define DEFAULT_I_INTERVAL          30
+#define DEFAULT_IDR_INTERVAL        1000
+#define DEFAULT_B_FRAMES            0
+#define DEFAULT_DISABLE_DEBLK_LEVEL 0
+#define DEFAULT_HPEL                1
+#define DEFAULT_QPEL                1
+#define DEFAULT_I4                  1
+#define DEFAULT_EPROFILE            IV_PROFILE_BASE
+#define DEFAULT_ENTROPY_MODE        0
+#define DEFAULT_SLICE_MODE          IVE_SLICE_MODE_NONE
+#define DEFAULT_SLICE_PARAM         256
+#define DEFAULT_ARCH                ARCH_ARM_A9Q
+#define DEFAULT_SOC                 SOC_GENERIC
+#define DEFAULT_INTRA4x4            0
+#define STRLENGTH                   500
+#define DEFAULT_CONSTRAINED_INTRA   0
+
+#define MIN(a, b) ((a) < (b))? (a) : (b)
+#define MAX(a, b) ((a) > (b))? (a) : (b)
+#define ALIGN16(x) ((((x) + 15) >> 4) << 4)
+#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define ALIGN4096(x) ((((x) + 4095) >> 12) << 12)
+
+/** Used to remove warnings about unused parameters */
+#define UNUSED(x) ((void)(x))
+
+/** Get time */
+#define GETTIME(a, b) gettimeofday(a, b);
+
+/** Compute difference between start and end */
+#define TIME_DIFF(start, end, diff) \
+    diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+            ((end).tv_usec - (start).tv_usec);
+
+#define ive_aligned_malloc(alignment, size) memalign(alignment, size)
+#define ive_aligned_free(buf) free(buf)
+
+struct C2SoftAvcEnc : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftAvcEnc(const char *name, c2_node_id_t id, const std::shared_ptr<IntfImpl> &intfImpl);
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+protected:
+    virtual ~C2SoftAvcEnc();
+
+private:
+    // OMX input buffer's timestamp and flags
+    typedef struct {
+        int64_t mTimeUs;
+        int32_t mFlags;
+    } InputBufferInfo;
+
+    std::shared_ptr<IntfImpl> mIntf;
+
+    int32_t mStride;
+
+    struct timeval mTimeStart;   // Time at the start of decode()
+    struct timeval mTimeEnd;     // Time at the end of decode()
+
+#ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+    char mOutFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+    IV_COLOR_FORMAT_T mIvVideoColorFormat;
+
+    IV_PROFILE_T mAVCEncProfile __unused;
+    WORD32   mAVCEncLevel;
+    bool     mStarted;
+    bool     mSpsPpsHeaderReceived;
+
+    bool     mSawInputEOS;
+    bool     mSignalledError;
+    bool     mIntra4x4;
+    bool     mEnableFastSad;
+    bool     mEnableAltRef;
+    bool     mReconEnable;
+    bool     mPSNREnable;
+    bool     mEntropyMode;
+    bool     mConstrainedIntraFlag;
+    IVE_SPEED_CONFIG     mEncSpeed;
+
+    iv_obj_t *mCodecCtx;         // Codec context
+    iv_mem_rec_t *mMemRecords;   // Memory records requested by the codec
+    size_t mNumMemRecords;       // Number of memory records requested by codec
+    size_t mNumCores;            // Number of cores used by the codec
+
+    std::shared_ptr<C2LinearBlock> mOutBlock;
+
+    // configurations used by component in process
+    // (TODO: keep this in intf but make them internal only)
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    std::shared_ptr<C2StreamIntraRefreshTuning::output> mIntraRefresh;
+    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
+
+    uint32_t mOutBufferSize;
+    UWORD32 mHeaderGenerated;
+    UWORD32 mBframes;
+    IV_ARCH_T mArch;
+    IVE_SLICE_MODE_T mSliceMode;
+    UWORD32 mSliceParam;
+    bool mHalfPelEnable;
+    UWORD32 mIInterval;
+    UWORD32 mIDRInterval;
+    UWORD32 mDisableDeblkLevel;
+    std::map<const void *, std::shared_ptr<C2Buffer>> mBuffers;
+    MemoryBlockPool mConversionBuffers;
+    std::map<const void *, MemoryBlock> mConversionBuffersInUse;
+
+    void initEncParams();
+    c2_status_t initEncoder();
+    c2_status_t releaseEncoder();
+
+    c2_status_t setFrameType(IV_PICTURE_CODING_TYPE_T  e_frame_type);
+    c2_status_t setQp();
+    c2_status_t setEncMode(IVE_ENC_MODE_T e_enc_mode);
+    c2_status_t setDimensions();
+    c2_status_t setNumCores();
+    c2_status_t setFrameRate();
+    c2_status_t setIpeParams();
+    c2_status_t setBitRate();
+    c2_status_t setAirParams();
+    c2_status_t setMeParams();
+    c2_status_t setGopParams();
+    c2_status_t setProfileParams();
+    c2_status_t setDeblockParams();
+    c2_status_t setVbvParams();
+    void logVersion();
+    c2_status_t setEncodeArgs(
+            ive_video_encode_ip_t *ps_encode_ip,
+            ive_video_encode_op_t *ps_encode_op,
+            const C2GraphicView *const input,
+            uint8_t *base,
+            uint32_t capacity,
+            uint64_t workIndex);
+    void finishWork(uint64_t workIndex,
+            const std::unique_ptr<C2Work> &work,
+            ive_video_encode_op_t *ps_encode_op);
+    c2_status_t drainInternal(uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool,
+            const std::unique_ptr<C2Work> &work);
+
+    C2_DO_NOT_COPY(C2SoftAvcEnc);
+};
+
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH     "/sdcard/media/avce_input"
+#define INPUT_DUMP_EXT      "yuv"
+#define OUTPUT_DUMP_PATH    "/sdcard/media/avce_output"
+#define OUTPUT_DUMP_EXT     "h264"
+
+#define GENERATE_FILE_NAMES() {                         \
+    GETTIME(&mTimeStart, NULL);                         \
+    strcpy(mInFile, "");                                \
+    sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH,  \
+            mTimeStart.tv_sec, mTimeStart.tv_usec,      \
+            INPUT_DUMP_EXT);                            \
+    strcpy(mOutFile, "");                               \
+    sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH,\
+            mTimeStart.tv_sec, mTimeStart.tv_usec,      \
+            OUTPUT_DUMP_EXT);                           \
+}
+
+#define CREATE_DUMP_FILE(m_filename) {                  \
+    FILE *fp = fopen(m_filename, "wb");                 \
+    if (fp != NULL) {                                   \
+        ALOGD("Opened file %s", m_filename);            \
+        fclose(fp);                                     \
+    } else {                                            \
+        ALOGD("Could not open file %s", m_filename);    \
+    }                                                   \
+}
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)         \
+{                                                       \
+    FILE *fp = fopen(m_filename, "ab");                 \
+    if (fp != NULL && m_buf != NULL) {                  \
+        int i;                                          \
+        i = fwrite(m_buf, 1, m_size, fp);               \
+        ALOGD("fwrite ret %d to write %d", i, m_size);  \
+        if (i != (int)m_size) {                         \
+            ALOGD("Error in fwrite, returned %d", i);   \
+            perror("Error in write to file");           \
+        }                                               \
+        fclose(fp);                                     \
+    } else {                                            \
+        ALOGD("Could not write to file %s", m_filename);\
+        if (fp != NULL)                                 \
+            fclose(fp);                                 \
+    }                                                   \
+}
+#else /* FILE_DUMP_ENABLE */
+#define INPUT_DUMP_PATH
+#define INPUT_DUMP_EXT
+#define OUTPUT_DUMP_PATH
+#define OUTPUT_DUMP_EXT
+#define GENERATE_FILE_NAMES()
+#define CREATE_DUMP_FILE(m_filename)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_AVC_ENC_H__
diff --git a/media/codec2/components/base/Android.bp b/media/codec2/components/base/Android.bp
new file mode 100644
index 0000000..f10835f
--- /dev/null
+++ b/media/codec2/components/base/Android.bp
@@ -0,0 +1,134 @@
+// DO NOT DEPEND ON THIS DIRECTLY
+// use libcodec2_soft-defaults instead
+cc_library_shared {
+    name: "libcodec2_soft_common",
+    defaults: ["libcodec2-impl-defaults"],
+    vendor_available: true,
+
+    srcs: [
+        "SimpleC2Component.cpp",
+        "SimpleC2Interface.cpp",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "libsfplugin_ccodec_utils",
+    ],
+
+    shared_libs: [
+        "libcutils", // for properties
+        "liblog",    // for ALOG
+        "libsfplugin_ccodec_utils", // for ImageCopy
+        "libstagefright_foundation", // for Mutexed
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+    },
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
+filegroup {
+    name: "codec2_soft_exports",
+    srcs: [ "exports.lds" ],
+}
+
+// public dependency for software codec implementation
+// to be used by code under media/codecs/* only as its stability is not guaranteed
+cc_defaults {
+    name: "libcodec2_soft-defaults",
+    defaults: ["libcodec2-impl-defaults"],
+    vendor_available: true,
+    version_script: ":codec2_soft_exports",
+    export_shared_lib_headers: [
+        "libsfplugin_ccodec_utils",
+    ],
+
+    shared_libs: [
+        "libcodec2_soft_common",
+        "libcutils", // for properties
+        "liblog", // for ALOG
+        "libsfplugin_ccodec_utils", // for ImageCopy
+        "libstagefright_foundation", // for ColorUtils and MIME
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
+// public dependency for software codec implementation
+// to be used by code under media/codecs/* only
+cc_defaults {
+    name: "libcodec2_soft_sanitize_all-defaults",
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+    },
+}
+
+// public dependency for software codec implementation
+// to be used by code under media/codecs/* only
+cc_defaults {
+    name: "libcodec2_soft_sanitize_signed-defaults",
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+    },
+}
+
+// TEMP: used by cheets2 project - remove when no longer used
+cc_library_shared {
+    name: "libcodec2_simple_component",
+    vendor_available: true,
+
+    srcs: [
+        "SimpleC2Interface.cpp",
+    ],
+
+    local_include_dirs: [
+        "include",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    shared_libs: [
+        "libcodec2",
+        "libcodec2_vndk",
+        "libcutils",
+        "liblog",
+        "libstagefright_foundation",
+        "libutils",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+    },
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp
new file mode 100644
index 0000000..fb3fbd0
--- /dev/null
+++ b/media/codec2/components/base/SimpleC2Component.cpp
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SimpleC2Component"
+#include <log/log.h>
+
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <inttypes.h>
+
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <SimpleC2Component.h>
+
+namespace android {
+
+std::unique_ptr<C2Work> SimpleC2Component::WorkQueue::pop_front() {
+    std::unique_ptr<C2Work> work = std::move(mQueue.front().work);
+    mQueue.pop_front();
+    return work;
+}
+
+void SimpleC2Component::WorkQueue::push_back(std::unique_ptr<C2Work> work) {
+    mQueue.push_back({ std::move(work), NO_DRAIN });
+}
+
+bool SimpleC2Component::WorkQueue::empty() const {
+    return mQueue.empty();
+}
+
+void SimpleC2Component::WorkQueue::clear() {
+    mQueue.clear();
+}
+
+uint32_t SimpleC2Component::WorkQueue::drainMode() const {
+    return mQueue.front().drainMode;
+}
+
+void SimpleC2Component::WorkQueue::markDrain(uint32_t drainMode) {
+    mQueue.push_back({ nullptr, drainMode });
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SimpleC2Component::WorkHandler::WorkHandler() : mRunning(false) {}
+
+void SimpleC2Component::WorkHandler::setComponent(
+        const std::shared_ptr<SimpleC2Component> &thiz) {
+    mThiz = thiz;
+}
+
+static void Reply(const sp<AMessage> &msg, int32_t *err = nullptr) {
+    sp<AReplyToken> replyId;
+    CHECK(msg->senderAwaitsResponse(&replyId));
+    sp<AMessage> reply = new AMessage;
+    if (err) {
+        reply->setInt32("err", *err);
+    }
+    reply->postReply(replyId);
+}
+
+void SimpleC2Component::WorkHandler::onMessageReceived(const sp<AMessage> &msg) {
+    std::shared_ptr<SimpleC2Component> thiz = mThiz.lock();
+    if (!thiz) {
+        ALOGD("component not yet set; msg = %s", msg->debugString().c_str());
+        sp<AReplyToken> replyId;
+        if (msg->senderAwaitsResponse(&replyId)) {
+            sp<AMessage> reply = new AMessage;
+            reply->setInt32("err", C2_CORRUPTED);
+            reply->postReply(replyId);
+        }
+        return;
+    }
+
+    switch (msg->what()) {
+        case kWhatProcess: {
+            if (mRunning) {
+                if (thiz->processQueue()) {
+                    (new AMessage(kWhatProcess, this))->post();
+                }
+            } else {
+                ALOGV("Ignore process message as we're not running");
+            }
+            break;
+        }
+        case kWhatInit: {
+            int32_t err = thiz->onInit();
+            Reply(msg, &err);
+            [[fallthrough]];
+        }
+        case kWhatStart: {
+            mRunning = true;
+            break;
+        }
+        case kWhatStop: {
+            int32_t err = thiz->onStop();
+            Reply(msg, &err);
+            break;
+        }
+        case kWhatReset: {
+            thiz->onReset();
+            mRunning = false;
+            Reply(msg);
+            break;
+        }
+        case kWhatRelease: {
+            thiz->onRelease();
+            mRunning = false;
+            Reply(msg);
+            break;
+        }
+        default: {
+            ALOGD("Unrecognized msg: %d", msg->what());
+            break;
+        }
+    }
+}
+
+class SimpleC2Component::BlockingBlockPool : public C2BlockPool {
+public:
+    BlockingBlockPool(const std::shared_ptr<C2BlockPool>& base): mBase{base} {}
+
+    virtual local_id_t getLocalId() const override {
+        return mBase->getLocalId();
+    }
+
+    virtual C2Allocator::id_t getAllocatorId() const override {
+        return mBase->getAllocatorId();
+    }
+
+    virtual c2_status_t fetchLinearBlock(
+            uint32_t capacity,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2LinearBlock>* block) {
+        c2_status_t status;
+        do {
+            status = mBase->fetchLinearBlock(capacity, usage, block);
+        } while (status == C2_BLOCKING);
+        return status;
+    }
+
+    virtual c2_status_t fetchCircularBlock(
+            uint32_t capacity,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2CircularBlock>* block) {
+        c2_status_t status;
+        do {
+            status = mBase->fetchCircularBlock(capacity, usage, block);
+        } while (status == C2_BLOCKING);
+        return status;
+    }
+
+    virtual c2_status_t fetchGraphicBlock(
+            uint32_t width, uint32_t height, uint32_t format,
+            C2MemoryUsage usage,
+            std::shared_ptr<C2GraphicBlock>* block) {
+        c2_status_t status;
+        do {
+            status = mBase->fetchGraphicBlock(width, height, format, usage,
+                                              block);
+        } while (status == C2_BLOCKING);
+        return status;
+    }
+
+private:
+    std::shared_ptr<C2BlockPool> mBase;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+struct DummyReadView : public C2ReadView {
+    DummyReadView() : C2ReadView(C2_NO_INIT) {}
+};
+
+}  // namespace
+
+SimpleC2Component::SimpleC2Component(
+        const std::shared_ptr<C2ComponentInterface> &intf)
+    : mDummyReadView(DummyReadView()),
+      mIntf(intf),
+      mLooper(new ALooper),
+      mHandler(new WorkHandler) {
+    mLooper->setName(intf->getName().c_str());
+    (void)mLooper->registerHandler(mHandler);
+    mLooper->start(false, false, ANDROID_PRIORITY_VIDEO);
+}
+
+SimpleC2Component::~SimpleC2Component() {
+    mLooper->unregisterHandler(mHandler->id());
+    (void)mLooper->stop();
+}
+
+c2_status_t SimpleC2Component::setListener_vb(
+        const std::shared_ptr<C2Component::Listener> &listener, c2_blocking_t mayBlock) {
+    mHandler->setComponent(shared_from_this());
+
+    Mutexed<ExecState>::Locked state(mExecState);
+    if (state->mState == RUNNING) {
+        if (listener) {
+            return C2_BAD_STATE;
+        } else if (!mayBlock) {
+            return C2_BLOCKING;
+        }
+    }
+    state->mListener = listener;
+    // TODO: wait for listener change to have taken place before returning
+    // (e.g. if there is an ongoing listener callback)
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::queue_nb(std::list<std::unique_ptr<C2Work>> * const items) {
+    {
+        Mutexed<ExecState>::Locked state(mExecState);
+        if (state->mState != RUNNING) {
+            return C2_BAD_STATE;
+        }
+    }
+    bool queueWasEmpty = false;
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        queueWasEmpty = queue->empty();
+        while (!items->empty()) {
+            queue->push_back(std::move(items->front()));
+            items->pop_front();
+        }
+    }
+    if (queueWasEmpty) {
+        (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
+    }
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::announce_nb(const std::vector<C2WorkOutline> &items) {
+    (void)items;
+    return C2_OMITTED;
+}
+
+c2_status_t SimpleC2Component::flush_sm(
+        flush_mode_t flushMode, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+    (void)flushMode;
+    {
+        Mutexed<ExecState>::Locked state(mExecState);
+        if (state->mState != RUNNING) {
+            return C2_BAD_STATE;
+        }
+    }
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        queue->incGeneration();
+        // TODO: queue->splicedBy(flushedWork, flushedWork->end());
+        while (!queue->empty()) {
+            std::unique_ptr<C2Work> work = queue->pop_front();
+            if (work) {
+                flushedWork->push_back(std::move(work));
+            }
+        }
+        while (!queue->pending().empty()) {
+            flushedWork->push_back(std::move(queue->pending().begin()->second));
+            queue->pending().erase(queue->pending().begin());
+        }
+    }
+
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::drain_nb(drain_mode_t drainMode) {
+    if (drainMode == DRAIN_CHAIN) {
+        return C2_OMITTED;
+    }
+    {
+        Mutexed<ExecState>::Locked state(mExecState);
+        if (state->mState != RUNNING) {
+            return C2_BAD_STATE;
+        }
+    }
+    bool queueWasEmpty = false;
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        queueWasEmpty = queue->empty();
+        queue->markDrain(drainMode);
+    }
+    if (queueWasEmpty) {
+        (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
+    }
+
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::start() {
+    Mutexed<ExecState>::Locked state(mExecState);
+    if (state->mState == RUNNING) {
+        return C2_BAD_STATE;
+    }
+    bool needsInit = (state->mState == UNINITIALIZED);
+    state.unlock();
+    if (needsInit) {
+        sp<AMessage> reply;
+        (new AMessage(WorkHandler::kWhatInit, mHandler))->postAndAwaitResponse(&reply);
+        int32_t err;
+        CHECK(reply->findInt32("err", &err));
+        if (err != C2_OK) {
+            return (c2_status_t)err;
+        }
+    } else {
+        (new AMessage(WorkHandler::kWhatStart, mHandler))->post();
+    }
+    state.lock();
+    state->mState = RUNNING;
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::stop() {
+    ALOGV("stop");
+    {
+        Mutexed<ExecState>::Locked state(mExecState);
+        if (state->mState != RUNNING) {
+            return C2_BAD_STATE;
+        }
+        state->mState = STOPPED;
+    }
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        queue->clear();
+        queue->pending().clear();
+    }
+    sp<AMessage> reply;
+    (new AMessage(WorkHandler::kWhatStop, mHandler))->postAndAwaitResponse(&reply);
+    int32_t err;
+    CHECK(reply->findInt32("err", &err));
+    if (err != C2_OK) {
+        return (c2_status_t)err;
+    }
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::reset() {
+    ALOGV("reset");
+    {
+        Mutexed<ExecState>::Locked state(mExecState);
+        state->mState = UNINITIALIZED;
+    }
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        queue->clear();
+        queue->pending().clear();
+    }
+    sp<AMessage> reply;
+    (new AMessage(WorkHandler::kWhatReset, mHandler))->postAndAwaitResponse(&reply);
+    return C2_OK;
+}
+
+c2_status_t SimpleC2Component::release() {
+    ALOGV("release");
+    sp<AMessage> reply;
+    (new AMessage(WorkHandler::kWhatRelease, mHandler))->postAndAwaitResponse(&reply);
+    return C2_OK;
+}
+
+std::shared_ptr<C2ComponentInterface> SimpleC2Component::intf() {
+    return mIntf;
+}
+
+namespace {
+
+std::list<std::unique_ptr<C2Work>> vec(std::unique_ptr<C2Work> &work) {
+    std::list<std::unique_ptr<C2Work>> ret;
+    ret.push_back(std::move(work));
+    return ret;
+}
+
+}  // namespace
+
+void SimpleC2Component::finish(
+        uint64_t frameIndex, std::function<void(const std::unique_ptr<C2Work> &)> fillWork) {
+    std::unique_ptr<C2Work> work;
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        if (queue->pending().count(frameIndex) == 0) {
+            ALOGW("unknown frame index: %" PRIu64, frameIndex);
+            return;
+        }
+        work = std::move(queue->pending().at(frameIndex));
+        queue->pending().erase(frameIndex);
+    }
+    if (work) {
+        fillWork(work);
+        std::shared_ptr<C2Component::Listener> listener = mExecState.lock()->mListener;
+        listener->onWorkDone_nb(shared_from_this(), vec(work));
+        ALOGV("returning pending work");
+    }
+}
+
+void SimpleC2Component::cloneAndSend(
+        uint64_t frameIndex,
+        const std::unique_ptr<C2Work> &currentWork,
+        std::function<void(const std::unique_ptr<C2Work> &)> fillWork) {
+    std::unique_ptr<C2Work> work(new C2Work);
+    if (currentWork->input.ordinal.frameIndex == frameIndex) {
+        work->input.flags = currentWork->input.flags;
+        work->input.ordinal = currentWork->input.ordinal;
+    } else {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        if (queue->pending().count(frameIndex) == 0) {
+            ALOGW("unknown frame index: %" PRIu64, frameIndex);
+            return;
+        }
+        work->input.flags = queue->pending().at(frameIndex)->input.flags;
+        work->input.ordinal = queue->pending().at(frameIndex)->input.ordinal;
+    }
+    work->worklets.emplace_back(new C2Worklet);
+    if (work) {
+        fillWork(work);
+        std::shared_ptr<C2Component::Listener> listener = mExecState.lock()->mListener;
+        listener->onWorkDone_nb(shared_from_this(), vec(work));
+        ALOGV("cloned and sending work");
+    }
+}
+
+bool SimpleC2Component::processQueue() {
+    std::unique_ptr<C2Work> work;
+    uint64_t generation;
+    int32_t drainMode;
+    bool isFlushPending = false;
+    bool hasQueuedWork = false;
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        if (queue->empty()) {
+            return false;
+        }
+
+        generation = queue->generation();
+        drainMode = queue->drainMode();
+        isFlushPending = queue->popPendingFlush();
+        work = queue->pop_front();
+        hasQueuedWork = !queue->empty();
+    }
+    if (isFlushPending) {
+        ALOGV("processing pending flush");
+        c2_status_t err = onFlush_sm();
+        if (err != C2_OK) {
+            ALOGD("flush err: %d", err);
+            // TODO: error
+        }
+    }
+
+    if (!mOutputBlockPool) {
+        c2_status_t err = [this] {
+            // TODO: don't use query_vb
+            C2StreamBufferTypeSetting::output outputFormat(0u);
+            std::vector<std::unique_ptr<C2Param>> params;
+            c2_status_t err = intf()->query_vb(
+                    { &outputFormat },
+                    { C2PortBlockPoolsTuning::output::PARAM_TYPE },
+                    C2_DONT_BLOCK,
+                    &params);
+            if (err != C2_OK && err != C2_BAD_INDEX) {
+                ALOGD("query err = %d", err);
+                return err;
+            }
+            C2BlockPool::local_id_t poolId =
+                outputFormat.value == C2BufferData::GRAPHIC
+                        ? C2BlockPool::BASIC_GRAPHIC
+                        : C2BlockPool::BASIC_LINEAR;
+            if (params.size()) {
+                C2PortBlockPoolsTuning::output *outputPools =
+                    C2PortBlockPoolsTuning::output::From(params[0].get());
+                if (outputPools && outputPools->flexCount() >= 1) {
+                    poolId = outputPools->m.values[0];
+                }
+            }
+
+            std::shared_ptr<C2BlockPool> blockPool;
+            err = GetCodec2BlockPool(poolId, shared_from_this(), &blockPool);
+            ALOGD("Using output block pool with poolID %llu => got %llu - %d",
+                    (unsigned long long)poolId,
+                    (unsigned long long)(
+                            blockPool ? blockPool->getLocalId() : 111000111),
+                    err);
+            if (err == C2_OK) {
+                mOutputBlockPool = std::make_shared<BlockingBlockPool>(blockPool);
+            }
+            return err;
+        }();
+        if (err != C2_OK) {
+            Mutexed<ExecState>::Locked state(mExecState);
+            std::shared_ptr<C2Component::Listener> listener = state->mListener;
+            state.unlock();
+            listener->onError_nb(shared_from_this(), err);
+            return hasQueuedWork;
+        }
+    }
+
+    if (!work) {
+        c2_status_t err = drain(drainMode, mOutputBlockPool);
+        if (err != C2_OK) {
+            Mutexed<ExecState>::Locked state(mExecState);
+            std::shared_ptr<C2Component::Listener> listener = state->mListener;
+            state.unlock();
+            listener->onError_nb(shared_from_this(), err);
+        }
+        return hasQueuedWork;
+    }
+
+    {
+        std::vector<C2Param *> updates;
+        for (const std::unique_ptr<C2Param> &param: work->input.configUpdate) {
+            if (param) {
+                updates.emplace_back(param.get());
+            }
+        }
+        if (!updates.empty()) {
+            std::vector<std::unique_ptr<C2SettingResult>> failures;
+            c2_status_t err = intf()->config_vb(updates, C2_MAY_BLOCK, &failures);
+            ALOGD("applied %zu configUpdates => %s (%d)", updates.size(), asString(err), err);
+        }
+    }
+
+    ALOGV("start processing frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
+    // If input buffer list is not empty, it means we have some input to process on.
+    // However, input could be a null buffer. In such case, clear the buffer list
+    // before making call to process().
+    if (!work->input.buffers.empty() && !work->input.buffers[0]) {
+        ALOGD("Encountered null input buffer. Clearing the input buffer");
+        work->input.buffers.clear();
+    }
+    process(work, mOutputBlockPool);
+    ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
+    Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+    if (queue->generation() != generation) {
+        ALOGD("work form old generation: was %" PRIu64 " now %" PRIu64,
+                queue->generation(), generation);
+        work->result = C2_NOT_FOUND;
+        queue.unlock();
+
+        Mutexed<ExecState>::Locked state(mExecState);
+        std::shared_ptr<C2Component::Listener> listener = state->mListener;
+        state.unlock();
+        listener->onWorkDone_nb(shared_from_this(), vec(work));
+        return hasQueuedWork;
+    }
+    if (work->workletsProcessed != 0u) {
+        queue.unlock();
+        Mutexed<ExecState>::Locked state(mExecState);
+        ALOGV("returning this work");
+        std::shared_ptr<C2Component::Listener> listener = state->mListener;
+        state.unlock();
+        listener->onWorkDone_nb(shared_from_this(), vec(work));
+    } else {
+        ALOGV("queue pending work");
+        work->input.buffers.clear();
+        std::unique_ptr<C2Work> unexpected;
+
+        uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
+        if (queue->pending().count(frameIndex) != 0) {
+            unexpected = std::move(queue->pending().at(frameIndex));
+            queue->pending().erase(frameIndex);
+        }
+        (void)queue->pending().insert({ frameIndex, std::move(work) });
+
+        queue.unlock();
+        if (unexpected) {
+            ALOGD("unexpected pending work");
+            unexpected->result = C2_CORRUPTED;
+            Mutexed<ExecState>::Locked state(mExecState);
+            std::shared_ptr<C2Component::Listener> listener = state->mListener;
+            state.unlock();
+            listener->onWorkDone_nb(shared_from_this(), vec(unexpected));
+        }
+    }
+    return hasQueuedWork;
+}
+
+std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
+        const std::shared_ptr<C2LinearBlock> &block) {
+    return createLinearBuffer(block, block->offset(), block->size());
+}
+
+std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
+        const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size) {
+    return C2Buffer::CreateLinearBuffer(block->share(offset, size, ::C2Fence()));
+}
+
+std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
+        const std::shared_ptr<C2GraphicBlock> &block) {
+    return createGraphicBuffer(block, C2Rect(block->width(), block->height()));
+}
+
+std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
+        const std::shared_ptr<C2GraphicBlock> &block, const C2Rect &crop) {
+    return C2Buffer::CreateGraphicBuffer(block->share(crop, ::C2Fence()));
+}
+
+} // namespace android
diff --git a/media/codec2/components/base/SimpleC2Interface.cpp b/media/codec2/components/base/SimpleC2Interface.cpp
new file mode 100644
index 0000000..c849a4e
--- /dev/null
+++ b/media/codec2/components/base/SimpleC2Interface.cpp
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SimpleC2Interface"
+#include <utils/Log.h>
+
+// use MediaDefs here vs. MediaCodecConstants as this is not MediaCodec specific/dependent
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <SimpleC2Interface.h>
+
+namespace android {
+
+/* SimpleInterface */
+
+SimpleInterface<void>::BaseParams::BaseParams(
+        const std::shared_ptr<C2ReflectorHelper> &reflector,
+        C2String name,
+        C2Component::kind_t kind,
+        C2Component::domain_t domain,
+        C2String mediaType,
+        std::vector<C2String> aliases)
+    : C2InterfaceHelper(reflector) {
+    setDerivedInstance(this);
+
+    addParameter(
+            DefineParam(mName, C2_PARAMKEY_COMPONENT_NAME)
+            .withConstValue(AllocSharedString<C2ComponentNameSetting>(name.c_str()))
+            .build());
+
+    if (aliases.size()) {
+        C2String joined;
+        for (const C2String &alias : aliases) {
+            if (joined.length()) {
+                joined += ",";
+            }
+            joined += alias;
+        }
+        addParameter(
+                DefineParam(mAliases, C2_PARAMKEY_COMPONENT_ALIASES)
+                .withConstValue(AllocSharedString<C2ComponentAliasesSetting>(joined.c_str()))
+                .build());
+    }
+
+    addParameter(
+            DefineParam(mKind, C2_PARAMKEY_COMPONENT_KIND)
+            .withConstValue(new C2ComponentKindSetting(kind))
+            .build());
+
+    addParameter(
+            DefineParam(mDomain, C2_PARAMKEY_COMPONENT_DOMAIN)
+            .withConstValue(new C2ComponentDomainSetting(domain))
+            .build());
+
+    // simple interfaces have single streams
+    addParameter(
+            DefineParam(mInputStreamCount, C2_PARAMKEY_INPUT_STREAM_COUNT)
+            .withConstValue(new C2PortStreamCountTuning::input(1))
+            .build());
+
+    addParameter(
+            DefineParam(mOutputStreamCount, C2_PARAMKEY_OUTPUT_STREAM_COUNT)
+            .withConstValue(new C2PortStreamCountTuning::output(1))
+            .build());
+
+    // set up buffer formats and allocators
+
+    // default to linear buffers and no media type
+    C2BufferData::type_t rawBufferType = C2BufferData::LINEAR;
+    C2String rawMediaType;
+    C2Allocator::id_t rawAllocator = C2AllocatorStore::DEFAULT_LINEAR;
+    C2BlockPool::local_id_t rawPoolId = C2BlockPool::BASIC_LINEAR;
+    C2BufferData::type_t codedBufferType = C2BufferData::LINEAR;
+    C2Allocator::id_t codedAllocator = C2AllocatorStore::DEFAULT_LINEAR;
+    C2BlockPool::local_id_t codedPoolId = C2BlockPool::BASIC_LINEAR;
+
+    switch (domain) {
+        case C2Component::DOMAIN_IMAGE:
+        case C2Component::DOMAIN_VIDEO:
+            // TODO: should we define raw image? The only difference is timestamp handling
+            rawBufferType = C2BufferData::GRAPHIC;
+            rawMediaType = MEDIA_MIMETYPE_VIDEO_RAW;
+            rawAllocator = C2AllocatorStore::DEFAULT_GRAPHIC;
+            rawPoolId = C2BlockPool::BASIC_GRAPHIC;
+            break;
+        case C2Component::DOMAIN_AUDIO:
+            rawBufferType = C2BufferData::LINEAR;
+            rawMediaType = MEDIA_MIMETYPE_AUDIO_RAW;
+            rawAllocator = C2AllocatorStore::DEFAULT_LINEAR;
+            rawPoolId = C2BlockPool::BASIC_LINEAR;
+            break;
+        default:
+            break;
+    }
+    bool isEncoder = kind == C2Component::KIND_ENCODER;
+
+    // handle raw decoders
+    if (mediaType == rawMediaType) {
+        codedBufferType = rawBufferType;
+        codedAllocator = rawAllocator;
+        codedPoolId = rawPoolId;
+    }
+
+    addParameter(
+            DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE)
+            .withConstValue(new C2StreamBufferTypeSetting::input(
+                    0u, isEncoder ? rawBufferType : codedBufferType))
+            .build());
+
+    addParameter(
+            DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
+            .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>(
+                    isEncoder ? rawMediaType : mediaType))
+            .build());
+
+    addParameter(
+            DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE)
+            .withConstValue(new C2StreamBufferTypeSetting::output(
+                    0u, isEncoder ? codedBufferType : rawBufferType))
+            .build());
+
+    addParameter(
+            DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
+            .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>(
+                    isEncoder ? mediaType : rawMediaType))
+            .build());
+
+    C2Allocator::id_t inputAllocators[1] = { isEncoder ? rawAllocator : codedAllocator };
+    C2Allocator::id_t outputAllocators[1] = { isEncoder ? codedAllocator : rawAllocator };
+    C2BlockPool::local_id_t outputPoolIds[1] = { isEncoder ? codedPoolId : rawPoolId };
+
+    addParameter(
+            DefineParam(mInputAllocators, C2_PARAMKEY_INPUT_ALLOCATORS)
+            .withDefault(C2PortAllocatorsTuning::input::AllocShared(inputAllocators))
+            .withFields({ C2F(mInputAllocators, m.values[0]).any(),
+                          C2F(mInputAllocators, m.values).inRange(0, 1) })
+            .withSetter(Setter<C2PortAllocatorsTuning::input>::NonStrictValuesWithNoDeps)
+            .build());
+
+    addParameter(
+            DefineParam(mOutputAllocators, C2_PARAMKEY_OUTPUT_ALLOCATORS)
+            .withDefault(C2PortAllocatorsTuning::output::AllocShared(outputAllocators))
+            .withFields({ C2F(mOutputAllocators, m.values[0]).any(),
+                          C2F(mOutputAllocators, m.values).inRange(0, 1) })
+            .withSetter(Setter<C2PortAllocatorsTuning::output>::NonStrictValuesWithNoDeps)
+            .build());
+
+    addParameter(
+            DefineParam(mOutputPoolIds, C2_PARAMKEY_OUTPUT_BLOCK_POOLS)
+            .withDefault(C2PortBlockPoolsTuning::output::AllocShared(outputPoolIds))
+            .withFields({ C2F(mOutputPoolIds, m.values[0]).any(),
+                          C2F(mOutputPoolIds, m.values).inRange(0, 1) })
+            .withSetter(Setter<C2PortBlockPoolsTuning::output>::NonStrictValuesWithNoDeps)
+            .build());
+
+    // add stateless params
+    addParameter(
+            DefineParam(mSubscribedParamIndices, C2_PARAMKEY_SUBSCRIBED_PARAM_INDICES)
+            .withDefault(C2SubscribedParamIndicesTuning::AllocShared(0u))
+            .withFields({ C2F(mSubscribedParamIndices, m.values[0]).any(),
+                          C2F(mSubscribedParamIndices, m.values).any() })
+            .withSetter(Setter<C2SubscribedParamIndicesTuning>::NonStrictValuesWithNoDeps)
+            .build());
+
+    /* TODO
+
+    addParameter(
+            DefineParam(mCurrentWorkOrdinal, C2_PARAMKEY_CURRENT_WORK)
+            .withDefault(new C2CurrentWorkTuning())
+            .withFields({ C2F(mCurrentWorkOrdinal, m.timeStamp).any(),
+                          C2F(mCurrentWorkOrdinal, m.frameIndex).any(),
+                          C2F(mCurrentWorkOrdinal, m.customOrdinal).any() })
+            .withSetter(Setter<C2CurrentWorkTuning>::NonStrictValuesWithNoDeps)
+            .build());
+
+    addParameter(
+            DefineParam(mLastInputQueuedWorkOrdinal, C2_PARAMKEY_LAST_INPUT_QUEUED)
+            .withDefault(new C2LastWorkQueuedTuning::input())
+            .withFields({ C2F(mLastInputQueuedWorkOrdinal, m.timeStamp).any(),
+                          C2F(mLastInputQueuedWorkOrdinal, m.frameIndex).any(),
+                          C2F(mLastInputQueuedWorkOrdinal, m.customOrdinal).any() })
+            .withSetter(Setter<C2LastWorkQueuedTuning::input>::NonStrictValuesWithNoDeps)
+            .build());
+
+    addParameter(
+            DefineParam(mLastOutputQueuedWorkOrdinal, C2_PARAMKEY_LAST_OUTPUT_QUEUED)
+            .withDefault(new C2LastWorkQueuedTuning::output())
+            .withFields({ C2F(mLastOutputQueuedWorkOrdinal, m.timeStamp).any(),
+                          C2F(mLastOutputQueuedWorkOrdinal, m.frameIndex).any(),
+                          C2F(mLastOutputQueuedWorkOrdinal, m.customOrdinal).any() })
+            .withSetter(Setter<C2LastWorkQueuedTuning::output>::NonStrictValuesWithNoDeps)
+            .build());
+
+    std::shared_ptr<C2OutOfMemoryTuning> mOutOfMemory;
+
+    std::shared_ptr<C2PortConfigCounterTuning::input> mInputConfigCounter;
+    std::shared_ptr<C2PortConfigCounterTuning::output> mOutputConfigCounter;
+    std::shared_ptr<C2ConfigCounterTuning> mDirectConfigCounter;
+
+    */
+}
+
+void SimpleInterface<void>::BaseParams::noInputLatency() {
+    addParameter(
+            DefineParam(mRequestedInputDelay, C2_PARAMKEY_INPUT_DELAY_REQUEST)
+            .withConstValue(new C2PortRequestedDelayTuning::input(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY)
+            .withConstValue(new C2PortActualDelayTuning::input(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noOutputLatency() {
+    addParameter(
+            DefineParam(mRequestedOutputDelay, C2_PARAMKEY_OUTPUT_DELAY_REQUEST)
+            .withConstValue(new C2PortRequestedDelayTuning::output(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+            .withConstValue(new C2PortActualDelayTuning::output(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noPipelineLatency() {
+    addParameter(
+            DefineParam(mRequestedPipelineDelay, C2_PARAMKEY_PIPELINE_DELAY_REQUEST)
+            .withConstValue(new C2RequestedPipelineDelayTuning(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mActualPipelineDelay, C2_PARAMKEY_PIPELINE_DELAY)
+            .withConstValue(new C2ActualPipelineDelayTuning(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noPrivateBuffers() {
+    addParameter(
+            DefineParam(mPrivateAllocators, C2_PARAMKEY_PRIVATE_ALLOCATORS)
+            .withConstValue(C2PrivateAllocatorsTuning::AllocShared(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mMaxPrivateBufferCount, C2_PARAMKEY_MAX_PRIVATE_BUFFER_COUNT)
+            .withConstValue(C2MaxPrivateBufferCountTuning::AllocShared(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mPrivatePoolIds, C2_PARAMKEY_PRIVATE_BLOCK_POOLS)
+            .withConstValue(C2PrivateBlockPoolsTuning::AllocShared(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noInputReferences() {
+    addParameter(
+            DefineParam(mMaxInputReferenceAge, C2_PARAMKEY_INPUT_MAX_REFERENCE_AGE)
+            .withConstValue(new C2StreamMaxReferenceAgeTuning::input(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mMaxInputReferenceCount, C2_PARAMKEY_INPUT_MAX_REFERENCE_COUNT)
+            .withConstValue(new C2StreamMaxReferenceCountTuning::input(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noOutputReferences() {
+    addParameter(
+            DefineParam(mMaxOutputReferenceAge, C2_PARAMKEY_OUTPUT_MAX_REFERENCE_AGE)
+            .withConstValue(new C2StreamMaxReferenceAgeTuning::output(0u))
+            .build());
+
+    addParameter(
+            DefineParam(mMaxOutputReferenceCount, C2_PARAMKEY_OUTPUT_MAX_REFERENCE_COUNT)
+            .withConstValue(new C2StreamMaxReferenceCountTuning::output(0u))
+            .build());
+}
+
+void SimpleInterface<void>::BaseParams::noTimeStretch() {
+    addParameter(
+            DefineParam(mTimeStretch, C2_PARAMKEY_TIME_STRETCH)
+            .withConstValue(new C2ComponentTimeStretchTuning(1.f))
+            .build());
+}
+
+/*
+    Clients need to handle the following base params due to custom dependency.
+
+    std::shared_ptr<C2ApiLevelSetting> mApiLevel;
+    std::shared_ptr<C2ApiFeaturesSetting> mApiFeatures;
+    std::shared_ptr<C2ComponentAttributesSetting> mAttrib;
+
+    std::shared_ptr<C2PortSuggestedBufferCountTuning::input> mSuggestedInputBufferCount;
+    std::shared_ptr<C2PortSuggestedBufferCountTuning::output> mSuggestedOutputBufferCount;
+
+    std::shared_ptr<C2TrippedTuning> mTripped;
+
+*/
+
+} // namespace android
diff --git a/media/codec2/components/base/exports.lds b/media/codec2/components/base/exports.lds
new file mode 100644
index 0000000..641bae8
--- /dev/null
+++ b/media/codec2/components/base/exports.lds
@@ -0,0 +1,7 @@
+{
+    global:
+        CreateCodec2Factory;
+        DestroyCodec2Factory;
+    local: *;
+};
+
diff --git a/media/codec2/components/base/include/SimpleC2Component.h b/media/codec2/components/base/include/SimpleC2Component.h
new file mode 100644
index 0000000..22d5714
--- /dev/null
+++ b/media/codec2/components/base/include/SimpleC2Component.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SIMPLE_C2_COMPONENT_H_
+#define SIMPLE_C2_COMPONENT_H_
+
+#include <list>
+#include <unordered_map>
+
+#include <C2Component.h>
+
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/Mutexed.h>
+
+namespace android {
+
+class SimpleC2Component
+        : public C2Component, public std::enable_shared_from_this<SimpleC2Component> {
+public:
+    explicit SimpleC2Component(
+            const std::shared_ptr<C2ComponentInterface> &intf);
+    virtual ~SimpleC2Component();
+
+    // C2Component
+    // From C2Component
+    virtual c2_status_t setListener_vb(
+            const std::shared_ptr<Listener> &listener, c2_blocking_t mayBlock) override;
+    virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
+    virtual c2_status_t announce_nb(const std::vector<C2WorkOutline> &items) override;
+    virtual c2_status_t flush_sm(
+            flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
+    virtual c2_status_t drain_nb(drain_mode_t mode) override;
+    virtual c2_status_t start() override;
+    virtual c2_status_t stop() override;
+    virtual c2_status_t reset() override;
+    virtual c2_status_t release() override;
+    virtual std::shared_ptr<C2ComponentInterface> intf() override;
+
+    // for handler
+    bool processQueue();
+
+protected:
+    /**
+     * Initialize internal states of the component according to the config set
+     * in the interface.
+     *
+     * This method is called during start(), but only at the first invocation or
+     * after reset().
+     */
+    virtual c2_status_t onInit() = 0;
+
+    /**
+     * Stop the component.
+     */
+    virtual c2_status_t onStop() = 0;
+
+    /**
+     * Reset the component.
+     */
+    virtual void onReset() = 0;
+
+    /**
+     * Release the component.
+     */
+    virtual void onRelease() = 0;
+
+    /**
+     * Flush the component.
+     */
+    virtual c2_status_t onFlush_sm() = 0;
+
+    /**
+     * Process the given work and finish pending work using finish().
+     *
+     * \param[in,out]   work    the work to process
+     * \param[in]       pool    the pool to use for allocating output blocks.
+     */
+    virtual void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) = 0;
+
+    /**
+     * Drain the component and finish pending work using finish().
+     *
+     * \param[in]   drainMode   mode of drain.
+     * \param[in]   pool        the pool to use for allocating output blocks.
+     *
+     * \retval C2_OK            The component has drained all pending output
+     *                          work.
+     * \retval C2_OMITTED       Unsupported mode (e.g. DRAIN_CHAIN)
+     */
+    virtual c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) = 0;
+
+    // for derived classes
+    /**
+     * Finish pending work.
+     *
+     * This method will retrieve the pending work according to |frameIndex| and
+     * feed the work into |fillWork| function. |fillWork| must be
+     * "non-blocking". Once |fillWork| returns the filled work will be returned
+     * to the client.
+     *
+     * \param[in]   frameIndex    the index of the pending work
+     * \param[in]   fillWork      the function to fill the retrieved work.
+     */
+    void finish(uint64_t frameIndex, std::function<void(const std::unique_ptr<C2Work> &)> fillWork);
+
+    /**
+     * Clone pending or current work and send the work back to client.
+     *
+     * This method will retrieve and clone the pending or current work according
+     * to |frameIndex| and feed the work into |fillWork| function. |fillWork|
+     * must be "non-blocking". Once |fillWork| returns the filled work will be
+     * returned to the client.
+     *
+     * \param[in]   frameIndex    the index of the work
+     * \param[in]   currentWork   the current work under processing
+     * \param[in]   fillWork      the function to fill the retrieved work.
+     */
+    void cloneAndSend(
+            uint64_t frameIndex,
+            const std::unique_ptr<C2Work> &currentWork,
+            std::function<void(const std::unique_ptr<C2Work> &)> fillWork);
+
+
+    std::shared_ptr<C2Buffer> createLinearBuffer(
+            const std::shared_ptr<C2LinearBlock> &block);
+
+    std::shared_ptr<C2Buffer> createLinearBuffer(
+            const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size);
+
+    std::shared_ptr<C2Buffer> createGraphicBuffer(
+            const std::shared_ptr<C2GraphicBlock> &block);
+
+    std::shared_ptr<C2Buffer> createGraphicBuffer(
+            const std::shared_ptr<C2GraphicBlock> &block,
+            const C2Rect &crop);
+
+    static constexpr uint32_t NO_DRAIN = ~0u;
+
+    C2ReadView mDummyReadView;
+
+private:
+    const std::shared_ptr<C2ComponentInterface> mIntf;
+
+    class WorkHandler : public AHandler {
+    public:
+        enum {
+            kWhatProcess,
+            kWhatInit,
+            kWhatStart,
+            kWhatStop,
+            kWhatReset,
+            kWhatRelease,
+        };
+
+        WorkHandler();
+        ~WorkHandler() override = default;
+
+        void setComponent(const std::shared_ptr<SimpleC2Component> &thiz);
+
+    protected:
+        void onMessageReceived(const sp<AMessage> &msg) override;
+
+    private:
+        std::weak_ptr<SimpleC2Component> mThiz;
+        bool mRunning;
+    };
+
+    enum {
+        UNINITIALIZED,
+        STOPPED,
+        RUNNING,
+    };
+
+    struct ExecState {
+        ExecState() : mState(UNINITIALIZED) {}
+
+        int mState;
+        std::shared_ptr<C2Component::Listener> mListener;
+    };
+    Mutexed<ExecState> mExecState;
+
+    sp<ALooper> mLooper;
+    sp<WorkHandler> mHandler;
+
+    class WorkQueue {
+    public:
+        typedef std::unordered_map<uint64_t, std::unique_ptr<C2Work>> PendingWork;
+
+        inline WorkQueue() : mFlush(false), mGeneration(0ul) {}
+
+        inline uint64_t generation() const { return mGeneration; }
+        inline void incGeneration() { ++mGeneration; mFlush = true; }
+
+        std::unique_ptr<C2Work> pop_front();
+        void push_back(std::unique_ptr<C2Work> work);
+        bool empty() const;
+        uint32_t drainMode() const;
+        void markDrain(uint32_t drainMode);
+        inline bool popPendingFlush() {
+            bool flush = mFlush;
+            mFlush = false;
+            return flush;
+        }
+        void clear();
+        PendingWork &pending() { return mPendingWork; }
+
+    private:
+        struct Entry {
+            std::unique_ptr<C2Work> work;
+            uint32_t drainMode;
+        };
+
+        bool mFlush;
+        uint64_t mGeneration;
+        std::list<Entry> mQueue;
+        PendingWork mPendingWork;
+    };
+    Mutexed<WorkQueue> mWorkQueue;
+
+    class BlockingBlockPool;
+    std::shared_ptr<BlockingBlockPool> mOutputBlockPool;
+
+    SimpleC2Component() = delete;
+};
+
+}  // namespace android
+
+#endif  // SIMPLE_C2_COMPONENT_H_
diff --git a/media/codec2/components/base/include/SimpleC2Interface.h b/media/codec2/components/base/include/SimpleC2Interface.h
new file mode 100644
index 0000000..2051d3d0
--- /dev/null
+++ b/media/codec2/components/base/include/SimpleC2Interface.h
@@ -0,0 +1,236 @@
+/*
+ * 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_SIMPLE_C2_INTERFACE_H_
+#define ANDROID_SIMPLE_C2_INTERFACE_H_
+
+#include <C2Component.h>
+#include <C2Config.h>
+#include <util/C2InterfaceHelper.h>
+
+namespace android {
+
+/**
+ * Wrap a common interface object (such as Codec2Client::Interface, or C2InterfaceHelper into
+ * a C2ComponentInterface.
+ *
+ * \param T common interface type
+ */
+template <typename T>
+class SimpleC2Interface : public C2ComponentInterface {
+public:
+    SimpleC2Interface(const char *name, c2_node_id_t id, const std::shared_ptr<T> &impl)
+        : mName(name),
+          mId(id),
+          mImpl(impl) {
+    }
+
+    ~SimpleC2Interface() override = default;
+
+    // From C2ComponentInterface
+    C2String getName() const override { return mName; }
+    c2_node_id_t getId() const override { return mId; }
+    c2_status_t query_vb(
+            const std::vector<C2Param*> &stackParams,
+            const std::vector<C2Param::Index> &heapParamIndices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const heapParams) const override {
+        return mImpl->query(stackParams, heapParamIndices, mayBlock, heapParams);
+    }
+    c2_status_t config_vb(
+            const std::vector<C2Param*> &params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
+        return mImpl->config(params, mayBlock, failures);
+    }
+    c2_status_t createTunnel_sm(c2_node_id_t) override { return C2_OMITTED; }
+    c2_status_t releaseTunnel_sm(c2_node_id_t) override { return C2_OMITTED; }
+    c2_status_t querySupportedParams_nb(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const override {
+        return mImpl->querySupportedParams(params);
+    }
+    c2_status_t querySupportedValues_vb(
+            std::vector<C2FieldSupportedValuesQuery> &fields,
+            c2_blocking_t mayBlock) const override {
+        return mImpl->querySupportedValues(fields, mayBlock);
+    }
+
+private:
+    C2String mName;
+    const c2_node_id_t mId;
+    const std::shared_ptr<T> mImpl;
+};
+
+/**
+ * Utility classes for common interfaces.
+ */
+template<>
+class SimpleC2Interface<void> {
+public:
+    /**
+     * Base Codec 2.0 parameters required for all components.
+     */
+    struct BaseParams : C2InterfaceHelper {
+        explicit BaseParams(
+                const std::shared_ptr<C2ReflectorHelper> &helper,
+                C2String name,
+                C2Component::kind_t kind,
+                C2Component::domain_t domain,
+                C2String mediaType,
+                std::vector<C2String> aliases = std::vector<C2String>());
+
+        /// Marks that this component has no input latency. Otherwise, component must
+        /// add support for C2PortRequestedDelayTuning::input and C2PortActualDelayTuning::input.
+        void noInputLatency();
+
+        /// Marks that this component has no output latency. Otherwise, component must
+        /// add support for C2PortRequestedDelayTuning::output and C2PortActualDelayTuning::output.
+        void noOutputLatency();
+
+        /// Marks that this component has no pipeline latency. Otherwise, component must
+        /// add support for C2RequestedPipelineDelayTuning and C2ActualPipelineDelayTuning.
+        void noPipelineLatency();
+
+        /// Marks that this component has no need for private buffers. Otherwise, component must
+        /// add support for C2MaxPrivateBufferCountTuning, C2PrivateAllocatorsTuning and
+        /// C2PrivateBlockPoolsTuning.
+        void noPrivateBuffers();
+
+        /// Marks that this component holds no references to input buffers. Otherwise, component
+        /// must add support for C2StreamMaxReferenceAgeTuning::input and
+        /// C2StreamMaxReferenceCountTuning::input.
+        void noInputReferences();
+
+        /// Marks that this component holds no references to output buffers. Otherwise, component
+        /// must add support for C2StreamMaxReferenceAgeTuning::output and
+        /// C2StreamMaxReferenceCountTuning::output.
+        void noOutputReferences();
+
+        /// Marks that this component does not stretch time. Otherwise, component
+        /// must add support for C2ComponentTimeStretchTuning.
+        void noTimeStretch();
+
+        std::shared_ptr<C2ApiLevelSetting> mApiLevel;
+        std::shared_ptr<C2ApiFeaturesSetting> mApiFeatures;
+
+        std::shared_ptr<C2PlatformLevelSetting> mPlatformLevel;
+        std::shared_ptr<C2PlatformFeaturesSetting> mPlatformFeatures;
+
+        std::shared_ptr<C2ComponentNameSetting> mName;
+        std::shared_ptr<C2ComponentAliasesSetting> mAliases;
+        std::shared_ptr<C2ComponentKindSetting> mKind;
+        std::shared_ptr<C2ComponentDomainSetting> mDomain;
+        std::shared_ptr<C2ComponentAttributesSetting> mAttrib;
+        std::shared_ptr<C2ComponentTimeStretchTuning> mTimeStretch;
+
+        std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
+        std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
+        std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat;
+        std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat;
+
+        std::shared_ptr<C2PortRequestedDelayTuning::input> mRequestedInputDelay;
+        std::shared_ptr<C2PortRequestedDelayTuning::output> mRequestedOutputDelay;
+        std::shared_ptr<C2RequestedPipelineDelayTuning> mRequestedPipelineDelay;
+
+        std::shared_ptr<C2PortActualDelayTuning::input> mActualInputDelay;
+        std::shared_ptr<C2PortActualDelayTuning::output> mActualOutputDelay;
+        std::shared_ptr<C2ActualPipelineDelayTuning> mActualPipelineDelay;
+
+        std::shared_ptr<C2StreamMaxReferenceAgeTuning::input> mMaxInputReferenceAge;
+        std::shared_ptr<C2StreamMaxReferenceCountTuning::input> mMaxInputReferenceCount;
+        std::shared_ptr<C2StreamMaxReferenceAgeTuning::output> mMaxOutputReferenceAge;
+        std::shared_ptr<C2StreamMaxReferenceCountTuning::output> mMaxOutputReferenceCount;
+        std::shared_ptr<C2MaxPrivateBufferCountTuning> mMaxPrivateBufferCount;
+
+        std::shared_ptr<C2PortStreamCountTuning::input> mInputStreamCount;
+        std::shared_ptr<C2PortStreamCountTuning::output> mOutputStreamCount;
+
+        std::shared_ptr<C2SubscribedParamIndicesTuning> mSubscribedParamIndices;
+        std::shared_ptr<C2PortSuggestedBufferCountTuning::input> mSuggestedInputBufferCount;
+        std::shared_ptr<C2PortSuggestedBufferCountTuning::output> mSuggestedOutputBufferCount;
+
+        std::shared_ptr<C2CurrentWorkTuning> mCurrentWorkOrdinal;
+        std::shared_ptr<C2LastWorkQueuedTuning::input> mLastInputQueuedWorkOrdinal;
+        std::shared_ptr<C2LastWorkQueuedTuning::output> mLastOutputQueuedWorkOrdinal;
+
+        std::shared_ptr<C2PortAllocatorsTuning::input> mInputAllocators;
+        std::shared_ptr<C2PortAllocatorsTuning::output> mOutputAllocators;
+        std::shared_ptr<C2PrivateAllocatorsTuning> mPrivateAllocators;
+        std::shared_ptr<C2PortBlockPoolsTuning::output> mOutputPoolIds;
+        std::shared_ptr<C2PrivateBlockPoolsTuning> mPrivatePoolIds;
+
+        std::shared_ptr<C2TrippedTuning> mTripped;
+        std::shared_ptr<C2OutOfMemoryTuning> mOutOfMemory;
+
+        std::shared_ptr<C2PortConfigCounterTuning::input> mInputConfigCounter;
+        std::shared_ptr<C2PortConfigCounterTuning::output> mOutputConfigCounter;
+        std::shared_ptr<C2ConfigCounterTuning> mDirectConfigCounter;
+    };
+};
+
+template<typename T>
+using SimpleInterface = SimpleC2Interface<T>;
+
+template<typename T, typename ...Args>
+std::shared_ptr<T> AllocSharedString(const Args(&... args), const char *str) {
+    size_t len = strlen(str) + 1;
+    std::shared_ptr<T> ret = T::AllocShared(len, args...);
+    strcpy(ret->m.value, str);
+    return ret;
+}
+
+template<typename T, typename ...Args>
+std::shared_ptr<T> AllocSharedString(const Args(&... args), const std::string &str) {
+    std::shared_ptr<T> ret = T::AllocShared(str.length() + 1, args...);
+    strcpy(ret->m.value, str.c_str());
+    return ret;
+}
+
+template <typename T>
+struct Setter {
+    typedef typename std::remove_reference<T>::type type;
+
+    static C2R NonStrictValueWithNoDeps(
+            bool mayBlock, C2InterfaceHelper::C2P<type> &me) {
+        (void)mayBlock;
+        return me.F(me.v.value).validatePossible(me.v.value);
+    }
+
+    static C2R NonStrictValuesWithNoDeps(
+            bool mayBlock, C2InterfaceHelper::C2P<type> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        for (size_t ix = 0; ix < me.v.flexCount(); ++ix) {
+            res.plus(me.F(me.v.m.values[ix]).validatePossible(me.v.m.values[ix]));
+        }
+        return res;
+    }
+
+    static C2R StrictValueWithNoDeps(
+            bool mayBlock,
+            const C2InterfaceHelper::C2P<type> &old,
+            C2InterfaceHelper::C2P<type> &me) {
+        (void)mayBlock;
+        if (!me.F(me.v.value).supportsNow(me.v.value)) {
+            me.set().value = old.v.value;
+        }
+        return me.F(me.v.value).validatePossible(me.v.value);
+    }
+};
+
+}  // namespace android
+
+#endif  // ANDROID_SIMPLE_C2_INTERFACE_H_
diff --git a/media/codec2/components/cmds/Android.bp b/media/codec2/components/cmds/Android.bp
new file mode 100644
index 0000000..35f689e
--- /dev/null
+++ b/media/codec2/components/cmds/Android.bp
@@ -0,0 +1,36 @@
+cc_binary {
+    name: "codec2play",
+    defaults: ["libcodec2-impl-defaults"],
+
+    srcs: [
+        "codec2.cpp",
+    ],
+
+    include_dirs: [
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libgui",
+        "liblog",
+        "libstagefright",
+        "libstagefright_foundation",
+        "libui",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+
+    sanitize: {
+        cfi: true,
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+    },
+}
diff --git a/media/codec2/components/cmds/codec2.cpp b/media/codec2/components/cmds/codec2.cpp
new file mode 100644
index 0000000..f2cf545
--- /dev/null
+++ b/media/codec2/components/cmds/codec2.cpp
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <thread>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "codec2"
+#include <log/log.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <media/DataSource.h>
+#include <media/ICrypto.h>
+#include <media/IMediaHTTPService.h>
+#include <media/MediaSource.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/DataSourceFactory.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaExtractorFactory.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include <gui/GLConsumer.h>
+#include <gui/IProducerListener.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2Buffer.h>
+#include <C2BufferPriv.h>
+#include <C2Component.h>
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <C2Work.h>
+
+using namespace android;
+using namespace std::chrono_literals;
+
+namespace {
+
+class LinearBuffer : public C2Buffer {
+public:
+    explicit LinearBuffer(const std::shared_ptr<C2LinearBlock> &block)
+        : C2Buffer({ block->share(block->offset(), block->size(), ::C2Fence()) }) {}
+};
+
+class Listener;
+
+class SimplePlayer {
+public:
+    SimplePlayer();
+    ~SimplePlayer();
+
+    void onWorkDone(std::weak_ptr<C2Component> component,
+                    std::list<std::unique_ptr<C2Work>> workItems);
+    void onTripped(std::weak_ptr<C2Component> component,
+                   std::vector<std::shared_ptr<C2SettingResult>> settingResult);
+    void onError(std::weak_ptr<C2Component> component, uint32_t errorCode);
+
+    void play(const sp<IMediaSource> &source);
+
+private:
+    typedef std::unique_lock<std::mutex> ULock;
+
+    std::shared_ptr<Listener> mListener;
+    std::shared_ptr<C2Component> mComponent;
+
+    sp<IProducerListener> mProducerListener;
+
+    std::atomic_int mLinearPoolId;
+
+    std::shared_ptr<C2Allocator> mAllocIon;
+    std::shared_ptr<C2BlockPool> mLinearPool;
+
+    std::mutex mQueueLock;
+    std::condition_variable mQueueCondition;
+    std::list<std::unique_ptr<C2Work>> mWorkQueue;
+
+    std::mutex mProcessedLock;
+    std::condition_variable mProcessedCondition;
+    std::list<std::unique_ptr<C2Work>> mProcessedWork;
+
+    sp<Surface> mSurface;
+    sp<SurfaceComposerClient> mComposerClient;
+    sp<SurfaceControl> mControl;
+};
+
+class Listener : public C2Component::Listener {
+public:
+    explicit Listener(SimplePlayer *thiz) : mThis(thiz) {}
+    virtual ~Listener() = default;
+
+    virtual void onWorkDone_nb(std::weak_ptr<C2Component> component,
+                            std::list<std::unique_ptr<C2Work>> workItems) override {
+        mThis->onWorkDone(component, std::move(workItems));
+    }
+
+    virtual void onTripped_nb(std::weak_ptr<C2Component> component,
+                           std::vector<std::shared_ptr<C2SettingResult>> settingResult) override {
+        mThis->onTripped(component, settingResult);
+    }
+
+    virtual void onError_nb(std::weak_ptr<C2Component> component,
+                         uint32_t errorCode) override {
+        mThis->onError(component, errorCode);
+    }
+
+private:
+    SimplePlayer * const mThis;
+};
+
+
+SimplePlayer::SimplePlayer()
+    : mListener(new Listener(this)),
+      mProducerListener(new DummyProducerListener),
+      mLinearPoolId(C2BlockPool::PLATFORM_START),
+      mComposerClient(new SurfaceComposerClient) {
+    CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
+
+    std::shared_ptr<C2AllocatorStore> store = GetCodec2PlatformAllocatorStore();
+    CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mAllocIon), C2_OK);
+    mLinearPool = std::make_shared<C2PooledBlockPool>(mAllocIon, mLinearPoolId++);
+
+    mControl = mComposerClient->createSurface(
+            String8("A Surface"),
+            1280,
+            800,
+            HAL_PIXEL_FORMAT_YV12);
+            //PIXEL_FORMAT_RGB_565);
+
+    CHECK(mControl != nullptr);
+    CHECK(mControl->isValid());
+
+    SurfaceComposerClient::Transaction{}
+            .setLayer(mControl, INT_MAX)
+            .show(mControl)
+            .apply();
+
+    mSurface = mControl->getSurface();
+    CHECK(mSurface != nullptr);
+    mSurface->connect(NATIVE_WINDOW_API_CPU, mProducerListener);
+}
+
+SimplePlayer::~SimplePlayer() {
+    mComposerClient->dispose();
+}
+
+void SimplePlayer::onWorkDone(
+        std::weak_ptr<C2Component> component, std::list<std::unique_ptr<C2Work>> workItems) {
+    ALOGV("SimplePlayer::onWorkDone");
+    (void) component;
+    ULock l(mProcessedLock);
+    for (auto & item : workItems) {
+        mProcessedWork.push_back(std::move(item));
+    }
+    mProcessedCondition.notify_all();
+}
+
+void SimplePlayer::onTripped(
+        std::weak_ptr<C2Component> component,
+        std::vector<std::shared_ptr<C2SettingResult>> settingResult) {
+    (void) component;
+    (void) settingResult;
+    // TODO
+}
+
+void SimplePlayer::onError(std::weak_ptr<C2Component> component, uint32_t errorCode) {
+    (void) component;
+    (void) errorCode;
+    // TODO
+}
+
+void SimplePlayer::play(const sp<IMediaSource> &source) {
+    ALOGV("SimplePlayer::play");
+    sp<AMessage> format;
+    (void) convertMetaDataToMessage(source->getFormat(), &format);
+
+    sp<ABuffer> csd0, csd1;
+    format->findBuffer("csd-0", &csd0);
+    format->findBuffer("csd-1", &csd1);
+
+    status_t err = source->start();
+
+    if (err != OK) {
+        fprintf(stderr, "source returned error %d (0x%08x)\n", err, err);
+        return;
+    }
+
+    std::shared_ptr<C2ComponentStore> store = GetCodec2PlatformComponentStore();
+    std::shared_ptr<C2Component> component;
+    (void)store->createComponent("c2.android.avc.decoder", &component);
+
+    (void)component->setListener_vb(mListener, C2_DONT_BLOCK);
+    std::unique_ptr<C2PortBlockPoolsTuning::output> pools =
+        C2PortBlockPoolsTuning::output::AllocUnique({ (uint64_t)C2BlockPool::BASIC_GRAPHIC });
+    std::vector<std::unique_ptr<C2SettingResult>> result;
+    (void)component->intf()->config_vb({pools.get()}, C2_DONT_BLOCK, &result);
+    component->start();
+
+    for (int i = 0; i < 8; ++i) {
+        mWorkQueue.emplace_back(new C2Work);
+    }
+
+    std::atomic_bool running(true);
+    std::thread surfaceThread([this, &running]() {
+        const sp<IGraphicBufferProducer> &igbp = mSurface->getIGraphicBufferProducer();
+        while (running) {
+            std::unique_ptr<C2Work> work;
+            {
+                ULock l(mProcessedLock);
+                if (mProcessedWork.empty()) {
+                    mProcessedCondition.wait_for(l, 100ms);
+                    if (mProcessedWork.empty()) {
+                        continue;
+                    }
+                }
+                work.swap(mProcessedWork.front());
+                mProcessedWork.pop_front();
+            }
+            int slot;
+            sp<Fence> fence;
+            ALOGV("Render: Frame #%lld", work->worklets.front()->output.ordinal.frameIndex.peekll());
+            const std::shared_ptr<C2Buffer> &output = work->worklets.front()->output.buffers[0];
+            if (output) {
+                const C2ConstGraphicBlock block = output->data().graphicBlocks().front();
+                native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(block.handle());
+                sp<GraphicBuffer> buffer(new GraphicBuffer(
+                        grallocHandle,
+                        GraphicBuffer::CLONE_HANDLE,
+                        block.width(),
+                        block.height(),
+                        HAL_PIXEL_FORMAT_YV12,
+                        1,
+                        (uint64_t)GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                        block.width()));
+                native_handle_delete(grallocHandle);
+
+                status_t err = igbp->attachBuffer(&slot, buffer);
+
+                IGraphicBufferProducer::QueueBufferInput qbi(
+                        (work->worklets.front()->output.ordinal.timestamp * 1000ll).peekll(),
+                        false,
+                        HAL_DATASPACE_UNKNOWN,
+                        Rect(block.width(), block.height()),
+                        NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+                        0,
+                        Fence::NO_FENCE,
+                        0);
+                IGraphicBufferProducer::QueueBufferOutput qbo;
+                err = igbp->queueBuffer(slot, qbi, &qbo);
+            }
+
+            work->input.buffers.clear();
+            work->worklets.clear();
+
+            ULock l(mQueueLock);
+            mWorkQueue.push_back(std::move(work));
+            mQueueCondition.notify_all();
+        }
+        ALOGV("render loop finished");
+    });
+
+    long numFrames = 0;
+    mLinearPool.reset(new C2PooledBlockPool(mAllocIon, mLinearPoolId++));
+
+    for (;;) {
+        size_t size = 0u;
+        void *data = nullptr;
+        int64_t timestamp = 0u;
+        MediaBufferBase *buffer = nullptr;
+        sp<ABuffer> csd;
+        if (csd0 != nullptr) {
+            csd = csd0;
+            csd0 = nullptr;
+        } else if (csd1 != nullptr) {
+            csd = csd1;
+            csd1 = nullptr;
+        } else {
+            status_t err = source->read(&buffer);
+            if (err != OK) {
+                CHECK(buffer == nullptr);
+
+                if (err == INFO_FORMAT_CHANGED) {
+                    continue;
+                }
+
+                break;
+            }
+            MetaDataBase &meta = buffer->meta_data();
+            CHECK(meta.findInt64(kKeyTime, &timestamp));
+
+            size = buffer->size();
+            data = buffer->data();
+        }
+
+        if (csd != nullptr) {
+            size = csd->size();
+            data = csd->data();
+        }
+
+        // Prepare C2Work
+
+        std::unique_ptr<C2Work> work;
+        while (!work) {
+            ULock l(mQueueLock);
+            if (!mWorkQueue.empty()) {
+                work.swap(mWorkQueue.front());
+                mWorkQueue.pop_front();
+            } else {
+                mQueueCondition.wait_for(l, 100ms);
+            }
+        }
+        work->input.flags = (C2FrameData::flags_t)0;
+        work->input.ordinal.timestamp = timestamp;
+        work->input.ordinal.frameIndex = numFrames;
+
+        std::shared_ptr<C2LinearBlock> block;
+        mLinearPool->fetchLinearBlock(
+                size,
+                { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE },
+                &block);
+        C2WriteView view = block->map().get();
+        if (view.error() != C2_OK) {
+            fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
+            break;
+        }
+        memcpy(view.base(), data, size);
+
+        work->input.buffers.clear();
+        work->input.buffers.emplace_back(new LinearBuffer(block));
+        work->worklets.clear();
+        work->worklets.emplace_back(new C2Worklet);
+
+        std::list<std::unique_ptr<C2Work>> items;
+        items.push_back(std::move(work));
+
+        ALOGV("Frame #%ld size = %zu", numFrames, size);
+        // DO THE DECODING
+        component->queue_nb(&items);
+
+        if (buffer) {
+            buffer->release();
+            buffer = nullptr;
+        }
+
+        ++numFrames;
+    }
+    ALOGV("main loop finished");
+    source->stop();
+    running.store(false);
+    surfaceThread.join();
+
+    component->release();
+    printf("\n");
+}
+
+}  // namespace
+
+static void usage(const char *me) {
+    fprintf(stderr, "usage: %s [options] [input_filename]\n", me);
+    fprintf(stderr, "       -h(elp)\n");
+}
+
+int main(int argc, char **argv) {
+    android::ProcessState::self()->startThreadPool();
+
+    int res;
+    while ((res = getopt(argc, argv, "h")) >= 0) {
+        switch (res) {
+            case 'h':
+            default:
+            {
+                usage(argv[0]);
+                exit(1);
+                break;
+            }
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 1) {
+        fprintf(stderr, "No input file specified\n");
+        return 1;
+    }
+
+    status_t err = OK;
+    SimplePlayer player;
+
+    for (int k = 0; k < argc && err == OK; ++k) {
+        const char *filename = argv[k];
+
+        sp<DataSource> dataSource =
+            DataSourceFactory::CreateFromURI(nullptr /* httpService */, filename);
+
+        if (strncasecmp(filename, "sine:", 5) && dataSource == nullptr) {
+            fprintf(stderr, "Unable to create data source.\n");
+            return 1;
+        }
+
+        Vector<sp<IMediaSource> > mediaSources;
+        sp<IMediaSource> mediaSource;
+
+        sp<IMediaExtractor> extractor = MediaExtractorFactory::Create(dataSource);
+
+        if (extractor == nullptr) {
+            fprintf(stderr, "could not create extractor.\n");
+            return -1;
+        }
+
+        sp<MetaData> meta = extractor->getMetaData();
+
+        if (meta != nullptr) {
+            const char *mime;
+            if (!meta->findCString(kKeyMIMEType, &mime)) {
+                fprintf(stderr, "extractor did not provide MIME type.\n");
+                return -1;
+            }
+        }
+
+        size_t numTracks = extractor->countTracks();
+
+        size_t i;
+        for (i = 0; i < numTracks; ++i) {
+            meta = extractor->getTrackMetaData(i, 0);
+
+            if (meta == nullptr) {
+                break;
+            }
+            const char *mime;
+            meta->findCString(kKeyMIMEType, &mime);
+
+            // TODO: allowing AVC only for the time being
+            if (!strncasecmp(mime, "video/avc", 9)) {
+                break;
+            }
+
+            meta = nullptr;
+        }
+
+        if (meta == nullptr) {
+            fprintf(stderr, "No AVC track found.\n");
+            return -1;
+        }
+
+        mediaSource = extractor->getTrack(i);
+        if (mediaSource == nullptr) {
+            fprintf(stderr, "skip NULL track %zu, total tracks %zu.\n", i, numTracks);
+            return -1;
+        }
+
+        player.play(mediaSource);
+    }
+
+    return 0;
+}
diff --git a/media/codec2/components/flac/Android.bp b/media/codec2/components/flac/Android.bp
new file mode 100644
index 0000000..e5eb51d
--- /dev/null
+++ b/media/codec2/components/flac/Android.bp
@@ -0,0 +1,30 @@
+cc_library_shared {
+    name: "libcodec2_soft_flacdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    header_libs: ["libFLAC-headers"],
+
+    srcs: ["C2SoftFlacDec.cpp"],
+
+    shared_libs: [
+        "libstagefright_flacdec",
+    ],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_flacenc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftFlacEnc.cpp"],
+
+    static_libs: [
+        "libaudioutils",
+        "libFLAC",
+    ],
+}
diff --git a/media/codec2/components/flac/C2SoftFlacDec.cpp b/media/codec2/components/flac/C2SoftFlacDec.cpp
new file mode 100644
index 0000000..4039b9b
--- /dev/null
+++ b/media/codec2/components/flac/C2SoftFlacDec.cpp
@@ -0,0 +1,378 @@
+/*
+ * 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 "C2SoftFlacDec"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftFlacDec.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.flac.decoder";
+
+}  // namespace
+
+class C2SoftFlacDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_FLAC) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::output(0u, 44100))
+                .withFields({C2F(mSampleRate, value).inRange(1, 655350)})
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::output(0u, 1))
+                .withFields({C2F(mChannelCount, value).inRange(1, 8)})
+                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::input(0u, 768000))
+                .withFields({C2F(mBitrate, value).inRange(1, 21000000)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 32768))
+                .build());
+
+        addParameter(
+                DefineParam(mPcmEncodingInfo, C2_PARAMKEY_PCM_ENCODING)
+                .withDefault(new C2StreamPcmEncodingInfo::output(0u, C2Config::PCM_16))
+                .withFields({C2F(mPcmEncodingInfo, value).oneOf({
+                     C2Config::PCM_16,
+                     // C2Config::PCM_8,
+                     C2Config::PCM_FLOAT})
+                })
+                .withSetter((Setter<decltype(*mPcmEncodingInfo)>::StrictValueWithNoDeps))
+                .build());
+    }
+
+    int32_t getPcmEncodingInfo() const { return mPcmEncodingInfo->value; }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+    std::shared_ptr<C2StreamPcmEncodingInfo::output> mPcmEncodingInfo;
+};
+
+C2SoftFlacDec::C2SoftFlacDec(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mFLACDecoder(nullptr) {
+}
+
+C2SoftFlacDec::~C2SoftFlacDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftFlacDec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+c2_status_t C2SoftFlacDec::onStop() {
+    if (mFLACDecoder) mFLACDecoder->flush();
+    memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+    mHasStreamInfo = false;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    return C2_OK;
+}
+
+void C2SoftFlacDec::onReset() {
+    mInputBufferCount = 0;
+    (void)onStop();
+}
+
+void C2SoftFlacDec::onRelease() {
+    mInputBufferCount = 0;
+    if (mFLACDecoder) delete mFLACDecoder;
+    mFLACDecoder = nullptr;
+}
+
+c2_status_t C2SoftFlacDec::onFlush_sm() {
+    return onStop();
+}
+
+status_t C2SoftFlacDec::initDecoder() {
+    if (mFLACDecoder) {
+        delete mFLACDecoder;
+    }
+    mFLACDecoder = FLACDecoder::Create();
+    if (!mFLACDecoder) {
+        ALOGE("initDecoder: failed to create FLACDecoder");
+        mSignalledError = true;
+        return NO_MEMORY;
+    }
+
+    memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+    mHasStreamInfo = false;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mInputBufferCount = 0;
+
+    return OK;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+// (TODO) add multiframe support, in plugin and FLACDecoder.cpp
+void C2SoftFlacDec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    C2ReadView rView = mDummyReadView;
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+    bool codecConfig = (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+          (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+
+    if (inSize == 0) {
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+
+    if (mInputBufferCount == 0 && !codecConfig) {
+        ALOGV("First frame has to include configuration, forcing config");
+        codecConfig = true;
+    }
+
+    uint8_t *input = const_cast<uint8_t *>(rView.data() + inOffset);
+    if (codecConfig) {
+        status_t decoderErr = mFLACDecoder->parseMetadata(input, inSize);
+        if (decoderErr != OK && decoderErr != WOULD_BLOCK) {
+            ALOGE("process: FLACDecoder parseMetaData returns error %d", decoderErr);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        mInputBufferCount++;
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+
+        if (decoderErr == WOULD_BLOCK) {
+            ALOGV("process: parseMetadata is Blocking, Continue %d", decoderErr);
+        } else {
+            mStreamInfo = mFLACDecoder->getStreamInfo();
+            if (mStreamInfo.sample_rate && mStreamInfo.max_blocksize &&
+                mStreamInfo.channels) {
+                mHasStreamInfo = true;
+                C2StreamSampleRateInfo::output sampleRateInfo(
+                    0u, mStreamInfo.sample_rate);
+                C2StreamChannelCountInfo::output channelCountInfo(
+                    0u, mStreamInfo.channels);
+                std::vector<std::unique_ptr<C2SettingResult>> failures;
+                c2_status_t err =
+                    mIntf->config({&sampleRateInfo, &channelCountInfo},
+                                  C2_MAY_BLOCK, &failures);
+                if (err == OK) {
+                    work->worklets.front()->output.configUpdate.push_back(
+                        C2Param::Copy(sampleRateInfo));
+                    work->worklets.front()->output.configUpdate.push_back(
+                        C2Param::Copy(channelCountInfo));
+                } else {
+                    ALOGE("Config Update failed");
+                    mSignalledError = true;
+                    work->result = C2_CORRUPTED;
+                    return;
+                }
+            }
+            ALOGD("process: decoder configuration : %d Hz, %d channels, %d samples,"
+                  " %d block size", mStreamInfo.sample_rate, mStreamInfo.channels,
+                  (int)mStreamInfo.total_samples, mStreamInfo.max_blocksize);
+        }
+        return;
+    }
+
+    const bool outputFloat = mIntf->getPcmEncodingInfo() == C2Config::PCM_FLOAT;
+    const size_t sampleSize = outputFloat ? sizeof(float) : sizeof(short);
+    size_t outSize = mHasStreamInfo ?
+            mStreamInfo.max_blocksize * mStreamInfo.channels * sampleSize
+          : kMaxBlockSize * FLACDecoder::kMaxChannels * sampleSize;
+
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(outSize, usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    status_t decoderErr = mFLACDecoder->decodeOneFrame(
+                            input, inSize, wView.data(), &outSize, outputFloat);
+    if (decoderErr != OK) {
+        ALOGE("process: FLACDecoder decodeOneFrame returns error %d", decoderErr);
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    mInputBufferCount++;
+    ALOGV("out buffer attr. size %zu", outSize);
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, 0, outSize));
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+    }
+}
+
+c2_status_t C2SoftFlacDec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    if (mFLACDecoder) mFLACDecoder->flush();
+
+    return C2_OK;
+}
+
+class C2SoftFlacDecFactory : public C2ComponentFactory {
+public:
+    C2SoftFlacDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftFlacDec(COMPONENT_NAME,
+                                      id,
+                                      std::make_shared<C2SoftFlacDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftFlacDec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftFlacDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftFlacDecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftFlacDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/flac/C2SoftFlacDec.h b/media/codec2/components/flac/C2SoftFlacDec.h
new file mode 100644
index 0000000..b491bfd
--- /dev/null
+++ b/media/codec2/components/flac/C2SoftFlacDec.h
@@ -0,0 +1,66 @@
+/*
+ * 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_C2_SOFT_FLAC_DEC_H_
+#define ANDROID_C2_SOFT_FLAC_DEC_H_
+
+#include <SimpleC2Component.h>
+
+#include "FLACDecoder.h"
+
+namespace android {
+
+struct C2SoftFlacDec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftFlacDec(const char *name, c2_node_id_t id,
+                      const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftFlacDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    enum {
+        kMaxBlockSize   = 4096
+    };
+
+    std::shared_ptr<IntfImpl> mIntf;
+    FLACDecoder *mFLACDecoder;
+    FLAC__StreamMetadata_StreamInfo mStreamInfo;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+    bool mHasStreamInfo;
+    size_t mInputBufferCount;
+
+    status_t initDecoder();
+
+    C2_DO_NOT_COPY(C2SoftFlacDec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_FLAC_DEC_H_
diff --git a/media/codec2/components/flac/C2SoftFlacEnc.cpp b/media/codec2/components/flac/C2SoftFlacEnc.cpp
new file mode 100644
index 0000000..408db7e
--- /dev/null
+++ b/media/codec2/components/flac/C2SoftFlacEnc.cpp
@@ -0,0 +1,492 @@
+/*
+ * 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 "C2SoftFlacEnc"
+#include <log/log.h>
+
+#include <audio_utils/primitives.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftFlacEnc.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.flac.encoder";
+
+}  // namespace
+
+class C2SoftFlacEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_FLAC) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::input(0u, 44100))
+                .withFields({C2F(mSampleRate, value).inRange(1, 655350)})
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::input(0u, 1))
+                .withFields({C2F(mChannelCount, value).inRange(1, 2)})
+                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
+                .build());
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::output(0u, 768000))
+                .withFields({C2F(mBitrate, value).inRange(1, 21000000)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+        addParameter(
+                DefineParam(mComplexity, C2_PARAMKEY_COMPLEXITY)
+                .withDefault(new C2StreamComplexityTuning::output(0u,
+                    FLAC_COMPRESSION_LEVEL_DEFAULT))
+                .withFields({C2F(mComplexity, value).inRange(
+                    FLAC_COMPRESSION_LEVEL_MIN, FLAC_COMPRESSION_LEVEL_MAX)})
+                .withSetter(Setter<decltype(*mComplexity)>::NonStrictValueWithNoDeps)
+                .build());
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 4608))
+                .build());
+
+        addParameter(
+                DefineParam(mPcmEncodingInfo, C2_PARAMKEY_PCM_ENCODING)
+                .withDefault(new C2StreamPcmEncodingInfo::input(0u, C2Config::PCM_16))
+                .withFields({C2F(mPcmEncodingInfo, value).oneOf({
+                     C2Config::PCM_16,
+                     // C2Config::PCM_8,
+                     C2Config::PCM_FLOAT})
+                })
+                .withSetter((Setter<decltype(*mPcmEncodingInfo)>::StrictValueWithNoDeps))
+                .build());
+    }
+
+    uint32_t getSampleRate() const { return mSampleRate->value; }
+    uint32_t getChannelCount() const { return mChannelCount->value; }
+    uint32_t getBitrate() const { return mBitrate->value; }
+    uint32_t getComplexity() const { return mComplexity->value; }
+    int32_t getPcmEncodingInfo() const { return mPcmEncodingInfo->value; }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamComplexityTuning::output> mComplexity;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+    std::shared_ptr<C2StreamPcmEncodingInfo::input> mPcmEncodingInfo;
+};
+
+C2SoftFlacEnc::C2SoftFlacEnc(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mFlacStreamEncoder(nullptr),
+      mInputBufferPcm32(nullptr) {
+}
+
+C2SoftFlacEnc::~C2SoftFlacEnc() {
+    onRelease();
+}
+
+c2_status_t C2SoftFlacEnc::onInit() {
+    mFlacStreamEncoder = FLAC__stream_encoder_new();
+    if (!mFlacStreamEncoder) return C2_CORRUPTED;
+
+    mInputBufferPcm32 = (FLAC__int32*) malloc(
+            kInBlockSize * kMaxNumChannels * sizeof(FLAC__int32));
+    if (!mInputBufferPcm32) return C2_NO_MEMORY;
+
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mIsFirstFrame = true;
+    mAnchorTimeStamp = 0ull;
+    mProcessedSamples = 0u;
+    mEncoderWriteData = false;
+    mEncoderReturnedNbBytes = 0;
+    mHeaderOffset = 0;
+    mWroteHeader = false;
+
+    status_t err = configureEncoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+void C2SoftFlacEnc::onRelease() {
+    if (mFlacStreamEncoder) {
+        FLAC__stream_encoder_delete(mFlacStreamEncoder);
+        mFlacStreamEncoder = nullptr;
+    }
+
+    if (mInputBufferPcm32) {
+        free(mInputBufferPcm32);
+        mInputBufferPcm32 = nullptr;
+    }
+}
+
+void C2SoftFlacEnc::onReset() {
+    (void) onStop();
+}
+
+c2_status_t C2SoftFlacEnc::onStop() {
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mIsFirstFrame = true;
+    mAnchorTimeStamp = 0ull;
+    mProcessedSamples = 0u;
+    mEncoderWriteData = false;
+    mEncoderReturnedNbBytes = 0;
+    mHeaderOffset = 0;
+    mWroteHeader = false;
+
+    c2_status_t status = drain(DRAIN_COMPONENT_NO_EOS, nullptr);
+    if (C2_OK != status) return status;
+
+    status_t err = configureEncoder();
+    if (err != OK) mSignalledError = true;
+    return C2_OK;
+}
+
+c2_status_t C2SoftFlacEnc::onFlush_sm() {
+    return onStop();
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+}
+
+void C2SoftFlacEnc::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    C2ReadView rView = mDummyReadView;
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+              inSize, (int)work->input.ordinal.timestamp.peeku(),
+              (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+    if (mIsFirstFrame && inSize) {
+        mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+        mIsFirstFrame = false;
+    }
+
+    if (!mWroteHeader) {
+        std::unique_ptr<C2StreamInitDataInfo::output> csd =
+            C2StreamInitDataInfo::output::AllocUnique(mHeaderOffset, 0u);
+        if (!csd) {
+            ALOGE("CSD allocation failed");
+            mSignalledError = true;
+            work->result = C2_NO_MEMORY;
+            return;
+        }
+        memcpy(csd->m.value, mHeader, mHeaderOffset);
+        ALOGV("put csd, %d bytes", mHeaderOffset);
+
+        work->worklets.front()->output.configUpdate.push_back(std::move(csd));
+        mWroteHeader = true;
+    }
+
+    const uint32_t sampleRate = mIntf->getSampleRate();
+    const uint32_t channelCount = mIntf->getChannelCount();
+    const bool inputFloat = mIntf->getPcmEncodingInfo() == C2Config::PCM_FLOAT;
+    const unsigned sampleSize = inputFloat ? sizeof(float) : sizeof(int16_t);
+    const unsigned frameSize = channelCount * sampleSize;
+    const uint64_t outTimeStamp = mProcessedSamples * 1000000ll / sampleRate;
+
+    size_t outCapacity = inSize;
+    outCapacity += mBlockSize * frameSize;
+
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &mOutputBlock);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = mOutputBlock->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    mEncoderWriteData = true;
+    mEncoderReturnedNbBytes = 0;
+    size_t inPos = 0;
+    while (inPos < inSize) {
+        const uint8_t *inPtr = rView.data() + inOffset;
+        const size_t processSize = MIN(kInBlockSize * frameSize, (inSize - inPos));
+        const unsigned nbInputFrames = processSize / frameSize;
+        const unsigned nbInputSamples = processSize / sampleSize;
+
+        ALOGV("about to encode %zu bytes", processSize);
+        if (inputFloat) {
+            const float * const pcmFloat = reinterpret_cast<const float *>(inPtr + inPos);
+            memcpy_to_q8_23_from_float_with_clamp(mInputBufferPcm32, pcmFloat, nbInputSamples);
+        } else {
+            const int16_t * const pcm16 = reinterpret_cast<const int16_t *>(inPtr + inPos);
+            for (unsigned i = 0; i < nbInputSamples; i++) {
+                mInputBufferPcm32[i] = (FLAC__int32) pcm16[i];
+            }
+        }
+
+        FLAC__bool ok = FLAC__stream_encoder_process_interleaved(
+                mFlacStreamEncoder, mInputBufferPcm32, nbInputFrames);
+        if (!ok) {
+            ALOGE("error encountered during encoding");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            mOutputBlock.reset();
+            return;
+        }
+        inPos += processSize;
+    }
+    if (eos && (C2_OK != drain(DRAIN_COMPONENT_WITH_EOS, pool))) {
+        ALOGE("error encountered during encoding");
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        mOutputBlock.reset();
+        return;
+    }
+    fillEmptyWork(work);
+    if (mEncoderReturnedNbBytes != 0) {
+        std::shared_ptr<C2Buffer> buffer = createLinearBuffer(std::move(mOutputBlock), 0, mEncoderReturnedNbBytes);
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+    } else {
+        ALOGV("encoder process_interleaved returned without data to write");
+    }
+    mOutputBlock = nullptr;
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+    }
+    mEncoderWriteData = false;
+    mEncoderReturnedNbBytes = 0;
+}
+
+FLAC__StreamEncoderWriteStatus C2SoftFlacEnc::onEncodedFlacAvailable(
+        const FLAC__byte buffer[], size_t bytes, unsigned samples,
+        unsigned current_frame) {
+    (void) current_frame;
+    ALOGV("%s (bytes=%zu, samples=%u, curr_frame=%u)", __func__, bytes, samples,
+          current_frame);
+
+    if (samples == 0) {
+        ALOGI("saving %zu bytes of header", bytes);
+        memcpy(mHeader + mHeaderOffset, buffer, bytes);
+        mHeaderOffset += bytes;// will contain header size when finished receiving header
+        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+    }
+
+    if ((samples == 0) || !mEncoderWriteData) {
+        // called by the encoder because there's header data to save, but it's not the role
+        // of this component (unless WRITE_FLAC_HEADER_IN_FIRST_BUFFER is defined)
+        ALOGV("ignoring %zu bytes of header data (samples=%d)", bytes, samples);
+        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+    }
+
+    // write encoded data
+    C2WriteView wView = mOutputBlock->map().get();
+    uint8_t* outData = wView.data();
+    ALOGV("writing %zu bytes of encoded data on output", bytes);
+    // increment mProcessedSamples to maintain audio synchronization during
+    // play back
+    mProcessedSamples += samples;
+    if (bytes + mEncoderReturnedNbBytes > mOutputBlock->capacity()) {
+        ALOGE("not enough space left to write encoded data, dropping %zu bytes", bytes);
+        // a fatal error would stop the encoding
+        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+    }
+    memcpy(outData + mEncoderReturnedNbBytes, buffer, bytes);
+    mEncoderReturnedNbBytes += bytes;
+    return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+}
+
+
+status_t C2SoftFlacEnc::configureEncoder() {
+    ALOGV("%s numChannel=%d, sampleRate=%d", __func__, mIntf->getChannelCount(), mIntf->getSampleRate());
+
+    if (mSignalledError || !mFlacStreamEncoder) {
+        ALOGE("can't configure encoder: no encoder or invalid state");
+        return UNKNOWN_ERROR;
+    }
+
+    const bool inputFloat = mIntf->getPcmEncodingInfo() == C2Config::PCM_FLOAT;
+    const int bitsPerSample = inputFloat ? 24 : 16;
+    FLAC__bool ok = true;
+    ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mIntf->getChannelCount());
+    ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mIntf->getSampleRate());
+    ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, bitsPerSample);
+    ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder,
+                    mIntf->getComplexity());
+    ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false);
+    if (!ok) {
+        ALOGE("unknown error when configuring encoder");
+        return UNKNOWN_ERROR;
+    }
+
+    ok &= FLAC__STREAM_ENCODER_INIT_STATUS_OK ==
+            FLAC__stream_encoder_init_stream(mFlacStreamEncoder,
+                    flacEncoderWriteCallback    /*write_callback*/,
+                    nullptr /*seek_callback*/,
+                    nullptr /*tell_callback*/,
+                    nullptr /*metadata_callback*/,
+                    (void *) this /*client_data*/);
+
+    if (!ok) {
+        ALOGE("unknown error when configuring encoder");
+        return UNKNOWN_ERROR;
+    }
+
+    mBlockSize = FLAC__stream_encoder_get_blocksize(mFlacStreamEncoder);
+
+    ALOGV("encoder successfully configured");
+    return OK;
+}
+
+FLAC__StreamEncoderWriteStatus C2SoftFlacEnc::flacEncoderWriteCallback(
+            const FLAC__StreamEncoder *,
+            const FLAC__byte buffer[],
+            size_t bytes,
+            unsigned samples,
+            unsigned current_frame,
+            void *client_data) {
+    return ((C2SoftFlacEnc*) client_data)->onEncodedFlacAvailable(
+            buffer, bytes, samples, current_frame);
+}
+
+c2_status_t C2SoftFlacEnc::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    switch (drainMode) {
+        case NO_DRAIN:
+            ALOGW("drain with NO_DRAIN: no-op");
+            return C2_OK;
+        case DRAIN_CHAIN:
+            ALOGW("DRAIN_CHAIN not supported");
+            return C2_OMITTED;
+        case DRAIN_COMPONENT_WITH_EOS:
+            // TODO: This flag is not being sent back to the client
+            // because there are no items in PendingWork queue as all the
+            // inputs are being sent back with emptywork or valid encoded data
+            // mSignalledOutputEos = true;
+        case DRAIN_COMPONENT_NO_EOS:
+            break;
+        default:
+            return C2_BAD_VALUE;
+    }
+    FLAC__bool ok = FLAC__stream_encoder_finish(mFlacStreamEncoder);
+    if (!ok) return C2_CORRUPTED;
+    mIsFirstFrame = true;
+    mAnchorTimeStamp = 0ull;
+    mProcessedSamples = 0u;
+
+    return C2_OK;
+}
+
+class C2SoftFlacEncFactory : public C2ComponentFactory {
+public:
+    C2SoftFlacEncFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftFlacEnc(COMPONENT_NAME,
+                                  id,
+                                  std::make_shared<C2SoftFlacEnc::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftFlacEnc::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftFlacEnc::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftFlacEncFactory() override = default;
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftFlacEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/flac/C2SoftFlacEnc.h b/media/codec2/components/flac/C2SoftFlacEnc.h
new file mode 100644
index 0000000..b3f01d5
--- /dev/null
+++ b/media/codec2/components/flac/C2SoftFlacEnc.h
@@ -0,0 +1,88 @@
+/*
+ * 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_C2_SOFT_FLAC_ENC_H_
+#define ANDROID_C2_SOFT_FLAC_ENC_H_
+
+#include <SimpleC2Component.h>
+
+#include "FLAC/stream_encoder.h"
+
+#define FLAC_COMPRESSION_LEVEL_MIN     0
+#define FLAC_COMPRESSION_LEVEL_DEFAULT 5
+#define FLAC_COMPRESSION_LEVEL_MAX     8
+
+#define FLAC_HEADER_SIZE               128
+
+#define MIN(a, b)                      (((a) < (b)) ? (a) : (b))
+
+namespace android {
+
+class C2SoftFlacEnc : public SimpleC2Component {
+public:
+    class IntfImpl;
+
+    C2SoftFlacEnc(const char *name, c2_node_id_t id, const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftFlacEnc();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    status_t configureEncoder();
+    static FLAC__StreamEncoderWriteStatus flacEncoderWriteCallback(
+            const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[],
+            size_t bytes, unsigned samples, unsigned current_frame,
+            void *client_data);
+    FLAC__StreamEncoderWriteStatus onEncodedFlacAvailable(
+            const FLAC__byte buffer[], size_t bytes, unsigned samples,
+            unsigned current_frame);
+
+    std::shared_ptr<IntfImpl> mIntf;
+    const unsigned int kInBlockSize = 1152;
+    const unsigned int kMaxNumChannels = 2;
+    FLAC__StreamEncoder* mFlacStreamEncoder;
+    FLAC__int32* mInputBufferPcm32;
+    std::shared_ptr<C2LinearBlock> mOutputBlock;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+    uint32_t mBlockSize;
+    bool mIsFirstFrame;
+    uint64_t mAnchorTimeStamp;
+    uint64_t mProcessedSamples;
+    // should the data received by the callback be written to the output port
+    bool mEncoderWriteData;
+    size_t mEncoderReturnedNbBytes;
+    unsigned mHeaderOffset;
+    bool mWroteHeader;
+    char mHeader[FLAC_HEADER_SIZE];
+
+    C2_DO_NOT_COPY(C2SoftFlacEnc);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_FLAC_ENC_H_
diff --git a/media/common_time/MODULE_LICENSE_APACHE2 b/media/codec2/components/flac/MODULE_LICENSE_APACHE2
similarity index 100%
copy from media/common_time/MODULE_LICENSE_APACHE2
copy to media/codec2/components/flac/MODULE_LICENSE_APACHE2
diff --git a/media/common_time/NOTICE b/media/codec2/components/flac/NOTICE
similarity index 100%
copy from media/common_time/NOTICE
copy to media/codec2/components/flac/NOTICE
diff --git a/media/codec2/components/g711/Android.bp b/media/codec2/components/g711/Android.bp
new file mode 100644
index 0000000..3ede68c
--- /dev/null
+++ b/media/codec2/components/g711/Android.bp
@@ -0,0 +1,23 @@
+cc_library_shared {
+    name: "libcodec2_soft_g711alawdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftG711Dec.cpp"],
+
+    cflags: [
+        "-DALAW",
+    ],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_g711mlawdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftG711Dec.cpp"],
+}
diff --git a/media/codec2/components/g711/C2SoftG711Dec.cpp b/media/codec2/components/g711/C2SoftG711Dec.cpp
new file mode 100644
index 0000000..43b843a
--- /dev/null
+++ b/media/codec2/components/g711/C2SoftG711Dec.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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 "C2SoftG711Dec"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftG711Dec.h"
+
+namespace android {
+
+namespace {
+
+#ifdef ALAW
+constexpr char COMPONENT_NAME[] = "c2.android.g711.alaw.decoder";
+#else
+constexpr char COMPONENT_NAME[] = "c2.android.g711.mlaw.decoder";
+#endif
+
+}  // namespace
+
+class C2SoftG711Dec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+#ifdef ALAW
+                MEDIA_MIMETYPE_AUDIO_G711_ALAW
+#else
+                MEDIA_MIMETYPE_AUDIO_G711_MLAW
+#endif
+                ) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::output(0u, 8000))
+                .withFields({C2F(mSampleRate, value).inRange(8000, 48000)})
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::output(0u, 1))
+                .withFields({C2F(mChannelCount, value).equalTo(1)})
+                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::input(0u, 64000))
+                .withFields({C2F(mBitrate, value).equalTo(64000)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 8192))
+                .build());
+    }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+};
+
+C2SoftG711Dec::C2SoftG711Dec(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl) {
+}
+
+C2SoftG711Dec::~C2SoftG711Dec() {
+    onRelease();
+}
+
+c2_status_t C2SoftG711Dec::onInit() {
+    mSignalledOutputEos = false;
+    return C2_OK;
+}
+
+c2_status_t C2SoftG711Dec::onStop() {
+    mSignalledOutputEos = false;
+    return C2_OK;
+}
+
+void C2SoftG711Dec::onReset() {
+    (void)onStop();
+}
+
+void C2SoftG711Dec::onRelease() {
+}
+
+c2_status_t C2SoftG711Dec::onFlush_sm() {
+    return onStop();
+}
+
+void C2SoftG711Dec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    C2ReadView rView = mDummyReadView;
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+    int outSize = inSize * sizeof(int16_t);
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+          (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+
+    if (inSize == 0) {
+        work->worklets.front()->output.flags = work->input.flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+
+    uint8_t *inputptr = const_cast<uint8_t *>(rView.data() + inOffset);
+
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(outSize, usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+    int16_t *outputptr = reinterpret_cast<int16_t *>(wView.data());
+
+#ifdef ALAW
+    DecodeALaw(outputptr, inputptr, inSize);
+#else
+    DecodeMLaw(outputptr, inputptr, inSize);
+#endif
+
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.buffers.push_back(createLinearBuffer(block));
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+    }
+}
+
+c2_status_t C2SoftG711Dec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    return C2_OK;
+}
+
+#ifdef ALAW
+void C2SoftG711Dec::DecodeALaw(
+        int16_t *out, const uint8_t *in, size_t inSize) {
+    while (inSize > 0) {
+        inSize--;
+        int32_t x = *in++;
+
+        int32_t ix = x ^ 0x55;
+        ix &= 0x7f;
+
+        int32_t iexp = ix >> 4;
+        int32_t mant = ix & 0x0f;
+
+        if (iexp > 0) {
+            mant += 16;
+        }
+
+        mant = (mant << 4) + 8;
+
+        if (iexp > 1) {
+            mant = mant << (iexp - 1);
+        }
+
+        *out++ = (x > 127) ? mant : -mant;
+    }
+}
+#else
+void C2SoftG711Dec::DecodeMLaw(
+        int16_t *out, const uint8_t *in, size_t inSize) {
+    while (inSize > 0) {
+        inSize--;
+        int32_t x = *in++;
+
+        int32_t mantissa = ~x;
+        int32_t exponent = (mantissa >> 4) & 7;
+        int32_t segment = exponent + 1;
+        mantissa &= 0x0f;
+
+        int32_t step = 4 << segment;
+
+        int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33;
+
+        *out++ = (x < 0x80) ? -abs : abs;
+    }
+}
+#endif
+
+class C2SoftG711DecFactory : public C2ComponentFactory {
+public:
+    C2SoftG711DecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftG711Dec(COMPONENT_NAME, id,
+                               std::make_shared<C2SoftG711Dec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftG711Dec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftG711Dec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftG711DecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftG711DecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/g711/C2SoftG711Dec.h b/media/codec2/components/g711/C2SoftG711Dec.h
new file mode 100644
index 0000000..23e8ffc
--- /dev/null
+++ b/media/codec2/components/g711/C2SoftG711Dec.h
@@ -0,0 +1,59 @@
+/*
+ * 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_C2_SOFT_G711_DEC_H_
+#define ANDROID_C2_SOFT_G711_DEC_H_
+
+#include <SimpleC2Component.h>
+
+
+namespace android {
+
+struct C2SoftG711Dec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftG711Dec(const char *name, c2_node_id_t id,
+               const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftG711Dec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+private:
+    std::shared_ptr<IntfImpl> mIntf;
+    bool mSignalledOutputEos;
+
+#ifdef ALAW
+    void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize);
+#else
+    void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize);
+#endif
+
+    C2_DO_NOT_COPY(C2SoftG711Dec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_G711_DEC_H_
diff --git a/media/common_time/MODULE_LICENSE_APACHE2 b/media/codec2/components/g711/MODULE_LICENSE_APACHE2
similarity index 100%
copy from media/common_time/MODULE_LICENSE_APACHE2
copy to media/codec2/components/g711/MODULE_LICENSE_APACHE2
diff --git a/media/common_time/NOTICE b/media/codec2/components/g711/NOTICE
similarity index 100%
copy from media/common_time/NOTICE
copy to media/codec2/components/g711/NOTICE
diff --git a/media/codec2/components/gsm/Android.bp b/media/codec2/components/gsm/Android.bp
new file mode 100644
index 0000000..9330c01
--- /dev/null
+++ b/media/codec2/components/gsm/Android.bp
@@ -0,0 +1,11 @@
+cc_library_shared {
+    name: "libcodec2_soft_gsmdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftGsmDec.cpp"],
+
+    static_libs: ["libgsm"],
+}
diff --git a/media/codec2/components/gsm/C2SoftGsmDec.cpp b/media/codec2/components/gsm/C2SoftGsmDec.cpp
new file mode 100644
index 0000000..287cfc6
--- /dev/null
+++ b/media/codec2/components/gsm/C2SoftGsmDec.cpp
@@ -0,0 +1,305 @@
+/*
+ * 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 "C2SoftGsmDec"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftGsmDec.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.gsm.decoder";
+
+}  // namespace
+
+class C2SoftGsmDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_MSGSM) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::output(0u, 8000))
+                .withFields({C2F(mSampleRate, value).equalTo(8000)})
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::output(0u, 1))
+                .withFields({C2F(mChannelCount, value).equalTo(1)})
+                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::input(0u, 13200))
+                .withFields({C2F(mBitrate, value).equalTo(13200)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 1024 / MSGSM_IN_FRM_SZ * MSGSM_IN_FRM_SZ))
+                .build());
+    }
+
+   private:
+    std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+};
+
+C2SoftGsmDec::C2SoftGsmDec(const char *name, c2_node_id_t id,
+                     const std::shared_ptr<IntfImpl>& intfImpl)
+    : SimpleC2Component(
+        std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mGsm(nullptr) {
+}
+
+C2SoftGsmDec::~C2SoftGsmDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftGsmDec::onInit() {
+    if (!mGsm) mGsm = gsm_create();
+    if (!mGsm) return C2_NO_MEMORY;
+    int msopt = 1;
+    (void)gsm_option(mGsm, GSM_OPT_WAV49, &msopt);
+    mSignalledError = false;
+    mSignalledEos = false;
+    return C2_OK;
+}
+
+c2_status_t C2SoftGsmDec::onStop() {
+    if (mGsm) {
+        gsm_destroy(mGsm);
+        mGsm = nullptr;
+    }
+    if (!mGsm) mGsm = gsm_create();
+    if (!mGsm) return C2_NO_MEMORY;
+    int msopt = 1;
+    (void)gsm_option(mGsm, GSM_OPT_WAV49, &msopt);
+    mSignalledError = false;
+    mSignalledEos = false;
+    return C2_OK;
+}
+
+void C2SoftGsmDec::onReset() {
+    (void)onStop();
+}
+
+void C2SoftGsmDec::onRelease() {
+    if (mGsm) {
+        gsm_destroy(mGsm);
+        mGsm = nullptr;
+    }
+}
+
+c2_status_t C2SoftGsmDec::onFlush_sm() {
+    return onStop();
+}
+
+static size_t decodeGSM(gsm handle, int16_t *out, size_t outCapacity,
+                        uint8_t *in, size_t inSize) {
+    size_t outSize = 0;
+
+    if (inSize % MSGSM_IN_FRM_SZ == 0
+            && (inSize / MSGSM_IN_FRM_SZ * MSGSM_OUT_FRM_SZ * sizeof(*out)
+                    <= outCapacity)) {
+        while (inSize > 0) {
+            gsm_decode(handle, in, out);
+            in += FRGSM_IN_FRM_SZ;
+            inSize -= FRGSM_IN_FRM_SZ;
+            out += FRGSM_OUT_FRM_SZ;
+            outSize += FRGSM_OUT_FRM_SZ;
+
+            gsm_decode(handle, in, out);
+            in += FRGSM_IN_FRM_SZ_MINUS_1;
+            inSize -= FRGSM_IN_FRM_SZ_MINUS_1;
+            out += FRGSM_OUT_FRM_SZ;
+            outSize += FRGSM_OUT_FRM_SZ;
+        }
+    }
+
+    return outSize * sizeof(int16_t);
+}
+
+void C2SoftGsmDec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    C2ReadView rView = mDummyReadView;
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = rView.error();
+            return;
+        }
+    }
+
+    if (inSize == 0) {
+        work->worklets.front()->output.flags = work->input.flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        if (eos) {
+            mSignalledEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+          (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+
+    size_t outCapacity = (inSize / MSGSM_IN_FRM_SZ ) * MSGSM_OUT_FRM_SZ * sizeof(int16_t);
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = wView.error();
+        return;
+    }
+
+    int16_t *output = reinterpret_cast<int16_t *>(wView.data());
+    uint8_t *input = const_cast<uint8_t *>(rView.data() + inOffset);
+    size_t outSize = decodeGSM(mGsm, output, outCapacity, input, inSize);
+    if (!outSize) {
+        ALOGE("encountered improper insize or outsize");
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        return;
+    }
+    ALOGV("out buffer attr. size %zu", outSize);
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, 0, outSize));
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    if (eos) {
+        mSignalledEos = true;
+        ALOGV("signalled EOS");
+    }
+}
+
+c2_status_t C2SoftGsmDec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    return C2_OK;
+}
+
+class C2SoftGSMDecFactory : public C2ComponentFactory {
+public:
+    C2SoftGSMDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftGsmDec(COMPONENT_NAME,
+                              id,
+                              std::make_shared<C2SoftGsmDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftGsmDec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftGsmDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftGSMDecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftGSMDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/gsm/C2SoftGsmDec.h b/media/codec2/components/gsm/C2SoftGsmDec.h
new file mode 100644
index 0000000..2b209fe
--- /dev/null
+++ b/media/codec2/components/gsm/C2SoftGsmDec.h
@@ -0,0 +1,65 @@
+/*
+ * 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_C2_SOFT_GSM_DEC_H_
+#define ANDROID_C2_SOFT_GSM_DEC_H_
+
+#include <SimpleC2Component.h>
+
+
+extern "C" {
+    #include "gsm.h"
+}
+
+namespace android {
+
+#define FRGSM_IN_FRM_SZ             33
+#define FRGSM_IN_FRM_SZ_MINUS_1     32
+#define FRGSM_OUT_FRM_SZ            160
+#define MSGSM_IN_FRM_SZ             (FRGSM_IN_FRM_SZ + FRGSM_IN_FRM_SZ_MINUS_1)
+#define MSGSM_OUT_FRM_SZ            (FRGSM_OUT_FRM_SZ * 2)
+
+struct C2SoftGsmDec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftGsmDec(const char *name, c2_node_id_t id,
+              const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftGsmDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+ private:
+    std::shared_ptr<IntfImpl> mIntf;
+    gsm mGsm;
+    bool mSignalledError;
+    bool mSignalledEos;
+
+    C2_DO_NOT_COPY(C2SoftGsmDec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_GSM_DEC_H_
diff --git a/media/common_time/MODULE_LICENSE_APACHE2 b/media/codec2/components/gsm/MODULE_LICENSE_APACHE2
similarity index 100%
copy from media/common_time/MODULE_LICENSE_APACHE2
copy to media/codec2/components/gsm/MODULE_LICENSE_APACHE2
diff --git a/media/common_time/NOTICE b/media/codec2/components/gsm/NOTICE
similarity index 100%
copy from media/common_time/NOTICE
copy to media/codec2/components/gsm/NOTICE
diff --git a/media/codec2/components/hevc/Android.bp b/media/codec2/components/hevc/Android.bp
new file mode 100644
index 0000000..369bd78
--- /dev/null
+++ b/media/codec2/components/hevc/Android.bp
@@ -0,0 +1,25 @@
+cc_library_shared {
+    name: "libcodec2_soft_hevcdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_signed-defaults",
+    ],
+
+    srcs: ["C2SoftHevcDec.cpp"],
+
+    static_libs: ["libhevcdec"],
+
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_hevcenc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_signed-defaults",
+    ],
+
+    srcs: ["C2SoftHevcEnc.cpp"],
+
+    static_libs: ["libhevcenc"],
+
+}
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
new file mode 100644
index 0000000..7232572
--- /dev/null
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -0,0 +1,1028 @@
+/*
+ * 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 "C2SoftHevcDec"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <Codec2Mapper.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftHevcDec.h"
+#include "ihevcd_cxa.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.hevc.decoder";
+
+}  // namespace
+
+class C2SoftHevcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_VIDEO,
+                MEDIA_MIMETYPE_VIDEO_HEVC) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+
+        // TODO: Proper support for reorder depth.
+        addParameter(
+                DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+                .withConstValue(new C2PortActualDelayTuning::output(8u))
+                .build());
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 4096, 2),
+                    C2F(mSize, height).inRange(2, 4096, 2),
+                })
+                .withSetter(SizeSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::input(0u,
+                        C2Config::PROFILE_HEVC_MAIN, C2Config::LEVEL_HEVC_MAIN_5_1))
+                .withFields({
+                    C2F(mProfileLevel, profile).oneOf({
+                            C2Config::PROFILE_HEVC_MAIN,
+                            C2Config::PROFILE_HEVC_MAIN_STILL}),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_HEVC_MAIN_1,
+                            C2Config::LEVEL_HEVC_MAIN_2, C2Config::LEVEL_HEVC_MAIN_2_1,
+                            C2Config::LEVEL_HEVC_MAIN_3, C2Config::LEVEL_HEVC_MAIN_3_1,
+                            C2Config::LEVEL_HEVC_MAIN_4, C2Config::LEVEL_HEVC_MAIN_4_1,
+                            C2Config::LEVEL_HEVC_MAIN_5, C2Config::LEVEL_HEVC_MAIN_5_1,
+                            C2Config::LEVEL_HEVC_MAIN_5_2, C2Config::LEVEL_HEVC_HIGH_4,
+                            C2Config::LEVEL_HEVC_HIGH_4_1, C2Config::LEVEL_HEVC_HIGH_5,
+                            C2Config::LEVEL_HEVC_HIGH_5_1, C2Config::LEVEL_HEVC_HIGH_5_2
+                    })
+                })
+                .withSetter(ProfileLevelSetter, mSize)
+                .build());
+
+        addParameter(
+                DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
+                .withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 4096, 2),
+                    C2F(mSize, height).inRange(2, 4096, 2),
+                })
+                .withSetter(MaxPictureSizeSetter, mSize)
+                .build());
+
+        addParameter(
+                DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 320 * 240 * 3 / 4))
+                .withFields({
+                    C2F(mMaxInputSize, value).any(),
+                })
+                .calculatedAs(MaxInputSizeSetter, mMaxSize)
+                .build());
+
+        C2ChromaOffsetStruct locations[1] = { C2ChromaOffsetStruct::ITU_YUV_420_0() };
+        std::shared_ptr<C2StreamColorInfo::output> defaultColorInfo =
+            C2StreamColorInfo::output::AllocShared(
+                    1u, 0u, 8u /* bitDepth */, C2Color::YUV_420);
+        memcpy(defaultColorInfo->m.locations, locations, sizeof(locations));
+
+        defaultColorInfo = C2StreamColorInfo::output::AllocShared(
+            {C2ChromaOffsetStruct::ITU_YUV_420_0()}, 0u, 8u /* bitDepth */,
+            C2Color::YUV_420);
+        helper->addStructDescriptors<C2ChromaOffsetStruct>();
+
+        addParameter(
+                DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
+                .withConstValue(defaultColorInfo)
+                .build());
+
+        addParameter(
+                DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsTuning::output(
+                        0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+                        C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({
+                    C2F(mDefaultColorAspects, range).inRange(
+                                C2Color::RANGE_UNSPECIFIED,     C2Color::RANGE_OTHER),
+                    C2F(mDefaultColorAspects, primaries).inRange(
+                                C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+                    C2F(mDefaultColorAspects, transfer).inRange(
+                                C2Color::TRANSFER_UNSPECIFIED,  C2Color::TRANSFER_OTHER),
+                    C2F(mDefaultColorAspects, matrix).inRange(
+                                C2Color::MATRIX_UNSPECIFIED,    C2Color::MATRIX_OTHER)
+                })
+                .withSetter(DefaultColorAspectsSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsInfo::input(
+                        0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
+                        C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({
+                    C2F(mCodedColorAspects, range).inRange(
+                                C2Color::RANGE_UNSPECIFIED,     C2Color::RANGE_OTHER),
+                    C2F(mCodedColorAspects, primaries).inRange(
+                                C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+                    C2F(mCodedColorAspects, transfer).inRange(
+                                C2Color::TRANSFER_UNSPECIFIED,  C2Color::TRANSFER_OTHER),
+                    C2F(mCodedColorAspects, matrix).inRange(
+                                C2Color::MATRIX_UNSPECIFIED,    C2Color::MATRIX_OTHER)
+                })
+                .withSetter(CodedColorAspectsSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsInfo::output(
+                        0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+                        C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({
+                    C2F(mColorAspects, range).inRange(
+                                C2Color::RANGE_UNSPECIFIED,     C2Color::RANGE_OTHER),
+                    C2F(mColorAspects, primaries).inRange(
+                                C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+                    C2F(mColorAspects, transfer).inRange(
+                                C2Color::TRANSFER_UNSPECIFIED,  C2Color::TRANSFER_OTHER),
+                    C2F(mColorAspects, matrix).inRange(
+                                C2Color::MATRIX_UNSPECIFIED,    C2Color::MATRIX_OTHER)
+                })
+                .withSetter(ColorAspectsSetter, mDefaultColorAspects, mCodedColorAspects)
+                .build());
+
+        // TODO: support more formats?
+        addParameter(
+                DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+                .withConstValue(new C2StreamPixelFormatInfo::output(
+                                     0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+                .build());
+    }
+
+    static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output> &oldMe,
+                          C2P<C2StreamPictureSizeInfo::output> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R MaxPictureSizeSetter(bool mayBlock, C2P<C2StreamMaxPictureSizeTuning::output> &me,
+                                    const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        // TODO: get max width/height from the size's field helpers vs. hardcoding
+        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 4096u);
+        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 4096u);
+        return C2R::Ok();
+    }
+
+    static C2R MaxInputSizeSetter(bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input> &me,
+                                  const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
+        (void)mayBlock;
+        // assume compression ratio of 2
+        me.set().value = (((maxSize.v.width + 63) / 64) * ((maxSize.v.height + 63) / 64) * 3072);
+        return C2R::Ok();
+    }
+
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input> &me,
+                                  const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        (void)size;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+    static C2R DefaultColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsTuning::output> &me) {
+        (void)mayBlock;
+        if (me.v.range > C2Color::RANGE_OTHER) {
+                me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+                me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+                me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+                me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R CodedColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input> &me) {
+        (void)mayBlock;
+        if (me.v.range > C2Color::RANGE_OTHER) {
+                me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+                me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+                me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+                me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output> &me,
+                                  const C2P<C2StreamColorAspectsTuning::output> &def,
+                                  const C2P<C2StreamColorAspectsInfo::input> &coded) {
+        (void)mayBlock;
+        // take default values for all unspecified fields, and coded values for specified ones
+        me.set().range = coded.v.range == RANGE_UNSPECIFIED ? def.v.range : coded.v.range;
+        me.set().primaries = coded.v.primaries == PRIMARIES_UNSPECIFIED
+                ? def.v.primaries : coded.v.primaries;
+        me.set().transfer = coded.v.transfer == TRANSFER_UNSPECIFIED
+                ? def.v.transfer : coded.v.transfer;
+        me.set().matrix = coded.v.matrix == MATRIX_UNSPECIFIED ? def.v.matrix : coded.v.matrix;
+        return C2R::Ok();
+    }
+
+    std::shared_ptr<C2StreamColorAspectsInfo::output> getColorAspects_l() {
+        return mColorAspects;
+    }
+
+private:
+    std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+    std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
+    std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mMaxInputSize;
+    std::shared_ptr<C2StreamColorInfo::output> mColorInfo;
+    std::shared_ptr<C2StreamColorAspectsInfo::input> mCodedColorAspects;
+    std::shared_ptr<C2StreamColorAspectsTuning::output> mDefaultColorAspects;
+    std::shared_ptr<C2StreamColorAspectsInfo::output> mColorAspects;
+    std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
+};
+
+static size_t getCpuCoreCount() {
+    long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGV("Number of CPU cores: %ld", cpuCoreCount);
+    return (size_t)cpuCoreCount;
+}
+
+static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
+    (void) ctxt;
+    return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *ctxt, void *mem) {
+    (void) ctxt;
+    free(mem);
+}
+
+C2SoftHevcDec::C2SoftHevcDec(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+        mIntf(intfImpl),
+        mDecHandle(nullptr),
+        mOutBufferFlush(nullptr),
+        mIvColorformat(IV_YUV_420P),
+        mWidth(320),
+        mHeight(240),
+        mHeaderDecoded(false),
+        mOutIndex(0u) {
+}
+
+C2SoftHevcDec::~C2SoftHevcDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftHevcDec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftHevcDec::onStop() {
+    if (OK != resetDecoder()) return C2_CORRUPTED;
+    resetPlugin();
+    return C2_OK;
+}
+
+void C2SoftHevcDec::onReset() {
+    (void) onStop();
+}
+
+void C2SoftHevcDec::onRelease() {
+    (void) deleteDecoder();
+    if (mOutBufferFlush) {
+        ivd_aligned_free(nullptr, mOutBufferFlush);
+        mOutBufferFlush = nullptr;
+    }
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
+}
+
+c2_status_t C2SoftHevcDec::onFlush_sm() {
+    if (OK != setFlushMode()) return C2_CORRUPTED;
+
+    uint32_t displayStride = mStride;
+    uint32_t displayHeight = mHeight;
+    uint32_t bufferSize = displayStride * displayHeight * 3 / 2;
+    mOutBufferFlush = (uint8_t *)ivd_aligned_malloc(nullptr, 128, bufferSize);
+    if (!mOutBufferFlush) {
+        ALOGE("could not allocate tmp output buffer (for flush) of size %u ", bufferSize);
+        return C2_NO_MEMORY;
+    }
+
+    while (true) {
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+
+        setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, nullptr, 0, 0, 0);
+        (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        if (0 == s_decode_op.u4_output_present) {
+            resetPlugin();
+            break;
+        }
+    }
+
+    if (mOutBufferFlush) {
+        ivd_aligned_free(nullptr, mOutBufferFlush);
+        mOutBufferFlush = nullptr;
+    }
+
+    return C2_OK;
+}
+
+status_t C2SoftHevcDec::createDecoder() {
+    ivdext_create_ip_t s_create_ip;
+    ivdext_create_op_t s_create_op;
+
+    s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
+    s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
+    s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
+    s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorformat;
+    s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc;
+    s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free;
+    s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = nullptr;
+    s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_create_ip,
+                                                     &s_create_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__,
+              s_create_op.s_ivd_create_op_t.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+    mDecHandle = (iv_obj_t*)s_create_op.s_ivd_create_op_t.pv_handle;
+    mDecHandle->pv_fxns = (void *)ivdec_api_function;
+    mDecHandle->u4_size = sizeof(iv_obj_t);
+
+    return OK;
+}
+
+status_t C2SoftHevcDec::setNumCores() {
+    ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip;
+    ivdext_ctl_set_num_cores_op_t s_set_num_cores_op;
+
+    s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+    s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+    s_set_num_cores_ip.u4_num_cores = mNumCores;
+    s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_num_cores_ip,
+                                                     &s_set_num_cores_op);
+    if (IV_SUCCESS != status) {
+        ALOGD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftHevcDec::setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode) {
+    ivd_ctl_set_config_ip_t s_set_dyn_params_ip;
+    ivd_ctl_set_config_op_t s_set_dyn_params_op;
+
+    s_set_dyn_params_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
+    s_set_dyn_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_dyn_params_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+    s_set_dyn_params_ip.u4_disp_wd = (UWORD32) stride;
+    s_set_dyn_params_ip.e_frm_skip_mode = IVD_SKIP_NONE;
+    s_set_dyn_params_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+    s_set_dyn_params_ip.e_vid_dec_mode = dec_mode;
+    s_set_dyn_params_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_dyn_params_ip,
+                                                     &s_set_dyn_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__, s_set_dyn_params_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftHevcDec::getVersion() {
+    ivd_ctl_getversioninfo_ip_t s_get_versioninfo_ip;
+    ivd_ctl_getversioninfo_op_t s_get_versioninfo_op;
+    UWORD8 au1_buf[512];
+
+    s_get_versioninfo_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
+    s_get_versioninfo_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_get_versioninfo_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
+    s_get_versioninfo_ip.pv_version_buffer = au1_buf;
+    s_get_versioninfo_ip.u4_version_buffer_size = sizeof(au1_buf);
+    s_get_versioninfo_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_get_versioninfo_ip,
+                                                     &s_get_versioninfo_op);
+    if (status != IV_SUCCESS) {
+        ALOGD("error in %s: 0x%x", __func__,
+              s_get_versioninfo_op.u4_error_code);
+    } else {
+        ALOGV("ittiam decoder version number: %s",
+              (char *) s_get_versioninfo_ip.pv_version_buffer);
+    }
+
+    return OK;
+}
+
+status_t C2SoftHevcDec::initDecoder() {
+    if (OK != createDecoder()) return UNKNOWN_ERROR;
+    mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
+    mStride = ALIGN64(mWidth);
+    mSignalledError = false;
+    resetPlugin();
+    (void) setNumCores();
+    if (OK != setParams(mStride, IVD_DECODE_FRAME)) return UNKNOWN_ERROR;
+    (void) getVersion();
+
+    return OK;
+}
+
+bool C2SoftHevcDec::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                                  ivd_video_decode_op_t *ps_decode_op,
+                                  C2ReadView *inBuffer,
+                                  C2GraphicView *outBuffer,
+                                  size_t inOffset,
+                                  size_t inSize,
+                                  uint32_t tsMarker) {
+    uint32_t displayStride = mStride;
+    uint32_t displayHeight = mHeight;
+    size_t lumaSize = displayStride * displayHeight;
+    size_t chromaSize = lumaSize >> 2;
+
+    ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+    ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+    if (inBuffer) {
+        ps_decode_ip->u4_ts = tsMarker;
+        ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer->data() + inOffset);
+        ps_decode_ip->u4_num_Bytes = inSize;
+    } else {
+        ps_decode_ip->u4_ts = 0;
+        ps_decode_ip->pv_stream_buffer = nullptr;
+        ps_decode_ip->u4_num_Bytes = 0;
+    }
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
+    if (outBuffer) {
+        if (outBuffer->width() < displayStride || outBuffer->height() < displayHeight) {
+            ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
+                  outBuffer->width(), outBuffer->height(), displayStride, displayHeight);
+            return false;
+        }
+        ps_decode_ip->s_out_buffer.pu1_bufs[0] = outBuffer->data()[C2PlanarLayout::PLANE_Y];
+        ps_decode_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[C2PlanarLayout::PLANE_U];
+        ps_decode_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[C2PlanarLayout::PLANE_V];
+    } else {
+        ps_decode_ip->s_out_buffer.pu1_bufs[0] = mOutBufferFlush;
+        ps_decode_ip->s_out_buffer.pu1_bufs[1] = mOutBufferFlush + lumaSize;
+        ps_decode_ip->s_out_buffer.pu1_bufs[2] = mOutBufferFlush + lumaSize + chromaSize;
+    }
+    ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
+    ps_decode_op->u4_size = sizeof(ivd_video_decode_op_t);
+    ps_decode_op->u4_output_present = 0;
+
+    return true;
+}
+
+bool C2SoftHevcDec::getVuiParams() {
+    ivdext_ctl_get_vui_params_ip_t s_get_vui_params_ip;
+    ivdext_ctl_get_vui_params_op_t s_get_vui_params_op;
+
+    s_get_vui_params_ip.u4_size = sizeof(ivdext_ctl_get_vui_params_ip_t);
+    s_get_vui_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_get_vui_params_ip.e_sub_cmd =
+            (IVD_CONTROL_API_COMMAND_TYPE_T) IHEVCD_CXA_CMD_CTL_GET_VUI_PARAMS;
+    s_get_vui_params_op.u4_size = sizeof(ivdext_ctl_get_vui_params_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_get_vui_params_ip,
+                                                     &s_get_vui_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGD("error in %s: 0x%x", __func__, s_get_vui_params_op.u4_error_code);
+        return false;
+    }
+
+    VuiColorAspects vuiColorAspects;
+    vuiColorAspects.primaries = s_get_vui_params_op.u1_colour_primaries;
+    vuiColorAspects.transfer = s_get_vui_params_op.u1_transfer_characteristics;
+    vuiColorAspects.coeffs = s_get_vui_params_op.u1_matrix_coefficients;
+    vuiColorAspects.fullRange = s_get_vui_params_op.u1_video_full_range_flag;
+
+    // convert vui aspects to C2 values if changed
+    if (!(vuiColorAspects == mBitstreamColorAspects)) {
+        mBitstreamColorAspects = vuiColorAspects;
+        ColorAspects sfAspects;
+        C2StreamColorAspectsInfo::input codedAspects = { 0u };
+        ColorUtils::convertIsoColorAspectsToCodecAspects(
+                vuiColorAspects.primaries, vuiColorAspects.transfer, vuiColorAspects.coeffs,
+                vuiColorAspects.fullRange, sfAspects);
+        if (!C2Mapper::map(sfAspects.mPrimaries, &codedAspects.primaries)) {
+            codedAspects.primaries = C2Color::PRIMARIES_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mRange, &codedAspects.range)) {
+            codedAspects.range = C2Color::RANGE_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mMatrixCoeffs, &codedAspects.matrix)) {
+            codedAspects.matrix = C2Color::MATRIX_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mTransfer, &codedAspects.transfer)) {
+            codedAspects.transfer = C2Color::TRANSFER_UNSPECIFIED;
+        }
+        std::vector<std::unique_ptr<C2SettingResult>> failures;
+        (void)mIntf->config({&codedAspects}, C2_MAY_BLOCK, &failures);
+    }
+    return true;
+}
+
+status_t C2SoftHevcDec::setFlushMode() {
+    ivd_ctl_flush_ip_t s_set_flush_ip;
+    ivd_ctl_flush_op_t s_set_flush_op;
+
+    s_set_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
+    s_set_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
+    s_set_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_flush_ip,
+                                                     &s_set_flush_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__, s_set_flush_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftHevcDec::resetDecoder() {
+    ivd_ctl_reset_ip_t s_reset_ip;
+    ivd_ctl_reset_op_t s_reset_op;
+
+    s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+    s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+    s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_reset_ip,
+                                                     &s_reset_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+    mStride = 0;
+    (void) setNumCores();
+    mSignalledError = false;
+    mHeaderDecoded = false;
+    return OK;
+}
+
+void C2SoftHevcDec::resetPlugin() {
+    mSignalledOutputEos = false;
+    gettimeofday(&mTimeStart, nullptr);
+    gettimeofday(&mTimeEnd, nullptr);
+}
+
+status_t C2SoftHevcDec::deleteDecoder() {
+    if (mDecHandle) {
+        ivdext_delete_ip_t s_delete_ip;
+        ivdext_delete_op_t s_delete_op;
+
+        s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
+        s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;
+        s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t);
+        IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                         &s_delete_ip,
+                                                         &s_delete_op);
+        if (status != IV_SUCCESS) {
+            ALOGE("error in %s: 0x%x", __func__,
+                  s_delete_op.s_ivd_delete_op_t.u4_error_code);
+            return UNKNOWN_ERROR;
+        }
+        mDecHandle = nullptr;
+    }
+
+    return OK;
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftHevcDec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work) {
+    std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mOutBlock),
+                                                           C2Rect(mWidth, mHeight));
+    mOutBlock = nullptr;
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        buffer->setInfo(mIntf->getColorAspects_l());
+    }
+
+    class FillWork {
+       public:
+        FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+                 const std::shared_ptr<C2Buffer>& buffer)
+            : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {}
+        ~FillWork() = default;
+
+        void operator()(const std::unique_ptr<C2Work>& work) {
+            work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+            work->worklets.front()->output.buffers.clear();
+            work->worklets.front()->output.ordinal = mOrdinal;
+            work->workletsProcessed = 1u;
+            work->result = C2_OK;
+            if (mBuffer) {
+                work->worklets.front()->output.buffers.push_back(mBuffer);
+            }
+            ALOGV("timestamp = %lld, index = %lld, w/%s buffer",
+                  mOrdinal.timestamp.peekll(), mOrdinal.frameIndex.peekll(),
+                  mBuffer ? "" : "o");
+        }
+
+       private:
+        const uint32_t mFlags;
+        const C2WorkOrdinalStruct mOrdinal;
+        const std::shared_ptr<C2Buffer> mBuffer;
+    };
+
+    auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+        // TODO: Check if cloneAndSend can be avoided by tracking number of frames remaining
+        if (eos) {
+            if (buffer) {
+                mOutIndex = index;
+                C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+                cloneAndSend(
+                    mOutIndex, work,
+                    FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+                buffer.reset();
+            }
+        } else {
+            fillWork(work);
+        }
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+c2_status_t C2SoftHevcDec::ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool) {
+    if (!mDecHandle) {
+        ALOGE("not supposed to be here, invalid decoder context");
+        return C2_CORRUPTED;
+    }
+    if (mStride != ALIGN64(mWidth)) {
+        mStride = ALIGN64(mWidth);
+        if (OK != setParams(mStride, IVD_DECODE_FRAME)) return C2_CORRUPTED;
+    }
+    if (mOutBlock &&
+            (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+        mOutBlock.reset();
+    }
+    if (!mOutBlock) {
+        uint32_t format = HAL_PIXEL_FORMAT_YV12;
+        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+        if (err != C2_OK) {
+            ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+            return err;
+        }
+        ALOGV("provided (%dx%d) required (%dx%d)",
+              mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+    }
+
+    return C2_OK;
+}
+
+// TODO: can overall error checking be improved?
+// TODO: allow configuration of color format and usage for graphic buffers instead
+//       of hard coding them to HAL_PIXEL_FORMAT_YV12
+// TODO: pass coloraspects information to surface
+// TODO: test support for dynamic change in resolution
+// TODO: verify if the decoder sent back all frames
+void C2SoftHevcDec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = rView.error();
+            return;
+        }
+    }
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    bool hasPicture = false;
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+    size_t inPos = 0;
+    while (inPos < inSize) {
+        if (C2_OK != ensureDecoderState(pool)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        C2GraphicView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            work->result = wView.error();
+            return;
+        }
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+        if (!setDecodeArgs(&s_decode_ip, &s_decode_op, &rView, &wView,
+                           inOffset + inPos, inSize - inPos, workIndex)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        if (false == mHeaderDecoded) {
+            /* Decode header and get dimensions */
+            setParams(mStride, IVD_DECODE_HEADER);
+        }
+        WORD32 delay;
+        GETTIME(&mTimeStart, nullptr);
+        TIME_DIFF(mTimeEnd, mTimeStart, delay);
+        (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        WORD32 decodeTime;
+        GETTIME(&mTimeEnd, nullptr);
+        TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
+        ALOGV("decodeTime=%6d delay=%6d numBytes=%6d", decodeTime, delay,
+              s_decode_op.u4_num_bytes_consumed);
+        if (IVD_MEM_ALLOC_FAILED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+            ALOGE("allocation failure in decoder");
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        } else if (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED ==
+                   (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+            ALOGE("unsupported resolution : %dx%d", mWidth, mHeight);
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        } else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & IVD_ERROR_MASK)) {
+            ALOGV("resolution changed");
+            drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+            resetDecoder();
+            resetPlugin();
+            work->workletsProcessed = 0u;
+
+            /* Decode header and get new dimensions */
+            setParams(mStride, IVD_DECODE_HEADER);
+            (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        } else if (IS_IVD_FATAL_ERROR(s_decode_op.u4_error_code)) {
+            ALOGE("Fatal error in decoder 0x%x", s_decode_op.u4_error_code);
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
+            if (mHeaderDecoded == false) {
+                mHeaderDecoded = true;
+                setParams(ALIGN64(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+            }
+            if (s_decode_op.u4_pic_wd != mWidth ||  s_decode_op.u4_pic_ht != mHeight) {
+                mWidth = s_decode_op.u4_pic_wd;
+                mHeight = s_decode_op.u4_pic_ht;
+                CHECK_EQ(0u, s_decode_op.u4_output_present);
+
+                C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+                std::vector<std::unique_ptr<C2SettingResult>> failures;
+                c2_status_t err =
+                    mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+                if (err == OK) {
+                    work->worklets.front()->output.configUpdate.push_back(
+                        C2Param::Copy(size));
+                } else {
+                    ALOGE("Cannot set width and height");
+                    mSignalledError = true;
+                    work->workletsProcessed = 1u;
+                    work->result = C2_CORRUPTED;
+                    return;
+                }
+                continue;
+            }
+        }
+        (void) getVuiParams();
+        hasPicture |= (1 == s_decode_op.u4_frame_decoded_flag);
+        if (s_decode_op.u4_output_present) {
+            finishWork(s_decode_op.u4_ts, work);
+        }
+        if (0 == s_decode_op.u4_num_bytes_consumed) {
+            ALOGD("Bytes consumed is zero. Ignoring remaining bytes");
+            break;
+        }
+        inPos += s_decode_op.u4_num_bytes_consumed;
+        if (hasPicture && (inSize - inPos)) {
+            ALOGD("decoded frame in current access nal, ignoring further trailing bytes %d",
+                  (int)inSize - (int)inPos);
+            break;
+        }
+    }
+
+    if (eos) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+        mSignalledOutputEos = true;
+    } else if (!hasPicture) {
+        fillEmptyWork(work);
+    }
+}
+
+c2_status_t C2SoftHevcDec::drainInternal(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work) {
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    if (OK != setFlushMode()) return C2_CORRUPTED;
+    while (true) {
+        if (C2_OK != ensureDecoderState(pool)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return C2_CORRUPTED;
+        }
+        C2GraphicView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            return C2_CORRUPTED;
+        }
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+        if (!setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, &wView, 0, 0, 0)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            return C2_CORRUPTED;
+        }
+        (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        if (s_decode_op.u4_output_present) {
+            finishWork(s_decode_op.u4_ts, work);
+        } else {
+            fillEmptyWork(work);
+            break;
+        }
+    }
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftHevcDec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftHevcDecFactory : public C2ComponentFactory {
+public:
+    C2SoftHevcDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+        GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftHevcDec(COMPONENT_NAME,
+                                 id,
+                                 std::make_shared<C2SoftHevcDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftHevcDec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftHevcDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftHevcDecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftHevcDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.h b/media/codec2/components/hevc/C2SoftHevcDec.h
new file mode 100644
index 0000000..b7664e6
--- /dev/null
+++ b/media/codec2/components/hevc/C2SoftHevcDec.h
@@ -0,0 +1,154 @@
+/*
+ * 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_C2_SOFT_HEVC_DEC_H_
+#define ANDROID_C2_SOFT_HEVC_DEC_H_
+
+#include <media/stagefright/foundation/ColorUtils.h>
+
+#include <atomic>
+#include <SimpleC2Component.h>
+
+#include "ihevc_typedefs.h"
+#include "iv.h"
+#include "ivd.h"
+
+namespace android {
+
+#define ivdec_api_function              ihevcd_cxa_api_function
+#define ivdext_create_ip_t              ihevcd_cxa_create_ip_t
+#define ivdext_create_op_t              ihevcd_cxa_create_op_t
+#define ivdext_delete_ip_t              ihevcd_cxa_delete_ip_t
+#define ivdext_delete_op_t              ihevcd_cxa_delete_op_t
+#define ivdext_ctl_set_num_cores_ip_t   ihevcd_cxa_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t   ihevcd_cxa_ctl_set_num_cores_op_t
+#define ivdext_ctl_get_vui_params_ip_t  ihevcd_cxa_ctl_get_vui_params_ip_t
+#define ivdext_ctl_get_vui_params_op_t  ihevcd_cxa_ctl_get_vui_params_op_t
+#define ALIGN64(x)                      ((((x) + 63) >> 6) << 6)
+#define MAX_NUM_CORES                   4
+#define IVDEXT_CMD_CTL_SET_NUM_CORES    \
+        (IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_SET_NUM_CORES
+#define MIN(a, b)                       (((a) < (b)) ? (a) : (b))
+#define GETTIME(a, b)                   gettimeofday(a, b);
+#define TIME_DIFF(start, end, diff)     \
+    diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+            ((end).tv_usec - (start).tv_usec);
+
+
+struct C2SoftHevcDec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftHevcDec(const char* name, c2_node_id_t id,
+                  const std::shared_ptr<IntfImpl>& intfImpl);
+    virtual ~C2SoftHevcDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+ private:
+    status_t createDecoder();
+    status_t setNumCores();
+    status_t setParams(size_t stride, IVD_VIDEO_DECODE_MODE_T dec_mode);
+    status_t getVersion();
+    status_t initDecoder();
+    bool setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                       ivd_video_decode_op_t *ps_decode_op,
+                       C2ReadView *inBuffer,
+                       C2GraphicView *outBuffer,
+                       size_t inOffset,
+                       size_t inSize,
+                       uint32_t tsMarker);
+    bool getVuiParams();
+    // TODO:This is not the right place for colorAspects functions. These should
+    // be part of c2-vndk so that they can be accessed by all video plugins
+    // until then, make them feel at home
+    bool colorAspectsDiffer(const ColorAspects &a, const ColorAspects &b);
+    void updateFinalColorAspects(
+            const ColorAspects &otherAspects, const ColorAspects &preferredAspects);
+    status_t handleColorAspectsChange();
+    c2_status_t ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool);
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work);
+    status_t setFlushMode();
+    c2_status_t drainInternal(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool,
+            const std::unique_ptr<C2Work> &work);
+    status_t resetDecoder();
+    void resetPlugin();
+    status_t deleteDecoder();
+
+    // TODO:This is not the right place for this enum. These should
+    // be part of c2-vndk so that they can be accessed by all video plugins
+    // until then, make them feel at home
+    enum {
+        kNotSupported,
+        kPreferBitstream,
+        kPreferContainer,
+    };
+
+    std::shared_ptr<IntfImpl> mIntf;
+    iv_obj_t *mDecHandle;
+    std::shared_ptr<C2GraphicBlock> mOutBlock;
+    uint8_t *mOutBufferFlush;
+
+    size_t mNumCores;
+    IV_COLOR_FORMAT_T mIvColorformat;
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mStride;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+    bool mHeaderDecoded;
+    std::atomic_uint64_t mOutIndex;
+
+    // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
+    // converting them to C2 values for each frame
+    struct VuiColorAspects {
+        uint8_t primaries;
+        uint8_t transfer;
+        uint8_t coeffs;
+        uint8_t fullRange;
+
+        // default color aspects
+        VuiColorAspects()
+            : primaries(2), transfer(2), coeffs(2), fullRange(0) { }
+
+        bool operator==(const VuiColorAspects &o) {
+            return primaries == o.primaries && transfer == o.transfer && coeffs == o.coeffs
+                    && fullRange == o.fullRange;
+        }
+    } mBitstreamColorAspects;
+
+    // profile
+    struct timeval mTimeStart;
+    struct timeval mTimeEnd;
+
+    C2_DO_NOT_COPY(C2SoftHevcDec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_HEVC_DEC_H_
diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
new file mode 100644
index 0000000..b129b1b
--- /dev/null
+++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
@@ -0,0 +1,974 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "C2SoftHevcEnc"
+#include <log/log.h>
+
+#include <media/hardware/VideoAPI.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/foundation/AUtils.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <Codec2BufferUtils.h>
+#include <SimpleC2Interface.h>
+#include <util/C2InterfaceHelper.h>
+
+#include "ihevc_typedefs.h"
+#include "itt_video_api.h"
+#include "ihevce_api.h"
+#include "ihevce_plugin.h"
+#include "C2SoftHevcEnc.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.hevc.encoder";
+
+} // namepsace
+
+class C2SoftHevcEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+  public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_VIDEO,
+                MEDIA_MIMETYPE_VIDEO_HEVC) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mActualInputDelay, C2_PARAMKEY_INPUT_DELAY)
+                .withDefault(new C2PortActualDelayTuning::input(
+                    DEFAULT_B_FRAMES + DEFAULT_RC_LOOKAHEAD))
+                .withFields({C2F(mActualInputDelay, value).inRange(
+                    0, MAX_B_FRAMES + MAX_RC_LOOKAHEAD)})
+                .withSetter(
+                    Setter<decltype(*mActualInputDelay)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
+                .withConstValue(new C2StreamUsageTuning::input(
+                        0u, (uint64_t)C2MemoryUsage::CPU_READ))
+                .build());
+
+        // matches size limits in codec library
+        addParameter(
+            DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 1920, 2),
+                    C2F(mSize, height).inRange(2, 1088, 2),
+                })
+                .withSetter(SizeSetter)
+                .build());
+
+        addParameter(
+            DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
+                .withDefault(new C2StreamFrameRateInfo::output(0u, 30.))
+                .withFields({C2F(mFrameRate, value).greaterThan(0.)})
+                .withSetter(
+                    Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
+                .build());
+
+        // matches limits in codec library
+        addParameter(
+            DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
+                .withDefault(new C2StreamBitrateModeTuning::output(
+                        0u, C2Config::BITRATE_VARIABLE))
+                .withFields({
+                    C2F(mBitrateMode, value).oneOf({
+                        C2Config::BITRATE_CONST,
+                        C2Config::BITRATE_VARIABLE,
+                        C2Config::BITRATE_IGNORE})
+                })
+                .withSetter(
+                    Setter<decltype(*mBitrateMode)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+            DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::output(0u, 64000))
+                .withFields({C2F(mBitrate, value).inRange(4096, 12000000)})
+                .withSetter(BitrateSetter)
+                .build());
+
+        // matches levels allowed within codec library
+        addParameter(
+                DefineParam(mComplexity, C2_PARAMKEY_COMPLEXITY)
+                .withDefault(new C2StreamComplexityTuning::output(0u, 0))
+                .withFields({C2F(mComplexity, value).inRange(0, 10)})
+                .withSetter(Setter<decltype(*mComplexity)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mQuality, C2_PARAMKEY_QUALITY)
+                .withDefault(new C2StreamQualityTuning::output(0u, 80))
+                .withFields({C2F(mQuality, value).inRange(0, 100)})
+                .withSetter(Setter<decltype(*mQuality)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+            DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::output(
+                    0u, PROFILE_HEVC_MAIN, LEVEL_HEVC_MAIN_1))
+                .withFields({
+                    C2F(mProfileLevel, profile)
+                        .oneOf({C2Config::PROFILE_HEVC_MAIN,
+                                C2Config::PROFILE_HEVC_MAIN_STILL}),
+                    C2F(mProfileLevel, level)
+                        .oneOf({LEVEL_HEVC_MAIN_1, LEVEL_HEVC_MAIN_2,
+                                LEVEL_HEVC_MAIN_2_1, LEVEL_HEVC_MAIN_3,
+                                LEVEL_HEVC_MAIN_3_1, LEVEL_HEVC_MAIN_4,
+                                LEVEL_HEVC_MAIN_4_1, LEVEL_HEVC_MAIN_5,
+                                LEVEL_HEVC_MAIN_5_1, LEVEL_HEVC_MAIN_5_2}),
+                })
+                .withSetter(ProfileLevelSetter, mSize, mFrameRate, mBitrate)
+                .build());
+
+        addParameter(
+                DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
+                .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
+                .withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) })
+                .withSetter(Setter<decltype(*mRequestSync)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+            DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL)
+                .withDefault(
+                    new C2StreamSyncFrameIntervalTuning::output(0u, 1000000))
+                .withFields({C2F(mSyncFramePeriod, value).any()})
+                .withSetter(
+                    Setter<decltype(*mSyncFramePeriod)>::StrictValueWithNoDeps)
+                .build());
+    }
+
+    static C2R BitrateSetter(bool mayBlock,
+                             C2P<C2StreamBitrateInfo::output>& me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (me.v.value < 4096) {
+            me.set().value = 4096;
+        }
+        return res;
+    }
+
+    static C2R SizeSetter(bool mayBlock,
+                          const C2P<C2StreamPictureSizeInfo::input>& oldMe,
+                          C2P<C2StreamPictureSizeInfo::input>& me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R ProfileLevelSetter(
+            bool mayBlock,
+            C2P<C2StreamProfileLevelInfo::output> &me,
+            const C2P<C2StreamPictureSizeInfo::input> &size,
+            const C2P<C2StreamFrameRateInfo::output> &frameRate,
+            const C2P<C2StreamBitrateInfo::output> &bitrate) {
+        (void)mayBlock;
+        if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
+            me.set().profile = PROFILE_HEVC_MAIN;
+        }
+
+        struct LevelLimits {
+            C2Config::level_t level;
+            uint64_t samplesPerSec;
+            uint64_t samples;
+            uint32_t bitrate;
+        };
+
+        constexpr LevelLimits kLimits[] = {
+            { LEVEL_HEVC_MAIN_1,       552960,    36864,    128000 },
+            { LEVEL_HEVC_MAIN_2,      3686400,   122880,   1500000 },
+            { LEVEL_HEVC_MAIN_2_1,    7372800,   245760,   3000000 },
+            { LEVEL_HEVC_MAIN_3,     16588800,   552960,   6000000 },
+            { LEVEL_HEVC_MAIN_3_1,   33177600,   983040,  10000000 },
+            { LEVEL_HEVC_MAIN_4,     66846720,  2228224,  12000000 },
+            { LEVEL_HEVC_MAIN_4_1,  133693440,  2228224,  20000000 },
+            { LEVEL_HEVC_MAIN_5,    267386880,  8912896,  25000000 },
+            { LEVEL_HEVC_MAIN_5_1,  534773760,  8912896,  40000000 },
+            { LEVEL_HEVC_MAIN_5_2, 1069547520,  8912896,  60000000 },
+            { LEVEL_HEVC_MAIN_6,   1069547520, 35651584,  60000000 },
+            { LEVEL_HEVC_MAIN_6_1, 2139095040, 35651584, 120000000 },
+            { LEVEL_HEVC_MAIN_6_2, 4278190080, 35651584, 240000000 },
+        };
+
+        uint64_t samples = size.v.width * size.v.height;
+        uint64_t samplesPerSec = samples * frameRate.v.value;
+
+        // Check if the supplied level meets the MB / bitrate requirements. If
+        // not, update the level with the lowest level meeting the requirements.
+
+        bool found = false;
+        // By default needsUpdate = false in case the supplied level does meet
+        // the requirements.
+        bool needsUpdate = false;
+        for (const LevelLimits &limit : kLimits) {
+            if (samples <= limit.samples && samplesPerSec <= limit.samplesPerSec &&
+                    bitrate.v.value <= limit.bitrate) {
+                // This is the lowest level that meets the requirements, and if
+                // we haven't seen the supplied level yet, that means we don't
+                // need the update.
+                if (needsUpdate) {
+                    ALOGD("Given level %x does not cover current configuration: "
+                          "adjusting to %x", me.v.level, limit.level);
+                    me.set().level = limit.level;
+                }
+                found = true;
+                break;
+            }
+            if (me.v.level == limit.level) {
+                // We break out of the loop when the lowest feasible level is
+                // found. The fact that we're here means that our level doesn't
+                // meet the requirement and needs to be updated.
+                needsUpdate = true;
+            }
+        }
+        if (!found) {
+            // We set to the highest supported level.
+            me.set().level = LEVEL_HEVC_MAIN_5_2;
+        }
+        return C2R::Ok();
+    }
+
+    UWORD32 getProfile_l() const {
+        switch (mProfileLevel->profile) {
+        case PROFILE_HEVC_MAIN:  [[fallthrough]];
+        case PROFILE_HEVC_MAIN_STILL: return 1;
+        default:
+            ALOGD("Unrecognized profile: %x", mProfileLevel->profile);
+            return 1;
+        }
+    }
+
+    UWORD32 getLevel_l() const {
+        struct Level {
+            C2Config::level_t c2Level;
+            UWORD32 hevcLevel;
+        };
+        constexpr Level levels[] = {
+            { LEVEL_HEVC_MAIN_1,    30 },
+            { LEVEL_HEVC_MAIN_2,    60 },
+            { LEVEL_HEVC_MAIN_2_1,  63 },
+            { LEVEL_HEVC_MAIN_3,    90 },
+            { LEVEL_HEVC_MAIN_3_1,  93 },
+            { LEVEL_HEVC_MAIN_4,   120 },
+            { LEVEL_HEVC_MAIN_4_1, 123 },
+            { LEVEL_HEVC_MAIN_5,   150 },
+            { LEVEL_HEVC_MAIN_5_1, 153 },
+            { LEVEL_HEVC_MAIN_5_2, 156 },
+            { LEVEL_HEVC_MAIN_6,   180 },
+            { LEVEL_HEVC_MAIN_6_1, 183 },
+            { LEVEL_HEVC_MAIN_6_2, 186 },
+        };
+        for (const Level &level : levels) {
+            if (mProfileLevel->level == level.c2Level) {
+                return level.hevcLevel;
+            }
+        }
+        ALOGD("Unrecognized level: %x", mProfileLevel->level);
+        return 156;
+    }
+    uint32_t getSyncFramePeriod_l() const {
+        if (mSyncFramePeriod->value < 0 ||
+            mSyncFramePeriod->value == INT64_MAX) {
+            return 0;
+        }
+        double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
+        return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
+    }
+
+    std::shared_ptr<C2StreamPictureSizeInfo::input> getSize_l() const {
+        return mSize;
+    }
+    std::shared_ptr<C2StreamFrameRateInfo::output> getFrameRate_l() const {
+        return mFrameRate;
+    }
+    std::shared_ptr<C2StreamBitrateModeTuning::output> getBitrateMode_l() const {
+        return mBitrateMode;
+    }
+    std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const {
+        return mBitrate;
+    }
+    std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const {
+        return mRequestSync;
+    }
+    std::shared_ptr<C2StreamComplexityTuning::output> getComplexity_l() const {
+        return mComplexity;
+    }
+    std::shared_ptr<C2StreamQualityTuning::output> getQuality_l() const {
+        return mQuality;
+    }
+
+   private:
+    std::shared_ptr<C2StreamUsageTuning::input> mUsage;
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+    std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
+    std::shared_ptr<C2StreamComplexityTuning::output> mComplexity;
+    std::shared_ptr<C2StreamQualityTuning::output> mQuality;
+    std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
+    std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
+};
+
+static size_t GetCPUCoreCount() {
+    long cpuCoreCount = 0;
+
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+
+    if (cpuCoreCount < 1)
+        cpuCoreCount = 1;
+    return (size_t)cpuCoreCount;
+}
+
+C2SoftHevcEnc::C2SoftHevcEnc(const char* name, c2_node_id_t id,
+                             const std::shared_ptr<IntfImpl>& intfImpl)
+    : SimpleC2Component(
+          std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mIvVideoColorFormat(IV_YUV_420P),
+      mHevcEncProfile(1),
+      mHevcEncLevel(30),
+      mStarted(false),
+      mSpsPpsHeaderReceived(false),
+      mSignalledEos(false),
+      mSignalledError(false),
+      mCodecCtx(nullptr) {
+    // If dump is enabled, then create an empty file
+    GENERATE_FILE_NAMES();
+    CREATE_DUMP_FILE(mInFile);
+    CREATE_DUMP_FILE(mOutFile);
+
+    gettimeofday(&mTimeStart, nullptr);
+    gettimeofday(&mTimeEnd, nullptr);
+}
+
+C2SoftHevcEnc::~C2SoftHevcEnc() {
+    onRelease();
+}
+
+c2_status_t C2SoftHevcEnc::onInit() {
+    return C2_OK;
+}
+
+c2_status_t C2SoftHevcEnc::onStop() {
+    return C2_OK;
+}
+
+void C2SoftHevcEnc::onReset() {
+    releaseEncoder();
+}
+
+void C2SoftHevcEnc::onRelease() {
+    releaseEncoder();
+}
+
+c2_status_t C2SoftHevcEnc::onFlush_sm() {
+    return C2_OK;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work>& work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("Signalling EOS");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+static int getQpFromQuality(int quality) {
+    int qp;
+#define MIN_QP 4
+#define MAX_QP 50
+    /* Quality: 100 -> Qp : MIN_QP
+     * Quality: 0 -> Qp : MAX_QP
+     * Qp = ((MIN_QP - MAX_QP) * quality / 100) + MAX_QP;
+     */
+    qp = ((MIN_QP - MAX_QP) * quality / 100) + MAX_QP;
+    qp = std::min(qp, MAX_QP);
+    qp = std::max(qp, MIN_QP);
+    return qp;
+}
+c2_status_t C2SoftHevcEnc::initEncParams() {
+    mCodecCtx = nullptr;
+    mNumCores = std::min(GetCPUCoreCount(), (size_t) CODEC_MAX_CORES);
+    memset(&mEncParams, 0, sizeof(ihevce_static_cfg_params_t));
+
+    // default configuration
+    IHEVCE_PLUGIN_STATUS_T err = ihevce_set_def_params(&mEncParams);
+    if (IHEVCE_EOK != err) {
+        ALOGE("HEVC default init failed : 0x%x", err);
+        return C2_CORRUPTED;
+    }
+
+    // update configuration
+    mEncParams.s_src_prms.i4_width = mSize->width;
+    mEncParams.s_src_prms.i4_height = mSize->height;
+    mEncParams.s_src_prms.i4_frm_rate_denom = 1000;
+    mEncParams.s_src_prms.i4_frm_rate_num =
+        mFrameRate->value * mEncParams.s_src_prms.i4_frm_rate_denom;
+    mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P5;
+    mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_tgt_bitrate[0] =
+        mBitrate->value;
+    mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_peak_bitrate[0] =
+        mBitrate->value << 1;
+    mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_codec_level = mHevcEncLevel;
+    mEncParams.s_coding_tools_prms.i4_max_i_open_gop_period = mIDRInterval;
+    mEncParams.s_coding_tools_prms.i4_max_cra_open_gop_period = mIDRInterval;
+    mIvVideoColorFormat = IV_YUV_420P;
+    mEncParams.s_multi_thrd_prms.i4_max_num_cores = mNumCores;
+    mEncParams.s_out_strm_prms.i4_codec_profile = mHevcEncProfile;
+    mEncParams.s_lap_prms.i4_rc_look_ahead_pics = DEFAULT_RC_LOOKAHEAD;
+    mEncParams.s_coding_tools_prms.i4_max_temporal_layers = DEFAULT_B_FRAMES;
+
+    switch (mBitrateMode->value) {
+        case C2Config::BITRATE_IGNORE:
+            mEncParams.s_config_prms.i4_rate_control_mode = 3;
+            mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_frame_qp[0] =
+                getQpFromQuality(mQuality->value);
+            break;
+        case C2Config::BITRATE_CONST:
+            mEncParams.s_config_prms.i4_rate_control_mode = 5;
+            break;
+        case C2Config::BITRATE_VARIABLE:
+            [[fallthrough]];
+        default:
+            mEncParams.s_config_prms.i4_rate_control_mode = 2;
+            break;
+        break;
+    }
+
+    if (mComplexity->value == 10) {
+        mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P0;
+    } else if (mComplexity->value >= 8) {
+        mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P2;
+    } else if (mComplexity->value >= 7) {
+        mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P3;
+    } else if (mComplexity->value >= 5) {
+        mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P4;
+    } else {
+        mEncParams.s_tgt_lyr_prms.as_tgt_params[0].i4_quality_preset = IHEVCE_QUALITY_P5;
+    }
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftHevcEnc::releaseEncoder() {
+    mSpsPpsHeaderReceived = false;
+    mSignalledEos = false;
+    mSignalledError = false;
+    mStarted = false;
+
+    if (mCodecCtx) {
+        IHEVCE_PLUGIN_STATUS_T err = ihevce_close(mCodecCtx);
+        if (IHEVCE_EOK != err) return C2_CORRUPTED;
+        mCodecCtx = nullptr;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftHevcEnc::drain(uint32_t drainMode,
+                                 const std::shared_ptr<C2BlockPool>& pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+c2_status_t C2SoftHevcEnc::initEncoder() {
+    CHECK(!mCodecCtx);
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        mSize = mIntf->getSize_l();
+        mBitrateMode = mIntf->getBitrateMode_l();
+        mBitrate = mIntf->getBitrate_l();
+        mFrameRate = mIntf->getFrameRate_l();
+        mHevcEncProfile = mIntf->getProfile_l();
+        mHevcEncLevel = mIntf->getLevel_l();
+        mIDRInterval = mIntf->getSyncFramePeriod_l();
+        mComplexity = mIntf->getComplexity_l();
+        mQuality = mIntf->getQuality_l();
+    }
+
+    c2_status_t status = initEncParams();
+
+    if (C2_OK != status) {
+        ALOGE("Failed to initialize encoder params : 0x%x", status);
+        mSignalledError = true;
+        return status;
+    }
+
+    IHEVCE_PLUGIN_STATUS_T err = IHEVCE_EOK;
+    err = ihevce_init(&mEncParams, &mCodecCtx);
+    if (IHEVCE_EOK != err) {
+        ALOGE("HEVC encoder init failed : 0x%x", err);
+        return C2_CORRUPTED;
+    }
+
+    mStarted = true;
+    return C2_OK;
+}
+
+c2_status_t C2SoftHevcEnc::setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip,
+                                         const C2GraphicView* const input,
+                                         uint64_t workIndex) {
+    ihevce_static_cfg_params_t* params = &mEncParams;
+    memset(ps_encode_ip, 0, sizeof(*ps_encode_ip));
+
+    if (!input) {
+        return C2_OK;
+    }
+
+    if (input->width() < mSize->width ||
+        input->height() < mSize->height) {
+        /* Expect width height to be configured */
+        ALOGW("unexpected Capacity Aspect %d(%d) x %d(%d)", input->width(),
+              mSize->width, input->height(), mSize->height);
+        return C2_BAD_VALUE;
+    }
+
+    const C2PlanarLayout& layout = input->layout();
+    uint8_t* yPlane =
+        const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_Y]);
+    uint8_t* uPlane =
+        const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_U]);
+    uint8_t* vPlane =
+        const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_V]);
+    int32_t yStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+    int32_t uStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+    int32_t vStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc;
+
+    const uint32_t width = mSize->width;
+    const uint32_t height = mSize->height;
+
+    // width and height must be even
+    if (width & 1u || height & 1u) {
+        ALOGW("height(%u) and width(%u) must both be even", height, width);
+        return C2_BAD_VALUE;
+    }
+
+    size_t yPlaneSize = width * height;
+
+    switch (layout.type) {
+        case C2PlanarLayout::TYPE_RGB:
+            [[fallthrough]];
+        case C2PlanarLayout::TYPE_RGBA: {
+            MemoryBlock conversionBuffer =
+                mConversionBuffers.fetch(yPlaneSize * 3 / 2);
+            mConversionBuffersInUse.emplace(conversionBuffer.data(),
+                                            conversionBuffer);
+            yPlane = conversionBuffer.data();
+            uPlane = yPlane + yPlaneSize;
+            vPlane = uPlane + yPlaneSize / 4;
+            yStride = width;
+            uStride = vStride = yStride / 2;
+            ConvertRGBToPlanarYUV(yPlane, yStride, height,
+                                  conversionBuffer.size(), *input);
+            break;
+        }
+        case C2PlanarLayout::TYPE_YUV: {
+            if (!IsYUV420(*input)) {
+                ALOGE("input is not YUV420");
+                return C2_BAD_VALUE;
+            }
+
+            if (layout.planes[layout.PLANE_Y].colInc == 1 &&
+                layout.planes[layout.PLANE_U].colInc == 1 &&
+                layout.planes[layout.PLANE_V].colInc == 1 &&
+                uStride == vStride && yStride == 2 * vStride) {
+                // I420 compatible - already set up above
+                break;
+            }
+
+            // copy to I420
+            yStride = width;
+            uStride = vStride = yStride / 2;
+            MemoryBlock conversionBuffer =
+                mConversionBuffers.fetch(yPlaneSize * 3 / 2);
+            mConversionBuffersInUse.emplace(conversionBuffer.data(),
+                                            conversionBuffer);
+            MediaImage2 img =
+                CreateYUV420PlanarMediaImage2(width, height, yStride, height);
+            status_t err = ImageCopy(conversionBuffer.data(), &img, *input);
+            if (err != OK) {
+                ALOGE("Buffer conversion failed: %d", err);
+                return C2_BAD_VALUE;
+            }
+            yPlane = conversionBuffer.data();
+            uPlane = yPlane + yPlaneSize;
+            vPlane = uPlane + yPlaneSize / 4;
+            break;
+        }
+
+        case C2PlanarLayout::TYPE_YUVA:
+            ALOGE("YUVA plane type is not supported");
+            return C2_BAD_VALUE;
+
+        default:
+            ALOGE("Unrecognized plane type: %d", layout.type);
+            return C2_BAD_VALUE;
+    }
+
+    switch (mIvVideoColorFormat) {
+        case IV_YUV_420P: {
+            // input buffer is supposed to be const but Ittiam API wants bare
+            // pointer.
+            ps_encode_ip->apv_inp_planes[0] = yPlane;
+            ps_encode_ip->apv_inp_planes[1] = uPlane;
+            ps_encode_ip->apv_inp_planes[2] = vPlane;
+
+            ps_encode_ip->ai4_inp_strd[0] = yStride;
+            ps_encode_ip->ai4_inp_strd[1] = uStride;
+            ps_encode_ip->ai4_inp_strd[2] = vStride;
+
+            ps_encode_ip->ai4_inp_size[0] = yStride * height;
+            ps_encode_ip->ai4_inp_size[1] = uStride * height >> 1;
+            ps_encode_ip->ai4_inp_size[2] = vStride * height >> 1;
+            break;
+        }
+
+        case IV_YUV_422ILE: {
+            // TODO
+            break;
+        }
+
+        case IV_YUV_420SP_UV:
+        case IV_YUV_420SP_VU:
+        default: {
+            ps_encode_ip->apv_inp_planes[0] = yPlane;
+            ps_encode_ip->apv_inp_planes[1] = uPlane;
+            ps_encode_ip->apv_inp_planes[2] = nullptr;
+
+            ps_encode_ip->ai4_inp_strd[0] = yStride;
+            ps_encode_ip->ai4_inp_strd[1] = uStride;
+            ps_encode_ip->ai4_inp_strd[2] = 0;
+
+            ps_encode_ip->ai4_inp_size[0] = yStride * height;
+            ps_encode_ip->ai4_inp_size[1] = uStride * height >> 1;
+            ps_encode_ip->ai4_inp_size[2] = 0;
+            break;
+        }
+    }
+
+    ps_encode_ip->i4_curr_bitrate =
+        params->s_tgt_lyr_prms.as_tgt_params[0].ai4_tgt_bitrate[0];
+    ps_encode_ip->i4_curr_peak_bitrate =
+        params->s_tgt_lyr_prms.as_tgt_params[0].ai4_peak_bitrate[0];
+    ps_encode_ip->i4_curr_rate_factor = params->s_config_prms.i4_rate_factor;
+    ps_encode_ip->u8_pts = workIndex;
+    return C2_OK;
+}
+
+void C2SoftHevcEnc::finishWork(uint64_t index,
+                               const std::unique_ptr<C2Work>& work,
+                               const std::shared_ptr<C2BlockPool>& pool,
+                               ihevce_out_buf_t* ps_encode_op) {
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+    c2_status_t status =
+        pool->fetchLinearBlock(ps_encode_op->i4_bytes_generated, usage, &block);
+    if (C2_OK != status) {
+        ALOGE("fetchLinearBlock for Output failed with status 0x%x", status);
+        mSignalledError = true;
+        work->result = status;
+        work->workletsProcessed = 1u;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    if (C2_OK != wView.error()) {
+        ALOGE("write view map failed with status 0x%x", wView.error());
+        mSignalledError = true;
+        work->result = wView.error();
+        work->workletsProcessed = 1u;
+        return;
+    }
+    memcpy(wView.data(), ps_encode_op->pu1_output_buf,
+           ps_encode_op->i4_bytes_generated);
+
+    std::shared_ptr<C2Buffer> buffer =
+        createLinearBuffer(block, 0, ps_encode_op->i4_bytes_generated);
+
+    DUMP_TO_FILE(mOutFile, ps_encode_op->pu1_output_buf,
+                 ps_encode_op->i4_bytes_generated);
+
+    if (ps_encode_op->i4_is_key_frame) {
+        ALOGV("IDR frame produced");
+        buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
+            0u /* stream id */, C2Config::SYNC_FRAME));
+    }
+
+    auto fillWork = [buffer](const std::unique_ptr<C2Work>& work) {
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        fillWork(work);
+        if (mSignalledEos) {
+            work->worklets.front()->output.flags =
+                C2FrameData::FLAG_END_OF_STREAM;
+        }
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+c2_status_t C2SoftHevcEnc::drainInternal(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work) {
+
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    while (true) {
+        ihevce_out_buf_t s_encode_op{};
+        memset(&s_encode_op, 0, sizeof(s_encode_op));
+
+        ihevce_encode(mCodecCtx, nullptr, &s_encode_op);
+        if (s_encode_op.i4_bytes_generated) {
+            finishWork(s_encode_op.u8_pts, work, pool, &s_encode_op);
+        } else {
+            if (work->workletsProcessed != 1u) fillEmptyWork(work);
+            break;
+        }
+    }
+    return C2_OK;
+}
+
+void C2SoftHevcEnc::process(const std::unique_ptr<C2Work>& work,
+                            const std::shared_ptr<C2BlockPool>& pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledEos) {
+        work->result = C2_BAD_VALUE;
+        ALOGD("Signalled Error / Signalled Eos");
+        return;
+    }
+    c2_status_t status = C2_OK;
+
+    // Initialize encoder if not already initialized
+    if (!mStarted) {
+        status = initEncoder();
+        if (C2_OK != status) {
+            ALOGE("Failed to initialize encoder : 0x%x", status);
+            mSignalledError = true;
+            work->result = status;
+            work->workletsProcessed = 1u;
+            return;
+        }
+    }
+
+    std::shared_ptr<const C2GraphicView> view;
+    std::shared_ptr<C2Buffer> inputBuffer = nullptr;
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    if (eos) mSignalledEos = true;
+
+    if (!work->input.buffers.empty()) {
+        inputBuffer = work->input.buffers[0];
+        view = std::make_shared<const C2GraphicView>(
+            inputBuffer->data().graphicBlocks().front().map().get());
+        if (view->error() != C2_OK) {
+            ALOGE("graphic view map err = %d", view->error());
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return;
+        }
+    }
+    IHEVCE_PLUGIN_STATUS_T err = IHEVCE_EOK;
+
+    if (!mSpsPpsHeaderReceived) {
+        ihevce_out_buf_t s_header_op{};
+        err = ihevce_encode_header(mCodecCtx, &s_header_op);
+        if (err == IHEVCE_EOK && s_header_op.i4_bytes_generated) {
+            std::unique_ptr<C2StreamInitDataInfo::output> csd =
+                C2StreamInitDataInfo::output::AllocUnique(
+                    s_header_op.i4_bytes_generated, 0u);
+            if (!csd) {
+                ALOGE("CSD allocation failed");
+                mSignalledError = true;
+                work->result = C2_NO_MEMORY;
+                work->workletsProcessed = 1u;
+                return;
+            }
+            memcpy(csd->m.value, s_header_op.pu1_output_buf,
+                   s_header_op.i4_bytes_generated);
+            DUMP_TO_FILE(mOutFile, csd->m.value, csd->flexCount());
+            work->worklets.front()->output.configUpdate.push_back(
+                std::move(csd));
+            mSpsPpsHeaderReceived = true;
+        }
+        if (!inputBuffer) {
+            work->workletsProcessed = 1u;
+            return;
+        }
+    }
+
+    // handle dynamic config parameters
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
+        lock.unlock();
+
+        if (bitrate != mBitrate) {
+            mBitrate = bitrate;
+            mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_tgt_bitrate[0] =
+                mBitrate->value;
+            mEncParams.s_tgt_lyr_prms.as_tgt_params[0].ai4_peak_bitrate[0] =
+                mBitrate->value << 1;
+        }
+    }
+
+    ihevce_inp_buf_t s_encode_ip{};
+    ihevce_out_buf_t s_encode_op{};
+    uint64_t workIndex = work->input.ordinal.frameIndex.peekull();
+
+    status = setEncodeArgs(&s_encode_ip, view.get(), workIndex);
+    if (C2_OK != status) {
+        ALOGE("setEncodeArgs failed : 0x%x", status);
+        mSignalledError = true;
+        work->result = status;
+        work->workletsProcessed = 1u;
+        return;
+    }
+
+    uint64_t timeDelay = 0;
+    uint64_t timeTaken = 0;
+    memset(&s_encode_op, 0, sizeof(s_encode_op));
+    GETTIME(&mTimeStart, nullptr);
+    TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+    if (inputBuffer) {
+        err = ihevce_encode(mCodecCtx, &s_encode_ip, &s_encode_op);
+        if (IHEVCE_EOK != err) {
+            ALOGE("Encode Frame failed : 0x%x", err);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            work->workletsProcessed = 1u;
+            return;
+        }
+    } else if (!eos) {
+        fillEmptyWork(work);
+    }
+
+    GETTIME(&mTimeEnd, nullptr);
+    /* Compute time taken for decode() */
+    TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
+
+    ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", (int)timeTaken,
+          (int)timeDelay, s_encode_op.i4_bytes_generated);
+
+    if (s_encode_op.i4_bytes_generated) {
+        finishWork(s_encode_op.u8_pts, work, pool, &s_encode_op);
+    }
+
+    if (eos) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+    }
+}
+
+class C2SoftHevcEncFactory : public C2ComponentFactory {
+   public:
+    C2SoftHevcEncFactory()
+        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+              GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+    c2_status_t createComponent(
+        c2_node_id_t id,
+        std::shared_ptr<C2Component>* const component,
+        std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+            new C2SoftHevcEnc(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftHevcEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    c2_status_t createInterface(
+        c2_node_id_t id,
+        std::shared_ptr<C2ComponentInterface>* const interface,
+        std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+            new SimpleInterface<C2SoftHevcEnc::IntfImpl>(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftHevcEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    ~C2SoftHevcEncFactory() override = default;
+
+   private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftHevcEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.h b/media/codec2/components/hevc/C2SoftHevcEnc.h
new file mode 100644
index 0000000..f2c7642
--- /dev/null
+++ b/media/codec2/components/hevc/C2SoftHevcEnc.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_C2_SOFT_HEVC_ENC_H_
+#define ANDROID_C2_SOFT_HEVC_ENC_H_
+
+#include <SimpleC2Component.h>
+#include <algorithm>
+#include <map>
+#include <media/stagefright/foundation/ColorUtils.h>
+#include <utils/Vector.h>
+
+#include "ihevc_typedefs.h"
+
+namespace android {
+
+/** Get time */
+#define GETTIME(a, b) gettimeofday(a, b)
+
+/** Compute difference between start and end */
+#define TIME_DIFF(start, end, diff)                      \
+    diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+           ((end).tv_usec - (start).tv_usec);
+
+#define CODEC_MAX_CORES  4
+#define MAX_B_FRAMES     1
+#define MAX_RC_LOOKAHEAD 1
+
+#define DEFAULT_B_FRAMES     0
+#define DEFAULT_RC_LOOKAHEAD 0
+
+struct C2SoftHevcEnc : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftHevcEnc(const char* name, c2_node_id_t id,
+                  const std::shared_ptr<IntfImpl>& intfImpl);
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(const std::unique_ptr<C2Work>& work,
+                 const std::shared_ptr<C2BlockPool>& pool) override;
+    c2_status_t drain(uint32_t drainMode,
+                      const std::shared_ptr<C2BlockPool>& pool) override;
+
+   protected:
+    ~C2SoftHevcEnc() override;
+
+   private:
+    std::shared_ptr<IntfImpl> mIntf;
+    ihevce_static_cfg_params_t mEncParams;
+    size_t mNumCores;
+    UWORD32 mIDRInterval;
+    IV_COLOR_FORMAT_T mIvVideoColorFormat;
+    UWORD32 mHevcEncProfile;
+    UWORD32 mHevcEncLevel;
+    bool mStarted;
+    bool mSpsPpsHeaderReceived;
+    bool mSignalledEos;
+    bool mSignalledError;
+    void* mCodecCtx;
+    MemoryBlockPool mConversionBuffers;
+    std::map<void*, MemoryBlock> mConversionBuffersInUse;
+    // configurations used by component in process
+    // (TODO: keep this in intf but make them internal only)
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
+    std::shared_ptr<C2StreamComplexityTuning::output> mComplexity;
+    std::shared_ptr<C2StreamQualityTuning::output> mQuality;
+
+#ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+    char mOutFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+    // profile
+    struct timeval mTimeStart;
+    struct timeval mTimeEnd;
+
+    c2_status_t initEncParams();
+    c2_status_t initEncoder();
+    c2_status_t releaseEncoder();
+    c2_status_t setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip,
+                              const C2GraphicView* const input,
+                              uint64_t workIndex);
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work>& work,
+                    const std::shared_ptr<C2BlockPool>& pool,
+                    ihevce_out_buf_t* ps_encode_op);
+    c2_status_t drainInternal(uint32_t drainMode,
+                              const std::shared_ptr<C2BlockPool>& pool,
+                              const std::unique_ptr<C2Work>& work);
+    C2_DO_NOT_COPY(C2SoftHevcEnc);
+};
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH "/data/local/tmp/hevc"
+#define INPUT_DUMP_EXT "yuv"
+#define OUTPUT_DUMP_PATH "/data/local/tmp/hevc"
+#define OUTPUT_DUMP_EXT "h265"
+#define GENERATE_FILE_NAMES()                                             \
+{                                                                         \
+    GETTIME(&mTimeStart, NULL);                                           \
+    strcpy(mInFile, "");                                                  \
+    ALOGD("GENERATE_FILE_NAMES");                                         \
+    sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, mTimeStart.tv_sec, \
+            mTimeStart.tv_usec, INPUT_DUMP_EXT);                          \
+    strcpy(mOutFile, "");                                                 \
+    sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH,                  \
+            mTimeStart.tv_sec, mTimeStart.tv_usec, OUTPUT_DUMP_EXT);      \
+}
+
+#define CREATE_DUMP_FILE(m_filename)                 \
+{                                                    \
+    FILE* fp = fopen(m_filename, "wb");              \
+    if (fp != NULL) {                                \
+        ALOGD("Opened file %s", m_filename);         \
+        fclose(fp);                                  \
+    } else {                                         \
+        ALOGD("Could not open file %s", m_filename); \
+    }                                                \
+}
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)          \
+{                                                        \
+    FILE* fp = fopen(m_filename, "ab");                  \
+    if (fp != NULL && m_buf != NULL) {                   \
+        int i;                                           \
+        ALOGD("Dump to file!");                          \
+        i = fwrite(m_buf, 1, m_size, fp);                \
+        if (i != (int)m_size) {                          \
+            ALOGD("Error in fwrite, returned %d", i);    \
+            perror("Error in write to file");            \
+        }                                                \
+        fclose(fp);                                      \
+    } else {                                             \
+        ALOGD("Could not write to file %s", m_filename); \
+        if (fp != NULL) fclose(fp);                      \
+    }                                                    \
+}
+#else /* FILE_DUMP_ENABLE */
+#define INPUT_DUMP_PATH
+#define INPUT_DUMP_EXT
+#define OUTPUT_DUMP_PATH
+#define OUTPUT_DUMP_EXT
+#define GENERATE_FILE_NAMES()
+#define CREATE_DUMP_FILE(m_filename)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+}  // namespace android
+
+#endif  // C2_SOFT_HEVC_ENC_H__
diff --git a/media/codec2/components/mp3/Android.bp b/media/codec2/components/mp3/Android.bp
new file mode 100644
index 0000000..66665ed
--- /dev/null
+++ b/media/codec2/components/mp3/Android.bp
@@ -0,0 +1,11 @@
+cc_library_shared {
+    name: "libcodec2_soft_mp3dec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftMp3Dec.cpp"],
+
+    static_libs: ["libstagefright_mp3dec"],
+}
diff --git a/media/codec2/components/mp3/C2SoftMp3Dec.cpp b/media/codec2/components/mp3/C2SoftMp3Dec.cpp
new file mode 100644
index 0000000..5ba7e3d
--- /dev/null
+++ b/media/codec2/components/mp3/C2SoftMp3Dec.cpp
@@ -0,0 +1,550 @@
+/*
+ * 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 "C2SoftMp3Dec"
+#include <log/log.h>
+
+#include <numeric>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftMp3Dec.h"
+#include "pvmp3decoder_api.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.mp3.decoder";
+
+}  // namespace
+
+class C2SoftMP3::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_MPEG) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::output(0u, 44100))
+                .withFields({C2F(mSampleRate, value).oneOf({8000, 11025, 12000, 16000,
+                    22050, 24000, 32000, 44100, 48000})})
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::output(0u, 2))
+                .withFields({C2F(mChannelCount, value).inRange(1, 2)})
+                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::input(0u, 64000))
+                .withFields({C2F(mBitrate, value).inRange(8000, 320000)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 8192))
+                .build());
+    }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+};
+
+C2SoftMP3::C2SoftMP3(const char *name, c2_node_id_t id,
+                     const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mConfig(nullptr),
+      mDecoderBuf(nullptr) {
+}
+
+C2SoftMP3::~C2SoftMP3() {
+    onRelease();
+}
+
+c2_status_t C2SoftMP3::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+c2_status_t C2SoftMP3::onStop() {
+    // Make sure that the next buffer output does not still
+    // depend on fragments from the last one decoded.
+    pvmp3_InitDecoder(mConfig, mDecoderBuf);
+    mSignalledError = false;
+    mIsFirst = true;
+    mSignalledOutputEos = false;
+    mAnchorTimeStamp = 0;
+    mProcessedSamples = 0;
+
+    return C2_OK;
+}
+
+void C2SoftMP3::onReset() {
+    (void)onStop();
+}
+
+void C2SoftMP3::onRelease() {
+    mGaplessBytes = false;
+    if (mDecoderBuf) {
+        free(mDecoderBuf);
+        mDecoderBuf = nullptr;
+    }
+
+    if (mConfig) {
+        delete mConfig;
+        mConfig = nullptr;
+    }
+}
+
+status_t C2SoftMP3::initDecoder() {
+    mConfig = new tPVMP3DecoderExternal{};
+    if (!mConfig) return NO_MEMORY;
+    mConfig->equalizerType = flat;
+    mConfig->crcEnabled = false;
+
+    size_t memRequirements = pvmp3_decoderMemRequirements();
+    mDecoderBuf = malloc(memRequirements);
+    if (!mDecoderBuf) return NO_MEMORY;
+
+    pvmp3_InitDecoder(mConfig, mDecoderBuf);
+
+    mIsFirst = true;
+    mGaplessBytes = false;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mAnchorTimeStamp = 0;
+    mProcessedSamples = 0;
+
+    return OK;
+}
+
+/* The below code is borrowed from ./test/mp3reader.cpp */
+static bool parseMp3Header(uint32_t header, size_t *frame_size,
+                           uint32_t *out_sampling_rate = nullptr,
+                           uint32_t *out_channels = nullptr,
+                           uint32_t *out_bitrate = nullptr,
+                           uint32_t *out_num_samples = nullptr) {
+    *frame_size = 0;
+    if (out_sampling_rate) *out_sampling_rate = 0;
+    if (out_channels) *out_channels = 0;
+    if (out_bitrate) *out_bitrate = 0;
+    if (out_num_samples) *out_num_samples = 1152;
+
+    if ((header & 0xffe00000) != 0xffe00000) return false;
+
+    unsigned version = (header >> 19) & 3;
+    if (version == 0x01) return false;
+
+    unsigned layer = (header >> 17) & 3;
+    if (layer == 0x00) return false;
+
+    unsigned bitrate_index = (header >> 12) & 0x0f;
+    if (bitrate_index == 0 || bitrate_index == 0x0f) return false;
+
+    unsigned sampling_rate_index = (header >> 10) & 3;
+    if (sampling_rate_index == 3) return false;
+
+    static const int kSamplingRateV1[] = { 44100, 48000, 32000 };
+    int sampling_rate = kSamplingRateV1[sampling_rate_index];
+    if (version == 2 /* V2 */) {
+        sampling_rate /= 2;
+    } else if (version == 0 /* V2.5 */) {
+        sampling_rate /= 4;
+    }
+
+    unsigned padding = (header >> 9) & 1;
+
+    if (layer == 3) { // layer I
+        static const int kBitrateV1[] =
+        {
+            32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448
+        };
+        static const int kBitrateV2[] =
+        {
+            32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256
+        };
+
+        int bitrate = (version == 3 /* V1 */) ? kBitrateV1[bitrate_index - 1] :
+                kBitrateV2[bitrate_index - 1];
+
+        if (out_bitrate) {
+            *out_bitrate = bitrate;
+        }
+        *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+        if (out_num_samples) {
+            *out_num_samples = 384;
+        }
+    } else { // layer II or III
+        static const int kBitrateV1L2[] =
+        {
+            32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384
+        };
+
+        static const int kBitrateV1L3[] =
+        {
+            32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320
+        };
+
+        static const int kBitrateV2[] =
+        {
+            8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160
+        };
+
+        int bitrate;
+        if (version == 3 /* V1 */) {
+            bitrate = (layer == 2 /* L2 */) ? kBitrateV1L2[bitrate_index - 1] :
+                    kBitrateV1L3[bitrate_index - 1];
+
+            if (out_num_samples) {
+                *out_num_samples = 1152;
+            }
+        } else { // V2 (or 2.5)
+            bitrate = kBitrateV2[bitrate_index - 1];
+            if (out_num_samples) {
+                *out_num_samples = (layer == 1 /* L3 */) ? 576 : 1152;
+            }
+        }
+
+        if (out_bitrate) {
+            *out_bitrate = bitrate;
+        }
+
+        if (version == 3 /* V1 */) {
+            *frame_size = 144000 * bitrate / sampling_rate + padding;
+        } else { // V2 or V2.5
+            size_t tmp = (layer == 1 /* L3 */) ? 72000 : 144000;
+            *frame_size = tmp * bitrate / sampling_rate + padding;
+        }
+    }
+
+    if (out_sampling_rate) {
+        *out_sampling_rate = sampling_rate;
+    }
+
+    if (out_channels) {
+        int channel_mode = (header >> 6) & 3;
+
+        *out_channels = (channel_mode == 3) ? 1 : 2;
+    }
+
+    return true;
+}
+
+static uint32_t U32_AT(const uint8_t *ptr) {
+    return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+static status_t calculateOutSize(uint8 *header, size_t inSize,
+                                 std::vector<size_t> *decodedSizes) {
+    uint32_t channels;
+    uint32_t numSamples;
+    size_t frameSize;
+    size_t totalInSize = 0;
+
+    while (totalInSize + 4 < inSize) {
+        if (!parseMp3Header(U32_AT(header + totalInSize), &frameSize,
+                            nullptr, &channels, nullptr, &numSamples)) {
+            ALOGE("Error in parse mp3 header during outSize estimation");
+            return UNKNOWN_ERROR;
+        }
+        totalInSize += frameSize;
+        decodedSizes->push_back(numSamples * channels * sizeof(int16_t));
+    }
+
+    if (decodedSizes->empty()) return UNKNOWN_ERROR;
+
+    return OK;
+}
+
+c2_status_t C2SoftMP3::onFlush_sm() {
+    return onStop();
+}
+
+c2_status_t C2SoftMP3::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    return C2_OK;
+}
+
+// TODO: Can overall error checking be improved? As in the check for validity of
+//       work, pool ptr, work->input.buffers.size() == 1, ...
+// TODO: Blind removal of 529 samples from the output may not work. Because
+//       mpeg layer 1 frame size is 384 samples per frame. This should introduce
+//       negative values and can cause SEG faults. Soft omx mp3 plugin can have
+//       this problem (CHECK!)
+void C2SoftMP3::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    size_t inSize = 0u;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = rView.error();
+            return;
+        }
+    }
+
+    if (inSize == 0 && (!mGaplessBytes || !eos)) {
+        work->worklets.front()->output.flags = work->input.flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        return;
+    }
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+          (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+
+    int32_t numChannels = mConfig->num_channels;
+    size_t calOutSize;
+    std::vector<size_t> decodedSizes;
+    if (inSize && OK != calculateOutSize(const_cast<uint8 *>(rView.data()),
+                                         inSize, &decodedSizes)) {
+        work->result = C2_CORRUPTED;
+        return;
+    }
+    calOutSize = std::accumulate(decodedSizes.begin(), decodedSizes.end(), 0);
+    if (eos) {
+        calOutSize += kPVMP3DecoderDelay * numChannels * sizeof(int16_t);
+    }
+
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(calOutSize, usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = wView.error();
+        return;
+    }
+
+    int outSize = 0;
+    int outOffset = 0;
+    auto it = decodedSizes.begin();
+    size_t inPos = 0;
+    int32_t samplingRate = mConfig->samplingRate;
+    while (inPos < inSize) {
+        if (it == decodedSizes.end()) {
+            ALOGE("unexpected trailing bytes, ignoring them");
+            break;
+        }
+
+        mConfig->pInputBuffer = const_cast<uint8 *>(rView.data() + inPos);
+        mConfig->inputBufferCurrentLength = (inSize - inPos);
+        mConfig->inputBufferMaxLength = 0;
+        mConfig->inputBufferUsedLength = 0;
+        mConfig->outputFrameSize = (calOutSize - outSize);
+        mConfig->pOutputBuffer = reinterpret_cast<int16_t *> (wView.data() + outSize);
+
+        ERROR_CODE decoderErr;
+        if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf))
+                != NO_DECODING_ERROR) {
+            ALOGE("mp3 decoder returned error %d", decoderErr);
+            if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR
+                    && decoderErr != SIDE_INFO_ERROR) {
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+
+            // This is recoverable, just ignore the current frame and
+            // play silence instead.
+            ALOGV("ignoring error and sending silence");
+            if (mConfig->outputFrameSize == 0) {
+                mConfig->outputFrameSize = *it / sizeof(int16_t);
+            }
+            memset(mConfig->pOutputBuffer, 0, mConfig->outputFrameSize * sizeof(int16_t));
+        } else if (mConfig->samplingRate != samplingRate
+                || mConfig->num_channels != numChannels) {
+            ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+                   samplingRate, mConfig->samplingRate,
+                   numChannels, mConfig->num_channels);
+            samplingRate = mConfig->samplingRate;
+            numChannels = mConfig->num_channels;
+
+            C2StreamSampleRateInfo::output sampleRateInfo(0u, samplingRate);
+            C2StreamChannelCountInfo::output channelCountInfo(0u, numChannels);
+            std::vector<std::unique_ptr<C2SettingResult>> failures;
+            c2_status_t err = mIntf->config(
+                    { &sampleRateInfo, &channelCountInfo },
+                    C2_MAY_BLOCK,
+                    &failures);
+            if (err == OK) {
+                work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(sampleRateInfo));
+                work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(channelCountInfo));
+            } else {
+                ALOGE("Config Update failed");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+        }
+        if (*it != mConfig->outputFrameSize * sizeof(int16_t)) {
+            ALOGE("panic, parsed size does not match decoded size");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        outSize += mConfig->outputFrameSize * sizeof(int16_t);
+        inPos += mConfig->inputBufferUsedLength;
+        it++;
+    }
+    if (mIsFirst) {
+        mIsFirst = false;
+        mGaplessBytes = true;
+        // The decoder delay is 529 samples, so trim that many samples off
+        // the start of the first output buffer. This essentially makes this
+        // decoder have zero delay, which the rest of the pipeline assumes.
+        outOffset = kPVMP3DecoderDelay * numChannels * sizeof(int16_t);
+        mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+    }
+    if (eos) {
+        if (calOutSize >=
+            outSize + kPVMP3DecoderDelay * numChannels * sizeof(int16_t)) {
+            if (!memset(reinterpret_cast<int16_t*>(wView.data() + outSize), 0,
+                        kPVMP3DecoderDelay * numChannels * sizeof(int16_t))) {
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+             }
+            ALOGV("Adding 529 samples at end");
+            mGaplessBytes = false;
+            outSize += kPVMP3DecoderDelay * numChannels * sizeof(int16_t);
+        }
+    }
+
+    uint64_t outTimeStamp = mProcessedSamples * 1000000ll / samplingRate;
+    mProcessedSamples += ((outSize - outOffset) / (numChannels * sizeof(int16_t)));
+    ALOGV("out buffer attr. offset %d size %d timestamp %u", outOffset, outSize - outOffset,
+          (uint32_t)(mAnchorTimeStamp + outTimeStamp));
+    decodedSizes.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.buffers.push_back(
+            createLinearBuffer(block, outOffset, outSize - outOffset));
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+    }
+}
+
+class C2SoftMp3DecFactory : public C2ComponentFactory {
+public:
+    C2SoftMp3DecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+              new C2SoftMP3(COMPONENT_NAME,
+                            id,
+                            std::make_shared<C2SoftMP3::IntfImpl>(mHelper)),
+              deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+              new SimpleInterface<C2SoftMP3::IntfImpl>(
+                      COMPONENT_NAME, id, std::make_shared<C2SoftMP3::IntfImpl>(mHelper)),
+              deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftMp3DecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftMp3DecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/mp3/C2SoftMp3Dec.h b/media/codec2/components/mp3/C2SoftMp3Dec.h
new file mode 100644
index 0000000..402bdc4
--- /dev/null
+++ b/media/codec2/components/mp3/C2SoftMp3Dec.h
@@ -0,0 +1,76 @@
+/*
+ * 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_C2_SOFT_MP3_DEC_H_
+#define ANDROID_C2_SOFT_MP3_DEC_H_
+
+#include <SimpleC2Component.h>
+
+
+struct tPVMP3DecoderExternal;
+
+bool parseMp3Header(uint32_t header, size_t *frame_size,
+                    uint32_t *out_sampling_rate = nullptr,
+                    uint32_t *out_channels = nullptr,
+                    uint32_t *out_bitrate = nullptr,
+                    uint32_t *out_num_samples = nullptr);
+
+namespace android {
+
+struct C2SoftMP3 : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftMP3(const char *name, c2_node_id_t id,
+              const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftMP3();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    enum {
+        kPVMP3DecoderDelay = 529 // samples
+    };
+
+    std::shared_ptr<IntfImpl> mIntf;
+    tPVMP3DecoderExternal *mConfig;
+    void *mDecoderBuf;
+
+    bool mIsFirst;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+    bool mGaplessBytes;
+    uint64_t mAnchorTimeStamp;
+    uint64_t mProcessedSamples;
+
+    status_t initDecoder();
+
+    C2_DO_NOT_COPY(C2SoftMP3);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_MP3_DEC_H_
diff --git a/media/common_time/MODULE_LICENSE_APACHE2 b/media/codec2/components/mp3/MODULE_LICENSE_APACHE2
similarity index 100%
copy from media/common_time/MODULE_LICENSE_APACHE2
copy to media/codec2/components/mp3/MODULE_LICENSE_APACHE2
diff --git a/media/common_time/NOTICE b/media/codec2/components/mp3/NOTICE
similarity index 100%
copy from media/common_time/NOTICE
copy to media/codec2/components/mp3/NOTICE
diff --git a/media/codec2/components/mp3/patent_disclaimer.txt b/media/codec2/components/mp3/patent_disclaimer.txt
new file mode 100644
index 0000000..b4bf11d
--- /dev/null
+++ b/media/codec2/components/mp3/patent_disclaimer.txt
@@ -0,0 +1,9 @@
+
+THIS IS NOT A GRANT OF PATENT RIGHTS.
+
+Google makes no representation or warranty that the codecs for which
+source code is made available hereunder are unencumbered by
+third-party patents.  Those intending to use this source code in
+hardware or software products are advised that implementations of
+these codecs, including in open source software or shareware, may
+require patent licenses from the relevant patent holders.
diff --git a/media/codec2/components/mpeg2/Android.bp b/media/codec2/components/mpeg2/Android.bp
new file mode 100644
index 0000000..841f0a9
--- /dev/null
+++ b/media/codec2/components/mpeg2/Android.bp
@@ -0,0 +1,16 @@
+cc_library_shared {
+    name: "libcodec2_soft_mpeg2dec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_signed-defaults",
+    ],
+
+    srcs: ["C2SoftMpeg2Dec.cpp"],
+
+    static_libs: ["libmpeg2dec"],
+
+    include_dirs: [
+        "external/libmpeg2/decoder",
+        "external/libmpeg2/common",
+    ],
+}
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
new file mode 100644
index 0000000..df7b403
--- /dev/null
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
@@ -0,0 +1,1110 @@
+/*
+ * 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 "C2SoftMpeg2Dec"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <Codec2Mapper.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftMpeg2Dec.h"
+#include "impeg2d.h"
+
+namespace android {
+
+constexpr char COMPONENT_NAME[] = "c2.android.mpeg2.decoder";
+
+class C2SoftMpeg2Dec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_VIDEO,
+                MEDIA_MIMETYPE_VIDEO_MPEG2) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+
+        // TODO: output latency and reordering
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(16, 1920, 4),
+                    C2F(mSize, height).inRange(16, 1088, 4),
+                })
+                .withSetter(SizeSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::input(0u,
+                        C2Config::PROFILE_MP2V_SIMPLE, C2Config::LEVEL_MP2V_HIGH))
+                .withFields({
+                    C2F(mProfileLevel, profile).oneOf({
+                            C2Config::PROFILE_MP2V_SIMPLE,
+                            C2Config::PROFILE_MP2V_MAIN}),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_MP2V_LOW,
+                            C2Config::LEVEL_MP2V_MAIN,
+                            C2Config::LEVEL_MP2V_HIGH_1440,
+                            C2Config::LEVEL_MP2V_HIGH})
+                })
+                .withSetter(ProfileLevelSetter, mSize)
+                .build());
+
+        addParameter(
+                DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
+                .withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 1920, 2),
+                    C2F(mSize, height).inRange(2, 1088, 2),
+                })
+                .withSetter(MaxPictureSizeSetter, mSize)
+                .build());
+
+        addParameter(
+                DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 320 * 240 * 3 / 2))
+                .withFields({
+                    C2F(mMaxInputSize, value).any(),
+                })
+                .calculatedAs(MaxInputSizeSetter, mMaxSize)
+                .build());
+
+        C2ChromaOffsetStruct locations[1] = { C2ChromaOffsetStruct::ITU_YUV_420_0() };
+        std::shared_ptr<C2StreamColorInfo::output> defaultColorInfo =
+            C2StreamColorInfo::output::AllocShared(
+                    1u, 0u, 8u /* bitDepth */, C2Color::YUV_420);
+        memcpy(defaultColorInfo->m.locations, locations, sizeof(locations));
+
+        defaultColorInfo =
+            C2StreamColorInfo::output::AllocShared(
+                    { C2ChromaOffsetStruct::ITU_YUV_420_0() },
+                    0u, 8u /* bitDepth */, C2Color::YUV_420);
+        helper->addStructDescriptors<C2ChromaOffsetStruct>();
+
+        addParameter(
+                DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
+                .withConstValue(defaultColorInfo)
+                .build());
+
+        addParameter(
+                DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsTuning::output(
+                        0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+                        C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({
+                    C2F(mDefaultColorAspects, range).inRange(
+                                C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
+                    C2F(mDefaultColorAspects, primaries).inRange(
+                                C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+                    C2F(mDefaultColorAspects, transfer).inRange(
+                                C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER),
+                    C2F(mDefaultColorAspects, matrix).inRange(
+                                C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)
+                })
+                .withSetter(DefaultColorAspectsSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsInfo::input(
+                        0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
+                        C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({
+                    C2F(mCodedColorAspects, range).inRange(
+                                C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
+                    C2F(mCodedColorAspects, primaries).inRange(
+                                C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+                    C2F(mCodedColorAspects, transfer).inRange(
+                                C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER),
+                    C2F(mCodedColorAspects, matrix).inRange(
+                                C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)
+                })
+                .withSetter(CodedColorAspectsSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsInfo::output(
+                        0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+                        C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({
+                    C2F(mColorAspects, range).inRange(
+                                C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
+                    C2F(mColorAspects, primaries).inRange(
+                                C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+                    C2F(mColorAspects, transfer).inRange(
+                                C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER),
+                    C2F(mColorAspects, matrix).inRange(
+                                C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)
+                })
+                .withSetter(ColorAspectsSetter, mDefaultColorAspects, mCodedColorAspects)
+                .build());
+
+        // TODO: support more formats?
+        addParameter(
+                DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+                .withConstValue(new C2StreamPixelFormatInfo::output(
+                                     0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+                .build());
+    }
+
+    static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output> &oldMe,
+                          C2P<C2StreamPictureSizeInfo::output> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R MaxPictureSizeSetter(bool mayBlock, C2P<C2StreamMaxPictureSizeTuning::output> &me,
+                                    const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        // TODO: get max width/height from the size's field helpers vs. hardcoding
+        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 1920u);
+        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 1088u);
+        return C2R::Ok();
+    }
+
+    static C2R MaxInputSizeSetter(bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input> &me,
+                                  const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
+        (void)mayBlock;
+        // assume compression ratio of 1
+        me.set().value = (((maxSize.v.width + 15) / 16) * ((maxSize.v.height + 15) / 16) * 384);
+        return C2R::Ok();
+    }
+
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input> &me,
+                                  const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        (void)size;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+    static C2R DefaultColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsTuning::output> &me) {
+        (void)mayBlock;
+        if (me.v.range > C2Color::RANGE_OTHER) {
+                me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+                me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+                me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+                me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R CodedColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input> &me) {
+        (void)mayBlock;
+        if (me.v.range > C2Color::RANGE_OTHER) {
+                me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+                me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+                me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+                me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output> &me,
+                                  const C2P<C2StreamColorAspectsTuning::output> &def,
+                                  const C2P<C2StreamColorAspectsInfo::input> &coded) {
+        (void)mayBlock;
+        // take default values for all unspecified fields, and coded values for specified ones
+        me.set().range = coded.v.range == RANGE_UNSPECIFIED ? def.v.range : coded.v.range;
+        me.set().primaries = coded.v.primaries == PRIMARIES_UNSPECIFIED
+                ? def.v.primaries : coded.v.primaries;
+        me.set().transfer = coded.v.transfer == TRANSFER_UNSPECIFIED
+                ? def.v.transfer : coded.v.transfer;
+        me.set().matrix = coded.v.matrix == MATRIX_UNSPECIFIED ? def.v.matrix : coded.v.matrix;
+        return C2R::Ok();
+    }
+
+    std::shared_ptr<C2StreamColorAspectsInfo::output> getColorAspects_l() {
+        return mColorAspects;
+    }
+
+private:
+    std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+    std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
+    std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mMaxInputSize;
+    std::shared_ptr<C2StreamColorInfo::output> mColorInfo;
+    std::shared_ptr<C2StreamColorAspectsInfo::input> mCodedColorAspects;
+    std::shared_ptr<C2StreamColorAspectsTuning::output> mDefaultColorAspects;
+    std::shared_ptr<C2StreamColorAspectsInfo::output> mColorAspects;
+    std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
+};
+
+static size_t getCpuCoreCount() {
+    long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGV("Number of CPU cores: %ld", cpuCoreCount);
+    return (size_t)cpuCoreCount;
+}
+
+static void *ivd_aligned_malloc(WORD32 alignment, WORD32 size) {
+    return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *mem) {
+    free(mem);
+}
+
+C2SoftMpeg2Dec::C2SoftMpeg2Dec(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+        mIntf(intfImpl),
+        mDecHandle(nullptr),
+        mMemRecords(nullptr),
+        mOutBufferDrain(nullptr),
+        mIvColorformat(IV_YUV_420P),
+        mWidth(320),
+        mHeight(240),
+        mOutIndex(0u) {
+    // If input dump is enabled, then open create an empty file
+    GENERATE_FILE_NAMES();
+    CREATE_DUMP_FILE(mInFile);
+}
+
+C2SoftMpeg2Dec::~C2SoftMpeg2Dec() {
+    onRelease();
+}
+
+c2_status_t C2SoftMpeg2Dec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftMpeg2Dec::onStop() {
+    if (OK != resetDecoder()) return C2_CORRUPTED;
+    resetPlugin();
+    return C2_OK;
+}
+
+void C2SoftMpeg2Dec::onReset() {
+    (void) onStop();
+}
+
+void C2SoftMpeg2Dec::onRelease() {
+    (void) deleteDecoder();
+    if (mOutBufferDrain) {
+        ivd_aligned_free(mOutBufferDrain);
+        mOutBufferDrain = nullptr;
+    }
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
+    if (mMemRecords) {
+        ivd_aligned_free(mMemRecords);
+        mMemRecords = nullptr;
+    }
+}
+
+c2_status_t C2SoftMpeg2Dec::onFlush_sm() {
+    if (OK != setFlushMode()) return C2_CORRUPTED;
+
+    uint32_t displayStride = mStride;
+    uint32_t displayHeight = mHeight;
+    uint32_t bufferSize = displayStride * displayHeight * 3 / 2;
+    mOutBufferDrain = (uint8_t *)ivd_aligned_malloc(128, bufferSize);
+    if (!mOutBufferDrain) {
+        ALOGE("could not allocate tmp output buffer (for flush) of size %u ", bufferSize);
+        return C2_NO_MEMORY;
+    }
+
+    while (true) {
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+
+        setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, nullptr, 0, 0, 0);
+        (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        if (0 == s_decode_op.u4_output_present) {
+            resetPlugin();
+            break;
+        }
+    }
+
+    if (mOutBufferDrain) {
+        ivd_aligned_free(mOutBufferDrain);
+        mOutBufferDrain = nullptr;
+    }
+
+    return C2_OK;
+}
+
+status_t C2SoftMpeg2Dec::getNumMemRecords() {
+    iv_num_mem_rec_ip_t s_num_mem_rec_ip;
+    iv_num_mem_rec_op_t s_num_mem_rec_op;
+
+    s_num_mem_rec_ip.u4_size = sizeof(s_num_mem_rec_ip);
+    s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+    s_num_mem_rec_op.u4_size = sizeof(s_num_mem_rec_op);
+
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_num_mem_rec_ip,
+                                                     &s_num_mem_rec_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("Error in getting mem records: 0x%x", s_num_mem_rec_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+    mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::fillMemRecords() {
+    iv_mem_rec_t *ps_mem_rec = (iv_mem_rec_t *) ivd_aligned_malloc(
+            128, mNumMemRecords * sizeof(iv_mem_rec_t));
+    if (!ps_mem_rec) {
+        ALOGE("Allocation failure");
+        return NO_MEMORY;
+    }
+    memset(ps_mem_rec, 0, mNumMemRecords * sizeof(iv_mem_rec_t));
+    for (size_t i = 0; i < mNumMemRecords; i++)
+        ps_mem_rec[i].u4_size = sizeof(iv_mem_rec_t);
+    mMemRecords = ps_mem_rec;
+
+    ivdext_fill_mem_rec_ip_t s_fill_mem_ip;
+    ivdext_fill_mem_rec_op_t s_fill_mem_op;
+
+    s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size = sizeof(ivdext_fill_mem_rec_ip_t);
+    s_fill_mem_ip.u4_share_disp_buf = 0;
+    s_fill_mem_ip.e_output_format = mIvColorformat;
+    s_fill_mem_ip.u4_deinterlace = 1;
+    s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+    s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.pv_mem_rec_location = mMemRecords;
+    s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_wd = mWidth;
+    s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_ht = mHeight;
+    s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_size = sizeof(ivdext_fill_mem_rec_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_fill_mem_ip,
+                                                     &s_fill_mem_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("Error in filling mem records: 0x%x",
+              s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    CHECK_EQ(mNumMemRecords, s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_num_mem_rec_filled);
+    for (size_t i = 0; i < mNumMemRecords; i++, ps_mem_rec++) {
+        ps_mem_rec->pv_base = ivd_aligned_malloc(
+                ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+        if (!ps_mem_rec->pv_base) {
+            ALOGE("Allocation failure for memory record #%zu of size %u",
+                  i, ps_mem_rec->u4_mem_size);
+            return NO_MEMORY;
+        }
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::createDecoder() {
+    ivdext_init_ip_t s_init_ip;
+    ivdext_init_op_t s_init_op;
+
+    s_init_ip.s_ivd_init_ip_t.u4_size = sizeof(ivdext_init_ip_t);
+    s_init_ip.s_ivd_init_ip_t.e_cmd = (IVD_API_COMMAND_TYPE_T)IV_CMD_INIT;
+    s_init_ip.s_ivd_init_ip_t.pv_mem_rec_location = mMemRecords;
+    s_init_ip.s_ivd_init_ip_t.u4_frm_max_wd = mWidth;
+    s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = mHeight;
+    s_init_ip.u4_share_disp_buf = 0;
+    s_init_ip.u4_deinterlace = 1;
+    s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords;
+    s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorformat;
+    s_init_op.s_ivd_init_op_t.u4_size = sizeof(ivdext_init_op_t);
+
+    mDecHandle = (iv_obj_t *)mMemRecords[0].pv_base;
+    mDecHandle->pv_fxns = (void *)ivdec_api_function;
+    mDecHandle->u4_size = sizeof(iv_obj_t);
+
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_init_ip,
+                                                     &s_init_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__,
+              s_init_op.s_ivd_init_op_t.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::setNumCores() {
+    ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip;
+    ivdext_ctl_set_num_cores_op_t s_set_num_cores_op;
+
+    s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+    s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+    s_set_num_cores_ip.u4_num_cores = mNumCores;
+    s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_num_cores_ip,
+                                                     &s_set_num_cores_op);
+    if (status != IV_SUCCESS) {
+        ALOGD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::setParams(size_t stride) {
+    ivd_ctl_set_config_ip_t s_set_dyn_params_ip;
+    ivd_ctl_set_config_op_t s_set_dyn_params_op;
+
+    s_set_dyn_params_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
+    s_set_dyn_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_dyn_params_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+    s_set_dyn_params_ip.u4_disp_wd = (UWORD32) stride;
+    s_set_dyn_params_ip.e_frm_skip_mode = IVD_SKIP_NONE;
+    s_set_dyn_params_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+    s_set_dyn_params_ip.e_vid_dec_mode = IVD_DECODE_FRAME;
+    s_set_dyn_params_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_dyn_params_ip,
+                                                     &s_set_dyn_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__, s_set_dyn_params_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::getVersion() {
+    ivd_ctl_getversioninfo_ip_t s_get_versioninfo_ip;
+    ivd_ctl_getversioninfo_op_t s_get_versioninfo_op;
+    UWORD8 au1_buf[512];
+
+    s_get_versioninfo_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
+    s_get_versioninfo_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_get_versioninfo_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
+    s_get_versioninfo_ip.pv_version_buffer = au1_buf;
+    s_get_versioninfo_ip.u4_version_buffer_size = sizeof(au1_buf);
+    s_get_versioninfo_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_get_versioninfo_ip,
+                                                     &s_get_versioninfo_op);
+    if (status != IV_SUCCESS) {
+        ALOGD("error in %s: 0x%x", __func__,
+              s_get_versioninfo_op.u4_error_code);
+    } else {
+        ALOGV("ittiam decoder version number: %s",
+              (char *) s_get_versioninfo_ip.pv_version_buffer);
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::initDecoder() {
+    status_t ret = getNumMemRecords();
+    if (OK != ret) return ret;
+
+    ret = fillMemRecords();
+    if (OK != ret) return ret;
+
+    if (OK != createDecoder()) return UNKNOWN_ERROR;
+
+    mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
+    mStride = ALIGN64(mWidth);
+    mSignalledError = false;
+    resetPlugin();
+    (void) setNumCores();
+    if (OK != setParams(mStride)) return UNKNOWN_ERROR;
+    (void) getVersion();
+
+    return OK;
+}
+
+bool C2SoftMpeg2Dec::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                                   ivd_video_decode_op_t *ps_decode_op,
+                                   C2ReadView *inBuffer,
+                                   C2GraphicView *outBuffer,
+                                   size_t inOffset,
+                                   size_t inSize,
+                                   uint32_t tsMarker) {
+    uint32_t displayStride = mStride;
+    uint32_t displayHeight = mHeight;
+    size_t lumaSize = displayStride * displayHeight;
+    size_t chromaSize = lumaSize >> 2;
+
+    ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+    ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+    if (inBuffer) {
+        ps_decode_ip->u4_ts = tsMarker;
+        ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer->data() + inOffset);
+        ps_decode_ip->u4_num_Bytes = inSize;
+    } else {
+        ps_decode_ip->u4_ts = 0;
+        ps_decode_ip->pv_stream_buffer = nullptr;
+        ps_decode_ip->u4_num_Bytes = 0;
+    }
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
+    if (outBuffer) {
+        if (outBuffer->width() < displayStride || outBuffer->height() < displayHeight) {
+            ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
+                  outBuffer->width(), outBuffer->height(), displayStride, displayHeight);
+            return false;
+        }
+        ps_decode_ip->s_out_buffer.pu1_bufs[0] = outBuffer->data()[C2PlanarLayout::PLANE_Y];
+        ps_decode_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[C2PlanarLayout::PLANE_U];
+        ps_decode_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[C2PlanarLayout::PLANE_V];
+    } else {
+        ps_decode_ip->s_out_buffer.pu1_bufs[0] = mOutBufferDrain;
+        ps_decode_ip->s_out_buffer.pu1_bufs[1] = mOutBufferDrain + lumaSize;
+        ps_decode_ip->s_out_buffer.pu1_bufs[2] = mOutBufferDrain + lumaSize + chromaSize;
+    }
+    ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
+    ps_decode_op->u4_size = sizeof(ivd_video_decode_op_t);
+
+    return true;
+}
+
+
+bool C2SoftMpeg2Dec::getSeqInfo() {
+    ivdext_ctl_get_seq_info_ip_t s_ctl_get_seq_info_ip;
+    ivdext_ctl_get_seq_info_op_t s_ctl_get_seq_info_op;
+
+    s_ctl_get_seq_info_ip.u4_size = sizeof(ivdext_ctl_get_seq_info_ip_t);
+    s_ctl_get_seq_info_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_ctl_get_seq_info_ip.e_sub_cmd =
+        (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_GET_SEQ_INFO;
+    s_ctl_get_seq_info_op.u4_size = sizeof(ivdext_ctl_get_seq_info_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_ctl_get_seq_info_ip,
+                                                     &s_ctl_get_seq_info_op);
+    if (status != IV_SUCCESS) {
+        ALOGW("Error in getting Sequence info: 0x%x", s_ctl_get_seq_info_op.u4_error_code);
+        return false;
+    }
+
+    VuiColorAspects vuiColorAspects;
+    vuiColorAspects.primaries = s_ctl_get_seq_info_op.u1_colour_primaries;
+    vuiColorAspects.transfer = s_ctl_get_seq_info_op.u1_transfer_characteristics;
+    vuiColorAspects.coeffs = s_ctl_get_seq_info_op.u1_matrix_coefficients;
+    vuiColorAspects.fullRange =  false;  // mpeg2 video has limited range.
+
+    // convert vui aspects to C2 values if changed
+    if (!(vuiColorAspects == mBitstreamColorAspects)) {
+        mBitstreamColorAspects = vuiColorAspects;
+        ColorAspects sfAspects;
+        C2StreamColorAspectsInfo::input codedAspects = { 0u };
+        ColorUtils::convertIsoColorAspectsToCodecAspects(
+                vuiColorAspects.primaries, vuiColorAspects.transfer, vuiColorAspects.coeffs,
+                vuiColorAspects.fullRange, sfAspects);
+        if (!C2Mapper::map(sfAspects.mPrimaries, &codedAspects.primaries)) {
+            codedAspects.primaries = C2Color::PRIMARIES_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mRange, &codedAspects.range)) {
+            codedAspects.range = C2Color::RANGE_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mMatrixCoeffs, &codedAspects.matrix)) {
+            codedAspects.matrix = C2Color::MATRIX_UNSPECIFIED;
+        }
+        if (!C2Mapper::map(sfAspects.mTransfer, &codedAspects.transfer)) {
+            codedAspects.transfer = C2Color::TRANSFER_UNSPECIFIED;
+        }
+        std::vector<std::unique_ptr<C2SettingResult>> failures;
+        (void)mIntf->config({&codedAspects}, C2_MAY_BLOCK, &failures);
+    }
+    return true;
+}
+
+status_t C2SoftMpeg2Dec::setFlushMode() {
+    ivd_ctl_flush_ip_t s_set_flush_ip;
+    ivd_ctl_flush_op_t s_set_flush_op;
+
+    s_set_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
+    s_set_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
+    s_set_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_flush_ip,
+                                                     &s_set_flush_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__, s_set_flush_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::resetDecoder() {
+    ivd_ctl_reset_ip_t s_reset_ip;
+    ivd_ctl_reset_op_t s_reset_op;
+
+    s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+    s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+    s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_reset_ip,
+                                                     &s_reset_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+    (void) setNumCores();
+    mStride = 0;
+    mSignalledError = false;
+
+    return OK;
+}
+
+void C2SoftMpeg2Dec::resetPlugin() {
+    mSignalledOutputEos = false;
+    gettimeofday(&mTimeStart, nullptr);
+    gettimeofday(&mTimeEnd, nullptr);
+}
+
+status_t C2SoftMpeg2Dec::deleteDecoder() {
+    if (mMemRecords) {
+        iv_mem_rec_t *ps_mem_rec = mMemRecords;
+
+        for (size_t i = 0; i < mNumMemRecords; i++, ps_mem_rec++) {
+            if (ps_mem_rec->pv_base) {
+                ivd_aligned_free(ps_mem_rec->pv_base);
+            }
+        }
+        ivd_aligned_free(mMemRecords);
+        mMemRecords = nullptr;
+    }
+    mDecHandle = nullptr;
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::reInitDecoder() {
+    deleteDecoder();
+
+    status_t ret = initDecoder();
+    if (OK != ret) {
+        ALOGE("Failed to initialize decoder");
+        deleteDecoder();
+        return ret;
+    }
+    return OK;
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftMpeg2Dec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work) {
+    std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mOutBlock),
+                                                           C2Rect(mWidth, mHeight));
+    mOutBlock = nullptr;
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        buffer->setInfo(mIntf->getColorAspects_l());
+    }
+
+    class FillWork {
+       public:
+        FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+                 const std::shared_ptr<C2Buffer>& buffer)
+            : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {}
+        ~FillWork() = default;
+
+        void operator()(const std::unique_ptr<C2Work>& work) {
+            work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+            work->worklets.front()->output.buffers.clear();
+            work->worklets.front()->output.ordinal = mOrdinal;
+            work->workletsProcessed = 1u;
+            work->result = C2_OK;
+            if (mBuffer) {
+                work->worklets.front()->output.buffers.push_back(mBuffer);
+            }
+            ALOGV("timestamp = %lld, index = %lld, w/%s buffer",
+                  mOrdinal.timestamp.peekll(), mOrdinal.frameIndex.peekll(),
+                  mBuffer ? "" : "o");
+        }
+
+       private:
+        const uint32_t mFlags;
+        const C2WorkOrdinalStruct mOrdinal;
+        const std::shared_ptr<C2Buffer> mBuffer;
+    };
+
+    auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+        // TODO: Check if cloneAndSend can be avoided by tracking number of frames remaining
+        if (eos) {
+            if (buffer) {
+                mOutIndex = index;
+                C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+                cloneAndSend(
+                    mOutIndex, work,
+                    FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+                buffer.reset();
+            }
+        } else {
+            fillWork(work);
+        }
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+c2_status_t C2SoftMpeg2Dec::ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool) {
+    if (!mDecHandle) {
+        ALOGE("not supposed to be here, invalid decoder context");
+        return C2_CORRUPTED;
+    }
+    if (mStride != ALIGN64(mWidth)) {
+        mStride = ALIGN64(mWidth);
+        if (OK != setParams(mStride)) return C2_CORRUPTED;
+    }
+    if (mOutBlock &&
+            (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+        mOutBlock.reset();
+    }
+    if (!mOutBlock) {
+        uint32_t format = HAL_PIXEL_FORMAT_YV12;
+        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+        if (err != C2_OK) {
+            ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+            return err;
+        }
+        ALOGV("provided (%dx%d) required (%dx%d)",
+              mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+    }
+
+    return C2_OK;
+}
+
+// TODO: can overall error checking be improved?
+// TODO: allow configuration of color format and usage for graphic buffers instead
+//       of hard coding them to HAL_PIXEL_FORMAT_YV12
+// TODO: pass coloraspects information to surface
+// TODO: test support for dynamic change in resolution
+// TODO: verify if the decoder sent back all frames
+void C2SoftMpeg2Dec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    bool hasPicture = false;
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+    size_t inPos = 0;
+    while (inPos < inSize) {
+        if (C2_OK != ensureDecoderState(pool)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        C2GraphicView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+        if (!setDecodeArgs(&s_decode_ip, &s_decode_op, &rView, &wView,
+                           inOffset + inPos, inSize - inPos, workIndex)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        // If input dump is enabled, then write to file
+        DUMP_TO_FILE(mInFile, s_decode_ip.pv_stream_buffer, s_decode_ip.u4_num_Bytes);
+        WORD32 delay;
+        GETTIME(&mTimeStart, nullptr);
+        TIME_DIFF(mTimeEnd, mTimeStart, delay);
+        (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        WORD32 decodeTime;
+        GETTIME(&mTimeEnd, nullptr);
+        TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
+        ALOGV("decodeTime=%6d delay=%6d numBytes=%6d ", decodeTime, delay,
+              s_decode_op.u4_num_bytes_consumed);
+        if (IMPEG2D_UNSUPPORTED_DIMENSIONS == s_decode_op.u4_error_code) {
+            ALOGV("unsupported resolution : %dx%d", s_decode_op.u4_pic_wd, s_decode_op.u4_pic_ht);
+            drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+            resetPlugin();
+            work->workletsProcessed = 0u;
+            mWidth = s_decode_op.u4_pic_wd;
+            mHeight = s_decode_op.u4_pic_ht;
+
+            ALOGI("Configuring decoder: mWidth %d , mHeight %d ",
+                   mWidth, mHeight);
+            C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+            std::vector<std::unique_ptr<C2SettingResult>> failures;
+            c2_status_t err =
+                mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+            if (err == OK) {
+                work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(size));
+            } else {
+                ALOGE("Cannot set width and height");
+                mSignalledError = true;
+                work->workletsProcessed = 1u;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+
+            if (OK != reInitDecoder()) {
+                ALOGE("Failed to reinitialize decoder");
+                mSignalledError = true;
+                work->workletsProcessed = 1u;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            continue;
+        } else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & 0xFF)) {
+            ALOGV("resolution changed");
+            drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+            resetDecoder();
+            resetPlugin();
+            work->workletsProcessed = 0u;
+            continue;
+        }
+        if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
+            if (s_decode_op.u4_pic_wd != mWidth ||  s_decode_op.u4_pic_ht != mHeight) {
+                mWidth = s_decode_op.u4_pic_wd;
+                mHeight = s_decode_op.u4_pic_ht;
+                CHECK_EQ(0u, s_decode_op.u4_output_present);
+
+                ALOGI("Configuring decoder out: mWidth %d , mHeight %d ",
+                       mWidth, mHeight);
+                C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+                std::vector<std::unique_ptr<C2SettingResult>> failures;
+                c2_status_t err =
+                    mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+                if (err == OK) {
+                    work->worklets.front()->output.configUpdate.push_back(
+                        C2Param::Copy(size));
+                } else {
+                    ALOGE("Cannot set width and height");
+                    mSignalledError = true;
+                    work->workletsProcessed = 1u;
+                    work->result = C2_CORRUPTED;
+                    return;
+                }
+            }
+        }
+
+        (void) getSeqInfo();
+        hasPicture |= (1 == s_decode_op.u4_frame_decoded_flag);
+        if (s_decode_op.u4_output_present) {
+            finishWork(s_decode_op.u4_ts, work);
+        }
+
+        inPos += s_decode_op.u4_num_bytes_consumed;
+        if (hasPicture && (inSize - inPos) != 0) {
+            ALOGD("decoded frame in current access nal, ignoring further trailing bytes %d",
+                  (int)inSize - (int)inPos);
+            break;
+        }
+    }
+
+    if (eos) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+        mSignalledOutputEos = true;
+    } else if (!hasPicture) {
+        fillEmptyWork(work);
+    }
+}
+
+c2_status_t C2SoftMpeg2Dec::drainInternal(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work) {
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    if (OK != setFlushMode()) return C2_CORRUPTED;
+    while (true) {
+        if (C2_OK != ensureDecoderState(pool)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return C2_CORRUPTED;
+        }
+        C2GraphicView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            return C2_CORRUPTED;
+        }
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+        if (!setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, &wView, 0, 0, 0)) {
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            return C2_CORRUPTED;
+        }
+        (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        if (s_decode_op.u4_output_present) {
+            finishWork(s_decode_op.u4_ts, work);
+        } else {
+            fillEmptyWork(work);
+            break;
+        }
+    }
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftMpeg2Dec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftMpeg2DecFactory : public C2ComponentFactory {
+public:
+    C2SoftMpeg2DecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftMpeg2Dec(COMPONENT_NAME,
+                                   id,
+                                   std::make_shared<C2SoftMpeg2Dec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftMpeg2Dec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftMpeg2Dec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftMpeg2DecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftMpeg2DecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
new file mode 100644
index 0000000..65d3b87
--- /dev/null
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
@@ -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.
+ */
+
+#ifndef ANDROID_C2_SOFT_MPEG2_DEC_H_
+#define ANDROID_C2_SOFT_MPEG2_DEC_H_
+
+#include <atomic>
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ColorUtils.h>
+
+#include "iv_datatypedef.h"
+#include "iv.h"
+#include "ivd.h"
+
+namespace android {
+
+#define ivdec_api_function              impeg2d_api_function
+#define ivdext_init_ip_t                impeg2d_init_ip_t
+#define ivdext_init_op_t                impeg2d_init_op_t
+#define ivdext_fill_mem_rec_ip_t        impeg2d_fill_mem_rec_ip_t
+#define ivdext_fill_mem_rec_op_t        impeg2d_fill_mem_rec_op_t
+#define ivdext_ctl_set_num_cores_ip_t   impeg2d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t   impeg2d_ctl_set_num_cores_op_t
+#define ivdext_ctl_get_seq_info_ip_t    impeg2d_ctl_get_seq_info_ip_t
+#define ivdext_ctl_get_seq_info_op_t    impeg2d_ctl_get_seq_info_op_t
+#define ALIGN64(x)                      ((((x) + 63) >> 6) << 6)
+#define MAX_NUM_CORES                   4
+#define IVDEXT_CMD_CTL_SET_NUM_CORES    \
+        (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES
+#define MIN(a, b)                       (((a) < (b)) ? (a) : (b))
+#define GETTIME(a, b)                   gettimeofday(a, b);
+#define TIME_DIFF(start, end, diff)     \
+    diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+            ((end).tv_usec - (start).tv_usec);
+
+#ifdef FILE_DUMP_ENABLE
+    #define INPUT_DUMP_PATH     "/sdcard/clips/mpeg2d_input"
+    #define INPUT_DUMP_EXT      "m2v"
+    #define GENERATE_FILE_NAMES() {                         \
+        GETTIME(&mTimeStart, NULL);                         \
+        strcpy(mInFile, "");                                \
+        sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH,  \
+                mTimeStart.tv_sec, mTimeStart.tv_usec,      \
+                INPUT_DUMP_EXT);                            \
+    }
+    #define CREATE_DUMP_FILE(m_filename) {                  \
+        FILE *fp = fopen(m_filename, "wb");                 \
+        if (fp != NULL) {                                   \
+            fclose(fp);                                     \
+        } else {                                            \
+            ALOGD("Could not open file %s", m_filename);    \
+        }                                                   \
+    }
+    #define DUMP_TO_FILE(m_filename, m_buf, m_size)         \
+    {                                                       \
+        FILE *fp = fopen(m_filename, "ab");                 \
+        if (fp != NULL && m_buf != NULL) {                  \
+            uint32_t i;                                     \
+            i = fwrite(m_buf, 1, m_size, fp);               \
+            ALOGD("fwrite ret %d to write %d", i, m_size);  \
+            if (i != (uint32_t)m_size) {                    \
+                ALOGD("Error in fwrite, returned %d", i);   \
+                perror("Error in write to file");           \
+            }                                               \
+            fclose(fp);                                     \
+        } else {                                            \
+            ALOGD("Could not write to file %s", m_filename);\
+        }                                                   \
+    }
+#else /* FILE_DUMP_ENABLE */
+    #define INPUT_DUMP_PATH
+    #define INPUT_DUMP_EXT
+    #define OUTPUT_DUMP_PATH
+    #define OUTPUT_DUMP_EXT
+    #define GENERATE_FILE_NAMES()
+    #define CREATE_DUMP_FILE(m_filename)
+    #define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+struct C2SoftMpeg2Dec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftMpeg2Dec(const char* name, c2_node_id_t id,
+                   const std::shared_ptr<IntfImpl>& intfImpl);
+    virtual ~C2SoftMpeg2Dec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+ private:
+    status_t getNumMemRecords();
+    status_t fillMemRecords();
+    status_t createDecoder();
+    status_t setNumCores();
+    status_t setParams(size_t stride);
+    status_t getVersion();
+    status_t initDecoder();
+    bool setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                       ivd_video_decode_op_t *ps_decode_op,
+                       C2ReadView *inBuffer,
+                       C2GraphicView *outBuffer,
+                       size_t inOffset,
+                       size_t inSize,
+                       uint32_t tsMarker);
+    bool getSeqInfo();
+    c2_status_t ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool);
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work);
+    status_t setFlushMode();
+    c2_status_t drainInternal(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool,
+            const std::unique_ptr<C2Work> &work);
+    status_t resetDecoder();
+    void resetPlugin();
+    status_t deleteDecoder();
+    status_t reInitDecoder();
+
+    // TODO:This is not the right place for this enum. These should
+    // be part of c2-vndk so that they can be accessed by all video plugins
+    // until then, make them feel at home
+    enum {
+        kNotSupported,
+        kPreferBitstream,
+        kPreferContainer,
+    };
+
+    std::shared_ptr<IntfImpl> mIntf;
+    iv_obj_t *mDecHandle;
+    iv_mem_rec_t *mMemRecords;
+    size_t mNumMemRecords;
+    std::shared_ptr<C2GraphicBlock> mOutBlock;
+    uint8_t *mOutBufferDrain;
+
+    size_t mNumCores;
+    IV_COLOR_FORMAT_T mIvColorformat;
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mStride;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+    std::atomic_uint64_t mOutIndex;
+
+    // Color aspects. These are ISO values and are meant to detect changes in aspects to avoid
+    // converting them to C2 values for each frame
+    struct VuiColorAspects {
+        uint8_t primaries;
+        uint8_t transfer;
+        uint8_t coeffs;
+        uint8_t fullRange;
+
+        // default color aspects
+        VuiColorAspects()
+            : primaries(2), transfer(2), coeffs(2), fullRange(0) { }
+
+        bool operator==(const VuiColorAspects &o) {
+            return primaries == o.primaries && transfer == o.transfer && coeffs == o.coeffs
+                    && fullRange == o.fullRange;
+        }
+    } mBitstreamColorAspects;
+
+    // profile
+    struct timeval mTimeStart;
+    struct timeval mTimeEnd;
+#ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+    C2_DO_NOT_COPY(C2SoftMpeg2Dec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_MPEG2_DEC_H_
diff --git a/media/codec2/components/mpeg4_h263/Android.bp b/media/codec2/components/mpeg4_h263/Android.bp
new file mode 100644
index 0000000..41e4f44
--- /dev/null
+++ b/media/codec2/components/mpeg4_h263/Android.bp
@@ -0,0 +1,66 @@
+cc_library_shared {
+    name: "libcodec2_soft_mpeg4dec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_signed-defaults",
+    ],
+
+    srcs: ["C2SoftMpeg4Dec.cpp"],
+
+    static_libs: ["libstagefright_m4vh263dec"],
+
+    cflags: [
+        "-DOSCL_IMPORT_REF=",
+        "-DMPEG4",
+    ],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_h263dec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_signed-defaults",
+    ],
+
+    srcs: ["C2SoftMpeg4Dec.cpp"],
+
+    static_libs: ["libstagefright_m4vh263dec"],
+
+    cflags: [
+        "-DOSCL_IMPORT_REF=",
+    ],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_mpeg4enc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_signed-defaults",
+    ],
+
+
+    srcs: ["C2SoftMpeg4Enc.cpp"],
+
+    static_libs: ["libstagefright_m4vh263enc"],
+
+    cflags: [
+        "-DMPEG4",
+        "-DOSCL_IMPORT_REF=",
+    ],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_h263enc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_signed-defaults",
+    ],
+
+    srcs: ["C2SoftMpeg4Enc.cpp"],
+
+    static_libs: [ "libstagefright_m4vh263enc" ],
+
+    cflags: [
+        "-DOSCL_IMPORT_REF=",
+    ],
+}
diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
new file mode 100644
index 0000000..7e6685e
--- /dev/null
+++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
@@ -0,0 +1,758 @@
+/*
+ * 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
+#ifdef MPEG4
+  #define LOG_TAG "C2SoftMpeg4Dec"
+#else
+  #define LOG_TAG "C2SoftH263Dec"
+#endif
+#include <log/log.h>
+
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftMpeg4Dec.h"
+#include "mp4dec_api.h"
+
+namespace android {
+
+#ifdef MPEG4
+constexpr char COMPONENT_NAME[] = "c2.android.mpeg4.decoder";
+#else
+constexpr char COMPONENT_NAME[] = "c2.android.h263.decoder";
+#endif
+
+class C2SoftMpeg4Dec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_VIDEO,
+#ifdef MPEG4
+                MEDIA_MIMETYPE_VIDEO_MPEG4
+#else
+                MEDIA_MIMETYPE_VIDEO_H263
+#endif
+                ) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+
+        // TODO: Proper support for reorder depth.
+        addParameter(
+                DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
+                .withConstValue(new C2PortActualDelayTuning::output(1u))
+                .build());
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                .withDefault(new C2StreamPictureSizeInfo::output(0u, 176, 144))
+                .withFields({
+#ifdef MPEG4
+                    C2F(mSize, width).inRange(2, 1920, 2),
+                    C2F(mSize, height).inRange(2, 1088, 2),
+#else
+                    C2F(mSize, width).inRange(2, 352, 2),
+                    C2F(mSize, height).inRange(2, 288, 2),
+#endif
+                })
+                .withSetter(SizeSetter)
+                .build());
+
+#ifdef MPEG4
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::input(0u,
+                        C2Config::PROFILE_MP4V_SIMPLE, C2Config::LEVEL_MP4V_3))
+                .withFields({
+                    C2F(mProfileLevel, profile).equalTo(
+                            C2Config::PROFILE_MP4V_SIMPLE),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_MP4V_0,
+                            C2Config::LEVEL_MP4V_0B,
+                            C2Config::LEVEL_MP4V_1,
+                            C2Config::LEVEL_MP4V_2,
+                            C2Config::LEVEL_MP4V_3,
+                            C2Config::LEVEL_MP4V_3B,
+                            C2Config::LEVEL_MP4V_4,
+                            C2Config::LEVEL_MP4V_4A,
+                            C2Config::LEVEL_MP4V_5,
+                            C2Config::LEVEL_MP4V_6})
+                })
+                .withSetter(ProfileLevelSetter, mSize)
+                .build());
+#else
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::input(0u,
+                        C2Config::PROFILE_H263_BASELINE, C2Config::LEVEL_H263_30))
+                .withFields({
+                    C2F(mProfileLevel, profile).oneOf({
+                            C2Config::PROFILE_H263_BASELINE,
+                            C2Config::PROFILE_H263_ISWV2}),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_H263_10,
+                            C2Config::LEVEL_H263_20,
+                            C2Config::LEVEL_H263_30,
+                            C2Config::LEVEL_H263_40,
+                            C2Config::LEVEL_H263_45})
+                })
+                .withSetter(ProfileLevelSetter, mSize)
+                .build());
+#endif
+
+        addParameter(
+                DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
+#ifdef MPEG4
+                .withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 1920, 1088))
+#else
+                .withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 352, 288))
+#endif
+                .withFields({
+#ifdef MPEG4
+                    C2F(mSize, width).inRange(2, 1920, 2),
+                    C2F(mSize, height).inRange(2, 1088, 2),
+#else
+                    C2F(mSize, width).inRange(2, 352, 2),
+                    C2F(mSize, height).inRange(2, 288, 2),
+#endif
+                })
+                .withSetter(MaxPictureSizeSetter, mSize)
+                .build());
+
+        addParameter(
+                DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+#ifdef MPEG4
+                .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 1920 * 1088 * 3 / 2))
+#else
+                .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 352 * 288 * 3 / 2))
+#endif
+                .withFields({
+                    C2F(mMaxInputSize, value).any(),
+                })
+                .calculatedAs(MaxInputSizeSetter, mMaxSize)
+                .build());
+
+        C2ChromaOffsetStruct locations[1] = { C2ChromaOffsetStruct::ITU_YUV_420_0() };
+        std::shared_ptr<C2StreamColorInfo::output> defaultColorInfo =
+            C2StreamColorInfo::output::AllocShared(
+                    1u, 0u, 8u /* bitDepth */, C2Color::YUV_420);
+        memcpy(defaultColorInfo->m.locations, locations, sizeof(locations));
+
+        defaultColorInfo =
+            C2StreamColorInfo::output::AllocShared(
+                    { C2ChromaOffsetStruct::ITU_YUV_420_0() },
+                    0u, 8u /* bitDepth */, C2Color::YUV_420);
+        helper->addStructDescriptors<C2ChromaOffsetStruct>();
+
+        addParameter(
+                DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
+                .withConstValue(defaultColorInfo)
+                .build());
+
+        // TODO: support more formats?
+        addParameter(
+                DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+                .withConstValue(new C2StreamPixelFormatInfo::output(
+                                     0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+                .build());
+    }
+
+    static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output> &oldMe,
+                          C2P<C2StreamPictureSizeInfo::output> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R MaxPictureSizeSetter(bool mayBlock, C2P<C2StreamMaxPictureSizeTuning::output> &me,
+                                    const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        // TODO: get max width/height from the size's field helpers vs. hardcoding
+#ifdef MPEG4
+        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 1920u);
+        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 1088u);
+#else
+        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 352u);
+        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 288u);
+#endif
+        return C2R::Ok();
+    }
+
+    static C2R MaxInputSizeSetter(bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input> &me,
+                                  const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
+        (void)mayBlock;
+        // assume compression ratio of 1
+        me.set().value = (((maxSize.v.width + 15) / 16) * ((maxSize.v.height + 15) / 16) * 384);
+        return C2R::Ok();
+    }
+
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input> &me,
+                                  const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        (void)size;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+    uint32_t getMaxWidth() const { return mMaxSize->width; }
+    uint32_t getMaxHeight() const { return mMaxSize->height; }
+
+private:
+    std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+    std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
+    std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mMaxInputSize;
+    std::shared_ptr<C2StreamColorInfo::output> mColorInfo;
+    std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
+};
+
+C2SoftMpeg4Dec::C2SoftMpeg4Dec(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mDecHandle(nullptr),
+      mOutputBuffer{},
+      mInitialized(false) {
+}
+
+C2SoftMpeg4Dec::~C2SoftMpeg4Dec() {
+    onRelease();
+}
+
+c2_status_t C2SoftMpeg4Dec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftMpeg4Dec::onStop() {
+    if (mInitialized) {
+        if (mDecHandle) {
+            PVCleanUpVideoDecoder(mDecHandle);
+        }
+        mInitialized = false;
+    }
+    for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+        if (mOutputBuffer[i]) {
+            free(mOutputBuffer[i]);
+            mOutputBuffer[i] = nullptr;
+        }
+    }
+    mNumSamplesOutput = 0;
+    mFramesConfigured = false;
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+
+    return C2_OK;
+}
+
+void C2SoftMpeg4Dec::onReset() {
+    (void)onStop();
+    (void)onInit();
+}
+
+void C2SoftMpeg4Dec::onRelease() {
+    if (mInitialized) {
+        if (mDecHandle) {
+            PVCleanUpVideoDecoder(mDecHandle);
+            delete mDecHandle;
+            mDecHandle = nullptr;
+        }
+        mInitialized = false;
+    }
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
+    for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+        if (mOutputBuffer[i]) {
+            free(mOutputBuffer[i]);
+            mOutputBuffer[i] = nullptr;
+        }
+    }
+}
+
+c2_status_t C2SoftMpeg4Dec::onFlush_sm() {
+    if (mInitialized) {
+        if (PV_TRUE != PVResetVideoDecoder(mDecHandle)) {
+            return C2_CORRUPTED;
+        }
+    }
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+    return C2_OK;
+}
+
+status_t C2SoftMpeg4Dec::initDecoder() {
+#ifdef MPEG4
+    mIsMpeg4 = true;
+#else
+    mIsMpeg4 = false;
+#endif
+    if (!mDecHandle) {
+        mDecHandle = new tagvideoDecControls;
+    }
+    if (!mDecHandle) {
+        ALOGE("mDecHandle is null");
+        return NO_MEMORY;
+    }
+    memset(mDecHandle, 0, sizeof(tagvideoDecControls));
+
+    /* TODO: bring these values to 352 and 288. It cannot be done as of now
+     * because, h263 doesn't seem to allow port reconfiguration. In OMX, the
+     * problem of larger width and height than default width and height is
+     * overcome by adaptivePlayBack() api call. This call gets width and height
+     * information from extractor. Such a thing is not possible here.
+     * So we are configuring to larger values.*/
+    mWidth = 1408;
+    mHeight = 1152;
+    mNumSamplesOutput = 0;
+    mInitialized = false;
+    mFramesConfigured = false;
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+
+    return OK;
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftMpeg4Dec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work) {
+    std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mOutBlock),
+                                                           C2Rect(mWidth, mHeight));
+    mOutBlock = nullptr;
+    auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
+        uint32_t flags = 0;
+        if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
+                (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
+            flags |= C2FrameData::FLAG_END_OF_STREAM;
+            ALOGV("signalling eos");
+        }
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        fillWork(work);
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+c2_status_t C2SoftMpeg4Dec::ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool) {
+    if (!mDecHandle) {
+        ALOGE("not supposed to be here, invalid decoder context");
+        return C2_CORRUPTED;
+    }
+
+    mOutputBufferSize = align(mIntf->getMaxWidth(), 16) * align(mIntf->getMaxHeight(), 16) * 3 / 2;
+    for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+        if (!mOutputBuffer[i]) {
+            mOutputBuffer[i] = (uint8_t *)malloc(mOutputBufferSize);
+            if (!mOutputBuffer[i]) {
+                return C2_NO_MEMORY;
+            }
+        }
+    }
+    if (mOutBlock &&
+            (mOutBlock->width() != align(mWidth, 16) || mOutBlock->height() != mHeight)) {
+        mOutBlock.reset();
+    }
+    if (!mOutBlock) {
+        uint32_t format = HAL_PIXEL_FORMAT_YV12;
+        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight, format, usage, &mOutBlock);
+        if (err != C2_OK) {
+            ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+            return err;
+        }
+        ALOGV("provided (%dx%d) required (%dx%d)",
+              mOutBlock->width(), mOutBlock->height(), mWidth, mHeight);
+    }
+    return C2_OK;
+}
+
+bool C2SoftMpeg4Dec::handleResChange(const std::unique_ptr<C2Work> &work) {
+    uint32_t disp_width, disp_height;
+    PVGetVideoDimensions(mDecHandle, (int32 *)&disp_width, (int32 *)&disp_height);
+
+    uint32_t buf_width, buf_height;
+    PVGetBufferDimensions(mDecHandle, (int32 *)&buf_width, (int32 *)&buf_height);
+
+    CHECK_LE(disp_width, buf_width);
+    CHECK_LE(disp_height, buf_height);
+
+    ALOGV("display size (%dx%d), buffer size (%dx%d)",
+           disp_width, disp_height, buf_width, buf_height);
+
+    bool resChanged = false;
+    if (disp_width != mWidth || disp_height != mHeight) {
+        mWidth = disp_width;
+        mHeight = disp_height;
+        resChanged = true;
+        for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+            if (mOutputBuffer[i]) {
+                free(mOutputBuffer[i]);
+                mOutputBuffer[i] = nullptr;
+            }
+        }
+
+        if (!mIsMpeg4) {
+            PVCleanUpVideoDecoder(mDecHandle);
+
+            uint8_t *vol_data[1]{};
+            int32_t vol_size = 0;
+
+            if (!PVInitVideoDecoder(
+                    mDecHandle, vol_data, &vol_size, 1, mIntf->getMaxWidth(), mIntf->getMaxHeight(), H263_MODE)) {
+                ALOGE("Error in PVInitVideoDecoder H263_MODE while resChanged was set to true");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return true;
+            }
+        }
+        mFramesConfigured = false;
+    }
+    return resChanged;
+}
+
+/* TODO: can remove temporary copy after library supports writing to display
+ * buffer Y, U and V plane pointers using stride info. */
+static void copyOutputBufferToYuvPlanarFrame(
+        uint8_t *dst, uint8_t *src,
+        size_t dstYStride, size_t dstUVStride,
+        size_t srcYStride, uint32_t width,
+        uint32_t height) {
+    size_t srcUVStride = srcYStride / 2;
+    uint8_t *srcStart = src;
+    uint8_t *dstStart = dst;
+    size_t vStride = align(height, 16);
+    for (size_t i = 0; i < height; ++i) {
+         memcpy(dst, src, width);
+         src += srcYStride;
+         dst += dstYStride;
+    }
+    /* U buffer */
+    src = srcStart + vStride * srcYStride;
+    dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
+    for (size_t i = 0; i < height / 2; ++i) {
+         memcpy(dst, src, width / 2);
+         src += srcUVStride;
+         dst += dstUVStride;
+    }
+    /* V buffer */
+    src = srcStart + vStride * srcYStride * 5 / 4;
+    dst = dstStart + (dstYStride * height);
+    for (size_t i = 0; i < height / 2; ++i) {
+         memcpy(dst, src, width / 2);
+         src += srcUVStride;
+         dst += dstUVStride;
+    }
+}
+
+void C2SoftMpeg4Dec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    if (inSize == 0) {
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+        }
+        return;
+    }
+
+    uint8_t *bitstream = const_cast<uint8_t *>(rView.data() + inOffset);
+    uint32_t *start_code = (uint32_t *)bitstream;
+    bool volHeader = *start_code == 0xB0010000;
+    if (volHeader) {
+        PVCleanUpVideoDecoder(mDecHandle);
+        mInitialized = false;
+    }
+
+    if (!mInitialized) {
+        uint8_t *vol_data[1]{};
+        int32_t vol_size = 0;
+
+        bool codecConfig = (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+        if (codecConfig || volHeader) {
+            vol_data[0] = bitstream;
+            vol_size = inSize;
+        }
+        MP4DecodingMode mode = (mIsMpeg4) ? MPEG4_MODE : H263_MODE;
+        if (!PVInitVideoDecoder(
+                mDecHandle, vol_data, &vol_size, 1,
+                mIntf->getMaxWidth(), mIntf->getMaxHeight(), mode)) {
+            ALOGE("PVInitVideoDecoder failed. Unsupported content?");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        mInitialized = true;
+        MP4DecodingMode actualMode = PVGetDecBitstreamMode(mDecHandle);
+        if (mode != actualMode) {
+            ALOGE("Decoded mode not same as actual mode of the decoder");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        PVSetPostProcType(mDecHandle, 0);
+        if (handleResChange(work)) {
+            ALOGI("Setting width and height");
+            C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+            std::vector<std::unique_ptr<C2SettingResult>> failures;
+            c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+            if (err == OK) {
+                work->worklets.front()->output.configUpdate.push_back(
+                    C2Param::Copy(size));
+            } else {
+                ALOGE("Config update size failed");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+        }
+        if (codecConfig) {
+            fillEmptyWork(work);
+            return;
+        }
+    }
+
+    size_t inPos = 0;
+    while (inPos < inSize) {
+        c2_status_t err = ensureDecoderState(pool);
+        if (C2_OK != err) {
+            mSignalledError = true;
+            work->result = err;
+            return;
+        }
+        C2GraphicView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        uint32_t yFrameSize = sizeof(uint8) * mDecHandle->size;
+        if (mOutputBufferSize < yFrameSize * 3 / 2){
+            ALOGE("Too small output buffer: %zu bytes", mOutputBufferSize);
+            mSignalledError = true;
+            work->result = C2_NO_MEMORY;
+            return;
+        }
+
+        if (!mFramesConfigured) {
+            PVSetReferenceYUV(mDecHandle,mOutputBuffer[1]);
+            mFramesConfigured = true;
+        }
+
+        // Need to check if header contains new info, e.g., width/height, etc.
+        VopHeaderInfo header_info;
+        uint32_t useExtTimestamp = (inPos == 0);
+        int32_t tmpInSize = (int32_t)inSize;
+        uint8_t *bitstreamTmp = bitstream;
+        uint32_t timestamp = workIndex;
+        if (PVDecodeVopHeader(
+                    mDecHandle, &bitstreamTmp, &timestamp, &tmpInSize,
+                    &header_info, &useExtTimestamp,
+                    mOutputBuffer[mNumSamplesOutput & 1]) != PV_TRUE) {
+            ALOGE("failed to decode vop header.");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        // H263 doesn't have VOL header, the frame size information is in short header, i.e. the
+        // decoder may detect size change after PVDecodeVopHeader.
+        bool resChange = handleResChange(work);
+        if (mIsMpeg4 && resChange) {
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        } else if (resChange) {
+            ALOGI("Setting width and height");
+            C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+            std::vector<std::unique_ptr<C2SettingResult>> failures;
+            c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+            if (err == OK) {
+                work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(size));
+            } else {
+                ALOGE("Config update size failed");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            continue;
+        }
+
+        if (PVDecodeVopBody(mDecHandle, &tmpInSize) != PV_TRUE) {
+            ALOGE("failed to decode video frame.");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        if (handleResChange(work)) {
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        uint8_t *outputBufferY = wView.data()[C2PlanarLayout::PLANE_Y];
+        C2PlanarLayout layout = wView.layout();
+        size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+        size_t dstUVStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+        (void)copyOutputBufferToYuvPlanarFrame(
+                outputBufferY,
+                mOutputBuffer[mNumSamplesOutput & 1],
+                dstYStride, dstUVStride,
+                align(mWidth, 16), mWidth, mHeight);
+
+        inPos += inSize - (size_t)tmpInSize;
+        finishWork(workIndex, work);
+        ++mNumSamplesOutput;
+        if (inSize - inPos != 0) {
+            ALOGD("decoded frame, ignoring further trailing bytes %d",
+                  (int)inSize - (int)inPos);
+            break;
+        }
+    }
+}
+
+c2_status_t C2SoftMpeg4Dec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void)pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+    return C2_OK;
+}
+
+class C2SoftMpeg4DecFactory : public C2ComponentFactory {
+public:
+    C2SoftMpeg4DecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+        GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftMpeg4Dec(COMPONENT_NAME,
+                                   id,
+                                   std::make_shared<C2SoftMpeg4Dec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftMpeg4Dec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftMpeg4Dec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftMpeg4DecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftMpeg4DecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.h b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.h
new file mode 100644
index 0000000..716a095
--- /dev/null
+++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2_SOFT_MPEG4_DEC_H_
+#define C2_SOFT_MPEG4_DEC_H_
+
+#include <SimpleC2Component.h>
+
+
+struct tagvideoDecControls;
+
+namespace android {
+
+struct C2SoftMpeg4Dec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftMpeg4Dec(const char* name, c2_node_id_t id,
+                   const std::shared_ptr<IntfImpl>& intfImpl);
+    virtual ~C2SoftMpeg4Dec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+ private:
+    enum {
+        kNumOutputBuffers = 2,
+    };
+
+    status_t initDecoder();
+    c2_status_t ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool);
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work);
+    bool handleResChange(const std::unique_ptr<C2Work> &work);
+
+    std::shared_ptr<IntfImpl> mIntf;
+    tagvideoDecControls *mDecHandle;
+    std::shared_ptr<C2GraphicBlock> mOutBlock;
+    uint8_t *mOutputBuffer[kNumOutputBuffers];
+    size_t  mOutputBufferSize;
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mNumSamplesOutput;
+
+    bool mIsMpeg4;
+    bool mInitialized;
+    bool mFramesConfigured;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+
+    C2_DO_NOT_COPY(C2SoftMpeg4Dec);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_MPEG4_DEC_H_
diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp
new file mode 100644
index 0000000..36053f6
--- /dev/null
+++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.cpp
@@ -0,0 +1,661 @@
+/*
+ * 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
+#ifdef MPEG4
+  #define LOG_TAG "C2SoftMpeg4Enc"
+#else
+  #define LOG_TAG "C2SoftH263Enc"
+#endif
+#include <log/log.h>
+
+#include <inttypes.h>
+
+#include <media/hardware/VideoAPI.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/MediaDefs.h>
+#include <utils/misc.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+#include <util/C2InterfaceHelper.h>
+
+#include "C2SoftMpeg4Enc.h"
+#include "mp4enc_api.h"
+
+namespace android {
+
+namespace {
+
+#ifdef MPEG4
+constexpr char COMPONENT_NAME[] = "c2.android.mpeg4.encoder";
+const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_MPEG4;
+#else
+constexpr char COMPONENT_NAME[] = "c2.android.h263.encoder";
+const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_H263;
+#endif
+
+} // namepsace
+
+class C2SoftMpeg4Enc::IntfImpl : public SimpleInterface<void>::BaseParams {
+   public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_VIDEO,
+                MEDIA_MIMETYPE_VIDEO) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
+                .withConstValue(new C2StreamUsageTuning::input(
+                        0u, (uint64_t)C2MemoryUsage::CPU_READ))
+                .build());
+
+        addParameter(
+            DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                .withDefault(new C2StreamPictureSizeInfo::input(0u, 176, 144))
+                .withFields({
+#ifdef MPEG4
+                    C2F(mSize, width).inRange(16, 176, 16),
+                    C2F(mSize, height).inRange(16, 144, 16),
+#else
+                    C2F(mSize, width).oneOf({176, 352}),
+                    C2F(mSize, height).oneOf({144, 288}),
+#endif
+                })
+                .withSetter(SizeSetter)
+                .build());
+
+        addParameter(
+            DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
+                .withDefault(new C2StreamFrameRateInfo::output(0u, 17.))
+                // TODO: More restriction?
+                .withFields({C2F(mFrameRate, value).greaterThan(0.)})
+                .withSetter(
+                    Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+            DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::output(0u, 64000))
+                .withFields({C2F(mBitrate, value).inRange(4096, 12000000)})
+                .withSetter(BitrateSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL)
+                .withDefault(new C2StreamSyncFrameIntervalTuning::output(0u, 1000000))
+                .withFields({C2F(mSyncFramePeriod, value).any()})
+                .withSetter(Setter<decltype(*mSyncFramePeriod)>::StrictValueWithNoDeps)
+                .build());
+
+#ifdef MPEG4
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::output(
+                        0u, PROFILE_MP4V_SIMPLE, LEVEL_MP4V_2))
+                .withFields({
+                    C2F(mProfileLevel, profile).equalTo(
+                            PROFILE_MP4V_SIMPLE),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_MP4V_0,
+                            C2Config::LEVEL_MP4V_0B,
+                            C2Config::LEVEL_MP4V_1,
+                            C2Config::LEVEL_MP4V_2})
+                })
+                .withSetter(ProfileLevelSetter)
+                .build());
+#else
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::output(
+                        0u, PROFILE_H263_BASELINE, LEVEL_H263_45))
+                .withFields({
+                    C2F(mProfileLevel, profile).equalTo(
+                            PROFILE_H263_BASELINE),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_H263_10,
+                            C2Config::LEVEL_H263_20,
+                            C2Config::LEVEL_H263_30,
+                            C2Config::LEVEL_H263_40,
+                            C2Config::LEVEL_H263_45})
+                })
+                .withSetter(ProfileLevelSetter)
+                .build());
+#endif
+    }
+
+    static C2R BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (me.v.value <= 4096) {
+            me.set().value = 4096;
+        }
+        return res;
+    }
+
+    static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input> &oldMe,
+                          C2P<C2StreamPictureSizeInfo::input> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R ProfileLevelSetter(
+            bool mayBlock,
+            C2P<C2StreamProfileLevelInfo::output> &me) {
+        (void)mayBlock;
+        if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
+#ifdef MPEG4
+            me.set().profile = PROFILE_MP4V_SIMPLE;
+#else
+            me.set().profile = PROFILE_H263_BASELINE;
+#endif
+        }
+        if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
+#ifdef MPEG4
+            me.set().level = LEVEL_MP4V_2;
+#else
+            me.set().level = LEVEL_H263_45;
+#endif
+        }
+        return C2R::Ok();
+    }
+
+    // unsafe getters
+    std::shared_ptr<C2StreamPictureSizeInfo::input> getSize_l() const { return mSize; }
+    std::shared_ptr<C2StreamFrameRateInfo::output> getFrameRate_l() const { return mFrameRate; }
+    std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const { return mBitrate; }
+    uint32_t getSyncFramePeriod() const {
+        if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) {
+            return 0;
+        }
+        double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
+        return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
+    }
+
+   private:
+    std::shared_ptr<C2StreamUsageTuning::input> mUsage;
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
+    std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
+};
+
+C2SoftMpeg4Enc::C2SoftMpeg4Enc(const char* name, c2_node_id_t id,
+                               const std::shared_ptr<IntfImpl>& intfImpl)
+    : SimpleC2Component(
+          std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mHandle(nullptr),
+      mEncParams(nullptr),
+      mStarted(false),
+      mOutBufferSize(524288) {
+}
+
+C2SoftMpeg4Enc::~C2SoftMpeg4Enc() {
+    onRelease();
+}
+
+c2_status_t C2SoftMpeg4Enc::onInit() {
+#ifdef MPEG4
+    mEncodeMode = COMBINE_MODE_WITH_ERR_RES;
+#else
+    mEncodeMode = H263_MODE;
+#endif
+    if (!mHandle) {
+        mHandle = new tagvideoEncControls;
+    }
+
+    if (!mEncParams) {
+        mEncParams = new tagvideoEncOptions;
+    }
+
+    if (!(mEncParams && mHandle)) return C2_NO_MEMORY;
+
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+
+    return initEncoder();
+}
+
+c2_status_t C2SoftMpeg4Enc::onStop() {
+    if (!mStarted) {
+        return C2_OK;
+    }
+    if (mHandle) {
+        (void)PVCleanUpVideoEncoder(mHandle);
+    }
+    mStarted = false;
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+    return C2_OK;
+}
+
+void C2SoftMpeg4Enc::onReset() {
+    onStop();
+    initEncoder();
+}
+
+void C2SoftMpeg4Enc::onRelease() {
+    onStop();
+    if (mEncParams) {
+        delete mEncParams;
+        mEncParams = nullptr;
+    }
+    if (mHandle) {
+        delete mHandle;
+        mHandle = nullptr;
+    }
+}
+
+c2_status_t C2SoftMpeg4Enc::onFlush_sm() {
+    return C2_OK;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+c2_status_t C2SoftMpeg4Enc::initEncParams() {
+    if (mHandle) {
+        memset(mHandle, 0, sizeof(tagvideoEncControls));
+    } else return C2_CORRUPTED;
+    if (mEncParams) {
+        memset(mEncParams, 0, sizeof(tagvideoEncOptions));
+    } else return C2_CORRUPTED;
+
+    if (!PVGetDefaultEncOption(mEncParams, 0)) {
+        ALOGE("Failed to get default encoding parameters");
+        return C2_CORRUPTED;
+    }
+
+    if (mFrameRate->value == 0) {
+        ALOGE("Framerate should not be 0");
+        return C2_BAD_VALUE;
+    }
+
+    mEncParams->encMode = mEncodeMode;
+    mEncParams->encWidth[0] = mSize->width;
+    mEncParams->encHeight[0] = mSize->height;
+    mEncParams->encFrameRate[0] = mFrameRate->value + 0.5;
+    mEncParams->rcType = VBR_1;
+    mEncParams->vbvDelay = 5.0f;
+    mEncParams->profile_level = CORE_PROFILE_LEVEL2;
+    mEncParams->packetSize = 32;
+    mEncParams->rvlcEnable = PV_OFF;
+    mEncParams->numLayers = 1;
+    mEncParams->timeIncRes = 1000;
+    mEncParams->tickPerSrc = mEncParams->timeIncRes / (mFrameRate->value + 0.5);
+    mEncParams->bitRate[0] = mBitrate->value;
+    mEncParams->iQuant[0] = 15;
+    mEncParams->pQuant[0] = 12;
+    mEncParams->quantType[0] = 0;
+    mEncParams->noFrameSkipped = PV_OFF;
+
+    // PV's MPEG4 encoder requires the video dimension of multiple
+    if (mSize->width % 16 != 0 || mSize->height % 16 != 0) {
+        ALOGE("Video frame size %dx%d must be a multiple of 16",
+              mSize->width, mSize->height);
+        return C2_BAD_VALUE;
+    }
+
+    // Set IDR frame refresh interval
+    mEncParams->intraPeriod = mIntf->getSyncFramePeriod();
+    mEncParams->numIntraMB = 0;
+    mEncParams->sceneDetect = PV_ON;
+    mEncParams->searchRange = 16;
+    mEncParams->mv8x8Enable = PV_OFF;
+    mEncParams->gobHeaderInterval = 0;
+    mEncParams->useACPred = PV_ON;
+    mEncParams->intraDCVlcTh = 0;
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftMpeg4Enc::initEncoder() {
+    if (mStarted) {
+        return C2_OK;
+    }
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        mSize = mIntf->getSize_l();
+        mBitrate = mIntf->getBitrate_l();
+        mFrameRate = mIntf->getFrameRate_l();
+    }
+    c2_status_t err = initEncParams();
+    if (C2_OK != err) {
+        ALOGE("Failed to initialized encoder params");
+        mSignalledError = true;
+        return err;
+    }
+    if (!PVInitVideoEncoder(mHandle, mEncParams)) {
+        ALOGE("Failed to initialize the encoder");
+        mSignalledError = true;
+        return C2_CORRUPTED;
+    }
+
+    // 1st buffer for codec specific data
+    mNumInputFrames = -1;
+    mStarted = true;
+    return C2_OK;
+}
+
+void C2SoftMpeg4Enc::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.flags = work->input.flags;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    // Initialize encoder if not already initialized
+    if (!mStarted && C2_OK != initEncoder()) {
+        ALOGE("Failed to initialize encoder");
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(mOutBufferSize, usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+
+    C2WriteView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = wView.error();
+        return;
+    }
+
+    uint8_t *outPtr = (uint8_t *)wView.data();
+    if (mNumInputFrames < 0) {
+        // The very first thing we want to output is the codec specific data.
+        int32_t outputSize = mOutBufferSize;
+        if (!PVGetVolHeader(mHandle, outPtr, &outputSize, 0)) {
+            ALOGE("Failed to get VOL header");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        } else {
+            ALOGV("Bytes Generated in header %d\n", outputSize);
+        }
+
+        ++mNumInputFrames;
+        std::unique_ptr<C2StreamInitDataInfo::output> csd =
+            C2StreamInitDataInfo::output::AllocUnique(outputSize, 0u);
+        if (!csd) {
+            ALOGE("CSD allocation failed");
+            mSignalledError = true;
+            work->result = C2_NO_MEMORY;
+            return;
+        }
+        memcpy(csd->m.value, outPtr, outputSize);
+        work->worklets.front()->output.configUpdate.push_back(std::move(csd));
+    }
+
+    std::shared_ptr<const C2GraphicView> rView;
+    std::shared_ptr<C2Buffer> inputBuffer;
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    if (!work->input.buffers.empty()) {
+        inputBuffer = work->input.buffers[0];
+        rView = std::make_shared<const C2GraphicView>(
+                inputBuffer->data().graphicBlocks().front().map().get());
+        if (rView->error() != C2_OK) {
+            ALOGE("graphic view map err = %d", rView->error());
+            work->result = rView->error();
+            return;
+        }
+    } else {
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+
+    uint64_t inputTimeStamp = work->input.ordinal.timestamp.peekull();
+    const C2ConstGraphicBlock inBuffer = inputBuffer->data().graphicBlocks().front();
+    if (inBuffer.width() < mSize->width ||
+        inBuffer.height() < mSize->height) {
+        /* Expect width height to be configured */
+        ALOGW("unexpected Capacity Aspect %d(%d) x %d(%d)", inBuffer.width(),
+              mSize->width, inBuffer.height(), mSize->height);
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    const C2PlanarLayout &layout = rView->layout();
+    uint8_t *yPlane = const_cast<uint8_t *>(rView->data()[C2PlanarLayout::PLANE_Y]);
+    uint8_t *uPlane = const_cast<uint8_t *>(rView->data()[C2PlanarLayout::PLANE_U]);
+    uint8_t *vPlane = const_cast<uint8_t *>(rView->data()[C2PlanarLayout::PLANE_V]);
+    int32_t yStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+    int32_t uStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+    int32_t vStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc;
+    uint32_t width = mSize->width;
+    uint32_t height = mSize->height;
+    // width and height are always even (as block size is 16x16)
+    CHECK_EQ((width & 1u), 0u);
+    CHECK_EQ((height & 1u), 0u);
+    size_t yPlaneSize = width * height;
+    switch (layout.type) {
+        case C2PlanarLayout::TYPE_RGB:
+            [[fallthrough]];
+        case C2PlanarLayout::TYPE_RGBA: {
+            MemoryBlock conversionBuffer = mConversionBuffers.fetch(yPlaneSize * 3 / 2);
+            mConversionBuffersInUse.emplace(conversionBuffer.data(), conversionBuffer);
+            yPlane = conversionBuffer.data();
+            uPlane = yPlane + yPlaneSize;
+            vPlane = uPlane + yPlaneSize / 4;
+            yStride = width;
+            uStride = vStride = width / 2;
+            ConvertRGBToPlanarYUV(yPlane, yStride, height, conversionBuffer.size(), *rView.get());
+            break;
+        }
+        case C2PlanarLayout::TYPE_YUV: {
+            if (!IsYUV420(*rView)) {
+                ALOGE("input is not YUV420");
+                work->result = C2_BAD_VALUE;
+                break;
+            }
+
+            if (layout.planes[layout.PLANE_Y].colInc == 1
+                    && layout.planes[layout.PLANE_U].colInc == 1
+                    && layout.planes[layout.PLANE_V].colInc == 1
+                    && uStride == vStride
+                    && yStride == 2 * vStride) {
+                // I420 compatible - planes are already set up above
+                break;
+            }
+
+            // copy to I420
+            MemoryBlock conversionBuffer = mConversionBuffers.fetch(yPlaneSize * 3 / 2);
+            mConversionBuffersInUse.emplace(conversionBuffer.data(), conversionBuffer);
+            MediaImage2 img = CreateYUV420PlanarMediaImage2(width, height, width, height);
+            status_t err = ImageCopy(conversionBuffer.data(), &img, *rView);
+            if (err != OK) {
+                ALOGE("Buffer conversion failed: %d", err);
+                work->result = C2_BAD_VALUE;
+                return;
+            }
+            yPlane = conversionBuffer.data();
+            uPlane = yPlane + yPlaneSize;
+            vPlane = uPlane + yPlaneSize / 4;
+            yStride = width;
+            uStride = vStride = width / 2;
+            break;
+        }
+
+        case C2PlanarLayout::TYPE_YUVA:
+            ALOGE("YUVA plane type is not supported");
+            work->result = C2_BAD_VALUE;
+            return;
+
+        default:
+            ALOGE("Unrecognized plane type: %d", layout.type);
+            work->result = C2_BAD_VALUE;
+            return;
+    }
+
+    CHECK(NULL != yPlane);
+    /* Encode frames */
+    VideoEncFrameIO vin, vout;
+    memset(&vin, 0, sizeof(vin));
+    memset(&vout, 0, sizeof(vout));
+    vin.yChan = yPlane;
+    vin.uChan = uPlane;
+    vin.vChan = vPlane;
+    vin.timestamp = (inputTimeStamp + 500) / 1000;  // in ms
+    vin.height = align(height, 16);
+    vin.pitch = align(width, 16);
+
+    uint32_t modTimeMs = 0;
+    int32_t nLayer = 0;
+    MP4HintTrack hintTrack;
+    int32_t outputSize = mOutBufferSize;
+    if (!PVEncodeVideoFrame(mHandle, &vin, &vout, &modTimeMs, outPtr, &outputSize, &nLayer) ||
+        !PVGetHintTrack(mHandle, &hintTrack)) {
+        ALOGE("Failed to encode frame or get hint track at frame %" PRId64, mNumInputFrames);
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        return;
+    }
+    ALOGV("outputSize filled : %d", outputSize);
+    ++mNumInputFrames;
+    CHECK(NULL == PVGetOverrunBuffer(mHandle));
+
+    fillEmptyWork(work);
+    if (outputSize) {
+        std::shared_ptr<C2Buffer> buffer = createLinearBuffer(block, 0, outputSize);
+        work->worklets.front()->output.ordinal.timestamp = inputTimeStamp;
+        if (hintTrack.CodeType == 0) {
+            buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
+                    0u /* stream id */, C2Config::SYNC_FRAME));
+        }
+        work->worklets.front()->output.buffers.push_back(buffer);
+    }
+    if (eos) {
+        mSignalledOutputEos = true;
+    }
+
+    mConversionBuffersInUse.erase(yPlane);
+}
+
+c2_status_t C2SoftMpeg4Enc::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void)pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    return C2_OK;
+}
+
+class C2SoftMpeg4EncFactory : public C2ComponentFactory {
+public:
+    C2SoftMpeg4EncFactory()
+        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+              GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+            new C2SoftMpeg4Enc(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftMpeg4Enc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+            new SimpleInterface<C2SoftMpeg4Enc::IntfImpl>(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftMpeg4Enc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftMpeg4EncFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftMpeg4EncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.h b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.h
new file mode 100644
index 0000000..43461fc
--- /dev/null
+++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Enc.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2_SOFT_MPEG4_ENC_H__
+#define C2_SOFT_MPEG4_ENC_H__
+
+#include <map>
+
+#include <Codec2BufferUtils.h>
+#include <SimpleC2Component.h>
+
+#include "mp4enc_api.h"
+
+namespace android {
+
+struct C2SoftMpeg4Enc : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftMpeg4Enc(const char* name, c2_node_id_t id,
+                   const std::shared_ptr<IntfImpl>& intfImpl);
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+protected:
+
+    virtual ~C2SoftMpeg4Enc();
+
+private:
+    std::shared_ptr<IntfImpl> mIntf;
+
+    tagvideoEncControls *mHandle;
+    tagvideoEncOptions  *mEncParams;
+
+    bool     mStarted;
+    bool     mSignalledOutputEos;
+    bool     mSignalledError;
+
+    uint32_t mOutBufferSize;
+    // configurations used by component in process
+    // (TODO: keep this in intf but make them internal only)
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+
+    int64_t  mNumInputFrames;
+    MP4EncodingMode mEncodeMode;
+
+    MemoryBlockPool mConversionBuffers;
+    std::map<void *, MemoryBlock> mConversionBuffersInUse;
+
+    c2_status_t initEncParams();
+    c2_status_t initEncoder();
+
+    C2_DO_NOT_COPY(C2SoftMpeg4Enc);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_MPEG4_ENC_H__
diff --git a/media/common_time/MODULE_LICENSE_APACHE2 b/media/codec2/components/mpeg4_h263/MODULE_LICENSE_APACHE2
similarity index 100%
copy from media/common_time/MODULE_LICENSE_APACHE2
copy to media/codec2/components/mpeg4_h263/MODULE_LICENSE_APACHE2
diff --git a/media/common_time/NOTICE b/media/codec2/components/mpeg4_h263/NOTICE
similarity index 100%
copy from media/common_time/NOTICE
copy to media/codec2/components/mpeg4_h263/NOTICE
diff --git a/media/codec2/components/mpeg4_h263/patent_disclaimer.txt b/media/codec2/components/mpeg4_h263/patent_disclaimer.txt
new file mode 100644
index 0000000..b4bf11d
--- /dev/null
+++ b/media/codec2/components/mpeg4_h263/patent_disclaimer.txt
@@ -0,0 +1,9 @@
+
+THIS IS NOT A GRANT OF PATENT RIGHTS.
+
+Google makes no representation or warranty that the codecs for which
+source code is made available hereunder are unencumbered by
+third-party patents.  Those intending to use this source code in
+hardware or software products are advised that implementations of
+these codecs, including in open source software or shareware, may
+require patent licenses from the relevant patent holders.
diff --git a/media/codec2/components/opus/Android.bp b/media/codec2/components/opus/Android.bp
new file mode 100644
index 0000000..0ed141b
--- /dev/null
+++ b/media/codec2/components/opus/Android.bp
@@ -0,0 +1,22 @@
+cc_library_shared {
+    name: "libcodec2_soft_opusdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftOpusDec.cpp"],
+
+    shared_libs: ["libopus"],
+}
+cc_library_shared {
+    name: "libcodec2_soft_opusenc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftOpusEnc.cpp"],
+
+    shared_libs: ["libopus"],
+}
diff --git a/media/codec2/components/opus/C2SoftOpusDec.cpp b/media/codec2/components/opus/C2SoftOpusDec.cpp
new file mode 100644
index 0000000..6b6974f
--- /dev/null
+++ b/media/codec2/components/opus/C2SoftOpusDec.cpp
@@ -0,0 +1,484 @@
+/*
+ * 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 "C2SoftOpusDec"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/foundation/OpusHeader.h>
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+#include "C2SoftOpusDec.h"
+
+extern "C" {
+    #include <opus.h>
+    #include <opus_multistream.h>
+}
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.opus.decoder";
+
+}  // namespace
+
+class C2SoftOpusDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_OPUS) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::output(0u, 48000))
+                .withFields({C2F(mSampleRate, value).equalTo(48000)})
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::output(0u, 1))
+                .withFields({C2F(mChannelCount, value).inRange(1, 8)})
+                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::input(0u, 6000))
+                .withFields({C2F(mBitrate, value).inRange(6000, 510000)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 960 * 6))
+                .build());
+    }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+};
+
+C2SoftOpusDec::C2SoftOpusDec(const char *name, c2_node_id_t id,
+                       const std::shared_ptr<IntfImpl>& intfImpl)
+    : SimpleC2Component(
+        std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mDecoder(nullptr) {
+}
+
+C2SoftOpusDec::~C2SoftOpusDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftOpusDec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+c2_status_t C2SoftOpusDec::onStop() {
+    if (mDecoder) {
+        opus_multistream_decoder_destroy(mDecoder);
+        mDecoder = nullptr;
+    }
+    memset(&mHeader, 0, sizeof(mHeader));
+    mCodecDelay = 0;
+    mSeekPreRoll = 0;
+    mSamplesToDiscard = 0;
+    mInputBufferCount = 0;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+
+    return C2_OK;
+}
+
+void C2SoftOpusDec::onReset() {
+    (void)onStop();
+}
+
+void C2SoftOpusDec::onRelease() {
+    if (mDecoder) {
+        opus_multistream_decoder_destroy(mDecoder);
+        mDecoder = nullptr;
+    }
+}
+
+status_t C2SoftOpusDec::initDecoder() {
+    memset(&mHeader, 0, sizeof(mHeader));
+    mCodecDelay = 0;
+    mSeekPreRoll = 0;
+    mSamplesToDiscard = 0;
+    mInputBufferCount = 0;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+
+    return OK;
+}
+
+c2_status_t C2SoftOpusDec::onFlush_sm() {
+    if (mDecoder) {
+        opus_multistream_decoder_ctl(mDecoder, OPUS_RESET_STATE);
+        mSamplesToDiscard = mSeekPreRoll;
+        mSignalledOutputEos = false;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftOpusDec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    return C2_OK;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+static const int kRate = 48000;
+
+// Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies
+// mappings for up to 8 channels. This information is part of the Vorbis I
+// Specification:
+// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
+static const int kMaxChannels = 8;
+
+// Maximum packet size used in Xiph's opusdec.
+static const int kMaxOpusOutputPacketSizeSamples = 960 * 6;
+
+// Default audio output channel layout. Used to initialize |stream_map| in
+// OpusHeader, and passed to opus_multistream_decoder_create() when the header
+// does not contain mapping information. The values are valid only for mono and
+// stereo output: Opus streams with more than 2 channels require a stream map.
+static const int kMaxChannelsWithDefaultLayout = 2;
+static const uint8_t kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = { 0, 1 };
+
+
+// Convert nanoseconds to number of samples.
+static uint64_t ns_to_samples(uint64_t ns, int rate) {
+    return static_cast<double>(ns) * rate / 1000000000;
+}
+
+void C2SoftOpusDec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+    if (inSize == 0) {
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+          (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+    const uint8_t *data = rView.data() + inOffset;
+    if (mInputBufferCount < 3) {
+        if (mInputBufferCount == 0) {
+            size_t opusHeadSize = 0;
+            size_t codecDelayBufSize = 0;
+            size_t seekPreRollBufSize = 0;
+            void *opusHeadBuf = NULL;
+            void *codecDelayBuf = NULL;
+            void *seekPreRollBuf = NULL;
+
+            if (!GetOpusHeaderBuffers(data, inSize, &opusHeadBuf,
+                                     &opusHeadSize, &codecDelayBuf,
+                                     &codecDelayBufSize, &seekPreRollBuf,
+                                     &seekPreRollBufSize)) {
+                ALOGE("%s encountered error in GetOpusHeaderBuffers", __func__);
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+
+            if (!ParseOpusHeader((uint8_t *)opusHeadBuf, opusHeadSize, &mHeader)) {
+                ALOGE("%s Encountered error while Parsing Opus Header.", __func__);
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            uint8_t channel_mapping[kMaxChannels] = {0};
+            if (mHeader.channels <= kMaxChannelsWithDefaultLayout) {
+                memcpy(&channel_mapping,
+                       kDefaultOpusChannelLayout,
+                       kMaxChannelsWithDefaultLayout);
+            } else {
+                memcpy(&channel_mapping,
+                       mHeader.stream_map,
+                       mHeader.channels);
+            }
+            int status = OPUS_INVALID_STATE;
+            mDecoder = opus_multistream_decoder_create(kRate,
+                                                       mHeader.channels,
+                                                       mHeader.num_streams,
+                                                       mHeader.num_coupled,
+                                                       channel_mapping,
+                                                       &status);
+            if (!mDecoder || status != OPUS_OK) {
+                ALOGE("opus_multistream_decoder_create failed status = %s",
+                      opus_strerror(status));
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            status = opus_multistream_decoder_ctl(mDecoder,
+                                                  OPUS_SET_GAIN(mHeader.gain_db));
+            if (status != OPUS_OK) {
+                ALOGE("Failed to set OPUS header gain; status = %s",
+                      opus_strerror(status));
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+
+            if (codecDelayBuf && codecDelayBufSize == sizeof(uint64_t)) {
+                uint64_t value;
+                memcpy(&value, codecDelayBuf, sizeof(uint64_t));
+                mCodecDelay = ns_to_samples(value, kRate);
+                mSamplesToDiscard = mCodecDelay;
+                ++mInputBufferCount;
+            }
+            if (seekPreRollBuf && seekPreRollBufSize == sizeof(uint64_t)) {
+                uint64_t value;
+                memcpy(&value, seekPreRollBuf, sizeof(uint64_t));
+                mSeekPreRoll = ns_to_samples(value, kRate);
+                ++mInputBufferCount;
+            }
+        } else {
+            if (inSize < 8) {
+                ALOGE("Input sample size is too small.");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            int64_t samples = ns_to_samples( *(reinterpret_cast<int64_t*>
+                              (const_cast<uint8_t *> (data))), kRate);
+            if (mInputBufferCount == 1) {
+                mCodecDelay = samples;
+                mSamplesToDiscard = mCodecDelay;
+            }
+            else {
+                mSeekPreRoll = samples;
+            }
+        }
+
+        ++mInputBufferCount;
+        if (mInputBufferCount == 3) {
+            ALOGI("Configuring decoder: %d Hz, %d channels",
+                   kRate, mHeader.channels);
+            C2StreamSampleRateInfo::output sampleRateInfo(0u, kRate);
+            C2StreamChannelCountInfo::output channelCountInfo(0u, mHeader.channels);
+            std::vector<std::unique_ptr<C2SettingResult>> failures;
+            c2_status_t err = mIntf->config(
+                    { &sampleRateInfo, &channelCountInfo },
+                    C2_MAY_BLOCK,
+                    &failures);
+            if (err == OK) {
+                work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(sampleRateInfo));
+                work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(channelCountInfo));
+            } else {
+                ALOGE("Config Update failed");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+        }
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+
+    // Ignore CSD re-submissions.
+    if ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG)) {
+        fillEmptyWork(work);
+        return;
+    }
+
+    // When seeking to zero, |mCodecDelay| samples has to be discarded
+    // instead of |mSeekPreRoll| samples (as we would when seeking to any
+    // other timestamp).
+    if (work->input.ordinal.timestamp.peeku() == 0) mSamplesToDiscard = mCodecDelay;
+
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(
+                          kMaxNumSamplesPerBuffer * kMaxChannels * sizeof(int16_t),
+                          usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    int numSamples = opus_multistream_decode(mDecoder,
+                                             data,
+                                             inSize,
+                                             reinterpret_cast<int16_t *> (wView.data()),
+                                             kMaxOpusOutputPacketSizeSamples,
+                                             0);
+    if (numSamples < 0) {
+        ALOGE("opus_multistream_decode returned numSamples %d", numSamples);
+        numSamples = 0;
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    int outOffset = 0;
+    if (mSamplesToDiscard > 0) {
+        if (mSamplesToDiscard > numSamples) {
+            mSamplesToDiscard -= numSamples;
+            numSamples = 0;
+        } else {
+            numSamples -= mSamplesToDiscard;
+            outOffset = mSamplesToDiscard * sizeof(int16_t) * mHeader.channels;
+            mSamplesToDiscard = 0;
+        }
+    }
+
+    if (numSamples) {
+        int outSize = numSamples * sizeof(int16_t) * mHeader.channels;
+        ALOGV("out buffer attr. offset %d size %d ", outOffset, outSize);
+
+        work->worklets.front()->output.flags = work->input.flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, outOffset, outSize));
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    } else {
+        fillEmptyWork(work);
+        block.reset();
+    }
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+    }
+}
+
+class C2SoftOpusDecFactory : public C2ComponentFactory {
+public:
+    C2SoftOpusDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftOpusDec(COMPONENT_NAME,
+                               id,
+                               std::make_shared<C2SoftOpusDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftOpusDec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftOpusDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftOpusDecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftOpusDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/opus/C2SoftOpusDec.h b/media/codec2/components/opus/C2SoftOpusDec.h
new file mode 100644
index 0000000..b0715ac
--- /dev/null
+++ b/media/codec2/components/opus/C2SoftOpusDec.h
@@ -0,0 +1,69 @@
+/*
+ * 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_C2_SOFT_OPUS_DEC_H_
+#define ANDROID_C2_SOFT_OPUS_DEC_H_
+
+#include <SimpleC2Component.h>
+
+
+struct OpusMSDecoder;
+
+namespace android {
+
+struct C2SoftOpusDec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftOpusDec(const char *name, c2_node_id_t id,
+               const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftOpusDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+private:
+    enum {
+        kMaxNumSamplesPerBuffer = 960 * 6
+    };
+
+    std::shared_ptr<IntfImpl> mIntf;
+    OpusMSDecoder *mDecoder;
+    OpusHeader mHeader;
+
+    int64_t mCodecDelay;
+    int64_t mSeekPreRoll;
+    int64_t mSamplesToDiscard;
+    size_t mInputBufferCount;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+
+    status_t initDecoder();
+
+    C2_DO_NOT_COPY(C2SoftOpusDec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_OPUS_DEC_H_
diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp
new file mode 100644
index 0000000..70d1965
--- /dev/null
+++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "C2SoftOpusEnc"
+#include <utils/Log.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/foundation/OpusHeader.h>
+#include "C2SoftOpusEnc.h"
+
+extern "C" {
+    #include <opus.h>
+    #include <opus_multistream.h>
+}
+
+#define DEFAULT_FRAME_DURATION_MS 20
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.opus.encoder";
+
+}  // namespace
+
+static const int kMaxNumChannelsSupported = 2;
+
+class C2SoftOpusEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_OPUS) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::input(0u, 48000))
+                .withFields({C2F(mSampleRate, value).oneOf({
+                    8000, 12000, 16000, 24000, 48000})})
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::input(0u, 1))
+                .withFields({C2F(mChannelCount, value).inRange(1, kMaxNumChannelsSupported)})
+                .withSetter((Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::output(0u, 128000))
+                .withFields({C2F(mBitrate, value).inRange(500, 512000)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mComplexity, C2_PARAMKEY_COMPLEXITY)
+                .withDefault(new C2StreamComplexityTuning::output(0u, 10))
+                .withFields({C2F(mComplexity, value).inRange(1, 10)})
+                .withSetter(Setter<decltype(*mComplexity)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 3840))
+                .build());
+    }
+
+    uint32_t getSampleRate() const { return mSampleRate->value; }
+    uint32_t getChannelCount() const { return mChannelCount->value; }
+    uint32_t getBitrate() const { return mBitrate->value; }
+    uint32_t getComplexity() const { return mComplexity->value; }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamComplexityTuning::output> mComplexity;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+};
+
+C2SoftOpusEnc::C2SoftOpusEnc(const char* name, c2_node_id_t id,
+                               const std::shared_ptr<IntfImpl>& intfImpl)
+    : SimpleC2Component(
+          std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mOutputBlock(nullptr),
+      mEncoder(nullptr),
+      mInputBufferPcm16(nullptr),
+      mOutIndex(0u) {
+}
+
+C2SoftOpusEnc::~C2SoftOpusEnc() {
+    onRelease();
+}
+
+c2_status_t C2SoftOpusEnc::onInit() {
+    return initEncoder();
+}
+
+c2_status_t C2SoftOpusEnc::configureEncoder() {
+    static const unsigned char mono_mapping[256] = {0};
+    static const unsigned char stereo_mapping[256] = {0, 1};
+    mSampleRate = mIntf->getSampleRate();
+    mChannelCount = mIntf->getChannelCount();
+    uint32_t bitrate = mIntf->getBitrate();
+    int complexity = mIntf->getComplexity();
+    mNumSamplesPerFrame = mSampleRate / (1000 / mFrameDurationMs);
+    mNumPcmBytesPerInputFrame =
+        mChannelCount * mNumSamplesPerFrame * sizeof(int16_t);
+    int err = C2_OK;
+
+    const unsigned char* mapping;
+    if (mChannelCount == 1) {
+        mapping = mono_mapping;
+    } else if (mChannelCount == 2) {
+        mapping = stereo_mapping;
+    } else {
+        ALOGE("Number of channels (%d) is not supported", mChannelCount);
+        return C2_BAD_VALUE;
+    }
+
+    if (mEncoder != nullptr) {
+        opus_multistream_encoder_destroy(mEncoder);
+    }
+
+    mEncoder = opus_multistream_encoder_create(mSampleRate, mChannelCount,
+        1, mChannelCount - 1, mapping, OPUS_APPLICATION_AUDIO, &err);
+    if (err) {
+        ALOGE("Could not create libopus encoder. Error code: %i", err);
+        return C2_CORRUPTED;
+    }
+
+    // Complexity
+    if (opus_multistream_encoder_ctl(
+            mEncoder, OPUS_SET_COMPLEXITY(complexity)) != OPUS_OK) {
+        ALOGE("failed to set complexity");
+        return C2_BAD_VALUE;
+    }
+
+    // DTX
+    if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_DTX(0) != OPUS_OK)) {
+        ALOGE("failed to set dtx");
+        return C2_BAD_VALUE;
+    }
+
+    // Application
+    if (opus_multistream_encoder_ctl(mEncoder,
+            OPUS_SET_APPLICATION(OPUS_APPLICATION_AUDIO)) != OPUS_OK) {
+        ALOGE("failed to set application");
+        return C2_BAD_VALUE;
+    }
+
+    // Signal type
+    if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_SIGNAL(OPUS_AUTO)) !=
+        OPUS_OK) {
+        ALOGE("failed to set signal");
+        return C2_BAD_VALUE;
+    }
+
+    // Constrained VBR
+    if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_VBR(1) != OPUS_OK)) {
+        ALOGE("failed to set vbr type");
+        return C2_BAD_VALUE;
+    }
+    if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_VBR_CONSTRAINT(1) !=
+            OPUS_OK)) {
+        ALOGE("failed to set vbr constraint");
+        return C2_BAD_VALUE;
+    }
+
+    // Bitrate
+    if (opus_multistream_encoder_ctl(mEncoder, OPUS_SET_BITRATE(bitrate)) !=
+            OPUS_OK) {
+        ALOGE("failed to set bitrate");
+        return C2_BAD_VALUE;
+    }
+
+    // Set seek preroll to 80 ms
+    mSeekPreRoll = 80000000;
+    return C2_OK;
+}
+
+c2_status_t C2SoftOpusEnc::initEncoder() {
+    mSignalledEos = false;
+    mSignalledError = false;
+    mHeaderGenerated = false;
+    mIsFirstFrame = true;
+    mEncoderFlushed = false;
+    mBufferAvailable = false;
+    mAnchorTimeStamp = 0ull;
+    mProcessedSamples = 0;
+    mFilledLen = 0;
+    mFrameDurationMs = DEFAULT_FRAME_DURATION_MS;
+    if (!mInputBufferPcm16) {
+        mInputBufferPcm16 =
+            (int16_t*)malloc(kFrameSize * kMaxNumChannels * sizeof(int16_t));
+    }
+    if (!mInputBufferPcm16) return C2_NO_MEMORY;
+
+    /* Default Configurations */
+    c2_status_t status = configureEncoder();
+    return status;
+}
+
+c2_status_t C2SoftOpusEnc::onStop() {
+    mSignalledEos = false;
+    mSignalledError = false;
+    mIsFirstFrame = true;
+    mEncoderFlushed = false;
+    mBufferAvailable = false;
+    mAnchorTimeStamp = 0ull;
+    mProcessedSamples = 0u;
+    mFilledLen = 0;
+    if (mEncoder) {
+        int status = opus_multistream_encoder_ctl(mEncoder, OPUS_RESET_STATE);
+        if (status != OPUS_OK) {
+            ALOGE("OPUS_RESET_STATE failed status = %s", opus_strerror(status));
+            mSignalledError = true;
+            return C2_CORRUPTED;
+        }
+    }
+    if (mOutputBlock) mOutputBlock.reset();
+    mOutputBlock = nullptr;
+
+    return C2_OK;
+}
+
+void C2SoftOpusEnc::onReset() {
+    (void)onStop();
+}
+
+void C2SoftOpusEnc::onRelease() {
+    (void)onStop();
+    if (mInputBufferPcm16) {
+        free(mInputBufferPcm16);
+        mInputBufferPcm16 = nullptr;
+    }
+    if (mEncoder) {
+        opus_multistream_encoder_destroy(mEncoder);
+        mEncoder = nullptr;
+    }
+}
+
+c2_status_t C2SoftOpusEnc::onFlush_sm() {
+    return onStop();
+}
+
+// Drain the encoder to get last frames (if any)
+int C2SoftOpusEnc::drainEncoder(uint8_t* outPtr) {
+    memset((uint8_t *)mInputBufferPcm16 + mFilledLen, 0,
+        (mNumPcmBytesPerInputFrame - mFilledLen));
+    int encodedBytes = opus_multistream_encode(
+        mEncoder, mInputBufferPcm16, mNumSamplesPerFrame, outPtr, kMaxPayload);
+    if (encodedBytes > mOutputBlock->capacity()) {
+        ALOGE("not enough space left to write encoded data, dropping %d bytes",
+              mBytesEncoded);
+        // a fatal error would stop the encoding
+        return -1;
+    }
+    ALOGV("encoded %i Opus bytes from %zu PCM bytes", encodedBytes,
+          mNumPcmBytesPerInputFrame);
+    mEncoderFlushed = true;
+    mFilledLen = 0;
+    return encodedBytes;
+}
+
+void C2SoftOpusEnc::process(const std::unique_ptr<C2Work>& work,
+                            const std::shared_ptr<C2BlockPool>& pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+    C2ReadView rView = mDummyReadView;
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    c2_status_t err = C2_OK;
+    if (!work->input.buffers.empty()) {
+        rView =
+            work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+
+    if (!mEncoder) {
+        if (initEncoder() != C2_OK) {
+            ALOGE("initEncoder failed with status %d", err);
+            work->result = err;
+            mSignalledError = true;
+            return;
+        }
+    }
+    if (mIsFirstFrame && inSize > 0) {
+        mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+        mIsFirstFrame = false;
+    }
+
+    C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+    err = pool->fetchLinearBlock(kMaxPayload, usage, &mOutputBlock);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+
+    C2WriteView wView = mOutputBlock->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        mOutputBlock.reset();
+        return;
+    }
+
+    size_t inPos = 0;
+    size_t processSize = 0;
+    mBytesEncoded = 0;
+    uint64_t outTimeStamp = 0u;
+    std::shared_ptr<C2Buffer> buffer;
+    uint64_t inputIndex = work->input.ordinal.frameIndex.peeku();
+    const uint8_t* inPtr = rView.data() + inOffset;
+
+    class FillWork {
+    public:
+        FillWork(uint32_t flags, C2WorkOrdinalStruct ordinal,
+                 const std::shared_ptr<C2Buffer> &buffer)
+            : mFlags(flags), mOrdinal(ordinal), mBuffer(buffer) {
+        }
+        ~FillWork() = default;
+
+        void operator()(const std::unique_ptr<C2Work>& work) {
+            work->worklets.front()->output.flags = (C2FrameData::flags_t)mFlags;
+            work->worklets.front()->output.buffers.clear();
+            work->worklets.front()->output.ordinal = mOrdinal;
+            work->workletsProcessed = 1u;
+            work->result = C2_OK;
+            if (mBuffer) {
+                work->worklets.front()->output.buffers.push_back(mBuffer);
+            }
+            ALOGV("timestamp = %lld, index = %lld, w/%s buffer",
+                  mOrdinal.timestamp.peekll(),
+                  mOrdinal.frameIndex.peekll(),
+                  mBuffer ? "" : "o");
+        }
+
+    private:
+        const uint32_t mFlags;
+        const C2WorkOrdinalStruct mOrdinal;
+        const std::shared_ptr<C2Buffer> mBuffer;
+    };
+
+    C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+
+    if (!mHeaderGenerated) {
+        uint8_t header[AOPUS_UNIFIED_CSD_MAXSIZE];
+        memset(header, 0, sizeof(header));
+
+        // Get codecDelay
+        int32_t lookahead;
+        if (opus_multistream_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&lookahead)) !=
+                OPUS_OK) {
+            ALOGE("failed to get lookahead");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        mCodecDelay = lookahead * 1000000000ll / mSampleRate;
+
+        OpusHeader opusHeader;
+        memset(&opusHeader, 0, sizeof(opusHeader));
+        opusHeader.channels = mChannelCount;
+        opusHeader.num_streams = mChannelCount;
+        opusHeader.num_coupled = 0;
+        opusHeader.channel_mapping = ((mChannelCount > 8) ? 255 : (mChannelCount > 2));
+        opusHeader.gain_db = 0;
+        opusHeader.skip_samples = lookahead;
+        int headerLen = WriteOpusHeaders(opusHeader, mSampleRate, header,
+            sizeof(header), mCodecDelay, mSeekPreRoll);
+
+        std::unique_ptr<C2StreamInitDataInfo::output> csd =
+            C2StreamInitDataInfo::output::AllocUnique(headerLen, 0u);
+        if (!csd) {
+            ALOGE("CSD allocation failed");
+            mSignalledError = true;
+            work->result = C2_NO_MEMORY;
+            return;
+        }
+        ALOGV("put csd, %d bytes", headerLen);
+        memcpy(csd->m.value, header, headerLen);
+        work->worklets.front()->output.configUpdate.push_back(std::move(csd));
+        mHeaderGenerated = true;
+    }
+
+    /*
+     * For buffer size which is not a multiple of mNumPcmBytesPerInputFrame, we will
+     * accumulate the input and keep it. Once the input is filled with expected number
+     * of bytes, we will send it to encoder. mFilledLen manages the bytes of input yet
+     * to be processed. The next call will fill mNumPcmBytesPerInputFrame - mFilledLen
+     * bytes to input and send it to the encoder.
+     */
+    while (inPos < inSize) {
+        const uint8_t* pcmBytes = inPtr + inPos;
+        int filledSamples = mFilledLen / sizeof(int16_t);
+        if ((inPos + (mNumPcmBytesPerInputFrame - mFilledLen)) <= inSize) {
+            processSize = mNumPcmBytesPerInputFrame - mFilledLen;
+            mBufferAvailable = true;
+        } else {
+            processSize = inSize - inPos;
+            mBufferAvailable = false;
+            if (eos) {
+                memset(mInputBufferPcm16 + filledSamples, 0,
+                       (mNumPcmBytesPerInputFrame - mFilledLen));
+                mBufferAvailable = true;
+            }
+        }
+        const unsigned nInputSamples = processSize / sizeof(int16_t);
+
+        for (unsigned i = 0; i < nInputSamples; i++) {
+            int32_t data = pcmBytes[2 * i + 1] << 8 | pcmBytes[2 * i];
+            data = ((data & 0xFFFF) ^ 0x8000) - 0x8000;
+            mInputBufferPcm16[i + filledSamples] = data;
+        }
+        inPos += processSize;
+        mFilledLen += processSize;
+        if (!mBufferAvailable) break;
+        uint8_t* outPtr = wView.data() + mBytesEncoded;
+        int encodedBytes =
+            opus_multistream_encode(mEncoder, mInputBufferPcm16,
+                                    mNumSamplesPerFrame, outPtr, kMaxPayload - mBytesEncoded);
+        ALOGV("encoded %i Opus bytes from %zu PCM bytes", encodedBytes,
+              processSize);
+
+        if (encodedBytes < 0 || encodedBytes > (kMaxPayload - mBytesEncoded)) {
+            ALOGE("opus_encode failed, encodedBytes : %d", encodedBytes);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        if (buffer) {
+            outOrdinal.frameIndex = mOutIndex++;
+            outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+            cloneAndSend(
+                inputIndex, work,
+                FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+            buffer.reset();
+        }
+        if (encodedBytes > 0) {
+            buffer =
+                createLinearBuffer(mOutputBlock, mBytesEncoded, encodedBytes);
+        }
+        mBytesEncoded += encodedBytes;
+        mProcessedSamples += (filledSamples + nInputSamples);
+        outTimeStamp =
+            mProcessedSamples * 1000000ll / mChannelCount / mSampleRate;
+        if ((processSize + mFilledLen) < mNumPcmBytesPerInputFrame)
+            mEncoderFlushed = true;
+        mFilledLen = 0;
+    }
+
+    uint32_t flags = 0;
+    if (eos) {
+        ALOGV("signalled eos");
+        mSignalledEos = true;
+        if (!mEncoderFlushed) {
+            if (buffer) {
+                outOrdinal.frameIndex = mOutIndex++;
+                outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+                cloneAndSend(
+                    inputIndex, work,
+                    FillWork(C2FrameData::FLAG_INCOMPLETE, outOrdinal, buffer));
+                buffer.reset();
+            }
+            // drain the encoder for last buffer
+            drainInternal(pool, work);
+        }
+        flags = C2FrameData::FLAG_END_OF_STREAM;
+    }
+    if (buffer) {
+        outOrdinal.frameIndex = mOutIndex++;
+        outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+        FillWork((C2FrameData::flags_t)(flags), outOrdinal, buffer)(work);
+        buffer.reset();
+    }
+    mOutputBlock = nullptr;
+}
+
+c2_status_t C2SoftOpusEnc::drainInternal(
+        const std::shared_ptr<C2BlockPool>& pool,
+        const std::unique_ptr<C2Work>& work) {
+    mBytesEncoded = 0;
+    std::shared_ptr<C2Buffer> buffer = nullptr;
+    C2WorkOrdinalStruct outOrdinal = work->input.ordinal;
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+
+    C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+    c2_status_t err = pool->fetchLinearBlock(kMaxPayload, usage, &mOutputBlock);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        return C2_NO_MEMORY;
+    }
+
+    C2WriteView wView = mOutputBlock->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        mOutputBlock.reset();
+        return C2_CORRUPTED;
+    }
+
+    int encBytes = drainEncoder(wView.data());
+    if (encBytes > 0) mBytesEncoded += encBytes;
+    if (mBytesEncoded > 0) {
+        buffer = createLinearBuffer(mOutputBlock, 0, mBytesEncoded);
+        mOutputBlock.reset();
+    }
+    mProcessedSamples += (mNumPcmBytesPerInputFrame / sizeof(int16_t));
+    uint64_t outTimeStamp =
+        mProcessedSamples * 1000000ll / mChannelCount / mSampleRate;
+    outOrdinal.frameIndex = mOutIndex++;
+    outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+    work->worklets.front()->output.flags =
+        (C2FrameData::flags_t)(eos ? C2FrameData::FLAG_END_OF_STREAM : 0);
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = outOrdinal;
+    work->workletsProcessed = 1u;
+    work->result = C2_OK;
+    if (buffer) {
+        work->worklets.front()->output.buffers.push_back(buffer);
+    }
+    mOutputBlock = nullptr;
+    return C2_OK;
+}
+
+c2_status_t C2SoftOpusEnc::drain(uint32_t drainMode,
+                                 const std::shared_ptr<C2BlockPool>& pool) {
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+    mIsFirstFrame = true;
+    mAnchorTimeStamp = 0ull;
+    mProcessedSamples = 0u;
+    return drainInternal(pool, nullptr);
+}
+
+class C2SoftOpusEncFactory : public C2ComponentFactory {
+public:
+    C2SoftOpusEncFactory()
+        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+              GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+    virtual c2_status_t createComponent(
+        c2_node_id_t id, std::shared_ptr<C2Component>* const component,
+        std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+            new C2SoftOpusEnc(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftOpusEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+        c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+        std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+            new SimpleInterface<C2SoftOpusEnc::IntfImpl>(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftOpusEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftOpusEncFactory() override = default;
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftOpusEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/opus/C2SoftOpusEnc.h b/media/codec2/components/opus/C2SoftOpusEnc.h
new file mode 100644
index 0000000..2b4d8f2
--- /dev/null
+++ b/media/codec2/components/opus/C2SoftOpusEnc.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_C2_SOFT_OPUS_ENC_H_
+#define ANDROID_C2_SOFT_OPUS_ENC_H_
+
+#include <atomic>
+#include <SimpleC2Component.h>
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+struct OpusMSEncoder;
+
+namespace android {
+
+struct C2SoftOpusEnc : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftOpusEnc(const char *name, c2_node_id_t id,
+               const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftOpusEnc();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+private:
+    /* OPUS_FRAMESIZE_20_MS */
+    const int kFrameSize = 960;
+    const int kMaxSampleRate = 48000;
+    const int kMinSampleRate = 8000;
+    const int kMaxPayload = (4000 * kMaxSampleRate) / kMinSampleRate;
+    const int kMaxNumChannels = 8;
+
+    std::shared_ptr<IntfImpl> mIntf;
+    std::shared_ptr<C2LinearBlock> mOutputBlock;
+
+    OpusMSEncoder* mEncoder;
+    int16_t* mInputBufferPcm16;
+
+    bool mHeaderGenerated;
+    bool mIsFirstFrame;
+    bool mEncoderFlushed;
+    bool mBufferAvailable;
+    bool mSignalledEos;
+    bool mSignalledError;
+    uint32_t mSampleRate;
+    uint32_t mChannelCount;
+    uint32_t mFrameDurationMs;
+    uint64_t mAnchorTimeStamp;
+    uint64_t mProcessedSamples;
+    // Codec delay in ns
+    uint64_t mCodecDelay;
+    // Seek pre-roll in ns
+    uint64_t mSeekPreRoll;
+    int mNumSamplesPerFrame;
+    int mBytesEncoded;
+    int32_t mFilledLen;
+    size_t mNumPcmBytesPerInputFrame;
+    std::atomic_uint64_t mOutIndex;
+    c2_status_t initEncoder();
+    c2_status_t configureEncoder();
+    int drainEncoder(uint8_t* outPtr);
+    c2_status_t drainInternal(const std::shared_ptr<C2BlockPool>& pool,
+                              const std::unique_ptr<C2Work>& work);
+
+    C2_DO_NOT_COPY(C2SoftOpusEnc);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_OPUS_ENC_H_
diff --git a/media/codec2/components/raw/Android.bp b/media/codec2/components/raw/Android.bp
new file mode 100644
index 0000000..dc944da
--- /dev/null
+++ b/media/codec2/components/raw/Android.bp
@@ -0,0 +1,9 @@
+cc_library_shared {
+    name: "libcodec2_soft_rawdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftRawDec.cpp"],
+}
diff --git a/media/codec2/components/raw/C2SoftRawDec.cpp b/media/codec2/components/raw/C2SoftRawDec.cpp
new file mode 100644
index 0000000..95eb909
--- /dev/null
+++ b/media/codec2/components/raw/C2SoftRawDec.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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 "C2SoftRawDec"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftRawDec.h"
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.raw.decoder";
+
+}  // namespace
+
+class C2SoftRawDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_RAW) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::output(0u, 44100))
+                .withFields({C2F(mSampleRate, value).inRange(8000, 384000)})
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::output(0u, 2))
+                .withFields({C2F(mChannelCount, value).inRange(1, 8)})
+                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::input(0u, 64000))
+                .withFields({C2F(mBitrate, value).inRange(1, 98304000)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 64 * 1024))
+                .build());
+
+        addParameter(
+                DefineParam(mPcmEncodingInfo, C2_PARAMKEY_PCM_ENCODING)
+                .withDefault(new C2StreamPcmEncodingInfo::output(0u, C2Config::PCM_16))
+                .withFields({C2F(mPcmEncodingInfo, value).oneOf({
+                     C2Config::PCM_16,
+                     C2Config::PCM_8,
+                     C2Config::PCM_FLOAT})
+                })
+                .withSetter((Setter<decltype(*mPcmEncodingInfo)>::StrictValueWithNoDeps))
+                .build());
+
+    }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+    std::shared_ptr<C2StreamPcmEncodingInfo::output> mPcmEncodingInfo;
+};
+
+C2SoftRawDec::C2SoftRawDec(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl) {
+}
+
+C2SoftRawDec::~C2SoftRawDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftRawDec::onInit() {
+    mSignalledEos = false;
+    return C2_OK;
+}
+
+c2_status_t C2SoftRawDec::onStop() {
+    mSignalledEos = false;
+    return C2_OK;
+}
+
+void C2SoftRawDec::onReset() {
+    (void)onStop();
+}
+
+void C2SoftRawDec::onRelease() {
+}
+
+c2_status_t C2SoftRawDec::onFlush_sm() {
+    return onStop();
+}
+
+void C2SoftRawDec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void)pool;
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+
+    if (mSignalledEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    ALOGV("in buffer attr. timestamp %d frameindex %d",
+          (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    if (!work->input.buffers.empty()) {
+        work->worklets.front()->output.buffers.push_back(work->input.buffers[0]);
+    }
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        mSignalledEos = true;
+        ALOGV("signalled EOS");
+    }
+}
+
+c2_status_t C2SoftRawDec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    return C2_OK;
+}
+
+class C2SoftRawDecFactory : public C2ComponentFactory {
+public:
+    C2SoftRawDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftRawDec(COMPONENT_NAME,
+                              id,
+                              std::make_shared<C2SoftRawDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftRawDec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftRawDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftRawDecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftRawDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/raw/C2SoftRawDec.h b/media/codec2/components/raw/C2SoftRawDec.h
new file mode 100644
index 0000000..7dfdec5
--- /dev/null
+++ b/media/codec2/components/raw/C2SoftRawDec.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 ANDROID_C2_SOFT_RAW_DEC_H_
+#define ANDROID_C2_SOFT_RAW_DEC_H_
+
+#include <SimpleC2Component.h>
+
+
+namespace android {
+
+struct C2SoftRawDec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftRawDec(const char* name, c2_node_id_t id,
+              const std::shared_ptr<IntfImpl>& intfImpl);
+    virtual ~C2SoftRawDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+private:
+    std::shared_ptr<IntfImpl> mIntf;
+    bool mSignalledEos;
+
+    C2_DO_NOT_COPY(C2SoftRawDec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_RAW_DEC_H_
diff --git a/media/common_time/MODULE_LICENSE_APACHE2 b/media/codec2/components/raw/MODULE_LICENSE_APACHE2
similarity index 100%
copy from media/common_time/MODULE_LICENSE_APACHE2
copy to media/codec2/components/raw/MODULE_LICENSE_APACHE2
diff --git a/media/common_time/NOTICE b/media/codec2/components/raw/NOTICE
similarity index 100%
copy from media/common_time/NOTICE
copy to media/codec2/components/raw/NOTICE
diff --git a/media/codec2/components/vorbis/Android.bp b/media/codec2/components/vorbis/Android.bp
new file mode 100644
index 0000000..a5f485d
--- /dev/null
+++ b/media/codec2/components/vorbis/Android.bp
@@ -0,0 +1,11 @@
+cc_library_shared {
+    name: "libcodec2_soft_vorbisdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftVorbisDec.cpp"],
+
+    shared_libs: ["libvorbisidec"],
+}
diff --git a/media/codec2/components/vorbis/C2SoftVorbisDec.cpp b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp
new file mode 100644
index 0000000..18e6db2
--- /dev/null
+++ b/media/codec2/components/vorbis/C2SoftVorbisDec.cpp
@@ -0,0 +1,484 @@
+/*
+ * 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 "C2SoftVorbisDec"
+#include <log/log.h>
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftVorbisDec.h"
+
+extern "C" {
+    #include <Tremolo/codec_internal.h>
+
+    int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb);
+    int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb);
+    int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb);
+}
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.vorbis.decoder";
+
+}  // namespace
+
+class C2SoftVorbisDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_VORBIS) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::output(0u, 48000))
+                .withFields({C2F(mSampleRate, value).inRange(8000, 96000)})
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::output(0u, 1))
+                .withFields({C2F(mChannelCount, value).inRange(1, 8)})
+                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::input(0u, 64000))
+                .withFields({C2F(mBitrate, value).inRange(32000, 500000)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 8192 * 2 * sizeof(int16_t)))
+                .build());
+    }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+};
+
+C2SoftVorbisDec::C2SoftVorbisDec(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mState(nullptr),
+      mVi(nullptr) {
+}
+
+C2SoftVorbisDec::~C2SoftVorbisDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftVorbisDec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+c2_status_t C2SoftVorbisDec::onStop() {
+    if (mState) {
+        vorbis_dsp_clear(mState);
+        delete mState;
+        mState = nullptr;
+    }
+
+    if (mVi) {
+        vorbis_info_clear(mVi);
+        delete mVi;
+        mVi = nullptr;
+    }
+    mNumFramesLeftOnPage = -1;
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+
+    return (initDecoder() == OK ? C2_OK : C2_CORRUPTED);
+}
+
+void C2SoftVorbisDec::onReset() {
+    (void)onStop();
+}
+
+void C2SoftVorbisDec::onRelease() {
+    if (mState) {
+        vorbis_dsp_clear(mState);
+        delete mState;
+        mState = nullptr;
+    }
+
+    if (mVi) {
+        vorbis_info_clear(mVi);
+        delete mVi;
+        mVi = nullptr;
+    }
+}
+
+status_t C2SoftVorbisDec::initDecoder() {
+    mVi = new vorbis_info{};
+    if (!mVi) return NO_MEMORY;
+    vorbis_info_clear(mVi);
+
+    mState = new vorbis_dsp_state{};
+    if (!mState) return NO_MEMORY;
+    vorbis_dsp_clear(mState);
+
+    mNumFramesLeftOnPage = -1;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mInfoUnpacked = false;
+    mBooksUnpacked = false;
+    return OK;
+}
+
+c2_status_t C2SoftVorbisDec::onFlush_sm() {
+    mNumFramesLeftOnPage = -1;
+    mSignalledOutputEos = false;
+    if (mState) vorbis_dsp_restart(mState);
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftVorbisDec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    return C2_OK;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+static void makeBitReader(
+        const void *data, size_t size,
+        ogg_buffer *buf, ogg_reference *ref, oggpack_buffer *bits) {
+    buf->data = (uint8_t *)data;
+    buf->size = size;
+    buf->refcount = 1;
+    buf->ptr.owner = nullptr;
+
+    ref->buffer = buf;
+    ref->begin = 0;
+    ref->length = size;
+    ref->next = nullptr;
+
+    oggpack_readinit(bits, ref);
+}
+
+// (CHECK!) multiframe is tricky. decode call doesnt return the number of bytes
+// consumed by the component. Also it is unclear why numPageFrames is being
+// tagged at the end of input buffers for new pages. Refer lines 297-300 in
+// SimpleDecodingSource.cpp
+void C2SoftVorbisDec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = rView.error();
+            return;
+        }
+    }
+
+    if (inSize == 0) {
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+          (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+    const uint8_t *data = rView.data() + inOffset;
+    int32_t numChannels  = mVi->channels;
+    int32_t samplingRate = mVi->rate;
+    /* Decode vorbis headers only once */
+    if (inSize > 7 && !memcmp(&data[1], "vorbis", 6) && (!mInfoUnpacked || !mBooksUnpacked)) {
+        if ((data[0] != 1) && (data[0] != 5)) {
+            ALOGE("unexpected type received %d", data[0]);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        ogg_buffer buf;
+        ogg_reference ref;
+        oggpack_buffer bits;
+
+        // skip 7 <type + "vorbis"> bytes
+        makeBitReader((const uint8_t *)data + 7, inSize - 7, &buf, &ref, &bits);
+        if (data[0] == 1) {
+            vorbis_info_init(mVi);
+            if (0 != _vorbis_unpack_info(mVi, &bits)) {
+                ALOGE("Encountered error while unpacking info");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            if (mVi->rate != samplingRate ||
+                    mVi->channels != numChannels) {
+                ALOGV("vorbis: rate/channels changed: %ld/%d", mVi->rate, mVi->channels);
+                samplingRate = mVi->rate;
+                numChannels = mVi->channels;
+
+                C2StreamSampleRateInfo::output sampleRateInfo(0u, samplingRate);
+                C2StreamChannelCountInfo::output channelCountInfo(0u, numChannels);
+                std::vector<std::unique_ptr<C2SettingResult>> failures;
+                c2_status_t err = mIntf->config(
+                        { &sampleRateInfo, &channelCountInfo },
+                        C2_MAY_BLOCK,
+                        &failures);
+                if (err == OK) {
+                    work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(sampleRateInfo));
+                    work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(channelCountInfo));
+                } else {
+                    ALOGE("Config Update failed");
+                    mSignalledError = true;
+                    work->result = C2_CORRUPTED;
+                    return;
+                }
+            }
+            mInfoUnpacked = true;
+        } else {
+            if (!mInfoUnpacked) {
+                ALOGE("Data with type:5 sent before sending type:1");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            if (0 != _vorbis_unpack_books(mVi, &bits)) {
+                ALOGE("Encountered error while unpacking books");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            if (0 != vorbis_dsp_init(mState, mVi)) {
+                ALOGE("Encountered error while dsp init");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            mBooksUnpacked = true;
+        }
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+
+    if (!mInfoUnpacked || !mBooksUnpacked) {
+        ALOGE("Missing CODEC_CONFIG data mInfoUnpacked: %d mBooksUnpack %d", mInfoUnpacked, mBooksUnpacked);
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    int32_t numPageFrames = 0;
+    if (inSize < sizeof(numPageFrames)) {
+        ALOGE("input header has size %zu, expected %zu", inSize, sizeof(numPageFrames));
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        return;
+    }
+    memcpy(&numPageFrames, data + inSize - sizeof(numPageFrames), sizeof(numPageFrames));
+    inSize -= sizeof(numPageFrames);
+    if (numPageFrames >= 0) {
+        mNumFramesLeftOnPage = numPageFrames;
+    }
+
+    ogg_buffer buf;
+    buf.data = const_cast<unsigned char*>(data);
+    buf.size = inSize;
+    buf.refcount = 1;
+    buf.ptr.owner = nullptr;
+
+    ogg_reference ref;
+    ref.buffer = &buf;
+    ref.begin = 0;
+    ref.length = buf.size;
+    ref.next = nullptr;
+
+    ogg_packet pack;
+    pack.packet = &ref;
+    pack.bytes = ref.length;
+    pack.b_o_s = 0;
+    pack.e_o_s = 0;
+    pack.granulepos = 0;
+    pack.packetno = 0;
+
+    size_t maxSamplesInBuffer = kMaxNumSamplesPerChannel * mVi->channels;
+    size_t outCapacity =  maxSamplesInBuffer * sizeof(int16_t);
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = wView.error();
+        return;
+    }
+
+    int numFrames = 0;
+    int ret = vorbis_dsp_synthesis(mState, &pack, 1);
+    if (0 != ret) {
+        ALOGD("vorbis_dsp_synthesis returned %d; ignored", ret);
+    } else {
+        numFrames = vorbis_dsp_pcmout(
+                mState,  reinterpret_cast<int16_t *> (wView.data()),
+                kMaxNumSamplesPerChannel);
+        if (numFrames < 0) {
+            ALOGD("vorbis_dsp_pcmout returned %d", numFrames);
+            numFrames = 0;
+        }
+    }
+
+    if (mNumFramesLeftOnPage >= 0) {
+        if (numFrames > mNumFramesLeftOnPage) {
+            ALOGV("discarding %d frames at end of page", numFrames - mNumFramesLeftOnPage);
+            numFrames = mNumFramesLeftOnPage;
+        }
+        mNumFramesLeftOnPage -= numFrames;
+    }
+
+    if (numFrames) {
+        int outSize = numFrames * sizeof(int16_t) * mVi->channels;
+
+        work->worklets.front()->output.flags = work->input.flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, 0, outSize));
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    } else {
+        fillEmptyWork(work);
+        block.reset();
+    }
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+    }
+}
+
+class C2SoftVorbisDecFactory : public C2ComponentFactory {
+public:
+    C2SoftVorbisDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftVorbisDec(COMPONENT_NAME,
+                              id,
+                              std::make_shared<C2SoftVorbisDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftVorbisDec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftVorbisDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftVorbisDecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftVorbisDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/vorbis/C2SoftVorbisDec.h b/media/codec2/components/vorbis/C2SoftVorbisDec.h
new file mode 100644
index 0000000..3bf7326
--- /dev/null
+++ b/media/codec2/components/vorbis/C2SoftVorbisDec.h
@@ -0,0 +1,70 @@
+/*
+ * 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_C2_SOFT_VORBIS_DEC_H_
+#define ANDROID_C2_SOFT_VORBIS_DEC_H_
+
+#include <SimpleC2Component.h>
+
+
+struct vorbis_dsp_state;
+struct vorbis_info;
+
+namespace android {
+
+struct C2SoftVorbisDec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftVorbisDec(const char *name, c2_node_id_t id,
+                 const std::shared_ptr<IntfImpl> &intfImpl);
+    virtual ~C2SoftVorbisDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+ private:
+    enum {
+        kMaxNumSamplesPerChannel = 8192,
+    };
+
+    std::shared_ptr<IntfImpl> mIntf;
+    vorbis_dsp_state *mState;
+    vorbis_info *mVi;
+
+    int32_t mNumFramesLeftOnPage;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+    bool mInfoUnpacked;
+    bool mBooksUnpacked;
+    status_t initDecoder();
+
+    C2_DO_NOT_COPY(C2SoftVorbisDec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_VORBIS_DEC_H_
+
diff --git a/media/common_time/MODULE_LICENSE_APACHE2 b/media/codec2/components/vorbis/MODULE_LICENSE_APACHE2
similarity index 100%
copy from media/common_time/MODULE_LICENSE_APACHE2
copy to media/codec2/components/vorbis/MODULE_LICENSE_APACHE2
diff --git a/media/common_time/NOTICE b/media/codec2/components/vorbis/NOTICE
similarity index 100%
copy from media/common_time/NOTICE
copy to media/codec2/components/vorbis/NOTICE
diff --git a/media/codec2/components/vpx/Android.bp b/media/codec2/components/vpx/Android.bp
new file mode 100644
index 0000000..34f5753
--- /dev/null
+++ b/media/codec2/components/vpx/Android.bp
@@ -0,0 +1,60 @@
+cc_library_shared {
+    name: "libcodec2_soft_vp9dec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftVpxDec.cpp"],
+
+    shared_libs: ["libvpx"],
+
+    cflags: [
+        "-DVP9",
+    ],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_vp8dec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftVpxDec.cpp"],
+
+    shared_libs: ["libvpx"],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_vp9enc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: [
+        "C2SoftVp9Enc.cpp",
+        "C2SoftVpxEnc.cpp",
+    ],
+
+    shared_libs: ["libvpx"],
+
+    cflags: ["-DVP9"],
+}
+
+cc_library_shared {
+    name: "libcodec2_soft_vp8enc",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: [
+        "C2SoftVp8Enc.cpp",
+        "C2SoftVpxEnc.cpp",
+    ],
+
+    shared_libs: ["libvpx"],
+}
+
diff --git a/media/codec2/components/vpx/C2SoftVp8Enc.cpp b/media/codec2/components/vpx/C2SoftVp8Enc.cpp
new file mode 100644
index 0000000..f18f5d0
--- /dev/null
+++ b/media/codec2/components/vpx/C2SoftVp8Enc.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftVp8Enc"
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include "C2SoftVp8Enc.h"
+
+namespace android {
+
+C2SoftVp8Enc::C2SoftVp8Enc(const char* name, c2_node_id_t id,
+                           const std::shared_ptr<IntfImpl>& intfImpl)
+    : C2SoftVpxEnc(name, id, intfImpl), mDCTPartitions(0), mProfile(1) {}
+
+void C2SoftVp8Enc::setCodecSpecificInterface() {
+    mCodecInterface = vpx_codec_vp8_cx();
+}
+
+void C2SoftVp8Enc::setCodecSpecificConfiguration() {
+    switch (mProfile) {
+        case 1:
+            mCodecConfiguration->g_profile = 0;
+            break;
+
+        case 2:
+            mCodecConfiguration->g_profile = 1;
+            break;
+
+        case 4:
+            mCodecConfiguration->g_profile = 2;
+            break;
+
+        case 8:
+            mCodecConfiguration->g_profile = 3;
+            break;
+
+        default:
+            mCodecConfiguration->g_profile = 0;
+    }
+}
+
+vpx_codec_err_t C2SoftVp8Enc::setCodecSpecificControls() {
+    vpx_codec_err_t codec_return = vpx_codec_control(mCodecContext,
+                                                     VP8E_SET_TOKEN_PARTITIONS,
+                                                     mDCTPartitions);
+    if (codec_return != VPX_CODEC_OK) {
+        ALOGE("Error setting dct partitions for vpx encoder.");
+    }
+    return codec_return;
+}
+
+class C2SoftVp8EncFactory : public C2ComponentFactory {
+public:
+    C2SoftVp8EncFactory()
+        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+              GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+            new C2SoftVp8Enc(COMPONENT_NAME, id,
+                             std::make_shared<C2SoftVpxEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+            new SimpleInterface<C2SoftVpxEnc::IntfImpl>(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftVpxEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftVp8EncFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftVp8EncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/vpx/C2SoftVp8Enc.h b/media/codec2/components/vpx/C2SoftVp8Enc.h
new file mode 100644
index 0000000..ed6f356
--- /dev/null
+++ b/media/codec2/components/vpx/C2SoftVp8Enc.h
@@ -0,0 +1,60 @@
+/*
+ * 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 ANDROID_C2_SOFT_VP8_ENC_H__
+#define ANDROID_C2_SOFT_VP8_ENC_H__
+
+#include "C2SoftVpxEnc.h"
+
+namespace android {
+
+// Exposes vp8 encoder as a c2 Component
+//
+// In addition to the base class settings, Only following encoder settings are
+// available:
+//    - token partitioning
+struct C2SoftVp8Enc : public C2SoftVpxEnc {
+    C2SoftVp8Enc(const char* name, c2_node_id_t id,
+                 const std::shared_ptr<IntfImpl>& intfImpl);
+
+ protected:
+    // Populates |mCodecInterface| with codec specific settings.
+    virtual void setCodecSpecificInterface();
+
+    // Sets codec specific configuration.
+    virtual void setCodecSpecificConfiguration();
+
+    // Initializes codec specific encoder settings.
+    virtual vpx_codec_err_t setCodecSpecificControls();
+
+ private:
+    // Max value supported for DCT partitions
+    static const uint32_t kMaxDCTPartitions = 3;
+
+    // vp8 specific configuration parameter
+    // that enables token partitioning of
+    // the stream into substreams
+    int32_t mDCTPartitions;
+
+    // C2 Profile parameter
+    int32_t mProfile;
+
+    C2_DO_NOT_COPY(C2SoftVp8Enc);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_VP8_ENC_H__
diff --git a/media/codec2/components/vpx/C2SoftVp9Enc.cpp b/media/codec2/components/vpx/C2SoftVp9Enc.cpp
new file mode 100644
index 0000000..740dbda
--- /dev/null
+++ b/media/codec2/components/vpx/C2SoftVp9Enc.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 "C2SoftVp9Enc"
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include "C2SoftVp9Enc.h"
+
+namespace android {
+
+C2SoftVp9Enc::C2SoftVp9Enc(const char* name, c2_node_id_t id,
+                           const std::shared_ptr<IntfImpl>& intfImpl)
+    : C2SoftVpxEnc(name, id, intfImpl),
+      mProfile(1),
+      mLevel(0),
+      mTileColumns(0),
+      mFrameParallelDecoding(false) {
+}
+
+void C2SoftVp9Enc::setCodecSpecificInterface() {
+    mCodecInterface = vpx_codec_vp9_cx();
+}
+
+void C2SoftVp9Enc::setCodecSpecificConfiguration() {
+    switch (mProfile) {
+        case 1:
+            mCodecConfiguration->g_profile = 0;
+            break;
+
+        case 2:
+            mCodecConfiguration->g_profile = 1;
+            break;
+
+        case 4:
+            mCodecConfiguration->g_profile = 2;
+            break;
+
+        case 8:
+            mCodecConfiguration->g_profile = 3;
+            break;
+
+        default:
+            mCodecConfiguration->g_profile = 0;
+    }
+}
+
+vpx_codec_err_t C2SoftVp9Enc::setCodecSpecificControls() {
+    vpx_codec_err_t codecReturn = vpx_codec_control(
+            mCodecContext, VP9E_SET_TILE_COLUMNS, mTileColumns);
+    if (codecReturn != VPX_CODEC_OK) {
+        ALOGE("Error setting VP9E_SET_TILE_COLUMNS to %d. vpx_codec_control() "
+              "returned %d", mTileColumns, codecReturn);
+        return codecReturn;
+    }
+    codecReturn = vpx_codec_control(
+            mCodecContext, VP9E_SET_FRAME_PARALLEL_DECODING,
+            mFrameParallelDecoding);
+    if (codecReturn != VPX_CODEC_OK) {
+        ALOGE("Error setting VP9E_SET_FRAME_PARALLEL_DECODING to %d."
+              "vpx_codec_control() returned %d", mFrameParallelDecoding,
+              codecReturn);
+        return codecReturn;
+    }
+    codecReturn = vpx_codec_control(mCodecContext, VP9E_SET_ROW_MT, 1);
+    if (codecReturn != VPX_CODEC_OK) {
+        ALOGE("Error setting VP9E_SET_ROW_MT to 1. vpx_codec_control() "
+              "returned %d", codecReturn);
+        return codecReturn;
+    }
+
+    // For VP9, we always set CPU_USED to 8 (because the realtime default is 0
+    // which is too slow).
+    codecReturn = vpx_codec_control(mCodecContext, VP8E_SET_CPUUSED, 8);
+    if (codecReturn != VPX_CODEC_OK) {
+        ALOGE("Error setting VP8E_SET_CPUUSED to 8. vpx_codec_control() "
+              "returned %d", codecReturn);
+        return codecReturn;
+    }
+    return codecReturn;
+}
+
+class C2SoftVp9EncFactory : public C2ComponentFactory {
+public:
+    C2SoftVp9EncFactory()
+        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+              GetCodec2PlatformComponentStore()->getParamReflector())) {}
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+            new C2SoftVp9Enc(COMPONENT_NAME, id,
+                             std::make_shared<C2SoftVpxEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+            new SimpleInterface<C2SoftVpxEnc::IntfImpl>(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftVpxEnc::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftVp9EncFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftVp9EncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/vpx/C2SoftVp9Enc.h b/media/codec2/components/vpx/C2SoftVp9Enc.h
new file mode 100644
index 0000000..77ef8fd
--- /dev/null
+++ b/media/codec2/components/vpx/C2SoftVp9Enc.h
@@ -0,0 +1,59 @@
+/*
+ * 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 ANDROID_C2_SOFT_VP9_ENC_H__
+#define ANDROID_C2_SOFT_VP9_ENC_H__
+
+#include "C2SoftVpxEnc.h"
+
+namespace android {
+
+// Exposes vp9 encoder as a c2 Component
+//
+// In addition to the base class settings, Only following encoder settings are
+// available:
+//    - tile rows
+//    - tile columns
+//    - frame parallel mode
+struct C2SoftVp9Enc : public C2SoftVpxEnc {
+    C2SoftVp9Enc(const char* name, c2_node_id_t id,
+                 const std::shared_ptr<IntfImpl>& intfImpl);
+
+ protected:
+    // Populates |mCodecInterface| with codec specific settings.
+    virtual void setCodecSpecificInterface();
+
+    // Sets codec specific configuration.
+    virtual void setCodecSpecificConfiguration();
+
+    // Initializes codec specific encoder settings.
+    virtual vpx_codec_err_t setCodecSpecificControls();
+
+ private:
+    // C2 Profile & Level parameter
+    int32_t mProfile;
+    int32_t mLevel __unused;
+
+    int32_t mTileColumns;
+
+    bool mFrameParallelDecoding;
+
+    C2_DO_NOT_COPY(C2SoftVp9Enc);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_VP9_ENC_H__
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp
new file mode 100644
index 0000000..a52ca15
--- /dev/null
+++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp
@@ -0,0 +1,939 @@
+/*
+ * 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 "C2SoftVpxDec"
+#include <log/log.h>
+
+#include <algorithm>
+
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftVpxDec.h"
+
+namespace android {
+
+#ifdef VP9
+constexpr char COMPONENT_NAME[] = "c2.android.vp9.decoder";
+#else
+constexpr char COMPONENT_NAME[] = "c2.android.vp8.decoder";
+#endif
+
+class C2SoftVpxDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_VIDEO,
+#ifdef VP9
+                MEDIA_MIMETYPE_VIDEO_VP9
+#else
+                MEDIA_MIMETYPE_VIDEO_VP8
+#endif
+                ) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+
+        // TODO: output latency and reordering
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 2048, 2),
+                    C2F(mSize, height).inRange(2, 2048, 2),
+                })
+                .withSetter(SizeSetter)
+                .build());
+
+#ifdef VP9
+        // TODO: Add C2Config::PROFILE_VP9_2HDR ??
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::input(0u,
+                        C2Config::PROFILE_VP9_0, C2Config::LEVEL_VP9_5))
+                .withFields({
+                    C2F(mProfileLevel, profile).oneOf({
+                            C2Config::PROFILE_VP9_0,
+                            C2Config::PROFILE_VP9_2}),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_VP9_1,
+                            C2Config::LEVEL_VP9_1_1,
+                            C2Config::LEVEL_VP9_2,
+                            C2Config::LEVEL_VP9_2_1,
+                            C2Config::LEVEL_VP9_3,
+                            C2Config::LEVEL_VP9_3_1,
+                            C2Config::LEVEL_VP9_4,
+                            C2Config::LEVEL_VP9_4_1,
+                            C2Config::LEVEL_VP9_5,
+                    })
+                })
+                .withSetter(ProfileLevelSetter, mSize)
+                .build());
+
+        mHdr10PlusInfoInput = C2StreamHdr10PlusInfo::input::AllocShared(0);
+        addParameter(
+                DefineParam(mHdr10PlusInfoInput, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO)
+                .withDefault(mHdr10PlusInfoInput)
+                .withFields({
+                    C2F(mHdr10PlusInfoInput, m.value).any(),
+                })
+                .withSetter(Hdr10PlusInfoInputSetter)
+                .build());
+
+        mHdr10PlusInfoOutput = C2StreamHdr10PlusInfo::output::AllocShared(0);
+        addParameter(
+                DefineParam(mHdr10PlusInfoOutput, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO)
+                .withDefault(mHdr10PlusInfoOutput)
+                .withFields({
+                    C2F(mHdr10PlusInfoOutput, m.value).any(),
+                })
+                .withSetter(Hdr10PlusInfoOutputSetter)
+                .build());
+
+#if 0
+        // sample BT.2020 static info
+        mHdrStaticInfo = std::make_shared<C2StreamHdrStaticInfo::output>();
+        mHdrStaticInfo->mastering = {
+            .red   = { .x = 0.708,  .y = 0.292 },
+            .green = { .x = 0.170,  .y = 0.797 },
+            .blue  = { .x = 0.131,  .y = 0.046 },
+            .white = { .x = 0.3127, .y = 0.3290 },
+            .maxLuminance = 1000,
+            .minLuminance = 0.1,
+        };
+        mHdrStaticInfo->maxCll = 1000;
+        mHdrStaticInfo->maxFall = 120;
+
+        mHdrStaticInfo->maxLuminance = 0; // disable static info
+
+        helper->addStructDescriptors<C2MasteringDisplayColorVolumeStruct, C2ColorXyStruct>();
+        addParameter(
+                DefineParam(mHdrStaticInfo, C2_PARAMKEY_HDR_STATIC_INFO)
+                .withDefault(mHdrStaticInfo)
+                .withFields({
+                    C2F(mHdrStaticInfo, mastering.red.x).inRange(0, 1),
+                    // TODO
+                })
+                .withSetter(HdrStaticInfoSetter)
+                .build());
+#endif
+#else
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withConstValue(new C2StreamProfileLevelInfo::input(0u,
+                        C2Config::PROFILE_UNUSED, C2Config::LEVEL_UNUSED))
+                .build());
+#endif
+
+        addParameter(
+                DefineParam(mMaxSize, C2_PARAMKEY_MAX_PICTURE_SIZE)
+                .withDefault(new C2StreamMaxPictureSizeTuning::output(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 2048, 2),
+                    C2F(mSize, height).inRange(2, 2048, 2),
+                })
+                .withSetter(MaxPictureSizeSetter, mSize)
+                .build());
+
+        addParameter(
+                DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 320 * 240 * 3 / 4))
+                .withFields({
+                    C2F(mMaxInputSize, value).any(),
+                })
+                .calculatedAs(MaxInputSizeSetter, mMaxSize)
+                .build());
+
+        C2ChromaOffsetStruct locations[1] = { C2ChromaOffsetStruct::ITU_YUV_420_0() };
+        std::shared_ptr<C2StreamColorInfo::output> defaultColorInfo =
+            C2StreamColorInfo::output::AllocShared(
+                    1u, 0u, 8u /* bitDepth */, C2Color::YUV_420);
+        memcpy(defaultColorInfo->m.locations, locations, sizeof(locations));
+
+        defaultColorInfo =
+            C2StreamColorInfo::output::AllocShared(
+                    { C2ChromaOffsetStruct::ITU_YUV_420_0() },
+                    0u, 8u /* bitDepth */, C2Color::YUV_420);
+        helper->addStructDescriptors<C2ChromaOffsetStruct>();
+
+        addParameter(
+                DefineParam(mColorInfo, C2_PARAMKEY_CODED_COLOR_INFO)
+                .withConstValue(defaultColorInfo)
+                .build());
+
+        addParameter(
+                DefineParam(mDefaultColorAspects, C2_PARAMKEY_DEFAULT_COLOR_ASPECTS)
+                .withDefault(new C2StreamColorAspectsTuning::output(
+                        0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+                        C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+                .withFields({
+                    C2F(mDefaultColorAspects, range).inRange(
+                                C2Color::RANGE_UNSPECIFIED,     C2Color::RANGE_OTHER),
+                    C2F(mDefaultColorAspects, primaries).inRange(
+                                C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+                    C2F(mDefaultColorAspects, transfer).inRange(
+                                C2Color::TRANSFER_UNSPECIFIED,  C2Color::TRANSFER_OTHER),
+                    C2F(mDefaultColorAspects, matrix).inRange(
+                                C2Color::MATRIX_UNSPECIFIED,    C2Color::MATRIX_OTHER)
+                })
+                .withSetter(DefaultColorAspectsSetter)
+                .build());
+
+        // TODO: support more formats?
+        addParameter(
+                DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+                .withConstValue(new C2StreamPixelFormatInfo::output(
+                                     0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+                .build());
+    }
+
+    static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output> &oldMe,
+                          C2P<C2StreamPictureSizeInfo::output> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R MaxPictureSizeSetter(bool mayBlock, C2P<C2StreamMaxPictureSizeTuning::output> &me,
+                                    const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        // TODO: get max width/height from the size's field helpers vs. hardcoding
+        me.set().width = c2_min(c2_max(me.v.width, size.v.width), 2048u);
+        me.set().height = c2_min(c2_max(me.v.height, size.v.height), 2048u);
+        return C2R::Ok();
+    }
+
+    static C2R MaxInputSizeSetter(bool mayBlock, C2P<C2StreamMaxBufferSizeInfo::input> &me,
+                                  const C2P<C2StreamMaxPictureSizeTuning::output> &maxSize) {
+        (void)mayBlock;
+        // assume compression ratio of 2
+        me.set().value = (((maxSize.v.width + 63) / 64) * ((maxSize.v.height + 63) / 64) * 3072);
+        return C2R::Ok();
+    }
+
+    static C2R DefaultColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsTuning::output> &me) {
+        (void)mayBlock;
+        if (me.v.range > C2Color::RANGE_OTHER) {
+            me.set().range = C2Color::RANGE_OTHER;
+        }
+        if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+            me.set().primaries = C2Color::PRIMARIES_OTHER;
+        }
+        if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+            me.set().transfer = C2Color::TRANSFER_OTHER;
+        }
+        if (me.v.matrix > C2Color::MATRIX_OTHER) {
+            me.set().matrix = C2Color::MATRIX_OTHER;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input> &me,
+                                  const C2P<C2StreamPictureSizeInfo::output> &size) {
+        (void)mayBlock;
+        (void)size;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+    std::shared_ptr<C2StreamColorAspectsTuning::output> getDefaultColorAspects_l() {
+        return mDefaultColorAspects;
+    }
+
+    static C2R Hdr10PlusInfoInputSetter(bool mayBlock, C2P<C2StreamHdr10PlusInfo::input> &me) {
+        (void)mayBlock;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+    static C2R Hdr10PlusInfoOutputSetter(bool mayBlock, C2P<C2StreamHdr10PlusInfo::output> &me) {
+        (void)mayBlock;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+
+private:
+    std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+    std::shared_ptr<C2StreamPictureSizeInfo::output> mSize;
+    std::shared_ptr<C2StreamMaxPictureSizeTuning::output> mMaxSize;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mMaxInputSize;
+    std::shared_ptr<C2StreamColorInfo::output> mColorInfo;
+    std::shared_ptr<C2StreamPixelFormatInfo::output> mPixelFormat;
+    std::shared_ptr<C2StreamColorAspectsTuning::output> mDefaultColorAspects;
+#ifdef VP9
+#if 0
+    std::shared_ptr<C2StreamHdrStaticInfo::output> mHdrStaticInfo;
+#endif
+    std::shared_ptr<C2StreamHdr10PlusInfo::input> mHdr10PlusInfoInput;
+    std::shared_ptr<C2StreamHdr10PlusInfo::output> mHdr10PlusInfoOutput;
+#endif
+};
+
+C2SoftVpxDec::ConverterThread::ConverterThread(
+        const std::shared_ptr<Mutexed<ConversionQueue>> &queue)
+    : Thread(false), mQueue(queue) {}
+
+bool C2SoftVpxDec::ConverterThread::threadLoop() {
+    Mutexed<ConversionQueue>::Locked queue(*mQueue);
+    if (queue->entries.empty()) {
+        queue.waitForCondition(queue->cond);
+        if (queue->entries.empty()) {
+            return true;
+        }
+    }
+    std::function<void()> convert = queue->entries.front();
+    queue->entries.pop_front();
+    if (!queue->entries.empty()) {
+        queue->cond.signal();
+    }
+    queue.unlock();
+
+    convert();
+
+    queue.lock();
+    if (--queue->numPending == 0u) {
+        queue->cond.broadcast();
+    }
+    return true;
+}
+
+C2SoftVpxDec::C2SoftVpxDec(
+        const char *name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mCodecCtx(nullptr),
+      mCoreCount(1),
+      mQueue(new Mutexed<ConversionQueue>) {
+}
+
+C2SoftVpxDec::~C2SoftVpxDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftVpxDec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftVpxDec::onStop() {
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+
+    return C2_OK;
+}
+
+void C2SoftVpxDec::onReset() {
+    (void)onStop();
+    c2_status_t err = onFlush_sm();
+    if (err != C2_OK)
+    {
+        ALOGW("Failed to flush decoder. Try to hard reset decoder");
+        destroyDecoder();
+        (void)initDecoder();
+    }
+}
+
+void C2SoftVpxDec::onRelease() {
+    destroyDecoder();
+}
+
+c2_status_t C2SoftVpxDec::onFlush_sm() {
+    if (mFrameParallelMode) {
+        // Flush decoder by passing nullptr data ptr and 0 size.
+        // Ideally, this should never fail.
+        if (vpx_codec_decode(mCodecCtx, nullptr, 0, nullptr, 0)) {
+            ALOGE("Failed to flush on2 decoder.");
+            return C2_CORRUPTED;
+        }
+    }
+
+    // Drop all the decoded frames in decoder.
+    vpx_codec_iter_t iter = nullptr;
+    while (vpx_codec_get_frame(mCodecCtx, &iter)) {
+    }
+
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    return C2_OK;
+}
+
+static int GetCPUCoreCount() {
+    int cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGV("Number of CPU cores: %d", cpuCoreCount);
+    return cpuCoreCount;
+}
+
+status_t C2SoftVpxDec::initDecoder() {
+#ifdef VP9
+    mMode = MODE_VP9;
+#else
+    mMode = MODE_VP8;
+#endif
+
+    mWidth = 320;
+    mHeight = 240;
+    mFrameParallelMode = false;
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+
+    if (!mCodecCtx) {
+        mCodecCtx = new vpx_codec_ctx_t;
+    }
+    if (!mCodecCtx) {
+        ALOGE("mCodecCtx is null");
+        return NO_MEMORY;
+    }
+
+    vpx_codec_dec_cfg_t cfg;
+    memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t));
+    cfg.threads = mCoreCount = GetCPUCoreCount();
+
+    vpx_codec_flags_t flags;
+    memset(&flags, 0, sizeof(vpx_codec_flags_t));
+    if (mFrameParallelMode) flags |= VPX_CODEC_USE_FRAME_THREADING;
+
+    vpx_codec_err_t vpx_err;
+    if ((vpx_err = vpx_codec_dec_init(
+                 mCodecCtx, mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo,
+                 &cfg, flags))) {
+        ALOGE("on2 decoder failed to initialize. (%d)", vpx_err);
+        return UNKNOWN_ERROR;
+    }
+
+    if (mMode == MODE_VP9) {
+        using namespace std::string_literals;
+        for (int i = 0; i < mCoreCount; ++i) {
+            sp<ConverterThread> thread(new ConverterThread(mQueue));
+            mConverterThreads.push_back(thread);
+            if (thread->run(("vp9conv #"s + std::to_string(i)).c_str(),
+                            ANDROID_PRIORITY_AUDIO) != OK) {
+                return UNKNOWN_ERROR;
+            }
+        }
+    }
+
+    return OK;
+}
+
+status_t C2SoftVpxDec::destroyDecoder() {
+    if  (mCodecCtx) {
+        vpx_codec_destroy(mCodecCtx);
+        delete mCodecCtx;
+        mCodecCtx = nullptr;
+    }
+    bool running = true;
+    for (const sp<ConverterThread> &thread : mConverterThreads) {
+        thread->requestExit();
+    }
+    while (running) {
+        mQueue->lock()->cond.broadcast();
+        running = false;
+        for (const sp<ConverterThread> &thread : mConverterThreads) {
+            if (thread->isRunning()) {
+                running = true;
+                break;
+            }
+        }
+    }
+    mConverterThreads.clear();
+
+    return OK;
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftVpxDec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work,
+                           const std::shared_ptr<C2GraphicBlock> &block) {
+    std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(block,
+                                                           C2Rect(mWidth, mHeight));
+    auto fillWork = [buffer, index, intf = this->mIntf](
+            const std::unique_ptr<C2Work> &work) {
+        uint32_t flags = 0;
+        if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
+                (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
+            flags |= C2FrameData::FLAG_END_OF_STREAM;
+            ALOGV("signalling eos");
+        }
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+
+        for (const std::unique_ptr<C2Param> &param: work->input.configUpdate) {
+            if (param) {
+                C2StreamHdr10PlusInfo::input *hdr10PlusInfo =
+                        C2StreamHdr10PlusInfo::input::From(param.get());
+
+                if (hdr10PlusInfo != nullptr) {
+                    std::vector<std::unique_ptr<C2SettingResult>> failures;
+                    std::unique_ptr<C2Param> outParam = C2Param::CopyAsStream(
+                            *param.get(), true /*output*/, param->stream());
+                    c2_status_t err = intf->config(
+                            { outParam.get() }, C2_MAY_BLOCK, &failures);
+                    if (err == C2_OK) {
+                        work->worklets.front()->output.configUpdate.push_back(
+                                C2Param::Copy(*outParam.get()));
+                    } else {
+                        ALOGE("finishWork: Config update size failed");
+                    }
+                    break;
+                }
+            }
+        }
+    };
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        fillWork(work);
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+void C2SoftVpxDec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    size_t inOffset = 0u;
+    size_t inSize = 0u;
+    C2ReadView rView = mDummyReadView;
+    if (!work->input.buffers.empty()) {
+        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        inSize = rView.capacity();
+        if (inSize && rView.error()) {
+            ALOGE("read view map failed %d", rView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+
+    bool codecConfig = ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) !=0);
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+
+    // Software VP9 Decoder does not need the Codec Specific Data (CSD)
+    // (specified in http://www.webmproject.org/vp9/profiles/). Ignore it if
+    // it was passed.
+    if (codecConfig) {
+        // Ignore CSD buffer for VP9.
+        if (mMode == MODE_VP9) {
+            fillEmptyWork(work);
+            return;
+        } else {
+            // Tolerate the CSD buffer for VP8. This is a workaround
+            // for b/28689536. continue
+            ALOGW("WARNING: Got CSD buffer for VP8. Continue");
+        }
+    }
+
+    int64_t frameIndex = work->input.ordinal.frameIndex.peekll();
+
+    if (inSize) {
+        uint8_t *bitstream = const_cast<uint8_t *>(rView.data() + inOffset);
+        vpx_codec_err_t err = vpx_codec_decode(
+                mCodecCtx, bitstream, inSize, &frameIndex, 0);
+        if (err != VPX_CODEC_OK) {
+            ALOGE("on2 decoder failed to decode frame. err: %d", err);
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    }
+
+    (void)outputBuffer(pool, work);
+
+    if (eos) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+        mSignalledOutputEos = true;
+    } else if (!inSize) {
+        fillEmptyWork(work);
+    }
+}
+
+static void copyOutputBufferToYuvPlanarFrame(
+        uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
+        size_t srcYStride, size_t srcUStride, size_t srcVStride,
+        size_t dstYStride, size_t dstUVStride,
+        uint32_t width, uint32_t height) {
+    uint8_t *dstStart = dst;
+
+    for (size_t i = 0; i < height; ++i) {
+         memcpy(dst, srcY, width);
+         srcY += srcYStride;
+         dst += dstYStride;
+    }
+
+    dst = dstStart + dstYStride * height;
+    for (size_t i = 0; i < height / 2; ++i) {
+         memcpy(dst, srcV, width / 2);
+         srcV += srcVStride;
+         dst += dstUVStride;
+    }
+
+    dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
+    for (size_t i = 0; i < height / 2; ++i) {
+         memcpy(dst, srcU, width / 2);
+         srcU += srcUStride;
+         dst += dstUVStride;
+    }
+}
+
+static void convertYUV420Planar16ToY410(uint32_t *dst,
+        const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV,
+        size_t srcYStride, size_t srcUStride, size_t srcVStride,
+        size_t dstStride, size_t width, size_t height) {
+
+    // Converting two lines at a time, slightly faster
+    for (size_t y = 0; y < height; y += 2) {
+        uint32_t *dstTop = (uint32_t *) dst;
+        uint32_t *dstBot = (uint32_t *) (dst + dstStride);
+        uint16_t *ySrcTop = (uint16_t*) srcY;
+        uint16_t *ySrcBot = (uint16_t*) (srcY + srcYStride);
+        uint16_t *uSrc = (uint16_t*) srcU;
+        uint16_t *vSrc = (uint16_t*) srcV;
+
+        uint32_t u01, v01, y01, y23, y45, y67, uv0, uv1;
+        size_t x = 0;
+        for (; x < width - 3; x += 4) {
+
+            u01 = *((uint32_t*)uSrc); uSrc += 2;
+            v01 = *((uint32_t*)vSrc); vSrc += 2;
+
+            y01 = *((uint32_t*)ySrcTop); ySrcTop += 2;
+            y23 = *((uint32_t*)ySrcTop); ySrcTop += 2;
+            y45 = *((uint32_t*)ySrcBot); ySrcBot += 2;
+            y67 = *((uint32_t*)ySrcBot); ySrcBot += 2;
+
+            uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
+            uv1 = (u01 >> 16) | ((v01 >> 16) << 20);
+
+            *dstTop++ = 3 << 30 | ((y01 & 0x3FF) << 10) | uv0;
+            *dstTop++ = 3 << 30 | ((y01 >> 16) << 10) | uv0;
+            *dstTop++ = 3 << 30 | ((y23 & 0x3FF) << 10) | uv1;
+            *dstTop++ = 3 << 30 | ((y23 >> 16) << 10) | uv1;
+
+            *dstBot++ = 3 << 30 | ((y45 & 0x3FF) << 10) | uv0;
+            *dstBot++ = 3 << 30 | ((y45 >> 16) << 10) | uv0;
+            *dstBot++ = 3 << 30 | ((y67 & 0x3FF) << 10) | uv1;
+            *dstBot++ = 3 << 30 | ((y67 >> 16) << 10) | uv1;
+        }
+
+        // There should be at most 2 more pixels to process. Note that we don't
+        // need to consider odd case as the buffer is always aligned to even.
+        if (x < width) {
+            u01 = *uSrc;
+            v01 = *vSrc;
+            y01 = *((uint32_t*)ySrcTop);
+            y45 = *((uint32_t*)ySrcBot);
+            uv0 = (u01 & 0x3FF) | ((v01 & 0x3FF) << 20);
+            *dstTop++ = ((y01 & 0x3FF) << 10) | uv0;
+            *dstTop++ = ((y01 >> 16) << 10) | uv0;
+            *dstBot++ = ((y45 & 0x3FF) << 10) | uv0;
+            *dstBot++ = ((y45 >> 16) << 10) | uv0;
+        }
+
+        srcY += srcYStride * 2;
+        srcU += srcUStride;
+        srcV += srcVStride;
+        dst += dstStride * 2;
+    }
+
+    return;
+}
+
+static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst,
+        const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV,
+        size_t srcYStride, size_t srcUStride, size_t srcVStride,
+        size_t dstYStride, size_t dstUVStride, size_t width, size_t height) {
+
+    uint8_t *dstY = (uint8_t *)dst;
+    size_t dstYSize = dstYStride * height;
+    size_t dstUVSize = dstUVStride * height / 2;
+    uint8_t *dstV = dstY + dstYSize;
+    uint8_t *dstU = dstV + dstUVSize;
+
+    for (size_t y = 0; y < height; ++y) {
+        for (size_t x = 0; x < width; ++x) {
+            dstY[x] = (uint8_t)(srcY[x] >> 2);
+        }
+
+        srcY += srcYStride;
+        dstY += dstYStride;
+    }
+
+    for (size_t y = 0; y < (height + 1) / 2; ++y) {
+        for (size_t x = 0; x < (width + 1) / 2; ++x) {
+            dstU[x] = (uint8_t)(srcU[x] >> 2);
+            dstV[x] = (uint8_t)(srcV[x] >> 2);
+        }
+
+        srcU += srcUStride;
+        srcV += srcVStride;
+        dstU += dstUVStride;
+        dstV += dstUVStride;
+    }
+    return;
+}
+bool C2SoftVpxDec::outputBuffer(
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work)
+{
+    if (!(work && pool)) return false;
+
+    vpx_codec_iter_t iter = nullptr;
+    vpx_image_t *img = vpx_codec_get_frame(mCodecCtx, &iter);
+
+    if (!img) return false;
+
+    if (img->d_w != mWidth || img->d_h != mHeight) {
+        mWidth = img->d_w;
+        mHeight = img->d_h;
+
+        C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
+        std::vector<std::unique_ptr<C2SettingResult>> failures;
+        c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
+        if (err == C2_OK) {
+            work->worklets.front()->output.configUpdate.push_back(
+                C2Param::Copy(size));
+        } else {
+            ALOGE("Config update size failed");
+            mSignalledError = true;
+            work->workletsProcessed = 1u;
+            work->result = C2_CORRUPTED;
+            return false;
+        }
+
+    }
+    CHECK(img->fmt == VPX_IMG_FMT_I420 || img->fmt == VPX_IMG_FMT_I42016);
+
+    std::shared_ptr<C2GraphicBlock> block;
+    uint32_t format = HAL_PIXEL_FORMAT_YV12;
+    if (img->fmt == VPX_IMG_FMT_I42016) {
+        IntfImpl::Lock lock = mIntf->lock();
+        std::shared_ptr<C2StreamColorAspectsTuning::output> defaultColorAspects = mIntf->getDefaultColorAspects_l();
+
+        if (defaultColorAspects->primaries == C2Color::PRIMARIES_BT2020 &&
+            defaultColorAspects->matrix == C2Color::MATRIX_BT2020 &&
+            defaultColorAspects->transfer == C2Color::TRANSFER_ST2084) {
+            format = HAL_PIXEL_FORMAT_RGBA_1010102;
+        }
+    }
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight, format, usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+        work->result = err;
+        return false;
+    }
+
+    C2GraphicView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("graphic view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return false;
+    }
+
+    ALOGV("provided (%dx%d) required (%dx%d), out frameindex %d",
+           block->width(), block->height(), mWidth, mHeight, (int)*(int64_t *)img->user_priv);
+
+    uint8_t *dst = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_Y]);
+    size_t srcYStride = img->stride[VPX_PLANE_Y];
+    size_t srcUStride = img->stride[VPX_PLANE_U];
+    size_t srcVStride = img->stride[VPX_PLANE_V];
+    C2PlanarLayout layout = wView.layout();
+    size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+    size_t dstUVStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+
+    if (img->fmt == VPX_IMG_FMT_I42016) {
+        const uint16_t *srcY = (const uint16_t *)img->planes[VPX_PLANE_Y];
+        const uint16_t *srcU = (const uint16_t *)img->planes[VPX_PLANE_U];
+        const uint16_t *srcV = (const uint16_t *)img->planes[VPX_PLANE_V];
+
+        if (format == HAL_PIXEL_FORMAT_RGBA_1010102) {
+            Mutexed<ConversionQueue>::Locked queue(*mQueue);
+            size_t i = 0;
+            constexpr size_t kHeight = 64;
+            for (; i < mHeight; i += kHeight) {
+                queue->entries.push_back(
+                        [dst, srcY, srcU, srcV,
+                         srcYStride, srcUStride, srcVStride, dstYStride,
+                         width = mWidth, height = std::min(mHeight - i, kHeight)] {
+                            convertYUV420Planar16ToY410(
+                                    (uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2,
+                                    srcUStride / 2, srcVStride / 2, dstYStride / sizeof(uint32_t),
+                                    width, height);
+                        });
+                srcY += srcYStride / 2 * kHeight;
+                srcU += srcUStride / 2 * (kHeight / 2);
+                srcV += srcVStride / 2 * (kHeight / 2);
+                dst += dstYStride * kHeight;
+            }
+            CHECK_EQ(0u, queue->numPending);
+            queue->numPending = queue->entries.size();
+            while (queue->numPending > 0) {
+                queue->cond.signal();
+                queue.waitForCondition(queue->cond);
+            }
+        } else {
+            convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2,
+                                                srcUStride / 2, srcVStride / 2,
+                                                dstYStride, dstUVStride,
+                                                mWidth, mHeight);
+        }
+    } else {
+        const uint8_t *srcY = (const uint8_t *)img->planes[VPX_PLANE_Y];
+        const uint8_t *srcU = (const uint8_t *)img->planes[VPX_PLANE_U];
+        const uint8_t *srcV = (const uint8_t *)img->planes[VPX_PLANE_V];
+        copyOutputBufferToYuvPlanarFrame(
+                dst, srcY, srcU, srcV,
+                srcYStride, srcUStride, srcVStride,
+                dstYStride, dstUVStride,
+                mWidth, mHeight);
+    }
+    finishWork(*(int64_t *)img->user_priv, work, std::move(block));
+    return true;
+}
+
+c2_status_t C2SoftVpxDec::drainInternal(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work) {
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    while ((outputBuffer(pool, work))) {
+    }
+
+    if (drainMode == DRAIN_COMPONENT_WITH_EOS &&
+            work && work->workletsProcessed == 0u) {
+        fillEmptyWork(work);
+    }
+
+    return C2_OK;
+}
+c2_status_t C2SoftVpxDec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftVpxFactory : public C2ComponentFactory {
+public:
+    C2SoftVpxFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+        GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+            new C2SoftVpxDec(COMPONENT_NAME, id,
+                          std::make_shared<C2SoftVpxDec::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+            new SimpleInterface<C2SoftVpxDec::IntfImpl>(
+                COMPONENT_NAME, id,
+                std::make_shared<C2SoftVpxDec::IntfImpl>(mHelper)),
+            deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftVpxFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftVpxFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.h b/media/codec2/components/vpx/C2SoftVpxDec.h
new file mode 100644
index 0000000..e51bcee
--- /dev/null
+++ b/media/codec2/components/vpx/C2SoftVpxDec.h
@@ -0,0 +1,101 @@
+/*
+ * 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_C2_SOFT_VPX_DEC_H_
+#define ANDROID_C2_SOFT_VPX_DEC_H_
+
+#include <SimpleC2Component.h>
+
+
+#include "vpx/vpx_decoder.h"
+#include "vpx/vp8dx.h"
+
+namespace android {
+
+struct C2SoftVpxDec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftVpxDec(const char* name, c2_node_id_t id,
+              const std::shared_ptr<IntfImpl>& intfImpl);
+    virtual ~C2SoftVpxDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+ private:
+    enum {
+        MODE_VP8,
+        MODE_VP9,
+    } mMode;
+
+    struct ConversionQueue;
+
+    class ConverterThread : public Thread {
+    public:
+        explicit ConverterThread(
+                const std::shared_ptr<Mutexed<ConversionQueue>> &queue);
+        ~ConverterThread() override = default;
+        bool threadLoop() override;
+
+    private:
+        std::shared_ptr<Mutexed<ConversionQueue>> mQueue;
+    };
+
+    std::shared_ptr<IntfImpl> mIntf;
+    vpx_codec_ctx_t *mCodecCtx;
+    bool mFrameParallelMode;  // Frame parallel is only supported by VP9 decoder.
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+
+    int mCoreCount;
+    struct ConversionQueue {
+        std::list<std::function<void()>> entries;
+        Condition cond;
+        size_t numPending{0u};
+    };
+    std::shared_ptr<Mutexed<ConversionQueue>> mQueue;
+    std::vector<sp<ConverterThread>> mConverterThreads;
+
+    status_t initDecoder();
+    status_t destroyDecoder();
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work,
+                    const std::shared_ptr<C2GraphicBlock> &block);
+    bool outputBuffer(
+            const std::shared_ptr<C2BlockPool> &pool,
+            const std::unique_ptr<C2Work> &work);
+    c2_status_t drainInternal(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool,
+            const std::unique_ptr<C2Work> &work);
+
+    C2_DO_NOT_COPY(C2SoftVpxDec);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_VPX_DEC_H_
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
new file mode 100644
index 0000000..6dab70b
--- /dev/null
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
@@ -0,0 +1,670 @@
+/*
+ * 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 "C2SoftVpxEnc"
+#include <log/log.h>
+#include <utils/misc.h>
+
+#include <media/hardware/VideoAPI.h>
+
+#include <Codec2BufferUtils.h>
+#include <C2Debug.h>
+#include "C2SoftVpxEnc.h"
+
+#ifndef INT32_MAX
+#define INT32_MAX   2147483647
+#endif
+
+namespace android {
+
+#if 0
+static size_t getCpuCoreCount() {
+    long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGV("Number of CPU cores: %ld", cpuCoreCount);
+    return (size_t)cpuCoreCount;
+}
+#endif
+
+C2SoftVpxEnc::C2SoftVpxEnc(const char* name, c2_node_id_t id,
+                           const std::shared_ptr<IntfImpl>& intfImpl)
+    : SimpleC2Component(
+          std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+      mIntf(intfImpl),
+      mCodecContext(nullptr),
+      mCodecConfiguration(nullptr),
+      mCodecInterface(nullptr),
+      mStrideAlign(2),
+      mColorFormat(VPX_IMG_FMT_I420),
+      mBitrateControlMode(VPX_VBR),
+      mErrorResilience(false),
+      mMinQuantizer(0),
+      mMaxQuantizer(0),
+      mTemporalLayers(0),
+      mTemporalPatternType(VPXTemporalLayerPatternNone),
+      mTemporalPatternLength(0),
+      mTemporalPatternIdx(0),
+      mLastTimestamp(0x7FFFFFFFFFFFFFFFull),
+      mSignalledOutputEos(false),
+      mSignalledError(false) {
+    memset(mTemporalLayerBitrateRatio, 0, sizeof(mTemporalLayerBitrateRatio));
+    mTemporalLayerBitrateRatio[0] = 100;
+}
+
+C2SoftVpxEnc::~C2SoftVpxEnc() {
+    onRelease();
+}
+
+c2_status_t C2SoftVpxEnc::onInit() {
+    status_t err = initEncoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+void C2SoftVpxEnc::onRelease() {
+    if (mCodecContext) {
+        vpx_codec_destroy(mCodecContext);
+        delete mCodecContext;
+        mCodecContext = nullptr;
+    }
+
+    if (mCodecConfiguration) {
+        delete mCodecConfiguration;
+        mCodecConfiguration = nullptr;
+    }
+
+    // this one is not allocated by us
+    mCodecInterface = nullptr;
+}
+
+c2_status_t C2SoftVpxEnc::onStop() {
+    onRelease();
+    mLastTimestamp = 0x7FFFFFFFFFFFFFFFLL;
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+    return C2_OK;
+}
+
+void C2SoftVpxEnc::onReset() {
+    (void)onStop();
+}
+
+c2_status_t C2SoftVpxEnc::onFlush_sm() {
+    return onStop();
+}
+
+status_t C2SoftVpxEnc::initEncoder() {
+    vpx_codec_err_t codec_return;
+    status_t result = UNKNOWN_ERROR;
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        mSize = mIntf->getSize_l();
+        mBitrate = mIntf->getBitrate_l();
+        mBitrateMode = mIntf->getBitrateMode_l();
+        mFrameRate = mIntf->getFrameRate_l();
+        mIntraRefresh = mIntf->getIntraRefresh_l();
+        mRequestSync = mIntf->getRequestSync_l();
+        mTemporalLayers = mIntf->getTemporalLayers_l()->m.layerCount;
+    }
+
+    switch (mBitrateMode->value) {
+        case C2Config::BITRATE_CONST:
+            mBitrateControlMode = VPX_CBR;
+            break;
+        case C2Config::BITRATE_VARIABLE:
+        [[fallthrough]];
+        default:
+            mBitrateControlMode = VPX_VBR;
+            break;
+    }
+
+    setCodecSpecificInterface();
+    if (!mCodecInterface) goto CleanUp;
+
+    ALOGD("VPx: initEncoder. BRMode: %u. TSLayers: %zu. KF: %u. QP: %u - %u",
+          (uint32_t)mBitrateControlMode, mTemporalLayers, mIntf->getSyncFramePeriod(),
+          mMinQuantizer, mMaxQuantizer);
+
+    mCodecConfiguration = new vpx_codec_enc_cfg_t;
+    if (!mCodecConfiguration) goto CleanUp;
+    codec_return = vpx_codec_enc_config_default(mCodecInterface,
+                                                mCodecConfiguration,
+                                                0);
+    if (codec_return != VPX_CODEC_OK) {
+        ALOGE("Error populating default configuration for vpx encoder.");
+        goto CleanUp;
+    }
+
+    mCodecConfiguration->g_w = mSize->width;
+    mCodecConfiguration->g_h = mSize->height;
+    //mCodecConfiguration->g_threads = getCpuCoreCount();
+    mCodecConfiguration->g_threads = 0;
+    mCodecConfiguration->g_error_resilient = mErrorResilience;
+
+    // timebase unit is microsecond
+    // g_timebase is in seconds (i.e. 1/1000000 seconds)
+    mCodecConfiguration->g_timebase.num = 1;
+    mCodecConfiguration->g_timebase.den = 1000000;
+    // rc_target_bitrate is in kbps, mBitrate in bps
+    mCodecConfiguration->rc_target_bitrate = (mBitrate->value + 500) / 1000;
+    mCodecConfiguration->rc_end_usage = mBitrateControlMode;
+    // Disable frame drop - not allowed in MediaCodec now.
+    mCodecConfiguration->rc_dropframe_thresh = 0;
+    // Disable lagged encoding.
+    mCodecConfiguration->g_lag_in_frames = 0;
+    if (mBitrateControlMode == VPX_CBR) {
+        // Disable spatial resizing.
+        mCodecConfiguration->rc_resize_allowed = 0;
+        // Single-pass mode.
+        mCodecConfiguration->g_pass = VPX_RC_ONE_PASS;
+        // Maximum amount of bits that can be subtracted from the target
+        // bitrate - expressed as percentage of the target bitrate.
+        mCodecConfiguration->rc_undershoot_pct = 100;
+        // Maximum amount of bits that can be added to the target
+        // bitrate - expressed as percentage of the target bitrate.
+        mCodecConfiguration->rc_overshoot_pct = 15;
+        // Initial value of the buffer level in ms.
+        mCodecConfiguration->rc_buf_initial_sz = 500;
+        // Amount of data that the encoder should try to maintain in ms.
+        mCodecConfiguration->rc_buf_optimal_sz = 600;
+        // The amount of data that may be buffered by the decoding
+        // application in ms.
+        mCodecConfiguration->rc_buf_sz = 1000;
+        // Enable error resilience - needed for packet loss.
+        mCodecConfiguration->g_error_resilient = 1;
+        // Maximum key frame interval - for CBR boost to 3000
+        mCodecConfiguration->kf_max_dist = 3000;
+        // Encoder determines optimal key frame placement automatically.
+        mCodecConfiguration->kf_mode = VPX_KF_AUTO;
+    }
+
+    // Frames temporal pattern - for now WebRTC like pattern is only supported.
+    switch (mTemporalLayers) {
+        case 0:
+            mTemporalPatternLength = 0;
+            break;
+        case 1:
+            mCodecConfiguration->ts_number_layers = 1;
+            mCodecConfiguration->ts_rate_decimator[0] = 1;
+            mCodecConfiguration->ts_periodicity = 1;
+            mCodecConfiguration->ts_layer_id[0] = 0;
+            mTemporalPattern[0] = kTemporalUpdateLastRefAll;
+            mTemporalPatternLength = 1;
+            break;
+        case 2:
+            mCodecConfiguration->ts_number_layers = 2;
+            mCodecConfiguration->ts_rate_decimator[0] = 2;
+            mCodecConfiguration->ts_rate_decimator[1] = 1;
+            mCodecConfiguration->ts_periodicity = 2;
+            mCodecConfiguration->ts_layer_id[0] = 0;
+            mCodecConfiguration->ts_layer_id[1] = 1;
+            mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef;
+            mTemporalPattern[1] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
+            mTemporalPattern[2] = kTemporalUpdateLastRefAltRef;
+            mTemporalPattern[3] = kTemporalUpdateGoldenRefAltRef;
+            mTemporalPattern[4] = kTemporalUpdateLastRefAltRef;
+            mTemporalPattern[5] = kTemporalUpdateGoldenRefAltRef;
+            mTemporalPattern[6] = kTemporalUpdateLastRefAltRef;
+            mTemporalPattern[7] = kTemporalUpdateNone;
+            mTemporalPatternLength = 8;
+            break;
+        case 3:
+            mCodecConfiguration->ts_number_layers = 3;
+            mCodecConfiguration->ts_rate_decimator[0] = 4;
+            mCodecConfiguration->ts_rate_decimator[1] = 2;
+            mCodecConfiguration->ts_rate_decimator[2] = 1;
+            mCodecConfiguration->ts_periodicity = 4;
+            mCodecConfiguration->ts_layer_id[0] = 0;
+            mCodecConfiguration->ts_layer_id[1] = 2;
+            mCodecConfiguration->ts_layer_id[2] = 1;
+            mCodecConfiguration->ts_layer_id[3] = 2;
+            mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef;
+            mTemporalPattern[1] = kTemporalUpdateNoneNoRefGoldenRefAltRef;
+            mTemporalPattern[2] = kTemporalUpdateGoldenWithoutDependencyRefAltRef;
+            mTemporalPattern[3] = kTemporalUpdateNone;
+            mTemporalPattern[4] = kTemporalUpdateLastRefAltRef;
+            mTemporalPattern[5] = kTemporalUpdateNone;
+            mTemporalPattern[6] = kTemporalUpdateGoldenRefAltRef;
+            mTemporalPattern[7] = kTemporalUpdateNone;
+            mTemporalPatternLength = 8;
+            break;
+        default:
+            ALOGE("Wrong number of temporal layers %zu", mTemporalLayers);
+            goto CleanUp;
+    }
+    // Set bitrate values for each layer
+    for (size_t i = 0; i < mCodecConfiguration->ts_number_layers; i++) {
+        mCodecConfiguration->ts_target_bitrate[i] =
+            mCodecConfiguration->rc_target_bitrate *
+            mTemporalLayerBitrateRatio[i] / 100;
+    }
+    if (mIntf->getSyncFramePeriod() >= 0) {
+        mCodecConfiguration->kf_max_dist = mIntf->getSyncFramePeriod();
+        mCodecConfiguration->kf_min_dist = mIntf->getSyncFramePeriod();
+        mCodecConfiguration->kf_mode = VPX_KF_AUTO;
+    }
+    if (mMinQuantizer > 0) {
+        mCodecConfiguration->rc_min_quantizer = mMinQuantizer;
+    }
+    if (mMaxQuantizer > 0) {
+        mCodecConfiguration->rc_max_quantizer = mMaxQuantizer;
+    }
+    setCodecSpecificConfiguration();
+    mCodecContext = new vpx_codec_ctx_t;
+    if (!mCodecContext) goto CleanUp;
+    codec_return = vpx_codec_enc_init(mCodecContext,
+                                      mCodecInterface,
+                                      mCodecConfiguration,
+                                      0);  // flags
+    if (codec_return != VPX_CODEC_OK) {
+        ALOGE("Error initializing vpx encoder");
+        goto CleanUp;
+    }
+
+    // Extra CBR settings
+    if (mBitrateControlMode == VPX_CBR) {
+        codec_return = vpx_codec_control(mCodecContext,
+                                         VP8E_SET_STATIC_THRESHOLD,
+                                         1);
+        if (codec_return == VPX_CODEC_OK) {
+            uint32_t rc_max_intra_target =
+                (uint32_t)(mCodecConfiguration->rc_buf_optimal_sz * mFrameRate->value / 20 + 0.5);
+            // Don't go below 3 times per frame bandwidth.
+            if (rc_max_intra_target < 300) {
+                rc_max_intra_target = 300;
+            }
+            codec_return = vpx_codec_control(mCodecContext,
+                                             VP8E_SET_MAX_INTRA_BITRATE_PCT,
+                                             rc_max_intra_target);
+        }
+        if (codec_return == VPX_CODEC_OK) {
+            codec_return = vpx_codec_control(mCodecContext,
+                                             VP8E_SET_CPUUSED,
+                                             -8);
+        }
+        if (codec_return != VPX_CODEC_OK) {
+            ALOGE("Error setting cbr parameters for vpx encoder.");
+            goto CleanUp;
+        }
+    }
+
+    codec_return = setCodecSpecificControls();
+    if (codec_return != VPX_CODEC_OK) goto CleanUp;
+
+    {
+        uint32_t width = mSize->width;
+        uint32_t height = mSize->height;
+        if (((uint64_t)width * height) >
+            ((uint64_t)INT32_MAX / 3)) {
+            ALOGE("b/25812794, Buffer size is too big, width=%u, height=%u.", width, height);
+        } else {
+            uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
+            uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
+            mConversionBuffer = MemoryBlock::Allocate(stride * vstride * 3 / 2);
+            if (!mConversionBuffer.size()) {
+                ALOGE("Allocating conversion buffer failed.");
+            } else {
+                mNumInputFrames = -1;
+                return OK;
+            }
+        }
+    }
+
+CleanUp:
+    onRelease();
+    return result;
+}
+
+vpx_enc_frame_flags_t C2SoftVpxEnc::getEncodeFlags() {
+    vpx_enc_frame_flags_t flags = 0;
+    if (mTemporalPatternLength > 0) {
+      int patternIdx = mTemporalPatternIdx % mTemporalPatternLength;
+      mTemporalPatternIdx++;
+      switch (mTemporalPattern[patternIdx]) {
+          case kTemporalUpdateLast:
+              flags |= VP8_EFLAG_NO_UPD_GF;
+              flags |= VP8_EFLAG_NO_UPD_ARF;
+              flags |= VP8_EFLAG_NO_REF_GF;
+              flags |= VP8_EFLAG_NO_REF_ARF;
+              break;
+          case kTemporalUpdateGoldenWithoutDependency:
+              flags |= VP8_EFLAG_NO_REF_GF;
+              [[fallthrough]];
+          case kTemporalUpdateGolden:
+              flags |= VP8_EFLAG_NO_REF_ARF;
+              flags |= VP8_EFLAG_NO_UPD_ARF;
+              flags |= VP8_EFLAG_NO_UPD_LAST;
+              break;
+          case kTemporalUpdateAltrefWithoutDependency:
+              flags |= VP8_EFLAG_NO_REF_ARF;
+              flags |= VP8_EFLAG_NO_REF_GF;
+              [[fallthrough]];
+          case kTemporalUpdateAltref:
+              flags |= VP8_EFLAG_NO_UPD_GF;
+              flags |= VP8_EFLAG_NO_UPD_LAST;
+              break;
+          case kTemporalUpdateNoneNoRefAltref:
+              flags |= VP8_EFLAG_NO_REF_ARF;
+              [[fallthrough]];
+          case kTemporalUpdateNone:
+              flags |= VP8_EFLAG_NO_UPD_GF;
+              flags |= VP8_EFLAG_NO_UPD_ARF;
+              flags |= VP8_EFLAG_NO_UPD_LAST;
+              flags |= VP8_EFLAG_NO_UPD_ENTROPY;
+              break;
+          case kTemporalUpdateNoneNoRefGoldenRefAltRef:
+              flags |= VP8_EFLAG_NO_REF_GF;
+              flags |= VP8_EFLAG_NO_UPD_GF;
+              flags |= VP8_EFLAG_NO_UPD_ARF;
+              flags |= VP8_EFLAG_NO_UPD_LAST;
+              flags |= VP8_EFLAG_NO_UPD_ENTROPY;
+              break;
+          case kTemporalUpdateGoldenWithoutDependencyRefAltRef:
+              flags |= VP8_EFLAG_NO_REF_GF;
+              flags |= VP8_EFLAG_NO_UPD_ARF;
+              flags |= VP8_EFLAG_NO_UPD_LAST;
+              break;
+          case kTemporalUpdateLastRefAltRef:
+              flags |= VP8_EFLAG_NO_UPD_GF;
+              flags |= VP8_EFLAG_NO_UPD_ARF;
+              flags |= VP8_EFLAG_NO_REF_GF;
+              break;
+          case kTemporalUpdateGoldenRefAltRef:
+              flags |= VP8_EFLAG_NO_UPD_ARF;
+              flags |= VP8_EFLAG_NO_UPD_LAST;
+              break;
+          case kTemporalUpdateLastAndGoldenRefAltRef:
+              flags |= VP8_EFLAG_NO_UPD_ARF;
+              flags |= VP8_EFLAG_NO_REF_GF;
+              break;
+          case kTemporalUpdateLastRefAll:
+              flags |= VP8_EFLAG_NO_UPD_ARF;
+              flags |= VP8_EFLAG_NO_UPD_GF;
+              break;
+      }
+    }
+    return flags;
+}
+
+// TODO: add support for YUV input color formats
+// TODO: add support for SVC, ARF. SVC and ARF returns multiple frames
+// (hierarchical / noshow) in one call. These frames should be combined in to
+// a single buffer and sent back to the client
+void C2SoftVpxEnc::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+    // Initialize encoder if not already
+    if (!mCodecContext && OK != initEncoder()) {
+        ALOGE("Failed to initialize encoder");
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    std::shared_ptr<const C2GraphicView> rView;
+    std::shared_ptr<C2Buffer> inputBuffer;
+    if (!work->input.buffers.empty()) {
+        inputBuffer = work->input.buffers[0];
+        rView = std::make_shared<const C2GraphicView>(
+                    inputBuffer->data().graphicBlocks().front().map().get());
+        if (rView->error() != C2_OK) {
+            ALOGE("graphic view map err = %d", rView->error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+    } else {
+        ALOGV("Empty input Buffer");
+        uint32_t flags = 0;
+        if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+            flags |= C2FrameData::FLAG_END_OF_STREAM;
+        }
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+        return;
+    }
+
+    const C2ConstGraphicBlock inBuffer =
+        inputBuffer->data().graphicBlocks().front();
+    if (inBuffer.width() != mSize->width ||
+        inBuffer.height() != mSize->height) {
+        ALOGE("unexpected Input buffer attributes %d(%d) x %d(%d)",
+              inBuffer.width(), mSize->width, inBuffer.height(),
+              mSize->height);
+        mSignalledError = true;
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    vpx_image_t raw_frame;
+    const C2PlanarLayout &layout = rView->layout();
+    uint32_t width = rView->width();
+    uint32_t height = rView->height();
+    if (width > 0x8000 || height > 0x8000) {
+        ALOGE("Image too big: %u x %u", width, height);
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+    uint32_t stride = (width + mStrideAlign - 1) & ~(mStrideAlign - 1);
+    uint32_t vstride = (height + mStrideAlign - 1) & ~(mStrideAlign - 1);
+    switch (layout.type) {
+        case C2PlanarLayout::TYPE_RGB:
+        case C2PlanarLayout::TYPE_RGBA: {
+            ConvertRGBToPlanarYUV(mConversionBuffer.data(), stride, vstride,
+                                  mConversionBuffer.size(), *rView.get());
+            vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, width, height,
+                         mStrideAlign, mConversionBuffer.data());
+            break;
+        }
+        case C2PlanarLayout::TYPE_YUV: {
+            if (!IsYUV420(*rView)) {
+                ALOGE("input is not YUV420");
+                work->result = C2_BAD_VALUE;
+                return;
+            }
+
+            if (layout.planes[layout.PLANE_Y].colInc == 1
+                    && layout.planes[layout.PLANE_U].colInc == 1
+                    && layout.planes[layout.PLANE_V].colInc == 1) {
+                // I420 compatible - though with custom offset and stride
+                vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, width, height,
+                             mStrideAlign, (uint8_t*)rView->data()[0]);
+                raw_frame.planes[1] = (uint8_t*)rView->data()[1];
+                raw_frame.planes[2] = (uint8_t*)rView->data()[2];
+                raw_frame.stride[0] = layout.planes[layout.PLANE_Y].rowInc;
+                raw_frame.stride[1] = layout.planes[layout.PLANE_U].rowInc;
+                raw_frame.stride[2] = layout.planes[layout.PLANE_V].rowInc;
+            } else {
+                // copy to I420
+                MediaImage2 img = CreateYUV420PlanarMediaImage2(width, height, stride, vstride);
+                if (mConversionBuffer.size() >= stride * vstride * 3 / 2) {
+                    status_t err = ImageCopy(mConversionBuffer.data(), &img, *rView);
+                    if (err != OK) {
+                        ALOGE("Buffer conversion failed: %d", err);
+                        work->result = C2_BAD_VALUE;
+                        return;
+                    }
+                    vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, stride, vstride,
+                                 mStrideAlign, (uint8_t*)rView->data()[0]);
+                    vpx_img_set_rect(&raw_frame, 0, 0, width, height);
+                } else {
+                    ALOGE("Conversion buffer is too small: %u x %u for %zu",
+                            stride, vstride, mConversionBuffer.size());
+                    work->result = C2_BAD_VALUE;
+                    return;
+                }
+            }
+            break;
+        }
+        default:
+            ALOGE("Unrecognized plane type: %d", layout.type);
+            work->result = C2_BAD_VALUE;
+            return;
+    }
+
+    vpx_enc_frame_flags_t flags = getEncodeFlags();
+    // handle dynamic config parameters
+    {
+        IntfImpl::Lock lock = mIntf->lock();
+        std::shared_ptr<C2StreamIntraRefreshTuning::output> intraRefresh = mIntf->getIntraRefresh_l();
+        std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
+        std::shared_ptr<C2StreamRequestSyncFrameTuning::output> requestSync = mIntf->getRequestSync_l();
+        lock.unlock();
+
+        if (intraRefresh != mIntraRefresh) {
+            mIntraRefresh = intraRefresh;
+            ALOGV("Got mIntraRefresh request");
+        }
+
+        if (requestSync != mRequestSync) {
+            // we can handle IDR immediately
+            if (requestSync->value) {
+                // unset request
+                C2StreamRequestSyncFrameTuning::output clearSync(0u, C2_FALSE);
+                std::vector<std::unique_ptr<C2SettingResult>> failures;
+                mIntf->config({ &clearSync }, C2_MAY_BLOCK, &failures);
+                ALOGV("Got sync request");
+                flags |= VPX_EFLAG_FORCE_KF;
+            }
+            mRequestSync = requestSync;
+        }
+
+        if (bitrate != mBitrate) {
+            mBitrate = bitrate;
+            mCodecConfiguration->rc_target_bitrate =
+                (mBitrate->value + 500) / 1000;
+            vpx_codec_err_t res = vpx_codec_enc_config_set(mCodecContext,
+                                                           mCodecConfiguration);
+            if (res != VPX_CODEC_OK) {
+                ALOGE("vpx encoder failed to update bitrate: %s",
+                      vpx_codec_err_to_string(res));
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+        }
+    }
+
+    uint64_t inputTimeStamp = work->input.ordinal.timestamp.peekull();
+    uint32_t frameDuration;
+    if (inputTimeStamp > mLastTimestamp) {
+        frameDuration = (uint32_t)(inputTimeStamp - mLastTimestamp);
+    } else {
+        // Use default of 30 fps in case of 0 frame rate.
+        float frameRate = mFrameRate->value;
+        if (frameRate < 0.001) {
+            frameRate = 30;
+        }
+        frameDuration = (uint32_t)(1000000 / frameRate + 0.5);
+    }
+    mLastTimestamp = inputTimeStamp;
+
+    vpx_codec_err_t codec_return = vpx_codec_encode(mCodecContext, &raw_frame,
+                                                    inputTimeStamp,
+                                                    frameDuration, flags,
+                                                    VPX_DL_REALTIME);
+    if (codec_return != VPX_CODEC_OK) {
+        ALOGE("vpx encoder failed to encode frame");
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    bool populated = false;
+    vpx_codec_iter_t encoded_packet_iterator = nullptr;
+    const vpx_codec_cx_pkt_t* encoded_packet;
+    while ((encoded_packet = vpx_codec_get_cx_data(
+                    mCodecContext, &encoded_packet_iterator))) {
+        if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) {
+            std::shared_ptr<C2LinearBlock> block;
+            C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+            c2_status_t err = pool->fetchLinearBlock(encoded_packet->data.frame.sz, usage, &block);
+            if (err != C2_OK) {
+                ALOGE("fetchLinearBlock for Output failed with status %d", err);
+                work->result = C2_NO_MEMORY;
+                return;
+            }
+            C2WriteView wView = block->map().get();
+            if (wView.error()) {
+                ALOGE("write view map failed %d", wView.error());
+                work->result = C2_CORRUPTED;
+                return;
+            }
+
+            memcpy(wView.data(), encoded_packet->data.frame.buf, encoded_packet->data.frame.sz);
+            ++mNumInputFrames;
+
+            ALOGD("bytes generated %zu", encoded_packet->data.frame.sz);
+            uint32_t flags = 0;
+            if (eos) {
+                flags |= C2FrameData::FLAG_END_OF_STREAM;
+            }
+            work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+            work->worklets.front()->output.buffers.clear();
+            std::shared_ptr<C2Buffer> buffer = createLinearBuffer(block);
+            if (encoded_packet->data.frame.flags & VPX_FRAME_IS_KEY) {
+                buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
+                        0u /* stream id */, C2Config::SYNC_FRAME));
+            }
+            work->worklets.front()->output.buffers.push_back(buffer);
+            work->worklets.front()->output.ordinal = work->input.ordinal;
+            work->worklets.front()->output.ordinal.timestamp = encoded_packet->data.frame.pts;
+            work->workletsProcessed = 1u;
+            populated = true;
+            if (eos) {
+                mSignalledOutputEos = true;
+                ALOGV("signalled EOS");
+            }
+        }
+    }
+    if (!populated) {
+        work->workletsProcessed = 0u;
+    }
+}
+
+c2_status_t C2SoftVpxEnc::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void)pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    return C2_OK;
+}
+
+}  // namespace android
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h
new file mode 100644
index 0000000..62ccd1b
--- /dev/null
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.h
@@ -0,0 +1,433 @@
+/*
+ * 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 ANDROID_C2_SOFT_VPX_ENC_H__
+#define ANDROID_C2_SOFT_VPX_ENC_H__
+
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include <C2PlatformSupport.h>
+#include <Codec2BufferUtils.h>
+#include <SimpleC2Component.h>
+#include <SimpleC2Interface.h>
+#include <util/C2InterfaceHelper.h>
+
+#include "vpx/vpx_encoder.h"
+#include "vpx/vpx_codec.h"
+#include "vpx/vpx_image.h"
+#include "vpx/vp8cx.h"
+
+namespace android {
+
+// TODO: These defs taken from deprecated OMX_VideoExt.h. Move these definitions
+// to a new header file and include it.
+
+/** Maximum number of temporal layers */
+#define MAXTEMPORALLAYERS 3
+
+/** temporal layer patterns */
+typedef enum TemporalPatternType {
+    VPXTemporalLayerPatternNone = 0,
+    VPXTemporalLayerPatternWebRTC = 1,
+    VPXTemporalLayerPatternMax = 0x7FFFFFFF
+} TemporalPatternType;
+
+// Base class for a VPX Encoder Component
+//
+// Only following encoder settings are available (codec specific settings might
+// be available in the sub-classes):
+//    - video resolution
+//    - target bitrate
+//    - rate control (constant / variable)
+//    - frame rate
+//    - error resilience
+//    - reconstruction & loop filters (g_profile)
+//
+// Only following color formats are recognized
+//    - C2PlanarLayout::TYPE_RGB
+//    - C2PlanarLayout::TYPE_RGBA
+//
+// Following settings are not configurable by the client
+//    - encoding deadline is realtime
+//    - multithreaded encoding utilizes a number of threads equal
+// to online cpu's available
+//    - the algorithm interface for encoder is decided by the sub-class in use
+//    - fractional bits of frame rate is discarded
+//    - timestamps are in microseconds, therefore encoder timebase is fixed
+// to 1/1000000
+
+struct C2SoftVpxEnc : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftVpxEnc(const char* name, c2_node_id_t id,
+                 const std::shared_ptr<IntfImpl>& intfImpl);
+
+    // From SimpleC2Component
+    c2_status_t onInit() override final;
+    c2_status_t onStop() override final;
+    void onReset() override final;
+    void onRelease() override final;
+    c2_status_t onFlush_sm() override final;
+
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override final;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override final;
+
+ protected:
+     std::shared_ptr<IntfImpl> mIntf;
+     virtual ~C2SoftVpxEnc();
+
+     // Initializes vpx encoder with available settings.
+     status_t initEncoder();
+
+     // Populates mCodecInterface with codec specific settings.
+     virtual void setCodecSpecificInterface() = 0;
+
+     // Sets codec specific configuration.
+     virtual void setCodecSpecificConfiguration() = 0;
+
+     // Sets codec specific encoder controls.
+     virtual vpx_codec_err_t setCodecSpecificControls() = 0;
+
+     // Get current encode flags.
+     virtual vpx_enc_frame_flags_t getEncodeFlags();
+
+     enum TemporalReferences {
+         // For 1 layer case: reference all (last, golden, and alt ref), but only
+         // update last.
+         kTemporalUpdateLastRefAll = 12,
+         // First base layer frame for 3 temporal layers, which updates last and
+         // golden with alt ref dependency.
+         kTemporalUpdateLastAndGoldenRefAltRef = 11,
+         // First enhancement layer with alt ref dependency.
+         kTemporalUpdateGoldenRefAltRef = 10,
+         // First enhancement layer with alt ref dependency.
+         kTemporalUpdateGoldenWithoutDependencyRefAltRef = 9,
+         // Base layer with alt ref dependency.
+         kTemporalUpdateLastRefAltRef = 8,
+         // Highest enhacement layer without dependency on golden with alt ref
+         // dependency.
+         kTemporalUpdateNoneNoRefGoldenRefAltRef = 7,
+         // Second layer and last frame in cycle, for 2 layers.
+         kTemporalUpdateNoneNoRefAltref = 6,
+         // Highest enhancement layer.
+         kTemporalUpdateNone = 5,
+         // Second enhancement layer.
+         kTemporalUpdateAltref = 4,
+         // Second enhancement layer without dependency on previous frames in
+         // the second enhancement layer.
+         kTemporalUpdateAltrefWithoutDependency = 3,
+         // First enhancement layer.
+         kTemporalUpdateGolden = 2,
+         // First enhancement layer without dependency on previous frames in
+         // the first enhancement layer.
+         kTemporalUpdateGoldenWithoutDependency = 1,
+         // Base layer.
+         kTemporalUpdateLast = 0,
+     };
+     enum {
+         kMaxTemporalPattern = 8
+     };
+
+     // vpx specific opaque data structure that
+     // stores encoder state
+     vpx_codec_ctx_t* mCodecContext;
+
+     // vpx specific data structure that
+     // stores encoder configuration
+     vpx_codec_enc_cfg_t* mCodecConfiguration;
+
+     // vpx specific read-only data structure
+     // that specifies algorithm interface (e.g. vp8)
+     vpx_codec_iface_t* mCodecInterface;
+
+     // align stride to the power of 2
+     int32_t mStrideAlign;
+
+     // Color format for the input port
+     vpx_img_fmt_t mColorFormat;
+
+     // Bitrate control mode, either constant or variable
+     vpx_rc_mode mBitrateControlMode;
+
+     // Parameter that denotes whether error resilience
+     // is enabled in encoder
+     bool mErrorResilience;
+
+     // Minimum (best quality) quantizer
+     uint32_t mMinQuantizer;
+
+     // Maximum (worst quality) quantizer
+     uint32_t mMaxQuantizer;
+
+     // Number of coding temporal layers to be used.
+     size_t mTemporalLayers;
+
+     // Temporal layer bitrare ratio in percentage
+     uint32_t mTemporalLayerBitrateRatio[MAXTEMPORALLAYERS];
+
+     // Temporal pattern type
+     TemporalPatternType mTemporalPatternType;
+
+     // Temporal pattern length
+     size_t mTemporalPatternLength;
+
+     // Temporal pattern current index
+     size_t mTemporalPatternIdx;
+
+     // Frame type temporal pattern
+     TemporalReferences mTemporalPattern[kMaxTemporalPattern];
+
+     // Last input buffer timestamp
+     uint64_t mLastTimestamp;
+
+     // Number of input frames
+     int64_t mNumInputFrames;
+
+     // Conversion buffer is needed to input to
+     // yuv420 planar format.
+     MemoryBlock mConversionBuffer;
+
+     // Signalled EOS
+     bool mSignalledOutputEos;
+
+     // Signalled Error
+     bool mSignalledError;
+
+    // configurations used by component in process
+    // (TODO: keep this in intf but make them internal only)
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    std::shared_ptr<C2StreamIntraRefreshTuning::output> mIntraRefresh;
+    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
+    std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
+
+     C2_DO_NOT_COPY(C2SoftVpxEnc);
+};
+
+namespace {
+
+#ifdef VP9
+constexpr char COMPONENT_NAME[] = "c2.android.vp9.encoder";
+const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_VP9;
+#else
+constexpr char COMPONENT_NAME[] = "c2.android.vp8.encoder";
+const char *MEDIA_MIMETYPE_VIDEO = MEDIA_MIMETYPE_VIDEO_VP8;
+#endif
+
+} // namepsace
+
+class C2SoftVpxEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
+   public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_ENCODER,
+                C2Component::DOMAIN_VIDEO,
+                MEDIA_MIMETYPE_VIDEO) {
+        noPrivateBuffers(); // TODO: account for our buffers here
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
+                .withConstValue(new C2StreamUsageTuning::input(
+                        0u, (uint64_t)C2MemoryUsage::CPU_READ))
+                .build());
+
+        addParameter(
+            DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+                .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
+                .withFields({
+                    C2F(mSize, width).inRange(2, 2048, 2),
+                    C2F(mSize, height).inRange(2, 2048, 2),
+                })
+                .withSetter(SizeSetter)
+                .build());
+
+        addParameter(
+            DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
+                .withDefault(new C2StreamBitrateModeTuning::output(
+                        0u, C2Config::BITRATE_VARIABLE))
+                .withFields({
+                    C2F(mBitrateMode, value).oneOf({
+                        C2Config::BITRATE_CONST, C2Config::BITRATE_VARIABLE })
+                })
+                .withSetter(
+                    Setter<decltype(*mBitrateMode)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+            DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
+                .withDefault(new C2StreamFrameRateInfo::output(0u, 30.))
+                // TODO: More restriction?
+                .withFields({C2F(mFrameRate, value).greaterThan(0.)})
+                .withSetter(
+                    Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+            DefineParam(mLayering, C2_PARAMKEY_TEMPORAL_LAYERING)
+                .withDefault(C2StreamTemporalLayeringTuning::output::AllocShared(0u, 0, 0, 0))
+                .withFields({
+                    C2F(mLayering, m.layerCount).inRange(0, 4),
+                    C2F(mLayering, m.bLayerCount).inRange(0, 0),
+                    C2F(mLayering, m.bitrateRatios).inRange(0., 1.)
+                })
+                .withSetter(LayeringSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL)
+                .withDefault(new C2StreamSyncFrameIntervalTuning::output(0u, 1000000))
+                .withFields({C2F(mSyncFramePeriod, value).any()})
+                .withSetter(Setter<decltype(*mSyncFramePeriod)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+            DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::output(0u, 64000))
+                .withFields({C2F(mBitrate, value).inRange(4096, 40000000)})
+                .withSetter(BitrateSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mIntraRefresh, C2_PARAMKEY_INTRA_REFRESH)
+                .withConstValue(new C2StreamIntraRefreshTuning::output(
+                             0u, C2Config::INTRA_REFRESH_DISABLED, 0.))
+                .build());
+
+        addParameter(
+        DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+        .withDefault(new C2StreamProfileLevelInfo::output(
+                0u, PROFILE_VP9_0, LEVEL_VP9_4_1))
+        .withFields({
+            C2F(mProfileLevel, profile).equalTo(
+                PROFILE_VP9_0
+            ),
+            C2F(mProfileLevel, level).equalTo(
+                LEVEL_VP9_4_1),
+        })
+        .withSetter(ProfileLevelSetter)
+        .build());
+
+        addParameter(
+                DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
+                .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
+                .withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) })
+                .withSetter(Setter<decltype(*mRequestSync)>::NonStrictValueWithNoDeps)
+                .build());
+    }
+
+    static C2R BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (me.v.value <= 4096) {
+            me.set().value = 4096;
+        }
+        return res;
+    }
+
+    static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input> &oldMe,
+                          C2P<C2StreamPictureSizeInfo::input> &me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+            me.set().width = oldMe.v.width;
+        }
+        if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+            res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+            me.set().height = oldMe.v.height;
+        }
+        return res;
+    }
+
+    static C2R ProfileLevelSetter(
+            bool mayBlock,
+            C2P<C2StreamProfileLevelInfo::output> &me) {
+        (void)mayBlock;
+        if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
+            me.set().profile = PROFILE_VP9_0;
+        }
+        if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
+            me.set().level = LEVEL_VP9_4_1;
+        }
+        return C2R::Ok();
+    }
+
+    static C2R LayeringSetter(bool mayBlock, C2P<C2StreamTemporalLayeringTuning::output>& me) {
+        (void)mayBlock;
+        C2R res = C2R::Ok();
+        if (me.v.m.layerCount > 4) {
+            me.set().m.layerCount = 4;
+        }
+        me.set().m.bLayerCount = 0;
+        // ensure ratios are monotonic and clamped between 0 and 1
+        for (size_t ix = 0; ix < me.v.flexCount(); ++ix) {
+            me.set().m.bitrateRatios[ix] = c2_clamp(
+                ix > 0 ? me.v.m.bitrateRatios[ix - 1] : 0, me.v.m.bitrateRatios[ix], 1.);
+        }
+        ALOGI("setting temporal layering %u + %u", me.v.m.layerCount, me.v.m.bLayerCount);
+        return res;
+    }
+
+    // unsafe getters
+    std::shared_ptr<C2StreamPictureSizeInfo::input> getSize_l() const { return mSize; }
+    std::shared_ptr<C2StreamIntraRefreshTuning::output> getIntraRefresh_l() const { return mIntraRefresh; }
+    std::shared_ptr<C2StreamFrameRateInfo::output> getFrameRate_l() const { return mFrameRate; }
+    std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const { return mBitrate; }
+    std::shared_ptr<C2StreamBitrateModeTuning::output> getBitrateMode_l() const { return mBitrateMode; }
+    std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const { return mRequestSync; }
+    std::shared_ptr<C2StreamTemporalLayeringTuning::output> getTemporalLayers_l() const { return mLayering; }
+    uint32_t getSyncFramePeriod() const {
+        if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) {
+            return 0;
+        }
+        double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
+        return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
+    }
+
+   private:
+    std::shared_ptr<C2StreamUsageTuning::input> mUsage;
+    std::shared_ptr<C2StreamPictureSizeInfo::input> mSize;
+    std::shared_ptr<C2StreamFrameRateInfo::output> mFrameRate;
+    std::shared_ptr<C2StreamTemporalLayeringTuning::output> mLayering;
+    std::shared_ptr<C2StreamIntraRefreshTuning::output> mIntraRefresh;
+    std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
+    std::shared_ptr<C2StreamSyncFrameIntervalTuning::output> mSyncFramePeriod;
+    std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
+    std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
+    std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2_SOFT_VPX_ENC_H__
diff --git a/media/common_time/MODULE_LICENSE_APACHE2 b/media/codec2/components/vpx/MODULE_LICENSE_APACHE2
similarity index 100%
copy from media/common_time/MODULE_LICENSE_APACHE2
copy to media/codec2/components/vpx/MODULE_LICENSE_APACHE2
diff --git a/media/codec2/components/vpx/NOTICE b/media/codec2/components/vpx/NOTICE
new file mode 100644
index 0000000..faed58a
--- /dev/null
+++ b/media/codec2/components/vpx/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-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.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/media/codec2/components/xaac/Android.bp b/media/codec2/components/xaac/Android.bp
new file mode 100644
index 0000000..7795cc1
--- /dev/null
+++ b/media/codec2/components/xaac/Android.bp
@@ -0,0 +1,11 @@
+cc_library_shared {
+    name: "libcodec2_soft_xaacdec",
+    defaults: [
+        "libcodec2_soft-defaults",
+        "libcodec2_soft_sanitize_all-defaults",
+    ],
+
+    srcs: ["C2SoftXaacDec.cpp"],
+
+    static_libs: ["libxaacdec"],
+}
diff --git a/media/codec2/components/xaac/C2SoftXaacDec.cpp b/media/codec2/components/xaac/C2SoftXaacDec.cpp
new file mode 100644
index 0000000..a3ebadb
--- /dev/null
+++ b/media/codec2/components/xaac/C2SoftXaacDec.cpp
@@ -0,0 +1,1595 @@
+/*
+ * 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 "C2SoftXaacDec"
+#include <log/log.h>
+
+#include <inttypes.h>
+
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "C2SoftXaacDec.h"
+
+#define DRC_DEFAULT_MOBILE_REF_LEVEL -16.0   /* 64*-0.25dB = -16 dB below full scale for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_CUT   1.0  /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_BOOST 1.0 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_HEAVY C2Config::DRC_COMPRESSION_HEAVY   /* switch for heavy compression for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_EFFECT 3  /* MPEG-D DRC effect type; 3 => Limited playback range */
+#define DRC_DEFAULT_MOBILE_ENC_LEVEL (0.25) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
+#define MAX_CHANNEL_COUNT            8  /* maximum number of audio channels that can be decoded */
+// names of properties that can be used to override the default DRC settings
+#define PROP_DRC_OVERRIDE_REF_LEVEL  "aac_drc_reference_level"
+#define PROP_DRC_OVERRIDE_CUT        "aac_drc_cut"
+#define PROP_DRC_OVERRIDE_BOOST      "aac_drc_boost"
+#define PROP_DRC_OVERRIDE_HEAVY      "aac_drc_heavy"
+#define PROP_DRC_OVERRIDE_ENC_LEVEL  "aac_drc_enc_target_level"
+#define PROP_DRC_OVERRIDE_EFFECT_TYPE "ro.aac_drc_effect_type"
+
+#define RETURN_IF_FATAL(retval, str)                       \
+    if (retval & IA_FATAL_ERROR) {                         \
+        ALOGE("Error in %s: Returned: %d", str, retval);   \
+        return retval;                                     \
+    } else if (retval != IA_NO_ERROR) {                    \
+        ALOGW("Warning in %s: Returned: %d", str, retval); \
+    }
+
+
+namespace android {
+
+namespace {
+
+constexpr char COMPONENT_NAME[] = "c2.android.xaac.decoder";
+
+}  // namespace
+
+class C2SoftXaacDec::IntfImpl : public SimpleInterface<void>::BaseParams {
+public:
+    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+        : SimpleInterface<void>::BaseParams(
+                helper,
+                COMPONENT_NAME,
+                C2Component::KIND_DECODER,
+                C2Component::DOMAIN_AUDIO,
+                MEDIA_MIMETYPE_AUDIO_AAC) {
+        noPrivateBuffers();
+        noInputReferences();
+        noOutputReferences();
+        noInputLatency();
+        noTimeStretch();
+
+        addParameter(
+                DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+                .withConstValue(new C2ComponentAttributesSetting(
+                    C2Component::ATTRIB_IS_TEMPORAL))
+                .build());
+
+        addParameter(
+                DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE)
+                .withDefault(new C2StreamSampleRateInfo::output(0u, 44100))
+                .withFields({C2F(mSampleRate, value).oneOf({
+                    7350, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+                })})
+                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
+                .build());
+
+        addParameter(
+                DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT)
+                .withDefault(new C2StreamChannelCountInfo::output(0u, 1))
+                .withFields({C2F(mChannelCount, value).inRange(1, 8)})
+                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+                .withDefault(new C2StreamBitrateInfo::input(0u, 64000))
+                .withFields({C2F(mBitrate, value).inRange(8000, 960000)})
+                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
+                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 8192))
+                .build());
+
+        addParameter(
+                DefineParam(mAacFormat, C2_PARAMKEY_AAC_PACKAGING)
+                .withDefault(new C2StreamAacFormatInfo::input(0u, C2Config::AAC_PACKAGING_RAW))
+                .withFields({C2F(mAacFormat, value).oneOf({
+                    C2Config::AAC_PACKAGING_RAW, C2Config::AAC_PACKAGING_ADTS
+                })})
+                .withSetter(Setter<decltype(*mAacFormat)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+                .withDefault(new C2StreamProfileLevelInfo::input(0u,
+                        C2Config::PROFILE_AAC_LC, C2Config::LEVEL_UNUSED))
+                .withFields({
+                    C2F(mProfileLevel, profile).oneOf({
+                            C2Config::PROFILE_AAC_LC,
+                            C2Config::PROFILE_AAC_HE,
+                            C2Config::PROFILE_AAC_HE_PS,
+                            C2Config::PROFILE_AAC_LD,
+                            C2Config::PROFILE_AAC_ELD,
+                            C2Config::PROFILE_AAC_XHE}),
+                    C2F(mProfileLevel, level).oneOf({
+                            C2Config::LEVEL_UNUSED
+                    })
+                })
+                .withSetter(ProfileLevelSetter)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcCompressMode, C2_PARAMKEY_DRC_COMPRESSION_MODE)
+                .withDefault(new C2StreamDrcCompressionModeTuning::input(0u, C2Config::DRC_COMPRESSION_HEAVY))
+                .withFields({
+                    C2F(mDrcCompressMode, value).oneOf({
+                            C2Config::DRC_COMPRESSION_ODM_DEFAULT,
+                            C2Config::DRC_COMPRESSION_NONE,
+                            C2Config::DRC_COMPRESSION_LIGHT,
+                            C2Config::DRC_COMPRESSION_HEAVY})
+                })
+                .withSetter(Setter<decltype(*mDrcCompressMode)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcTargetRefLevel, C2_PARAMKEY_DRC_TARGET_REFERENCE_LEVEL)
+                .withDefault(new C2StreamDrcTargetReferenceLevelTuning::input(0u, DRC_DEFAULT_MOBILE_REF_LEVEL))
+                .withFields({C2F(mDrcTargetRefLevel, value).inRange(-31.75, 0.25)})
+                .withSetter(Setter<decltype(*mDrcTargetRefLevel)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcEncTargetLevel, C2_PARAMKEY_DRC_ENCODED_TARGET_LEVEL)
+                .withDefault(new C2StreamDrcEncodedTargetLevelTuning::input(0u, DRC_DEFAULT_MOBILE_ENC_LEVEL))
+                .withFields({C2F(mDrcEncTargetLevel, value).inRange(-31.75, 0.25)})
+                .withSetter(Setter<decltype(*mDrcEncTargetLevel)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcBoostFactor, C2_PARAMKEY_DRC_BOOST_FACTOR)
+                .withDefault(new C2StreamDrcBoostFactorTuning::input(0u, DRC_DEFAULT_MOBILE_DRC_BOOST))
+                .withFields({C2F(mDrcBoostFactor, value).inRange(0, 1.)})
+                .withSetter(Setter<decltype(*mDrcBoostFactor)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcAttenuationFactor, C2_PARAMKEY_DRC_ATTENUATION_FACTOR)
+                .withDefault(new C2StreamDrcAttenuationFactorTuning::input(0u, DRC_DEFAULT_MOBILE_DRC_CUT))
+                .withFields({C2F(mDrcAttenuationFactor, value).inRange(0, 1.)})
+                .withSetter(Setter<decltype(*mDrcAttenuationFactor)>::StrictValueWithNoDeps)
+                .build());
+
+        addParameter(
+                DefineParam(mDrcEffectType, C2_PARAMKEY_DRC_EFFECT_TYPE)
+                .withDefault(new C2StreamDrcEffectTypeTuning::input(0u, C2Config::DRC_EFFECT_LIMITED_PLAYBACK_RANGE))
+                .withFields({
+                    C2F(mDrcEffectType, value).oneOf({
+                            C2Config::DRC_EFFECT_ODM_DEFAULT,
+                            C2Config::DRC_EFFECT_OFF,
+                            C2Config::DRC_EFFECT_NONE,
+                            C2Config::DRC_EFFECT_LATE_NIGHT,
+                            C2Config::DRC_EFFECT_NOISY_ENVIRONMENT,
+                            C2Config::DRC_EFFECT_LIMITED_PLAYBACK_RANGE,
+                            C2Config::DRC_EFFECT_LOW_PLAYBACK_LEVEL,
+                            C2Config::DRC_EFFECT_DIALOG_ENHANCEMENT,
+                            C2Config::DRC_EFFECT_GENERAL_COMPRESSION})
+                })
+                .withSetter(Setter<decltype(*mDrcEffectType)>::StrictValueWithNoDeps)
+                .build());
+    }
+
+    bool isAdts() const { return mAacFormat->value == C2Config::AAC_PACKAGING_ADTS; }
+    uint32_t getBitrate() const { return mBitrate->value; }
+    static C2R ProfileLevelSetter(bool mayBlock, C2P<C2StreamProfileLevelInfo::input> &me) {
+        (void)mayBlock;
+        (void)me;  // TODO: validate
+        return C2R::Ok();
+    }
+    int32_t getDrcCompressMode() const { return mDrcCompressMode->value == C2Config::DRC_COMPRESSION_HEAVY ? 1 : 0; }
+    int32_t getDrcTargetRefLevel() const { return (mDrcTargetRefLevel->value <= 0 ? -mDrcTargetRefLevel->value * 4. + 0.5 : -1); }
+    int32_t getDrcEncTargetLevel() const { return (mDrcEncTargetLevel->value <= 0 ? -mDrcEncTargetLevel->value * 4. + 0.5 : -1); }
+    int32_t getDrcBoostFactor() const { return mDrcBoostFactor->value * 127. + 0.5; }
+    int32_t getDrcAttenuationFactor() const { return mDrcAttenuationFactor->value * 127. + 0.5; }
+    int32_t getDrcEffectType() const { return mDrcEffectType->value; }
+
+private:
+    std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate;
+    std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount;
+    std::shared_ptr<C2StreamBitrateInfo::input> mBitrate;
+    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
+    std::shared_ptr<C2StreamAacFormatInfo::input> mAacFormat;
+    std::shared_ptr<C2StreamProfileLevelInfo::input> mProfileLevel;
+    std::shared_ptr<C2StreamDrcCompressionModeTuning::input> mDrcCompressMode;
+    std::shared_ptr<C2StreamDrcTargetReferenceLevelTuning::input> mDrcTargetRefLevel;
+    std::shared_ptr<C2StreamDrcEncodedTargetLevelTuning::input> mDrcEncTargetLevel;
+    std::shared_ptr<C2StreamDrcBoostFactorTuning::input> mDrcBoostFactor;
+    std::shared_ptr<C2StreamDrcAttenuationFactorTuning::input> mDrcAttenuationFactor;
+    std::shared_ptr<C2StreamDrcEffectTypeTuning::input> mDrcEffectType;
+    // TODO Add : C2StreamAacSbrModeTuning
+};
+
+C2SoftXaacDec::C2SoftXaacDec(
+        const char* name,
+        c2_node_id_t id,
+        const std::shared_ptr<IntfImpl> &intfImpl)
+    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
+        mIntf(intfImpl),
+        mXheaacCodecHandle(nullptr),
+        mMpegDDrcHandle(nullptr),
+        mOutputDrainBuffer(nullptr) {
+}
+
+C2SoftXaacDec::~C2SoftXaacDec() {
+    onRelease();
+}
+
+c2_status_t C2SoftXaacDec::onInit() {
+    mOutputFrameLength = 1024;
+    mInputBuffer = nullptr;
+    mOutputBuffer = nullptr;
+    mSampFreq = 0;
+    mNumChannels = 0;
+    mPcmWdSz = 0;
+    mChannelMask = 0;
+    mNumOutBytes = 0;
+    mCurFrameIndex = 0;
+    mCurTimestamp = 0;
+    mIsCodecInitialized = false;
+    mIsCodecConfigFlushRequired = false;
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+    mOutputDrainBufferWritePos = 0;
+    mDRCFlag = 0;
+    mMpegDDRCPresent = 0;
+    mMemoryVec.clear();
+    mDrcMemoryVec.clear();
+
+    IA_ERRORCODE err = initDecoder();
+    return err == IA_NO_ERROR ? C2_OK : C2_CORRUPTED;
+
+}
+
+c2_status_t C2SoftXaacDec::onStop() {
+    mOutputFrameLength = 1024;
+    drainDecoder();
+    // reset the "configured" state
+    mSampFreq = 0;
+    mNumChannels = 0;
+    mPcmWdSz = 0;
+    mChannelMask = 0;
+    mNumOutBytes = 0;
+    mCurFrameIndex = 0;
+    mCurTimestamp = 0;
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+    mOutputDrainBufferWritePos = 0;
+    mDRCFlag = 0;
+    mMpegDDRCPresent = 0;
+
+    return C2_OK;
+}
+
+void C2SoftXaacDec::onReset() {
+    (void)onStop();
+}
+
+void C2SoftXaacDec::onRelease() {
+    IA_ERRORCODE errCode = deInitXAACDecoder();
+    if (IA_NO_ERROR != errCode) ALOGE("deInitXAACDecoder() failed %d", errCode);
+
+    errCode = deInitMPEGDDDrc();
+    if (IA_NO_ERROR != errCode) ALOGE("deInitMPEGDDDrc() failed %d", errCode);
+
+    if (mOutputDrainBuffer) {
+        delete[] mOutputDrainBuffer;
+        mOutputDrainBuffer = nullptr;
+    }
+}
+
+IA_ERRORCODE C2SoftXaacDec::initDecoder() {
+    ALOGV("initDecoder()");
+    IA_ERRORCODE err_code = IA_NO_ERROR;
+
+    err_code = initXAACDecoder();
+    if (err_code != IA_NO_ERROR) {
+        ALOGE("initXAACDecoder Failed");
+        /* Call deInit to free any allocated memory */
+        deInitXAACDecoder();
+        return IA_FATAL_ERROR;
+    }
+
+    if (!mOutputDrainBuffer) {
+        mOutputDrainBuffer = new (std::nothrow) char[kOutputDrainBufferSize];
+        if (!mOutputDrainBuffer) return IA_FATAL_ERROR;
+    }
+
+    err_code = initXAACDrc();
+    RETURN_IF_FATAL(err_code,  "initXAACDrc");
+
+
+    return IA_NO_ERROR;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work>& work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftXaacDec::finishWork(const std::unique_ptr<C2Work>& work,
+                            const std::shared_ptr<C2BlockPool>& pool) {
+    ALOGV("mCurFrameIndex = %" PRIu64, mCurFrameIndex);
+
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+    // TODO: error handling, proper usage, etc.
+    c2_status_t err =
+        pool->fetchLinearBlock(mOutputDrainBufferWritePos, usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock failed : err = %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    int16_t* outBuffer = reinterpret_cast<int16_t*>(wView.data());
+    memcpy(outBuffer, mOutputDrainBuffer, mOutputDrainBufferWritePos);
+    mOutputDrainBufferWritePos = 0;
+
+    auto fillWork = [buffer = createLinearBuffer(block)](
+        const std::unique_ptr<C2Work>& work) {
+        uint32_t flags = 0;
+        if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+            flags |= C2FrameData::FLAG_END_OF_STREAM;
+            ALOGV("signalling eos");
+        }
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    };
+    if (work && work->input.ordinal.frameIndex == c2_cntr64_t(mCurFrameIndex)) {
+        fillWork(work);
+    } else {
+        finish(mCurFrameIndex, fillWork);
+    }
+
+    ALOGV("out timestamp %" PRIu64 " / %u", mCurTimestamp, block->capacity());
+}
+
+void C2SoftXaacDec::process(const std::unique_ptr<C2Work>& work,
+                         const std::shared_ptr<C2BlockPool>& pool) {
+    // Initialize output work
+    work->result = C2_OK;
+    work->workletsProcessed = 1u;
+    work->worklets.front()->output.configUpdate.clear();
+    work->worklets.front()->output.flags = work->input.flags;
+
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+    uint8_t* inBuffer = nullptr;
+    uint32_t inBufferLength = 0;
+    C2ReadView view = mDummyReadView;
+    size_t offset = 0u;
+    size_t size = 0u;
+    if (!work->input.buffers.empty()) {
+        view = work->input.buffers[0]->data().linearBlocks().front().map().get();
+        size = view.capacity();
+    }
+    if (size && view.error()) {
+        ALOGE("read view map failed %d", view.error());
+        work->result = view.error();
+        return;
+    }
+
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+    bool codecConfig =
+        (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+    if (codecConfig) {
+        if (size == 0u) {
+            ALOGE("empty codec config");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        // const_cast because of libAACdec method signature.
+        inBuffer = const_cast<uint8_t*>(view.data() + offset);
+        inBufferLength = size;
+
+        /* GA header configuration sent to Decoder! */
+        IA_ERRORCODE err_code = configXAACDecoder(inBuffer, inBufferLength);
+        if (IA_NO_ERROR != err_code) {
+            ALOGE("configXAACDecoder err_code = %d", err_code);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        work->worklets.front()->output.flags = work->input.flags;
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->worklets.front()->output.buffers.clear();
+        return;
+    }
+
+    mCurFrameIndex = work->input.ordinal.frameIndex.peeku();
+    mCurTimestamp = work->input.ordinal.timestamp.peeku();
+    mOutputDrainBufferWritePos = 0;
+    char* tempOutputDrainBuffer = mOutputDrainBuffer;
+    while (size > 0u) {
+        if ((kOutputDrainBufferSize * sizeof(int16_t) -
+             mOutputDrainBufferWritePos) <
+            (mOutputFrameLength * sizeof(int16_t) * mNumChannels)) {
+            ALOGV("skipping decode: not enough space left in DrainBuffer");
+            break;
+        }
+
+        ALOGV("inAttribute size = %zu", size);
+        if (mIntf->isAdts()) {
+            ALOGV("ADTS");
+            size_t adtsHeaderSize = 0;
+            // skip 30 bits, aac_frame_length follows.
+            // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
+
+            const uint8_t* adtsHeader = view.data() + offset;
+            bool signalError = false;
+            if (size < 7) {
+                ALOGE("Audio data too short to contain even the ADTS header. "
+                      "Got %zu bytes.", size);
+                hexdump(adtsHeader, size);
+                signalError = true;
+            } else {
+                bool protectionAbsent = (adtsHeader[1] & 1);
+                unsigned aac_frame_length = ((adtsHeader[3] & 3) << 11) |
+                                            (adtsHeader[4] << 3) |
+                                            (adtsHeader[5] >> 5);
+
+                if (size < aac_frame_length) {
+                    ALOGE("Not enough audio data for the complete frame. "
+                          "Got %zu bytes, frame size according to the ADTS "
+                          "header is %u bytes.", size, aac_frame_length);
+                    hexdump(adtsHeader, size);
+                    signalError = true;
+                } else {
+                    adtsHeaderSize = (protectionAbsent ? 7 : 9);
+                    if (aac_frame_length < adtsHeaderSize) {
+                        signalError = true;
+                    } else {
+                        // const_cast because of libAACdec method signature.
+                        inBuffer =
+                            const_cast<uint8_t*>(adtsHeader + adtsHeaderSize);
+                        inBufferLength = aac_frame_length - adtsHeaderSize;
+
+                        offset += adtsHeaderSize;
+                        size -= adtsHeaderSize;
+                    }
+                }
+            }
+
+            if (signalError) {
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+        } else {
+            ALOGV("Non ADTS");
+            // const_cast because of libAACdec method signature.
+            inBuffer = const_cast<uint8_t*>(view.data() + offset);
+            inBufferLength = size;
+        }
+
+        signed int prevSampleRate = mSampFreq;
+        signed int prevNumChannels = mNumChannels;
+
+        /* XAAC decoder expects first frame to be fed via configXAACDecoder API
+         * which should initialize the codec. Once this state is reached, call the
+         * decodeXAACStream API with same frame to decode! */
+        if (!mIsCodecInitialized) {
+            IA_ERRORCODE err_code = configXAACDecoder(inBuffer, inBufferLength);
+            if (IA_NO_ERROR != err_code) {
+                ALOGE("configXAACDecoder Failed 2 err_code = %d", err_code);
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+
+            if ((mSampFreq != prevSampleRate) ||
+                (mNumChannels != prevNumChannels)) {
+                ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+                      prevSampleRate, mSampFreq, prevNumChannels, mNumChannels);
+
+                C2StreamSampleRateInfo::output sampleRateInfo(0u, mSampFreq);
+                C2StreamChannelCountInfo::output channelCountInfo(0u, mNumChannels);
+                std::vector<std::unique_ptr<C2SettingResult>> failures;
+                c2_status_t err = mIntf->config(
+                        { &sampleRateInfo, &channelCountInfo },
+                        C2_MAY_BLOCK,
+                        &failures);
+                if (err == OK) {
+                    work->worklets.front()->output.configUpdate.push_back(
+                        C2Param::Copy(sampleRateInfo));
+                    work->worklets.front()->output.configUpdate.push_back(
+                        C2Param::Copy(channelCountInfo));
+                } else {
+                    ALOGE("Config Update failed");
+                    mSignalledError = true;
+                    work->result = C2_CORRUPTED;
+                    return;
+                }
+            }
+        }
+
+        signed int bytesConsumed = 0;
+        IA_ERRORCODE errorCode = IA_NO_ERROR;
+        if (mIsCodecInitialized) {
+            mIsCodecConfigFlushRequired = true;
+            errorCode = decodeXAACStream(inBuffer, inBufferLength,
+                                         &bytesConsumed, &mNumOutBytes);
+        } else if (!mIsCodecConfigFlushRequired) {
+            ALOGW("Assumption that first frame after header initializes decoder Failed!");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        size -= bytesConsumed;
+        offset += bytesConsumed;
+
+        if (inBufferLength != (uint32_t)bytesConsumed)
+            ALOGW("All data not consumed");
+
+        /* In case of error, decoder would have given out empty buffer */
+        if ((IA_NO_ERROR != errorCode) && (0 == mNumOutBytes) && mIsCodecInitialized)
+            mNumOutBytes = mOutputFrameLength * (mPcmWdSz / 8) * mNumChannels;
+
+        if (!bytesConsumed) {
+            ALOGW("bytesConsumed = 0 should never happen");
+        }
+
+        if ((uint32_t)mNumOutBytes >
+            mOutputFrameLength * sizeof(int16_t) * mNumChannels) {
+            ALOGE("mNumOutBytes > mOutputFrameLength * sizeof(int16_t) * mNumChannels, should never happen");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        if (IA_NO_ERROR != errorCode) {
+            // TODO: check for overflow, ASAN
+            memset(mOutputBuffer, 0, mNumOutBytes);
+
+            // Discard input buffer.
+            size = 0;
+
+            // fall through
+        }
+        memcpy(tempOutputDrainBuffer, mOutputBuffer, mNumOutBytes);
+        tempOutputDrainBuffer += mNumOutBytes;
+        mOutputDrainBufferWritePos += mNumOutBytes;
+    }
+
+    if (mOutputDrainBufferWritePos) {
+        finishWork(work, pool);
+    } else {
+        fillEmptyWork(work);
+    }
+    if (eos) mSignalledOutputEos = true;
+}
+
+c2_status_t C2SoftXaacDec::drain(uint32_t drainMode,
+                              const std::shared_ptr<C2BlockPool>& pool) {
+    (void)pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    return C2_OK;
+}
+
+IA_ERRORCODE C2SoftXaacDec::configflushDecode() {
+    IA_ERRORCODE err_code;
+    uint32_t ui_init_done;
+    uint32_t inBufferLength = 8203;
+
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_INIT,
+                                IA_CMD_TYPE_FLUSH_MEM,
+                                nullptr);
+    RETURN_IF_FATAL(err_code,  "IA_CMD_TYPE_FLUSH_MEM");
+
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_SET_INPUT_BYTES,
+                                0,
+                                &inBufferLength);
+    RETURN_IF_FATAL(err_code,  "IA_API_CMD_SET_INPUT_BYTES");
+
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_INIT,
+                                IA_CMD_TYPE_FLUSH_MEM,
+                                nullptr);
+    RETURN_IF_FATAL(err_code,  "IA_CMD_TYPE_FLUSH_MEM");
+
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_INIT,
+                                IA_CMD_TYPE_INIT_DONE_QUERY,
+                                &ui_init_done);
+    RETURN_IF_FATAL(err_code,  "IA_CMD_TYPE_INIT_DONE_QUERY");
+
+    if (ui_init_done) {
+        err_code = getXAACStreamInfo();
+        RETURN_IF_FATAL(err_code, "getXAACStreamInfo");
+        ALOGV("Found Codec with below config---\nsampFreq %d\nnumChannels %d\npcmWdSz %d\nchannelMask %d\noutputFrameLength %d",
+               mSampFreq, mNumChannels, mPcmWdSz, mChannelMask, mOutputFrameLength);
+        mIsCodecInitialized = true;
+    }
+    return IA_NO_ERROR;
+}
+
+c2_status_t C2SoftXaacDec::onFlush_sm() {
+    if (mIsCodecInitialized) {
+        IA_ERRORCODE err_code = configflushDecode();
+        if (err_code != IA_NO_ERROR) {
+            ALOGE("Error in configflushDecode: Error %d", err_code);
+        }
+    }
+    drainDecoder();
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+
+    return C2_OK;
+}
+
+IA_ERRORCODE C2SoftXaacDec::drainDecoder() {
+    /* Output delay compensation logic should sit here. */
+    /* Nothing to be done as XAAC decoder does not introduce output buffer delay */
+
+    return 0;
+}
+
+IA_ERRORCODE C2SoftXaacDec::initXAACDecoder() {
+    /* First part                                        */
+    /* Error Handler Init                                */
+    /* Get Library Name, Library Version and API Version */
+    /* Initialize API structure + Default config set     */
+    /* Set config params from user                       */
+    /* Initialize memory tables                          */
+    /* Get memory information and allocate memory        */
+
+    mInputBufferSize = 0;
+    mInputBuffer = nullptr;
+    mOutputBuffer = nullptr;
+    /* Process struct initing end */
+
+    /* ******************************************************************/
+    /* Initialize API structure and set config params to default        */
+    /* ******************************************************************/
+    /* API size */
+    uint32_t pui_api_size;
+    /* Get the API size */
+    IA_ERRORCODE err_code = ixheaacd_dec_api(nullptr,
+                                             IA_API_CMD_GET_API_SIZE,
+                                             0,
+                                             &pui_api_size);
+    RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_API_SIZE");
+
+    /* Allocate memory for API */
+    mXheaacCodecHandle = memalign(4, pui_api_size);
+    if (!mXheaacCodecHandle) {
+        ALOGE("malloc for pui_api_size + 4 >> %d Failed", pui_api_size + 4);
+        return IA_FATAL_ERROR;
+    }
+    mMemoryVec.push(mXheaacCodecHandle);
+
+    /* Set the config params to default values */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_INIT,
+                                IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS,
+                                nullptr);
+    RETURN_IF_FATAL(err_code,  "IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS");
+
+    /* Get the API size */
+    err_code = ia_drc_dec_api(nullptr, IA_API_CMD_GET_API_SIZE, 0, &pui_api_size);
+
+    RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_API_SIZE");
+
+    /* Allocate memory for API */
+    mMpegDDrcHandle = memalign(4, pui_api_size);
+    if (!mMpegDDrcHandle) {
+        ALOGE("malloc for pui_api_size + 4 >> %d Failed", pui_api_size + 4);
+        return IA_FATAL_ERROR;
+    }
+    mMemoryVec.push(mMpegDDrcHandle);
+
+    /* Set the config params to default values */
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+                              IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, nullptr);
+
+    RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS");
+
+    /* ******************************************************************/
+    /* Set config parameters                                            */
+    /* ******************************************************************/
+    uint32_t ui_mp4_flag = 1;
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_SET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4,
+                                &ui_mp4_flag);
+    RETURN_IF_FATAL(err_code,  "IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4");
+
+    /* ******************************************************************/
+    /* Initialize Memory info tables                                    */
+    /* ******************************************************************/
+    uint32_t ui_proc_mem_tabs_size;
+    pVOID pv_alloc_ptr;
+    /* Get memory info tables size */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_GET_MEMTABS_SIZE,
+                                0,
+                                &ui_proc_mem_tabs_size);
+    RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_MEMTABS_SIZE");
+
+    pv_alloc_ptr = memalign(4, ui_proc_mem_tabs_size);
+    if (!pv_alloc_ptr) {
+        ALOGE("Malloc for size (ui_proc_mem_tabs_size + 4) = %d failed!", ui_proc_mem_tabs_size + 4);
+        return IA_FATAL_ERROR;
+    }
+    mMemoryVec.push(pv_alloc_ptr);
+
+    /* Set pointer for process memory tables    */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_SET_MEMTABS_PTR,
+                                0,
+                                pv_alloc_ptr);
+    RETURN_IF_FATAL(err_code,  "IA_API_CMD_SET_MEMTABS_PTR");
+
+    /* initialize the API, post config, fill memory tables  */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_INIT,
+                                IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS,
+                                nullptr);
+    RETURN_IF_FATAL(err_code,  "IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS");
+
+    /* ******************************************************************/
+    /* Allocate Memory with info from library                           */
+    /* ******************************************************************/
+    /* There are four different types of memories, that needs to be allocated */
+    /* persistent,scratch,input and output */
+    for (int i = 0; i < 4; i++) {
+        int ui_size = 0, ui_alignment = 0, ui_type = 0;
+
+        /* Get memory size */
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                    IA_API_CMD_GET_MEM_INFO_SIZE,
+                                    i,
+                                    &ui_size);
+        RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_MEM_INFO_SIZE");
+
+        /* Get memory alignment */
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                    IA_API_CMD_GET_MEM_INFO_ALIGNMENT,
+                                    i,
+                                    &ui_alignment);
+        RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_MEM_INFO_ALIGNMENT");
+
+        /* Get memory type */
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                    IA_API_CMD_GET_MEM_INFO_TYPE,
+                                    i,
+                                    &ui_type);
+        RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_MEM_INFO_TYPE");
+
+        pv_alloc_ptr = memalign(ui_alignment, ui_size);
+        if (!pv_alloc_ptr) {
+            ALOGE("Malloc for size (ui_size + ui_alignment) = %d failed!",
+                   ui_size + ui_alignment);
+            return IA_FATAL_ERROR;
+        }
+        mMemoryVec.push(pv_alloc_ptr);
+
+        /* Set the buffer pointer */
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                    IA_API_CMD_SET_MEM_PTR,
+                                    i,
+                                    pv_alloc_ptr);
+        RETURN_IF_FATAL(err_code,  "IA_API_CMD_SET_MEM_PTR");
+        if (ui_type == IA_MEMTYPE_INPUT) {
+            mInputBuffer = (pWORD8)pv_alloc_ptr;
+            mInputBufferSize = ui_size;
+        }
+        if (ui_type == IA_MEMTYPE_OUTPUT)
+            mOutputBuffer = (pWORD8)pv_alloc_ptr;
+    }
+    /* End first part */
+
+    return IA_NO_ERROR;
+}
+
+status_t C2SoftXaacDec::initXAACDrc() {
+    IA_ERRORCODE err_code = IA_NO_ERROR;
+    unsigned int ui_drc_val;
+    //  DRC_PRES_MODE_WRAP_DESIRED_TARGET
+    int32_t targetRefLevel = mIntf->getDrcTargetRefLevel();
+    ALOGV("AAC decoder using desired DRC target reference level of %d", targetRefLevel);
+    ui_drc_val = (unsigned int)targetRefLevel;
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_SET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL,
+                                &ui_drc_val);
+    RETURN_IF_FATAL(err_code,  "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL");
+
+    /* Use ui_drc_val from PROP_DRC_OVERRIDE_REF_LEVEL or DRC_DEFAULT_MOBILE_REF_LEVEL
+     * for IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS too */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS, &ui_drc_val);
+
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS");
+
+    int32_t attenuationFactor = mIntf->getDrcAttenuationFactor();
+    ALOGV("AAC decoder using desired DRC attenuation factor of %d", attenuationFactor);
+    ui_drc_val = (unsigned int)attenuationFactor;
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_SET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT,
+                                &ui_drc_val);
+    RETURN_IF_FATAL(err_code,  "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT");
+
+    //  DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR
+    int32_t boostFactor = mIntf->getDrcBoostFactor();
+    ALOGV("AAC decoder using desired DRC boost factor of %d", boostFactor);
+    ui_drc_val = (unsigned int)boostFactor;
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_SET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST,
+                                &ui_drc_val);
+    RETURN_IF_FATAL(err_code,  "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST");
+
+    //  DRC_PRES_MODE_WRAP_DESIRED_HEAVY
+    int32_t compressMode = mIntf->getDrcCompressMode();
+    ALOGV("AAC decoder using desried DRC heavy compression switch of %d", compressMode);
+    ui_drc_val = (unsigned int)compressMode;
+
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_SET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP,
+                                &ui_drc_val);
+    RETURN_IF_FATAL(err_code,  "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP");
+
+    // AAC_UNIDRC_SET_EFFECT
+    int32_t effectType = mIntf->getDrcEffectType();
+    ALOGV("AAC decoder using MPEG-D DRC effect type %d", effectType);
+    ui_drc_val = (unsigned int)effectType;
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE, &ui_drc_val);
+
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE");
+
+    return IA_NO_ERROR;
+}
+
+IA_ERRORCODE C2SoftXaacDec::deInitXAACDecoder() {
+    ALOGV("deInitXAACDecoder");
+
+    /* Error code */
+    IA_ERRORCODE err_code = IA_NO_ERROR;
+
+    if (mXheaacCodecHandle) {
+        /* Tell that the input is over in this buffer */
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                    IA_API_CMD_INPUT_OVER,
+                                    0,
+                                    nullptr);
+    }
+
+    /* Irrespective of error returned in IA_API_CMD_INPUT_OVER, free allocated memory */
+    for (void* buf : mMemoryVec) {
+        if (buf) free(buf);
+    }
+    mMemoryVec.clear();
+    mXheaacCodecHandle = nullptr;
+
+    return err_code;
+}
+
+IA_ERRORCODE C2SoftXaacDec::deInitMPEGDDDrc() {
+    ALOGV("deInitMPEGDDDrc");
+
+    for (void* buf : mDrcMemoryVec) {
+        if (buf) free(buf);
+    }
+    mDrcMemoryVec.clear();
+    return IA_NO_ERROR;
+}
+
+IA_ERRORCODE C2SoftXaacDec::configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength) {
+    if (mInputBufferSize < inBufferLength) {
+        ALOGE("Cannot config AAC, input buffer size %d < inBufferLength %d", mInputBufferSize, inBufferLength);
+        return false;
+    }
+    /* Copy the buffer passed by Android plugin to codec input buffer */
+    memcpy(mInputBuffer, inBuffer, inBufferLength);
+
+    /* Set number of bytes to be processed */
+    IA_ERRORCODE err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                             IA_API_CMD_SET_INPUT_BYTES,
+                                             0,
+                                             &inBufferLength);
+    RETURN_IF_FATAL(err_code,  "IA_API_CMD_SET_INPUT_BYTES");
+
+    if (mIsCodecConfigFlushRequired) {
+        /* If codec is already initialized, then GA header is passed again */
+        /* Need to call the Flush API instead of INIT_PROCESS */
+        mIsCodecInitialized = false; /* Codec needs to be Reinitialized after flush */
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                    IA_API_CMD_INIT,
+                                    IA_CMD_TYPE_GA_HDR,
+                                    nullptr);
+        RETURN_IF_FATAL(err_code,  "IA_CMD_TYPE_GA_HDR");
+    } else {
+        /* Initialize the process */
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                    IA_API_CMD_INIT,
+                                    IA_CMD_TYPE_INIT_PROCESS,
+                                    nullptr);
+        RETURN_IF_FATAL(err_code,  "IA_CMD_TYPE_INIT_PROCESS");
+    }
+
+    uint32_t ui_init_done;
+    /* Checking for end of initialization */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_INIT,
+                                IA_CMD_TYPE_INIT_DONE_QUERY,
+                                &ui_init_done);
+    RETURN_IF_FATAL(err_code,  "IA_CMD_TYPE_INIT_DONE_QUERY");
+
+    /* How much buffer is used in input buffers */
+    int32_t i_bytes_consumed;
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_GET_CURIDX_INPUT_BUF,
+                                0,
+                                &i_bytes_consumed);
+    RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_CURIDX_INPUT_BUF");
+
+    if (ui_init_done) {
+        err_code = getXAACStreamInfo();
+        RETURN_IF_FATAL(err_code, "getXAACStreamInfo");
+        ALOGI("Found Codec with below config---\nsampFreq %d\nnumChannels %d\npcmWdSz %d\nchannelMask %d\noutputFrameLength %d",
+               mSampFreq, mNumChannels, mPcmWdSz, mChannelMask, mOutputFrameLength);
+        mIsCodecInitialized = true;
+
+        err_code = configMPEGDDrc();
+        RETURN_IF_FATAL(err_code, "configMPEGDDrc");
+    }
+
+    return IA_NO_ERROR;
+}
+IA_ERRORCODE C2SoftXaacDec::initMPEGDDDrc() {
+    IA_ERRORCODE err_code = IA_NO_ERROR;
+
+    for (int i = 0; i < (WORD32)2; i++) {
+        WORD32 ui_size, ui_alignment, ui_type;
+        pVOID pv_alloc_ptr;
+
+        /* Get memory size */
+        err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_SIZE, i, &ui_size);
+
+        RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_SIZE");
+
+        /* Get memory alignment */
+        err_code =
+            ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_ALIGNMENT, i, &ui_alignment);
+
+        RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_ALIGNMENT");
+
+        /* Get memory type */
+        err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_TYPE, i, &ui_type);
+        RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_TYPE");
+
+        pv_alloc_ptr = memalign(4, ui_size);
+        if (pv_alloc_ptr == nullptr) {
+            ALOGE(" Cannot create requested memory  %d", ui_size);
+            return IA_FATAL_ERROR;
+        }
+        mDrcMemoryVec.push(pv_alloc_ptr);
+
+        /* Set the buffer pointer */
+        err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, i, pv_alloc_ptr);
+
+        RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR");
+    }
+
+    WORD32 ui_size;
+    ui_size = 8192 * 2;
+
+    mDrcInBuf = (int8_t*)memalign(4, ui_size);
+    if (mDrcInBuf == nullptr) {
+        ALOGE(" Cannot create requested memory  %d", ui_size);
+        return IA_FATAL_ERROR;
+    }
+    mDrcMemoryVec.push(mDrcInBuf);
+
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, 2, mDrcInBuf);
+    RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR");
+
+    mDrcOutBuf = (int8_t*)memalign(4, ui_size);
+    if (mDrcOutBuf == nullptr) {
+        ALOGE(" Cannot create requested memory  %d", ui_size);
+        return IA_FATAL_ERROR;
+    }
+    mDrcMemoryVec.push(mDrcOutBuf);
+
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, 3, mDrcOutBuf);
+    RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR");
+
+    return IA_NO_ERROR;
+}
+int C2SoftXaacDec::configMPEGDDrc() {
+    IA_ERRORCODE err_code = IA_NO_ERROR;
+    int i_effect_type;
+    int i_loud_norm;
+    int i_target_loudness;
+    unsigned int i_sbr_mode;
+    uint32_t ui_proc_mem_tabs_size = 0;
+    pVOID pv_alloc_ptr = NULL;
+
+    /* Sampling Frequency */
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                              IA_DRC_DEC_CONFIG_PARAM_SAMP_FREQ, &mSampFreq);
+    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_SAMP_FREQ");
+    /* Total Number of Channels */
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                              IA_DRC_DEC_CONFIG_PARAM_NUM_CHANNELS, &mNumChannels);
+    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_NUM_CHANNELS");
+
+    /* PCM word size  */
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                              IA_DRC_DEC_CONFIG_PARAM_PCM_WDSZ, &mPcmWdSz);
+    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_PCM_WDSZ");
+
+    /*Set Effect Type*/
+
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE, &i_effect_type);
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE");
+
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                              IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE, &i_effect_type);
+    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE");
+
+    /*Set target loudness */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS,
+                                &i_target_loudness);
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS");
+
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                              IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS, &i_target_loudness);
+    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS");
+
+    /*Set loud_norm_flag*/
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM, &i_loud_norm);
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM");
+
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                              IA_DRC_DEC_CONFIG_DRC_LOUD_NORM, &i_loud_norm);
+    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_LOUD_NORM");
+
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE, &i_sbr_mode);
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE");
+
+    /* Get memory info tables size */
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEMTABS_SIZE, 0,
+                              &ui_proc_mem_tabs_size);
+    RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEMTABS_SIZE");
+
+    pv_alloc_ptr = memalign(4, ui_proc_mem_tabs_size);
+    if (pv_alloc_ptr == NULL) {
+        ALOGE(" Cannot create requested memory  %d", ui_proc_mem_tabs_size);
+        return IA_FATAL_ERROR;
+    }
+    memset(pv_alloc_ptr, 0, ui_proc_mem_tabs_size);
+    mMemoryVec.push(pv_alloc_ptr);
+
+    /* Set pointer for process memory tables */
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEMTABS_PTR, 0,
+                              pv_alloc_ptr);
+    RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEMTABS_PTR");
+
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+                              IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, nullptr);
+
+    RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS");
+
+    /* Free any memory that is allocated for MPEG D Drc so far */
+    deInitMPEGDDDrc();
+
+    err_code = initMPEGDDDrc();
+    if (err_code != IA_NO_ERROR) {
+        ALOGE("initMPEGDDDrc failed with error %d", err_code);
+        deInitMPEGDDDrc();
+        return err_code;
+    }
+
+    /* DRC buffers
+        buf[0] - contains extension element pay load loudness related
+        buf[1] - contains extension element pay load*/
+    {
+        VOID* p_array[2][16];
+        WORD32 ii;
+        WORD32 buf_sizes[2][16];
+        WORD32 num_elements;
+        WORD32 num_config_ext;
+        WORD32 bit_str_fmt = 1;
+
+        WORD32 uo_num_chan;
+
+        memset(buf_sizes, 0, 32 * sizeof(WORD32));
+
+        err_code =
+            ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                             IA_ENHAACPLUS_DEC_CONFIG_EXT_ELE_BUF_SIZES, &buf_sizes[0][0]);
+        RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_EXT_ELE_BUF_SIZES");
+
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                    IA_ENHAACPLUS_DEC_CONFIG_EXT_ELE_PTR, &p_array);
+        RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_EXT_ELE_PTR");
+
+        err_code =
+            ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, IA_CMD_TYPE_INIT_SET_BUFF_PTR, nullptr);
+        RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_SET_BUFF_PTR");
+
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                    IA_ENHAACPLUS_DEC_CONFIG_NUM_ELE, &num_elements);
+        RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_NUM_ELE");
+
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                    IA_ENHAACPLUS_DEC_CONFIG_NUM_CONFIG_EXT, &num_config_ext);
+        RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_NUM_CONFIG_EXT");
+
+        for (ii = 0; ii < num_config_ext; ii++) {
+            /*copy loudness bitstream*/
+            if (buf_sizes[0][ii] > 0) {
+                memcpy(mDrcInBuf, p_array[0][ii], buf_sizes[0][ii]);
+
+                /*Set bitstream_split_format */
+                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                          IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
+                RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+                /* Set number of bytes to be processed */
+                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_IL_BS, 0,
+                                          &buf_sizes[0][ii]);
+                RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES_IL_BS");
+
+                /* Execute process */
+                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+                                          IA_CMD_TYPE_INIT_CPY_IL_BSF_BUFF, nullptr);
+                RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_CPY_IL_BSF_BUFF");
+
+                mDRCFlag = 1;
+            }
+        }
+
+        for (ii = 0; ii < num_elements; ii++) {
+            /*copy config bitstream*/
+            if (buf_sizes[1][ii] > 0) {
+                memcpy(mDrcInBuf, p_array[1][ii], buf_sizes[1][ii]);
+                /* Set number of bytes to be processed */
+
+                /*Set bitstream_split_format */
+                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                          IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
+                RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_IC_BS, 0,
+                                          &buf_sizes[1][ii]);
+                RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES_IC_BS");
+
+                /* Execute process */
+                err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+                                          IA_CMD_TYPE_INIT_CPY_IC_BSF_BUFF, nullptr);
+
+                RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_CPY_IC_BSF_BUFF");
+
+                mDRCFlag = 1;
+            }
+        }
+
+        if (mDRCFlag == 1) {
+            mMpegDDRCPresent = 1;
+        } else {
+            mMpegDDRCPresent = 0;
+        }
+
+        /*Read interface buffer config file bitstream*/
+        if (mMpegDDRCPresent == 1) {
+            WORD32 interface_is_present = 1;
+
+            if (i_sbr_mode != 0) {
+                if (i_sbr_mode == 1) {
+                    mOutputFrameLength = 2048;
+                } else if (i_sbr_mode == 3) {
+                    mOutputFrameLength = 4096;
+                } else {
+                    mOutputFrameLength = 1024;
+                }
+            } else {
+                mOutputFrameLength = 4096;
+            }
+
+            err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                      IA_DRC_DEC_CONFIG_PARAM_FRAME_SIZE, (WORD32 *)&mOutputFrameLength);
+            RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_FRAME_SIZE");
+
+            err_code =
+                ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                               IA_DRC_DEC_CONFIG_PARAM_INT_PRESENT, &interface_is_present);
+            RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_INT_PRESENT");
+
+            /* Execute process */
+            err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+                                      IA_CMD_TYPE_INIT_CPY_IN_BSF_BUFF, nullptr);
+            RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_CPY_IN_BSF_BUFF");
+
+            err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+                                      IA_CMD_TYPE_INIT_PROCESS, nullptr);
+            RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_PROCESS");
+
+            err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                      IA_DRC_DEC_CONFIG_PARAM_NUM_CHANNELS, &uo_num_chan);
+            RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_NUM_CHANNELS");
+        }
+    }
+
+    return err_code;
+}
+
+IA_ERRORCODE C2SoftXaacDec::decodeXAACStream(uint8_t* inBuffer,
+                                 uint32_t inBufferLength,
+                                 int32_t* bytesConsumed,
+                                 int32_t* outBytes) {
+    if (mInputBufferSize < inBufferLength) {
+        ALOGE("Cannot config AAC, input buffer size %d < inBufferLength %d", mInputBufferSize, inBufferLength);
+        return -1;
+    }
+    /* Copy the buffer passed by Android plugin to codec input buffer */
+    memcpy(mInputBuffer, inBuffer, inBufferLength);
+
+    /* Set number of bytes to be processed */
+    IA_ERRORCODE err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                             IA_API_CMD_SET_INPUT_BYTES,
+                                             0,
+                                             &inBufferLength);
+    RETURN_IF_FATAL(err_code,  "IA_API_CMD_SET_INPUT_BYTES");
+
+    /* Execute process */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_EXECUTE,
+                                IA_CMD_TYPE_DO_EXECUTE,
+                                nullptr);
+    RETURN_IF_FATAL(err_code,  "IA_CMD_TYPE_DO_EXECUTE");
+
+    /* Checking for end of processing */
+    uint32_t ui_exec_done;
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_EXECUTE,
+                                IA_CMD_TYPE_DONE_QUERY,
+                                &ui_exec_done);
+    RETURN_IF_FATAL(err_code,  "IA_CMD_TYPE_DONE_QUERY");
+
+    if (ui_exec_done != 1) {
+        VOID* p_array;        // ITTIAM:buffer to handle gain payload
+        WORD32 buf_size = 0;  // ITTIAM:gain payload length
+        WORD32 bit_str_fmt = 1;
+        WORD32 gain_stream_flag = 1;
+
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                    IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size);
+        RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN");
+
+        err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                    IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array);
+        RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF");
+
+        if (buf_size > 0) {
+            /*Set bitstream_split_format */
+            err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                      IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt);
+            RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+            memcpy(mDrcInBuf, p_array, buf_size);
+            /* Set number of bytes to be processed */
+            err_code =
+                ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS, 0, &buf_size);
+            RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+            err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                      IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, &gain_stream_flag);
+            RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+            /* Execute process */
+            err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT,
+                                      IA_CMD_TYPE_INIT_CPY_BSF_BUFF, nullptr);
+            RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT");
+
+            mMpegDDRCPresent = 1;
+        }
+    }
+
+    /* How much buffer is used in input buffers */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_GET_CURIDX_INPUT_BUF,
+                                0,
+                                bytesConsumed);
+    RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_CURIDX_INPUT_BUF");
+
+    /* Get the output bytes */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_GET_OUTPUT_BYTES,
+                                0,
+                                outBytes);
+    RETURN_IF_FATAL(err_code,  "IA_API_CMD_GET_OUTPUT_BYTES");
+
+    if (mMpegDDRCPresent == 1) {
+        memcpy(mDrcInBuf, mOutputBuffer, *outBytes);
+        err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES, 0, outBytes);
+        RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES");
+
+        err_code =
+            ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, nullptr);
+        RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE");
+
+        memcpy(mOutputBuffer, mDrcOutBuf, *outBytes);
+    }
+    return IA_NO_ERROR;
+}
+
+IA_ERRORCODE C2SoftXaacDec::getXAACStreamInfo() {
+    IA_ERRORCODE err_code = IA_NO_ERROR;
+
+    /* Sampling frequency */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ,
+                                &mSampFreq);
+    RETURN_IF_FATAL(err_code,  "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ");
+
+    /* Total Number of Channels */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS,
+                                &mNumChannels);
+    RETURN_IF_FATAL(err_code,  "IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS");
+    if (mNumChannels > MAX_CHANNEL_COUNT) {
+        ALOGE(" No of channels are more than max channels\n");
+        return IA_FATAL_ERROR;
+    }
+
+    /* PCM word size */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ,
+                                &mPcmWdSz);
+    RETURN_IF_FATAL(err_code,  "IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ");
+    if ((mPcmWdSz / 8) != 2) {
+        ALOGE(" No of channels are more than max channels\n");
+        return IA_FATAL_ERROR;
+    }
+
+    /* channel mask to tell the arrangement of channels in bit stream */
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK,
+                                &mChannelMask);
+    RETURN_IF_FATAL(err_code,  "IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK");
+
+    /* Channel mode to tell MONO/STEREO/DUAL-MONO/NONE_OF_THESE */
+    uint32_t ui_channel_mode;
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MODE,
+                                &ui_channel_mode);
+    RETURN_IF_FATAL(err_code,  "IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MODE");
+    if (ui_channel_mode == 0)
+        ALOGV("Channel Mode: MONO_OR_PS\n");
+    else if (ui_channel_mode == 1)
+        ALOGV("Channel Mode: STEREO\n");
+    else if (ui_channel_mode == 2)
+        ALOGV("Channel Mode: DUAL-MONO\n");
+    else
+        ALOGV("Channel Mode: NONE_OF_THESE or MULTICHANNEL\n");
+
+    /* Channel mode to tell SBR PRESENT/NOT_PRESENT */
+    uint32_t ui_sbr_mode;
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle,
+                                IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE,
+                                &ui_sbr_mode);
+    RETURN_IF_FATAL(err_code,  "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE");
+    if (ui_sbr_mode == 0)
+        ALOGV("SBR Mode: NOT_PRESENT\n");
+    else if (ui_sbr_mode == 1)
+        ALOGV("SBR Mode: PRESENT\n");
+    else
+        ALOGV("SBR Mode: ILLEGAL\n");
+
+    /* mOutputFrameLength = 1024 * (1 + SBR_MODE) for AAC */
+    /* For USAC it could be 1024 * 3 , support to query  */
+    /* not yet added in codec                            */
+    mOutputFrameLength = 1024 * (1 + ui_sbr_mode);
+    ALOGI("mOutputFrameLength %d ui_sbr_mode %d", mOutputFrameLength, ui_sbr_mode);
+
+    return IA_NO_ERROR;
+}
+
+IA_ERRORCODE C2SoftXaacDec::setXAACDRCInfo(int32_t drcCut, int32_t drcBoost,
+                                           int32_t drcRefLevel,
+                                           int32_t drcHeavyCompression,
+                                           int32_t drEffectType) {
+    IA_ERRORCODE err_code = IA_NO_ERROR;
+
+    int32_t ui_drc_enable = 1;
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_ENABLE,
+                                &ui_drc_enable);
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_ENABLE");
+    if (drcCut != -1) {
+        err_code =
+            ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                             IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT, &drcCut);
+        RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT");
+    }
+
+    if (drcBoost != -1) {
+        err_code = ixheaacd_dec_api(
+            mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM,
+            IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST, &drcBoost);
+        RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST");
+    }
+
+    if (drcRefLevel != -1) {
+        err_code = ixheaacd_dec_api(
+            mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM,
+            IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL, &drcRefLevel);
+        RETURN_IF_FATAL(err_code,
+                        "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL");
+    }
+
+    if (drcRefLevel != -1) {
+        err_code = ixheaacd_dec_api(
+            mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM,
+            IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS, &drcRefLevel);
+        RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS");
+    }
+
+    if (drcHeavyCompression != -1) {
+        err_code =
+            ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                             IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP,
+                             &drcHeavyCompression);
+        RETURN_IF_FATAL(err_code,
+                        "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP");
+    }
+
+    err_code =
+        ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                         IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE, &drEffectType);
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE");
+
+    int32_t i_effect_type, i_target_loudness, i_loud_norm;
+    /*Set Effect Type*/
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE,
+                                &i_effect_type);
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE");
+
+    err_code =
+        ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                       IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE, &i_effect_type);
+
+    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE");
+
+    /*Set target loudness */
+    err_code = ixheaacd_dec_api(
+        mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+        IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS, &i_target_loudness);
+    RETURN_IF_FATAL(err_code,
+                    "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS");
+
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                              IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS,
+                              &i_target_loudness);
+    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS");
+
+    /*Set loud_norm_flag*/
+    err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM,
+                                IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM,
+                                &i_loud_norm);
+    RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM");
+
+    err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM,
+                              IA_DRC_DEC_CONFIG_DRC_LOUD_NORM, &i_loud_norm);
+
+    RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_LOUD_NORM");
+
+    return IA_NO_ERROR;
+}
+
+class C2SoftXaacDecFactory : public C2ComponentFactory {
+public:
+    C2SoftXaacDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
+            GetCodec2PlatformComponentStore()->getParamReflector())) {
+    }
+
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(
+                new C2SoftXaacDec(COMPONENT_NAME,
+                               id,
+                               std::make_shared<C2SoftXaacDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = std::shared_ptr<C2ComponentInterface>(
+                new SimpleInterface<C2SoftXaacDec::IntfImpl>(
+                        COMPONENT_NAME, id, std::make_shared<C2SoftXaacDec::IntfImpl>(mHelper)),
+                deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftXaacDecFactory() override = default;
+
+private:
+    std::shared_ptr<C2ReflectorHelper> mHelper;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftXaacDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/codec2/components/xaac/C2SoftXaacDec.h b/media/codec2/components/xaac/C2SoftXaacDec.h
new file mode 100644
index 0000000..5c8567f
--- /dev/null
+++ b/media/codec2/components/xaac/C2SoftXaacDec.h
@@ -0,0 +1,131 @@
+/*
+ * 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_C2_SOFT_XAAC_DEC_H_
+#define ANDROID_C2_SOFT_XAAC_DEC_H_
+#include <utils/Vector.h>
+#include <SimpleC2Component.h>
+
+#include "ixheaacd_type_def.h"
+#include "ixheaacd_error_standards.h"
+#include "ixheaacd_error_handler.h"
+#include "ixheaacd_apicmd_standards.h"
+#include "ixheaacd_memory_standards.h"
+#include "ixheaacd_aac_config.h"
+
+#include "impd_apicmd_standards.h"
+#include "impd_drc_config_params.h"
+
+#define MAX_CHANNEL_COUNT           8  /* maximum number of audio channels that can be decoded */
+#define MAX_NUM_BLOCKS              8  /* maximum number of audio blocks that can be decoded */
+
+extern "C" IA_ERRORCODE ixheaacd_dec_api(pVOID p_ia_module_obj,
+                        WORD32 i_cmd, WORD32 i_idx, pVOID pv_value);
+extern "C" IA_ERRORCODE ia_drc_dec_api(pVOID p_ia_module_obj,
+                        WORD32 i_cmd, WORD32 i_idx, pVOID pv_value);
+extern "C"  IA_ERRORCODE ixheaacd_get_config_param(pVOID p_ia_process_api_obj,
+                                       pWORD32 pi_samp_freq,
+                                       pWORD32 pi_num_chan,
+                                       pWORD32 pi_pcm_wd_sz,
+                                       pWORD32 pi_channel_mask);
+
+namespace android {
+
+struct C2SoftXaacDec : public SimpleC2Component {
+    class IntfImpl;
+
+    C2SoftXaacDec(const char* name, c2_node_id_t id,
+               const std::shared_ptr<IntfImpl>& intfImpl);
+    virtual ~C2SoftXaacDec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    enum {
+        kOutputDrainBufferSize      = 2048 * MAX_CHANNEL_COUNT * MAX_NUM_BLOCKS,
+    };
+
+    std::shared_ptr<IntfImpl> mIntf;
+    void* mXheaacCodecHandle;
+    void* mMpegDDrcHandle;
+    uint32_t mInputBufferSize;
+    uint32_t mOutputFrameLength;
+    int8_t* mInputBuffer;
+    int8_t* mOutputBuffer;
+    int32_t mSampFreq;
+    int32_t mNumChannels;
+    int32_t mPcmWdSz;
+    int32_t mChannelMask;
+    int32_t mNumOutBytes;
+    uint64_t mCurFrameIndex;
+    uint64_t mCurTimestamp;
+    bool mIsCodecInitialized;
+    bool mIsCodecConfigFlushRequired;
+    int8_t* mDrcInBuf;
+    int8_t* mDrcOutBuf;
+    int32_t mMpegDDRCPresent;
+    int32_t mDRCFlag;
+
+    Vector<void*> mMemoryVec;
+    Vector<void*> mDrcMemoryVec;
+
+    size_t mInputBufferCount __unused;
+    size_t mOutputBufferCount __unused;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+    char* mOutputDrainBuffer;
+    uint32_t mOutputDrainBufferWritePos;
+
+    IA_ERRORCODE initDecoder();
+    IA_ERRORCODE setDrcParameter();
+    IA_ERRORCODE configflushDecode();
+    IA_ERRORCODE drainDecoder();
+    void finishWork(const std::unique_ptr<C2Work>& work,
+                    const std::shared_ptr<C2BlockPool>& pool);
+
+    IA_ERRORCODE initXAACDrc();
+    IA_ERRORCODE initXAACDecoder();
+    IA_ERRORCODE deInitXAACDecoder();
+    IA_ERRORCODE initMPEGDDDrc();
+    IA_ERRORCODE deInitMPEGDDDrc();
+    IA_ERRORCODE configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength);
+    int configMPEGDDrc();
+    IA_ERRORCODE decodeXAACStream(uint8_t* inBuffer,
+                         uint32_t inBufferLength,
+                         int32_t* bytesConsumed,
+                         int32_t* outBytes);
+    IA_ERRORCODE getXAACStreamInfo();
+    IA_ERRORCODE setXAACDRCInfo(int32_t drcCut, int32_t drcBoost,
+                                int32_t drcRefLevel, int32_t drcHeavyCompression,
+                                int32_t drEffectType);
+
+    C2_DO_NOT_COPY(C2SoftXaacDec);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_XAAC_H_
diff --git a/media/codec2/core/Android.bp b/media/codec2/core/Android.bp
new file mode 100644
index 0000000..a7e8997
--- /dev/null
+++ b/media/codec2/core/Android.bp
@@ -0,0 +1,48 @@
+cc_library_headers {
+    name: "libcodec2_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+}
+
+cc_library_shared {
+    name: "libcodec2",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+
+    srcs: ["C2.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    header_libs: [
+        "libcodec2_headers",
+        "libhardware_headers",
+        "libutils_headers",
+        "media_plugin_headers",
+    ],
+
+    export_header_lib_headers: [
+        "libcodec2_headers",
+        "libhardware_headers",
+        "libutils_headers",
+        "media_plugin_headers",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+        cfi: false, // true,
+        diag: {
+            cfi: false, // true,
+        },
+    },
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
diff --git a/media/codec2/core/C2.cpp b/media/codec2/core/C2.cpp
new file mode 100644
index 0000000..359d4e5
--- /dev/null
+++ b/media/codec2/core/C2.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#include <C2.h>
+#include <C2Buffer.h>
+#include <C2Component.h>
+#include <C2Config.h>
+#include <C2Param.h>
+#include <C2ParamDef.h>
+#include <C2Work.h>
+
+/**
+ * There is nothing here yet. This library is built to see what symbols and methods get
+ * defined as part of the API include files.
+ *
+ * Going forward, the Codec2 library will contain utility methods that are useful for
+ * Codec2 clients.
+ */
+
+
diff --git a/media/codec2/core/include/C2.h b/media/codec2/core/include/C2.h
new file mode 100644
index 0000000..ef3466d
--- /dev/null
+++ b/media/codec2/core/include/C2.h
@@ -0,0 +1,552 @@
+/*
+ * 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.
+ */
+
+#ifndef C2_H_
+#define C2_H_
+
+#include <errno.h>
+
+#include <string>
+
+/** nanoseconds with arbitrary origin. */
+typedef int64_t c2_nsecs_t;
+
+/** \mainpage Codec2
+ *
+ * Codec2 is a generic frame-based data processing API.
+ *
+ * The media subsystem accesses components via the \ref API.
+ */
+
+/** \ingroup API
+ *
+ * The Codec2 API defines the operation of data processing components and their interaction with
+ * the rest of the system.
+ *
+ * Coding Conventions
+ *
+ * Mitigating Binary Compatibility.
+ *
+ * While full binary compatibility is not a goal of the API (due to our use of STL), we try to
+ * mitigate binary breaks by adhering to the following conventions:
+ *
+ * - at most one vtable with placeholder virtual methods
+ * - all optional/placeholder virtual methods returning a c2_status_t, with C2_OMITTED not requiring
+ *   any update to input/output arguments.
+ * - limiting symbol export of inline methods
+ * - use of pimpl (or shared-pimpl)
+ *
+ * Naming
+ *
+ * - all classes and types prefix with C2
+ * - classes for internal use prefix with _C2
+ * - enum values in global namespace prefix with C2_ all caps
+ * - enum values inside classes have no C2_ prefix as class already has it
+ * - supporting two kinds of enum naming: all-caps and kCamelCase
+ * \todo revisit kCamelCase for param-type
+ *
+ * Aspects
+ *
+ * Aspects define certain common behavior across a group of objects.
+ * - classes whose name matches _C2.*Aspect
+ * - only protected constructors
+ * - no desctructor and copiable
+ * - all methods are inline or static (this is opposite of the interface paradigm where all methods
+ *   are virtual, which would not work due to the at most one vtable rule.)
+ * - only private variables (this prevents subclasses interfering with the aspects.)
+ */
+
+/// \defgroup types Common Types
+/// @{
+
+/**
+ * C2String: basic string implementation
+ */
+typedef std::string C2String;
+
+/**
+ * C2StringLiteral: basic string literal implementation.
+ * \note these are never owned by any object, and can only refer to C string literals.
+ */
+typedef const char *C2StringLiteral;
+
+/**
+ * c2_status_t: status codes used.
+ */
+enum c2_status_t : int32_t {
+/*
+ * Use POSIX errno constants.
+ */
+    C2_OK        = 0,            ///< operation completed successfully
+
+    // bad input
+    C2_BAD_VALUE = EINVAL,       ///< argument has invalid value (user error)
+    C2_BAD_INDEX = ENXIO,        ///< argument uses invalid index (user error)
+    C2_CANNOT_DO = ENOTSUP,      ///< argument/index is valid but not possible
+
+    // bad sequencing of events
+    C2_DUPLICATE = EEXIST,       ///< object already exists
+    C2_NOT_FOUND = ENOENT,       ///< object not found
+    C2_BAD_STATE = EPERM,        ///< operation is not permitted in the current state
+    C2_BLOCKING  = EWOULDBLOCK,  ///< operation would block but blocking is not permitted
+    C2_CANCELED  = EINTR,        ///< operation interrupted/canceled
+
+    // bad environment
+    C2_NO_MEMORY = ENOMEM,       ///< not enough memory to complete operation
+    C2_REFUSED   = EACCES,       ///< missing permission to complete operation
+
+    C2_TIMED_OUT = ETIMEDOUT,    ///< operation did not complete within timeout
+
+    // bad versioning
+    C2_OMITTED   = ENOSYS,       ///< operation is not implemented/supported (optional only)
+
+    // unknown fatal
+    C2_CORRUPTED = EFAULT,       ///< some unexpected error prevented the operation
+    C2_NO_INIT   = ENODEV,       ///< status has not been initialized
+};
+
+/**
+ * Type that describes the desired blocking behavior for variable blocking calls. Blocking in this
+ * API is used in a somewhat modified meaning such that operations that merely update variables
+ * protected by mutexes are still considered "non-blocking" (always used in quotes).
+ */
+enum c2_blocking_t : int32_t {
+    /**
+     * The operation SHALL be "non-blocking". This means that it shall not perform any file
+     * operations, or call/wait on other processes. It may use a protected region as long as the
+     * mutex is never used to protect code that is otherwise "may block".
+     */
+    C2_DONT_BLOCK = false,
+    /**
+     * The operation MAY be temporarily blocking.
+     */
+    C2_MAY_BLOCK = true,
+};
+
+/// @}
+
+/// \defgroup utils Utilities
+/// @{
+
+#define C2_DO_NOT_COPY(type) \
+    type& operator=(const type &) = delete; \
+    type(const type &) = delete; \
+
+#define C2_DEFAULT_MOVE(type) \
+    type& operator=(type &&) = default; \
+    type(type &&) = default; \
+
+#define C2_ALLOW_OVERFLOW __attribute__((no_sanitize("integer")))
+#define C2_CONST    __attribute__((const))
+#define C2_HIDE     __attribute__((visibility("hidden")))
+#define C2_INLINE   inline C2_HIDE
+#define C2_INTERNAL __attribute__((internal_linkage))
+#define C2_PACK     __attribute__((aligned(4)))
+#define C2_PURE     __attribute__((pure))
+
+#define DEFINE_OTHER_COMPARISON_OPERATORS(type) \
+    inline bool operator!=(const type &other) const { return !(*this == other); } \
+    inline bool operator<=(const type &other) const { return (*this == other) || (*this < other); } \
+    inline bool operator>=(const type &other) const { return !(*this < other); } \
+    inline bool operator>(const type &other) const { return !(*this < other) && !(*this == other); }
+
+#define DEFINE_FIELD_BASED_COMPARISON_OPERATORS(type, field) \
+    inline bool operator<(const type &other) const { return field < other.field; } \
+    inline bool operator==(const type &other) const { return field == other.field; } \
+    DEFINE_OTHER_COMPARISON_OPERATORS(type)
+
+#define DEFINE_FIELD_AND_MASK_BASED_COMPARISON_OPERATORS(type, field, mask) \
+    inline bool operator<(const type &other) const { \
+        return (field & mask) < (other.field & (mask)); \
+    } \
+    inline bool operator==(const type &other) const { \
+        return (field & mask) == (other.field & (mask)); \
+    } \
+    DEFINE_OTHER_COMPARISON_OPERATORS(type)
+
+#define DEFINE_ENUM_OPERATORS(etype) \
+    inline constexpr etype operator|(etype a, etype b) { return (etype)(std::underlying_type<etype>::type(a) | std::underlying_type<etype>::type(b)); } \
+    inline constexpr etype &operator|=(etype &a, etype b) { a = (etype)(std::underlying_type<etype>::type(a) | std::underlying_type<etype>::type(b)); return a; } \
+    inline constexpr etype operator&(etype a, etype b) { return (etype)(std::underlying_type<etype>::type(a) & std::underlying_type<etype>::type(b)); } \
+    inline constexpr etype &operator&=(etype &a, etype b) { a = (etype)(std::underlying_type<etype>::type(a) & std::underlying_type<etype>::type(b)); return a; } \
+    inline constexpr etype operator^(etype a, etype b) { return (etype)(std::underlying_type<etype>::type(a) ^ std::underlying_type<etype>::type(b)); } \
+    inline constexpr etype &operator^=(etype &a, etype b) { a = (etype)(std::underlying_type<etype>::type(a) ^ std::underlying_type<etype>::type(b)); return a; } \
+    inline constexpr etype operator~(etype a) { return (etype)(~std::underlying_type<etype>::type(a)); }
+
+template<typename T, typename B>
+class C2_HIDE c2_cntr_t;
+
+/// \cond INTERNAL
+
+/// \defgroup utils_internal
+/// @{
+
+template<typename T>
+struct C2_HIDE _c2_cntr_compat_helper {
+    template<typename U, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
+    C2_ALLOW_OVERFLOW
+    inline static constexpr T get(const U &value) {
+        return T(value);
+    }
+
+    template<typename U, typename E=typename std::enable_if<(sizeof(U) >= sizeof(T))>::type>
+    C2_ALLOW_OVERFLOW
+    inline static constexpr T get(const c2_cntr_t<U, void> &value) {
+        return T(value.mValue);
+    }
+};
+
+/// @}
+
+/// \endcond
+
+/**
+ * Integral counter type.
+ *
+ * This is basically an unsigned integral type that is NEVER checked for overflow/underflow - and
+ * comparison operators are redefined.
+ *
+ * \note Comparison of counter types is not fully transitive, e.g.
+ * it could be that a > b > c but a !> c.
+ * std::less<>, greater<>, less_equal<> and greater_equal<> specializations yield total ordering,
+ * but may not match semantic ordering of the values.
+ *
+ * Technically: counter types represent integer values: A * 2^N + value, where A can be arbitrary.
+ * This makes addition, subtraction, multiplication (as well as bitwise operations) well defined.
+ * However, division is in general not well defined, as the result may depend on A. This is also
+ * true for logical operators and boolean conversion.
+ *
+ * Even though well defined, bitwise operators are not implemented for counter types as they are not
+ * meaningful.
+ */
+template<typename T, typename B=typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value>::type>
+class C2_HIDE c2_cntr_t {
+    using compat = _c2_cntr_compat_helper<T>;
+
+    T mValue;
+    constexpr static T HALF_RANGE = T(~0) ^ (T(~0) >> 1);
+
+    template<typename U>
+    friend struct _c2_cntr_compat_helper;
+public:
+
+    /**
+     * Default constructor. Initialized counter to 0.
+     */
+    inline constexpr c2_cntr_t() : mValue(T(0)) {}
+
+    /**
+     * Construct from a compatible type.
+     */
+    template<typename U>
+    inline constexpr c2_cntr_t(const U &value) : mValue(compat::get(value)) {}
+
+    /**
+     * Peek as underlying signed type.
+     */
+    C2_ALLOW_OVERFLOW
+    inline constexpr typename std::make_signed<T>::type peek() const {
+        return static_cast<typename std::make_signed<T>::type>(mValue);
+    }
+
+    /**
+     * Peek as underlying unsigned type.
+     */
+    inline constexpr T peeku() const {
+        return mValue;
+    }
+
+    /**
+     * Peek as long long - e.g. for printing.
+     */
+    C2_ALLOW_OVERFLOW
+    inline constexpr long long peekll() const {
+        return (long long)mValue;
+    }
+
+    /**
+     * Peek as unsigned long long - e.g. for printing.
+     */
+    C2_ALLOW_OVERFLOW
+    inline constexpr unsigned long long peekull() const {
+        return (unsigned long long)mValue;
+    }
+
+    /**
+     * Convert to a smaller counter type. This is always safe.
+     */
+    template<typename U, typename E=typename std::enable_if<(sizeof(U) < sizeof(T))>::type>
+    inline constexpr operator c2_cntr_t<U>() {
+        return c2_cntr_t<U>(mValue);
+    }
+
+    /**
+     * Arithmetic operators
+     */
+
+#define DEFINE_C2_CNTR_BINARY_OP(attrib, op, op_assign) \
+    template<typename U> \
+    attrib inline c2_cntr_t<T>& operator op_assign(const U &value) { \
+        mValue op_assign compat::get(value); \
+        return *this; \
+    } \
+    \
+    template<typename U, typename E=decltype(compat::get(U(0)))> \
+    attrib inline constexpr c2_cntr_t<T> operator op(const U &value) const { \
+        return c2_cntr_t<T>(mValue op compat::get(value)); \
+    } \
+    \
+    template<typename U, typename E=typename std::enable_if<(sizeof(U) < sizeof(T))>::type> \
+    attrib inline constexpr c2_cntr_t<U> operator op(const c2_cntr_t<U> &value) const { \
+        return c2_cntr_t<U>(U(mValue) op value.peeku()); \
+    }
+
+#define DEFINE_C2_CNTR_UNARY_OP(attrib, op) \
+    attrib inline constexpr c2_cntr_t<T> operator op() const { \
+        return c2_cntr_t<T>(op mValue); \
+    }
+
+#define DEFINE_C2_CNTR_CREMENT_OP(attrib, op) \
+    attrib inline c2_cntr_t<T> &operator op() { \
+        op mValue; \
+        return *this; \
+    } \
+    attrib inline c2_cntr_t<T> operator op(int) { \
+        return c2_cntr_t<T, void>(mValue op); \
+    }
+
+    DEFINE_C2_CNTR_BINARY_OP(C2_ALLOW_OVERFLOW, +, +=)
+    DEFINE_C2_CNTR_BINARY_OP(C2_ALLOW_OVERFLOW, -, -=)
+    DEFINE_C2_CNTR_BINARY_OP(C2_ALLOW_OVERFLOW, *, *=)
+
+    DEFINE_C2_CNTR_UNARY_OP(C2_ALLOW_OVERFLOW, -)
+    DEFINE_C2_CNTR_UNARY_OP(C2_ALLOW_OVERFLOW, +)
+
+    DEFINE_C2_CNTR_CREMENT_OP(C2_ALLOW_OVERFLOW, ++)
+    DEFINE_C2_CNTR_CREMENT_OP(C2_ALLOW_OVERFLOW, --)
+
+    template<typename U, typename E=typename std::enable_if<std::is_unsigned<U>::value>::type>
+    C2_ALLOW_OVERFLOW
+    inline constexpr c2_cntr_t<T> operator<<(const U &value) const {
+        return c2_cntr_t<T>(mValue << value);
+    }
+
+    template<typename U, typename E=typename std::enable_if<std::is_unsigned<U>::value>::type>
+    C2_ALLOW_OVERFLOW
+    inline c2_cntr_t<T> &operator<<=(const U &value) {
+        mValue <<= value;
+        return *this;
+    }
+
+    /**
+     * Comparison operators
+     */
+    C2_ALLOW_OVERFLOW
+    inline constexpr bool operator<=(const c2_cntr_t<T> &other) const {
+        return T(other.mValue - mValue) < HALF_RANGE;
+    }
+
+    C2_ALLOW_OVERFLOW
+    inline constexpr bool operator>=(const c2_cntr_t<T> &other) const {
+        return T(mValue - other.mValue) < HALF_RANGE;
+    }
+
+    inline constexpr bool operator==(const c2_cntr_t<T> &other) const {
+        return mValue == other.mValue;
+    }
+
+    inline constexpr bool operator!=(const c2_cntr_t<T> &other) const {
+        return !(*this == other);
+    }
+
+    inline constexpr bool operator<(const c2_cntr_t<T> &other) const {
+        return *this <= other && *this != other;
+    }
+
+    inline constexpr bool operator>(const c2_cntr_t<T> &other) const {
+        return *this >= other && *this != other;
+    }
+};
+
+template<typename U, typename T, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
+inline constexpr c2_cntr_t<T> operator+(const U &a, const c2_cntr_t<T> &b) {
+    return b + a;
+}
+
+template<typename U, typename T, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
+inline constexpr c2_cntr_t<T> operator-(const U &a, const c2_cntr_t<T> &b) {
+    return c2_cntr_t<T>(a) - b;
+}
+
+template<typename U, typename T, typename E=typename std::enable_if<std::is_integral<U>::value>::type>
+inline constexpr c2_cntr_t<T> operator*(const U &a, const c2_cntr_t<T> &b) {
+    return b * a;
+}
+
+typedef c2_cntr_t<uint32_t> c2_cntr32_t; /** 32-bit counter type */
+typedef c2_cntr_t<uint64_t> c2_cntr64_t; /** 64-bit counter type */
+
+/// \cond INTERNAL
+
+/// \defgroup utils_internal
+/// @{
+
+template<typename... T> struct c2_types;
+
+/** specialization for a single type */
+template<typename T>
+struct c2_types<T> {
+    typedef typename std::decay<T>::type wide_type;
+    typedef wide_type narrow_type;
+    typedef wide_type min_type; // type for min(T...)
+};
+
+/** specialization for two types */
+template<typename T, typename U>
+struct c2_types<T, U> {
+    static_assert(std::is_floating_point<T>::value == std::is_floating_point<U>::value,
+                  "mixing floating point and non-floating point types is disallowed");
+    static_assert(std::is_signed<T>::value == std::is_signed<U>::value,
+                  "mixing signed and unsigned types is disallowed");
+
+    typedef typename std::decay<
+            decltype(true ? std::declval<T>() : std::declval<U>())>::type wide_type;
+    typedef typename std::decay<
+            typename std::conditional<sizeof(T) < sizeof(U), T, U>::type>::type narrow_type;
+    typedef typename std::conditional<
+            std::is_signed<T>::value, wide_type, narrow_type>::type min_type;
+};
+
+/// @}
+
+/// \endcond
+
+/**
+ * Type support utility class. Only supports similar classes, such as:
+ * - all floating point
+ * - all unsigned/all signed
+ * - all pointer
+ */
+template<typename T, typename U, typename... V>
+struct c2_types<T, U, V...> {
+    /** Common type that accommodates all template parameter types. */
+    typedef typename c2_types<typename c2_types<T, U>::wide_type, V...>::wide_type wide_type;
+    /** Narrowest type of the template parameter types. */
+    typedef typename c2_types<typename c2_types<T, U>::narrow_type, V...>::narrow_type narrow_type;
+    /** Type that accommodates the minimum value for any input for the template parameter types. */
+    typedef typename c2_types<typename c2_types<T, U>::min_type, V...>::min_type min_type;
+};
+
+/**
+ *  \ingroup utils_internal
+ * specialization for two values */
+template<typename T, typename U>
+inline constexpr typename c2_types<T, U>::wide_type c2_max(const T a, const U b) {
+    typedef typename c2_types<T, U>::wide_type wide_type;
+    return ({ wide_type a_(a), b_(b); a_ > b_ ? a_ : b_; });
+}
+
+/**
+ * Finds the maximum value of a list of "similarly typed" values.
+ *
+ * This is an extension to std::max where the types do not have to be identical, and the smallest
+ * resulting type is used that accommodates the argument types.
+ *
+ * \note Value types must be similar, e.g. all floating point, all pointers, all signed, or all
+ * unsigned.
+ *
+ * @return the largest of the input arguments.
+ */
+template<typename T, typename U, typename... V>
+constexpr typename c2_types<T, U, V...>::wide_type c2_max(const T a, const U b, const V ... c) {
+    typedef typename c2_types<T, U, V...>::wide_type wide_type;
+    return ({ wide_type a_(a), b_(c2_max(b, c...)); a_ > b_ ? a_ : b_; });
+}
+
+/**
+ *  \ingroup utils_internal
+ * specialization for two values */
+template<typename T, typename U>
+inline constexpr typename c2_types<T, U>::min_type c2_min(const T a, const U b) {
+    typedef typename c2_types<T, U>::wide_type wide_type;
+    return ({
+        wide_type a_(a), b_(b);
+        static_cast<typename c2_types<T, U>::min_type>(a_ < b_ ? a_ : b_);
+    });
+}
+
+/**
+ * Finds the minimum value of a list of "similarly typed" values.
+ *
+ * This is an extension to std::min where the types do not have to be identical, and the smallest
+ * resulting type is used that accommodates the argument types.
+ *
+ * \note Value types must be similar, e.g. all floating point, all pointers, all signed, or all
+ * unsigned.
+ *
+ * @return the smallest of the input arguments.
+ */
+template<typename T, typename U, typename... V>
+constexpr typename c2_types<T, U, V...>::min_type c2_min(const T a, const U b, const V ... c) {
+    typedef typename c2_types<U, V...>::min_type rest_type;
+    typedef typename c2_types<T, rest_type>::wide_type wide_type;
+    return ({
+        wide_type a_(a), b_(c2_min(b, c...));
+        static_cast<typename c2_types<T, rest_type>::min_type>(a_ < b_ ? a_ : b_);
+    });
+}
+
+/**
+ *  \ingroup utils_internal
+ */
+template<typename T, typename U, typename V>
+inline constexpr typename c2_types<T, V>::wide_type c2_clamp(const T a, const U b, const V c) {
+    typedef typename c2_types<T, U, V>::wide_type wide_type;
+    return ({
+        wide_type a_(a), b_(b), c_(c);
+        static_cast<typename c2_types<T, V>::wide_type>(b_ < a_ ? a_ : b_ > c_ ? c_ : b_);
+    });
+}
+
+/// @}
+
+#include <functional>
+template<typename T>
+struct std::less<::c2_cntr_t<T>> {
+    constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
+        return lh.peeku() < rh.peeku();
+    }
+};
+template<typename T>
+struct std::less_equal<::c2_cntr_t<T>> {
+    constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
+        return lh.peeku() <= rh.peeku();
+    }
+};
+template<typename T>
+struct std::greater<::c2_cntr_t<T>> {
+    constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
+        return lh.peeku() > rh.peeku();
+    }
+};
+template<typename T>
+struct std::greater_equal<::c2_cntr_t<T>> {
+    constexpr bool operator()(const ::c2_cntr_t<T> &lh, const ::c2_cntr_t<T> &rh) const {
+        return lh.peeku() >= rh.peeku();
+    }
+};
+
+#endif  // C2_H_
diff --git a/media/codec2/core/include/C2Buffer.h b/media/codec2/core/include/C2Buffer.h
new file mode 100644
index 0000000..3d3587c
--- /dev/null
+++ b/media/codec2/core/include/C2Buffer.h
@@ -0,0 +1,2184 @@
+/*
+ * 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.
+ */
+
+#ifndef C2BUFFER_H_
+#define C2BUFFER_H_
+
+#include <C2.h>
+#include <C2BufferBase.h>
+#include <C2Param.h> // for C2Info
+
+#include <memory>
+#include <vector>
+
+#ifdef __ANDROID__
+#include <android-C2Buffer.h>
+#else
+
+typedef void* C2Handle;
+
+#endif
+
+/// \defgroup buffer Buffers
+/// @{
+
+/// \defgroup buffer_sync Synchronization
+/// @{
+
+/**
+ * Synchronization is accomplished using event and fence objects.
+ *
+ * These are cross-process extensions of promise/future infrastructure.
+ * Events are analogous to std::promise<void>, whereas fences are to std::shared_future<void>.
+ *
+ * Fences and events are shareable/copyable.
+ *
+ * Fences are used in two scenarios, and all copied instances refer to the same event.
+ * \todo do events need to be copyable or should they be unique?
+ *
+ * acquire sync fence object: signaled when it is safe for the component or client to access
+ * (the contents of) an object.
+ *
+ * release sync fence object: \todo
+ *
+ * Fences can be backed by hardware. Hardware fences are guaranteed to signal NO MATTER WHAT within
+ * a short (platform specific) amount of time; this guarantee is usually less than 15 msecs.
+ */
+
+/**
+ * Fence object used by components and the framework.
+ *
+ * Implements the waiting for an event, analogous to a 'future'.
+ *
+ * To be implemented by vendors if using HW fences.
+ */
+class C2Fence {
+public:
+    /**
+     * Waits for a fence to be signaled with a timeout.
+     *
+     * \todo a mechanism to cancel a wait - for now the only way to do this is to abandon the
+     * event, but fences are shared so canceling a wait will cancel all waits.
+     *
+     * \param timeoutNs           the maximum time to wait in nsecs
+     *
+     * \retval C2_OK            the fence has been signaled
+     * \retval C2_TIMED_OUT     the fence has not been signaled within the timeout
+     * \retval C2_BAD_STATE     the fence has been abandoned without being signaled (it will never
+     *                          be signaled)
+     * \retval C2_REFUSED       no permission to wait for the fence (unexpected - system)
+     * \retval C2_CORRUPTED     some unknown error prevented waiting for the fence (unexpected)
+     */
+    c2_status_t wait(c2_nsecs_t timeoutNs);
+
+    /**
+     * Used to check if this fence is valid (if there is a chance for it to be signaled.)
+     * A fence becomes invalid if the controling event is destroyed without it signaling the fence.
+     *
+     * \return whether this fence is valid
+     */
+    bool valid() const;
+
+    /**
+     * Used to check if this fence has been signaled (is ready).
+     *
+     * \return whether this fence has been signaled
+     */
+    bool ready() const;
+
+    /**
+     * Returns a file descriptor that can be used to wait for this fence in a select system call.
+     * \note The returned file descriptor, if valid, must be closed by the caller.
+     *
+     * This can be used in e.g. poll() system calls. This file becomes readable (POLLIN) when the
+     * fence is signaled, and bad (POLLERR) if the fence is abandoned.
+     *
+     * \return a file descriptor representing this fence (with ownership), or -1 if the fence
+     * has already been signaled (\todo or abandoned).
+     *
+     * \todo this must be compatible with fences used by gralloc
+     */
+    int fd() const;
+
+    /**
+     * Returns whether this fence is a hardware-backed fence.
+     * \return whether this is a hardware fence
+     */
+    bool isHW() const;
+
+    /**
+     * Null-fence. A fence that has fired.
+     */
+    constexpr C2Fence() : mImpl(nullptr) { }
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+    C2Fence(std::shared_ptr<Impl> impl);
+    friend struct _C2FenceFactory;
+};
+
+/**
+ * Event object used by components and the framework.
+ *
+ * Implements the signaling of an event, analogous to a 'promise'.
+ *
+ * Hardware backed events do not go through this object, and must be exposed directly as fences
+ * by vendors.
+ */
+class C2Event {
+public:
+    /**
+     * Returns a fence for this event.
+     */
+    C2Fence fence() const;
+
+    /**
+     * Signals (all) associated fence(s).
+     * This has no effect no effect if the event was already signaled or abandoned.
+     *
+     * \retval C2_OK            the fence(s) were successfully signaled
+     * \retval C2_BAD_STATE     the fence(s) have already been abandoned or merged (caller error)
+     * \retval C2_DUPLICATE     the fence(s) have already been signaled (caller error)
+     * \retval C2_REFUSED       no permission to signal the fence (unexpected - system)
+     * \retval C2_CORRUPTED     some unknown error prevented signaling the fence(s) (unexpected)
+     */
+    c2_status_t fire();
+
+    /**
+     * Trigger this event from the merging of the supplied fences. This means that it will be
+     * abandoned if any of these fences have been abandoned, and it will be fired if all of these
+     * fences have been signaled.
+     *
+     * \retval C2_OK            the merging was successfully done
+     * \retval C2_NO_MEMORY     not enough memory to perform the merging
+     * \retval C2_DUPLICATE     the fence have already been merged (caller error)
+     * \retval C2_BAD_STATE     the fence have already been signaled or abandoned (caller error)
+     * \retval C2_REFUSED       no permission to merge the fence (unexpected - system)
+     * \retval C2_CORRUPTED     some unknown error prevented merging the fence(s) (unexpected)
+     */
+    c2_status_t merge(std::vector<C2Fence> fences);
+
+    /**
+     * Abandons the event and any associated fence(s).
+     * \note Call this to explicitly abandon an event before it is destructed to avoid a warning.
+     *
+     * This has no effect no effect if the event was already signaled or abandoned.
+     *
+     * \retval C2_OK            the fence(s) were successfully signaled
+     * \retval C2_BAD_STATE     the fence(s) have already been signaled or merged (caller error)
+     * \retval C2_DUPLICATE     the fence(s) have already been abandoned (caller error)
+     * \retval C2_REFUSED       no permission to abandon the fence (unexpected - system)
+     * \retval C2_CORRUPTED     some unknown error prevented signaling the fence(s) (unexpected)
+     */
+    c2_status_t abandon();
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/// \addtogroup buf_internal Internal
+/// @{
+
+/**
+ * Interface for objects that encapsulate an updatable status value.
+ */
+struct _C2InnateStatus {
+    inline c2_status_t status() const { return mStatus; }
+
+protected:
+    _C2InnateStatus(c2_status_t status) : mStatus(status) { }
+
+    c2_status_t mStatus; // this status is updatable by the object
+};
+
+/// @}
+
+/**
+ * This is a utility template for objects protected by an acquire fence, so that errors during
+ * acquiring the object are propagated to the object itself.
+ */
+template<typename T>
+class C2Acquirable : public C2Fence {
+public:
+    /**
+     * Acquires the object protected by an acquire fence. Any errors during the mapping will be
+     * passed to the object.
+     *
+     * \return acquired object potentially invalidated if waiting for the fence failed.
+     */
+    T get() {
+        // TODO:
+        // wait();
+        return mT;
+    }
+
+protected:
+    C2Acquirable(c2_status_t error, C2Fence fence, T t) : C2Fence(fence), mInitialError(error), mT(t) { }
+
+private:
+    c2_status_t mInitialError;
+    T mT; // TODO: move instead of copy
+};
+
+/// @}
+
+/// \defgroup linear Linear Data Blocks
+/// @{
+
+/**************************************************************************************************
+  LINEAR ASPECTS, BLOCKS AND VIEWS
+**************************************************************************************************/
+
+/**
+ * Basic segment math support.
+ */
+struct C2Segment {
+    uint32_t offset;
+    uint32_t size;
+
+    inline constexpr C2Segment(uint32_t offset_, uint32_t size_)
+        : offset(offset_),
+          size(size_) {
+    }
+
+    inline constexpr bool isEmpty() const {
+        return size == 0;
+    }
+
+    inline constexpr bool isValid() const {
+        return offset <= ~size;
+    }
+
+    inline constexpr operator bool() const {
+        return isValid() && !isEmpty();
+    }
+
+    inline constexpr bool operator!() const {
+        return !bool(*this);
+    }
+
+    C2_ALLOW_OVERFLOW
+    inline constexpr bool contains(const C2Segment &other) const {
+        if (!isValid() || !other.isValid()) {
+            return false;
+        } else {
+            return offset <= other.offset
+                    && offset + size >= other.offset + other.size;
+        }
+    }
+
+    inline constexpr bool operator==(const C2Segment &other) const {
+        if (!isValid()) {
+            return !other.isValid();
+        } else {
+            return offset == other.offset && size == other.size;
+        }
+    }
+
+    inline constexpr bool operator!=(const C2Segment &other) const {
+        return !operator==(other);
+    }
+
+    inline constexpr bool operator>=(const C2Segment &other) const {
+        return contains(other);
+    }
+
+    inline constexpr bool operator>(const C2Segment &other) const {
+        return contains(other) && !operator==(other);
+    }
+
+    inline constexpr bool operator<=(const C2Segment &other) const {
+        return other.contains(*this);
+    }
+
+    inline constexpr bool operator<(const C2Segment &other) const {
+        return other.contains(*this) && !operator==(other);
+    }
+
+    C2_ALLOW_OVERFLOW
+    inline constexpr uint32_t end() const {
+        return offset + size;
+    }
+
+    C2_ALLOW_OVERFLOW
+    inline constexpr C2Segment intersect(const C2Segment &other) const {
+        return C2Segment(c2_max(offset, other.offset),
+                         c2_min(end(), other.end()) - c2_max(offset, other.offset));
+    }
+
+    /** clamps end to offset if it overflows */
+    inline constexpr C2Segment normalize() const {
+        return C2Segment(offset, c2_max(offset, end()) - offset);
+    }
+
+    /** clamps end to max if it overflows */
+    inline constexpr C2Segment saturate() const {
+        return C2Segment(offset, c2_min(size, ~offset));
+    }
+
+};
+
+/**
+ * Common aspect for all objects that have a linear capacity.
+ */
+class _C2LinearCapacityAspect {
+/// \name Linear capacity interface
+/// @{
+public:
+    inline constexpr uint32_t capacity() const { return mCapacity; }
+
+    inline constexpr operator C2Segment() const {
+        return C2Segment(0, mCapacity);
+    }
+
+protected:
+
+#if UINTPTR_MAX == 0xffffffff
+    static_assert(sizeof(size_t) == sizeof(uint32_t), "size_t is too big");
+#else
+    static_assert(sizeof(size_t) > sizeof(uint32_t), "size_t is too small");
+    // explicitly disable construction from size_t
+    inline explicit _C2LinearCapacityAspect(size_t capacity) = delete;
+#endif
+
+    inline explicit constexpr _C2LinearCapacityAspect(uint32_t capacity)
+      : mCapacity(capacity) { }
+
+    inline explicit constexpr _C2LinearCapacityAspect(const _C2LinearCapacityAspect *parent)
+        : mCapacity(parent == nullptr ? 0 : parent->capacity()) { }
+
+private:
+    uint32_t mCapacity;
+/// @}
+};
+
+/**
+ * Aspect for objects that have a linear range inside a linear capacity.
+ *
+ * This class is copiable.
+ */
+class _C2LinearRangeAspect : public _C2LinearCapacityAspect {
+/// \name Linear range interface
+/// @{
+public:
+    inline constexpr uint32_t offset() const { return mOffset; }
+    inline constexpr uint32_t endOffset() const { return mOffset + mSize; }
+    inline constexpr uint32_t size() const { return mSize; }
+
+    inline constexpr operator C2Segment() const {
+        return C2Segment(mOffset, mSize);
+    }
+
+private:
+    // subrange of capacity [0, capacity] & [size, size + offset]
+    inline constexpr _C2LinearRangeAspect(uint32_t capacity_, size_t offset, size_t size)
+        : _C2LinearCapacityAspect(capacity_),
+          mOffset(c2_min(offset, capacity())),
+          mSize(c2_min(size, capacity() - mOffset)) {
+    }
+
+protected:
+    // copy constructor (no error check)
+    inline constexpr _C2LinearRangeAspect(const _C2LinearRangeAspect &other)
+        : _C2LinearCapacityAspect(other.capacity()),
+          mOffset(other.offset()),
+          mSize(other.size()) {
+    }
+
+    // parent capacity range [0, capacity]
+    inline constexpr explicit _C2LinearRangeAspect(const _C2LinearCapacityAspect *parent)
+        : _C2LinearCapacityAspect(parent),
+          mOffset(0),
+          mSize(capacity()) {
+    }
+
+    // subrange of parent capacity [0, capacity] & [size, size + offset]
+    inline constexpr _C2LinearRangeAspect(const _C2LinearCapacityAspect *parent, size_t offset, size_t size)
+        : _C2LinearCapacityAspect(parent),
+          mOffset(c2_min(offset, capacity())),
+          mSize(c2_min(size, capacity() - mOffset)) {
+    }
+
+    // subsection of the parent's and [offset, offset + size] ranges
+    inline constexpr _C2LinearRangeAspect(const _C2LinearRangeAspect *parent, size_t offset, size_t size)
+        : _C2LinearCapacityAspect(parent),
+          mOffset(c2_min(c2_max(offset, parent == nullptr ? 0 : parent->offset()), capacity())),
+          mSize(std::min(c2_min(size, parent == nullptr ? 0 : parent->size()), capacity() - mOffset)) {
+    }
+
+public:
+    inline constexpr _C2LinearRangeAspect childRange(size_t offset, size_t size) const {
+        return _C2LinearRangeAspect(
+            mSize,
+            c2_min(c2_max(offset, mOffset), capacity()) - mOffset,
+            c2_min(c2_min(size, mSize), capacity() - c2_min(c2_max(offset, mOffset), capacity())));
+    }
+
+    friend class _C2EditableLinearRangeAspect;
+    // invariants 0 <= mOffset <= mOffset + mSize <= capacity()
+    uint32_t mOffset;
+    uint32_t mSize;
+/// @}
+};
+
+/**
+ * Utility class for safe range calculations using size_t-s.
+ */
+class C2LinearRange : public _C2LinearRangeAspect {
+public:
+    inline constexpr C2LinearRange(const _C2LinearCapacityAspect &parent, size_t offset, size_t size)
+        : _C2LinearRangeAspect(&parent, offset, size) { }
+
+    inline constexpr C2LinearRange(const _C2LinearRangeAspect &parent, size_t offset, size_t size)
+        : _C2LinearRangeAspect(&parent, offset, size) { }
+
+    inline constexpr C2LinearRange intersect(size_t offset, size_t size) const {
+        return C2LinearRange(*this, offset, size);
+    }
+};
+
+/**
+ * Utility class for simple and safe capacity and range construction.
+ */
+class C2LinearCapacity : public _C2LinearCapacityAspect {
+public:
+    inline constexpr explicit C2LinearCapacity(size_t capacity)
+        : _C2LinearCapacityAspect(c2_min(capacity, std::numeric_limits<uint32_t>::max())) { }
+
+    inline constexpr C2LinearRange range(size_t offset, size_t size) const {
+        return C2LinearRange(*this, offset, size);
+    }
+};
+
+/**
+ * Aspect for objects that have an editable linear range.
+ *
+ * This class is copiable.
+ */
+class _C2EditableLinearRangeAspect : public _C2LinearRangeAspect {
+    using _C2LinearRangeAspect::_C2LinearRangeAspect;
+
+public:
+/// \name Editable linear range interface
+/// @{
+
+    /**
+     * Sets the offset to |offset|, while trying to keep the end of the buffer unchanged (e.g.
+     * size will grow if offset is decreased, and may shrink if offset is increased.) Returns
+     * true if successful, which is equivalent to if 0 <= |offset| <= capacity().
+     *
+     * Note: setting offset and size will yield different result depending on the order of the
+     * operations. Always set offset first to ensure proper size.
+     */
+    inline bool setOffset(uint32_t offset) {
+        if (offset > capacity()) {
+            return false;
+        }
+
+        if (offset > mOffset + mSize) {
+            mSize = 0;
+        } else {
+            mSize = mOffset + mSize - offset;
+        }
+        mOffset = offset;
+        return true;
+    }
+
+    /**
+     * Sets the size to |size|. Returns true if successful, which is equivalent to
+     * if 0 <= |size| <= capacity() - offset().
+     *
+     * Note: setting offset and size will yield different result depending on the order of the
+     * operations. Always set offset first to ensure proper size.
+     */
+    inline bool setSize(uint32_t size) {
+        if (size > capacity() - mOffset) {
+            return false;
+        } else {
+            mSize = size;
+            return true;
+        }
+    }
+
+    /**
+     * Sets the offset to |offset| with best effort. Same as setOffset() except that offset will
+     * be clamped to the buffer capacity.
+     *
+     * Note: setting offset and size (even using best effort) will yield different result depending
+     * on the order of the operations. Always set offset first to ensure proper size.
+     */
+    inline void setOffset_be(uint32_t offset) {
+        (void)setOffset(c2_min(offset, capacity()));
+    }
+
+    /**
+     * Sets the size to |size| with best effort. Same as setSize() except that the selected region
+     * will be clamped to the buffer capacity (e.g. size is clamped to [0, capacity() - offset()]).
+     *
+     * Note: setting offset and size (even using best effort) will yield different result depending
+     * on the order of the operations. Always set offset first to ensure proper size.
+     */
+    inline void setSize_be(uint32_t size) {
+        mSize = c2_min(size, capacity() - mOffset);
+    }
+/// @}
+};
+
+/**************************************************************************************************
+  ALLOCATIONS
+**************************************************************************************************/
+
+/// \ingroup allocator Allocation and memory placement
+/// @{
+
+class C2LinearAllocation;
+class C2GraphicAllocation;
+
+/**
+ *  Allocators are used by the framework to allocate memory (allocations) for buffers. They can
+ *  support either 1D or 2D allocations.
+ *
+ *  \note In theory they could support both, but in practice, we will use only one or the other.
+ *
+ *  Never constructed on stack.
+ *
+ *  Allocators are provided by vendors.
+ */
+class C2Allocator {
+public:
+    /**
+     * Allocator ID type.
+     */
+    typedef uint32_t id_t;
+    enum : id_t {
+        BAD_ID = 0xBADD, // invalid allocator ID
+    };
+
+    /**
+     * Allocation types. This is a bitmask and is used in C2Allocator::Info
+     * to list the supported allocation types of an allocator.
+     */
+    enum type_t : uint32_t {
+        LINEAR  = 1 << 0, //
+        GRAPHIC = 1 << 1,
+    };
+
+    /**
+     * Information about an allocator.
+     *
+     * Allocators don't have a query API so all queriable information is stored here.
+     */
+    struct Traits {
+        C2String name;              ///< allocator name
+        id_t id;                    ///< allocator ID
+        type_t supportedTypes;      ///< supported allocation types
+        C2MemoryUsage minimumUsage; ///< usage that is minimally required for allocations
+        C2MemoryUsage maximumUsage; ///< usage that is maximally allowed for allocations
+    };
+
+    /**
+     * Returns the unique name of this allocator.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \return the name of this allocator.
+     * \retval an empty string if there was not enough memory to allocate the actual name.
+     */
+    virtual C2String getName() const = 0;
+
+    /**
+     * Returns a unique ID for this allocator. This ID is used to get this allocator from the
+     * allocator store, and to identify this allocator across all processes.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \return a unique ID for this allocator.
+     */
+    virtual id_t getId() const = 0;
+
+    /**
+     * Returns the allocator traits.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * Allocators don't have a full-fledged query API, only this method.
+     *
+     * \return allocator information
+     */
+    virtual std::shared_ptr<const Traits> getTraits() const = 0;
+
+    /**
+     * Allocates a 1D allocation of given |capacity| and |usage|. If successful, the allocation is
+     * stored in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+     *
+     * \param capacity      the size of requested allocation (the allocation could be slightly
+     *                      larger, e.g. to account for any system-required alignment)
+     * \param usage         the memory usage info for the requested allocation. \note that the
+     *                      returned allocation may be later used/mapped with different usage.
+     *                      The allocator should layout the buffer to be optimized for this usage,
+     *                      but must support any usage. One exception: protected buffers can
+     *                      only be used in a protected scenario.
+     * \param allocation    pointer to where the allocation shall be stored on success. nullptr
+     *                      will be stored here on failure
+     *
+     * \retval C2_OK        the allocation was successful
+     * \retval C2_NO_MEMORY not enough memory to complete the allocation
+     * \retval C2_TIMED_OUT the allocation timed out
+     * \retval C2_REFUSED   no permission to complete the allocation
+     * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
+     * \retval C2_OMITTED   this allocator does not support 1D allocations
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+     */
+    virtual c2_status_t newLinearAllocation(
+            uint32_t capacity __unused, C2MemoryUsage usage __unused,
+            std::shared_ptr<C2LinearAllocation> *allocation /* nonnull */) {
+        *allocation = nullptr;
+        return C2_OMITTED;
+    }
+
+    /**
+     * (Re)creates a 1D allocation from a native |handle|. If successful, the allocation is stored
+     * in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+     *
+     * \param handle      the handle for the existing allocation. On success, the allocation will
+     *                    take ownership of |handle|.
+     * \param allocation  pointer to where the allocation shall be stored on success. nullptr
+     *                    will be stored here on failure
+     *
+     * \retval C2_OK        the allocation was recreated successfully
+     * \retval C2_NO_MEMORY not enough memory to recreate the allocation
+     * \retval C2_TIMED_OUT the recreation timed out (unexpected)
+     * \retval C2_REFUSED   no permission to recreate the allocation
+     * \retval C2_BAD_VALUE invalid handle (caller error)
+     * \retval C2_OMITTED   this allocator does not support 1D allocations
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+     */
+    virtual c2_status_t priorLinearAllocation(
+            const C2Handle *handle __unused,
+            std::shared_ptr<C2LinearAllocation> *allocation /* nonnull */) {
+        *allocation = nullptr;
+        return C2_OMITTED;
+    }
+
+    /**
+     * Allocates a 2D allocation of given |width|, |height|, |format| and |usage|. If successful,
+     * the allocation is stored in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+     *
+     * \param width         the width of requested allocation (the allocation could be slightly
+     *                      larger, e.g. to account for any system-required alignment)
+     * \param height        the height of requested allocation (the allocation could be slightly
+     *                      larger, e.g. to account for any system-required alignment)
+     * \param format        the pixel format of requested allocation. This could be a vendor
+     *                      specific format.
+     * \param usage         the memory usage info for the requested allocation. \note that the
+     *                      returned allocation may be later used/mapped with different usage.
+     *                      The allocator should layout the buffer to be optimized for this usage,
+     *                      but must support any usage. One exception: protected buffers can
+     *                      only be used in a protected scenario.
+     * \param allocation    pointer to where the allocation shall be stored on success. nullptr
+     *                      will be stored here on failure
+     *
+     * \retval C2_OK        the allocation was successful
+     * \retval C2_NO_MEMORY not enough memory to complete the allocation
+     * \retval C2_TIMED_OUT the allocation timed out
+     * \retval C2_REFUSED   no permission to complete the allocation
+     * \retval C2_BAD_VALUE width, height, format or usage are not supported (invalid) (caller error)
+     * \retval C2_OMITTED   this allocator does not support 2D allocations
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+     */
+    virtual c2_status_t newGraphicAllocation(
+            uint32_t width __unused, uint32_t height __unused, uint32_t format __unused,
+            C2MemoryUsage usage __unused,
+            std::shared_ptr<C2GraphicAllocation> *allocation /* nonnull */) {
+        *allocation = nullptr;
+        return C2_OMITTED;
+    }
+
+    /**
+     * (Re)creates a 2D allocation from a native handle.  If successful, the allocation is stored
+     * in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+     *
+     * \param handle      the handle for the existing allocation. On success, the allocation will
+     *                    take ownership of |handle|.
+     * \param allocation  pointer to where the allocation shall be stored on success. nullptr
+     *                    will be stored here on failure
+     *
+     * \retval C2_OK        the allocation was recreated successfully
+     * \retval C2_NO_MEMORY not enough memory to recreate the allocation
+     * \retval C2_TIMED_OUT the recreation timed out (unexpected)
+     * \retval C2_REFUSED   no permission to recreate the allocation
+     * \retval C2_BAD_VALUE invalid handle (caller error)
+     * \retval C2_OMITTED   this allocator does not support 2D allocations
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during recreation (unexpected)
+     */
+    virtual c2_status_t priorGraphicAllocation(
+            const C2Handle *handle __unused,
+            std::shared_ptr<C2GraphicAllocation> *allocation /* nonnull */) {
+        *allocation = nullptr;
+        return C2_OMITTED;
+    }
+
+    virtual ~C2Allocator() = default;
+protected:
+    C2Allocator() = default;
+};
+
+/**
+ * \ingroup linear allocator
+ * 1D allocation interface.
+ */
+class C2LinearAllocation : public _C2LinearCapacityAspect {
+public:
+    /**
+     * Maps a portion of an allocation starting from |offset| with |size| into local process memory.
+     * Stores the starting address into |addr|, or NULL if the operation was unsuccessful.
+     * |fence| will contain an acquire sync fence object. If it is already
+     * safe to access the buffer contents, then it will contain an empty (already fired) fence.
+     *
+     * \param offset        starting position of the portion to be mapped (this does not have to
+     *                      be page aligned)
+     * \param size          size of the portion to be mapped (this does not have to be page
+     *                      aligned)
+     * \param usage         the desired usage. \todo this must be kSoftwareRead and/or
+     *                      kSoftwareWrite.
+     * \param fence         a pointer to a fence object if an async mapping is requested. If
+     *                      not-null, and acquire fence will be stored here on success, or empty
+     *                      fence on failure. If null, the mapping will be synchronous.
+     * \param addr          a pointer to where the starting address of the mapped portion will be
+     *                      stored. On failure, nullptr will be stored here.
+     *
+     * \todo Only one portion can be mapped at the same time - this is true for gralloc, but there
+     *       is no need for this for 1D buffers.
+     * \todo Do we need to support sync operation as we could just wait for the fence?
+     *
+     * \retval C2_OK        the operation was successful
+     * \retval C2_REFUSED   no permission to map the portion
+     * \retval C2_TIMED_OUT the operation timed out
+     * \retval C2_DUPLICATE if the allocation is already mapped.
+     * \retval C2_NO_MEMORY not enough memory to complete the operation
+     * \retval C2_BAD_VALUE the parameters (offset/size) are invalid or outside the allocation, or
+     *                      the usage flags are invalid (caller error)
+     * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+     */
+    virtual c2_status_t map(
+            size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence /* nullable */,
+            void **addr /* nonnull */) = 0;
+
+    /**
+     * Unmaps a portion of an allocation at |addr| with |size|. These must be parameters previously
+     * passed to and returned by |map|; otherwise, this operation is a no-op.
+     *
+     * \param addr          starting address of the mapped region
+     * \param size          size of the mapped region
+     * \param fence         a pointer to a fence object if an async unmapping is requested. If
+     *                      not-null, a release fence will be stored here on success, or empty fence
+     *                      on failure. This fence signals when the original allocation contains
+     *                      all changes that happened to the mapped region. If null, the unmapping
+     *                      will be synchronous.
+     *
+     * \retval C2_OK        the operation was successful
+     * \retval C2_TIMED_OUT the operation timed out
+     * \retval C2_NOT_FOUND if the allocation was not mapped previously.
+     * \retval C2_BAD_VALUE the parameters (addr/size) do not correspond to previously mapped
+     *                      regions (caller error)
+     * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+     * \retval C2_REFUSED   no permission to unmap the portion (unexpected - system)
+     */
+    virtual c2_status_t unmap(void *addr, size_t size, C2Fence *fence /* nullable */) = 0;
+
+    /**
+     * Returns the allocator ID for this allocation. This is useful to put the handle into context.
+     */
+    virtual C2Allocator::id_t getAllocatorId() const = 0;
+
+    /**
+     * Returns a pointer to the allocation handle.
+     */
+    virtual const C2Handle *handle() const = 0;
+
+    /**
+     * Returns true if this is the same allocation as |other|.
+     */
+    virtual bool equals(const std::shared_ptr<C2LinearAllocation> &other) const = 0;
+
+protected:
+    // \todo should we limit allocation directly?
+    C2LinearAllocation(size_t capacity) : _C2LinearCapacityAspect(c2_min(capacity, UINT32_MAX)) {}
+    virtual ~C2LinearAllocation() = default;
+};
+
+class C2CircularBlock;
+class C2LinearBlock;
+class C2GraphicBlock;
+
+/**
+ *  Block pools are used by components to obtain output buffers in an efficient way. They can
+ *  support either linear (1D), circular (1D) or graphic (2D) blocks.
+ *
+ *  Block pools decouple the recycling of memory/allocations from the components. They are meant to
+ *  be an opaque service (there are no public APIs other than obtaining blocks) provided by the
+ *  platform. Block pools are also meant to decouple allocations from memory used by buffers. This
+ *  is accomplished by allowing pools to allot multiple memory 'blocks' on a single allocation. As
+ *  their name suggest, block pools maintain a pool of memory blocks. When a component asks for
+ *  a memory block, pools will try to return a free memory block already in the pool. If no such
+ *  block exists, they will allocate memory using the backing allocator and allot a block on that
+ *  allocation. When blocks are no longer used in the system, they are recycled back to the block
+ *  pool and are available as free blocks.
+ *
+ *  Never constructed on stack.
+ */
+class C2BlockPool {
+public:
+    /**
+     * Block pool ID type.
+     */
+    typedef uint64_t local_id_t;
+
+    enum : local_id_t {
+        BASIC_LINEAR = 0,  ///< ID of basic (unoptimized) block pool for fetching 1D blocks
+        BASIC_GRAPHIC = 1, ///< ID of basic (unoptimized) block pool for fetching 2D blocks
+        PLATFORM_START = 0x10,
+    };
+
+    /**
+     * Returns the ID for this block pool. This ID is used to get this block pool from the platform.
+     * It is only valid in the current process.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \return a local ID for this block pool.
+     */
+    virtual local_id_t getLocalId() const = 0;
+
+    /**
+     * Returns the ID of the backing allocator of this block pool.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \return the ID of the backing allocator of this block pool.
+     */
+    virtual C2Allocator::id_t getAllocatorId() const = 0;
+
+    /**
+     * Obtains a linear writeable block of given |capacity| and |usage|. If successful, the
+     * block is stored in |block|. Otherwise, |block| is set to 'nullptr'.
+     *
+     * \param capacity the size of requested block.
+     * \param usage    the memory usage info for the requested block. Returned blocks will be
+     *                 optimized for this usage, but may be used with any usage. One exception:
+     *                 protected blocks/buffers can only be used in a protected scenario.
+     * \param block    pointer to where the obtained block shall be stored on success. nullptr will
+     *                 be stored here on failure
+     *
+     * \retval C2_OK        the operation was successful
+     * \retval C2_NO_MEMORY not enough memory to complete any required allocation
+     * \retval C2_TIMED_OUT the operation timed out
+     * \retval C2_BLOCKING  the operation is blocked
+     * \retval C2_REFUSED   no permission to complete any required allocation
+     * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
+     * \retval C2_OMITTED   this pool does not support linear blocks
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during operation (unexpected)
+     */
+    virtual c2_status_t fetchLinearBlock(
+            uint32_t capacity __unused, C2MemoryUsage usage __unused,
+            std::shared_ptr<C2LinearBlock> *block /* nonnull */) {
+        *block = nullptr;
+        return C2_OMITTED;
+    }
+
+    /**
+     * Obtains a circular writeable block of given |capacity| and |usage|. If successful, the
+     * block is stored in |block|. Otherwise, |block| is set to 'nullptr'.
+     *
+     * \param capacity the size of requested circular block. (note: the size of the obtained
+     *                 block could be slightly larger, e.g. to accommodate any system-required
+     *                 alignment)
+     * \param usage    the memory usage info for the requested block. Returned blocks will be
+     *                 optimized for this usage, but may be used with any usage. One exception:
+     *                 protected blocks/buffers can only be used in a protected scenario.
+     * \param block    pointer to where the obtained block shall be stored on success. nullptr
+     *                 will be stored here on failure
+     *
+     * \retval C2_OK        the operation was successful
+     * \retval C2_NO_MEMORY not enough memory to complete any required allocation
+     * \retval C2_TIMED_OUT the operation timed out
+     * \retval C2_BLOCKING  the operation is blocked
+     * \retval C2_REFUSED   no permission to complete any required allocation
+     * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
+     * \retval C2_OMITTED   this pool does not support circular blocks
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during operation (unexpected)
+     */
+    virtual c2_status_t fetchCircularBlock(
+            uint32_t capacity __unused, C2MemoryUsage usage __unused,
+            std::shared_ptr<C2CircularBlock> *block /* nonnull */) {
+        *block = nullptr;
+        return C2_OMITTED;
+    }
+
+    /**
+     * Obtains a 2D graphic block of given |width|, |height|, |format| and |usage|. If successful,
+     * the block is stored in |block|. Otherwise, |block| is set to 'nullptr'.
+     *
+     * \param width  the width of requested block (the obtained block could be slightly larger, e.g.
+     *               to accommodate any system-required alignment)
+     * \param height the height of requested block (the obtained block could be slightly larger,
+     *               e.g. to accommodate any system-required alignment)
+     * \param format the pixel format of requested block. This could be a vendor specific format.
+     * \param usage  the memory usage info for the requested block. Returned blocks will be
+     *               optimized for this usage, but may be used with any usage. One exception:
+     *               protected blocks/buffers can only be used in a protected scenario.
+     * \param block  pointer to where the obtained block shall be stored on success. nullptr
+     *               will be stored here on failure
+     *
+     * \retval C2_OK        the operation was successful
+     * \retval C2_NO_MEMORY not enough memory to complete any required allocation
+     * \retval C2_TIMED_OUT the operation timed out
+     * \retval C2_BLOCKING  the operation is blocked
+     * \retval C2_REFUSED   no permission to complete any required allocation
+     * \retval C2_BAD_VALUE width, height, format or usage are not supported (invalid) (caller
+     *                      error)
+     * \retval C2_OMITTED   this pool does not support 2D blocks
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during operation (unexpected)
+     */
+    virtual c2_status_t fetchGraphicBlock(
+            uint32_t width __unused, uint32_t height __unused, uint32_t format __unused,
+            C2MemoryUsage usage __unused,
+            std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+        *block = nullptr;
+        return C2_OMITTED;
+    }
+
+    virtual ~C2BlockPool() = default;
+protected:
+    C2BlockPool() = default;
+};
+
+/// @}
+
+// ================================================================================================
+//  BLOCKS
+// ================================================================================================
+
+/**
+ * Blocks are sections of allocations. They can be either 1D or 2D.
+ */
+
+class C2LinearAllocation;
+
+/**
+ * A 1D block.
+ *
+ * \note capacity() is not meaningful for users of blocks; instead size() is the capacity of the
+ * usable portion. Use and offset() and size() if accessing the block directly through its handle
+ * to represent the allotted range of the underlying allocation to this block.
+ */
+class C2Block1D : public _C2LinearRangeAspect {
+public:
+    /**
+     * Returns the underlying handle for this allocation.
+     *
+     * \note that the block and its block pool has shared ownership of the handle
+     *       and if all references to the block are released, the underlying block
+     *       allocation may get reused even if a client keeps a clone of this handle.
+     */
+    const C2Handle *handle() const;
+
+    /**
+     * Returns the allocator's ID that created the underlying allocation for this block. This
+     * provides the context for understanding the handle.
+     */
+    C2Allocator::id_t getAllocatorId() const;
+
+protected:
+    class Impl;
+    /** construct a block. */
+    C2Block1D(std::shared_ptr<Impl> impl, const _C2LinearRangeAspect &range);
+
+    friend struct _C2BlockFactory;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * Read view provides read-only access for a linear memory segment.
+ *
+ * This class is copiable.
+ */
+class C2ReadView : public _C2LinearCapacityAspect {
+public:
+    /**
+     * \return pointer to the start of the block or nullptr on error.
+     *         This pointer is only valid during the lifetime of this view or until it is released.
+     */
+    const uint8_t *data() const;
+
+    /**
+     * Returns a portion of this view.
+     *
+     * \param offset  the start offset of the portion. \note This is clamped to the capacity of this
+     *              view.
+     * \param size    the size of the portion. \note This is clamped to the remaining data from offset.
+     *
+     * \return a read view containing a portion of this view
+     */
+    C2ReadView subView(size_t offset, size_t size) const;
+
+    /**
+     * \return error during the creation/mapping of this view.
+     */
+    c2_status_t error() const;
+
+    /**
+     * Releases this view. This sets error to C2_NO_INIT.
+     */
+    //void release();
+
+protected:
+    class Impl;
+    C2ReadView(std::shared_ptr<Impl> impl, uint32_t offset, uint32_t size);
+    explicit C2ReadView(c2_status_t error);
+
+private:
+    friend struct _C2BlockFactory;
+    std::shared_ptr<Impl> mImpl;
+    uint32_t mOffset; /**< offset into the linear block backing this read view */
+};
+
+/**
+ * Write view provides read/write access for a linear memory segment.
+ *
+ * This class is copiable. \todo movable only?
+ */
+class C2WriteView : public _C2EditableLinearRangeAspect {
+public:
+    /**
+     * Start of the block.
+     *
+     * \return pointer to the start of the block or nullptr on error.
+     *         This pointer is only valid during the lifetime of this view or until it is released.
+     */
+    uint8_t *base();
+
+    /**
+     * \return pointer to the block at the current offset or nullptr on error.
+     *         This pointer is only valid during the lifetime of this view or until it is released.
+     */
+    uint8_t *data();
+
+    /**
+     * \return error during the creation/mapping of this view.
+     */
+    c2_status_t error() const;
+
+    /**
+     * Releases this view. This sets error to C2_NO_INIT.
+     */
+    //void release();
+
+protected:
+    class Impl;
+    C2WriteView(std::shared_ptr<Impl> impl);
+    explicit C2WriteView(c2_status_t error);
+
+private:
+    friend struct _C2BlockFactory;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * A constant (read-only) linear block (portion of an allocation) with an acquire fence.
+ * Blocks are unmapped when created, and can be mapped into a read view on demand.
+ *
+ * This class is copiable and contains a reference to the allocation that it is based on.
+ */
+class C2ConstLinearBlock : public C2Block1D {
+public:
+    /**
+     * Maps this block into memory and returns a read view for it.
+     *
+     * \return a read view for this block.
+     */
+    C2Acquirable<C2ReadView> map() const;
+
+    /**
+     * Returns a portion of this block.
+     *
+     * \param offset  the start offset of the portion. \note This is clamped to the capacity of this
+     *              block.
+     * \param size    the size of the portion. \note This is clamped to the remaining data from offset.
+     *
+     * \return a constant linear block containing a portion of this block
+     */
+    C2ConstLinearBlock subBlock(size_t offset, size_t size) const;
+
+    /**
+     * Returns the acquire fence for this block.
+     *
+     * \return a fence that must be waited on before reading the block.
+     */
+    C2Fence fence() const { return mFence; }
+
+protected:
+    C2ConstLinearBlock(std::shared_ptr<Impl> impl, const _C2LinearRangeAspect &range, C2Fence mFence);
+
+private:
+    friend struct _C2BlockFactory;
+    C2Fence mFence;
+};
+
+/**
+ * Linear block is a writeable 1D block. Once written, it can be shared in whole or in parts with
+ * consumers/readers as read-only const linear block(s).
+ */
+class C2LinearBlock : public C2Block1D {
+public:
+    /**
+     * Maps this block into memory and returns a write view for it.
+     *
+     * \return a write view for this block.
+     */
+    C2Acquirable<C2WriteView> map();
+
+    /**
+     * Creates a read-only const linear block for a portion of this block; optionally protected
+     * by an acquire fence. There are two ways to use this:
+     *
+     * 1) share ready block after writing data into the block. In this case no fence shall be
+     *    supplied, and the block shall not be modified after calling this method.
+     * 2) share block metadata before actually (finishing) writing the data into the block. In
+     *    this case a fence must be supplied that will be triggered when the data is written.
+     *    The block shall be modified only until firing the event for the fence.
+     */
+    C2ConstLinearBlock share(size_t offset, size_t size, C2Fence fence);
+
+protected:
+    C2LinearBlock(std::shared_ptr<Impl> impl, const _C2LinearRangeAspect &range);
+
+    friend struct _C2BlockFactory;
+};
+
+/// @}
+
+/**************************************************************************************************
+  CIRCULAR BLOCKS AND VIEWS
+**************************************************************************************************/
+
+/// \defgroup circular Circular buffer support
+/// @{
+
+/**
+ * Circular blocks can be used to share data between a writer and a reader (and/or other consumers)-
+ * in a memory-efficient way by reusing a section of memory. Circular blocks are a bit more complex
+ * than single reader/single writer schemes to facilitate block-based consuming of data.
+ *
+ * They can operate in two modes:
+ *
+ * 1) one writer that creates blocks to be consumed (this model can be used by components)
+ *
+ * 2) one writer that writes continuously, and one reader that can creates blocks to be consumed
+ *    by further recipients (this model is used by the framework, and cannot be used by components.)
+ *
+ * Circular blocks have four segments with running pointers:
+ *  - reserved: data reserved and available for the writer
+ *  - committed: data committed by the writer and available to the reader (if present)
+ *  - used: data used by consumers (if present)
+ *  - available: unused data available to be reserved
+ */
+class C2CircularBlock : public C2Block1D {
+    // TODO: add methods
+
+private:
+    size_t mReserved __unused;   // end of reserved section
+    size_t mCommitted __unused;  // end of committed section
+    size_t mUsed __unused;       // end of used section
+    size_t mFree __unused;       // end of free section
+};
+
+class _C2CircularBlockSegment : public _C2LinearCapacityAspect {
+public:
+    /**
+     * Returns the available size for this segment.
+     *
+     * \return currently available size for this segment
+     */
+    size_t available() const;
+
+    /**
+     * Reserve some space for this segment from its current start.
+     *
+     * \param size    desired space in bytes
+     * \param fence   a pointer to an acquire fence. If non-null, the reservation is asynchronous and
+     *              a fence will be stored here that will be signaled when the reservation is
+     *              complete. If null, the reservation is synchronous.
+     *
+     * \retval C2_OK            the space was successfully reserved
+     * \retval C2_NO_MEMORY     the space requested cannot be reserved
+     * \retval C2_TIMED_OUT     the reservation timed out \todo when?
+     * \retval C2_CORRUPTED     some unknown error prevented reserving space. (unexpected)
+     */
+    c2_status_t reserve(size_t size, C2Fence *fence /* nullable */);
+
+    /**
+     * Abandons a portion of this segment. This will move to the beginning of this segment.
+     *
+     * \note This methods is only allowed if this segment is producing blocks.
+     *
+     * \param size    number of bytes to abandon
+     *
+     * \retval C2_OK            the data was successfully abandoned
+     * \retval C2_TIMED_OUT     the operation timed out (unexpected)
+     * \retval C2_CORRUPTED     some unknown error prevented abandoning the data (unexpected)
+     */
+    c2_status_t abandon(size_t size);
+
+    /**
+     * Share a portion as block(s) with consumers (these are moved to the used section).
+     *
+     * \note This methods is only allowed if this segment is producing blocks.
+     * \note Share does not move the beginning of the segment. (\todo add abandon/offset?)
+     *
+     * \param size    number of bytes to share
+     * \param fence   fence to be used for the section
+     * \param blocks  vector where the blocks of the section are appended to
+     *
+     * \retval C2_OK            the portion was successfully shared
+     * \retval C2_NO_MEMORY     not enough memory to share the portion
+     * \retval C2_TIMED_OUT     the operation timed out (unexpected)
+     * \retval C2_CORRUPTED     some unknown error prevented sharing the data (unexpected)
+     */
+    c2_status_t share(size_t size, C2Fence fence, std::vector<C2ConstLinearBlock> &blocks);
+
+    /**
+     * Returns the beginning offset of this segment from the start of this circular block.
+     *
+     * @return beginning offset
+     */
+    size_t begin();
+
+    /**
+     * Returns the end offset of this segment from the start of this circular block.
+     *
+     * @return end offset
+     */
+    size_t end();
+};
+
+/**
+ * A circular write-view is a dynamic mapped view for a segment of a circular block. Care must be
+ * taken when using this view so that only the section owned by the segment is modified.
+ */
+class C2CircularWriteView : public _C2LinearCapacityAspect {
+public:
+    /**
+     * Start of the circular block.
+     * \note the segment does not own this pointer.
+     *
+     * \return pointer to the start of the circular block or nullptr on error.
+     */
+    uint8_t *base();
+
+    /**
+     * \return error during the creation/mapping of this view.
+     */
+    c2_status_t error() const;
+};
+
+/**
+ * The writer of a circular buffer.
+ *
+ * Can commit data to a reader (not supported for components) OR share data blocks directly with a
+ * consumer.
+ *
+ * If a component supports outputting data into circular buffers, it must allocate a circular
+ * block and use a circular writer.
+ */
+class C2CircularWriter : public _C2CircularBlockSegment {
+public:
+    /**
+     * Commits a portion of this segment to the next segment. This moves the beginning of the
+     * segment.
+     *
+     * \param size    number of bytes to commit to the next segment
+     * \param fence   fence used for the commit (the fence must signal before the data is committed)
+     */
+    c2_status_t commit(size_t size, C2Fence fence);
+
+    /**
+     * Maps this block into memory and returns a write view for it.
+     *
+     * \return a write view for this block.
+     */
+    C2Acquirable<C2CircularWriteView> map();
+};
+
+/// @}
+
+/// \defgroup graphic Graphic Data Blocks
+/// @{
+
+/**
+ * C2Rect: rectangle type with non-negative coordinates.
+ *
+ * \note This struct has public fields without getters/setters. All methods are inline.
+ */
+struct C2Rect {
+// public:
+    uint32_t width;
+    uint32_t height;
+    uint32_t left;
+    uint32_t top;
+
+    constexpr inline C2Rect()
+        : C2Rect(0, 0, 0, 0) { }
+
+    constexpr inline C2Rect(uint32_t width_, uint32_t height_)
+        : C2Rect(width_, height_, 0, 0) { }
+
+    constexpr C2Rect inline at(uint32_t left_, uint32_t top_) const {
+        return C2Rect(width, height, left_, top_);
+    }
+
+    // utility methods
+
+    inline constexpr bool isEmpty() const {
+        return width == 0 || height == 0;
+    }
+
+    inline constexpr bool isValid() const {
+        return left <= ~width && top <= ~height;
+    }
+
+    inline constexpr operator bool() const {
+        return isValid() && !isEmpty();
+    }
+
+    inline constexpr bool operator!() const {
+        return !bool(*this);
+    }
+
+    C2_ALLOW_OVERFLOW
+    inline constexpr bool contains(const C2Rect &other) const {
+        if (!isValid() || !other.isValid()) {
+            return false;
+        } else {
+            return left <= other.left && top <= other.top
+                    && left + width >= other.left + other.width
+                    && top + height >= other.top + other.height;
+        }
+    }
+
+    inline constexpr bool operator==(const C2Rect &other) const {
+        if (!isValid()) {
+            return !other.isValid();
+        } else {
+            return left == other.left && top == other.top
+                    && width == other.width && height == other.height;
+        }
+    }
+
+    inline constexpr bool operator!=(const C2Rect &other) const {
+        return !operator==(other);
+    }
+
+    inline constexpr bool operator>=(const C2Rect &other) const {
+        return contains(other);
+    }
+
+    inline constexpr bool operator>(const C2Rect &other) const {
+        return contains(other) && !operator==(other);
+    }
+
+    inline constexpr bool operator<=(const C2Rect &other) const {
+        return other.contains(*this);
+    }
+
+    inline constexpr bool operator<(const C2Rect &other) const {
+        return other.contains(*this) && !operator==(other);
+    }
+
+    C2_ALLOW_OVERFLOW
+    inline constexpr uint32_t right() const {
+        return left + width;
+    }
+
+    C2_ALLOW_OVERFLOW
+    inline constexpr uint32_t bottom() const {
+        return top + height;
+    }
+
+    C2_ALLOW_OVERFLOW
+    inline constexpr C2Rect intersect(const C2Rect &other) const {
+        return C2Rect(c2_min(right(), other.right()) - c2_max(left, other.left),
+                      c2_min(bottom(), other.bottom()) - c2_max(top, other.top),
+                      c2_max(left, other.left),
+                      c2_max(top, other.top));
+    }
+
+    /** clamps right and bottom to top, left if they overflow */
+    inline constexpr C2Rect normalize() const {
+        return C2Rect(c2_max(left, right()) - left, c2_max(top, bottom()) - top, left, top);
+    }
+
+private:
+    /// note: potentially unusual argument order
+    constexpr inline C2Rect(uint32_t width_, uint32_t height_, uint32_t left_, uint32_t top_)
+        : width(width_),
+          height(height_),
+          left(left_),
+          top(top_) { }
+};
+
+/**
+ * Interface for objects that have a width and height (planar capacity).
+ */
+class _C2PlanarCapacityAspect {
+/// \name Planar capacity interface
+/// @{
+public:
+    inline constexpr uint32_t width() const { return _mWidth; }
+    inline constexpr uint32_t height() const { return _mHeight; }
+
+    inline constexpr operator C2Rect() const {
+        return C2Rect(_mWidth, _mHeight);
+    }
+
+protected:
+    inline constexpr _C2PlanarCapacityAspect(uint32_t width, uint32_t height)
+      : _mWidth(width), _mHeight(height) { }
+
+    inline explicit constexpr _C2PlanarCapacityAspect(const _C2PlanarCapacityAspect *parent)
+        : _mWidth(parent == nullptr ? 0 : parent->width()),
+          _mHeight(parent == nullptr ? 0 : parent->height()) { }
+
+private:
+    uint32_t _mWidth;
+    uint32_t _mHeight;
+/// @}
+};
+
+/**
+ * C2PlaneInfo: information on the layout of a singe flexible plane.
+ *
+ * Public fields without getters/setters.
+ */
+struct C2PlaneInfo {
+//public:
+    enum channel_t : uint32_t {
+        CHANNEL_Y,  ///< luma
+        CHANNEL_R,  ///< red
+        CHANNEL_G,  ///< green
+        CHANNEL_B,  ///< blue
+        CHANNEL_A,  ///< alpha
+        CHANNEL_CR, ///< Cr
+        CHANNEL_CB, ///< Cb
+    } channel;
+
+    int32_t colInc;       ///< column increment in bytes. may be negative
+    int32_t rowInc;       ///< row increment in bytes. may be negative
+
+    uint32_t colSampling; ///< subsampling compared to width (must be a power of 2)
+    uint32_t rowSampling; ///< subsampling compared to height (must be a power of 2)
+
+    uint32_t allocatedDepth; ///< size of each sample (must be a multiple of 8)
+    uint32_t bitDepth;       ///< significant bits per sample
+    /**
+     * the right shift of the significant bits in the sample. E.g. if a 10-bit significant
+     * value is laid out in a 16-bit allocation aligned to LSB (values 0-1023), rightShift
+     * would be 0 as the 16-bit value read from the sample does not need to be right shifted
+     * and can be used as is (after applying a 10-bit mask of 0x3FF).
+     *
+     * +--------+--------+
+     * |      VV|VVVVVVVV|
+     * +--------+--------+
+     *  15     8 7      0
+     *
+     * If the value is laid out aligned to MSB, rightShift would be 6, as the value read
+     * from the allocated sample must be right-shifted by 6 to get the actual sample value.
+     *
+     * +--------+--------+
+     * |VVVVVVVV|VV      |
+     * +--------+--------+
+     *  15     8 7      0
+     */
+    uint32_t rightShift;
+
+    enum endianness_t : uint32_t {
+        NATIVE,
+        LITTLE_END, // LITTLE_ENDIAN is reserved macro
+        BIG_END,    // BIG_ENDIAN is a reserved macro
+    } endianness; ///< endianness of the samples
+
+    /**
+     * The following two fields define the relation between multiple planes. If multiple planes are
+     * interleaved, they share a root plane (whichever plane's start address is the lowest), and
+     * |offset| is the offset of this plane inside the root plane (in bytes). |rootIx| is the index
+     * of the root plane. If a plane is independent, rootIx is its index and offset is 0.
+     */
+    uint32_t rootIx; ///< index of the root plane
+    uint32_t offset; ///< offset of this plane inside of the root plane
+
+    inline constexpr ssize_t minOffset(uint32_t width, uint32_t height) const {
+        ssize_t offs = 0;
+        if (width > 0 && colInc < 0) {
+            offs += colInc * (ssize_t)(width - 1);
+        }
+        if (height > 0 && rowInc < 0) {
+            offs += rowInc * (ssize_t)(height - 1);
+        }
+        return offs;
+    }
+
+    inline constexpr ssize_t maxOffset(uint32_t width, uint32_t height) const {
+        ssize_t offs = (allocatedDepth + 7) >> 3;
+        if (width > 0 && colInc > 0) {
+            offs += colInc * (ssize_t)(width - 1);
+        }
+        if (height > 0 && rowInc > 0) {
+            offs += rowInc * (ssize_t)(height - 1);
+        }
+        return offs;
+    }
+} C2_PACK;
+
+struct C2PlanarLayout {
+//public:
+    enum type_t : uint32_t {
+        TYPE_UNKNOWN = 0,
+        TYPE_YUV = 0x100,   ///< YUV image with 3 planes
+        TYPE_YUVA,          ///< YUVA image with 4 planes
+        TYPE_RGB,           ///< RGB image with 3 planes
+        TYPE_RGBA,          ///< RBGA image with 4 planes
+    };
+
+    type_t type;                    // image type
+    uint32_t numPlanes;             // number of component planes
+    uint32_t rootPlanes;            // number of layout planes (root planes)
+
+    enum plane_index_t : uint32_t {
+        PLANE_Y = 0,
+        PLANE_U = 1,
+        PLANE_V = 2,
+        PLANE_R = 0,
+        PLANE_G = 1,
+        PLANE_B = 2,
+        PLANE_A = 3,
+        MAX_NUM_PLANES = 4,
+    };
+
+    C2PlaneInfo planes[MAX_NUM_PLANES];
+};
+
+/**
+ * Aspect for objects that have a planar section (crop rectangle).
+ *
+ * This class is copiable.
+ */
+class _C2PlanarSectionAspect : public _C2PlanarCapacityAspect {
+/// \name Planar section interface
+/// @{
+private:
+    inline constexpr _C2PlanarSectionAspect(uint32_t width, uint32_t height, const C2Rect &crop)
+        : _C2PlanarCapacityAspect(width, height),
+          mCrop(C2Rect(std::min(width - std::min(crop.left, width), crop.width),
+                       std::min(height - std::min(crop.top, height), crop.height)).at(
+                               std::min(crop.left, width),
+                               std::min(crop.height, height))) {
+    }
+
+public:
+    // crop can be an empty rect, does not have to line up with subsampling
+    // NOTE: we do not support floating-point crop
+    inline constexpr C2Rect crop() const { return mCrop; }
+
+    /**
+     * Returns a child planar section for |crop|, where the capacity represents this section.
+     */
+    inline constexpr _C2PlanarSectionAspect childSection(const C2Rect &crop) const {
+        return _C2PlanarSectionAspect(
+                mCrop.width, mCrop.height,
+                // crop and translate |crop| rect
+                C2Rect(c2_min(mCrop.right() - c2_clamp(mCrop.left, crop.left, mCrop.right()),
+                              crop.width),
+                       c2_min(mCrop.bottom() - c2_clamp(mCrop.top, crop.top, mCrop.bottom()),
+                              crop.height))
+                .at(c2_clamp(mCrop.left, crop.left, mCrop.right()) - mCrop.left,
+                    c2_clamp(mCrop.top, crop.top, mCrop.bottom()) - mCrop.top));
+    }
+
+protected:
+    inline constexpr _C2PlanarSectionAspect(const _C2PlanarCapacityAspect *parent)
+        : _C2PlanarCapacityAspect(parent), mCrop(width(), height()) {}
+
+    inline constexpr _C2PlanarSectionAspect(const _C2PlanarCapacityAspect *parent, const C2Rect &crop)
+        : _C2PlanarCapacityAspect(parent),
+          mCrop(parent == nullptr ? C2Rect() : ((C2Rect)*parent).intersect(crop).normalize()) { }
+
+    inline constexpr _C2PlanarSectionAspect(const _C2PlanarSectionAspect *parent, const C2Rect &crop)
+        : _C2PlanarCapacityAspect(parent),
+          mCrop(parent == nullptr ? C2Rect() : parent->crop().intersect(crop).normalize()) { }
+
+private:
+    friend class _C2EditablePlanarSectionAspect;
+    C2Rect mCrop;
+/// @}
+};
+
+/**
+ * Aspect for objects that have an editable planar section (crop rectangle).
+ *
+ * This class is copiable.
+ */
+class _C2EditablePlanarSectionAspect : public _C2PlanarSectionAspect {
+/// \name Planar section interface
+/// @{
+    using _C2PlanarSectionAspect::_C2PlanarSectionAspect;
+
+public:
+    // crop can be an empty rect, does not have to line up with subsampling
+    // NOTE: we do not support floating-point crop
+    inline constexpr C2Rect crop() const { return mCrop; }
+
+    /**
+     *  Sets crop to crop intersected with [(0,0) .. (width, height)]
+     */
+    inline void setCrop_be(const C2Rect &crop) {
+        mCrop.left = std::min(width(), crop.left);
+        mCrop.top = std::min(height(), crop.top);
+        // It's guaranteed that mCrop.left <= width() && mCrop.top <= height()
+        mCrop.width = std::min(width() - mCrop.left, crop.width);
+        mCrop.height = std::min(height() - mCrop.top, crop.height);
+    }
+
+    /**
+     * If crop is within the dimensions of this object, it sets crop to it.
+     *
+     * \return true iff crop is within the dimensions of this object
+     */
+    inline bool setCrop(const C2Rect &crop) {
+        if (width() < crop.width || height() < crop.height
+                || width() - crop.width < crop.left || height() - crop.height < crop.top) {
+            return false;
+        }
+        mCrop = crop;
+        return true;
+    }
+/// @}
+};
+
+/**
+ * Utility class for safe range calculations using size_t-s.
+ */
+class C2PlanarSection : public _C2PlanarSectionAspect {
+public:
+    inline constexpr C2PlanarSection(const _C2PlanarCapacityAspect &parent, const C2Rect &crop)
+        : _C2PlanarSectionAspect(&parent, crop) { }
+
+    inline constexpr C2PlanarSection(const _C2PlanarSectionAspect &parent, const C2Rect &crop)
+        : _C2PlanarSectionAspect(&parent, crop) { }
+
+    inline constexpr C2PlanarSection intersect(const C2Rect &crop) const {
+        return C2PlanarSection(*this, crop);
+    }
+};
+
+/**
+ * Utility class for simple and safe planar capacity and section construction.
+ */
+class C2PlanarCapacity : public _C2PlanarCapacityAspect {
+public:
+    inline constexpr explicit C2PlanarCapacity(size_t width, size_t height)
+        : _C2PlanarCapacityAspect(c2_min(width, std::numeric_limits<uint32_t>::max()),
+                                  c2_min(height, std::numeric_limits<uint32_t>::max())) { }
+
+    inline constexpr C2PlanarSection section(const C2Rect &crop) const {
+        return C2PlanarSection(*this, crop);
+    }
+};
+
+
+/**
+ * \ingroup graphic allocator
+ * 2D allocation interface.
+ */
+class C2GraphicAllocation : public _C2PlanarCapacityAspect {
+public:
+    /**
+     * Maps a rectangular section (as defined by |rect|) of a 2D allocation into local process
+     * memory for flexible access. On success, it fills out |layout| with the plane specifications
+     * and fills the |addr| array with pointers to the first byte of the top-left pixel of each
+     * plane used. Otherwise, it leaves |layout| and |addr| untouched. |fence| will contain
+     * an acquire sync fence object. If it is already safe to access the
+     * buffer contents, then it will be an empty (already fired) fence.
+     *
+     * Safe regions for the pointer addresses returned can be gotten via C2LayoutInfo.minOffset()/
+     * maxOffset().
+     *
+     * \param rect          section to be mapped (this does not have to be aligned)
+     * \param usage         the desired usage. \todo this must be kSoftwareRead and/or
+     *                      kSoftwareWrite.
+     * \param fence         a pointer to a fence object if an async mapping is requested. If
+     *                      not-null, and acquire fence will be stored here on success, or empty
+     *                      fence on failure. If null, the mapping will be synchronous.
+     * \param layout        a pointer to where the mapped planes' descriptors will be
+     *                      stored. On failure, nullptr will be stored here.
+     * \param addr          pointer to an array with at least C2PlanarLayout::MAX_NUM_PLANES
+     *                      elements. Only layout.numPlanes elements will be modified on success.
+     *
+     * \retval C2_OK        the operation was successful
+     * \retval C2_REFUSED   no permission to map the section
+     * \retval C2_DUPLICATE there is already a mapped region and this allocation cannot support
+     *                      multi-mapping (caller error)
+     * \retval C2_TIMED_OUT the operation timed out
+     * \retval C2_NO_MEMORY not enough memory to complete the operation
+     * \retval C2_BAD_VALUE the parameters (rect) are invalid or outside the allocation, or the
+     *                      usage flags are invalid (caller error)
+     * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+
+     */
+    virtual c2_status_t map(
+            C2Rect rect, C2MemoryUsage usage, C2Fence *fence,
+            C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) = 0;
+
+    /**
+     * Unmaps a section of an allocation at |addr| with |rect|. These must be parameters previously
+     * passed to and returned by |map|; otherwise, this operation is a no-op.
+     *
+     * \param addr          pointer to an array with at least C2PlanarLayout::MAX_NUM_PLANES
+     *                      elements containing the starting addresses of the mapped layers
+     * \param rect          boundaries of the mapped section
+     * \param fence         a pointer to a fence object if an async unmapping is requested. If
+     *                      not-null, a release fence will be stored here on success, or empty fence
+     *                      on failure. This fence signals when the original allocation contains
+     *                      all changes that happened to the mapped section. If null, the unmapping
+     *                      will be synchronous.
+     *
+     * \retval C2_OK        the operation was successful
+     * \retval C2_TIMED_OUT the operation timed out
+     * \retval C2_NOT_FOUND there is no such mapped region (caller error)
+     * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+     * \retval C2_REFUSED   no permission to unmap the section (unexpected - system)
+     */
+    virtual c2_status_t unmap(
+            uint8_t **addr /* nonnull */, C2Rect rect, C2Fence *fence /* nullable */) = 0;
+
+    /**
+     * Returns the allocator ID for this allocation. This is useful to put the handle into context.
+     */
+    virtual C2Allocator::id_t getAllocatorId() const = 0;
+
+    /**
+     * Returns a pointer to the allocation handle.
+     */
+    virtual const C2Handle *handle() const = 0;
+
+    /**
+     * Returns true if this is the same allocation as |other|.
+     */
+    virtual bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) const = 0;
+
+protected:
+    using _C2PlanarCapacityAspect::_C2PlanarCapacityAspect;
+    virtual ~C2GraphicAllocation() = default;
+};
+
+class C2GraphicAllocation;
+
+/**
+ * A 2D block.
+ *
+ * \note width()/height() is not meaningful for users of blocks; instead, crop().width() and
+ * crop().height() is the capacity of the usable portion. Use and crop() if accessing the block
+ * directly through its handle to represent the allotted region of the underlying allocation to this
+ * block.
+ */
+class C2Block2D : public _C2PlanarSectionAspect {
+public:
+    /**
+     * Returns the underlying handle for this allocation.
+     *
+     * \note that the block and its block pool has shared ownership of the handle
+     *       and if all references to the block are released, the underlying block
+     *       allocation may get reused even if a client keeps a clone of this handle.
+     */
+    const C2Handle *handle() const;
+
+    /**
+     * Returns the allocator's ID that created the underlying allocation for this block. This
+     * provides the context for understanding the handle.
+     */
+    C2Allocator::id_t getAllocatorId() const;
+
+protected:
+    class Impl;
+    C2Block2D(std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section);
+
+    friend struct _C2BlockFactory;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * Graphic view provides read or read-write access for a graphic block.
+ *
+ * This class is copiable.
+ *
+ * \note Due to the subsampling of graphic buffers, a read view must still contain a crop rectangle
+ * to ensure subsampling is followed. This results in nearly identical interface between read and
+ * write views, so C2GraphicView can encompass both of them.
+ */
+class C2GraphicView : public _C2EditablePlanarSectionAspect {
+public:
+    /**
+     * \return array of pointers (of layout().numPlanes elements) to the start of the planes or
+     * nullptr on error. Regardless of crop rect, they always point to the top-left corner of each
+     * plane. Access outside of the crop rect results in an undefined behavior.
+     */
+    const uint8_t *const *data() const;
+
+    /**
+     * \return array of pointers (of layout().numPlanes elements) to the start of the planes or
+     * nullptr on error. Regardless of crop rect, they always point to the top-left corner of each
+     * plane. Access outside of the crop rect results in an undefined behavior.
+     */
+    uint8_t *const *data();
+
+    /**
+     * \return layout of the graphic block to interpret the returned data.
+     */
+    const C2PlanarLayout layout() const;
+
+    /**
+     * Returns a section of this view.
+     *
+     * \param rect    the dimension of the section. \note This is clamped to the crop of this view.
+     *
+     * \return a read view containing the requested section of this view
+     */
+    const C2GraphicView subView(const C2Rect &rect) const;
+    C2GraphicView subView(const C2Rect &rect);
+
+    /**
+     * \return error during the creation/mapping of this view.
+     */
+    c2_status_t error() const;
+
+protected:
+    class Impl;
+    C2GraphicView(std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section);
+    explicit C2GraphicView(c2_status_t error);
+
+private:
+    friend struct _C2BlockFactory;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * A constant (read-only) graphic block (portion of an allocation) with an acquire fence.
+ * Blocks are unmapped when created, and can be mapped into a read view on demand.
+ *
+ * This class is copiable and contains a reference to the allocation that it is based on.
+ */
+class C2ConstGraphicBlock : public C2Block2D {
+public:
+    /**
+     * Maps this block into memory and returns a read view for it.
+     *
+     * \return a read view for this block.
+     */
+    C2Acquirable<const C2GraphicView> map() const;
+
+    /**
+     * Returns a section of this block.
+     *
+     * \param rect    the coordinates of the section. \note This is clamped to the crop rectangle of
+     *              this block.
+     *
+     * \return a constant graphic block containing a portion of this block
+     */
+    C2ConstGraphicBlock subBlock(const C2Rect &rect) const;
+
+    /**
+     * Returns the acquire fence for this block.
+     *
+     * \return a fence that must be waited on before reading the block.
+     */
+    C2Fence fence() const { return mFence; }
+
+protected:
+    C2ConstGraphicBlock(
+            std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section, C2Fence fence);
+
+private:
+    friend struct _C2BlockFactory;
+    C2Fence mFence;
+};
+
+/**
+ * Graphic block is a writeable 2D block. Once written, it can be shared in whole or in part with
+ * consumers/readers as read-only const graphic block.
+ */
+class C2GraphicBlock : public C2Block2D {
+public:
+    /**
+     * Maps this block into memory and returns a write view for it.
+     *
+     * \return a write view for this block.
+     */
+    C2Acquirable<C2GraphicView> map();
+
+    /**
+     * Creates a read-only const linear block for a portion of this block; optionally protected
+     * by an acquire fence. There are two ways to use this:
+     *
+     * 1) share ready block after writing data into the block. In this case no fence shall be
+     *    supplied, and the block shall not be modified after calling this method.
+     * 2) share block metadata before actually (finishing) writing the data into the block. In
+     *    this case a fence must be supplied that will be triggered when the data is written.
+     *    The block shall be modified only until firing the event for the fence.
+     */
+    C2ConstGraphicBlock share(const C2Rect &crop, C2Fence fence);
+
+protected:
+    C2GraphicBlock(std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section);
+
+    friend struct _C2BlockFactory;
+};
+
+/// @}
+
+/// \defgroup buffer_onj Buffer objects
+/// @{
+
+// ================================================================================================
+//  BUFFERS
+// ================================================================================================
+
+/// \todo: Do we still need this?
+///
+// There are 2 kinds of buffers: linear or graphic. Linear buffers can contain a single block, or
+// a list of blocks (LINEAR_CHUNKS). Support for list of blocks is optional, and can allow consuming
+// data from circular buffers or scattered data sources without extra memcpy. Currently, list of
+// graphic blocks is not supported.
+
+class C2LinearBuffer;   // read-write buffer
+class C2GraphicBuffer;  // read-write buffer
+class C2LinearChunksBuffer;
+
+/**
+ * C2BufferData: the main, non-meta data of a buffer. A buffer can contain either linear blocks
+ * or graphic blocks, and can contain either a single block or multiple blocks. This is determined
+ * by its type.
+ */
+class C2BufferData {
+public:
+    /**
+     *  The type of buffer data.
+     */
+    enum type_t : uint32_t {
+        INVALID,            ///< invalid buffer type. Do not use.
+        LINEAR,             ///< the buffer contains a single linear block
+        LINEAR_CHUNKS,      ///< the buffer contains one or more linear blocks
+        GRAPHIC,            ///< the buffer contains a single graphic block
+        GRAPHIC_CHUNKS,     ///< the buffer contains one of more graphic blocks
+    };
+
+    /**
+     * Gets the type of this buffer (data).
+     * \return the type of this buffer data.
+     */
+    type_t type() const;
+
+    /**
+     * Gets the linear blocks of this buffer.
+     * \return a constant list of const linear blocks of this buffer.
+     * \retval empty list if this buffer does not contain linear block(s).
+     */
+    const std::vector<C2ConstLinearBlock> linearBlocks() const;
+
+    /**
+     * Gets the graphic blocks of this buffer.
+     * \return a constant list of const graphic blocks of this buffer.
+     * \retval empty list if this buffer does not contain graphic block(s).
+     */
+    const std::vector<C2ConstGraphicBlock> graphicBlocks() const;
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+
+protected:
+    // no public constructor
+    explicit C2BufferData(const std::vector<C2ConstLinearBlock> &blocks);
+    explicit C2BufferData(const std::vector<C2ConstGraphicBlock> &blocks);
+};
+
+/**
+ * C2Buffer: buffer base class. These are always used as shared_ptrs. Though the underlying buffer
+ * objects (native buffers, ion buffers, or dmabufs) are reference-counted by the system,
+ * C2Buffers hold only a single reference.
+ *
+ * These objects cannot be used on the stack.
+ */
+class C2Buffer {
+public:
+    /**
+     * Gets the buffer's data.
+     *
+     * \return the buffer's data.
+     */
+    const C2BufferData data() const;
+
+    ///@name Pre-destroy notification handling
+    ///@{
+
+    /**
+     * Register for notification just prior to the destruction of this object.
+     */
+    typedef void (*OnDestroyNotify) (const C2Buffer *buf, void *arg);
+
+    /**
+     * Registers for a pre-destroy notification. This is called just prior to the destruction of
+     * this buffer (when this buffer is no longer valid.)
+     *
+     * \param onDestroyNotify   the notification callback
+     * \param arg               an arbitrary parameter passed to the callback
+     *
+     * \retval C2_OK        the registration was successful.
+     * \retval C2_DUPLICATE a notification was already registered for this callback and argument
+     * \retval C2_NO_MEMORY not enough memory to register for this callback
+     * \retval C2_CORRUPTED an unknown error prevented the registration (unexpected)
+     */
+    c2_status_t registerOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg = nullptr);
+
+    /**
+     * Unregisters a previously registered pre-destroy notification.
+     *
+     * \param onDestroyNotify   the notification callback
+     * \param arg               an arbitrary parameter passed to the callback
+     *
+     * \retval C2_OK        the unregistration was successful.
+     * \retval C2_NOT_FOUND the notification was not found
+     * \retval C2_CORRUPTED an unknown error prevented the registration (unexpected)
+     */
+    c2_status_t unregisterOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg = nullptr);
+
+    ///@}
+
+    virtual ~C2Buffer() = default;
+
+    ///@name Buffer-specific arbitrary metadata handling
+    ///@{
+
+    /**
+     * Gets the list of metadata associated with this buffer.
+     *
+     * \return a constant list of info objects associated with this buffer.
+     */
+    const std::vector<std::shared_ptr<const C2Info>> info() const;
+
+    /**
+     * Attaches (or updates) an (existing) metadata for this buffer.
+     * If the metadata is stream specific, the stream information will be reset.
+     *
+     * \param info Metadata to update
+     *
+     * \retval C2_OK        the metadata was successfully attached/updated.
+     * \retval C2_NO_MEMORY not enough memory to attach the metadata (this return value is not
+     *                      used if the same kind of metadata is already attached to the buffer).
+     */
+    c2_status_t setInfo(const std::shared_ptr<C2Info> &info);
+
+    /**
+     * Checks if there is a certain type of metadata attached to this buffer.
+     *
+     * \param index the parameter type of the metadata
+     *
+     * \return true iff there is a metadata with the parameter type attached to this buffer.
+     */
+    bool hasInfo(C2Param::Type index) const;
+
+    /**
+     * Checks if there is a certain type of metadata attached to this buffer, and returns a
+     * shared pointer to it if there is. Returns an empty shared pointer object (nullptr) if there
+     * is not.
+     *
+     * \param index the parameter type of the metadata
+     *
+     * \return shared pointer to the metadata.
+     */
+    std::shared_ptr<const C2Info> getInfo(C2Param::Type index) const;
+
+    /**
+     * Removes a metadata from the buffer.
+     */
+    std::shared_ptr<C2Info> removeInfo(C2Param::Type index);
+    ///@}
+
+    /**
+     * Creates a buffer containing a single linear block.
+     *
+     * \param block the content of the buffer.
+     *
+     * \return shared pointer to the created buffer.
+     */
+    static std::shared_ptr<C2Buffer> CreateLinearBuffer(const C2ConstLinearBlock &block);
+
+    /**
+     * Creates a buffer containing a single graphic block.
+     *
+     * \param block the content of the buffer.
+     *
+     * \return shared pointer to the created buffer.
+     */
+    static std::shared_ptr<C2Buffer> CreateGraphicBuffer(const C2ConstGraphicBlock &block);
+
+protected:
+    // no public constructor
+    explicit C2Buffer(const std::vector<C2ConstLinearBlock> &blocks);
+    explicit C2Buffer(const std::vector<C2ConstGraphicBlock> &blocks);
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * An extension of C2Info objects that can contain arbitrary buffer data.
+ *
+ * \note This object is not describable and contains opaque data.
+ */
+class C2InfoBuffer {
+public:
+    /**
+     * Gets the index of this info object.
+     *
+     * \return the parameter index.
+     */
+    const C2Param::Index index() const;
+
+    /**
+     * Gets the buffer's data.
+     *
+     * \return the buffer's data.
+     */
+    const C2BufferData data() const;
+};
+
+/// @}
+
+/// @}
+
+#endif  // C2BUFFER_H_
diff --git a/media/codec2/core/include/C2BufferBase.h b/media/codec2/core/include/C2BufferBase.h
new file mode 100644
index 0000000..2bd48f0
--- /dev/null
+++ b/media/codec2/core/include/C2BufferBase.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#ifndef C2BUFFER_BASE_H_
+#define C2BUFFER_BASE_H_
+
+/// \defgroup allocator Allocation and memory placement
+/// @{
+
+/**
+ * Buffer/memory usage bits. These shall be used by the allocators to select optimal memory type/
+ * pool and buffer layout. Usage bits are conceptually separated into read and write usage, while
+ * the buffer use life-cycle is separated into producers (writers) and consumers (readers).
+ * These two concepts are related but not equivalent: consumers may only read buffers and only
+ * producers may write to buffers; note, however, that buffer producers may also want or need to
+ * read the buffers.
+ *
+ * Read and write buffer usage bits shall be or-ed to arrive at the full buffer usage. Admittedly,
+ * this does not account for the amount of reading and writing (e.g. a buffer may have one or more
+ * readers); however, the proper information necessary to properly weigh the various usages would be
+ * the amount of data read/written for each usage type. This would result in an integer array of
+ * size 64 (or the number of distinct usages) for memory usage, and likely such detailed information
+ * would not always be available.
+ *
+ * That platform-agnostic Codec 2.0 API only defines the bare minimum usages. Platforms shall define
+ * usage bits that are appropriate for the platform.
+ */
+struct C2MemoryUsage {
+// public:
+    /**
+     * Buffer read usage.
+     */
+    enum read_t : uint64_t {
+        /** Buffer is read by the CPU. */
+        CPU_READ        = 1 << 0,
+        /**
+         * Buffer shall only be read by trusted hardware. The definition of trusted hardware is
+         * platform specific, but this flag is reserved to prevent mapping this block into CPU
+         * readable memory resulting in bus fault. This flag can be used when buffer access must be
+         * protected.
+         */
+        READ_PROTECTED  = 1 << 1,
+    };
+
+    /**
+     * Buffer write usage.
+     */
+    enum write_t : uint64_t {
+        /** Buffer is writted to by the CPU. */
+        CPU_WRITE        = 1 << 2,
+        /**
+         * Buffer shall only be written to by trusted hardware. The definition of trusted hardware
+         * is platform specific, but this flag is reserved to prevent mapping this block into CPU
+         * writable memory resulting in bus fault. This flag can be used when buffer integrity must
+         * be protected.
+         */
+        WRITE_PROTECTED  = 1 << 3,
+    };
+
+    enum : uint64_t {
+        /**
+         * Buffer usage bits reserved for the platform. We don't separately reserve read and
+         * write usages as platforms may have asymmetric distribution between them.
+         */
+        PLATFORM_MASK     = ~(CPU_READ | CPU_WRITE | READ_PROTECTED | WRITE_PROTECTED),
+    };
+
+    /** Create a usage from separate consumer and producer usage mask. \deprecated */
+    inline C2MemoryUsage(uint64_t consumer, uint64_t producer)
+        : expected(consumer | producer) { }
+
+    inline explicit C2MemoryUsage(uint64_t expected_)
+        : expected(expected_) { }
+
+    uint64_t expected; // expected buffer usage
+};
+
+/// @}
+
+#endif  // C2BUFFER_BASE_H_
+
diff --git a/media/codec2/core/include/C2Component.h b/media/codec2/core/include/C2Component.h
new file mode 100644
index 0000000..ecf8d2e
--- /dev/null
+++ b/media/codec2/core/include/C2Component.h
@@ -0,0 +1,976 @@
+/*
+ * 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.
+ */
+
+#ifndef C2COMPONENT_H_
+
+#define C2COMPONENT_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <list>
+#include <memory>
+#include <vector>
+#include <functional>
+
+#include <C2Enum.h>
+#include <C2Param.h>
+#include <C2Work.h>
+
+/// \defgroup components Components
+/// @{
+
+struct C2FieldSupportedValuesQuery {
+    enum type_t : uint32_t {
+        POSSIBLE, ///< query all possible values regardless of other settings
+        CURRENT,  ///< query currently possible values given dependent settings
+    };
+
+private:
+    C2ParamField _mField;
+    type_t _mType;
+public:
+    c2_status_t status;
+    C2FieldSupportedValues values;
+
+    C2FieldSupportedValuesQuery(const C2ParamField &field_, type_t type_)
+        : _mField(field_), _mType(type_), status(C2_NO_INIT) { }
+
+    static C2FieldSupportedValuesQuery
+    Current(const C2ParamField &field_) {
+        return C2FieldSupportedValuesQuery(field_, CURRENT);
+    }
+
+    static C2FieldSupportedValuesQuery
+    Possible(const C2ParamField &field_) {
+        return C2FieldSupportedValuesQuery(field_, POSSIBLE);
+    }
+
+    inline C2ParamField field() const { return _mField; };
+
+    inline type_t type() const { return _mType; }
+};
+
+/**
+ * Component interface object. This object contains all of the configuration of a potential or
+ * actual component. It can be created and used independently of an actual C2Component instance to
+ * query support and parameters for various component settings and configurations for a potential
+ * component. Actual components also expose this interface.
+ */
+
+class C2ComponentInterface {
+public:
+    // ALWAYS AVAILABLE METHODS
+    // =============================================================================================
+
+    /**
+     * Returns the name of this component or component interface object.
+     * This is a unique name for this component or component interface 'class'; however, multiple
+     * instances of this component SHALL have the same name.
+     *
+     * When attached to a component, this method MUST be supported in any component state.
+     * This call does not change the state nor the internal configuration of the component.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \return the name of this component or component interface object.
+     * \retval an empty string if there was not enough memory to allocate the actual name.
+     */
+    virtual C2String getName() const = 0;
+
+    /**
+     * Returns a unique ID for this component or interface object.
+     * This ID is used as work targets, unique work IDs, and when configuring tunneling.
+     *
+     * When attached to a component, this method MUST be supported in any component state.
+     * This call does not change the state nor the internal configuration of the component.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \return a unique node ID for this component or component interface instance.
+     */
+    virtual c2_node_id_t getId() const = 0;
+
+    /**
+     * Queries a set of parameters from the component or interface object.
+     * Querying is performed at best effort: the component SHALL query all supported parameters and
+     * skip unsupported ones, heap allocated parameters that could not be allocated or parameters
+     * that could not be queried without blocking. Any errors are communicated in the return value.
+     * Additionally, preallocated (e.g. stack) parameters that could not be queried are invalidated.
+     * Invalid or blocking parameters to be allocated on the heap are omitted from the result.
+     *
+     * \note Parameter values do not depend on the order of query.
+     *
+     * \todo This method cannot be used to query info-buffers. Is that a problem?
+     *
+     * When attached to a component, this method MUST be supported in any component state except
+     * released.
+     * This call does not change the state nor the internal configuration of the component.
+     *
+     * This method has a variable blocking behavior based on state.
+     * In the stopped state this method MUST be "non-blocking" and return within 1ms.
+     * In the running states this method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * \param[in,out] stackParams  a list of params queried. These are initialized specific to each
+     *                             setting; e.g. size and index are set and rest of the members are
+     *                             cleared.
+     *                             \note Flexible settings that are of incorrect size will be
+     *                             invalidated.
+     * \param[in] heapParamIndices a vector of param indices for params to be queried and returned
+     *                             on the heap. These parameters will be returned in heapParams.
+     *                             Unsupported param indices will be ignored.
+     * \param[in] mayBlock         if true (C2_MAY_BLOCK), implementation may momentarily block.
+     *                             Otherwise (C2_DONT_BLOCK), it must be "non-blocking".
+     * \param[out] heapParams      a list of params where to which the supported heap parameters
+     *                             will be appended in the order they appear in heapParamIndices.
+     *
+     * \retval C2_OK        all parameters could be queried
+     * \retval C2_BAD_INDEX all supported parameters could be queried, but some parameters were not
+     *                      supported
+     * \retval C2_BAD_STATE when called in the released component state (user error)
+     *                      (this error code is only allowed for interfaces connected to components)
+     * \retval C2_NO_MEMORY could not allocate memory for a supported parameter
+     * \retval C2_BLOCKING  the operation must block to complete but mayBlock is false
+     *                      (this error code is only allowed for interfaces connected to components)
+     * \retval C2_TIMED_OUT could not query the parameters within the time limit (unexpected)
+     *                      (this error code is only allowed for interfaces connected to components
+     *                      in the running state)
+     * \retval C2_CORRUPTED some unknown error prevented the querying of the parameters
+     *                      (unexpected)
+     *                      (this error code is only allowed for interfaces connected to components)
+     */
+    virtual c2_status_t query_vb(
+        const std::vector<C2Param*> &stackParams,
+        const std::vector<C2Param::Index> &heapParamIndices,
+        c2_blocking_t mayBlock,
+        std::vector<std::unique_ptr<C2Param>>* const heapParams) const = 0;
+
+    /**
+     * Sets a set of parameters for the component or interface object.
+     *
+     * Tuning is performed at best effort: the component SHALL process the configuration updates in
+     * the order they appear in |params|. If any parameter update fails, the component shall
+     * communicate the failure in the return value and in |failures|, and still process the
+     * remaining parameters. Unsupported parameters are skipped, though they are communicated in
+     * ther return value. Most parameters are updated at best effort - such that even if client
+     * specifies an unsupported value for a field, the closest supported value is used. On the
+     * other hand, strict parameters only accept specific values for their fields, and if the client
+     * specifies an unsupported value, the parameter setting shall fail for that field.
+     * If the client tries to change the value of a field that requires momentary blocking without
+     * setting |mayBlock| to C2_MAY_BLOCK, that parameter shall also be skipped and a specific
+     * return value shall be used. Final values for all parameters set are propagated back to the
+     * caller in |params|.
+     *
+     * \note Parameter tuning DOES depend on the order of the tuning parameters. E.g. some parameter
+     * update may allow some subsequent values for further parameter updates.
+     *
+     * When attached to a component, this method MUST be supported in any component state except
+     * released.
+     *
+     * This method has a variable blocking behavior based on state.
+     * In the stopped state this method MUST be "non-blocking" and return within 1ms.
+     * In the running states this method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * \param[in,out] params a list of parameter updates. These will be updated to the actual
+     *                       parameter values after the updates (this is because tuning is performed
+     *                       at best effort).
+     *                       \todo params that could not be updated are not marked here, so are
+     *                       confusing - are they "existing" values or intended to be configured
+     *                       values?
+     * \param[in] mayBlock   if true (C2_MAY_BLOCK), implementation may momentarily block.
+     *                       Otherwise (C2_DONT_BLOCK), it must be "non-blocking".
+     * \param[out] failures  a list of parameter failures and optional guidance
+     *
+     * \retval C2_OK        all parameters could be updated successfully
+     * \retval C2_BAD_INDEX all supported parameters could be updated successfully, but some
+     *                      parameters were not supported
+     * \retval C2_BAD_VALUE some supported parameters could not be updated successfully because
+     *                      they contained unsupported values. These are returned in |failures|.
+     * \retval C2_BAD_STATE when called in the released component state (user error)
+     *                      (this error code is only allowed for interfaces connected to components)
+     * \retval C2_NO_MEMORY some supported parameters could not be updated successfully because
+     *                      they contained unsupported values, but could not allocate a failure
+     *                      object for them.
+     * \retval C2_TIMED_OUT could not set the parameters within the time limit (unexpected)
+     *                      (this error code is only allowed for interfaces connected to components
+     *                      in the running state)
+     * \retval C2_BLOCKING  the operation must block to complete but mayBlock is false
+     *                      (this error code is only allowed for interfaces connected to components)
+     * \retval C2_CORRUPTED some unknown error prevented the update of the parameters
+     *                      (unexpected)
+     *                      (this error code is only allowed for interfaces connected to components)
+     */
+    virtual c2_status_t config_vb(
+            const std::vector<C2Param*> &params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) = 0;
+
+    // TUNNELING
+    // =============================================================================================
+
+    /**
+     * Creates a tunnel from this component to the target component.
+     *
+     * If the component is successfully created, subsequent work items queued may include a
+     * tunneled path between these components.
+     *
+     * When attached to a component, this method MUST be supported in any component state except
+     * released.
+     *
+     * This method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * \retval C2_OK        the tunnel was successfully created
+     * \retval C2_BAD_INDEX the target component does not exist
+     * \retval C2_DUPLICATE the tunnel already exists
+     * \retval C2_OMITTED   tunneling is not supported by this component
+     * \retval C2_CANNOT_DO the specific tunnel is not supported
+     * \retval C2_BAD_STATE when called in the released component state (user error)
+     *                      (this error code is only allowed for interfaces connected to components)
+     *
+     * \retval C2_TIMED_OUT could not create the tunnel within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the creation of the tunnel (unexpected)
+     *                      (this error code is only allowed for interfaces connected to components)
+     */
+    virtual c2_status_t createTunnel_sm(c2_node_id_t targetComponent) = 0;
+
+    /**
+     * Releases a tunnel from this component to the target component.
+     *
+     * The release of a tunnel is delayed while there are pending work items for the tunnel.
+     * After releasing a tunnel, subsequent work items queued MUST NOT include a tunneled
+     * path between these components.
+     *
+     * When attached to a component, this method MUST be supported in any component state except
+     * released.
+     *
+     * This method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * \retval C2_OK        the tunnel was marked for release successfully
+     * \retval C2_BAD_INDEX the target component does not exist
+     * \retval C2_NOT_FOUND the tunnel does not exist
+     * \retval C2_OMITTED   tunneling is not supported by this component
+     * \retval C2_BAD_STATE when called in the released component state (user error)
+     *                      (this error code is only allowed for interfaces connected to components)
+     *
+     * \retval C2_TIMED_OUT could not mark the tunnel for release within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the release of the tunnel (unexpected)
+     *                      (this error code is only allowed for interfaces connected to components)
+     */
+    virtual c2_status_t releaseTunnel_sm(c2_node_id_t targetComponent) = 0;
+
+    // REFLECTION MECHANISM (USED FOR EXTENSION)
+    // =============================================================================================
+
+    /**
+     * Returns the set of supported parameters.
+     *
+     * When attached to a component, this method MUST be supported in any component state except
+     * released.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \param[out] params a vector of supported parameters will be appended to this vector.
+     *
+     * \retval C2_OK        the operation completed successfully.
+     * \retval C2_BAD_STATE when called in the released component state (user error)
+     *                      (this error code is only allowed for interfaces connected to components)
+     * \retval C2_NO_MEMORY not enough memory to complete this method.
+     */
+    virtual c2_status_t querySupportedParams_nb(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const = 0;
+
+    /**
+     * Retrieves the supported values for the queried fields.
+     *
+     * Client SHALL set the parameter-field specifier and the type of supported values query (e.g.
+     * currently supported values, or potential supported values) in fields.
+     * Upon return the component SHALL fill in the supported values for the fields listed as well
+     * as a status for each field. Component shall process all fields queried even if some queries
+     * fail.
+     *
+     * When attached to a component, this method MUST be supported in any component state except
+     * released.
+     *
+     * This method has a variable blocking behavior based on state.
+     * In the stopped state this method MUST be "non-blocking" and return within 1ms.
+     * In the running states this method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * \param[in out] fields a vector of fields descriptor structures.
+     * \param[in] mayBlock   if true (C2_MAY_BLOCK), implementation may momentarily block.
+     *                       Otherwise (C2_DONT_BLOCK), it must be "non-blocking".
+     *
+     * \retval C2_OK        the operation completed successfully.
+     * \retval C2_BAD_STATE when called in the released component state (user error)
+     *                      (this error code is only allowed for interfaces connected to components)
+     * \retval C2_BAD_INDEX at least one field was not recognized as a component field
+     * \retval C2_TIMED_OUT could not query supported values within the time limit (unexpected)
+     *                      (this error code is only allowed for interfaces connected to components
+     *                      in the running state)
+     * \retval C2_BLOCKING  the operation must block to complete but mayBlock is false
+     *                      (this error code is only allowed for interfaces connected to components)
+     * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+     *                      (this error code is only allowed for interfaces connected to components)
+     */
+    virtual c2_status_t querySupportedValues_vb(
+            std::vector<C2FieldSupportedValuesQuery> &fields, c2_blocking_t mayBlock) const = 0;
+
+    virtual ~C2ComponentInterface() = default;
+};
+
+class C2Component {
+public:
+    class Listener {
+    public:
+        virtual void onWorkDone_nb(std::weak_ptr<C2Component> component,
+                                std::list<std::unique_ptr<C2Work>> workItems) = 0;
+
+        virtual void onTripped_nb(std::weak_ptr<C2Component> component,
+                               std::vector<std::shared_ptr<C2SettingResult>> settingResult) = 0;
+
+        virtual void onError_nb(std::weak_ptr<C2Component> component,
+                             uint32_t errorCode) = 0;
+
+        // virtual void onTunnelReleased(<from>, <to>) = 0;
+
+        // virtual void onComponentReleased(<id>) = 0;
+
+        virtual ~Listener() = default;
+    };
+
+    /**
+     * Sets the listener for this component
+     *
+     * This method MUST be supported in all states except released.
+     * The listener can only be set to non-null value in stopped state (that does not include
+     * tripped or error). It can be set to nullptr in both stopped and running states.
+     * Components only use the listener in running state.
+     *
+     * If listener is nullptr, the component SHALL guarantee that no more listener callbacks are
+     * done to the original listener once this method returns. (Any pending listener callbacks will
+     * need to be completed during this call - hence this call may be temporarily blocking.)
+     *
+     * This method has a variable blocking behavior based on state.
+     * In the stopped state this method MUST be "non-blocking" and return within 1ms.
+     * In the running states this method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * Component SHALL handle listener notifications from the same thread (the thread used is
+     * at the component's discretion.)
+     *
+     * \note This could also be accomplished by passing a weak_ptr to a component-specific listener
+     * here and requiring the client to always promote the weak_ptr before any callback. This would
+     * put the burden on the client to clear the listener - wait for its deletion - at which point
+     * it is guaranteed that no more listener callbacks will occur.
+     *
+     * \param[in] listener the component listener object
+     * \param[in] mayBlock if true (C2_MAY_BLOCK), implementation may momentarily block.
+     *                     Otherwise (C2_DONT_BLOCK), it must be "non-blocking".
+     *
+     * \retval C2_BAD_STATE attempting to change the listener in the running state to a non-null
+     *                      value (user error), or called in the released state
+     * \retval C2_BLOCKING  the operation must block to complete but mayBlock is false
+     * \retval C2_OK        listener was updated successfully.
+     */
+    virtual c2_status_t setListener_vb(
+            const std::shared_ptr<Listener> &listener, c2_blocking_t mayBlock) = 0;
+
+    /// component domain (e.g. audio or video)
+    enum domain_t : uint32_t;
+
+    /// component kind (e.g. encoder, decoder or filter)
+    enum kind_t : uint32_t;
+
+    /// component rank. This number is used to determine component ordering (the lower the sooner)
+    /// in the component list.
+    typedef uint32_t rank_t;
+
+    /// component attributes
+    enum attrib_t : uint64_t;
+
+    /**
+     * Information about a component.
+     */
+    struct Traits {
+    // public:
+        C2String name; ///< name of the component
+        domain_t domain; ///< component domain
+        kind_t kind; ///< component kind
+        rank_t rank; ///< component rank
+        C2String mediaType; ///< media type supported by the component
+        C2String owner; ///< name of the component store owning this component
+
+        /**
+         * name alias(es) for backward compatibility.
+         * \note Multiple components can have the same alias as long as their media-type differs.
+         */
+        std::vector<C2String> aliases; ///< name aliases for backward compatibility
+    };
+
+    // METHODS AVAILABLE WHEN RUNNING
+    // =============================================================================================
+
+    /**
+     * Queues up work for the component.
+     *
+     * This method MUST be supported in running (including tripped and error) states.
+     *
+     * This method MUST be "non-blocking" and return within 1 ms
+     *
+     * It is acceptable for this method to return OK and return an error value using the
+     * onWorkDone() callback.
+     *
+     * \retval C2_OK        the work was successfully queued
+     * \retval C2_BAD_INDEX some component(s) in the work do(es) not exist
+     * \retval C2_CANNOT_DO the components are not tunneled
+     * \retval C2_BAD_STATE when called in the stopped or released state (user error)
+     *
+     * \retval C2_NO_MEMORY not enough memory to queue the work
+     * \retval C2_CORRUPTED some unknown error prevented queuing the work (unexpected)
+     */
+    virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) = 0;
+
+    /**
+     * Announces a work to be queued later for the component. This reserves a slot for the queue
+     * to ensure correct work ordering even if the work is queued later.
+     *
+     * This method MUST be supported in running (including tripped and error) states.
+     *
+     * This method MUST be "non-blocking" and return within 1 ms
+     *
+     * \retval C2_OK        the work announcement has been successfully recorded
+     * \retval C2_BAD_INDEX some component(s) in the work outline do(es) not exist
+     * \retval C2_CANNOT_DO the componentes are not tunneled
+     * \retval C2_BAD_STATE when called in the stopped or released state (user error)
+     *
+     * \retval C2_NO_MEMORY not enough memory to record the work announcement
+     * \retval C2_CORRUPTED some unknown error prevented recording the announcement (unexpected)
+     *
+     * \todo Can this be rolled into queue_nb?
+     * \todo Expose next work item for each component to detect stalls
+     */
+    virtual c2_status_t announce_nb(const std::vector<C2WorkOutline> &items) = 0;
+
+    enum flush_mode_t : uint32_t {
+        /// flush work from this component only
+        FLUSH_COMPONENT,
+
+        /// flush work from this component and all components connected downstream from it via
+        /// tunneling
+        FLUSH_CHAIN = (1 << 16),
+    };
+
+    /**
+     * Discards and abandons any pending work for the component, and optionally any component
+     * downstream.
+     *
+     * \todo define this: we could flush all work before last item queued for component across all
+     *                    components linked to this; flush only work items that are queued to this
+     *                    component
+     * \todo return work # of last flushed item; or all flushed (but not returned items)
+     * \todo we could make flush take a work item and flush all work before/after that item to allow
+     *       TBD (slicing/seek?)
+     * \todo we could simply take a list of numbers and flush those... this is bad for decoders
+     *       also, what would happen to fine grade references?
+     *
+     * This method MUST be supported in running (including tripped and error) states.
+     *
+     * This method may be momentarily blocking, but must return within 5ms.
+     *
+     * Work that could be immediately abandoned/discarded SHALL be returned in |flushedWork|; this
+     * can be done in an arbitrary order.
+     *
+     * Work that could not be abandoned or discarded immediately SHALL be marked to be
+     * discarded at the earliest opportunity, and SHALL be returned via the onWorkDone() callback.
+     * This shall be completed within 500ms.
+     *
+     * \param mode flush mode
+     *
+     * \retval C2_OK        the component has been successfully flushed
+     * \retval C2_BAD_STATE when called in the stopped or released state (user error)
+     * \retval C2_TIMED_OUT the flush could not be completed within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented flushing from completion (unexpected)
+     */
+    virtual c2_status_t flush_sm(flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) = 0;
+
+    enum drain_mode_t : uint32_t {
+        /// drain component only and add an "end-of-stream" marker. Component shall process all
+        /// queued work and complete the current stream. If new input is received, it shall start
+        /// a new stream. \todo define what a stream is.
+        DRAIN_COMPONENT_WITH_EOS,
+        /// drain component without setting "end-of-stream" marker. Component shall process all
+        /// queued work but shall expect more work items for the same stream.
+        DRAIN_COMPONENT_NO_EOS = (1 << 0),
+
+        /// marks the last work item with a persistent "end-of-stream" marker that will drain
+        /// downstream components
+        /// \todo this may confuse work-ordering downstream
+        DRAIN_CHAIN = (1 << 16),
+
+        /**
+         * \todo define this; we could place EOS to all upstream components, just this component, or
+         *       all upstream and downstream component.
+         * \todo should EOS carry over to downstream components?
+         */
+    };
+
+    /**
+     * Drains the component, and optionally downstream components. This is a signalling method;
+     * as such it does not wait for any work completion.
+     *
+     * Marks last work item as "drain-till-here", so component is notified not to wait for further
+     * work before it processes work already queued. This method can also used to set the
+     * end-of-stream flag after work has been queued. Client can continue to queue further work
+     * immediately after this method returns.
+     *
+     * This method MUST be supported in running (including tripped) states.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * Work that is completed SHALL be returned via the onWorkDone() callback.
+     *
+     * \param mode drain mode
+     *
+     * \retval C2_OK        the drain request has been successfully recorded
+     * \retval C2_BAD_STATE when called in the stopped or released state (user error)
+     * \retval C2_BAD_VALUE the drain mode is not supported by the component
+     *                      \todo define supported modes discovery
+     * \retval C2_TIMED_OUT the flush could not be completed within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented flushing from completion (unexpected)
+     */
+    virtual c2_status_t drain_nb(drain_mode_t mode) = 0;
+
+    // STATE CHANGE METHODS
+    // =============================================================================================
+
+    /**
+     * Starts the component.
+     *
+     * This method MUST be supported in stopped state, as well as during the tripped state.
+     *
+     * If the return value is C2_OK, the component shall be in the running state.
+     * If the return value is C2_BAD_STATE or C2_DUPLICATE, no state change is expected as a
+     * response to this call.
+     * Otherwise, the component shall be in the stopped state.
+     *
+     * \note If a component is in the tripped state and start() is called while the component
+     * configuration still results in a trip, start shall succeed and a new onTripped callback
+     * should be used to communicate the configuration conflict that results in the new trip.
+     *
+     * \todo This method MUST return within 500ms. Seems this should be able to return quickly, as
+     * there are no immediate guarantees. Though there are guarantees for responsiveness immediately
+     * after start returns.
+     *
+     * \retval C2_OK        the component has started (or resumed) successfully
+     * \retval C2_DUPLICATE when called during another start call from another thread
+     * \retval C2_BAD_STATE when called in any state other than the stopped state or tripped state,
+     *                      including when called during another state change call from another
+     *                      thread (user error)
+     * \retval C2_NO_MEMORY not enough memory to start the component
+     * \retval C2_TIMED_OUT the component could not be started within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented starting the component (unexpected)
+     */
+    virtual c2_status_t start() = 0;
+
+    /**
+     * Stops the component.
+     *
+     * This method MUST be supported in running (including tripped) state.
+     *
+     * This method MUST return withing 500ms.
+     *
+     * Upon this call, all pending work SHALL be abandoned and all buffer references SHALL be
+     * released.
+     * If the return value is C2_BAD_STATE or C2_DUPLICATE, no state change is expected as a
+     * response to this call.
+     * For all other return values, the component shall be in the stopped state.
+     *
+     * \todo should this return completed work, since client will just free it? Perhaps just to
+     * verify accounting.
+     *
+     * This does not alter any settings and tunings that may have resulted in a tripped state.
+     * (Is this material given the definition? Perhaps in case we want to start again.)
+     *
+     * \retval C2_OK        the component has started successfully
+     * \retval C2_DUPLICATE when called during another stop call from another thread
+     * \retval C2_BAD_STATE when called in any state other than the running state, including when
+     *                      called during another state change call from another thread (user error)
+     * \retval C2_TIMED_OUT the component could not be stopped within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented stopping the component (unexpected)
+     */
+    virtual c2_status_t stop() = 0;
+
+    /**
+     * Resets the component.
+     *
+     * This method MUST be supported in all (including tripped) states other than released.
+     *
+     * This method MUST be supported during any other blocking call.
+     *
+     * This method MUST return withing 500ms.
+     *
+     * After this call returns all work SHALL be abandoned, all buffer references SHALL be released.
+     * If the return value is C2_BAD_STATE or C2_DUPLICATE, no state change is expected as a
+     * response to this call.
+     * For all other return values, the component shall be in the stopped state.
+     *
+     * \todo should this return completed work, since client will just free it? Also, if it unblocks
+     * a stop, where should completed work be returned?
+     *
+     * This brings settings back to their default - "guaranteeing" no tripped space.
+     *
+     * \todo reclaim support - it seems that since ownership is passed, this will allow reclaiming
+     * stuff.
+     *
+     * \retval C2_OK        the component has been reset
+     * \retval C2_DUPLICATE when called during another reset call from another thread
+     * \retval C2_BAD_STATE when called in the released state
+     * \retval C2_TIMED_OUT the component could not be reset within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented resetting the component (unexpected)
+     */
+    virtual c2_status_t reset() = 0;
+
+    /**
+     * Releases the component.
+     *
+     * This method MUST be supported in stopped state.
+     *
+     * This method MUST return withing 500ms. Upon return all references shall be abandoned.
+     *
+     * \retval C2_OK        the component has been released
+     * \retval C2_DUPLICATE the component is already released
+     * \retval C2_BAD_STATE the component is running
+     * \retval C2_TIMED_OUT the component could not be released within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented releasing the component (unexpected)
+     */
+    virtual c2_status_t release() = 0;
+
+    /**
+     * Returns the interface for this component.
+     *
+     * \return the component interface
+     */
+    virtual std::shared_ptr<C2ComponentInterface> intf() = 0;
+
+    virtual ~C2Component() = default;
+};
+
+C2ENUM(C2Component::kind_t, uint32_t,
+    KIND_OTHER,
+    KIND_DECODER,
+    KIND_ENCODER
+);
+
+C2ENUM(C2Component::domain_t, uint32_t,
+    DOMAIN_OTHER,
+    DOMAIN_VIDEO,
+    DOMAIN_AUDIO,
+    DOMAIN_IMAGE
+);
+
+class C2FrameInfoParser {
+public:
+    /**
+     * \return the content type supported by this info parser.
+     *
+     * \todo this may be redundant
+     */
+    virtual C2StringLiteral getType() const = 0;
+
+    /**
+     * \return a vector of supported parameter indices parsed by this info parser.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \todo sticky vs. non-sticky params? this may be communicated by param-reflector.
+     */
+    virtual const std::vector<C2Param::Index> getParsedParams() const = 0;
+
+    /**
+     * Resets this info parser. This brings this parser to its initial state after creation.
+     *
+     * This method SHALL return within 5ms.
+     *
+     * \retval C2_OK        the info parser was reset
+     * \retval C2_TIMED_OUT could not reset the parser within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the resetting of the parser (unexpected)
+     */
+    virtual c2_status_t reset() { return C2_OK; }
+
+    virtual c2_status_t parseFrame(C2FrameData &frame);
+
+    virtual ~C2FrameInfoParser() = default;
+};
+
+class C2AllocatorStore {
+public:
+    typedef C2Allocator::id_t id_t;
+
+    enum : C2Allocator::id_t {
+        DEFAULT_LINEAR,     ///< basic linear allocator type
+        DEFAULT_GRAPHIC,    ///< basic graphic allocator type
+        PLATFORM_START = 0x10,
+        VENDOR_START   = 0x100,
+        BAD_ID         = C2Allocator::BAD_ID, ///< DO NOT USE
+    };
+
+    /**
+     * Returns the unique name of this allocator store.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \return the name of this allocator store.
+     * \retval an empty string if there was not enough memory to allocate the actual name.
+     */
+    virtual C2String getName() const = 0;
+
+    /**
+     * Returns the set of allocators supported by this allocator store.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \retval vector of allocator information (as shared pointers)
+     * \retval an empty vector if there was not enough memory to allocate the whole vector.
+     */
+    virtual std::vector<std::shared_ptr<const C2Allocator::Traits>> listAllocators_nb() const = 0;
+
+    /**
+     * Retrieves/creates a shared allocator object.
+     *
+     * This method MUST be return within 5ms.
+     *
+     * The allocator is created on first use, and the same allocator is returned on subsequent
+     * concurrent uses in the same process. The allocator is freed when it is no longer referenced.
+     *
+     * \param id      the ID of the allocator to create. This is defined by the store, but
+     *                the ID of the default linear and graphic allocators is formalized.
+     * \param allocator shared pointer where the created allocator is stored. Cleared on failure
+     *                  and updated on success.
+     *
+     * \retval C2_OK        the allocator was created successfully
+     * \retval C2_TIMED_OUT could not create the allocator within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the creation of the allocator (unexpected)
+     *
+     * \retval C2_NOT_FOUND no such allocator
+     * \retval C2_NO_MEMORY not enough memory to create the allocator
+     */
+    virtual c2_status_t fetchAllocator(id_t id, std::shared_ptr<C2Allocator>* const allocator) = 0;
+
+    virtual ~C2AllocatorStore() = default;
+};
+
+class C2ComponentStore {
+public:
+    /**
+     * Returns the name of this component or component interface object.
+     * This is a unique name for this component or component interface 'class'; however, multiple
+     * instances of this component SHALL have the same name.
+     *
+     * This method MUST be supported in any state. This call does not change the state nor the
+     * internal states of the component.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \return the name of this component or component interface object.
+     * \retval an empty string if there was not enough memory to allocate the actual name.
+     */
+    virtual C2String getName() const = 0;
+
+    /**
+     * Creates a component.
+     *
+     * This method SHALL return within 100ms.
+     *
+     * \param name          name of the component to create
+     * \param component     shared pointer where the created component is stored. Cleared on
+     *                      failure and updated on success.
+     *
+     * \retval C2_OK        the component was created successfully
+     * \retval C2_TIMED_OUT could not create the component within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the creation of the component (unexpected)
+     *
+     * \retval C2_NOT_FOUND no such component
+     * \retval C2_NO_MEMORY not enough memory to create the component
+     */
+    virtual c2_status_t createComponent(
+            C2String name, std::shared_ptr<C2Component>* const component) = 0;
+
+    /**
+     * Creates a component interface.
+     *
+     * This method SHALL return within 100ms.
+     *
+     * \param name          name of the component interface to create
+     * \param interface     shared pointer where the created interface is stored
+     *
+     * \retval C2_OK        the component interface was created successfully
+     * \retval C2_TIMED_OUT could not create the component interface within the time limit
+     *                      (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the creation of the component interface
+     *                      (unexpected)
+     *
+     * \retval C2_NOT_FOUND no such component interface
+     * \retval C2_NO_MEMORY not enough memory to create the component interface
+     *
+     * \todo Do we need an interface, or could this just be a component that is never started?
+     */
+    virtual c2_status_t createInterface(
+            C2String name, std::shared_ptr<C2ComponentInterface>* const interface) = 0;
+
+    /**
+     * Returns the list of components supported by this component store.
+     *
+     * This method MUST return within 500ms.
+     *
+     * \retval vector of component information.
+     */
+    virtual std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() = 0;
+
+    // -------------------------------------- UTILITY METHODS --------------------------------------
+
+    // on-demand buffer layout conversion (swizzling)
+    //
+    virtual c2_status_t copyBuffer(
+            std::shared_ptr<C2GraphicBuffer> src, std::shared_ptr<C2GraphicBuffer> dst) = 0;
+
+    // -------------------------------------- CONFIGURATION API -----------------------------------
+    // e.g. for global settings (system-wide stride, etc.)
+
+    /**
+     * Queries a set of system-wide parameters.
+     * Querying is performed at best effort: the store SHALL query all supported parameters and
+     * skip unsupported ones, or heap allocated parameters that could not be allocated. Any errors
+     * are communicated in the return value. Additionally, preallocated (e.g. stack) parameters that
+     * could not be queried are invalidated. Parameters to be allocated on the heap are omitted from
+     * the result.
+     *
+     * \note Parameter values do not depend on the order of query.
+     *
+     * This method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * \param stackParams   a list of params queried. These are initialized specific to each
+     *                      setting; e.g. size and index are set and rest of the members are
+     *                      cleared.
+     *                      NOTE: Flexible settings that are of incorrect size will be invalidated.
+     * \param heapParamIndices a vector of param indices for params to be queried and returned on the
+     *                      heap. These parameters will be returned in heapParams. Unsupported param
+     *                      indices will be ignored.
+     * \param heapParams    a list of params where to which the supported heap parameters will be
+     *                      appended in the order they appear in heapParamIndices.
+     *
+     * \retval C2_OK        all parameters could be queried
+     * \retval C2_BAD_INDEX all supported parameters could be queried, but some parameters were not
+     *                      supported
+     * \retval C2_NO_MEMORY could not allocate memory for a supported parameter
+     * \retval C2_CORRUPTED some unknown error prevented the querying of the parameters
+     *                      (unexpected)
+     */
+    virtual c2_status_t query_sm(
+        const std::vector<C2Param*> &stackParams,
+        const std::vector<C2Param::Index> &heapParamIndices,
+        std::vector<std::unique_ptr<C2Param>>* const heapParams) const = 0;
+
+    /**
+     * Sets a set of system-wide parameters.
+     *
+     * \note There are no settable system-wide parameters defined thus far, but may be added in the
+     * future.
+     *
+     * Tuning is performed at best effort: the store SHALL update all supported configuration at
+     * best effort (unless configured otherwise) and skip unsupported ones. Any errors are
+     * communicated in the return value and in |failures|.
+     *
+     * \note Parameter tuning DOES depend on the order of the tuning parameters. E.g. some parameter
+     * update may allow some subsequent parameter update.
+     *
+     * This method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * \param params        a list of parameter updates. These will be updated to the actual
+     *                      parameter values after the updates (this is because tuning is performed
+     *                      at best effort).
+     *                      \todo params that could not be updated are not marked here, so are
+     *                      confusing - are they "existing" values or intended to be configured
+     *                      values?
+     * \param failures      a list of parameter failures
+     *
+     * \retval C2_OK        all parameters could be updated successfully
+     * \retval C2_BAD_INDEX all supported parameters could be updated successfully, but some
+     *                      parameters were not supported
+     * \retval C2_BAD_VALUE some supported parameters could not be updated successfully because
+     *                      they contained unsupported values. These are returned in |failures|.
+     * \retval C2_NO_MEMORY some supported parameters could not be updated successfully because
+     *                      they contained unsupported values, but could not allocate a failure
+     *                      object for them.
+     * \retval C2_CORRUPTED some unknown error prevented the update of the parameters
+     *                      (unexpected)
+     */
+    virtual c2_status_t config_sm(
+            const std::vector<C2Param*> &params,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) = 0;
+
+    // REFLECTION MECHANISM (USED FOR EXTENSION)
+    // =============================================================================================
+
+    /**
+     * Returns the parameter reflector.
+     *
+     * This is used to describe parameter fields. This is shared for all components created by
+     * this component store.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \return a shared parameter reflector object.
+     */
+    virtual std::shared_ptr<C2ParamReflector> getParamReflector() const = 0;
+
+    /**
+     * Returns the set of supported parameters.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \param[out] params a vector of supported parameters will be appended to this vector.
+     *
+     * \retval C2_OK        the operation completed successfully.
+     * \retval C2_NO_MEMORY not enough memory to complete this method.
+     */
+    virtual c2_status_t querySupportedParams_nb(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const = 0;
+
+    /**
+     * Retrieves the supported values for the queried fields.
+     *
+     * Client SHALL set the parameter-field specifier and the type of supported values query (e.g.
+     * currently supported values, or potential supported values) in fields.
+     * Upon return the store SHALL fill in the supported values for the fields listed as well
+     * as a status for each field. Store shall process all fields queried even if some queries
+     * fail.
+     *
+     * This method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * \param[in out] fields a vector of fields descriptor structures.
+     *
+     * \retval C2_OK        the operation completed successfully.
+     * \retval C2_BAD_INDEX at least one field was not recognized as a component store field
+     */
+    virtual c2_status_t querySupportedValues_sm(
+            std::vector<C2FieldSupportedValuesQuery> &fields) const = 0;
+
+    virtual ~C2ComponentStore() = default;
+};
+
+// ================================================================================================
+
+/// @}
+
+#endif  // C2COMPONENT_H_
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
new file mode 100644
index 0000000..3820f90
--- /dev/null
+++ b/media/codec2/core/include/C2Config.h
@@ -0,0 +1,2149 @@
+/*
+ * 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.
+ */
+
+#ifndef C2CONFIG_H_
+#define C2CONFIG_H_
+
+#include <C2.h>
+#include <C2Component.h>
+#include <C2Enum.h>
+#include <C2ParamDef.h>
+
+/// \defgroup config Component configuration
+/// @{
+
+/**
+ * Enumerated boolean.
+ */
+C2ENUM(c2_bool_t, uint32_t,
+    C2_FALSE, ///< true
+    C2_TRUE,  ///< false
+)
+
+typedef C2SimpleValueStruct<c2_bool_t> C2BoolValue;
+
+typedef C2SimpleValueStruct<C2EasyEnum<c2_bool_t>> C2EasyBoolValue;
+
+/**
+ * Enumerated set tri-state.
+ *
+ * Used for optional configurations to distinguish between values set by the client,
+ * default values set by the component, or unset values.
+ */
+C2ENUM(c2_set_t, uint32_t,
+    C2_UNSET,   // parameter is unset and has no value
+    C2_SET,     // parameter is/has been set by the client
+    C2_DEFAULT, // parameter has not been set by the client, but is set by the component
+)
+
+/** Enumerations used by configuration parameters */
+struct C2Config {
+    enum aac_packaging_t : uint32_t;        ///< AAC packaging (RAW vs ADTS)
+    enum aac_sbr_mode_t : uint32_t;         ///< AAC SBR mode
+    enum api_feature_t : uint64_t;          ///< API features
+    enum api_level_t : uint32_t;            ///< API level
+    enum bitrate_mode_t : uint32_t;         ///< bitrate control mode
+    enum drc_compression_mode_t : int32_t;  ///< DRC compression mode
+    enum drc_effect_type_t : int32_t;       ///< DRC effect type
+    enum intra_refresh_mode_t : uint32_t;   ///< intra refresh modes
+    enum level_t : uint32_t;                ///< coding level
+    enum ordinal_key_t : uint32_t;          ///< work ordering keys
+    enum pcm_encoding_t : uint32_t;         ///< PCM encoding
+    enum picture_type_t : uint32_t;         ///< picture types
+    enum platform_feature_t : uint64_t;     ///< platform features
+    enum platform_level_t : uint32_t;       ///< platform level
+    enum prepend_header_mode_t : uint32_t;  ///< prepend header operational modes
+    enum profile_t : uint32_t;              ///< coding profile
+    enum scaling_method_t : uint32_t;       ///< scaling methods
+    enum scan_order_t : uint32_t;           ///< scan orders
+    enum secure_mode_t : uint32_t;          ///< secure/protected modes
+    enum supplemental_info_t : uint32_t;    ///< supplemental information types
+    enum tiling_mode_t : uint32_t;          ///< tiling modes
+};
+
+namespace {
+
+enum C2ParamIndexKind : C2Param::type_index_t {
+    C2_PARAM_INDEX_INVALID             = 0x0,    ///< do not use
+    C2_PARAM_INDEX_STRUCT_START        = 0x1,    ///< struct only indices
+    C2_PARAM_INDEX_PARAM_START         = 0x800,  ///< regular parameters
+    C2_PARAM_INDEX_CODER_PARAM_START   = 0x1000, ///< en/transcoder parameters
+    C2_PARAM_INDEX_PICTURE_PARAM_START = 0x1800, ///< image/video parameters
+    C2_PARAM_INDEX_VIDEO_PARAM_START   = 0x2000, ///< video parameters
+    C2_PARAM_INDEX_IMAGE_PARAM_START   = 0x2800, ///< image parameters
+    C2_PARAM_INDEX_AUDIO_PARAM_START   = 0x3000, ///< image parameters
+    C2_PARAM_INDEX_PLATFORM_START      = 0x4000, ///< platform-defined parameters
+
+    /* =================================== structure indices =================================== */
+
+    kParamIndexColorXy = C2_PARAM_INDEX_STRUCT_START,
+    kParamIndexMasteringDisplayColorVolume,
+    kParamIndexChromaOffset,
+    kParamIndexGopLayer,
+
+    /* =================================== parameter indices =================================== */
+
+    kParamIndexApiLevel = C2_PARAM_INDEX_PARAM_START,
+    kParamIndexApiFeatures,
+
+    /* ------------------------------------ all components ------------------------------------ */
+
+    /* generic component characteristics */
+    kParamIndexName,
+    kParamIndexAliases,
+    kParamIndexKind,
+    kParamIndexDomain,
+    kParamIndexAttributes,
+    kParamIndexTimeStretch,
+
+    /* coding characteristics */
+    kParamIndexProfileLevel,
+    kParamIndexInitData,
+    kParamIndexSupplementalData,
+    kParamIndexSubscribedSupplementalData,
+
+    /* pipeline characteristics */
+    kParamIndexMediaType,
+    __kParamIndexRESERVED_0,
+    kParamIndexDelay,
+    kParamIndexMaxReferenceAge,
+    kParamIndexMaxReferenceCount,
+    kParamIndexReorderBufferDepth,
+    kParamIndexReorderKey,
+    kParamIndexStreamCount,
+    kParamIndexSubscribedParamIndices,
+    kParamIndexSuggestedBufferCount,
+    kParamIndexBatchSize,
+    kParamIndexCurrentWork,
+    kParamIndexLastWorkQueued,
+
+    /* memory allocation */
+    kParamIndexAllocators,
+    kParamIndexBlockPools,
+    kParamIndexBufferType,
+    kParamIndexUsage,
+    kParamIndexOutOfMemory,
+    kParamIndexMaxBufferSize,
+
+    /* misc. state */
+    kParamIndexTripped,
+    kParamIndexConfigCounter,
+
+    /* resources */
+    kParamIndexResourcesNeeded,
+    kParamIndexResourcesReserved,
+    kParamIndexOperatingRate,
+    kParamIndexRealTimePriority,
+
+    /* protected content */
+    kParamIndexSecureMode,
+
+    // deprecated
+    kParamIndexDelayRequest = kParamIndexDelay | C2Param::CoreIndex::IS_REQUEST_FLAG,
+
+    /* ------------------------------------ (trans/en)coders ------------------------------------ */
+
+    kParamIndexBitrate = C2_PARAM_INDEX_CODER_PARAM_START,
+    kParamIndexBitrateMode,
+    kParamIndexQuality,
+    kParamIndexComplexity,
+    kParamIndexPrependHeaderMode,
+
+    /* --------------------------------- image/video components --------------------------------- */
+
+    kParamIndexPictureSize = C2_PARAM_INDEX_PICTURE_PARAM_START,
+    kParamIndexCropRect,
+    kParamIndexPixelFormat,
+    kParamIndexRotation,
+    kParamIndexPixelAspectRatio,
+    kParamIndexScaledPictureSize,
+    kParamIndexScaledCropRect,
+    kParamIndexScalingMethod,
+    kParamIndexColorInfo,
+    kParamIndexColorAspects,
+    kParamIndexHdrStaticMetadata,
+    kParamIndexDefaultColorAspects,
+
+    kParamIndexBlockSize,
+    kParamIndexBlockCount,
+    kParamIndexBlockRate,
+
+    kParamIndexPictureTypeMask,
+    kParamIndexPictureType,
+    kParamIndexHdr10PlusMetadata,
+
+    /* ------------------------------------ video components ------------------------------------ */
+
+    kParamIndexFrameRate = C2_PARAM_INDEX_VIDEO_PARAM_START,
+    kParamIndexMaxBitrate,
+    kParamIndexMaxFrameRate,
+    kParamIndexMaxPictureSize,
+    kParamIndexGop,
+    kParamIndexSyncFrameInterval,
+    kParamIndexRequestSyncFrame,
+    kParamIndexTemporalLayering,
+    kParamIndexLayerIndex,
+    kParamIndexLayerCount,
+    kParamIndexIntraRefresh,
+
+    /* ------------------------------------ image components ------------------------------------ */
+
+    kParamIndexTileLayout = C2_PARAM_INDEX_IMAGE_PARAM_START,
+    kParamIndexTileHandling,
+
+    /* ------------------------------------ audio components ------------------------------------ */
+
+    kParamIndexSampleRate = C2_PARAM_INDEX_AUDIO_PARAM_START,
+    kParamIndexChannelCount,
+    kParamIndexPcmEncoding,
+    kParamIndexAacPackaging,
+    kParamIndexMaxChannelCount,
+    kParamIndexAacSbrMode, // aac encode, enum
+    kParamIndexDrcEncodedTargetLevel,  // drc, float (dBFS)
+    kParamIndexDrcTargetReferenceLevel, // drc, float (dBFS)
+    kParamIndexDrcCompression, // drc, enum
+    kParamIndexDrcBoostFactor, // drc, float (0-1)
+    kParamIndexDrcAttenuationFactor, // drc, float (0-1)
+    kParamIndexDrcEffectType, // drc, enum
+
+    /* ============================== platform-defined parameters ============================== */
+
+    kParamIndexPlatformLevel = C2_PARAM_INDEX_PLATFORM_START, // all, u32
+    kParamIndexPlatformFeatures, // all, u64 mask
+    kParamIndexStoreIonUsage, // store, struct
+    kParamIndexAspectsToDataSpace, // store, struct
+    kParamIndexFlexiblePixelFormatDescriptor, // store, struct
+    kParamIndexFlexiblePixelFormatDescriptors, // store, struct[]
+    kParamIndexDataSpaceToAspects, // store, struct
+    kParamIndexDataSpace, // u32
+    kParamIndexSurfaceScaling, // u32
+
+    // input surface
+    kParamIndexInputSurfaceEos, // input-surface, eos
+    kParamIndexTimedControl, // struct
+    kParamIndexStartAt, // input-surface, struct
+    kParamIndexSuspendAt, // input-surface, struct
+    kParamIndexResumeAt, // input-surface, struct
+    kParamIndexStopAt, // input-surface, struct
+    kParamIndexTimeOffset, // input-surface, struct
+    kParamIndexMinFrameRate, // input-surface, float
+    kParamIndexTimestampGapAdjustment, // input-surface, struct
+
+    kParamIndexSurfaceAllocator, // u32
+};
+
+}
+
+/**
+ * Codec 2.0 parameter types follow the following naming convention:
+ *
+ * C2<group><domain><index><type>
+ *
+ * E.g. C2StreamPictureSizeInfo: group="" domain="Stream" index="PictureSize" type="Info".
+ * Group is somewhat arbitrary, but denotes kind of objects the parameter is defined.
+ * At this point we use Component and Store to distinguish basic component/store parameters.
+ *
+ * Parameter keys are named C2_PARAMKEY_[<group>_]<domain>_<index> as type is not expected
+ * to distinguish parameters. E.g. a component could change the type of the parameter and it
+ * is not expected users would need to change the key.
+ */
+
+/* ----------------------------------------- API level ----------------------------------------- */
+
+enum C2Config::api_level_t : uint32_t {
+    API_L0_1 = 0,   ///< support for API level 0.1
+};
+
+// read-only
+typedef C2GlobalParam<C2Setting, C2SimpleValueStruct<C2Config::api_level_t>, kParamIndexApiLevel>
+        C2ApiLevelSetting;
+constexpr char C2_PARAMKEY_API_LEVEL[] = "api.level";
+
+enum C2Config::api_feature_t : uint64_t {
+    API_REFLECTION       = (1U << 0),  ///< ability to list supported parameters
+    API_VALUES           = (1U << 1),  ///< ability to list supported values for each parameter
+    API_CURRENT_VALUES   = (1U << 2),  ///< ability to list currently supported values for each parameter
+    API_DEPENDENCY       = (1U << 3),  ///< have a defined parameter dependency
+
+    API_STREAMS          = (1ULL << 32),  ///< supporting variable number of streams
+
+    API_TUNNELING        = (1ULL << 48),  ///< tunneling API
+};
+
+// read-only
+typedef C2GlobalParam<C2Setting, C2SimpleValueStruct<C2Config::api_feature_t>, kParamIndexApiFeatures>
+        C2ApiFeaturesSetting;
+constexpr char C2_PARAMKEY_API_FEATURES[] = "api.features";
+
+/* ----------------------------- generic component characteristics ----------------------------- */
+
+/**
+ * The name of the component.
+ *
+ * This must contain only alphanumeric characters or dot '.', hyphen '-', plus '+', or
+ * underline '_'. The name of each component must be unique.
+ *
+ * For Android: Component names must start with 'c2.' followed by the company name or abbreviation
+ * and another dot, e.g. 'c2.android.'. Use of lowercase is preferred but not required.
+ */
+// read-only
+typedef C2GlobalParam<C2Setting, C2StringValue, kParamIndexName> C2ComponentNameSetting;
+constexpr char C2_PARAMKEY_COMPONENT_NAME[]  = "component.name";
+
+/**
+ * Alternate names (aliases) of the component.
+ *
+ * This is a comma ',' separated list of alternate component names. Unlike component names that
+ * must be unique, multiple components can have the same alias.
+ */
+// read-only
+typedef C2GlobalParam<C2Setting, C2StringValue, kParamIndexAliases> C2ComponentAliasesSetting;
+constexpr char C2_PARAMKEY_COMPONENT_ALIASES[]  = "component.aliases";
+
+/**
+ * Component kind.
+ */
+// read-only
+typedef C2GlobalParam<C2Setting, C2SimpleValueStruct<C2Component::kind_t>, kParamIndexKind>
+        C2ComponentKindSetting;
+constexpr char C2_PARAMKEY_COMPONENT_KIND[]  = "component.kind";
+
+/**
+ * Component domain.
+ */
+// read-only
+typedef C2GlobalParam<C2Setting, C2SimpleValueStruct<C2Component::domain_t>, kParamIndexDomain>
+        C2ComponentDomainSetting;
+constexpr char C2_PARAMKEY_COMPONENT_DOMAIN[]  = "component.domain";
+
+/**
+ * Component attributes.
+ *
+ * These are a set of flags provided by the component characterizing its processing algorithm.
+ */
+C2ENUM(C2Component::attrib_t, uint64_t,
+    ATTRIB_IS_TEMPORAL = 1u << 0, ///< component input ordering matters for processing
+)
+
+// read-only
+typedef C2GlobalParam<C2Setting, C2SimpleValueStruct<C2Component::attrib_t>, kParamIndexAttributes>
+        C2ComponentAttributesSetting;
+constexpr char C2_PARAMKEY_COMPONENT_ATTRIBUTES[] = "component.attributes";
+
+/**
+ * Time stretching.
+ *
+ * This is the ratio between the rate of the input timestamp, and the rate of the output timestamp.
+ * E.g. if this is 4.0, for every 1 seconds of input timestamp difference, the output shall differ
+ * by 4 seconds.
+ */
+typedef C2GlobalParam<C2Tuning, C2FloatValue, kParamIndexTimeStretch> C2ComponentTimeStretchTuning;
+constexpr char C2_PARAMKEY_TIME_STRETCH[]  = "algo.time-stretch";
+
+/* ----------------------------------- coding characteristics ----------------------------------- */
+
+/**
+ * Profile and level.
+ *
+ * Profile determines the tools used by the component.
+ * Level determines the level of resources used by the component.
+ */
+
+namespace {
+
+enum : uint32_t {
+    _C2_PL_MP2V_BASE = 0x1000,
+    _C2_PL_AAC_BASE  = 0x2000,
+    _C2_PL_H263_BASE = 0x3000,
+    _C2_PL_MP4V_BASE = 0x4000,
+    _C2_PL_AVC_BASE  = 0x5000,
+    _C2_PL_HEVC_BASE = 0x6000,
+    _C2_PL_VP9_BASE  = 0x7000,
+    _C2_PL_DV_BASE   = 0x8000,
+    _C2_PL_AV1_BASE  = 0x9000,
+
+    C2_PROFILE_LEVEL_VENDOR_START = 0x70000000,
+};
+
+}
+
+enum C2Config::profile_t : uint32_t {
+    PROFILE_UNUSED = 0,                         ///< profile is not used by this media type
+
+    // AAC (MPEG-2 Part 7 and MPEG-4 Part 3) profiles
+    PROFILE_AAC_LC = _C2_PL_AAC_BASE,           ///< AAC Low-Complexity
+    PROFILE_AAC_MAIN,                           ///< AAC Main
+    PROFILE_AAC_SSR,                            ///< AAC Scalable Sampling Rate
+    PROFILE_AAC_LTP,                            ///< AAC Long Term Prediction
+    PROFILE_AAC_HE,                             ///< AAC High-Efficiency
+    PROFILE_AAC_SCALABLE,                       ///< AAC Scalable
+    PROFILE_AAC_ER_LC,                          ///< AAC Error Resilient Low-Complexity
+    PROFILE_AAC_ER_SCALABLE,                    ///< AAC Error Resilient Scalable
+    PROFILE_AAC_LD,                             ///< AAC Low Delay
+    PROFILE_AAC_HE_PS,                          ///< AAC High-Efficiency Parametric Stereo
+    PROFILE_AAC_ELD,                            ///< AAC Enhanced Low Delay
+    PROFILE_AAC_XHE,                            ///< AAC Extended High-Efficiency
+
+    // MPEG-2 Video profiles
+    PROFILE_MP2V_SIMPLE = _C2_PL_MP2V_BASE,     ///< MPEG-2 Video (H.262) Simple
+    PROFILE_MP2V_MAIN,                          ///< MPEG-2 Video (H.262) Main
+    PROFILE_MP2V_SNR_SCALABLE,                  ///< MPEG-2 Video (H.262) SNR Scalable
+    PROFILE_MP2V_SPATIALLY_SCALABLE,            ///< MPEG-2 Video (H.262) Spatially Scalable
+    PROFILE_MP2V_HIGH,                          ///< MPEG-2 Video (H.262) High
+    PROFILE_MP2V_422,                           ///< MPEG-2 Video (H.262) 4:2:2
+    PROFILE_MP2V_MULTIVIEW,                     ///< MPEG-2 Video (H.262) Multi-view
+
+    // H.263 profiles
+    PROFILE_H263_BASELINE = _C2_PL_H263_BASE,   ///< H.263 Baseline (Profile 0)
+    PROFILE_H263_H320,                          ///< H.263 H.320 Coding Efficiency Version 2 Backward-Compatibility (Profile 1)
+    PROFILE_H263_V1BC,                          ///< H.263 Version 1 Backward-Compatibility (Profile 2)
+    PROFILE_H263_ISWV2,                         ///< H.263 Version 2 Interactive and Streaming Wireless (Profile 3)
+    PROFILE_H263_ISWV3,                         ///< H.263 Version 3 Interactive and Streaming Wireless (Profile 4)
+    PROFILE_H263_HIGH_COMPRESSION,              ///< H.263 Conversational High Compression (Profile 5)
+    PROFILE_H263_INTERNET,                      ///< H.263 Conversational Internet (Profile 6)
+    PROFILE_H263_INTERLACE,                     ///< H.263 Conversational Interlace (Profile 7)
+    PROFILE_H263_HIGH_LATENCY,                  ///< H.263 High Latency (Profile 8)
+
+    // MPEG-4 Part 2 (Video) Natural Visual Profiles
+    PROFILE_MP4V_SIMPLE,                        ///< MPEG-4 Video Simple
+    PROFILE_MP4V_SIMPLE_SCALABLE,               ///< MPEG-4 Video Simple Scalable
+    PROFILE_MP4V_CORE,                          ///< MPEG-4 Video Core
+    PROFILE_MP4V_MAIN,                          ///< MPEG-4 Video Main
+    PROFILE_MP4V_NBIT,                          ///< MPEG-4 Video N-Bit
+    PROFILE_MP4V_ARTS,                          ///< MPEG-4 Video Advanced Realtime Simple
+    PROFILE_MP4V_CORE_SCALABLE,                 ///< MPEG-4 Video Core Scalable
+    PROFILE_MP4V_ACE,                           ///< MPEG-4 Video Advanced Coding Efficiency
+    PROFILE_MP4V_ADVANCED_CORE,                 ///< MPEG-4 Video Advanced Core
+    PROFILE_MP4V_SIMPLE_STUDIO,                 ///< MPEG-4 Video Simple Studio
+    PROFILE_MP4V_CORE_STUDIO,                   ///< MPEG-4 Video Core Studio
+    PROFILE_MP4V_ADVANCED_SIMPLE,               ///< MPEG-4 Video Advanced Simple
+    PROFILE_MP4V_FGS,                           ///< MPEG-4 Video Fine Granularity Scalable
+
+    // AVC / MPEG-4 Part 10 (H.264) profiles
+    PROFILE_AVC_BASELINE = _C2_PL_AVC_BASE,     ///< AVC (H.264) Baseline
+    PROFILE_AVC_CONSTRAINED_BASELINE,           ///< AVC (H.264) Constrained Baseline
+    PROFILE_AVC_MAIN,                           ///< AVC (H.264) Main
+    PROFILE_AVC_EXTENDED,                       ///< AVC (H.264) Extended
+    PROFILE_AVC_HIGH,                           ///< AVC (H.264) High
+    PROFILE_AVC_PROGRESSIVE_HIGH,               ///< AVC (H.264) Progressive High
+    PROFILE_AVC_CONSTRAINED_HIGH,               ///< AVC (H.264) Constrained High
+    PROFILE_AVC_HIGH_10,                        ///< AVC (H.264) High 10
+    PROFILE_AVC_PROGRESSIVE_HIGH_10,            ///< AVC (H.264) Progressive High 10
+    PROFILE_AVC_HIGH_422,                       ///< AVC (H.264) High 4:2:2
+    PROFILE_AVC_HIGH_444_PREDICTIVE,            ///< AVC (H.264) High 4:4:4 Predictive
+    PROFILE_AVC_HIGH_10_INTRA,                  ///< AVC (H.264) High 10 Intra
+    PROFILE_AVC_HIGH_422_INTRA,                 ///< AVC (H.264) High 4:2:2 Intra
+    PROFILE_AVC_HIGH_444_INTRA,                 ///< AVC (H.264) High 4:4:4 Intra
+    PROFILE_AVC_CAVLC_444_INTRA,                ///< AVC (H.264) CAVLC 4:4:4 Intra
+    PROFILE_AVC_SCALABLE_BASELINE = _C2_PL_AVC_BASE + 0x100,  ///< AVC (H.264) Scalable Baseline
+    PROFILE_AVC_SCALABLE_CONSTRAINED_BASELINE,  ///< AVC (H.264) Scalable Constrained Baseline
+    PROFILE_AVC_SCALABLE_HIGH,                  ///< AVC (H.264) Scalable High
+    PROFILE_AVC_SCALABLE_CONSTRAINED_HIGH,      ///< AVC (H.264) Scalable Constrained High
+    PROFILE_AVC_SCALABLE_HIGH_INTRA,            ///< AVC (H.264) Scalable High Intra
+    PROFILE_AVC_MULTIVIEW_HIGH = _C2_PL_AVC_BASE + 0x200,  ///< AVC (H.264) Multiview High
+    PROFILE_AVC_STEREO_HIGH,                    ///< AVC (H.264) Stereo High
+    PROFILE_AVC_MFC_HIGH,                       ///< AVC (H.264) MFC High
+    PROFILE_AVC_MULTIVIEW_DEPTH_HIGH = _C2_PL_AVC_BASE + 0x300,  ///< AVC (H.264) Multiview Depth High
+    PROFILE_AVC_MFC_DEPTH_HIGH,                 ///< AVC (H.264) MFC Depth High
+    PROFILE_AVC_ENHANCED_MULTIVIEW_DEPTH_HIGH = _C2_PL_AVC_BASE + 0x400,  ///< AVC (H.264) Enhanced Multiview Depth High
+
+    // HEVC profiles
+    PROFILE_HEVC_MAIN = _C2_PL_HEVC_BASE,       ///< HEVC (H.265) Main
+    PROFILE_HEVC_MAIN_10,                       ///< HEVC (H.265) Main 10
+    PROFILE_HEVC_MAIN_STILL,                    ///< HEVC (H.265) Main Still Picture
+    PROFILE_HEVC_MONO = _C2_PL_HEVC_BASE + 0x100,  ///< HEVC (H.265) Monochrome
+    PROFILE_HEVC_MONO_12,                       ///< HEVC (H.265) Monochrome 12
+    PROFILE_HEVC_MONO_16,                       ///< HEVC (H.265) Monochrome 16
+    PROFILE_HEVC_MAIN_12,                       ///< HEVC (H.265) Main 12
+    PROFILE_HEVC_MAIN_422_10,                   ///< HEVC (H.265) Main 4:2:2 10
+    PROFILE_HEVC_MAIN_422_12,                   ///< HEVC (H.265) Main 4:2:2 12
+    PROFILE_HEVC_MAIN_444,                      ///< HEVC (H.265) Main 4:4:4
+    PROFILE_HEVC_MAIN_444_10,                   ///< HEVC (H.265) Main 4:4:4 10
+    PROFILE_HEVC_MAIN_444_12,                   ///< HEVC (H.265) Main 4:4:4 12
+    PROFILE_HEVC_MAIN_INTRA,                    ///< HEVC (H.265) Main Intra
+    PROFILE_HEVC_MAIN_10_INTRA,                 ///< HEVC (H.265) Main 10 Intra
+    PROFILE_HEVC_MAIN_12_INTRA,                 ///< HEVC (H.265) Main 12 Intra
+    PROFILE_HEVC_MAIN_422_10_INTRA,             ///< HEVC (H.265) Main 4:2:2 10 Intra
+    PROFILE_HEVC_MAIN_422_12_INTRA,             ///< HEVC (H.265) Main 4:2:2 12 Intra
+    PROFILE_HEVC_MAIN_444_INTRA,                ///< HEVC (H.265) Main 4:4:4 Intra
+    PROFILE_HEVC_MAIN_444_10_INTRA,             ///< HEVC (H.265) Main 4:4:4 10 Intra
+    PROFILE_HEVC_MAIN_444_12_INTRA,             ///< HEVC (H.265) Main 4:4:4 12 Intra
+    PROFILE_HEVC_MAIN_444_16_INTRA,             ///< HEVC (H.265) Main 4:4:4 16 Intra
+    PROFILE_HEVC_MAIN_444_STILL,                ///< HEVC (H.265) Main 4:4:4 Still Picture
+    PROFILE_HEVC_MAIN_444_16_STILL,             ///< HEVC (H.265) Main 4:4:4 16 Still Picture
+    PROFILE_HEVC_HIGH_444 = _C2_PL_HEVC_BASE + 0x200,  ///< HEVC (H.265) High Throughput 4:4:4
+    PROFILE_HEVC_HIGH_444_10,                   ///< HEVC (H.265) High Throughput 4:4:4 10
+    PROFILE_HEVC_HIGH_444_14,                   ///< HEVC (H.265) High Throughput 4:4:4 14
+    PROFILE_HEVC_HIGH_444_16_INTRA,             ///< HEVC (H.265) High Throughput 4:4:4 16 Intra
+    PROFILE_HEVC_SX_MAIN = _C2_PL_HEVC_BASE + 0x300,  ///< HEVC (H.265) Screen-Extended Main
+    PROFILE_HEVC_SX_MAIN_10,                    ///< HEVC (H.265) Screen-Extended Main 10
+    PROFILE_HEVC_SX_MAIN_444,                   ///< HEVC (H.265) Screen-Extended Main 4:4:4
+    PROFILE_HEVC_SX_MAIN_444_10,                ///< HEVC (H.265) Screen-Extended Main 4:4:4 10
+    PROFILE_HEVC_SX_HIGH_444,                   ///< HEVC (H.265) Screen-Extended High Throughput 4:4:4
+    PROFILE_HEVC_SX_HIGH_444_10,                ///< HEVC (H.265) Screen-Extended High Throughput 4:4:4 10
+    PROFILE_HEVC_SX_HIGH_444_14,                ///< HEVC (H.265) Screen-Extended High Throughput 4:4:4 14
+    PROFILE_HEVC_MULTIVIEW_MAIN = _C2_PL_HEVC_BASE + 0x400,  ///< HEVC (H.265) Multiview Main
+    PROFILE_HEVC_SCALABLE_MAIN = _C2_PL_HEVC_BASE + 0x500,  ///< HEVC (H.265) Scalable Main
+    PROFILE_HEVC_SCALABLE_MAIN_10,              ///< HEVC (H.265) Scalable Main 10
+    PROFILE_HEVC_SCALABLE_MONO = _C2_PL_HEVC_BASE + 0x600,  ///< HEVC (H.265) Scalable Monochrome
+    PROFILE_HEVC_SCALABLE_MONO_12,              ///< HEVC (H.265) Scalable Monochrome 12
+    PROFILE_HEVC_SCALABLE_MONO_16,              ///< HEVC (H.265) Scalable Monochrome 16
+    PROFILE_HEVC_SCALABLE_MAIN_444,             ///< HEVC (H.265) Scalable Main 4:4:4
+    PROFILE_HEVC_3D_MAIN = _C2_PL_HEVC_BASE + 0x700,  ///< HEVC (H.265) 3D Main
+
+    // VP9 profiles
+    PROFILE_VP9_0 = _C2_PL_VP9_BASE,            ///< VP9 Profile 0 (4:2:0)
+    PROFILE_VP9_1,                              ///< VP9 Profile 1 (4:2:2 or 4:4:4)
+    PROFILE_VP9_2,                              ///< VP9 Profile 2 (4:2:0, 10 or 12 bit)
+    PROFILE_VP9_3,                              ///< VP9 Profile 3 (4:2:2 or 4:4:4, 10 or 12 bit)
+
+    // Dolby Vision profiles
+    PROFILE_DV_AV_PER = _C2_PL_DV_BASE + 0,     ///< Dolby Vision dvav.per profile (deprecated)
+    PROFILE_DV_AV_PEN,                          ///< Dolby Vision dvav.pen profile (deprecated)
+    PROFILE_DV_HE_DER,                          ///< Dolby Vision dvhe.der profile (deprecated)
+    PROFILE_DV_HE_DEN,                          ///< Dolby Vision dvhe.den profile (deprecated)
+    PROFILE_DV_HE_04 = _C2_PL_DV_BASE + 4,      ///< Dolby Vision dvhe.04 profile
+    PROFILE_DV_HE_05 = _C2_PL_DV_BASE + 5,      ///< Dolby Vision dvhe.05 profile
+    PROFILE_DV_HE_DTH,                          ///< Dolby Vision dvhe.dth profile (deprecated)
+    PROFILE_DV_HE_07 = _C2_PL_DV_BASE + 7,      ///< Dolby Vision dvhe.07 profile
+    PROFILE_DV_HE_08 = _C2_PL_DV_BASE + 8,      ///< Dolby Vision dvhe.08 profile
+    PROFILE_DV_AV_09 = _C2_PL_DV_BASE + 9,      ///< Dolby Vision dvav.09 profile
+
+    // AV1 profiles
+    PROFILE_AV1_0 = _C2_PL_AV1_BASE,            ///< AV1 Profile 0 (4:2:0, 8 to 10 bit)
+    PROFILE_AV1_1,                              ///< AV1 Profile 1 (8 to 10 bit)
+    PROFILE_AV1_2,                              ///< AV1 Profile 2 (8 to 12 bit)
+};
+
+enum C2Config::level_t : uint32_t {
+    LEVEL_UNUSED = 0,                           ///< level is not used by this media type
+
+    // MPEG-2 Video levels
+    LEVEL_MP2V_LOW = _C2_PL_MP2V_BASE,          ///< MPEG-2 Video (H.262) Low Level
+    LEVEL_MP2V_MAIN,                            ///< MPEG-2 Video (H.262) Main Level
+    LEVEL_MP2V_HIGH_1440,                       ///< MPEG-2 Video (H.262) High 1440 Level
+    LEVEL_MP2V_HIGH,                            ///< MPEG-2 Video (H.262) High Level
+    LEVEL_MP2V_HIGHP,                           ///< MPEG-2 Video (H.262) HighP Level
+
+    // H.263 levels
+    LEVEL_H263_10 = _C2_PL_H263_BASE,           ///< H.263 Level 10
+    LEVEL_H263_20,                              ///< H.263 Level 20
+    LEVEL_H263_30,                              ///< H.263 Level 30
+    LEVEL_H263_40,                              ///< H.263 Level 40
+    LEVEL_H263_45,                              ///< H.263 Level 45
+    LEVEL_H263_50,                              ///< H.263 Level 50
+    LEVEL_H263_60,                              ///< H.263 Level 60
+    LEVEL_H263_70,                              ///< H.263 Level 70
+
+    // MPEG-4 Part 2 (Video) levels
+    LEVEL_MP4V_0 = _C2_PL_MP4V_BASE,            ///< MPEG-4 Video Level 0
+    LEVEL_MP4V_0B,                              ///< MPEG-4 Video Level 0b
+    LEVEL_MP4V_1,                               ///< MPEG-4 Video Level 1
+    LEVEL_MP4V_2,                               ///< MPEG-4 Video Level 2
+    LEVEL_MP4V_3,                               ///< MPEG-4 Video Level 3
+    LEVEL_MP4V_3B,                              ///< MPEG-4 Video Level 3b
+    LEVEL_MP4V_4,                               ///< MPEG-4 Video Level 4
+    LEVEL_MP4V_4A,                              ///< MPEG-4 Video Level 4a
+    LEVEL_MP4V_5,                               ///< MPEG-4 Video Level 5
+    LEVEL_MP4V_6,                               ///< MPEG-4 Video Level 6
+
+    // AVC / MPEG-4 Part 10 (H.264) levels
+    LEVEL_AVC_1 = _C2_PL_AVC_BASE,              ///< AVC (H.264) Level 1
+    LEVEL_AVC_1B,                               ///< AVC (H.264) Level 1b
+    LEVEL_AVC_1_1,                              ///< AVC (H.264) Level 1.1
+    LEVEL_AVC_1_2,                              ///< AVC (H.264) Level 1.2
+    LEVEL_AVC_1_3,                              ///< AVC (H.264) Level 1.3
+    LEVEL_AVC_2,                                ///< AVC (H.264) Level 2
+    LEVEL_AVC_2_1,                              ///< AVC (H.264) Level 2.1
+    LEVEL_AVC_2_2,                              ///< AVC (H.264) Level 2.2
+    LEVEL_AVC_3,                                ///< AVC (H.264) Level 3
+    LEVEL_AVC_3_1,                              ///< AVC (H.264) Level 3.1
+    LEVEL_AVC_3_2,                              ///< AVC (H.264) Level 3.2
+    LEVEL_AVC_4,                                ///< AVC (H.264) Level 4
+    LEVEL_AVC_4_1,                              ///< AVC (H.264) Level 4.1
+    LEVEL_AVC_4_2,                              ///< AVC (H.264) Level 4.2
+    LEVEL_AVC_5,                                ///< AVC (H.264) Level 5
+    LEVEL_AVC_5_1,                              ///< AVC (H.264) Level 5.1
+    LEVEL_AVC_5_2,                              ///< AVC (H.264) Level 5.2
+    LEVEL_AVC_6,                                ///< AVC (H.264) Level 6
+    LEVEL_AVC_6_1,                              ///< AVC (H.264) Level 6.1
+    LEVEL_AVC_6_2,                              ///< AVC (H.264) Level 6.2
+
+    // HEVC (H.265) tiers and levels
+    LEVEL_HEVC_MAIN_1 = _C2_PL_HEVC_BASE,       ///< HEVC (H.265) Main Tier Level 1
+    LEVEL_HEVC_MAIN_2,                          ///< HEVC (H.265) Main Tier Level 2
+    LEVEL_HEVC_MAIN_2_1,                        ///< HEVC (H.265) Main Tier Level 2.1
+    LEVEL_HEVC_MAIN_3,                          ///< HEVC (H.265) Main Tier Level 3
+    LEVEL_HEVC_MAIN_3_1,                        ///< HEVC (H.265) Main Tier Level 3.1
+    LEVEL_HEVC_MAIN_4,                          ///< HEVC (H.265) Main Tier Level 4
+    LEVEL_HEVC_MAIN_4_1,                        ///< HEVC (H.265) Main Tier Level 4.1
+    LEVEL_HEVC_MAIN_5,                          ///< HEVC (H.265) Main Tier Level 5
+    LEVEL_HEVC_MAIN_5_1,                        ///< HEVC (H.265) Main Tier Level 5.1
+    LEVEL_HEVC_MAIN_5_2,                        ///< HEVC (H.265) Main Tier Level 5.2
+    LEVEL_HEVC_MAIN_6,                          ///< HEVC (H.265) Main Tier Level 6
+    LEVEL_HEVC_MAIN_6_1,                        ///< HEVC (H.265) Main Tier Level 6.1
+    LEVEL_HEVC_MAIN_6_2,                        ///< HEVC (H.265) Main Tier Level 6.2
+
+    LEVEL_HEVC_HIGH_4 = _C2_PL_HEVC_BASE + 0x100,  ///< HEVC (H.265) High Tier Level 4
+    LEVEL_HEVC_HIGH_4_1,                        ///< HEVC (H.265) High Tier Level 4.1
+    LEVEL_HEVC_HIGH_5,                          ///< HEVC (H.265) High Tier Level 5
+    LEVEL_HEVC_HIGH_5_1,                        ///< HEVC (H.265) High Tier Level 5.1
+    LEVEL_HEVC_HIGH_5_2,                        ///< HEVC (H.265) High Tier Level 5.2
+    LEVEL_HEVC_HIGH_6,                          ///< HEVC (H.265) High Tier Level 6
+    LEVEL_HEVC_HIGH_6_1,                        ///< HEVC (H.265) High Tier Level 6.1
+    LEVEL_HEVC_HIGH_6_2,                        ///< HEVC (H.265) High Tier Level 6.2
+
+    // VP9 levels
+    LEVEL_VP9_1 = _C2_PL_VP9_BASE,              ///< VP9 Level 1
+    LEVEL_VP9_1_1,                              ///< VP9 Level 1.1
+    LEVEL_VP9_2,                                ///< VP9 Level 2
+    LEVEL_VP9_2_1,                              ///< VP9 Level 2.1
+    LEVEL_VP9_3,                                ///< VP9 Level 3
+    LEVEL_VP9_3_1,                              ///< VP9 Level 3.1
+    LEVEL_VP9_4,                                ///< VP9 Level 4
+    LEVEL_VP9_4_1,                              ///< VP9 Level 4.1
+    LEVEL_VP9_5,                                ///< VP9 Level 5
+    LEVEL_VP9_5_1,                              ///< VP9 Level 5.1
+    LEVEL_VP9_5_2,                              ///< VP9 Level 5.2
+    LEVEL_VP9_6,                                ///< VP9 Level 6
+    LEVEL_VP9_6_1,                              ///< VP9 Level 6.1
+    LEVEL_VP9_6_2,                              ///< VP9 Level 6.2
+
+    // Dolby Vision levels
+    LEVEL_DV_MAIN_HD_24 = _C2_PL_DV_BASE,       ///< Dolby Vision main tier hd24
+    LEVEL_DV_MAIN_HD_30,                        ///< Dolby Vision main tier hd30
+    LEVEL_DV_MAIN_FHD_24,                       ///< Dolby Vision main tier fhd24
+    LEVEL_DV_MAIN_FHD_30,                       ///< Dolby Vision main tier fhd30
+    LEVEL_DV_MAIN_FHD_60,                       ///< Dolby Vision main tier fhd60
+    LEVEL_DV_MAIN_UHD_24,                       ///< Dolby Vision main tier uhd24
+    LEVEL_DV_MAIN_UHD_30,                       ///< Dolby Vision main tier uhd30
+    LEVEL_DV_MAIN_UHD_48,                       ///< Dolby Vision main tier uhd48
+    LEVEL_DV_MAIN_UHD_60,                       ///< Dolby Vision main tier uhd60
+
+    LEVEL_DV_HIGH_HD_24 = _C2_PL_DV_BASE + 0x100,  ///< Dolby Vision high tier hd24
+    LEVEL_DV_HIGH_HD_30,                        ///< Dolby Vision high tier hd30
+    LEVEL_DV_HIGH_FHD_24,                       ///< Dolby Vision high tier fhd24
+    LEVEL_DV_HIGH_FHD_30,                       ///< Dolby Vision high tier fhd30
+    LEVEL_DV_HIGH_FHD_60,                       ///< Dolby Vision high tier fhd60
+    LEVEL_DV_HIGH_UHD_24,                       ///< Dolby Vision high tier uhd24
+    LEVEL_DV_HIGH_UHD_30,                       ///< Dolby Vision high tier uhd30
+    LEVEL_DV_HIGH_UHD_48,                       ///< Dolby Vision high tier uhd48
+    LEVEL_DV_HIGH_UHD_60,                       ///< Dolby Vision high tier uhd60
+
+    // AV1 levels
+    LEVEL_AV1_2    = _C2_PL_AV1_BASE ,          ///< AV1 Level 2
+    LEVEL_AV1_2_1,                              ///< AV1 Level 2.1
+    LEVEL_AV1_2_2,                              ///< AV1 Level 2.2
+    LEVEL_AV1_2_3,                              ///< AV1 Level 2.3
+    LEVEL_AV1_3,                                ///< AV1 Level 3
+    LEVEL_AV1_3_1,                              ///< AV1 Level 3.1
+    LEVEL_AV1_3_2,                              ///< AV1 Level 3.2
+    LEVEL_AV1_3_3,                              ///< AV1 Level 3.3
+    LEVEL_AV1_4,                                ///< AV1 Level 4
+    LEVEL_AV1_4_1,                              ///< AV1 Level 4.1
+    LEVEL_AV1_4_2,                              ///< AV1 Level 4.2
+    LEVEL_AV1_4_3,                              ///< AV1 Level 4.3
+    LEVEL_AV1_5,                                ///< AV1 Level 5
+    LEVEL_AV1_5_1,                              ///< AV1 Level 5.1
+    LEVEL_AV1_5_2,                              ///< AV1 Level 5.2
+    LEVEL_AV1_5_3,                              ///< AV1 Level 5.3
+    LEVEL_AV1_6,                                ///< AV1 Level 6
+    LEVEL_AV1_6_1,                              ///< AV1 Level 6.1
+    LEVEL_AV1_6_2,                              ///< AV1 Level 6.2
+    LEVEL_AV1_6_3,                              ///< AV1 Level 6.3
+    LEVEL_AV1_7,                                ///< AV1 Level 7
+    LEVEL_AV1_7_1,                              ///< AV1 Level 7.1
+    LEVEL_AV1_7_2,                              ///< AV1 Level 7.2
+    LEVEL_AV1_7_3,                              ///< AV1 Level 7.3
+};
+
+struct C2ProfileLevelStruct {
+    C2Config::profile_t profile;  ///< coding profile
+    C2Config::level_t   level;    ///< coding level
+
+    C2ProfileLevelStruct(
+            C2Config::profile_t profile_ = C2Config::PROFILE_UNUSED,
+            C2Config::level_t level_ = C2Config::LEVEL_UNUSED)
+        : profile(profile_), level(level_) { }
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(ProfileLevel)
+    C2FIELD(profile, "profile")
+    C2FIELD(level,   "level")
+};
+
+// TODO: may need to make this explicit (have .set member)
+typedef C2StreamParam<C2Info, C2ProfileLevelStruct, kParamIndexProfileLevel>
+        C2StreamProfileLevelInfo;
+constexpr char C2_PARAMKEY_PROFILE_LEVEL[] = "coded.pl";
+
+/**
+ * Codec-specific initialization data.
+ *
+ * This is initialization data for the codec.
+ *
+ * For AVC/HEVC, these are the concatenated SPS/PPS/VPS NALs.
+ *
+ * TODO: define for other codecs.
+ */
+typedef C2StreamParam<C2Info, C2BlobValue, kParamIndexInitData> C2StreamInitDataInfo;
+constexpr char C2_PARAMKEY_INIT_DATA[] = "coded.init-data";
+
+/**
+ * Supplemental Data.
+ *
+ * This is coding-specific supplemental informational data, e.g. SEI for AVC/HEVC.
+ * This structure is not a configuration so it does not have a parameter key.
+ * This structure shall be returned in the configuration update, and can be repeated as needed
+ * in the same update.
+ */
+C2ENUM(C2Config::supplemental_info_t, uint32_t,
+    INFO_NONE = 0,
+
+    INFO_PREFIX_SEI_UNIT = 0x10000, ///< prefix SEI payload types add this flag
+    INFO_SUFFIX_SEI_UNIT = 0x20000, ///< suffix SEI payload types add this flag
+
+    INFO_SEI_USER_DATA = INFO_PREFIX_SEI_UNIT | 4,    ///< closed-captioning data (ITU-T T35)
+    INFO_SEI_MDCV      = INFO_PREFIX_SEI_UNIT | 137,  ///< mastering display color volume
+    INFO_SET_USER_DATA_SFX = INFO_SUFFIX_SEI_UNIT | 4, ///< closed-captioning data (ITU-T T35)
+
+    INFO_VENDOR_START = 0x70000000
+)
+
+struct C2SupplementalDataStruct {
+    C2SupplementalDataStruct()
+        : type_(INFO_NONE) { }
+
+    C2SupplementalDataStruct(
+            size_t flexCount, C2Config::supplemental_info_t type, std::vector<uint8_t> data_)
+        : type_(type) {
+            memcpy(data, &data_[0], c2_min(data_.size(), flexCount));
+    }
+
+    C2Config::supplemental_info_t type_;
+    uint8_t data[];
+
+    DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(SupplementalData, data)
+    C2FIELD(type_, "type")
+    C2FIELD(data, "data")
+};
+typedef C2StreamParam<C2Info, C2SupplementalDataStruct, kParamIndexSupplementalData>
+        C2StreamSupplementalDataInfo;
+
+/**
+ * Supplemental Data Subscription
+ */
+typedef C2StreamParam<C2Tuning, C2SimpleArrayStruct<C2Config::supplemental_info_t>,
+                kParamIndexSubscribedSupplementalData>
+        C2StreamSubscribedSupplementalDataTuning;
+constexpr char C2_PARAMKEY_SUBSCRIBED_SUPPLEMENTAL_DATA[] = "output.subscribed-supplemental";
+
+/* ---------------------------------- pipeline characteristics ---------------------------------- */
+
+/**
+ * Media-type.
+ *
+ * This is defined for both port and stream, but stream media type may be a subtype of the
+ * port media type.
+ */
+typedef C2PortParam<C2Setting, C2StringValue, kParamIndexMediaType> C2PortMediaTypeSetting;
+constexpr char C2_PARAMKEY_INPUT_MEDIA_TYPE[] = "input.media-type";
+constexpr char C2_PARAMKEY_OUTPUT_MEDIA_TYPE[] = "output.media-type";
+
+typedef C2StreamParam<C2Setting, C2StringValue, kParamIndexMediaType> C2StreamMediaTypeSetting;
+
+/**
+ * Pipeline delays.
+ *
+ * Input delay is the number of additional input frames requested by the component to process
+ * an input frame.
+ *
+ * Output delay is the number of additional output frames that need to be generated before an
+ * output can be released by the component.
+ *
+ * Pipeline delay is the number of additional frames that are processed at one time by the
+ * component.
+ *
+ * As these may vary from frame to frame, the number is the maximum required value. E.g. if
+ * input delay is 0, the component is expected to consume each frame queued even if no further
+ * frames are queued. Similarly, if input delay is 1, as long as there are always exactly 2
+ * outstanding input frames queued to the component, it shall produce output.
+ */
+
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexDelay | C2Param::CoreIndex::IS_REQUEST_FLAG>
+        C2PortRequestedDelayTuning;
+constexpr char C2_PARAMKEY_INPUT_DELAY_REQUEST[] = "input.delay"; // deprecated
+constexpr char C2_PARAMKEY_OUTPUT_DELAY_REQUEST[] = "output.delay"; // deprecated
+
+typedef C2GlobalParam<C2Tuning, C2Uint32Value,
+                kParamIndexDelay | C2Param::CoreIndex::IS_REQUEST_FLAG>
+        C2RequestedPipelineDelayTuning;
+constexpr char C2_PARAMKEY_PIPELINE_DELAY_REQUEST[] = "algo.delay"; // deprecated
+
+// read-only
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexDelay> C2PortDelayTuning;
+typedef C2PortDelayTuning C2PortActualDelayTuning; // deprecated
+constexpr char C2_PARAMKEY_INPUT_DELAY[] = "input.delay";
+constexpr char C2_PARAMKEY_OUTPUT_DELAY[] = "output.delay";
+
+// read-only
+typedef C2GlobalParam<C2Tuning, C2Uint32Value, kParamIndexDelay> C2PipelineDelayTuning;
+typedef C2PipelineDelayTuning C2ActualPipelineDelayTuning; // deprecated
+constexpr char C2_PARAMKEY_PIPELINE_DELAY[] = "algo.delay";
+
+/**
+ * Reference characteristics.
+ *
+ * The component may hold onto input and output buffers even after completing the corresponding
+ * work item.
+ *
+ * Max reference age is the longest number of additional frame processing that a component may
+ * hold onto a buffer for. Max reference count is the number of buffers that a component may
+ * hold onto at the same time at the worst case. These numbers assume single frame per buffers.
+ *
+ * Use max-uint32 if there is no limit for the max age or count.
+ */
+typedef C2StreamParam<C2Tuning, C2Uint32Value, kParamIndexMaxReferenceAge>
+        C2StreamMaxReferenceAgeTuning;
+constexpr char C2_PARAMKEY_INPUT_MAX_REFERENCE_AGE[] = "input.reference.max-age";
+constexpr char C2_PARAMKEY_OUTPUT_MAX_REFERENCE_AGE[] = "output.reference.max-age";
+
+typedef C2StreamParam<C2Tuning, C2Uint32Value, kParamIndexMaxReferenceCount>
+        C2StreamMaxReferenceCountTuning;
+constexpr char C2_PARAMKEY_INPUT_MAX_REFERENCE_COUNT[] = "input.reference.max-count";
+constexpr char C2_PARAMKEY_OUTPUT_MAX_REFERENCE_COUNT[] = "output.reference.max-count";
+
+/**
+ * Output reordering.
+ *
+ * The size of the window to use for output buffer reordering. 0 is interpreted as 1.
+ */
+// output only
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexReorderBufferDepth>
+        C2PortReorderBufferDepthTuning;
+constexpr char C2_PARAMKEY_OUTPUT_REORDER_DEPTH[] = "output.reorder.depth";
+
+C2ENUM(C2Config::ordinal_key_t, uint32_t,
+        ORDINAL,
+        TIMESTAMP,
+        CUSTOM)
+
+// read-only, output only
+typedef C2PortParam<C2Setting, C2SimpleValueStruct<C2Config::ordinal_key_t>, kParamIndexReorderKey>
+        C2PortReorderKeySetting;
+constexpr char C2_PARAMKEY_OUTPUT_REORDER_KEY[] = "output.reorder.key";
+
+/**
+ * Stream count.
+ */
+// private
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexStreamCount> C2PortStreamCountTuning;
+constexpr char C2_PARAMKEY_INPUT_STREAM_COUNT[] = "input.stream-count";
+constexpr char C2_PARAMKEY_OUTPUT_STREAM_COUNT[] = "output.stream-count";
+
+/**
+ * Config update subscription.
+ */
+// private
+typedef C2GlobalParam<C2Tuning, C2Uint32Array, kParamIndexSubscribedParamIndices>
+        C2SubscribedParamIndicesTuning;
+constexpr char C2_PARAMKEY_SUBSCRIBED_PARAM_INDICES[] = "output.subscribed-indices";
+
+/**
+ * Suggested buffer (C2Frame) count. This is a suggestion by the component for the number of
+ * input and output frames allocated for the component's use in the buffer pools.
+ *
+ * Component shall set the acceptable range of buffers allocated for it. E.g. client shall
+ * allocate at least the minimum required value.
+ */
+// read-only
+typedef C2PortParam<C2Tuning, C2Uint64Array, kParamIndexSuggestedBufferCount>
+        C2PortSuggestedBufferCountTuning;
+constexpr char C2_PARAMKEY_INPUT_SUGGESTED_BUFFER_COUNT[] = "input.buffers.pool-size";
+constexpr char C2_PARAMKEY_OUTPUT_SUGGESTED_BUFFER_COUNT[] = "output.buffers.pool-size";
+
+/**
+ * Input/output batching.
+ *
+ * For input, component requests that client batches work in batches of specified size. For output,
+ * client requests that the component batches work completion in given batch size.
+ * Value 0 means don't care.
+ */
+typedef C2PortParam<C2Tuning, C2Uint64Array, kParamIndexBatchSize> C2PortBatchSizeTuning;
+constexpr char C2_PARAMKEY_INPUT_BATCH_SIZE[] = "input.buffers.batch-size";
+constexpr char C2_PARAMKEY_OUTPUT_BATCH_SIZE[] = "output.buffers.batch-size";
+
+/**
+ * Current & last work ordinals.
+ *
+ * input port: last work queued to component.
+ * output port: last work completed by component.
+ * global: current work.
+ */
+typedef C2PortParam<C2Tuning, C2WorkOrdinalStruct, kParamIndexLastWorkQueued> C2LastWorkQueuedTuning;
+typedef C2GlobalParam<C2Tuning, C2WorkOrdinalStruct, kParamIndexCurrentWork> C2CurrentWorkTuning;
+
+
+/* ------------------------------------- memory allocation ------------------------------------- */
+
+/**
+ * Allocators to use.
+ *
+ * These are requested by the component.
+ *
+ * If none specified, client will use the default allocator ID based on the component domain and
+ * kind.
+ */
+typedef C2PortParam<C2Tuning, C2SimpleArrayStruct<C2Allocator::id_t>, kParamIndexAllocators>
+        C2PortAllocatorsTuning;
+constexpr char C2_PARAMKEY_INPUT_ALLOCATORS[] = "input.buffers.allocator-ids";
+constexpr char C2_PARAMKEY_OUTPUT_ALLOCATORS[] = "output.buffers.allocator-ids";
+
+typedef C2GlobalParam<C2Tuning, C2SimpleArrayStruct<C2Allocator::id_t>, kParamIndexAllocators>
+        C2PrivateAllocatorsTuning;
+constexpr char C2_PARAMKEY_PRIVATE_ALLOCATORS[] = "algo.buffers.allocator-ids";
+
+/**
+ * Allocator to use for outputting to surface.
+ *
+ * Components can optionally request allocator type for outputting to surface.
+ *
+ * If none specified, client will use the default BufferQueue-backed allocator ID for outputting to
+ * surface.
+ */
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexSurfaceAllocator>
+        C2PortSurfaceAllocatorTuning;
+constexpr char C2_PARAMKEY_OUTPUT_SURFACE_ALLOCATOR[] = "output.buffers.surface-allocator-id";
+
+/**
+ * Block pools to use.
+ *
+ * These are allocated by the client for the component using the allocator IDs specified by the
+ * component. This is not used for the input port.
+ */
+typedef C2PortParam<C2Tuning, C2SimpleArrayStruct<C2BlockPool::local_id_t>, kParamIndexBlockPools>
+        C2PortBlockPoolsTuning;
+constexpr char C2_PARAMKEY_OUTPUT_BLOCK_POOLS[] = "output.buffers.pool-ids";
+
+typedef C2GlobalParam<C2Tuning, C2SimpleArrayStruct<C2BlockPool::local_id_t>, kParamIndexBlockPools>
+        C2PrivateBlockPoolsTuning;
+constexpr char C2_PARAMKEY_PRIVATE_BLOCK_POOLS[] = "algo.buffers.pool-ids";
+
+/**
+ * The max number of private allocations at any one time by the component.
+ * (This is an array with a corresponding value for each private allocator)
+ */
+typedef C2GlobalParam<C2Tuning, C2Uint32Array, kParamIndexMaxReferenceCount>
+        C2MaxPrivateBufferCountTuning;
+constexpr char C2_PARAMKEY_MAX_PRIVATE_BUFFER_COUNT[] = "algo.buffers.max-count";
+
+/**
+ * Buffer type
+ *
+ * This is provided by the component for the client to allocate the proper buffer type for the
+ * input port, and can be provided by the client to control the buffer type for the output.
+ */
+// private
+typedef C2StreamParam<C2Setting, C2SimpleValueStruct<C2EasyEnum<C2BufferData::type_t>>,
+                kParamIndexBufferType>
+        C2StreamBufferTypeSetting;
+constexpr char C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE[] = "input.buffers.type";
+constexpr char C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE[] = "output.buffers.type";
+
+/**
+ * Memory usage.
+ *
+ * Suggested by component for input and negotiated between client and component for output.
+ */
+typedef C2StreamParam<C2Tuning, C2Uint64Value, kParamIndexUsage> C2StreamUsageTuning;
+constexpr char C2_PARAMKEY_INPUT_STREAM_USAGE[] = "input.buffers.usage";
+constexpr char C2_PARAMKEY_OUTPUT_STREAM_USAGE[] = "output.buffers.usage";
+
+/**
+ * Picture (video or image frame) size.
+ */
+struct C2PictureSizeStruct {
+    inline C2PictureSizeStruct()
+        : width(0), height(0) { }
+
+    inline C2PictureSizeStruct(uint32_t width_, uint32_t height_)
+        : width(width_), height(height_) { }
+
+    uint32_t width;     ///< video width
+    uint32_t height;    ///< video height
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(PictureSize)
+    C2FIELD(width, "width")
+    C2FIELD(height, "height")
+};
+
+/**
+ * Out of memory signaling
+ *
+ * This is a configuration for the client to mark that it cannot allocate necessary private and/
+ * or output buffers to continue operation, and to signal the failing configuration.
+ */
+struct C2OutOfMemoryStruct {
+    C2BlockPool::local_id_t pool;   ///< pool ID that failed the allocation
+    uint64_t usage;                 ///< memory usage used
+    C2PictureSizeStruct planar;     ///< buffer dimensions to be allocated if 2D
+    uint32_t format;                ///< pixel format to be used if 2D
+    uint32_t capacity;              ///< buffer capacity to be allocated if 1D
+    c2_bool_t outOfMemory;           ///< true if component is out of memory
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(OutOfMemory)
+    C2FIELD(pool, "pool")
+    C2FIELD(usage, "usage")
+    C2FIELD(planar, "planar")
+    C2FIELD(format, "format")
+    C2FIELD(capacity, "capacity")
+    C2FIELD(outOfMemory, "out-of-memory")
+};
+
+typedef C2GlobalParam<C2Tuning, C2OutOfMemoryStruct, kParamIndexOutOfMemory> C2OutOfMemoryTuning;
+constexpr char C2_PARAMKEY_OUT_OF_MEMORY[] = "algo.oom";
+
+/**
+ * Max buffer size
+ *
+ * This is a hint provided by the component for the maximum buffer size expected on a stream for the
+ * current configuration on its input and output streams. This is communicated to clients so they
+ * can preallocate input buffers, or configure downstream components that require a maximum size on
+ * their buffers.
+ *
+ * Read-only. Required to be provided by components on all compressed streams.
+ */
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexMaxBufferSize> C2StreamMaxBufferSizeInfo;
+constexpr char C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE[] = "input.buffers.max-size";
+constexpr char C2_PARAMKEY_OUTPUT_MAX_BUFFER_SIZE[] = "output.buffers.max-size";
+
+/* ---------------------------------------- misc. state ---------------------------------------- */
+
+/**
+ * Tripped state,
+ *
+ * This state exists to be able to provide reasoning for a tripped state during normal
+ * interface operations, as well as to allow client to trip the component on demand.
+ */
+typedef C2GlobalParam<C2Tuning, C2BoolValue, kParamIndexTripped>
+        C2TrippedTuning;
+constexpr char C2_PARAMKEY_TRIPPED[] = "algo.tripped";
+
+/**
+ * Configuration counters.
+ *
+ * Configurations are tracked using three counters. The input counter is incremented exactly
+ * once with each work accepted by the component. The output counter is incremented exactly
+ * once with each work completed by the component (in the order of work completion). The
+ * global counter is incremented exactly once during to each config() call. These counters
+ * shall be read-only.
+ *
+ * TODO: these should be counters.
+ */
+typedef C2PortParam<C2Tuning, C2Uint64Value, kParamIndexConfigCounter> C2PortConfigCounterTuning;
+typedef C2GlobalParam<C2Tuning, C2Uint64Value, kParamIndexConfigCounter> C2ConfigCounterTuning;
+constexpr char C2_PARAMKEY_INPUT_COUNTER[] = "input.buffers.counter";
+constexpr char C2_PARAMKEY_OUTPUT_COUNTER[] = "output.buffers.counter";
+constexpr char C2_PARAMKEY_CONFIG_COUNTER[] = "algo.config.counter";
+
+/* ----------------------------------------- resources ----------------------------------------- */
+
+/**
+ * Resources needed and resources reserved for current configuration.
+ *
+ * Resources are tracked as a vector of positive numbers. Available resources are defined by
+ * the vendor.
+ *
+ * By default, no resources are reserved for a component. If resource reservation is successful,
+ * the component shall be able to use those resources exclusively. If however, the component is
+ * not using all of the reserved resources, those may be shared with other components.
+ *
+ * TODO: define some of the resources.
+ */
+typedef C2GlobalParam<C2Tuning, C2Uint64Array, kParamIndexResourcesNeeded> C2ResourcesNeededTuning;
+typedef C2GlobalParam<C2Tuning, C2Uint64Array, kParamIndexResourcesReserved>
+        C2ResourcesReservedTuning;
+constexpr char C2_PARAMKEY_RESOURCES_NEEDED[] = "resources.needed";
+constexpr char C2_PARAMKEY_RESOURCES_RESERVED[] = "resources.reserved";
+
+/**
+ * Operating rate.
+ *
+ * Operating rate is the expected rate of work through the component. Negative values is
+ * invalid.
+ *
+ * TODO: this could distinguish set value
+ */
+typedef C2GlobalParam<C2Tuning, C2FloatValue, kParamIndexOperatingRate> C2OperatingRateTuning;
+constexpr char C2_PARAMKEY_OPERATING_RATE[] = "algo.rate";
+
+/**
+ * Realtime / operating point.
+ *
+ * Priority value defines the operating point for the component. Operating points are defined by
+ * the vendor. Priority value of 0 means that the client requires operation at the given operating
+ * rate. Priority values -1 and below define operating points in decreasing performance. In this
+ * case client expects best effort without exceeding the specific operating point. This allows
+ * client to run components deeper in the background by using larger priority values. In these
+ * cases operating rate is a hint for the maximum rate that the client anticipates.
+ *
+ * Operating rate and priority are used in tandem. E.g. if there are components that run at a
+ * higher operating point (priority) it will make more resources available for components at
+ * a lower operating point, so operating rate can be used to gate those components.
+ *
+ * Positive priority values are not defined at the moment and shall be treated equivalent to 0.
+ */
+typedef C2GlobalParam<C2Tuning, C2Int32Value, kParamIndexRealTimePriority>
+        C2RealTimePriorityTuning;
+constexpr char C2_PARAMKEY_PRIORITY[] = "algo.priority";
+
+/* ------------------------------------- protected content ------------------------------------- */
+
+/**
+ * Secure mode.
+ */
+C2ENUM(C2Config::secure_mode_t, uint32_t,
+    SM_UNPROTECTED,    ///< no content protection
+    SM_READ_PROTECTED, ///< input and output buffers shall be protected from reading
+)
+
+typedef C2GlobalParam<C2Tuning, C2SimpleValueStruct<C2Config::secure_mode_t>, kParamIndexSecureMode>
+        C2SecureModeTuning;
+constexpr char C2_PARAMKEY_SECURE_MODE[] = "algo.secure-mode";
+
+/* ===================================== ENCODER COMPONENTS ===================================== */
+
+/**
+ * Bitrate
+ */
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexBitrate> C2StreamBitrateInfo;
+constexpr char C2_PARAMKEY_BITRATE[] = "coded.bitrate";
+
+/**
+ * Bitrate mode.
+ *
+ * TODO: refine this with bitrate ranges and suggested window
+ */
+C2ENUM(C2Config::bitrate_mode_t, uint32_t,
+    BITRATE_CONST_SKIP_ALLOWED = 0,      ///< constant bitrate, frame skipping allowed
+    BITRATE_CONST = 1,                   ///< constant bitrate, keep all frames
+    BITRATE_VARIABLE_SKIP_ALLOWED = 2,   ///< bitrate can vary, frame skipping allowed
+    BITRATE_VARIABLE = 3,                ///< bitrate can vary, keep all frames
+    BITRATE_IGNORE = 7,                  ///< bitrate can be exceeded at will to achieve
+                                         ///< quality or other settings
+
+    // bitrate modes are composed of the following flags
+    BITRATE_FLAG_KEEP_ALL_FRAMES = 1,
+    BITRATE_FLAG_CAN_VARY = 2,
+    BITRATE_FLAG_CAN_EXCEED = 4,
+)
+
+typedef C2StreamParam<C2Tuning, C2SimpleValueStruct<C2Config::bitrate_mode_t>,
+                kParamIndexBitrateMode>
+        C2StreamBitrateModeTuning;
+constexpr char C2_PARAMKEY_BITRATE_MODE[] = "algo.bitrate-mode";
+
+/**
+ * Quality.
+ *
+ * This is defined by each component, the higher the better the output quality at the expense of
+ * less compression efficiency. This setting is defined for the output streams in case the
+ * component can support varying quality on each stream, or as an output port tuning in case the
+ * quality is global to all streams.
+ */
+typedef C2StreamParam<C2Tuning, C2Uint32Value, kParamIndexQuality> C2StreamQualityTuning;
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexQuality> C2QualityTuning;
+constexpr char C2_PARAMKEY_QUALITY[] = "algo.quality";
+
+/**
+ * Complexity.
+ *
+ * This is defined by each component, this higher the value, the more resources the component
+ * will use to produce better quality at the same compression efficiency or better compression
+ * efficiency at the same quality. This setting is defined for the output streams in case the
+ * component can support varying complexity on each stream, or as an output port tuning in case the
+ * quality is global to all streams
+ */
+typedef C2StreamParam<C2Tuning, C2Uint32Value, kParamIndexComplexity> C2StreamComplexityTuning;
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexComplexity> C2ComplexityTuning;
+constexpr char C2_PARAMKEY_COMPLEXITY[] = "algo.complexity";
+
+/**
+ * Header (init-data) handling around sync frames.
+ */
+C2ENUM(C2Config::prepend_header_mode_t, uint32_t,
+    /**
+     * don't prepend header. Signal header only through C2StreamInitDataInfo.
+     */
+    PREPEND_HEADER_TO_NONE,
+
+    /**
+     * prepend header before the first output frame and thereafter before the next sync frame
+     * if it changes.
+     */
+    PREPEND_HEADER_ON_CHANGE,
+
+    /**
+     * prepend header before every sync frame.
+     */
+    PREPEND_HEADER_TO_ALL_SYNC,
+)
+
+typedef C2GlobalParam<C2Setting, C2SimpleValueStruct<C2Config::prepend_header_mode_t>,
+                kParamIndexPrependHeaderMode>
+        C2PrependHeaderModeSetting;
+constexpr char C2_PARAMKEY_PREPEND_HEADER_MODE[] = "output.buffers.prepend-header";
+
+/* =================================== IMAGE/VIDEO COMPONENTS =================================== */
+
+/*
+ * Order of transformation is:
+ *
+ * crop => (scaling => scaled-crop) => sample-aspect-ratio => flip => rotation
+ */
+
+/**
+ * Picture (image- and video frame) size.
+ *
+ * This is used for the output of the video decoder, and the input of the video encoder.
+ */
+typedef C2StreamParam<C2Info, C2PictureSizeStruct, kParamIndexPictureSize> C2StreamPictureSizeInfo;
+constexpr char C2_PARAMKEY_PICTURE_SIZE[] = "raw.size";
+
+/**
+ * Crop rectangle.
+ */
+struct C2RectStruct : C2Rect {
+    C2RectStruct() = default;
+    C2RectStruct(const C2Rect &rect) : C2Rect(rect) { }
+
+    bool operator==(const C2RectStruct &) = delete;
+    bool operator!=(const C2RectStruct &) = delete;
+
+    DEFINE_AND_DESCRIBE_BASE_C2STRUCT(Rect)
+    C2FIELD(width, "width")
+    C2FIELD(height, "height")
+    C2FIELD(left, "left")
+    C2FIELD(top, "top")
+};
+
+typedef C2StreamParam<C2Info, C2RectStruct, kParamIndexCropRect> C2StreamCropRectInfo;
+constexpr char C2_PARAMKEY_CROP_RECT[] = "raw.crop";
+constexpr char C2_PARAMKEY_CODED_CROP_RECT[] = "coded.crop";
+
+/**
+ * Pixel format.
+ */
+// TODO: define some
+
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexPixelFormat> C2StreamPixelFormatInfo;
+constexpr char C2_PARAMKEY_PIXEL_FORMAT[] = "raw.pixel-format";
+
+/**
+ * Extended rotation information also incorporating a flip.
+ *
+ * Rotation is counter clock-wise.
+ */
+struct C2RotationStruct {
+    C2RotationStruct(int32_t rotation = 0)
+        : flip(0), value(rotation) { }
+
+    int32_t flip;   ///< horizontal flip (left-right flip applied prior to rotation)
+    int32_t value;  ///< rotation in degrees counter clockwise
+
+    DEFINE_AND_DESCRIBE_BASE_C2STRUCT(Rotation)
+    C2FIELD(flip, "flip")
+    C2FIELD(value, "value")
+};
+
+typedef C2StreamParam<C2Info, C2RotationStruct, kParamIndexRotation> C2StreamRotationInfo;
+constexpr char C2_PARAMKEY_ROTATION[] = "raw.rotation";
+constexpr char C2_PARAMKEY_VUI_ROTATION[] = "coded.vui.rotation";
+
+/**
+ * Pixel (sample) aspect ratio.
+ */
+typedef C2StreamParam<C2Info, C2PictureSizeStruct, kParamIndexPixelAspectRatio>
+        C2StreamPixelAspectRatioInfo;
+constexpr char C2_PARAMKEY_PIXEL_ASPECT_RATIO[] = "raw.sar";
+constexpr char C2_PARAMKEY_VUI_PIXEL_ASPECT_RATIO[] = "coded.vui.sar";
+
+/**
+ * In-line scaling.
+ *
+ * Components can optionally support scaling of raw image/video frames.  Or scaling only a
+ * portion of raw image/video frames (scaled-crop).
+ */
+
+C2ENUM(C2Config::scaling_method_t, uint32_t,
+    SCALING_ARBITRARY,   ///< arbitrary, unspecified
+)
+
+typedef C2StreamParam<C2Tuning, C2SimpleValueStruct<C2Config::scaling_method_t>,
+                kParamIndexScalingMethod>
+        C2StreamScalingMethodTuning;
+constexpr char C2_PARAMKEY_SCALING_MODE[] = "raw.scaling-method";
+
+typedef C2StreamParam<C2Tuning, C2PictureSizeStruct, kParamIndexScaledPictureSize>
+        C2StreamScaledPictureSizeTuning;
+constexpr char C2_PARAMKEY_SCALED_PICTURE_SIZE[] = "raw.scaled-size";
+
+typedef C2StreamParam<C2Tuning, C2RectStruct, kParamIndexScaledCropRect>
+        C2StreamScaledCropRectTuning;
+constexpr char C2_PARAMKEY_SCALED_CROP_RECT[] = "raw.scaled-crop";
+
+/* ------------------------------------- color information ------------------------------------- */
+
+/**
+ * Color Info
+ *
+ * Chroma location can vary for top and bottom fields, so use an array, that can have 0 to 2
+ * values. Empty array is used for non YUV formats.
+ */
+
+struct C2Color {
+    enum matrix_t : uint32_t;  ///< matrix coefficient (YUV <=> RGB)
+    enum plane_layout_t : uint32_t;  ///< plane layout for flexible formats
+    enum primaries_t : uint32_t;  ///< color primaries and white point
+    enum range_t : uint32_t;  ///< range of color component values
+    enum subsampling_t : uint32_t;  ///< chroma subsampling
+    enum transfer_t : uint32_t;  ///< transfer function
+};
+
+/// Chroma subsampling
+C2ENUM(C2Color::subsampling_t, uint32_t,
+    MONOCHROME,     ///< there are no Cr nor Cb planes
+    MONOCHROME_ALPHA, ///< there are no Cr nor Cb planes, but there is an alpha plane
+    RGB,            ///< RGB
+    RGBA,           ///< RGBA
+    YUV_420,        ///< Cr and Cb planes are subsampled by 2 both horizontally and vertically
+    YUV_422,        ///< Cr and Cb planes are subsampled horizontally
+    YUV_444,        ///< Cr and Cb planes are not subsampled
+    YUVA_444,       ///< Cr and Cb planes are not subsampled, there is an alpha plane
+)
+
+struct C2ChromaOffsetStruct {
+    // chroma offsets defined by ITU
+    constexpr static C2ChromaOffsetStruct ITU_YUV_444() { return { 0.0f, 0.0f }; }
+    constexpr static C2ChromaOffsetStruct ITU_YUV_422() { return { 0.0f, 0.0f }; }
+    constexpr static C2ChromaOffsetStruct ITU_YUV_420_0() { return { 0.0f, 0.5f }; }
+    constexpr static C2ChromaOffsetStruct ITU_YUV_420_1() { return { 0.5f, 0.5f }; }
+    constexpr static C2ChromaOffsetStruct ITU_YUV_420_2() { return { 0.0f, 0.0f }; }
+    constexpr static C2ChromaOffsetStruct ITU_YUV_420_3() { return { 0.5f, 0.0f }; }
+    constexpr static C2ChromaOffsetStruct ITU_YUV_420_4() { return { 0.0f, 1.0f }; }
+    constexpr static C2ChromaOffsetStruct ITU_YUV_420_5() { return { 0.5f, 1.0f }; }
+
+    float x;    ///< x offset in pixels (towards right)
+    float y;    ///< y offset in pixels (towards down)
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(ChromaOffset)
+    C2FIELD(x, "x")
+    C2FIELD(y, "y")
+};
+
+struct C2ColorInfoStruct {
+    C2ColorInfoStruct()
+        : bitDepth(8), subsampling(C2Color::YUV_420) { }
+
+    uint32_t bitDepth;
+    C2Color::subsampling_t subsampling;
+    C2ChromaOffsetStruct locations[]; // max 2 elements
+
+    C2ColorInfoStruct(
+            size_t /* flexCount */, uint32_t bitDepth_, C2Color::subsampling_t subsampling_)
+        : bitDepth(bitDepth_), subsampling(subsampling_) { }
+
+    C2ColorInfoStruct(
+            size_t flexCount, uint32_t bitDepth_, C2Color::subsampling_t subsampling_,
+            std::initializer_list<C2ChromaOffsetStruct> locations_)
+        : bitDepth(bitDepth_), subsampling(subsampling_) {
+        size_t ix = 0;
+        for (const C2ChromaOffsetStruct &location : locations_) {
+            if (ix == flexCount) {
+                break;
+            }
+            locations[ix] = location;
+            ++ix;
+        }
+    }
+
+    DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(ColorInfo, locations)
+    C2FIELD(bitDepth, "bit-depth")
+    C2FIELD(subsampling, "subsampling")
+    C2FIELD(locations, "locations")
+};
+
+typedef C2StreamParam<C2Info, C2ColorInfoStruct, kParamIndexColorInfo> C2StreamColorInfo;
+constexpr char C2_PARAMKEY_COLOR_INFO[] = "raw.color-format";
+constexpr char C2_PARAMKEY_CODED_COLOR_INFO[] = "coded.color-format";
+
+/**
+ * Color Aspects
+ */
+
+/* The meaning of the following enumerators is as described in ITU-T H.273. */
+
+/// Range
+C2ENUM(C2Color::range_t, uint32_t,
+    RANGE_UNSPECIFIED,          ///< range is unspecified
+    RANGE_FULL,                 ///< full range
+    RANGE_LIMITED,              ///< limited range
+
+    RANGE_VENDOR_START = 0x80,  ///< vendor-specific range values start here
+    RANGE_OTHER = 0XFF          ///< max value, reserved for undefined values
+)
+
+/// Color primaries
+C2ENUM(C2Color::primaries_t, uint32_t,
+    PRIMARIES_UNSPECIFIED,          ///< primaries are unspecified
+    PRIMARIES_BT709,                ///< Rec.ITU-R BT.709-6 or equivalent
+    PRIMARIES_BT470_M,              ///< Rec.ITU-R BT.470-6 System M or equivalent
+    PRIMARIES_BT601_625,            ///< Rec.ITU-R BT.601-6 625 or equivalent
+    PRIMARIES_BT601_525,            ///< Rec.ITU-R BT.601-6 525 or equivalent
+    PRIMARIES_GENERIC_FILM,         ///< Generic Film
+    PRIMARIES_BT2020,               ///< Rec.ITU-R BT.2020 or equivalent
+    PRIMARIES_RP431,                ///< SMPTE RP 431-2 or equivalent
+    PRIMARIES_EG432,                ///< SMPTE EG 432-1 or equivalent
+    PRIMARIES_EBU3213,              ///< EBU Tech.3213-E or equivalent
+                                    ///
+    PRIMARIES_VENDOR_START = 0x80,  ///< vendor-specific primaries values start here
+    PRIMARIES_OTHER = 0xff          ///< max value, reserved for undefined values
+)
+
+/// Transfer function
+C2ENUM(C2Color::transfer_t, uint32_t,
+    TRANSFER_UNSPECIFIED,           ///< transfer is unspecified
+    TRANSFER_LINEAR,                ///< Linear transfer characteristics
+    TRANSFER_SRGB,                  ///< sRGB or equivalent
+    TRANSFER_170M,                  ///< SMPTE 170M or equivalent (e.g. BT.601/709/2020)
+    TRANSFER_GAMMA22,               ///< Assumed display gamma 2.2
+    TRANSFER_GAMMA28,               ///< Assumed display gamma 2.8
+    TRANSFER_ST2084,                ///< SMPTE ST 2084 for 10/12/14/16 bit systems
+    TRANSFER_HLG,                   ///< ARIB STD-B67 hybrid-log-gamma
+
+    TRANSFER_240M = 0x40,           ///< SMPTE 240M or equivalent
+    TRANSFER_XVYCC,                 ///< IEC 61966-2-4 or equivalent
+    TRANSFER_BT1361,                ///< Rec.ITU-R BT.1361 extended gamut
+    TRANSFER_ST428,                 ///< SMPTE ST 428-1 or equivalent
+                                    ///
+    TRANSFER_VENDOR_START = 0x80,   ///< vendor-specific transfer values start here
+    TRANSFER_OTHER = 0xff           ///< max value, reserved for undefined values
+)
+
+/// Matrix coefficient
+C2ENUM(C2Color::matrix_t, uint32_t,
+    MATRIX_UNSPECIFIED,             ///< matrix coefficients are unspecified
+    MATRIX_BT709,                   ///< Rec.ITU-R BT.709-5 or equivalent
+    MATRIX_FCC47_73_682,            ///< FCC Title 47 CFR 73.682 or equivalent (KR=0.30, KB=0.11)
+    MATRIX_BT601,                   ///< Rec.ITU-R BT.470, BT.601-6 625 or equivalent
+    MATRIX_240M,                    ///< SMPTE 240M or equivalent
+    MATRIX_BT2020,                  ///< Rec.ITU-R BT.2020 non-constant luminance
+    MATRIX_BT2020_CONSTANT,         ///< Rec.ITU-R BT.2020 constant luminance
+    MATRIX_VENDOR_START = 0x80,     ///< vendor-specific matrix coefficient values start here
+    MATRIX_OTHER = 0xff,            ///< max value, reserved for undefined values
+)
+
+struct C2ColorAspectsStruct {
+    C2Color::range_t range;
+    C2Color::primaries_t primaries;
+    C2Color::transfer_t transfer;
+    C2Color::matrix_t matrix;
+
+    C2ColorAspectsStruct()
+        : range(C2Color::RANGE_UNSPECIFIED),
+          primaries(C2Color::PRIMARIES_UNSPECIFIED),
+          transfer(C2Color::TRANSFER_UNSPECIFIED),
+          matrix(C2Color::MATRIX_UNSPECIFIED) { }
+
+    C2ColorAspectsStruct(C2Color::range_t range_, C2Color::primaries_t primaries_,
+                         C2Color::transfer_t transfer_, C2Color::matrix_t matrix_)
+        : range(range_), primaries(primaries_), transfer(transfer_), matrix(matrix_) {}
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(ColorAspects)
+    C2FIELD(range, "range")
+    C2FIELD(primaries, "primaries")
+    C2FIELD(transfer, "transfer")
+    C2FIELD(matrix, "matrix")
+};
+
+typedef C2StreamParam<C2Info, C2ColorAspectsStruct, kParamIndexColorAspects>
+        C2StreamColorAspectsInfo;
+constexpr char C2_PARAMKEY_COLOR_ASPECTS[] = "raw.color";
+constexpr char C2_PARAMKEY_VUI_COLOR_ASPECTS[] = "coded.vui.color";
+
+/**
+ * Default color aspects to use. These come from the container or client and shall be handled
+ * according to the coding standard.
+ */
+typedef C2StreamParam<C2Tuning, C2ColorAspectsStruct, kParamIndexDefaultColorAspects>
+        C2StreamColorAspectsTuning;
+constexpr char C2_PARAMKEY_DEFAULT_COLOR_ASPECTS[] = "default.color";
+
+/**
+ * HDR Static Metadata Info.
+ */
+struct C2ColorXyStruct {
+    float x; ///< x color coordinate in xyY space [0-1]
+    float y; ///< y color coordinate in xyY space [0-1]
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(ColorXy)
+    C2FIELD(x, "x")
+    C2FIELD(y, "y")
+};
+
+struct C2MasteringDisplayColorVolumeStruct {
+    C2ColorXyStruct red;    ///< coordinates of red display primary
+    C2ColorXyStruct green;  ///< coordinates of green display primary
+    C2ColorXyStruct blue;   ///< coordinates of blue display primary
+    C2ColorXyStruct white;  ///< coordinates of white point
+
+    float maxLuminance;  ///< max display mastering luminance in cd/m^2
+    float minLuminance;  ///< min display mastering luminance in cd/m^2
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(MasteringDisplayColorVolume)
+    C2FIELD(red, "red")
+    C2FIELD(green, "green")
+    C2FIELD(blue, "blue")
+    C2FIELD(white, "white")
+
+    C2FIELD(maxLuminance, "max-luminance")
+    C2FIELD(minLuminance, "min-luminance")
+};
+
+struct C2HdrStaticMetadataStruct {
+    C2MasteringDisplayColorVolumeStruct mastering;
+
+    // content descriptors
+    float maxCll;  ///< max content light level (pixel luminance) in cd/m^2
+    float maxFall; ///< max frame average light level (frame luminance) in cd/m^2
+
+    DEFINE_AND_DESCRIBE_BASE_C2STRUCT(HdrStaticMetadata)
+    C2FIELD(mastering, "mastering")
+    C2FIELD(maxCll, "max-cll")
+    C2FIELD(maxFall, "max-fall")
+};
+typedef C2StreamParam<C2Info, C2HdrStaticMetadataStruct, kParamIndexHdrStaticMetadata>
+        C2StreamHdrStaticInfo;
+constexpr char C2_PARAMKEY_HDR_STATIC_INFO[] = "raw.hdr-static-info";
+
+/**
+ * HDR10+ Metadata Info.
+ */
+typedef C2StreamParam<C2Info, C2BlobValue, kParamIndexHdr10PlusMetadata>
+        C2StreamHdr10PlusInfo;
+constexpr char C2_PARAMKEY_INPUT_HDR10_PLUS_INFO[] = "input.hdr10-plus-info";
+constexpr char C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO[] = "output.hdr10-plus-info";
+
+/* ------------------------------------ block-based coding ----------------------------------- */
+
+/**
+ * Block-size, block count and block rate. Used to determine or communicate profile-level
+ * requirements.
+ */
+typedef C2StreamParam<C2Info, C2PictureSizeStruct, kParamIndexBlockSize> C2StreamBlockSizeInfo;
+constexpr char C2_PARAMKEY_BLOCK_SIZE[] = "coded.block-size";
+
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexBlockCount> C2StreamBlockCountInfo;
+constexpr char C2_PARAMKEY_BLOCK_COUNT[] = "coded.block-count";
+
+typedef C2StreamParam<C2Info, C2FloatValue, kParamIndexBlockRate> C2StreamBlockRateInfo;
+constexpr char C2_PARAMKEY_BLOCK_RATE[] = "coded.block-rate";
+
+/* ====================================== VIDEO COMPONENTS ====================================== */
+
+/**
+ * Frame rate (coded and port for raw data)
+ *
+ * Coded frame rates are what is represented in the compressed bitstream and should correspond to
+ * the timestamp.
+ *
+ * Frame rates on raw ports should still correspond to the timestamps.
+ *
+ * For slow motion or timelapse recording, the timestamp shall be adjusted prior to feeding an
+ * encoder, and the time stretch parameter should be used to signal the relationship between
+ * timestamp and real-world time.
+ */
+typedef C2StreamParam<C2Info, C2FloatValue, kParamIndexFrameRate> C2StreamFrameRateInfo;
+constexpr char C2_PARAMKEY_FRAME_RATE[] = "coded.frame-rate";
+
+typedef C2PortParam<C2Info, C2FloatValue, kParamIndexFrameRate> C2PortFrameRateInfo;
+constexpr char C2_PARAMKEY_INPUT_FRAME_RATE[] = "input.frame-rate";
+constexpr char C2_PARAMKEY_OUTPUT_FRAME_RATE[] = "output.frame-rate";
+
+/**
+ * Time stretch. Ratio between real-world time and timestamp. E.g. time stretch of 4.0 means that
+ * timestamp grows 1/4 the speed of real-world time (e.g. 4x slo-mo input). This can be used to
+ * optimize encoding.
+ */
+typedef C2PortParam<C2Info, C2FloatValue, kParamIndexTimeStretch> C2PortTimeStretchInfo;
+constexpr char C2_PARAMKEY_INPUT_TIME_STRETCH[] = "input.time-stretch";
+constexpr char C2_PARAMKEY_OUTPUT_TIME_STRETCH[] = "output.time-stretch";
+
+/**
+ * Max video frame size.
+ */
+typedef C2StreamParam<C2Tuning, C2PictureSizeStruct, kParamIndexMaxPictureSize>
+        C2StreamMaxPictureSizeTuning;
+typedef C2StreamMaxPictureSizeTuning C2MaxVideoSizeHintPortSetting;
+constexpr char C2_PARAMKEY_MAX_PICTURE_SIZE[] = "raw.max-size";
+
+/**
+ * Picture type mask.
+ */
+C2ENUM(C2Config::picture_type_t, uint32_t,
+    SYNC_FRAME = (1 << 0),  ///< sync frame, e.g. IDR
+    I_FRAME    = (1 << 1),  ///< intra frame that is completely encoded
+    P_FRAME    = (1 << 2),  ///< inter predicted frame from previous frames
+    B_FRAME    = (1 << 3),  ///< backward predicted (out-of-order) frame
+)
+
+/**
+ * Allowed picture types.
+ */
+typedef C2StreamParam<C2Tuning, C2SimpleValueStruct<C2EasyEnum<C2Config::picture_type_t>>,
+                kParamIndexPictureTypeMask>
+        C2StreamPictureTypeMaskTuning;
+constexpr char C2_PARAMKEY_PICTURE_TYPE_MASK[] = "coding.picture-type-mask";
+
+/**
+ * Resulting picture type
+ */
+typedef C2StreamParam<C2Info, C2SimpleValueStruct<C2EasyEnum<C2Config::picture_type_t>>,
+                kParamIndexPictureType>
+        C2StreamPictureTypeInfo;
+typedef C2StreamPictureTypeInfo C2StreamPictureTypeMaskInfo;
+constexpr char C2_PARAMKEY_PICTURE_TYPE[] = "coded.picture-type";
+
+/**
+ * GOP specification.
+ *
+ * GOP is specified in layers between sync frames, by specifying the number of specific type of
+ * frames between the previous type (starting with sync frames for the first layer):
+ *
+ * E.g.
+ *      - 4 I frames between each sync frame
+ *      - 2 P frames between each I frame
+ *      - 1 B frame between each P frame
+ *
+ *      [ { I, 4 }, { P, 2 }, { B, 1 } ] ==> (Sync)BPBPB IBPBPB IBPBPB IBPBPB IBPBPB (Sync)BPBPB
+ *
+ * For infinite GOP, I layer can be omitted (as the first frame is always a sync frame.):
+ *
+ *      [ { P, MAX_UINT } ]   ==> (Sync)PPPPPPPPPPPPPPPPPP...
+ *
+ * Sync frames can also be requested on demand, and as a time-based interval. For time-based
+ * interval, if there hasn't been a sync frame in at least the given time, the next I frame shall
+ * be encoded as a sync frame.  For sync request, the next I frame shall be encoded as a sync frame.
+ *
+ * Temporal layering will determine GOP structure other than the I frame count between sync
+ * frames.
+ */
+struct C2GopLayerStruct {
+    C2GopLayerStruct() : type_((C2Config::picture_type_t)0), count(0) {}
+    C2GopLayerStruct(C2Config::picture_type_t type, uint32_t count_)
+        : type_(type), count(count_) { }
+
+    C2Config::picture_type_t type_;
+    uint32_t count;
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(GopLayer)
+    C2FIELD(type_, "type")
+    C2FIELD(count, "count")
+};
+
+typedef C2StreamParam<C2Tuning, C2SimpleArrayStruct<C2GopLayerStruct>, kParamIndexGop>
+        C2StreamGopTuning;
+constexpr char C2_PARAMKEY_GOP[] = "coding.gop";
+
+/**
+ * Sync frame can be requested on demand by the client.
+ *
+ * If true, the next I frame shall be encoded as a sync frame. This config can be passed
+ * synchronously with the work, or directly to the component - leading to different result.
+ * If it is passed with work, it shall take effect when that work item is being processed (so
+ * the first I frame at or after that work item shall be a sync frame).
+ */
+typedef C2StreamParam<C2Tuning, C2EasyBoolValue, kParamIndexRequestSyncFrame>
+        C2StreamRequestSyncFrameTuning;
+constexpr char C2_PARAMKEY_REQUEST_SYNC_FRAME[] = "coding.request-sync-frame";
+
+/**
+ * Sync frame interval in time domain (timestamp).
+ *
+ * If there hasn't been a sync frame in at least this value, the next intra frame shall be encoded
+ * as a sync frame. The value of MAX_I64 or a negative value means no sync frames after the first
+ * frame. A value of 0 means all sync frames.
+ */
+typedef C2StreamParam<C2Tuning, C2Int64Value, kParamIndexSyncFrameInterval>
+        C2StreamSyncFrameIntervalTuning;
+constexpr char C2_PARAMKEY_SYNC_FRAME_INTERVAL[] = "coding.sync-frame-interval";
+
+/**
+ * Temporal layering
+ *
+ * Layer index is a value between 0 and layer count - 1. Layers with higher index have higher
+ * frequency:
+ *     0
+ *   1   1
+ *  2 2 2 2
+ */
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexLayerIndex> C2StreamLayerIndexInfo;
+constexpr char C2_PARAMKEY_LAYER_INDEX[] = "coded.layer-index";
+
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexLayerCount> C2StreamLayerCountInfo;
+constexpr char C2_PARAMKEY_LAYER_COUNT[] = "coded.layer-count";
+
+struct C2TemporalLayeringStruct {
+    C2TemporalLayeringStruct()
+        : layerCount(0), bLayerCount(0) { }
+
+    C2TemporalLayeringStruct(size_t /* flexCount */, uint32_t layerCount_, uint32_t bLayerCount_)
+        : layerCount(layerCount_), bLayerCount(c2_min(layerCount_, bLayerCount_)) { }
+
+    C2TemporalLayeringStruct(size_t flexCount, uint32_t layerCount_, uint32_t bLayerCount_,
+                             std::initializer_list<float> ratios)
+        : layerCount(layerCount_), bLayerCount(c2_min(layerCount_, bLayerCount_)) {
+        size_t ix = 0;
+        for (float ratio : ratios) {
+            if (ix == flexCount) {
+                break;
+            }
+            bitrateRatios[ix++] = ratio;
+        }
+    }
+
+    uint32_t layerCount;     ///< total number of layers (0 means no temporal layering)
+    uint32_t bLayerCount;    ///< total number of bidirectional layers (<= num layers)
+    /**
+     * Bitrate budgets for each layer and the layers below, given as a ratio of the total
+     * stream bitrate. This can be omitted or partially specififed by the client while configuring,
+     * in which case the component shall fill in appropriate values for the missing layers.
+     * This must be provided by the component when queried for at least layer count - 1 (as the
+     * last layer's budget is always 1.0).
+     */
+    float bitrateRatios[];   ///< 1.0-based
+
+    DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(TemporalLayering, bitrateRatios)
+    C2FIELD(layerCount, "layer-count")
+    C2FIELD(bLayerCount, "b-layer-count")
+    C2FIELD(bitrateRatios, "bitrate-ratios")
+};
+
+typedef C2StreamParam<C2Tuning, C2TemporalLayeringStruct, kParamIndexTemporalLayering>
+        C2StreamTemporalLayeringTuning;
+constexpr char C2_PARAMKEY_TEMPORAL_LAYERING[] = "coding.temporal-layering";
+
+/**
+ * Intra-refresh.
+ */
+
+C2ENUM(C2Config::intra_refresh_mode_t, uint32_t,
+    INTRA_REFRESH_DISABLED,     ///< no intra refresh
+    INTRA_REFRESH_ARBITRARY,    ///< arbitrary, unspecified
+)
+
+struct C2IntraRefreshStruct {
+    C2IntraRefreshStruct()
+        : mode(C2Config::INTRA_REFRESH_DISABLED), period(0.) { }
+
+    C2IntraRefreshStruct(C2Config::intra_refresh_mode_t mode_, float period_)
+        : mode(mode_), period(period_) { }
+
+    C2Config::intra_refresh_mode_t mode; ///< refresh mode
+    float period;         ///< intra refresh period in frames (must be >= 1), 0 means disabled
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(IntraRefresh)
+    C2FIELD(mode, "mode")
+    C2FIELD(period, "period")
+};
+
+typedef C2StreamParam<C2Tuning, C2IntraRefreshStruct, kParamIndexIntraRefresh>
+        C2StreamIntraRefreshTuning;
+constexpr char C2_PARAMKEY_INTRA_REFRESH[] = "coding.intra-refresh";
+
+/* ====================================== IMAGE COMPONENTS ====================================== */
+
+/**
+ * Tile layout.
+ *
+ * This described how the image is decomposed into tiles.
+ */
+C2ENUM(C2Config::scan_order_t, uint32_t,
+    SCAN_LEFT_TO_RIGHT_THEN_DOWN
+)
+
+struct C2TileLayoutStruct {
+    C2PictureSizeStruct tile;       ///< tile size
+    uint32_t columnCount;           ///< number of tiles horizontally
+    uint32_t rowCount;              ///< number of tiles vertically
+    C2Config::scan_order_t order;   ///< tile order
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(TileLayout)
+    C2FIELD(tile, "tile")
+    C2FIELD(columnCount, "columns")
+    C2FIELD(rowCount, "rows")
+    C2FIELD(order, "order")
+};
+
+typedef C2StreamParam<C2Info, C2TileLayoutStruct, kParamIndexTileLayout> C2StreamTileLayoutInfo;
+constexpr char C2_PARAMKEY_TILE_LAYOUT[] = "coded.tile-layout";
+
+/**
+ * Tile handling.
+ *
+ * Whether to concatenate tiles or output them each.
+ */
+C2ENUM(C2Config::tiling_mode_t, uint32_t,
+    TILING_SEPARATE,    ///< output each tile in a separate onWorkDone
+    TILING_CONCATENATE  ///< output one work completion per frame (concatenate tiles)
+)
+
+typedef C2StreamParam<C2Tuning, C2TileLayoutStruct, kParamIndexTileHandling>
+        C2StreamTileHandlingTuning;
+constexpr char C2_PARAMKEY_TILE_HANDLING[] = "coding.tile-handling";
+
+/* ====================================== AUDIO COMPONENTS ====================================== */
+
+/**
+ * Sample rate
+ */
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexSampleRate> C2StreamSampleRateInfo;
+constexpr char C2_PARAMKEY_SAMPLE_RATE[] = "raw.sample-rate";
+constexpr char C2_PARAMKEY_CODED_SAMPLE_RATE[] = "coded.sample-rate";
+
+/**
+ * Channel count.
+ */
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexChannelCount> C2StreamChannelCountInfo;
+constexpr char C2_PARAMKEY_CHANNEL_COUNT[] = "raw.channel-count";
+constexpr char C2_PARAMKEY_CODED_CHANNEL_COUNT[] = "coded.channel-count";
+
+/**
+ * Max channel count. Used to limit the number of coded or decoded channels.
+ */
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexMaxChannelCount> C2StreamMaxChannelCountInfo;
+constexpr char C2_PARAMKEY_MAX_CHANNEL_COUNT[] = "raw.max-channel-count";
+constexpr char C2_PARAMKEY_MAX_CODED_CHANNEL_COUNT[] = "coded.max-channel-count";
+
+/**
+ * Audio sample format (PCM encoding)
+ */
+C2ENUM(C2Config::pcm_encoding_t, uint32_t,
+    PCM_16,
+    PCM_8,
+    PCM_FLOAT
+)
+
+typedef C2StreamParam<C2Info, C2SimpleValueStruct<C2Config::pcm_encoding_t>, kParamIndexPcmEncoding>
+        C2StreamPcmEncodingInfo;
+constexpr char C2_PARAMKEY_PCM_ENCODING[] = "raw.pcm-encoding";
+constexpr char C2_PARAMKEY_CODED_PCM_ENCODING[] = "coded.pcm-encoding";
+
+/**
+ * AAC SBR Mode. Used during encoding.
+ */
+C2ENUM(C2Config::aac_sbr_mode_t, uint32_t,
+    AAC_SBR_OFF,
+    AAC_SBR_SINGLE_RATE,
+    AAC_SBR_DUAL_RATE,
+    AAC_SBR_AUTO ///< let the codec decide
+)
+
+typedef C2StreamParam<C2Info, C2SimpleValueStruct<C2Config::aac_sbr_mode_t>, kParamIndexAacSbrMode>
+        C2StreamAacSbrModeTuning;
+constexpr char C2_PARAMKEY_AAC_SBR_MODE[] = "coding.aac-sbr-mode";
+
+/**
+ * DRC Compression. Used during decoding.
+ */
+C2ENUM(C2Config::drc_compression_mode_t, int32_t,
+    DRC_COMPRESSION_ODM_DEFAULT, ///< odm's default
+    DRC_COMPRESSION_NONE,
+    DRC_COMPRESSION_LIGHT,
+    DRC_COMPRESSION_HEAVY ///<
+)
+
+typedef C2StreamParam<C2Info, C2SimpleValueStruct<C2Config::drc_compression_mode_t>,
+                kParamIndexDrcCompression>
+        C2StreamDrcCompressionModeTuning;
+constexpr char C2_PARAMKEY_DRC_COMPRESSION_MODE[] = "coding.drc.compression-mode";
+
+/**
+ * DRC target reference level in dBFS. Used during decoding.
+ */
+typedef C2StreamParam<C2Info, C2FloatValue, kParamIndexDrcTargetReferenceLevel>
+        C2StreamDrcTargetReferenceLevelTuning;
+constexpr char C2_PARAMKEY_DRC_TARGET_REFERENCE_LEVEL[] = "coding.drc.reference-level";
+
+/**
+ * DRC target reference level in dBFS. Used during decoding.
+ */
+typedef C2StreamParam<C2Info, C2FloatValue, kParamIndexDrcEncodedTargetLevel>
+        C2StreamDrcEncodedTargetLevelTuning;
+constexpr char C2_PARAMKEY_DRC_ENCODED_TARGET_LEVEL[] = "coding.drc.encoded-level";
+
+/**
+ * DRC target reference level in dBFS. Used during decoding.
+ */
+typedef C2StreamParam<C2Info, C2FloatValue, kParamIndexDrcBoostFactor>
+        C2StreamDrcBoostFactorTuning;
+constexpr char C2_PARAMKEY_DRC_BOOST_FACTOR[] = "coding.drc.boost-factor";
+
+/**
+ * DRC target reference level in dBFS. Used during decoding.
+ */
+typedef C2StreamParam<C2Info, C2FloatValue, kParamIndexDrcAttenuationFactor>
+        C2StreamDrcAttenuationFactorTuning;
+constexpr char C2_PARAMKEY_DRC_ATTENUATION_FACTOR[] = "coding.drc.attenuation-factor";
+
+/**
+ * DRC Effect Type (see ISO 23003-4) Uniform Dynamic Range Control. Used during decoding.
+ */
+C2ENUM(C2Config::drc_effect_type_t, int32_t,
+    DRC_EFFECT_ODM_DEFAULT = -2, ///< odm's default
+    DRC_EFFECT_OFF = -1,    ///< no DRC
+    DRC_EFFECT_NONE = 0,    ///< no DRC except to prevent clipping
+    DRC_EFFECT_LATE_NIGHT,
+    DRC_EFFECT_NOISY_ENVIRONMENT,
+    DRC_EFFECT_LIMITED_PLAYBACK_RANGE,
+    DRC_EFFECT_LOW_PLAYBACK_LEVEL,
+    DRC_EFFECT_DIALOG_ENHANCEMENT,
+    DRC_EFFECT_GENERAL_COMPRESSION
+)
+
+typedef C2StreamParam<C2Info, C2SimpleValueStruct<C2Config::drc_effect_type_t>,
+                kParamIndexDrcEffectType>
+        C2StreamDrcEffectTypeTuning;
+constexpr char C2_PARAMKEY_DRC_EFFECT_TYPE[] = "coding.drc.effect-type";
+
+/* --------------------------------------- AAC components --------------------------------------- */
+
+/**
+ * AAC stream format
+ */
+C2ENUM(C2Config::aac_packaging_t, uint32_t,
+    AAC_PACKAGING_RAW,
+    AAC_PACKAGING_ADTS
+)
+
+typedef C2StreamParam<C2Info, C2SimpleValueStruct<C2EasyEnum<C2Config::aac_packaging_t>>,
+        kParamIndexAacPackaging> C2StreamAacPackagingInfo;
+typedef C2StreamAacPackagingInfo C2StreamAacFormatInfo;
+constexpr char C2_PARAMKEY_AAC_PACKAGING[] = "coded.aac-packaging";
+
+/* ================================ PLATFORM-DEFINED PARAMETERS ================================ */
+
+/**
+ * Platform level and features.
+ */
+enum C2Config::platform_level_t : uint32_t {
+    PLATFORM_P,   ///< support for Android 9.0 feature set
+};
+
+// read-only
+typedef C2GlobalParam<C2Setting, C2SimpleValueStruct<C2Config::platform_level_t>,
+                kParamIndexPlatformLevel>
+        C2PlatformLevelSetting;
+constexpr char C2_PARAMKEY_PLATFORM_LEVEL[] = "api.platform-level";
+
+enum C2Config::platform_feature_t : uint64_t {
+    // no platform-specific features have been defined
+};
+
+// read-only
+typedef C2GlobalParam<C2Setting, C2SimpleValueStruct<C2Config::platform_feature_t>,
+                kParamIndexPlatformFeatures>
+        C2PlatformFeaturesSetting;
+constexpr char C2_PARAMKEY_PLATFORM_FEATURES[] = "api.platform-features";
+
+/**
+ * This structure describes the preferred ion allocation parameters for a given memory usage.
+ */
+struct C2StoreIonUsageStruct {
+    inline C2StoreIonUsageStruct() {
+        memset(this, 0, sizeof(*this));
+    }
+
+    inline C2StoreIonUsageStruct(uint64_t usage_, uint32_t capacity_)
+        : usage(usage_), capacity(capacity_), heapMask(0), allocFlags(0), minAlignment(0) { }
+
+    uint64_t usage;        ///< C2MemoryUsage
+    uint32_t capacity;     ///< capacity
+    int32_t heapMask;      ///< ion heapMask
+    int32_t allocFlags;    ///< ion allocation flags
+    uint32_t minAlignment; ///< minimum alignment
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(StoreIonUsage)
+    C2FIELD(usage, "usage")
+    C2FIELD(capacity, "capacity")
+    C2FIELD(heapMask, "heap-mask")
+    C2FIELD(allocFlags, "alloc-flags")
+    C2FIELD(minAlignment, "min-alignment")
+};
+
+// store, private
+typedef C2GlobalParam<C2Info, C2StoreIonUsageStruct, kParamIndexStoreIonUsage>
+        C2StoreIonUsageInfo;
+
+/**
+ * Flexible pixel format descriptors
+ */
+struct C2FlexiblePixelFormatDescriptorStruct {
+    uint32_t pixelFormat;
+    uint32_t bitDepth;
+    C2Color::subsampling_t subsampling;
+    C2Color::plane_layout_t layout;
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(FlexiblePixelFormatDescriptor)
+    C2FIELD(pixelFormat, "pixel-format")
+    C2FIELD(bitDepth, "bit-depth")
+    C2FIELD(subsampling, "subsampling")
+    C2FIELD(layout, "layout")
+};
+
+/**
+ * Plane layout of flexible pixel formats.
+ *
+ * bpp: bytes per color component, e.g. 1 for 8-bit formats, and 2 for 10-16-bit formats.
+ */
+C2ENUM(C2Color::plane_layout_t, uint32_t,
+       /** Unknown layout */
+       UNKNOWN_LAYOUT,
+
+       /** Planar layout with rows of each plane packed (colInc = bpp) */
+       PLANAR_PACKED,
+
+       /** Semiplanar layout with rows of each plane packed (colInc_Y/A = bpp (planar),
+        *  colInc_Cb/Cr = 2*bpp (interleaved). Used only for YUV(A) formats. */
+       SEMIPLANAR_PACKED,
+
+       /** Interleaved packed. colInc = N*bpp (N are the number of color components) */
+       INTERLEAVED_PACKED,
+
+       /** Interleaved aligned. colInc = smallest power of 2 >= N*bpp (N are the number of color
+        *  components) */
+       INTERLEAVED_ALIGNED
+)
+
+typedef C2GlobalParam<C2Info, C2SimpleArrayStruct<C2FlexiblePixelFormatDescriptorStruct>,
+                kParamIndexFlexiblePixelFormatDescriptors>
+        C2StoreFlexiblePixelFormatDescriptorsInfo;
+
+/**
+ * This structure describes the android dataspace for a raw video/image frame.
+ */
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexDataSpace> C2StreamDataSpaceInfo;
+constexpr char C2_PARAMKEY_DATA_SPACE[] = "raw.data-space";
+
+/**
+ * This structure describes the android surface scaling mode for a raw video/image frame.
+ */
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexSurfaceScaling> C2StreamSurfaceScalingInfo;
+constexpr char C2_PARAMKEY_SURFACE_SCALING_MODE[] = "raw.surface-scaling";
+
+/* ======================================= INPUT SURFACE ======================================= */
+
+/**
+ * Input surface EOS
+ */
+typedef C2GlobalParam<C2Tuning, C2EasyBoolValue, kParamIndexInputSurfaceEos>
+        C2InputSurfaceEosTuning;
+constexpr char C2_PARAMKEY_INPUT_SURFACE_EOS[] = "input-surface.eos";
+
+/**
+ * Start/suspend/resume/stop controls and timestamps for input surface.
+ *
+ * TODO: make these counters
+ */
+
+struct C2TimedControlStruct {
+    c2_bool_t enabled; ///< control is enabled
+    int64_t timestamp; ///< if enabled, time the control should take effect
+
+    C2TimedControlStruct()
+        : enabled(C2_FALSE), timestamp(0) { }
+
+    /* implicit */ C2TimedControlStruct(uint64_t timestamp_)
+        : enabled(C2_TRUE), timestamp(timestamp_) { }
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(TimedControl)
+    C2FIELD(enabled,   "enabled")
+    C2FIELD(timestamp, "timestamp")
+};
+
+typedef C2PortParam<C2Tuning, C2TimedControlStruct, kParamIndexStartAt>
+        C2PortStartTimestampTuning;
+constexpr char C2_PARAMKEY_INPUT_SURFACE_START_AT[] = "input-surface.start";
+typedef C2PortParam<C2Tuning, C2TimedControlStruct, kParamIndexSuspendAt>
+        C2PortSuspendTimestampTuning;
+constexpr char C2_PARAMKEY_INPUT_SURFACE_SUSPEND_AT[] = "input-surface.suspend";
+typedef C2PortParam<C2Tuning, C2TimedControlStruct, kParamIndexResumeAt>
+        C2PortResumeTimestampTuning;
+constexpr char C2_PARAMKEY_INPUT_SURFACE_RESUME_AT[] = "input-surface.resume";
+typedef C2PortParam<C2Tuning, C2TimedControlStruct, kParamIndexStopAt>
+        C2PortStopTimestampTuning;
+constexpr char C2_PARAMKEY_INPUT_SURFACE_STOP_AT[] = "input-surface.stop";
+
+/**
+ * Time offset for input surface. Input timestamp to codec is surface buffer timestamp plus this
+ * time offset.
+ */
+typedef C2GlobalParam<C2Tuning, C2Int64Value, kParamIndexTimeOffset> C2ComponentTimeOffsetTuning;
+constexpr char C2_PARAMKEY_INPUT_SURFACE_TIME_OFFSET[] = "input-surface.time-offset";
+
+/**
+ * Minimum fps for input surface.
+ *
+ * Repeat frame to meet this.
+ */
+typedef C2PortParam<C2Tuning, C2FloatValue, kParamIndexMinFrameRate> C2PortMinFrameRateTuning;
+constexpr char C2_PARAMKEY_INPUT_SURFACE_MIN_FRAME_RATE[] = "input-surface.min-frame-rate";
+
+/**
+ * Timestamp adjustment (override) for input surface buffers. These control the input timestamp
+ * fed to the codec, but do not impact the output timestamp.
+ */
+struct C2TimestampGapAdjustmentStruct {
+    /// control modes
+    enum mode_t : uint32_t;
+
+    inline C2TimestampGapAdjustmentStruct();
+
+    inline C2TimestampGapAdjustmentStruct(mode_t mode_, uint64_t value_)
+        : mode(mode_), value(value_) { }
+
+    mode_t mode;    ///< control mode
+    uint64_t value; ///< control value for gap between two timestamp
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(TimestampGapAdjustment)
+    C2FIELD(mode, "mode")
+    C2FIELD(value, "value")
+};
+
+C2ENUM(C2TimestampGapAdjustmentStruct::mode_t, uint32_t,
+    NONE,
+    MIN_GAP,
+    FIXED_GAP,
+);
+
+inline C2TimestampGapAdjustmentStruct::C2TimestampGapAdjustmentStruct()
+    : mode(C2TimestampGapAdjustmentStruct::NONE), value(0) { }
+
+typedef C2PortParam<C2Tuning, C2TimestampGapAdjustmentStruct> C2PortTimestampGapTuning;
+constexpr char C2_PARAMKEY_INPUT_SURFACE_TIMESTAMP_ADJUSTMENT[] = "input-surface.timestamp-adjustment";
+
+/// @}
+
+#endif  // C2CONFIG_H_
diff --git a/media/codec2/core/include/C2Enum.h b/media/codec2/core/include/C2Enum.h
new file mode 100644
index 0000000..b0fad8f
--- /dev/null
+++ b/media/codec2/core/include/C2Enum.h
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+#ifndef C2ENUM_H_
+#define C2ENUM_H_
+
+#include <C2Param.h>
+#include <_C2MacroUtils.h>
+
+#include <utility>
+#include <vector>
+
+/** \file
+ * Tools for easier enum support.
+ */
+
+/// \cond INTERNAL
+
+/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
+
+/**
+ * Utility class that allows ignoring enum value assignment (e.g. both '(_C2EnumConst)kValue = x'
+ * and '(_C2EnumConst)kValue' will eval to kValue.
+ */
+template<typename T>
+class _C2EnumConst {
+public:
+    // implicit conversion from T
+    inline _C2EnumConst(T value) : _mValue(value) {}
+    // implicit conversion to T
+    inline operator T() { return _mValue; }
+    // implicit conversion to C2Value::Primitive
+    inline operator C2Value::Primitive() { return (T)_mValue; }
+    // ignore assignment and return T here to avoid implicit conversion to T later
+    inline T &operator =(T value __unused) { return _mValue; }
+private:
+    T _mValue;
+};
+
+/// mapper to get name of enum
+/// \note this will contain any initialization, which we will remove when converting to lower-case
+#define _C2_GET_ENUM_NAME(x, y) #x
+/// mapper to get value of enum
+#define _C2_GET_ENUM_VALUE(x, type) (_C2EnumConst<type>)x
+
+/// \endcond
+
+class _C2EnumUtils {
+    static C2String camelCaseToDashed(C2String name);
+
+    static std::vector<C2String> sanitizeEnumValueNames(
+            const std::vector<C2StringLiteral> names,
+            C2StringLiteral _prefix = nullptr);
+
+    friend class C2UtilTest_EnumUtilsTest_Test;
+
+public:
+    // this may not be used...
+    static C2_HIDE std::vector<C2String> parseEnumValuesFromString(C2StringLiteral value);
+
+    template<typename T>
+    static C2_HIDE C2FieldDescriptor::NamedValuesType sanitizeEnumValues(
+            std::vector<T> values,
+            std::vector<C2StringLiteral> names,
+            C2StringLiteral prefix = nullptr) {
+        C2FieldDescriptor::NamedValuesType namedValues;
+        std::vector<C2String> sanitizedNames = sanitizeEnumValueNames(names, prefix);
+        for (size_t i = 0; i < values.size() && i < sanitizedNames.size(); ++i) {
+            namedValues.emplace_back(sanitizedNames[i], values[i]);
+        }
+        return namedValues;
+    }
+
+    template<typename E>
+    static C2_HIDE C2FieldDescriptor::NamedValuesType customEnumValues(
+            std::vector<std::pair<C2StringLiteral, E>> items) {
+        C2FieldDescriptor::NamedValuesType namedValues;
+        for (auto &item : items) {
+            namedValues.emplace_back(item.first, item.second);
+        }
+        return namedValues;
+    }
+};
+
+#define DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, ...) \
+    _DEFINE_C2_ENUM_VALUE_AUTO_HELPER(__C2_GENERATE_GLOBAL_VARS__, name, type, prefix, \
+            ##__VA_ARGS__)
+#define _DEFINE_C2_ENUM_VALUE_AUTO_HELPER(enabled, name, type, prefix, ...) \
+    __DEFINE_C2_ENUM_VALUE_AUTO_HELPER(enabled, name, type, prefix, ##__VA_ARGS__)
+#define __DEFINE_C2_ENUM_VALUE_AUTO_HELPER(enabled, name, type, prefix, ...) \
+    ___DEFINE_C2_ENUM_VALUE_AUTO_HELPER##enabled(name, type, prefix, ##__VA_ARGS__)
+#define ___DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, ...) \
+template<> \
+C2FieldDescriptor::NamedValuesType C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
+    return _C2EnumUtils::sanitizeEnumValues( \
+            std::vector<C2Value::Primitive> { _C2_MAP(_C2_GET_ENUM_VALUE, type, __VA_ARGS__) }, \
+            { _C2_MAP(_C2_GET_ENUM_NAME, type, __VA_ARGS__) }, \
+            prefix); \
+}
+#define ___DEFINE_C2_ENUM_VALUE_AUTO_HELPER__C2_GENERATE_GLOBAL_VARS__(name, type, prefix, ...)
+
+#define DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, names) \
+    _DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(__C2_GENERATE_GLOBAL_VARS__, name, names)
+#define _DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(enabled, name, names) \
+    __DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(enabled, name, names)
+#define __DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(enabled, name, names) \
+    ___DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER##enabled(name, names)
+#define ___DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, names) \
+template<> \
+C2FieldDescriptor::NamedValuesType C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
+    return _C2EnumUtils::customEnumValues( \
+            std::vector<std::pair<C2StringLiteral, name>> names); \
+}
+#define ___DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER__C2_GENERATE_GLOBAL_VARS__(name, names)
+
+/**
+ * Defines an enum type with the default named value mapper. The default mapper
+ * finds and removes the longest common prefix across all of the enum value names, and
+ * replaces camel-case separators with dashes ('-').
+ *
+ * This macro must be used in the global scope and namespace.
+ *
+ *  ~~~~~~~~~~~~~ (.cpp)
+ *  C2ENUM(c2_enum_t, uint32_t,
+ *    C2_VALUE1,
+ *    C2_VALUE2 = 5,
+ *    C2_VALUE3 = C2_VALUE1 + 1)
+ *  // named values are: C2_VALUE1 => "1", C2_VALUE2 => "2", ...
+ *  // longest common prefix is "C2_VALUE"
+ *  ~~~~~~~~~~~~~
+ *
+ * \param name name of the enum type (This can be an inner class enum.)
+ * \param type underlying type
+ */
+#define C2ENUM(name, type, ...) \
+enum name : type { __VA_ARGS__ }; \
+DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, nullptr, __VA_ARGS__)
+
+/**
+ * Defines an enum type with the default named value mapper but custom prefix. The default
+ * mapper removes the prefix from all of the enum value names (if present), and
+ * inserts dashes at camel-case separators (lowHigh becomes low-high) and also replaces
+ * non-leading underscores with dashes ('-').
+ *
+ * This macro must be used in the global scope and namespace.
+ *
+ *  ~~~~~~~~~~~~~ (.cpp)
+ *  C2ENUM_CUSTOM_PREFIX(c2_enum_t, uint32_t, "C2_",
+ *    C2_VALUE1,
+ *    C2_VALUE2 = 5,
+ *    C2_VALUE3 = C2_VALUE1 + 1)
+ *  // named values are: C2_VALUE1 => "VALUE1", C2_VALUE2 => "VALUE2", ...
+ *  ~~~~~~~~~~~~~
+ *
+ * \param name name of the enum type (This can be an inner class enum.)
+ * \param type underlying type
+ * \param prefix prefix to remove
+ */
+#define C2ENUM_CUSTOM_PREFIX(name, type, prefix, ...) \
+enum name : type { __VA_ARGS__ }; \
+DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, __VA_ARGS__)
+
+/**
+ * Defines an enum type with custom names.
+ *
+ * This macro must be used in the global scope and namespace.
+ *
+ *  ~~~~~~~~~~~~~ (.cpp)
+ *  C2ENUM_CUSTOM_NAMES(SomeStruct::c2_enum_t, uint32_t, ({
+ *      { "One", SomeStruct::C2_VALUE1 },
+ *      { "Two", SomeStruct::C2_VALUE2 },
+ *      { "Three", SomeStruct::C2_VALUE3 } }),
+ *    C2_VALUE1,
+ *    C2_VALUE2 = 5,
+ *    C2_VALUE3 = C2_VALUE1 + 1)
+ *
+ *  // named values are: C2_VALUE1 => "One", C2_VALUE2 => "Two", ...
+ *  ~~~~~~~~~~~~~
+ */
+#define C2ENUM_CUSTOM_NAMES(name, type, names, ...) \
+enum name : type { __VA_ARGS__ }; \
+DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, names)
+
+/**
+ * Make enums usable as their integral types.
+ *
+ * Note: this makes them not usable in printf()
+ */
+template<class E>
+struct C2EasyEnum {
+    using U = typename std::underlying_type<E>::type;
+    E value;
+    // define conversion functions
+    inline constexpr operator E() const { return value; }
+    inline constexpr C2EasyEnum(E value_) : value(value_) { }
+    inline constexpr C2EasyEnum(U value_) : value(E(value_)) { }
+    inline constexpr C2EasyEnum() = default;
+};
+
+// make C2EasyEnum behave like a regular enum
+
+namespace std {
+    template<typename E>
+    struct underlying_type<C2EasyEnum<E>> {
+        typedef typename underlying_type<E>::type type;
+    };
+
+    template<typename E>
+    struct is_enum<C2EasyEnum<E>> {
+        constexpr static bool value = true;
+    };
+}
+
+#endif  // C2ENUM_H_
+
diff --git a/media/codec2/core/include/C2Param.h b/media/codec2/core/include/C2Param.h
new file mode 100644
index 0000000..51d417a
--- /dev/null
+++ b/media/codec2/core/include/C2Param.h
@@ -0,0 +1,1687 @@
+/*
+ * 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.
+ */
+
+#ifndef C2PARAM_H_
+#define C2PARAM_H_
+
+#include <C2.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+/// \addtogroup Parameters
+/// @{
+
+/// \defgroup internal Internal helpers.
+
+/*!
+ * \file
+ * PARAMETERS: SETTINGs, TUNINGs, and INFOs
+ * ===
+ *
+ * These represent miscellaneous control and metadata information and are likely copied into
+ * kernel space. Therefore, these are C-like structures designed to carry just a small amount of
+ * information. We are using C++ to be able to add constructors, as well as non-virtual and class
+ * methods.
+ *
+ * ==Specification details:
+ *
+ * Restrictions:
+ *   - must be POD struct, e.g. no vtable (no virtual destructor)
+ *   - must have the same size in 64-bit and 32-bit mode (no size_t)
+ *   - as such, no pointer members
+ *   - some common member field names are reserved as they are defined as methods for all
+ *     parameters:
+ *     they are: size, type, kind, index and stream
+ *
+ * Behavior:
+ * - Params can be global (not related to input or output), related to input or output,
+ *   or related to an input/output stream.
+ * - All params are queried/set using a unique param index, which incorporates a potential stream
+ *   index and/or port.
+ * - Querying (supported) params MUST never fail.
+ * - All params MUST have default values.
+ * - If some fields have "unsupported" or "invalid" values during setting, this SHOULD be
+ *   communicated to the app.
+ *   a) Ideally, this should be avoided.  When setting parameters, in general, component should do
+ *     "best effort" to apply all settings. It should change "invalid/unsupported" values to the
+ *     nearest supported values.
+ *   - This is communicated to the client by changing the source values in tune()/
+ *     configure().
+ *   b) If falling back to a supported value is absolutely impossible, the component SHALL return
+ *     an error for the specific setting, but should continue to apply other settings.
+ *     TODO: this currently may result in unintended results.
+ *
+ * **NOTE:** unlike OMX, params are not versioned. Instead, a new struct with new param index
+ * SHALL be added as new versions are required.
+ *
+ * The proper subtype (Setting, Info or Param) is incorporated into the class type. Define structs
+ * to define multiple subtyped versions of related parameters.
+ *
+ * ==Implementation details:
+ *
+ * - Use macros to define parameters
+ * - All parameters must have a default constructor
+ *   - This is only used for instantiating the class in source (e.g. will not be used
+ *     when building a parameter by the framework from key/value pairs.)
+ */
+
+/// \ingroup internal
+
+/**
+ * Parameter base class.
+ */
+struct C2Param {
+    // param index encompasses the following:
+    //
+    // - kind (setting, tuning, info, struct)
+    // - scope
+    //   - direction (global, input, output)
+    //   - stream flag
+    //   - stream ID (usually 0)
+    // - and the parameter's type (core index)
+    //   - flexible parameter flag
+    //   - vendor extension flag
+    //   - type index (this includes the vendor extension flag)
+    //
+    // layout:
+    //
+    //        kind : <------- scope -------> : <----- core index ----->
+    //      +------+-----+---+------+--------+----|------+--------------+
+    //      | kind | dir | - |stream|streamID|flex|vendor|  type index  |
+    //      +------+-----+---+------+--------+----+------+--------------+
+    //  bit: 31..30 29.28       25   24 .. 17  16    15   14    ..     0
+    //
+public:
+    /**
+     * C2Param kinds, usable as bitmaps.
+     */
+    enum kind_t : uint32_t {
+        NONE    = 0,
+        STRUCT  = (1 << 0),
+        INFO    = (1 << 1),
+        SETTING = (1 << 2),
+        TUNING  = (1 << 3) | SETTING, // tunings are settings
+    };
+
+    /**
+     * The parameter type index specifies the underlying parameter type of a parameter as
+     * an integer value.
+     *
+     * Parameter types are divided into two groups: platform types and vendor types.
+     *
+     * Platform types are defined by the platform and are common for all implementations.
+     *
+     * Vendor types are defined by each vendors, so they may differ between implementations.
+     * It is recommended that vendor types be the same for all implementations by a specific
+     * vendor.
+     */
+    typedef uint32_t type_index_t;
+    enum : uint32_t {
+            TYPE_INDEX_VENDOR_START = 0x00008000, ///< vendor indices SHALL start after this
+    };
+
+    /**
+     * Core index is the underlying parameter type for a parameter. It is used to describe the
+     * layout of the parameter structure regardless of the component or parameter kind/scope.
+     *
+     * It is used to identify and distinguish global parameters, and also parameters on a given
+     * port or stream. They must be unique for the set of global parameters, as well as for the
+     * set of parameters on each port or each stream, but the same core index can be used for
+     * parameters on different streams or ports, as well as for global parameters and port/stream
+     * parameters.
+     *
+     * Multiple parameter types can share the same layout.
+     *
+     * \note The layout for all parameters with the same core index across all components must
+     * be identical.
+     */
+    struct CoreIndex {
+    //public:
+        enum : uint32_t {
+            IS_FLEX_FLAG    = 0x00010000,
+            IS_REQUEST_FLAG = 0x00020000,
+        };
+
+    protected:
+        enum : uint32_t {
+            KIND_MASK      = 0xC0000000,
+            KIND_STRUCT    = 0x00000000,
+            KIND_TUNING    = 0x40000000,
+            KIND_SETTING   = 0x80000000,
+            KIND_INFO      = 0xC0000000,
+
+            DIR_MASK       = 0x30000000,
+            DIR_GLOBAL     = 0x20000000,
+            DIR_UNDEFINED  = DIR_MASK, // MUST have all bits set
+            DIR_INPUT      = 0x00000000,
+            DIR_OUTPUT     = 0x10000000,
+
+            IS_STREAM_FLAG  = 0x02000000,
+            STREAM_ID_MASK  = 0x01F00000,
+            STREAM_ID_SHIFT = 20,
+            MAX_STREAM_ID   = STREAM_ID_MASK >> STREAM_ID_SHIFT,
+            STREAM_MASK     = IS_STREAM_FLAG | STREAM_ID_MASK,
+
+            IS_VENDOR_FLAG  = 0x00008000,
+            TYPE_INDEX_MASK = 0x0000FFFF,
+            CORE_MASK       = TYPE_INDEX_MASK | IS_FLEX_FLAG,
+        };
+
+    public:
+        /// constructor/conversion from uint32_t
+        inline CoreIndex(uint32_t index) : mIndex(index) { }
+
+        // no conversion from uint64_t
+        inline CoreIndex(uint64_t index) = delete;
+
+        /// returns true iff this is a vendor extension parameter
+        inline bool isVendor() const { return mIndex & IS_VENDOR_FLAG; }
+
+        /// returns true iff this is a flexible parameter (with variable size)
+        inline bool isFlexible() const { return mIndex & IS_FLEX_FLAG; }
+
+        /// returns the core index
+        /// This is the combination of the parameter type index and the flexible flag.
+        inline uint32_t coreIndex() const { return mIndex & CORE_MASK; }
+
+        /// returns the parameter type index
+        inline type_index_t typeIndex() const { return mIndex & TYPE_INDEX_MASK; }
+
+        DEFINE_FIELD_AND_MASK_BASED_COMPARISON_OPERATORS(CoreIndex, mIndex, CORE_MASK)
+
+    protected:
+        uint32_t mIndex;
+    };
+
+    /**
+     * Type encompasses the parameter's kind (tuning, setting, info), its scope (whether the
+     * parameter is global, input or output, and whether it is for a stream) and the its base
+     * index (which also determines its layout).
+     */
+    struct Type : public CoreIndex {
+    //public:
+        /// returns true iff this is a global parameter (not for input nor output)
+        inline bool isGlobal() const { return (mIndex & DIR_MASK) == DIR_GLOBAL; }
+        /// returns true iff this is an input or input stream parameter
+        inline bool forInput() const { return (mIndex & DIR_MASK) == DIR_INPUT; }
+        /// returns true iff this is an output or output stream parameter
+        inline bool forOutput() const { return (mIndex & DIR_MASK) == DIR_OUTPUT; }
+
+        /// returns true iff this is a stream parameter
+        inline bool forStream() const { return mIndex & IS_STREAM_FLAG; }
+        /// returns true iff this is a port (input or output) parameter
+        inline bool forPort() const   { return !forStream() && !isGlobal(); }
+
+        /// returns the parameter type: the parameter index without the stream ID
+        inline uint32_t type() const { return mIndex & (~STREAM_ID_MASK); }
+
+        /// return the kind (struct, info, setting or tuning) of this param
+        inline kind_t kind() const {
+            switch (mIndex & KIND_MASK) {
+                case KIND_STRUCT: return STRUCT;
+                case KIND_INFO: return INFO;
+                case KIND_SETTING: return SETTING;
+                case KIND_TUNING: return TUNING;
+                default: return NONE; // should not happen
+            }
+        }
+
+        /// constructor/conversion from uint32_t
+        inline Type(uint32_t index) : CoreIndex(index) { }
+
+        // no conversion from uint64_t
+        inline Type(uint64_t index) = delete;
+
+        DEFINE_FIELD_AND_MASK_BASED_COMPARISON_OPERATORS(Type, mIndex, ~STREAM_ID_MASK)
+
+    private:
+        friend struct C2Param;   // for setPort()
+        friend struct C2Tuning;  // for KIND_TUNING
+        friend struct C2Setting; // for KIND_SETTING
+        friend struct C2Info;    // for KIND_INFO
+        // for DIR_GLOBAL
+        template<typename T, typename S, int I, class F> friend struct C2GlobalParam;
+        template<typename T, typename S, int I, class F> friend struct C2PortParam;   // for kDir*
+        template<typename T, typename S, int I, class F> friend struct C2StreamParam; // for kDir*
+        friend struct _C2ParamInspector; // for testing
+
+        /**
+         * Sets the port/stream direction.
+         * @return true on success, false if could not set direction (e.g. it is global param).
+         */
+        inline bool setPort(bool output) {
+            if (isGlobal()) {
+                return false;
+            } else {
+                mIndex = (mIndex & ~DIR_MASK) | (output ? DIR_OUTPUT : DIR_INPUT);
+                return true;
+            }
+        }
+    };
+
+    /**
+     * index encompasses all remaining information: basically the stream ID.
+     */
+    struct Index : public Type {
+        /// returns the index as uint32_t
+        inline operator uint32_t() const { return mIndex; }
+
+        /// constructor/conversion from uint32_t
+        inline Index(uint32_t index) : Type(index) { }
+
+        /// copy constructor
+        inline Index(const Index &index) = default;
+
+        // no conversion from uint64_t
+        inline Index(uint64_t index) = delete;
+
+        /// returns the stream ID or ~0 if not a stream
+        inline unsigned stream() const {
+            return forStream() ? rawStream() : ~0U;
+        }
+
+        /// Returns an index with stream field set to given stream.
+        inline Index withStream(unsigned stream) const {
+            Index ix = mIndex;
+            (void)ix.setStream(stream);
+            return ix;
+        }
+
+        /// sets the port (direction). Returns true iff successful.
+        inline Index withPort(bool output) const {
+            Index ix = mIndex;
+            (void)ix.setPort(output);
+            return ix;
+        }
+
+        DEFINE_FIELD_BASED_COMPARISON_OPERATORS(Index, mIndex)
+
+    private:
+        friend struct C2Param;           // for setStream, MakeStreamId, isValid
+        friend struct _C2ParamInspector; // for testing
+
+        /**
+         * @return true if the type is valid, e.g. direction is not undefined AND
+         * stream is 0 if not a stream param.
+         */
+        inline bool isValid() const {
+            // there is no Type::isValid (even though some of this check could be
+            // performed on types) as this is only used on index...
+            return (forStream() ? rawStream() < MAX_STREAM_ID : rawStream() == 0)
+                    && (mIndex & DIR_MASK) != DIR_UNDEFINED;
+        }
+
+        /// returns the raw stream ID field
+        inline unsigned rawStream() const {
+            return (mIndex & STREAM_ID_MASK) >> STREAM_ID_SHIFT;
+        }
+
+        /// returns the streamId bitfield for a given |stream|. If stream is invalid,
+        /// returns an invalid bitfield.
+        inline static uint32_t MakeStreamId(unsigned stream) {
+            // saturate stream ID (max value is invalid)
+            if (stream > MAX_STREAM_ID) {
+                stream = MAX_STREAM_ID;
+            }
+            return (stream << STREAM_ID_SHIFT) & STREAM_ID_MASK;
+        }
+
+        inline bool convertToStream(bool output, unsigned stream) {
+            mIndex = (mIndex & ~DIR_MASK) | IS_STREAM_FLAG;
+            (void)setPort(output);
+            return setStream(stream);
+        }
+
+        inline void convertToPort(bool output) {
+            mIndex = (mIndex & ~(DIR_MASK | IS_STREAM_FLAG));
+            (void)setPort(output);
+        }
+
+        inline void convertToGlobal() {
+            mIndex = (mIndex & ~(DIR_MASK | IS_STREAM_FLAG)) | DIR_GLOBAL;
+        }
+
+        inline void convertToRequest() {
+            mIndex = mIndex | IS_REQUEST_FLAG;
+        }
+
+        /**
+         * Sets the stream index.
+         * \return true on success, false if could not set index (e.g. not a stream param).
+         */
+        inline bool setStream(unsigned stream) {
+            if (forStream()) {
+                mIndex = (mIndex & ~STREAM_ID_MASK) | MakeStreamId(stream);
+                return this->stream() < MAX_STREAM_ID;
+            }
+            return false;
+        }
+    };
+
+public:
+    // public getters for Index methods
+
+    /// returns true iff this is a vendor extension parameter
+    inline bool isVendor() const { return _mIndex.isVendor(); }
+    /// returns true iff this is a flexible parameter
+    inline bool isFlexible() const { return _mIndex.isFlexible(); }
+    /// returns true iff this is a global parameter (not for input nor output)
+    inline bool isGlobal() const { return _mIndex.isGlobal(); }
+    /// returns true iff this is an input or input stream parameter
+    inline bool forInput() const { return _mIndex.forInput(); }
+    /// returns true iff this is an output or output stream parameter
+    inline bool forOutput() const { return _mIndex.forOutput(); }
+
+    /// returns true iff this is a stream parameter
+    inline bool forStream() const { return _mIndex.forStream(); }
+    /// returns true iff this is a port (input or output) parameter
+    inline bool forPort() const   { return _mIndex.forPort(); }
+
+    /// returns the stream ID or ~0 if not a stream
+    inline unsigned stream() const { return _mIndex.stream(); }
+
+    /// returns the parameter type: the parameter index without the stream ID
+    inline Type type() const { return _mIndex.type(); }
+
+    /// returns the index of this parameter
+    /// \todo: should we restrict this to C2ParamField?
+    inline uint32_t index() const { return (uint32_t)_mIndex; }
+
+    /// returns the core index of this parameter
+    inline CoreIndex coreIndex() const { return _mIndex.coreIndex(); }
+
+    /// returns the kind of this parameter
+    inline kind_t kind() const { return _mIndex.kind(); }
+
+    /// returns the size of the parameter or 0 if the parameter is invalid
+    inline size_t size() const { return _mSize; }
+
+    /// returns true iff the parameter is valid
+    inline operator bool() const { return _mIndex.isValid() && _mSize > 0; }
+
+    /// returns true iff the parameter is invalid
+    inline bool operator!() const { return !operator bool(); }
+
+    // equality is done by memcmp (use equals() to prevent any overread)
+    inline bool operator==(const C2Param &o) const {
+        return equals(o) && memcmp(this, &o, _mSize) == 0;
+    }
+    inline bool operator!=(const C2Param &o) const { return !operator==(o); }
+
+    /// safe(r) type cast from pointer and size
+    inline static C2Param* From(void *addr, size_t len) {
+        // _mSize must fit into size, but really C2Param must also to be a valid param
+        if (len < sizeof(C2Param)) {
+            return nullptr;
+        }
+        // _mSize must match length
+        C2Param *param = (C2Param*)addr;
+        if (param->_mSize != len) {
+            return nullptr;
+        }
+        return param;
+    }
+
+    /// Returns managed clone of |orig| at heap.
+    inline static std::unique_ptr<C2Param> Copy(const C2Param &orig) {
+        if (orig.size() == 0) {
+            return nullptr;
+        }
+        void *mem = ::operator new (orig.size());
+        C2Param *param = new (mem) C2Param(orig.size(), orig._mIndex);
+        param->updateFrom(orig);
+        return std::unique_ptr<C2Param>(param);
+    }
+
+    /// Returns managed clone of |orig| as a stream parameter at heap.
+    inline static std::unique_ptr<C2Param> CopyAsStream(
+            const C2Param &orig, bool output, unsigned stream) {
+        std::unique_ptr<C2Param> copy = Copy(orig);
+        if (copy) {
+            copy->_mIndex.convertToStream(output, stream);
+        }
+        return copy;
+    }
+
+    /// Returns managed clone of |orig| as a port parameter at heap.
+    inline static std::unique_ptr<C2Param> CopyAsPort(const C2Param &orig, bool output) {
+        std::unique_ptr<C2Param> copy = Copy(orig);
+        if (copy) {
+            copy->_mIndex.convertToPort(output);
+        }
+        return copy;
+    }
+
+    /// Returns managed clone of |orig| as a global parameter at heap.
+    inline static std::unique_ptr<C2Param> CopyAsGlobal(const C2Param &orig) {
+        std::unique_ptr<C2Param> copy = Copy(orig);
+        if (copy) {
+            copy->_mIndex.convertToGlobal();
+        }
+        return copy;
+    }
+
+    /// Returns managed clone of |orig| as a stream parameter at heap.
+    inline static std::unique_ptr<C2Param> CopyAsRequest(const C2Param &orig) {
+        std::unique_ptr<C2Param> copy = Copy(orig);
+        if (copy) {
+            copy->_mIndex.convertToRequest();
+        }
+        return copy;
+    }
+
+#if 0
+    template<typename P, class=decltype(C2Param(P()))>
+    P *As() { return P::From(this); }
+    template<typename P>
+    const P *As() const { return const_cast<const P*>(P::From(const_cast<C2Param*>(this))); }
+#endif
+
+protected:
+    /// sets the stream field. Returns true iff successful.
+    inline bool setStream(unsigned stream) {
+        return _mIndex.setStream(stream);
+    }
+
+    /// sets the port (direction). Returns true iff successful.
+    inline bool setPort(bool output) {
+        return _mIndex.setPort(output);
+    }
+
+public:
+    /// invalidate this parameter. There is no recovery from this call; e.g. parameter
+    /// cannot be 'corrected' to be valid.
+    inline void invalidate() { _mSize = 0; }
+
+    // if other is the same kind of (valid) param as this, copy it into this and return true.
+    // otherwise, do not copy anything, and return false.
+    inline bool updateFrom(const C2Param &other) {
+        if (other._mSize <= _mSize && other._mIndex == _mIndex && _mSize > 0) {
+            memcpy(this, &other, other._mSize);
+            return true;
+        }
+        return false;
+    }
+
+protected:
+    // returns |o| if it is a null ptr, or if can suitably be a param of given |type| (e.g. has
+    // same type (ignoring stream ID), and size). Otherwise, returns null. If |checkDir| is false,
+    // allow undefined or different direction (e.g. as constructed from C2PortParam() vs.
+    // C2PortParam::input), but still require equivalent type (stream, port or global); otherwise,
+    // return null.
+    inline static const C2Param* IfSuitable(
+            const C2Param* o, size_t size, Type type, size_t flexSize = 0, bool checkDir = true) {
+        if (o == nullptr || o->_mSize < size || (flexSize && ((o->_mSize - size) % flexSize))) {
+            return nullptr;
+        } else if (checkDir) {
+            return o->_mIndex.type() == type.mIndex ? o : nullptr;
+        } else if (o->_mIndex.isGlobal()) {
+            return nullptr;
+        } else {
+            return ((o->_mIndex.type() ^ type.mIndex) & ~Type::DIR_MASK) ? nullptr : o;
+        }
+    }
+
+    /// base constructor
+    inline C2Param(uint32_t paramSize, Index paramIndex)
+        : _mSize(paramSize),
+          _mIndex(paramIndex) {
+        if (paramSize > sizeof(C2Param)) {
+            memset(this + 1, 0, paramSize - sizeof(C2Param));
+        }
+    }
+
+    /// base constructor with stream set
+    inline C2Param(uint32_t paramSize, Index paramIndex, unsigned stream)
+        : _mSize(paramSize),
+          _mIndex(paramIndex | Index::MakeStreamId(stream)) {
+        if (paramSize > sizeof(C2Param)) {
+            memset(this + 1, 0, paramSize - sizeof(C2Param));
+        }
+        if (!forStream()) {
+            invalidate();
+        }
+    }
+
+private:
+    friend struct _C2ParamInspector; // for testing
+
+    /// returns true iff |o| has the same size and index as this. This performs the
+    /// basic check for equality.
+    inline bool equals(const C2Param &o) const {
+        return _mSize == o._mSize && _mIndex == o._mIndex;
+    }
+
+    uint32_t _mSize;
+    Index _mIndex;
+};
+
+/// \ingroup internal
+/// allow C2Params access to private methods, e.g. constructors
+#define C2PARAM_MAKE_FRIENDS \
+    template<typename U, typename S, int I, class F> friend struct C2GlobalParam; \
+    template<typename U, typename S, int I, class F> friend struct C2PortParam; \
+    template<typename U, typename S, int I, class F> friend struct C2StreamParam; \
+
+/**
+ * Setting base structure for component method signatures. Wrap constructors.
+ */
+struct C2Setting : public C2Param {
+protected:
+    template<typename ...Args>
+    inline C2Setting(const Args(&... args)) : C2Param(args...) { }
+public: // TODO
+    enum : uint32_t { PARAM_KIND = Type::KIND_SETTING };
+};
+
+/**
+ * Tuning base structure for component method signatures. Wrap constructors.
+ */
+struct C2Tuning : public C2Setting {
+protected:
+    template<typename ...Args>
+    inline C2Tuning(const Args(&... args)) : C2Setting(args...) { }
+public: // TODO
+    enum : uint32_t { PARAM_KIND = Type::KIND_TUNING };
+};
+
+/**
+ * Info base structure for component method signatures. Wrap constructors.
+ */
+struct C2Info : public C2Param {
+protected:
+    template<typename ...Args>
+    inline C2Info(const Args(&... args)) : C2Param(args...) { }
+public: // TODO
+    enum : uint32_t { PARAM_KIND = Type::KIND_INFO };
+};
+
+/**
+ * Structure uniquely specifying a field in an arbitrary structure.
+ *
+ * \note This structure is used differently in C2FieldDescriptor to
+ * identify array fields, such that _mSize is the size of each element. This is
+ * because the field descriptor contains the array-length, and we want to keep
+ * a relevant element size for variable length arrays.
+ */
+struct _C2FieldId {
+//public:
+    /**
+     * Constructor used for C2FieldDescriptor that removes the array extent.
+     *
+     * \param[in] offset pointer to the field in an object at address 0.
+     */
+    template<typename T, class B=typename std::remove_extent<T>::type>
+    inline _C2FieldId(T* offset)
+        : // offset is from "0" so will fit on 32-bits
+          _mOffset((uint32_t)(uintptr_t)(offset)),
+          _mSize(sizeof(B)) { }
+
+    /**
+     * Direct constructor from offset and size.
+     *
+     * \param[in] offset offset of the field.
+     * \param[in] size size of the field.
+     */
+    inline _C2FieldId(size_t offset, size_t size)
+        : _mOffset(offset), _mSize(size) {}
+
+    /**
+     * Constructor used to identify a field in an object.
+     *
+     * \param U[type] pointer to the object that contains this field. This is needed in case the
+     *        field is in an (inherited) base class, in which case T will be that base class.
+     * \param pm[im] member pointer to the field
+     */
+    template<typename R, typename T, typename U, typename B=typename std::remove_extent<R>::type>
+    inline _C2FieldId(U *, R T::* pm)
+        : _mOffset((uint32_t)(uintptr_t)(&(((U*)256)->*pm)) - 256u),
+          _mSize(sizeof(B)) { }
+
+    /**
+     * Constructor used to identify a field in an object.
+     *
+     * \param pm[im] member pointer to the field
+     */
+    template<typename R, typename T, typename B=typename std::remove_extent<R>::type>
+    inline _C2FieldId(R T::* pm)
+        : _mOffset((uint32_t)(uintptr_t)(&(((T*)0)->*pm))),
+          _mSize(sizeof(B)) { }
+
+    inline bool operator==(const _C2FieldId &other) const {
+        return _mOffset == other._mOffset && _mSize == other._mSize;
+    }
+
+    inline bool operator<(const _C2FieldId &other) const {
+        return _mOffset < other._mOffset ||
+            // NOTE: order parent structure before sub field
+            (_mOffset == other._mOffset && _mSize > other._mSize);
+    }
+
+    DEFINE_OTHER_COMPARISON_OPERATORS(_C2FieldId)
+
+#if 0
+    inline uint32_t offset() const { return _mOffset; }
+    inline uint32_t size() const { return _mSize; }
+#endif
+
+#if defined(FRIEND_TEST)
+    friend void PrintTo(const _C2FieldId &d, ::std::ostream*);
+#endif
+
+private:
+    friend struct _C2ParamInspector;
+    friend struct C2FieldDescriptor;
+
+    uint32_t _mOffset; // offset of field
+    uint32_t _mSize;   // size of field
+};
+
+/**
+ * Structure uniquely specifying a 'field' in a configuration. The field
+ * can be a field of a configuration, a subfield of a field of a configuration,
+ * and even the whole configuration. Moreover, if the field can point to an
+ * element in a array field, or to the entire array field.
+ *
+ * This structure is used for querying supported values for a field, as well
+ * as communicating configuration failures and conflicts when trying to change
+ * a configuration for a component/interface or a store.
+ */
+struct C2ParamField {
+//public:
+    /**
+     * Create a field identifier using a configuration parameter (variable),
+     * and a pointer to member.
+     *
+     * ~~~~~~~~~~~~~ (.cpp)
+     *
+     * struct C2SomeParam {
+     *   uint32_t mField;
+     *   uint32_t mArray[2];
+     *   C2OtherStruct mStruct;
+     *   uint32_t mFlexArray[];
+     * } *mParam;
+     *
+     * C2ParamField(mParam, &mParam->mField);
+     * C2ParamField(mParam, &mParam->mArray);
+     * C2ParamField(mParam, &mParam->mArray[0]);
+     * C2ParamField(mParam, &mParam->mStruct.mSubField);
+     * C2ParamField(mParam, &mParam->mFlexArray);
+     * C2ParamField(mParam, &mParam->mFlexArray[2]);
+     *
+     * ~~~~~~~~~~~~~
+     *
+     * \todo fix what this is for T[] (for now size becomes T[1])
+     *
+     * \note this does not work for 64-bit members as it triggers a
+     * 'taking address of packed member' warning.
+     *
+     * \param param pointer to parameter
+     * \param offset member pointer
+     */
+    template<typename S, typename T>
+    inline C2ParamField(S* param, T* offset)
+        : _mIndex(param->index()),
+          _mFieldId((T*)((uintptr_t)offset - (uintptr_t)param)) {}
+
+    template<typename S, typename T>
+    inline static C2ParamField Make(S& param, T& offset) {
+        return C2ParamField(param.index(), (uintptr_t)&offset - (uintptr_t)&param, sizeof(T));
+    }
+
+    /**
+     * Create a field identifier using a configuration parameter (variable),
+     * and a member pointer. This method cannot be used to refer to an
+     * array element or a subfield.
+     *
+     * ~~~~~~~~~~~~~ (.cpp)
+     *
+     * C2SomeParam mParam;
+     * C2ParamField(&mParam, &C2SomeParam::mMemberField);
+     *
+     * ~~~~~~~~~~~~~
+     *
+     * \param p pointer to parameter
+     * \param T member pointer to the field member
+     */
+    template<typename R, typename T, typename U>
+    inline C2ParamField(U *p, R T::* pm) : _mIndex(p->index()), _mFieldId(p, pm) { }
+
+    /**
+     * Create a field identifier to a configuration parameter (variable).
+     *
+     * ~~~~~~~~~~~~~ (.cpp)
+     *
+     * C2SomeParam mParam;
+     * C2ParamField(&mParam);
+     *
+     * ~~~~~~~~~~~~~
+     *
+     * \param param pointer to parameter
+     */
+    template<typename S>
+    inline C2ParamField(S* param)
+        : _mIndex(param->index()), _mFieldId(0u, param->size()) { }
+
+    /** Copy constructor. */
+    inline C2ParamField(const C2ParamField &other) = default;
+
+    /**
+     * Equality operator.
+     */
+    inline bool operator==(const C2ParamField &other) const {
+        return _mIndex == other._mIndex && _mFieldId == other._mFieldId;
+    }
+
+    /**
+     * Ordering operator.
+     */
+    inline bool operator<(const C2ParamField &other) const {
+        return _mIndex < other._mIndex ||
+            (_mIndex == other._mIndex && _mFieldId < other._mFieldId);
+    }
+
+    DEFINE_OTHER_COMPARISON_OPERATORS(C2ParamField)
+
+protected:
+    inline C2ParamField(C2Param::Index index, uint32_t offset, uint32_t size)
+        : _mIndex(index), _mFieldId(offset, size) {}
+
+private:
+    friend struct _C2ParamInspector;
+
+    C2Param::Index _mIndex; ///< parameter index
+    _C2FieldId _mFieldId;   ///< field identifier
+};
+
+/**
+ * A shared (union) representation of numeric values
+ */
+class C2Value {
+public:
+    /// A union of supported primitive types.
+    union Primitive {
+        // first member is always zero initialized so it must be the largest
+        uint64_t    u64;   ///< uint64_t value
+        int64_t     i64;   ///< int64_t value
+        c2_cntr64_t c64;   ///< c2_cntr64_t value
+        uint32_t    u32;   ///< uint32_t value
+        int32_t     i32;   ///< int32_t value
+        c2_cntr32_t c32;   ///< c2_cntr32_t value
+        float       fp;    ///< float value
+
+        // constructors - implicit
+        Primitive(uint64_t value)    : u64(value) { }
+        Primitive(int64_t value)     : i64(value) { }
+        Primitive(c2_cntr64_t value) : c64(value) { }
+        Primitive(uint32_t value)    : u32(value) { }
+        Primitive(int32_t value)     : i32(value) { }
+        Primitive(c2_cntr32_t value) : c32(value) { }
+        Primitive(uint8_t value)     : u32(value) { }
+        Primitive(char value)        : i32(value) { }
+        Primitive(float value)       : fp(value)  { }
+
+        // allow construction from enum type
+        template<typename E, typename = typename std::enable_if<std::is_enum<E>::value>::type>
+        Primitive(E value)
+            : Primitive(static_cast<typename std::underlying_type<E>::type>(value)) { }
+
+        Primitive() : u64(0) { }
+
+        /** gets value out of the union */
+        template<typename T> const T &ref() const;
+
+        // verify that we can assume standard aliasing
+        static_assert(sizeof(u64) == sizeof(i64), "");
+        static_assert(sizeof(u64) == sizeof(c64), "");
+        static_assert(sizeof(u32) == sizeof(i32), "");
+        static_assert(sizeof(u32) == sizeof(c32), "");
+    };
+    // verify that we can assume standard aliasing
+    static_assert(offsetof(Primitive, u64) == offsetof(Primitive, i64), "");
+    static_assert(offsetof(Primitive, u64) == offsetof(Primitive, c64), "");
+    static_assert(offsetof(Primitive, u32) == offsetof(Primitive, i32), "");
+    static_assert(offsetof(Primitive, u32) == offsetof(Primitive, c32), "");
+
+    enum type_t : uint32_t {
+        NO_INIT,
+        INT32,
+        UINT32,
+        CNTR32,
+        INT64,
+        UINT64,
+        CNTR64,
+        FLOAT,
+    };
+
+    template<typename T, bool = std::is_enum<T>::value>
+    inline static constexpr type_t TypeFor() {
+        using U = typename std::underlying_type<T>::type;
+        return TypeFor<U>();
+    }
+
+    // deprectated
+    template<typename T, bool B = std::is_enum<T>::value>
+    inline static constexpr type_t typeFor() {
+        return TypeFor<T, B>();
+    }
+
+    // constructors - implicit
+    template<typename T>
+    C2Value(T value)  : _mType(typeFor<T>()), _mValue(value) { }
+
+    C2Value() : _mType(NO_INIT) { }
+
+    inline type_t type() const { return _mType; }
+
+    template<typename T>
+    inline bool get(T *value) const {
+        if (_mType == typeFor<T>()) {
+            *value = _mValue.ref<T>();
+            return true;
+        }
+        return false;
+    }
+
+    /// returns the address of the value
+    void *get() const {
+        return _mType == NO_INIT ? nullptr : (void*)&_mValue;
+    }
+
+    /// returns the size of the contained value
+    size_t inline sizeOf() const {
+        return SizeFor(_mType);
+    }
+
+    static size_t SizeFor(type_t type) {
+        switch (type) {
+            case INT32:
+            case UINT32:
+            case CNTR32: return sizeof(_mValue.i32);
+            case INT64:
+            case UINT64:
+            case CNTR64: return sizeof(_mValue.i64);
+            case FLOAT: return sizeof(_mValue.fp);
+            default: return 0;
+        }
+    }
+
+private:
+    type_t _mType;
+    Primitive _mValue;
+};
+
+template<> inline const int32_t &C2Value::Primitive::ref<int32_t>() const { return i32; }
+template<> inline const int64_t &C2Value::Primitive::ref<int64_t>() const { return i64; }
+template<> inline const uint32_t &C2Value::Primitive::ref<uint32_t>() const { return u32; }
+template<> inline const uint64_t &C2Value::Primitive::ref<uint64_t>() const { return u64; }
+template<> inline const c2_cntr32_t &C2Value::Primitive::ref<c2_cntr32_t>() const { return c32; }
+template<> inline const c2_cntr64_t &C2Value::Primitive::ref<c2_cntr64_t>() const { return c64; }
+template<> inline const float &C2Value::Primitive::ref<float>() const { return fp; }
+
+// provide types for enums and uint8_t, char even though we don't provide reading as them
+template<> constexpr C2Value::type_t C2Value::TypeFor<char, false>() { return INT32; }
+template<> constexpr C2Value::type_t C2Value::TypeFor<int32_t, false>() { return INT32; }
+template<> constexpr C2Value::type_t C2Value::TypeFor<int64_t, false>() { return INT64; }
+template<> constexpr C2Value::type_t C2Value::TypeFor<uint8_t, false>() { return UINT32; }
+template<> constexpr C2Value::type_t C2Value::TypeFor<uint32_t, false>() { return UINT32; }
+template<> constexpr C2Value::type_t C2Value::TypeFor<uint64_t, false>() { return UINT64; }
+template<> constexpr C2Value::type_t C2Value::TypeFor<c2_cntr32_t, false>() { return CNTR32; }
+template<> constexpr C2Value::type_t C2Value::TypeFor<c2_cntr64_t, false>() { return CNTR64; }
+template<> constexpr C2Value::type_t C2Value::TypeFor<float, false>() { return FLOAT; }
+
+// forward declare easy enum template
+template<typename E> struct C2EasyEnum;
+
+/**
+ * field descriptor. A field is uniquely defined by an index into a parameter.
+ * (Note: Stream-id is not captured as a field.)
+ *
+ * Ordering of fields is by offset. In case of structures, it is depth first,
+ * with a structure taking an index just before and in addition to its members.
+ */
+struct C2FieldDescriptor {
+//public:
+    /** field types and flags
+     * \note: only 32-bit and 64-bit fields are supported (e.g. no boolean, as that
+     * is represented using INT32).
+     */
+    enum type_t : uint32_t {
+        // primitive types
+        INT32   = C2Value::INT32,  ///< 32-bit signed integer
+        UINT32  = C2Value::UINT32, ///< 32-bit unsigned integer
+        CNTR32  = C2Value::CNTR32, ///< 32-bit counter
+        INT64   = C2Value::INT64,  ///< 64-bit signed integer
+        UINT64  = C2Value::UINT64, ///< 64-bit signed integer
+        CNTR64  = C2Value::CNTR64, ///< 64-bit counter
+        FLOAT   = C2Value::FLOAT,  ///< 32-bit floating point
+
+        // array types
+        STRING = 0x100, ///< fixed-size string (POD)
+        BLOB,           ///< blob. Blobs have no sub-elements and can be thought of as byte arrays;
+                        ///< however, bytes cannot be individually addressed by clients.
+
+        // complex types
+        STRUCT_FLAG = 0x20000, ///< structs. Marked with this flag in addition to their coreIndex.
+    };
+
+    typedef std::pair<C2String, C2Value::Primitive> NamedValueType;
+    typedef std::vector<NamedValueType> NamedValuesType;
+    //typedef std::pair<std::vector<C2String>, std::vector<C2Value::Primitive>> NamedValuesType;
+
+    /**
+     * Template specialization that returns the named values for a type.
+     *
+     * \todo hide from client.
+     *
+     * \return a vector of name-value pairs.
+     */
+    template<typename B>
+    static NamedValuesType namedValuesFor(const B &);
+
+    /** specialization for easy enums */
+    template<typename E>
+    inline static NamedValuesType namedValuesFor(const C2EasyEnum<E> &) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnull-dereference"
+        return namedValuesFor(*(E*)nullptr);
+#pragma GCC diagnostic pop
+    }
+
+private:
+    template<typename B, bool enabled=std::is_arithmetic<B>::value || std::is_enum<B>::value>
+    struct C2_HIDE _NamedValuesGetter;
+
+public:
+    inline C2FieldDescriptor(uint32_t type, uint32_t extent, C2String name, size_t offset, size_t size)
+        : _mType((type_t)type), _mExtent(extent), _mName(name), _mFieldId(offset, size) { }
+
+    inline C2FieldDescriptor(const C2FieldDescriptor &) = default;
+
+    template<typename T, class B=typename std::remove_extent<T>::type>
+    inline C2FieldDescriptor(const T* offset, const char *name)
+        : _mType(this->GetType((B*)nullptr)),
+          _mExtent(std::is_array<T>::value ? std::extent<T>::value : 1),
+          _mName(name),
+          _mNamedValues(_NamedValuesGetter<B>::getNamedValues()),
+          _mFieldId(offset) {}
+
+    /// \deprecated
+    template<typename T, typename S, class B=typename std::remove_extent<T>::type>
+    inline C2FieldDescriptor(S*, T S::* field, const char *name)
+        : _mType(this->GetType((B*)nullptr)),
+          _mExtent(std::is_array<T>::value ? std::extent<T>::value : 1),
+          _mName(name),
+          _mFieldId(&(((S*)0)->*field)) {}
+
+    /// returns the type of this field
+    inline type_t type() const { return _mType; }
+    /// returns the length of the field in case it is an array. Returns 0 for
+    /// T[] arrays, returns 1 for T[1] arrays as well as if the field is not an array.
+    inline size_t extent() const { return _mExtent; }
+    /// returns the name of the field
+    inline C2String name() const { return _mName; }
+
+    const NamedValuesType &namedValues() const { return _mNamedValues; }
+
+#if defined(FRIEND_TEST)
+    friend void PrintTo(const C2FieldDescriptor &, ::std::ostream*);
+    friend bool operator==(const C2FieldDescriptor &, const C2FieldDescriptor &);
+    FRIEND_TEST(C2ParamTest_ParamFieldList, VerifyStruct);
+#endif
+
+private:
+    /**
+     * Construct an offseted field descriptor.
+     */
+    inline C2FieldDescriptor(const C2FieldDescriptor &desc, size_t offset)
+        : _mType(desc._mType), _mExtent(desc._mExtent),
+          _mName(desc._mName), _mNamedValues(desc._mNamedValues),
+          _mFieldId(desc._mFieldId._mOffset + offset, desc._mFieldId._mSize) { }
+
+    type_t _mType;
+    uint32_t _mExtent; // the last member can be arbitrary length if it is T[] array,
+                       // extending to the end of the parameter (this is marked with
+                       // 0). T[0]-s are not fields.
+    C2String _mName;
+    NamedValuesType _mNamedValues;
+
+    _C2FieldId _mFieldId;   // field identifier (offset and size)
+
+    // NOTE: We do not capture default value(s) here as that may depend on the component.
+    // NOTE: We also do not capture bestEffort, as 1) this should be true for most fields,
+    // 2) this is at parameter granularity.
+
+    // type resolution
+    inline static type_t GetType(int32_t*)     { return INT32; }
+    inline static type_t GetType(uint32_t*)    { return UINT32; }
+    inline static type_t GetType(c2_cntr32_t*) { return CNTR32; }
+    inline static type_t GetType(int64_t*)     { return INT64; }
+    inline static type_t GetType(uint64_t*)    { return UINT64; }
+    inline static type_t GetType(c2_cntr64_t*) { return CNTR64; }
+    inline static type_t GetType(float*)       { return FLOAT; }
+    inline static type_t GetType(char*)        { return STRING; }
+    inline static type_t GetType(uint8_t*)     { return BLOB; }
+
+    template<typename T,
+             class=typename std::enable_if<std::is_enum<T>::value>::type>
+    inline static type_t GetType(T*) {
+        typename std::underlying_type<T>::type underlying(0);
+        return GetType(&underlying);
+    }
+
+    // verify C2Struct by having a FieldList() and a CORE_INDEX.
+    template<typename T,
+             class=decltype(T::CORE_INDEX + 1), class=decltype(T::FieldList())>
+    inline static type_t GetType(T*) {
+        static_assert(!std::is_base_of<C2Param, T>::value, "cannot use C2Params as fields");
+        return (type_t)(T::CORE_INDEX | STRUCT_FLAG);
+    }
+
+    friend struct _C2ParamInspector;
+};
+
+// no named values for compound types
+template<typename B>
+struct C2FieldDescriptor::_NamedValuesGetter<B, false> {
+    inline static C2FieldDescriptor::NamedValuesType getNamedValues() {
+        return NamedValuesType();
+    }
+};
+
+template<typename B>
+struct C2FieldDescriptor::_NamedValuesGetter<B, true> {
+    inline static C2FieldDescriptor::NamedValuesType getNamedValues() {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnull-dereference"
+        return C2FieldDescriptor::namedValuesFor(*(B*)nullptr);
+#pragma GCC diagnostic pop
+    }
+};
+
+#define DEFINE_NO_NAMED_VALUES_FOR(type) \
+template<> inline C2FieldDescriptor::NamedValuesType C2FieldDescriptor::namedValuesFor(const type &) { \
+    return NamedValuesType(); \
+}
+
+// We cannot subtype constructor for enumerated types so insted define no named values for
+// non-enumerated integral types.
+DEFINE_NO_NAMED_VALUES_FOR(int32_t)
+DEFINE_NO_NAMED_VALUES_FOR(uint32_t)
+DEFINE_NO_NAMED_VALUES_FOR(c2_cntr32_t)
+DEFINE_NO_NAMED_VALUES_FOR(int64_t)
+DEFINE_NO_NAMED_VALUES_FOR(uint64_t)
+DEFINE_NO_NAMED_VALUES_FOR(c2_cntr64_t)
+DEFINE_NO_NAMED_VALUES_FOR(uint8_t)
+DEFINE_NO_NAMED_VALUES_FOR(char)
+DEFINE_NO_NAMED_VALUES_FOR(float)
+
+/**
+ * Describes the fields of a structure.
+ */
+struct C2StructDescriptor {
+public:
+    /// Returns the core index of the struct
+    inline C2Param::CoreIndex coreIndex() const { return _mType.coreIndex(); }
+
+    // Returns the number of fields in this struct (not counting any recursive fields).
+    // Must be at least 1 for valid structs.
+    inline size_t numFields() const { return _mFields.size(); }
+
+    // Returns the list of direct fields (not counting any recursive fields).
+    typedef std::vector<C2FieldDescriptor>::const_iterator field_iterator;
+    inline field_iterator cbegin() const { return _mFields.cbegin(); }
+    inline field_iterator cend() const { return _mFields.cend(); }
+
+    // only supplying const iterator - but these names are needed for range based loops
+    inline field_iterator begin() const { return _mFields.cbegin(); }
+    inline field_iterator end() const { return _mFields.cend(); }
+
+    template<typename T>
+    inline C2StructDescriptor(T*)
+        : C2StructDescriptor(T::CORE_INDEX, T::FieldList()) { }
+
+    inline C2StructDescriptor(
+            C2Param::CoreIndex type,
+            const std::vector<C2FieldDescriptor> &fields)
+        : _mType(type), _mFields(fields) { }
+
+private:
+    friend struct _C2ParamInspector;
+
+    inline C2StructDescriptor(
+            C2Param::CoreIndex type,
+            std::vector<C2FieldDescriptor> &&fields)
+        : _mType(type), _mFields(std::move(fields)) { }
+
+    const C2Param::CoreIndex _mType;
+    const std::vector<C2FieldDescriptor> _mFields;
+};
+
+/**
+ * Describes parameters for a component.
+ */
+struct C2ParamDescriptor {
+public:
+    /**
+     * Returns whether setting this param is required to configure this component.
+     * This can only be true for builtin params for platform-defined components (e.g. video and
+     * audio encoders/decoders, video/audio filters).
+     * For vendor-defined components, it can be true even for vendor-defined params,
+     * but it is not recommended, in case the component becomes platform-defined.
+     */
+    inline bool isRequired() const { return _mAttrib & IS_REQUIRED; }
+
+    /**
+     * Returns whether this parameter is persistent. This is always true for C2Tuning and C2Setting,
+     * but may be false for C2Info. If true, this parameter persists across frames and applies to
+     * the current and subsequent frames. If false, this C2Info parameter only applies to the
+     * current frame and is not assumed to have the same value (or even be present) on subsequent
+     * frames, unless it is specified for those frames.
+     */
+    inline bool isPersistent() const { return _mAttrib & IS_PERSISTENT; }
+
+    inline bool isStrict() const { return _mAttrib & IS_STRICT; }
+
+    inline bool isReadOnly() const { return _mAttrib & IS_READ_ONLY; }
+
+    inline bool isVisible() const { return !(_mAttrib & IS_HIDDEN); }
+
+    inline bool isPublic() const { return !(_mAttrib & IS_INTERNAL); }
+
+    /// Returns the name of this param.
+    /// This defaults to the underlying C2Struct's name, but could be altered for a component.
+    inline C2String name() const { return _mName; }
+
+    /// Returns the parameter index
+    inline C2Param::Index index() const { return _mIndex; }
+
+    /// Returns the indices of parameters that this parameter has a dependency on
+    inline const std::vector<C2Param::Index> &dependencies() const { return _mDependencies; }
+
+    /// \deprecated
+    template<typename T>
+    inline C2ParamDescriptor(bool isRequired, C2StringLiteral name, const T*)
+        : _mIndex(T::PARAM_TYPE),
+          _mAttrib(IS_PERSISTENT | (isRequired ? IS_REQUIRED : 0)),
+          _mName(name) { }
+
+    /// \deprecated
+    inline C2ParamDescriptor(
+            bool isRequired, C2StringLiteral name, C2Param::Index index)
+        : _mIndex(index),
+          _mAttrib(IS_PERSISTENT | (isRequired ? IS_REQUIRED : 0)),
+          _mName(name) { }
+
+    enum attrib_t : uint32_t {
+        // flags that default on
+        IS_REQUIRED   = 1u << 0, ///< parameter is required to be specified
+        IS_PERSISTENT = 1u << 1, ///< parameter retains its value
+        // flags that default off
+        IS_STRICT     = 1u << 2, ///< parameter is strict
+        IS_READ_ONLY  = 1u << 3, ///< parameter is publicly read-only
+        IS_HIDDEN     = 1u << 4, ///< parameter shall not be visible to clients
+        IS_INTERNAL   = 1u << 5, ///< parameter shall not be used by framework (other than testing)
+        IS_CONST      = 1u << 6 | IS_READ_ONLY, ///< parameter is publicly const (hence read-only)
+    };
+
+    inline C2ParamDescriptor(
+        C2Param::Index index, attrib_t attrib, C2StringLiteral name)
+        : _mIndex(index),
+          _mAttrib(attrib),
+          _mName(name) { }
+
+    inline C2ParamDescriptor(
+        C2Param::Index index, attrib_t attrib, C2String &&name,
+        std::vector<C2Param::Index> &&dependencies)
+        : _mIndex(index),
+          _mAttrib(attrib),
+          _mName(name),
+          _mDependencies(std::move(dependencies)) { }
+
+private:
+    const C2Param::Index _mIndex;
+    const uint32_t _mAttrib;
+    const C2String _mName;
+    std::vector<C2Param::Index> _mDependencies;
+
+    friend struct _C2ParamInspector;
+};
+
+DEFINE_ENUM_OPERATORS(::C2ParamDescriptor::attrib_t)
+
+
+/// \ingroup internal
+/// Define a structure without CORE_INDEX.
+/// \note _FIELD_LIST is used only during declaration so that C2Struct declarations can end with
+/// a simple list of C2FIELD-s and closing bracket. Mark it unused as it is not used in templated
+/// structs.
+#define DEFINE_BASE_C2STRUCT(name) \
+private: \
+    const static std::vector<C2FieldDescriptor> _FIELD_LIST __unused; /**< structure fields */ \
+public: \
+    typedef C2##name##Struct _type; /**< type name shorthand */ \
+    static const std::vector<C2FieldDescriptor> FieldList(); /**< structure fields factory */
+
+/// Define a structure with matching CORE_INDEX.
+#define DEFINE_C2STRUCT(name) \
+public: \
+    enum : uint32_t { CORE_INDEX = kParamIndex##name }; \
+    DEFINE_BASE_C2STRUCT(name)
+
+/// Define a flexible structure without CORE_INDEX.
+#define DEFINE_BASE_FLEX_C2STRUCT(name, flexMember) \
+public: \
+    FLEX(C2##name##Struct, flexMember) \
+    DEFINE_BASE_C2STRUCT(name)
+
+/// Define a flexible structure with matching CORE_INDEX.
+#define DEFINE_FLEX_C2STRUCT(name, flexMember) \
+public: \
+    FLEX(C2##name##Struct, flexMember) \
+    enum : uint32_t { CORE_INDEX = kParamIndex##name | C2Param::CoreIndex::IS_FLEX_FLAG }; \
+    DEFINE_BASE_C2STRUCT(name)
+
+/// \ingroup internal
+/// Describe a structure of a templated structure.
+// Use list... as the argument gets resubsitituted and it contains commas. Alternative would be
+// to wrap list in an expression, e.g. ({ std::vector<C2FieldDescriptor> list; })) which converts
+// it from an initializer list to a vector.
+#define DESCRIBE_TEMPLATED_C2STRUCT(strukt, list...) \
+    _DESCRIBE_TEMPLATABLE_C2STRUCT(template<>, strukt, __C2_GENERATE_GLOBAL_VARS__, list)
+
+/// \deprecated
+/// Describe the fields of a structure using an initializer list.
+#define DESCRIBE_C2STRUCT(name, list...) \
+    _DESCRIBE_TEMPLATABLE_C2STRUCT(, C2##name##Struct, __C2_GENERATE_GLOBAL_VARS__, list)
+
+/// \ingroup internal
+/// Macro layer to get value of enabled that is passed in as a macro variable
+#define _DESCRIBE_TEMPLATABLE_C2STRUCT(template, strukt, enabled, list...) \
+    __DESCRIBE_TEMPLATABLE_C2STRUCT(template, strukt, enabled, list)
+
+/// \ingroup internal
+/// Macro layer to resolve to the specific macro based on macro variable
+#define __DESCRIBE_TEMPLATABLE_C2STRUCT(template, strukt, enabled, list...) \
+    ___DESCRIBE_TEMPLATABLE_C2STRUCT##enabled(template, strukt, list)
+
+#define ___DESCRIBE_TEMPLATABLE_C2STRUCT(template, strukt, list...) \
+    template \
+    const std::vector<C2FieldDescriptor> strukt::FieldList() { return list; }
+
+#define ___DESCRIBE_TEMPLATABLE_C2STRUCT__C2_GENERATE_GLOBAL_VARS__(template, strukt, list...)
+
+/**
+ * Describe a field of a structure.
+ * These must be in order.
+ *
+ * There are two ways to use this macro:
+ *
+ *  ~~~~~~~~~~~~~ (.cpp)
+ *  struct C2VideoWidthStruct {
+ *      int32_t width;
+ *      C2VideoWidthStruct() {} // optional default constructor
+ *      C2VideoWidthStruct(int32_t _width) : width(_width) {}
+ *
+ *      DEFINE_AND_DESCRIBE_C2STRUCT(VideoWidth)
+ *      C2FIELD(width, "width")
+ *  };
+ *  ~~~~~~~~~~~~~
+ *
+ *  ~~~~~~~~~~~~~ (.cpp)
+ *  struct C2VideoWidthStruct {
+ *      int32_t width;
+ *      C2VideoWidthStruct() = default; // optional default constructor
+ *      C2VideoWidthStruct(int32_t _width) : width(_width) {}
+ *
+ *      DEFINE_C2STRUCT(VideoWidth)
+ *  } C2_PACK;
+ *
+ *  DESCRIBE_C2STRUCT(VideoWidth, {
+ *      C2FIELD(width, "width")
+ *  })
+ *  ~~~~~~~~~~~~~
+ *
+ *  For flexible structures (those ending in T[]), use the flexible macros:
+ *
+ *  ~~~~~~~~~~~~~ (.cpp)
+ *  struct C2VideoFlexWidthsStruct {
+ *      int32_t widths[];
+ *      C2VideoFlexWidthsStruct(); // must have a default constructor
+ *
+ *  private:
+ *      // may have private constructors taking number of widths as the first argument
+ *      // This is used by the C2Param factory methods, e.g.
+ *      //   C2VideoFlexWidthsGlobalParam::AllocUnique(size_t, int32_t);
+ *      C2VideoFlexWidthsStruct(size_t flexCount, int32_t value) {
+ *          for (size_t i = 0; i < flexCount; ++i) {
+ *              widths[i] = value;
+ *          }
+ *      }
+ *
+ *      // If the last argument is T[N] or std::initializer_list<T>, the flexCount will
+ *      // be automatically calculated and passed by the C2Param factory methods, e.g.
+ *      //   int widths[] = { 1, 2, 3 };
+ *      //   C2VideoFlexWidthsGlobalParam::AllocUnique(widths);
+ *      template<unsigned N>
+ *      C2VideoFlexWidthsStruct(size_t flexCount, const int32_t(&init)[N]) {
+ *          for (size_t i = 0; i < flexCount; ++i) {
+ *              widths[i] = init[i];
+ *          }
+ *      }
+ *
+ *      DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(VideoFlexWidths, widths)
+ *      C2FIELD(widths, "widths")
+ *  };
+ *  ~~~~~~~~~~~~~
+ *
+ *  ~~~~~~~~~~~~~ (.cpp)
+ *  struct C2VideoFlexWidthsStruct {
+ *      int32_t mWidths[];
+ *      C2VideoFlexWidthsStruct(); // must have a default constructor
+ *
+ *      DEFINE_FLEX_C2STRUCT(VideoFlexWidths, mWidths)
+ *  } C2_PACK;
+ *
+ *  DESCRIBE_C2STRUCT(VideoFlexWidths, {
+ *      C2FIELD(mWidths, "widths")
+ *  })
+ *  ~~~~~~~~~~~~~
+ *
+ */
+#define DESCRIBE_C2FIELD(member, name) \
+  C2FieldDescriptor(&((_type*)(nullptr))->member, name),
+
+#define C2FIELD(member, name) _C2FIELD(member, name, __C2_GENERATE_GLOBAL_VARS__)
+/// \if 0
+#define _C2FIELD(member, name, enabled) __C2FIELD(member, name, enabled)
+#define __C2FIELD(member, name, enabled) DESCRIBE_C2FIELD##enabled(member, name)
+#define DESCRIBE_C2FIELD__C2_GENERATE_GLOBAL_VARS__(member, name)
+/// \endif
+
+/// Define a structure with matching CORE_INDEX and start describing its fields.
+/// This must be at the end of the structure definition.
+#define DEFINE_AND_DESCRIBE_C2STRUCT(name) \
+    _DEFINE_AND_DESCRIBE_C2STRUCT(name, DEFINE_C2STRUCT, __C2_GENERATE_GLOBAL_VARS__)
+
+/// Define a base structure (with no CORE_INDEX) and start describing its fields.
+/// This must be at the end of the structure definition.
+#define DEFINE_AND_DESCRIBE_BASE_C2STRUCT(name) \
+    _DEFINE_AND_DESCRIBE_C2STRUCT(name, DEFINE_BASE_C2STRUCT, __C2_GENERATE_GLOBAL_VARS__)
+
+/// Define a flexible structure with matching CORE_INDEX and start describing its fields.
+/// This must be at the end of the structure definition.
+#define DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(name, flexMember) \
+    _DEFINE_AND_DESCRIBE_FLEX_C2STRUCT( \
+            name, flexMember, DEFINE_FLEX_C2STRUCT, __C2_GENERATE_GLOBAL_VARS__)
+
+/// Define a flexible base structure (with no CORE_INDEX) and start describing its fields.
+/// This must be at the end of the structure definition.
+#define DEFINE_AND_DESCRIBE_BASE_FLEX_C2STRUCT(name, flexMember) \
+    _DEFINE_AND_DESCRIBE_FLEX_C2STRUCT( \
+            name, flexMember, DEFINE_BASE_FLEX_C2STRUCT, __C2_GENERATE_GLOBAL_VARS__)
+
+/// \if 0
+/*
+   Alternate declaration of field definitions in case no field list is to be generated.
+   The specific macro is chosed based on the value of __C2_GENERATE_GLOBAL_VARS__ (whether it is
+   defined (to be empty) or not. This requires two level of macro substitution.
+   TRICKY: use namespace declaration to handle closing bracket that is normally after
+   these macros.
+*/
+
+#define _DEFINE_AND_DESCRIBE_C2STRUCT(name, defineMacro, enabled) \
+    __DEFINE_AND_DESCRIBE_C2STRUCT(name, defineMacro, enabled)
+#define __DEFINE_AND_DESCRIBE_C2STRUCT(name, defineMacro, enabled) \
+    ___DEFINE_AND_DESCRIBE_C2STRUCT##enabled(name, defineMacro)
+#define ___DEFINE_AND_DESCRIBE_C2STRUCT__C2_GENERATE_GLOBAL_VARS__(name, defineMacro) \
+    defineMacro(name) } C2_PACK; namespace {
+#define ___DEFINE_AND_DESCRIBE_C2STRUCT(name, defineMacro) \
+    defineMacro(name) } C2_PACK; \
+    const std::vector<C2FieldDescriptor> C2##name##Struct::FieldList() { return _FIELD_LIST; } \
+    const std::vector<C2FieldDescriptor> C2##name##Struct::_FIELD_LIST = {
+
+#define _DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(name, flexMember, defineMacro, enabled) \
+    __DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(name, flexMember, defineMacro, enabled)
+#define __DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(name, flexMember, defineMacro, enabled) \
+    ___DEFINE_AND_DESCRIBE_FLEX_C2STRUCT##enabled(name, flexMember, defineMacro)
+#define ___DEFINE_AND_DESCRIBE_FLEX_C2STRUCT__C2_GENERATE_GLOBAL_VARS__(name, flexMember, defineMacro) \
+    defineMacro(name, flexMember) } C2_PACK; namespace {
+#define ___DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(name, flexMember, defineMacro) \
+    defineMacro(name, flexMember) } C2_PACK; \
+    const std::vector<C2FieldDescriptor> C2##name##Struct::FieldList() { return _FIELD_LIST; } \
+    const std::vector<C2FieldDescriptor> C2##name##Struct::_FIELD_LIST = {
+/// \endif
+
+
+/**
+ * Parameter reflector class.
+ *
+ * This class centralizes the description of parameter structures. This can be shared
+ * by multiple components as describing a parameter does not imply support of that
+ * parameter. However, each supported parameter and any dependent structures within
+ * must be described by the parameter reflector provided by a component.
+ */
+class C2ParamReflector {
+public:
+    /**
+     *  Describes a parameter structure.
+     *
+     *  \param[in] coreIndex the core index of the parameter structure containing at least the
+     *  core index
+     *
+     *  \return the description of the parameter structure
+     *  \retval nullptr if the parameter is not supported by this reflector
+     *
+     *  This methods shall not block and return immediately.
+     *
+     *  \note this class does not take a set of indices because we would then prefer
+     *  to also return any dependent structures, and we don't want this logic to be
+     *  repeated in each reflector. Alternately, this could just return a map of all
+     *  descriptions, but we want to conserve memory if client only wants the description
+     *  of a few indices.
+     */
+    virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex coreIndex) const = 0;
+
+protected:
+    virtual ~C2ParamReflector() = default;
+};
+
+/**
+ * Generic supported values for a field.
+ *
+ * This can be either a range or a set of values. The range can be a simple range, an arithmetic,
+ * geometric or multiply-accumulate series with a clear minimum and maximum value. Values can
+ * be discrete values, or can optionally represent flags to be or-ed.
+ *
+ * \note Do not use flags to represent bitfields. Use individual values or separate fields instead.
+ */
+struct C2FieldSupportedValues {
+//public:
+    enum type_t {
+        EMPTY,      ///< no supported values
+        RANGE,      ///< a numeric range that can be continuous or discrete
+        VALUES,     ///< a list of values
+        FLAGS       ///< a list of flags that can be OR-ed
+    };
+
+    type_t type; /** Type of values for this field. */
+
+    typedef C2Value::Primitive Primitive;
+
+    /**
+     * Range specifier for supported value. Used if type is RANGE.
+     *
+     * If step is 0 and num and denom are both 1, the supported values are any value, for which
+     * min <= value <= max.
+     *
+     * Otherwise, the range represents a geometric/arithmetic/multiply-accumulate series, where
+     * successive supported values can be derived from previous values (starting at min), using the
+     * following formula:
+     *  v[0] = min
+     *  v[i] = v[i-1] * num / denom + step for i >= 1, while min < v[i] <= max.
+     */
+    struct {
+        /** Lower end of the range (inclusive). */
+        Primitive min;
+        /** Upper end of the range (inclusive if permitted by series). */
+        Primitive max;
+        /** Step between supported values. */
+        Primitive step;
+        /** Numerator of a geometric series. */
+        Primitive num;
+        /** Denominator of a geometric series. */
+        Primitive denom;
+    } range;
+
+    /**
+     * List of values. Used if type is VALUES or FLAGS.
+     *
+     * If type is VALUES, this is the list of supported values in decreasing preference.
+     *
+     * If type is FLAGS, this vector contains { min-mask, flag1, flag2... }. Basically, the first
+     * value is the required set of flags to be set, and the rest of the values are flags that can
+     * be set independently. FLAGS is only supported for integral types. Supported flags should
+     * not overlap, as it can make validation non-deterministic. The standard validation method
+     * is that starting from the original value, if each flag is removed when fully present (the
+     * min-mask must be fully present), we shall arrive at 0.
+     */
+    std::vector<Primitive> values;
+
+    C2FieldSupportedValues()
+        : type(EMPTY) {
+    }
+
+    template<typename T>
+    C2FieldSupportedValues(T min, T max, T step = T(std::is_floating_point<T>::value ? 0 : 1))
+        : type(RANGE),
+          range{min, max, step, (T)1, (T)1} { }
+
+    template<typename T>
+    C2FieldSupportedValues(T min, T max, T num, T den) :
+        type(RANGE),
+        range{min, max, (T)0, num, den} { }
+
+    template<typename T>
+    C2FieldSupportedValues(T min, T max, T step, T num, T den)
+        : type(RANGE),
+          range{min, max, step, num, den} { }
+
+    /// \deprecated
+    template<typename T>
+    C2FieldSupportedValues(bool flags, std::initializer_list<T> list)
+        : type(flags ? FLAGS : VALUES),
+          range{(T)0, (T)0, (T)0, (T)0, (T)0} {
+        for (T value : list) {
+            values.emplace_back(value);
+        }
+    }
+
+    /// \deprecated
+    template<typename T>
+    C2FieldSupportedValues(bool flags, const std::vector<T>& list)
+        : type(flags ? FLAGS : VALUES),
+          range{(T)0, (T)0, (T)0, (T)0, (T)0} {
+        for(T value : list) {
+            values.emplace_back(value);
+        }
+    }
+
+    /// \internal
+    /// \todo: create separate values vs. flags initializer as for flags we want
+    /// to list both allowed and required flags
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnull-dereference"
+    template<typename T, typename E=decltype(C2FieldDescriptor::namedValuesFor(*(T*)nullptr))>
+    C2FieldSupportedValues(bool flags, const T*)
+        : type(flags ? FLAGS : VALUES),
+          range{(T)0, (T)0, (T)0, (T)0, (T)0} {
+              C2FieldDescriptor::NamedValuesType named = C2FieldDescriptor::namedValuesFor(*(T*)nullptr);
+        if (flags) {
+            values.emplace_back(0); // min-mask defaults to 0
+        }
+        for (const C2FieldDescriptor::NamedValueType &item : named){
+            values.emplace_back(item.second);
+        }
+    }
+};
+#pragma GCC diagnostic pop
+
+/**
+ * Supported values for a specific field.
+ *
+ * This is a pair of the field specifier together with an optional supported values object.
+ * This structure is used when reporting parameter configuration failures and conflicts.
+ */
+struct C2ParamFieldValues {
+    C2ParamField paramOrField; ///< the field or parameter
+    /// optional supported values for the field if paramOrField specifies an actual field that is
+    /// numeric (non struct, blob or string). Supported values for arrays (including string and
+    /// blobs) describe the supported values for each element (character for string, and bytes for
+    /// blobs). It is optional for read-only strings and blobs.
+    std::unique_ptr<C2FieldSupportedValues> values;
+
+    // This struct is meant to be move constructed.
+    C2_DEFAULT_MOVE(C2ParamFieldValues);
+
+    // Copy constructor/assignment is also provided as this object may get copied.
+    C2ParamFieldValues(const C2ParamFieldValues &other)
+        : paramOrField(other.paramOrField),
+          values(other.values ? std::make_unique<C2FieldSupportedValues>(*other.values) : nullptr) { }
+
+    C2ParamFieldValues& operator=(const C2ParamFieldValues &other) {
+        paramOrField = other.paramOrField;
+        values = other.values ? std::make_unique<C2FieldSupportedValues>(*other.values) : nullptr;
+        return *this;
+    }
+
+
+    /**
+     * Construct with no values.
+     */
+    C2ParamFieldValues(const C2ParamField &paramOrField_)
+        : paramOrField(paramOrField_) { }
+
+    /**
+     * Construct with values.
+     */
+    C2ParamFieldValues(const C2ParamField &paramOrField_, const C2FieldSupportedValues &values_)
+        : paramOrField(paramOrField_),
+          values(std::make_unique<C2FieldSupportedValues>(values_)) { }
+
+    /**
+     * Construct from fields.
+     */
+    C2ParamFieldValues(const C2ParamField &paramOrField_, std::unique_ptr<C2FieldSupportedValues> &&values_)
+        : paramOrField(paramOrField_),
+          values(std::move(values_)) { }
+};
+
+/// @}
+
+// include debug header for C2Params.h if C2Debug.h was already included
+#ifdef C2UTILS_DEBUG_H_
+#include <util/C2Debug-param.h>
+#endif
+
+#endif  // C2PARAM_H_
diff --git a/media/codec2/core/include/C2ParamDef.h b/media/codec2/core/include/C2ParamDef.h
new file mode 100644
index 0000000..0a33283
--- /dev/null
+++ b/media/codec2/core/include/C2ParamDef.h
@@ -0,0 +1,931 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * Templates used to declare parameters.
+ */
+#ifndef C2PARAM_DEF_H_
+#define C2PARAM_DEF_H_
+
+#include <type_traits>
+
+#include <C2Param.h>
+
+/// \addtogroup Parameters
+/// @{
+
+/* ======================== UTILITY TEMPLATES FOR PARAMETER DEFINITIONS ======================== */
+
+/// \addtogroup internal
+/// @{
+
+/// Helper class that checks if a type has equality and inequality operators.
+struct C2_HIDE _C2Comparable_impl
+{
+    template<typename S, typename=decltype(S() == S())>
+    static std::true_type TestEqual(int);
+    template<typename>
+    static std::false_type TestEqual(...);
+
+    template<typename S, typename=decltype(S() != S())>
+    static std::true_type TestNotEqual(int);
+    template<typename>
+    static std::false_type TestNotEqual(...);
+};
+
+/**
+ * Helper template that returns if a type has equality and inequality operators.
+ *
+ * Use as _C2Comparable<typename S>::value.
+ */
+template<typename S>
+struct C2_HIDE _C2Comparable
+    : public std::integral_constant<bool, decltype(_C2Comparable_impl::TestEqual<S>(0))::value
+                        || decltype(_C2Comparable_impl::TestNotEqual<S>(0))::value> {
+};
+
+///  Helper class that checks if a type has a CORE_INDEX constant.
+struct C2_HIDE _C2CoreIndexHelper_impl
+{
+    template<typename S, int=S::CORE_INDEX>
+    static std::true_type TestCoreIndex(int);
+    template<typename>
+    static std::false_type TestCoreIndex(...);
+};
+
+/// Macro that defines and thus overrides a type's CORE_INDEX for a setting
+#define _C2_CORE_INDEX_OVERRIDE(coreIndex) \
+public: \
+    enum : uint32_t { CORE_INDEX = coreIndex };
+
+
+/// Helper template that adds a CORE_INDEX to a type if it does not have one (for testing)
+template<typename S, int CoreIndex>
+struct C2_HIDE _C2AddCoreIndex : public S {
+    _C2_CORE_INDEX_OVERRIDE(CoreIndex)
+};
+
+/**
+ * \brief Helper class to check struct requirements for parameters.
+ *
+ * Features:
+ *  - verify default constructor, no virtual methods, and no equality operators.
+ *  - expose PARAM_TYPE, and non-flex FLEX_SIZE.
+ */
+template<typename S, int CoreIndex, unsigned TypeFlags>
+struct C2_HIDE _C2StructCheck {
+    static_assert(
+            std::is_default_constructible<S>::value, "C2 structure must have default constructor");
+    static_assert(!std::is_polymorphic<S>::value, "C2 structure must not have virtual methods");
+    static_assert(!_C2Comparable<S>::value, "C2 structure must not have operator== or !=");
+
+public:
+    enum : uint32_t {
+        PARAM_TYPE = CoreIndex | TypeFlags
+    };
+
+protected:
+    enum : uint32_t {
+        FLEX_SIZE = 0,
+    };
+};
+
+/// Helper class that checks if a type has an integer FLEX_SIZE member.
+struct C2_HIDE _C2Flexible_impl {
+    /// specialization for types that have a FLEX_SIZE member
+    template<typename S, unsigned=S::FLEX_SIZE>
+    static std::true_type TestFlexSize(int);
+    template<typename>
+    static std::false_type TestFlexSize(...);
+};
+
+/// Helper template that returns if a type has an integer FLEX_SIZE member.
+template<typename S>
+struct C2_HIDE _C2Flexible
+    : public std::integral_constant<bool, decltype(_C2Flexible_impl::TestFlexSize<S>(0))::value> {
+};
+
+/// Macro to test if a type is flexible (has a FLEX_SIZE member).
+#define IF_FLEXIBLE(S) ENABLE_IF(_C2Flexible<S>::value)
+/// Shorthand for std::enable_if
+#define ENABLE_IF(cond) typename std::enable_if<cond>::type
+
+template<typename T, typename V=void>
+struct C2_HIDE _c2_enable_if_type {
+    typedef V type;
+};
+
+/// Helper template that exposes the flexible subtype of a struct.
+template<typename S, typename E=void>
+struct C2_HIDE _C2FlexHelper {
+    typedef void FlexType;
+    enum : uint32_t { FLEX_SIZE = 0 };
+};
+
+/// Specialization for flexible types. This only works if _FlexMemberType is public.
+template<typename S>
+struct C2_HIDE _C2FlexHelper<S,
+        typename _c2_enable_if_type<typename S::_FlexMemberType>::type> {
+    typedef typename _C2FlexHelper<typename S::_FlexMemberType>::FlexType FlexType;
+    enum : uint32_t { FLEX_SIZE = _C2FlexHelper<typename S::_FlexMemberType>::FLEX_SIZE };
+};
+
+/// Specialization for flex arrays.
+template<typename S>
+struct C2_HIDE _C2FlexHelper<S[],
+        typename std::enable_if<std::is_void<typename _C2FlexHelper<S>::FlexType>::value>::type> {
+    typedef S FlexType;
+    enum : uint32_t { FLEX_SIZE = sizeof(S) };
+};
+
+/**
+ * \brief Helper class to check flexible struct requirements and add common operations.
+ *
+ * Features:
+ *  - expose CORE_INDEX and FieldList (this is normally inherited from the struct, but flexible
+ *    structs cannot be base classes and thus inherited from)
+ *  - disable copy assignment and construction (TODO: this is already done in the FLEX macro for the
+ *    flexible struct, so may not be needed here)
+ */
+template<typename S, int ParamIndex, unsigned TypeFlags>
+struct C2_HIDE _C2FlexStructCheck :
+// add flexible flag as _C2StructCheck defines PARAM_TYPE
+        public _C2StructCheck<S, ParamIndex | C2Param::CoreIndex::IS_FLEX_FLAG, TypeFlags> {
+public:
+    enum : uint32_t {
+        /// \hideinitializer
+        CORE_INDEX = ParamIndex | C2Param::CoreIndex::IS_FLEX_FLAG, ///< flexible struct core-index
+    };
+
+    inline static const std::vector<C2FieldDescriptor> FieldList() { return S::FieldList(); }
+
+    // default constructor needed because of the disabled copy constructor
+    inline _C2FlexStructCheck() = default;
+
+protected:
+    // cannot copy flexible params
+    _C2FlexStructCheck(const _C2FlexStructCheck<S, ParamIndex, TypeFlags> &) = delete;
+    _C2FlexStructCheck& operator= (const _C2FlexStructCheck<S, ParamIndex, TypeFlags> &) = delete;
+
+    // constants used for helper methods
+    enum : uint32_t {
+        /// \hideinitializer
+        FLEX_SIZE = _C2FlexHelper<S>::FLEX_SIZE, ///< size of flexible type
+        /// \hideinitializer
+        MAX_SIZE = (uint32_t)std::min((size_t)UINT32_MAX, SIZE_MAX), // TODO: is this always u32 max?
+        /// \hideinitializer
+        BASE_SIZE = sizeof(S) + sizeof(C2Param), ///< size of the base param
+    };
+
+    /// returns the allocated size of this param with flexCount, or 0 if it would overflow.
+    inline static size_t CalcSize(size_t flexCount, size_t size = BASE_SIZE) {
+        if (flexCount <= (MAX_SIZE - size) / S::FLEX_SIZE) {
+            return size + S::FLEX_SIZE * flexCount;
+        }
+        return 0;
+    }
+
+    /// dynamic new operator usable for params of type S
+    inline void* operator new(size_t size, size_t flexCount) noexcept {
+        // TODO: assert(size == BASE_SIZE);
+        size = CalcSize(flexCount, size);
+        if (size > 0) {
+            return ::operator new(size);
+        }
+        return nullptr;
+    }
+};
+
+/// Define From() cast operators for params.
+#define DEFINE_CAST_OPERATORS(_Type) \
+    inline static _Type* From(C2Param *other) { \
+        return (_Type*)C2Param::IfSuitable( \
+                other, sizeof(_Type), _Type::PARAM_TYPE, _Type::FLEX_SIZE, \
+                (_Type::PARAM_TYPE & T::Index::DIR_UNDEFINED) != T::Index::DIR_UNDEFINED); \
+    } \
+    inline static const _Type* From(const C2Param *other) { \
+        return const_cast<const _Type*>(From(const_cast<C2Param *>(other))); \
+    } \
+    inline static _Type* From(std::nullptr_t) { return nullptr; } \
+
+/**
+ * Define flexible allocators (AllocShared or AllocUnique) for flexible params.
+ *  - P::AllocXyz(flexCount, args...): allocate for given flex-count. This maps to
+ *          T(flexCount, args...)\
+ *
+ * Clang does not support args... followed by templated param as args... eats it. Hence,
+ * provide specializations where the initializer replaces the flexCount.
+ *
+ * Specializations that deduce flexCount:
+ *  - P::AllocXyz(T[], args...): allocate for size of (and with) init array.
+ *  - P::AllocXyz(std::initializer_list<T>, args...): allocate for size of (and with) initializer
+ *            list.
+ *  - P::AllocXyz(std::vector<T>, args...): allocate for size of (and with) init vector.
+ *  These specializations map to T(flexCount = size-of-init, args..., init)
+ */
+#define DEFINE_FLEXIBLE_ALLOC(_Type, S, ptr, Ptr) \
+    template<typename ...Args> \
+    inline static std::ptr##_ptr<_Type> Alloc##Ptr(size_t flexCount, const Args(&... args)) { \
+        return std::ptr##_ptr<_Type>(new(flexCount) _Type(flexCount, args...)); \
+    } \
+    template<typename ...Args, typename U=typename S::FlexType> \
+    inline static std::ptr##_ptr<_Type> Alloc##Ptr( \
+            const std::initializer_list<U> &init, const Args(&... args)) { \
+        return std::ptr##_ptr<_Type>(new(init.size()) _Type(init.size(), args..., init)); \
+    } \
+    template<typename ...Args, typename U=typename S::FlexType> \
+    inline static std::ptr##_ptr<_Type> Alloc##Ptr( \
+            const std::vector<U> &init, const Args(&... args)) { \
+        return std::ptr##_ptr<_Type>(new(init.size()) _Type(init.size(), args..., init)); \
+    } \
+    template<typename ...Args, typename U=typename S::FlexType, unsigned N> \
+    inline static std::ptr##_ptr<_Type> Alloc##Ptr(const U(&init)[N], const Args(&... args)) { \
+        return std::ptr##_ptr<_Type>(new(N) _Type(N, args..., init)); \
+    } \
+
+/**
+ * Define flexible methods AllocShared, AllocUnique and flexCount.
+ */
+#define DEFINE_FLEXIBLE_METHODS(_Type, S) \
+    DEFINE_FLEXIBLE_ALLOC(_Type, S, shared, Shared) \
+    DEFINE_FLEXIBLE_ALLOC(_Type, S, unique, Unique) \
+    inline size_t flexCount() const { \
+        static_assert(sizeof(_Type) == _Type::BASE_SIZE, "incorrect BASE_SIZE"); \
+        size_t sz = this->size(); \
+        if (sz >= sizeof(_Type)) { \
+            return (sz - sizeof(_Type)) / _Type::FLEX_SIZE; \
+        } \
+        return 0; \
+    } \
+
+/// Mark flexible member variable and make structure flexible.
+#define FLEX(cls, m) \
+    C2_DO_NOT_COPY(cls) \
+private: \
+    C2PARAM_MAKE_FRIENDS \
+    /** \if 0 */ \
+    template<typename, typename> friend struct _C2FlexHelper; \
+public: \
+    typedef decltype(m) _FlexMemberType; \
+    /* default constructor with flexCount */ \
+    inline cls(size_t) : cls() {} \
+    /* constexpr static _FlexMemberType cls::* flexMember = &cls::m; */ \
+    typedef typename _C2FlexHelper<_FlexMemberType>::FlexType FlexType; \
+    static_assert(\
+            !std::is_void<FlexType>::value, \
+            "member is not flexible, or a flexible array of a flexible type"); \
+    enum : uint32_t { FLEX_SIZE = _C2FlexHelper<_FlexMemberType>::FLEX_SIZE }; \
+    /** \endif */ \
+
+/// @}
+
+/**
+ * Global-parameter template.
+ *
+ * Base template to define a global setting/tuning or info based on a structure and
+ * an optional ParamIndex. Global parameters are not tied to a port (input or output).
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped
+ * structure can be accessed directly, and constructors and potential public methods are also
+ * wrapped.
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped structure
+ * \tparam ParamIndex optional parameter index override. Must be specified for base/reused
+ * structures.
+ */
+template<typename T, typename S, int ParamIndex=S::CORE_INDEX, class Flex=void>
+struct C2_HIDE C2GlobalParam : public T, public S,
+        public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_GLOBAL> {
+    _C2_CORE_INDEX_OVERRIDE(ParamIndex)
+private:
+    typedef C2GlobalParam<T, S, ParamIndex> _Type;
+
+public:
+    /// Wrapper around base structure's constructor.
+    template<typename ...Args>
+    inline C2GlobalParam(const Args(&... args)) : T(sizeof(_Type), _Type::PARAM_TYPE), S(args...) { }
+
+    DEFINE_CAST_OPERATORS(_Type)
+};
+
+/**
+ * Global-parameter template for flexible structures.
+ *
+ * Base template to define a global setting/tuning or info based on a flexible structure and
+ * an optional ParamIndex. Global parameters are not tied to a port (input or output).
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped flexible structure
+ * \tparam ParamIndex optional parameter index override. Must be specified for base/reused
+ *         structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible
+ * structures can be accessed via the m member variable; however, the constructors of the structure
+ * are wrapped directly. (This is because flexible types cannot be subclassed.)
+ */
+template<typename T, typename S, int ParamIndex>
+struct C2_HIDE C2GlobalParam<T, S, ParamIndex, IF_FLEXIBLE(S)>
+    : public T, public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_GLOBAL> {
+private:
+    typedef C2GlobalParam<T, S, ParamIndex> _Type;
+
+    /// Wrapper around base structure's constructor.
+    template<typename ...Args>
+    inline C2GlobalParam(size_t flexCount, const Args(&... args))
+        : T(_Type::CalcSize(flexCount), _Type::PARAM_TYPE), m(flexCount, args...) { }
+
+public:
+    S m; ///< wrapped flexible structure
+
+    DEFINE_FLEXIBLE_METHODS(_Type, S)
+    DEFINE_CAST_OPERATORS(_Type)
+};
+
+/**
+ * Port-parameter template.
+ *
+ * Base template to define a port setting/tuning or info based on a structure and
+ * an optional ParamIndex. Port parameters are tied to a port (input or output), but not to a
+ * specific stream.
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped structure
+ * \tparam ParamIndex optional parameter index override. Must be specified for base/reused
+ *         structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped
+ * structure can be accessed directly, and constructors and potential public methods are also
+ * wrapped.
+ *
+ * There are 3 flavors of port parameters: unspecified, input and output. Parameters with
+ * unspecified port expose a setPort method, and add an initial port parameter to the constructor.
+ */
+template<typename T, typename S, int ParamIndex=S::CORE_INDEX, class Flex=void>
+struct C2_HIDE C2PortParam : public T, public S,
+        private _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_UNDEFINED> {
+    _C2_CORE_INDEX_OVERRIDE(ParamIndex)
+private:
+    typedef C2PortParam<T, S, ParamIndex> _Type;
+
+public:
+    /// Default constructor.
+    inline C2PortParam() : T(sizeof(_Type), _Type::PARAM_TYPE) { }
+    template<typename ...Args>
+    /// Wrapper around base structure's constructor while specifying port/direction.
+    inline C2PortParam(bool _output, const Args(&... args))
+        : T(sizeof(_Type), _output ? output::PARAM_TYPE : input::PARAM_TYPE), S(args...) { }
+    /// Set port/direction.
+    inline void setPort(bool output) { C2Param::setPort(output); }
+
+    DEFINE_CAST_OPERATORS(_Type)
+
+    /// Specialization for an input port parameter.
+    struct input : public T, public S,
+            public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_INPUT> {
+        _C2_CORE_INDEX_OVERRIDE(ParamIndex)
+        /// Wrapper around base structure's constructor.
+        template<typename ...Args>
+        inline input(const Args(&... args)) : T(sizeof(_Type), input::PARAM_TYPE), S(args...) { }
+
+        DEFINE_CAST_OPERATORS(input)
+
+    };
+
+    /// Specialization for an output port parameter.
+    struct output : public T, public S,
+            public _C2StructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_OUTPUT> {
+        _C2_CORE_INDEX_OVERRIDE(ParamIndex)
+        /// Wrapper around base structure's constructor.
+        template<typename ...Args>
+        inline output(const Args(&... args)) : T(sizeof(_Type), output::PARAM_TYPE), S(args...) { }
+
+        DEFINE_CAST_OPERATORS(output)
+    };
+};
+
+/**
+ * Port-parameter template for flexible structures.
+ *
+ * Base template to define a port setting/tuning or info based on a flexible structure and
+ * an optional ParamIndex. Port parameters are tied to a port (input or output), but not to a
+ * specific stream.
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped flexible structure
+ * \tparam ParamIndex optional parameter index override. Must be specified for base/reused
+ *         structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible
+ * structures can be accessed via the m member variable; however, the constructors of the structure
+ * are wrapped directly. (This is because flexible types cannot be subclassed.)
+ *
+ * There are 3 flavors of port parameters: unspecified, input and output. Parameters with
+ * unspecified port expose a setPort method, and add an initial port parameter to the constructor.
+ */
+template<typename T, typename S, int ParamIndex>
+struct C2_HIDE C2PortParam<T, S, ParamIndex, IF_FLEXIBLE(S)>
+    : public T, public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Type::DIR_UNDEFINED> {
+private:
+    typedef C2PortParam<T, S, ParamIndex> _Type;
+
+    /// Default constructor for basic allocation: new(flexCount) P.
+    inline C2PortParam(size_t flexCount) : T(_Type::CalcSize(flexCount), _Type::PARAM_TYPE) { }
+    template<typename ...Args>
+    /// Wrapper around base structure's constructor while also specifying port/direction.
+    inline C2PortParam(size_t flexCount, bool _output, const Args(&... args))
+        : T(_Type::CalcSize(flexCount), _output ? output::PARAM_TYPE : input::PARAM_TYPE),
+          m(flexCount, args...) { }
+
+public:
+    /// Set port/direction.
+    inline void setPort(bool output) { C2Param::setPort(output); }
+
+    S m; ///< wrapped flexible structure
+
+    DEFINE_FLEXIBLE_METHODS(_Type, S)
+    DEFINE_CAST_OPERATORS(_Type)
+
+    /// Specialization for an input port parameter.
+    struct input : public T,
+            public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_INPUT> {
+    private:
+        /// Wrapper around base structure's constructor while also specifying port/direction.
+        template<typename ...Args>
+        inline input(size_t flexCount, const Args(&... args))
+            : T(_Type::CalcSize(flexCount), input::PARAM_TYPE), m(flexCount, args...) { }
+
+    public:
+        S m; ///< wrapped flexible structure
+
+        DEFINE_FLEXIBLE_METHODS(input, S)
+        DEFINE_CAST_OPERATORS(input)
+    };
+
+    /// Specialization for an output port parameter.
+    struct output : public T,
+            public _C2FlexStructCheck<S, ParamIndex, T::PARAM_KIND | T::Index::DIR_OUTPUT> {
+    private:
+        /// Wrapper around base structure's constructor while also specifying port/direction.
+        template<typename ...Args>
+        inline output(size_t flexCount, const Args(&... args))
+            : T(_Type::CalcSize(flexCount), output::PARAM_TYPE), m(flexCount, args...) { }
+
+    public:
+        S m; ///< wrapped flexible structure
+
+        DEFINE_FLEXIBLE_METHODS(output, S)
+        DEFINE_CAST_OPERATORS(output)
+    };
+};
+
+/**
+ * Stream-parameter template.
+ *
+ * Base template to define a stream setting/tuning or info based on a structure and
+ * an optional ParamIndex. Stream parameters are tied to a specific stream on a port (input or
+ * output).
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped structure
+ * \tparam ParamIndex optional paramter index override. Must be specified for base/reused
+ *         structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped
+ * structure can be accessed directly, and constructors and potential public methods are also
+ * wrapped.
+ *
+ * There are 3 flavors of stream parameters: unspecified port, input and output. All of these expose
+ * a setStream method and an extra initial streamID parameter for the constructor. Moreover,
+ * parameters with unspecified port expose a setPort method, and add an additional initial port
+ * parameter to the constructor.
+ */
+template<typename T, typename S, int ParamIndex=S::CORE_INDEX, class Flex=void>
+struct C2_HIDE C2StreamParam : public T, public S,
+        private _C2StructCheck<S, ParamIndex,
+                T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Index::DIR_UNDEFINED> {
+    _C2_CORE_INDEX_OVERRIDE(ParamIndex)
+private:
+    typedef C2StreamParam<T, S, ParamIndex> _Type;
+
+public:
+    /// Default constructor. Port/direction and stream-ID is undefined.
+    inline C2StreamParam() : T(sizeof(_Type), _Type::PARAM_TYPE) { }
+    /// Wrapper around base structure's constructor while also specifying port/direction and
+    /// stream-ID.
+    template<typename ...Args>
+    inline C2StreamParam(bool _output, unsigned stream, const Args(&... args))
+        : T(sizeof(_Type), _output ? output::PARAM_TYPE : input::PARAM_TYPE, stream),
+          S(args...) { }
+    /// Set port/direction.
+    inline void setPort(bool output) { C2Param::setPort(output); }
+    /// Set stream-id. \retval true if the stream-id was successfully set.
+    inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+    DEFINE_CAST_OPERATORS(_Type)
+
+    /// Specialization for an input stream parameter.
+    struct input : public T, public S,
+            public _C2StructCheck<S, ParamIndex,
+                    T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_INPUT> {
+        _C2_CORE_INDEX_OVERRIDE(ParamIndex)
+
+        /// Default constructor. Stream-ID is undefined.
+        inline input() : T(sizeof(_Type), input::PARAM_TYPE) { }
+        /// Wrapper around base structure's constructor while also specifying stream-ID.
+        template<typename ...Args>
+        inline input(unsigned stream, const Args(&... args))
+            : T(sizeof(_Type), input::PARAM_TYPE, stream), S(args...) { }
+        /// Set stream-id. \retval true if the stream-id was successfully set.
+        inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+        DEFINE_CAST_OPERATORS(input)
+    };
+
+    /// Specialization for an output stream parameter.
+    struct output : public T, public S,
+            public _C2StructCheck<S, ParamIndex,
+                    T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_OUTPUT> {
+        _C2_CORE_INDEX_OVERRIDE(ParamIndex)
+
+        /// Default constructor. Stream-ID is undefined.
+        inline output() : T(sizeof(_Type), output::PARAM_TYPE) { }
+        /// Wrapper around base structure's constructor while also specifying stream-ID.
+        template<typename ...Args>
+        inline output(unsigned stream, const Args(&... args))
+            : T(sizeof(_Type), output::PARAM_TYPE, stream), S(args...) { }
+        /// Set stream-id. \retval true if the stream-id was successfully set.
+        inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+        DEFINE_CAST_OPERATORS(output)
+    };
+};
+
+/**
+ * Stream-parameter template for flexible structures.
+ *
+ * Base template to define a stream setting/tuning or info based on a flexible structure and
+ * an optional ParamIndex. Stream parameters are tied to a specific stream on a port (input or
+ * output).
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped flexible structure
+ * \tparam ParamIndex optional parameter index override. Must be specified for base/reused
+ *         structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible
+ * structures can be accessed via the m member variable; however, the constructors of the structure
+ * are wrapped directly. (This is because flexible types cannot be subclassed.)
+ *
+ * There are 3 flavors of stream parameters: unspecified port, input and output. All of these expose
+ * a setStream method and an extra initial streamID parameter for the constructor. Moreover,
+ * parameters with unspecified port expose a setPort method, and add an additional initial port
+ * parameter to the constructor.
+ */
+template<typename T, typename S, int ParamIndex>
+struct C2_HIDE C2StreamParam<T, S, ParamIndex, IF_FLEXIBLE(S)>
+    : public T,
+      public _C2FlexStructCheck<S, ParamIndex,
+              T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Index::DIR_UNDEFINED> {
+private:
+    typedef C2StreamParam<T, S, ParamIndex> _Type;
+    /// Default constructor. Port/direction and stream-ID is undefined.
+    inline C2StreamParam(size_t flexCount) : T(_Type::CalcSize(flexCount), _Type::PARAM_TYPE, 0u) { }
+    /// Wrapper around base structure's constructor while also specifying port/direction and
+    /// stream-ID.
+    template<typename ...Args>
+    inline C2StreamParam(size_t flexCount, bool _output, unsigned stream, const Args(&... args))
+        : T(_Type::CalcSize(flexCount), _output ? output::PARAM_TYPE : input::PARAM_TYPE, stream),
+          m(flexCount, args...) { }
+
+public:
+    S m; ///< wrapped flexible structure
+
+    /// Set port/direction.
+    inline void setPort(bool output) { C2Param::setPort(output); }
+    /// Set stream-id. \retval true if the stream-id was successfully set.
+    inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+    DEFINE_FLEXIBLE_METHODS(_Type, S)
+    DEFINE_CAST_OPERATORS(_Type)
+
+    /// Specialization for an input stream parameter.
+    struct input : public T,
+            public _C2FlexStructCheck<S, ParamIndex,
+                    T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_INPUT> {
+    private:
+        /// Default constructor. Stream-ID is undefined.
+        inline input(size_t flexCount) : T(_Type::CalcSize(flexCount), input::PARAM_TYPE) { }
+        /// Wrapper around base structure's constructor while also specifying stream-ID.
+        template<typename ...Args>
+        inline input(size_t flexCount, unsigned stream, const Args(&... args))
+            : T(_Type::CalcSize(flexCount), input::PARAM_TYPE, stream), m(flexCount, args...) { }
+
+    public:
+        S m; ///< wrapped flexible structure
+
+        /// Set stream-id. \retval true if the stream-id was successfully set.
+        inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+        DEFINE_FLEXIBLE_METHODS(input, S)
+        DEFINE_CAST_OPERATORS(input)
+    };
+
+    /// Specialization for an output stream parameter.
+    struct output : public T,
+            public _C2FlexStructCheck<S, ParamIndex,
+                    T::PARAM_KIND | T::Index::IS_STREAM_FLAG | T::Type::DIR_OUTPUT> {
+    private:
+        /// Default constructor. Stream-ID is undefined.
+        inline output(size_t flexCount) : T(_Type::CalcSize(flexCount), output::PARAM_TYPE) { }
+        /// Wrapper around base structure's constructor while also specifying stream-ID.
+        template<typename ...Args>
+        inline output(size_t flexCount, unsigned stream, const Args(&... args))
+            : T(_Type::CalcSize(flexCount), output::PARAM_TYPE, stream), m(flexCount, args...) { }
+
+    public:
+        S m; ///< wrapped flexible structure
+
+        /// Set stream-id. \retval true if the stream-id was successfully set.
+        inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+        DEFINE_FLEXIBLE_METHODS(output, S)
+        DEFINE_CAST_OPERATORS(output)
+    };
+};
+
+/* ======================== SIMPLE VALUE PARAMETERS ======================== */
+
+/**
+ * \ingroup internal
+ * A structure template encapsulating a single element with default constructors and no core-index.
+ */
+template<typename T>
+struct C2SimpleValueStruct {
+    T value; ///< simple value of the structure
+    // Default constructor.
+    inline C2SimpleValueStruct() = default;
+    // Constructor with an initial value.
+    inline C2SimpleValueStruct(T value) : value(value) {}
+    DEFINE_BASE_C2STRUCT(SimpleValue)
+};
+
+// TODO: move this and next to some generic place
+/**
+ * Interface to a block of (mapped) memory containing an array of some type (T).
+ */
+template<typename T>
+struct C2MemoryBlock {
+    /// \returns the number of elements in this block.
+    virtual size_t size() const = 0;
+    /// \returns a const pointer to the start of this block. Care must be taken to not read outside
+    /// the block.
+    virtual const T *data() const = 0; // TODO: should this be friend access only in some C2Memory module?
+    /// \returns a pointer to the start of this block. Care must be taken to not read or write
+    /// outside the block.
+    inline T *data() { return const_cast<T*>(const_cast<const C2MemoryBlock*>(this)->data()); }
+
+protected:
+    // TODO: for now it should never be deleted as C2MemoryBlock
+    virtual ~C2MemoryBlock() = default;
+};
+
+/**
+ * Interface to a block of memory containing a constant (constexpr) array of some type (T).
+ */
+template<typename T>
+struct C2ConstMemoryBlock : public C2MemoryBlock<T> {
+    virtual const T * data() const { return _mData; }
+    virtual size_t size() const { return _mSize; }
+
+    /// Constructor.
+    template<unsigned N>
+    inline constexpr C2ConstMemoryBlock(const T(&init)[N]) : _mData(init), _mSize(N) {}
+
+private:
+    const T *_mData;
+    const size_t _mSize;
+};
+
+/// \addtogroup internal
+/// @{
+
+/// Helper class to initialize flexible arrays with various initalizers.
+struct _C2ValueArrayHelper {
+    // char[]-s are used as null terminated strings, so the last element is never inited.
+
+    /// Initialize a flexible array using a constexpr memory block.
+    template<typename T>
+    static void init(T(&array)[], size_t arrayLen, const C2MemoryBlock<T> &block) {
+        // reserve last element for terminal 0 for strings
+        if (arrayLen && std::is_same<T, char>::value) {
+            --arrayLen;
+        }
+        if (block.data()) {
+            memcpy(array, block.data(), std::min(arrayLen, block.size()) * sizeof(T));
+        }
+    }
+
+    /// Initialize a flexible array using an initializer list.
+    template<typename T>
+    static void init(T(&array)[], size_t arrayLen, const std::initializer_list<T> &init) {
+        size_t ix = 0;
+        // reserve last element for terminal 0 for strings
+        if (arrayLen && std::is_same<T, char>::value) {
+            --arrayLen;
+        }
+        for (const T &item : init) {
+            if (ix == arrayLen) {
+                break;
+            }
+            array[ix++] = item;
+        }
+    }
+
+    /// Initialize a flexible array using a vector.
+    template<typename T>
+    static void init(T(&array)[], size_t arrayLen, const std::vector<T> &init) {
+        size_t ix = 0;
+        // reserve last element for terminal 0 for strings
+        if (arrayLen && std::is_same<T, char>::value) {
+            --arrayLen;
+        }
+        for (const T &item : init) {
+            if (ix == arrayLen) {
+                break;
+            }
+            array[ix++] = item;
+        }
+    }
+
+    /// Initialize a flexible array using another flexible array.
+    template<typename T, unsigned N>
+    static void init(T(&array)[], size_t arrayLen, const T(&str)[N]) {
+        // reserve last element for terminal 0 for strings
+        if (arrayLen && std::is_same<T, char>::value) {
+            --arrayLen;
+        }
+        if (arrayLen) {
+            memcpy(array, str, std::min(arrayLen, (size_t)N) * sizeof(T));
+        }
+    }
+};
+
+/**
+ * Specialization for a flexible blob and string arrays. A structure template encapsulating a single
+ * flexible array member with default flexible constructors and no core-index. This type cannot be
+ * constructed on its own as it's size is 0.
+ *
+ * \internal This is different from C2SimpleArrayStruct<T[]> simply because its member has the name
+ * as value to reflect this is a single value.
+ */
+template<typename T>
+struct C2SimpleValueStruct<T[]> {
+    static_assert(std::is_same<T, char>::value || std::is_same<T, uint8_t>::value,
+                  "C2SimpleValueStruct<T[]> is only for BLOB or STRING");
+    T value[];
+
+    inline C2SimpleValueStruct() = default;
+    DEFINE_BASE_C2STRUCT(SimpleValue)
+    FLEX(C2SimpleValueStruct, value)
+
+private:
+    inline C2SimpleValueStruct(size_t flexCount, const C2MemoryBlock<T> &block) {
+        _C2ValueArrayHelper::init(value, flexCount, block);
+    }
+
+    inline C2SimpleValueStruct(size_t flexCount, const std::initializer_list<T> &init) {
+        _C2ValueArrayHelper::init(value, flexCount, init);
+    }
+
+    inline C2SimpleValueStruct(size_t flexCount, const std::vector<T> &init) {
+        _C2ValueArrayHelper::init(value, flexCount, init);
+    }
+
+    template<unsigned N>
+    inline C2SimpleValueStruct(size_t flexCount, const T(&init)[N]) {
+        _C2ValueArrayHelper::init(value, flexCount, init);
+    }
+};
+
+/// @}
+
+/**
+ * A structure template encapsulating a single flexible array element of a specific type (T) with
+ * default constructors and no core-index. This type cannot be constructed on its own as it's size
+ * is 0. Instead, it is meant to be used as a parameter, e.g.
+ *
+ *   typedef C2StreamParam<C2Info, C2SimpleArrayStruct<C2MyFancyStruct>,
+ *           kParamIndexMyFancyArrayStreamParam> C2MyFancyArrayStreamInfo;
+ */
+template<typename T>
+struct C2SimpleArrayStruct {
+    static_assert(!std::is_same<T, char>::value && !std::is_same<T, uint8_t>::value,
+                  "use C2SimpleValueStruct<T[]> is for BLOB or STRING");
+
+    T values[]; ///< array member
+    /// Default constructor
+    inline C2SimpleArrayStruct() = default;
+    DEFINE_BASE_FLEX_C2STRUCT(SimpleArray, values)
+    //FLEX(C2SimpleArrayStruct, values)
+
+private:
+    /// Construct from a C2MemoryBlock.
+    /// Used only by the flexible parameter allocators (AllocUnique & AllocShared).
+    inline C2SimpleArrayStruct(size_t flexCount, const C2MemoryBlock<T> &block) {
+        _C2ValueArrayHelper::init(values, flexCount, block);
+    }
+
+    /// Construct from an initializer list.
+    /// Used only by the flexible parameter allocators (AllocUnique & AllocShared).
+    inline C2SimpleArrayStruct(size_t flexCount, const std::initializer_list<T> &init) {
+        _C2ValueArrayHelper::init(values, flexCount, init);
+    }
+
+    /// Construct from an vector.
+    /// Used only by the flexible parameter allocators (AllocUnique & AllocShared).
+    inline C2SimpleArrayStruct(size_t flexCount, const std::vector<T> &init) {
+        _C2ValueArrayHelper::init(values, flexCount, init);
+    }
+
+    /// Construct from another flexible array.
+    /// Used only by the flexible parameter allocators (AllocUnique & AllocShared).
+    template<unsigned N>
+    inline C2SimpleArrayStruct(size_t flexCount, const T(&init)[N]) {
+        _C2ValueArrayHelper::init(values, flexCount, init);
+    }
+};
+
+/**
+ * \addtogroup simplevalue Simple value and array structures.
+ * @{
+ *
+ * Simple value structures.
+ *
+ * Structures containing a single simple value. These can be reused to easily define simple
+ * parameters of various types:
+ *
+ *   typedef C2PortParam<C2Tuning, C2Int32Value, kParamIndexMyIntegerPortParam>
+ *           C2MyIntegerPortParamTuning;
+ *
+ * They contain a single member (value or values) that is described as "value" or "values".
+ *
+ * These structures don't define a core index, and as such, they cannot be used in structure
+ * declarations. Use type[] instead, such as int32_t field[].
+ */
+/// A 32-bit signed integer parameter in value, described as "value"
+typedef C2SimpleValueStruct<int32_t> C2Int32Value;
+/// A 32-bit signed integer array parameter in values, described as "values"
+typedef C2SimpleArrayStruct<int32_t> C2Int32Array;
+/// A 32-bit unsigned integer parameter in value, described as "value"
+typedef C2SimpleValueStruct<uint32_t> C2Uint32Value;
+/// A 32-bit unsigned integer array parameter in values, described as "values"
+typedef C2SimpleArrayStruct<uint32_t> C2Uint32Array;
+/// A 64-bit signed integer parameter in value, described as "value"
+typedef C2SimpleValueStruct<int64_t> C2Int64Value;
+/// A 64-bit signed integer array parameter in values, described as "values"
+typedef C2SimpleArrayStruct<int64_t> C2Int64Array;
+/// A 64-bit unsigned integer parameter in value, described as "value"
+typedef C2SimpleValueStruct<uint64_t> C2Uint64Value;
+/// A 64-bit unsigned integer array parameter in values, described as "values"
+typedef C2SimpleArrayStruct<uint64_t> C2Uint64Array;
+/// A float parameter in value, described as "value"
+typedef C2SimpleValueStruct<float> C2FloatValue;
+/// A float array parameter in values, described as "values"
+typedef C2SimpleArrayStruct<float> C2FloatArray;
+/// A blob flexible parameter in value, described as "value"
+typedef C2SimpleValueStruct<uint8_t[]> C2BlobValue;
+/// A string flexible parameter in value, described as "value"
+typedef C2SimpleValueStruct<char[]> C2StringValue;
+
+template<typename T>
+const std::vector<C2FieldDescriptor> C2SimpleValueStruct<T>::FieldList() {
+    return { DESCRIBE_C2FIELD(value, "value") };
+}
+template<typename T>
+const std::vector<C2FieldDescriptor> C2SimpleValueStruct<T[]>::FieldList() {
+    return { DESCRIBE_C2FIELD(value, "value") };
+}
+template<typename T>
+const std::vector<C2FieldDescriptor> C2SimpleArrayStruct<T>::FieldList() {
+    return { DESCRIBE_C2FIELD(values, "values") };
+}
+
+/// @}
+
+/// @}
+
+#endif  // C2PARAM_DEF_H_
diff --git a/media/codec2/core/include/C2Work.h b/media/codec2/core/include/C2Work.h
new file mode 100644
index 0000000..6923f3e
--- /dev/null
+++ b/media/codec2/core/include/C2Work.h
@@ -0,0 +1,235 @@
+/*
+ * 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.
+ */
+
+#ifndef C2WORK_H_
+
+#define C2WORK_H_
+
+#include <C2Buffer.h>
+#include <C2Param.h>
+
+#include <memory>
+#include <list>
+#include <vector>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/// \defgroup work Work and data processing
+/// @{
+
+/**
+ * Information describing the reason a parameter settings may fail, or
+ * may be overriden.
+ */
+struct C2SettingResult {
+    enum Failure : uint32_t {
+        /* parameter failures below */
+        BAD_TYPE,   ///< parameter is not supported
+        BAD_PORT,   ///< parameter is not supported on the specific port
+        BAD_INDEX,  ///< parameter is not supported on the specific stream
+        READ_ONLY,  ///< parameter is read-only and cannot be set
+        MISMATCH,   ///< parameter mismatches input data
+
+        /* field failures below */
+        BAD_VALUE,  ///< strict parameter does not accept value for the field at all
+        CONFLICT,   ///< strict parameter field value is in conflict with an/other setting(s)
+
+        /// parameter field is out of range due to other settings (this failure mode
+        /// can only be used for strict calculated parameters)
+        UNSUPPORTED,
+
+        /// field does not access the requested parameter value at all. It has been corrected to
+        /// the closest supported value. This failure mode is provided to give guidance as to what
+        /// are the currently supported values for this field (which may be a subset of the at-all-
+        /// potential values)
+        INFO_BAD_VALUE,
+
+        /// requested parameter value is in conflict with an/other setting(s)
+        /// and has been corrected to the closest supported value. This failure
+        /// mode is given to provide guidance as to what are the currently supported values as well
+        /// as to optionally provide suggestion to the client as to how to enable the requested
+        /// parameter value.
+        INFO_CONFLICT,
+    };
+
+    Failure failure;    ///< failure code
+
+    /// Failing (or corrected) field or parameterand optionally, currently supported values for the
+    /// field. Values must only be set for field failures other than BAD_VALUE, and only if they are
+    /// different from the globally supported values (e.g. due to restrictions by another param or
+    /// input data).
+    C2ParamFieldValues field;
+
+    /// Conflicting parameters or fields with optional suggestions with (optional) suggested values
+    /// for any conflicting fields to avoid the conflict. Must only be set for CONFLICT, UNSUPPORTED
+    /// and INFO_CONFLICT failure codes.
+    std::vector<C2ParamFieldValues> conflicts;
+};
+
+// ================================================================================================
+//  WORK
+// ================================================================================================
+
+/** Unique ID for a processing node. */
+typedef uint32_t c2_node_id_t;
+
+enum {
+    kParamIndexWorkOrdinal,
+};
+
+/**
+ * Information for ordering work items on a component port.
+ */
+struct C2WorkOrdinalStruct {
+//public:
+    c2_cntr64_t timestamp;     /** frame timestamp in microseconds */
+    c2_cntr64_t frameIndex;    /** submission ordinal on the initial component */
+    c2_cntr64_t customOrdinal; /** can be given by the component, e.g. decode order */
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(WorkOrdinal)
+    C2FIELD(timestamp, "timestamp")
+    C2FIELD(frameIndex, "frame-index")
+    C2FIELD(customOrdinal, "custom-ordinal")
+};
+
+/**
+ * This structure represents a Codec 2.0 frame with its metadata.
+ *
+ * A frame basically consists of an ordered sets of buffers, configuration changes and info buffers
+ * along with some non-configuration metadata.
+ */
+struct C2FrameData {
+//public:
+    enum flags_t : uint32_t {
+        /**
+         * For input frames: no output frame shall be generated when processing this frame, but
+         * metadata shall still be processed.
+         * For output frames: this frame shall be discarded and but metadata is still valid.
+         */
+        FLAG_DROP_FRAME    = (1 << 0),
+        /**
+         * This frame is the last frame of the current stream. Further frames are part of a new
+         * stream.
+         */
+        FLAG_END_OF_STREAM = (1 << 1),
+        /**
+         * This frame shall be discarded with its metadata.
+         * This flag is only set by components - e.g. as a response to the flush command.
+         */
+        FLAG_DISCARD_FRAME = (1 << 2),
+        /**
+         * This frame is not the last frame produced for the input.
+         *
+         * This flag is normally set by the component - e.g. when an input frame results in multiple
+         * output frames, this flag is set on all but the last output frame.
+         *
+         * Also, when components are chained, this flag should be propagated down the
+         * work chain. That is, if set on an earlier frame of a work-chain, it should be propagated
+         * to all later frames in that chain. Additionally, components down the chain could set
+         * this flag even if not set earlier, e.g. if multiple output frame is generated at that
+         * component for the input frame.
+         */
+        FLAG_INCOMPLETE = (1 << 3),
+        /**
+         * This frame contains only codec-specific configuration data, and no actual access unit.
+         *
+         * \deprecated pass codec configuration with using the \todo codec-specific configuration
+         * info together with the access unit.
+         */
+        FLAG_CODEC_CONFIG  = (1u << 31),
+    };
+
+    /**
+     * Frame flags */
+    flags_t  flags;
+    C2WorkOrdinalStruct ordinal;
+    std::vector<std::shared_ptr<C2Buffer>> buffers;
+    //< for initial work item, these may also come from the parser - if provided
+    //< for output buffers, these are the responses to requestedInfos
+    std::vector<std::unique_ptr<C2Param>>      configUpdate;
+    std::vector<std::shared_ptr<C2InfoBuffer>> infoBuffers;
+};
+
+struct C2Worklet {
+//public:
+    // IN
+    c2_node_id_t component;
+
+    /** Configuration changes to be applied before processing this worklet. */
+    std::vector<std::unique_ptr<C2Tuning>> tunings;
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+
+    // OUT
+    C2FrameData output;
+};
+
+/**
+ * Information about partial work-chains not part of the current work items.
+ *
+ * To be defined later.
+ */
+struct C2WorkChainInfo;
+
+/**
+ * This structure holds information about all a single work item.
+ *
+ * This structure shall be passed by the client to the component for the first worklet. As such,
+ * worklets must not be empty. The ownership of this object is passed.
+ */
+struct C2Work {
+//public:
+    /// additional work chain info not part of this work
+    std::shared_ptr<C2WorkChainInfo> chainInfo;
+
+    /// The input data to be processed as part of this work/work-chain. This is provided by the
+    /// client with ownership. When the work is returned (via onWorkDone), the input buffer-pack's
+    /// buffer vector shall contain nullptrs.
+    C2FrameData input;
+
+    /// The chain of components, tunings (including output buffer pool IDs) and info requests that the
+    /// data must pass through. If this has more than a single element, the tunnels between successive
+    /// components of the worklet chain must have been (successfully) pre-registered at the time that
+    /// the work is submitted. Allocating the output buffers in the worklets is the responsibility of
+    /// each component. Upon work submission, each output buffer-pack shall be an appropriately sized
+    /// vector containing nullptrs. When the work is completed/returned to the client, output buffers
+    /// pointers from all but the final worklet shall be nullptrs.
+    std::list<std::unique_ptr<C2Worklet>> worklets;
+
+    /// Number of worklets successfully processed in this chain. This shall be initialized to 0 by the
+    /// client when the work is submitted. It shall contain the number of worklets that were
+    /// successfully processed when the work is returned to the client. If this is less then the number
+    /// of worklets, result must not be success. It must be in the range of [0, worklets.size()].
+    uint32_t workletsProcessed;
+
+    /// The final outcome of the work (corresponding to the current workletsProcessed). If 0 when
+    /// work is returned, it is assumed that all worklets have been processed.
+    c2_status_t result;
+};
+
+/**
+ * Information about a future work to be submitted to the component. The information is used to
+ * reserve the work for work ordering purposes.
+ */
+struct C2WorkOutline {
+//public:
+    C2WorkOrdinalStruct ordinal;
+    std::vector<c2_node_id_t> chain;
+};
+
+/// @}
+
+#endif  // C2WORK_H_
diff --git a/media/codec2/core/include/_C2MacroUtils.h b/media/codec2/core/include/_C2MacroUtils.h
new file mode 100644
index 0000000..04e9ba5
--- /dev/null
+++ b/media/codec2/core/include/_C2MacroUtils.h
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+#ifndef C2UTILS_MACRO_UTILS_H_
+#define C2UTILS_MACRO_UTILS_H_
+
+/** \file
+ * Macro utilities for the utils library used by Codec2 implementations.
+ */
+
+/// \if 0
+
+/* --------------------------------- VARIABLE ARGUMENT COUNTING --------------------------------- */
+
+// remove empty arguments - _C2_ARG() expands to '', while _C2_ARG(x) expands to ', x'
+// _C2_ARGn(...) does the same for n arguments
+#define _C2_ARG(...) , ##__VA_ARGS__
+#define _C2_ARG2(_1, _2) _C2_ARG(_1) _C2_ARG(_2)
+#define _C2_ARG4(_1, _2, _3, _4) _C2_ARG2(_1, _2) _C2_ARG2(_3, _4)
+#define _C2_ARG8(_1, _2, _3, _4, _5, _6, _7, _8) _C2_ARG4(_1, _2, _3, _4) _C2_ARG4(_5, _6, _7, _8)
+#define _C2_ARG16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
+        _C2_ARG8(_1, _2, _3, _4, _5, _6, _7, _8) _C2_ARG8(_9, _10, _11, _12, _13, _14, _15, _16)
+
+// return the 65th argument
+#define _C2_ARGC_3(_, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, \
+        _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, \
+        _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, \
+        _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, ...) _64
+
+/// \endif
+
+/**
+ * Returns the number of arguments.
+ */
+// We do this by prepending 1 and appending 65 designed values such that the 65th element
+// will be the number of arguments.
+#define _C2_ARGC(...) _C2_ARGC_1(0, ##__VA_ARGS__, \
+        64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, \
+        42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \
+        20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+
+/// \if 0
+
+// step 1. remove empty arguments - this is needed to allow trailing comma in enum definitions
+// (NOTE: we don't know which argument will have this trailing comma so we have to try all)
+#define _C2_ARGC_1(_, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, \
+        _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, \
+        _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, \
+        _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, ...) \
+    _C2_ARGC_2(_ _C2_ARG(_0) \
+    _C2_ARG16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
+    _C2_ARG16(_17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) \
+    _C2_ARG16(_33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) \
+    _C2_ARG16(_49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64), \
+    ##__VA_ARGS__)
+
+// step 2. this is needed as removed arguments cannot be passed directly as empty into a macro
+#define _C2_ARGC_2(...) _C2_ARGC_3(__VA_ARGS__)
+
+/// \endif
+
+/* -------------------------------- VARIABLE ARGUMENT CONVERSION -------------------------------- */
+
+/// \if 0
+
+// macros that convert _1, _2, _3, ... to fn(_1, arg), fn(_2, arg), fn(_3, arg), ...
+#define _C2_MAP_64(fn, arg, head, ...) fn(head, arg), _C2_MAP_63(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_63(fn, arg, head, ...) fn(head, arg), _C2_MAP_62(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_62(fn, arg, head, ...) fn(head, arg), _C2_MAP_61(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_61(fn, arg, head, ...) fn(head, arg), _C2_MAP_60(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_60(fn, arg, head, ...) fn(head, arg), _C2_MAP_59(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_59(fn, arg, head, ...) fn(head, arg), _C2_MAP_58(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_58(fn, arg, head, ...) fn(head, arg), _C2_MAP_57(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_57(fn, arg, head, ...) fn(head, arg), _C2_MAP_56(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_56(fn, arg, head, ...) fn(head, arg), _C2_MAP_55(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_55(fn, arg, head, ...) fn(head, arg), _C2_MAP_54(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_54(fn, arg, head, ...) fn(head, arg), _C2_MAP_53(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_53(fn, arg, head, ...) fn(head, arg), _C2_MAP_52(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_52(fn, arg, head, ...) fn(head, arg), _C2_MAP_51(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_51(fn, arg, head, ...) fn(head, arg), _C2_MAP_50(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_50(fn, arg, head, ...) fn(head, arg), _C2_MAP_49(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_49(fn, arg, head, ...) fn(head, arg), _C2_MAP_48(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_48(fn, arg, head, ...) fn(head, arg), _C2_MAP_47(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_47(fn, arg, head, ...) fn(head, arg), _C2_MAP_46(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_46(fn, arg, head, ...) fn(head, arg), _C2_MAP_45(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_45(fn, arg, head, ...) fn(head, arg), _C2_MAP_44(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_44(fn, arg, head, ...) fn(head, arg), _C2_MAP_43(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_43(fn, arg, head, ...) fn(head, arg), _C2_MAP_42(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_42(fn, arg, head, ...) fn(head, arg), _C2_MAP_41(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_41(fn, arg, head, ...) fn(head, arg), _C2_MAP_40(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_40(fn, arg, head, ...) fn(head, arg), _C2_MAP_39(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_39(fn, arg, head, ...) fn(head, arg), _C2_MAP_38(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_38(fn, arg, head, ...) fn(head, arg), _C2_MAP_37(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_37(fn, arg, head, ...) fn(head, arg), _C2_MAP_36(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_36(fn, arg, head, ...) fn(head, arg), _C2_MAP_35(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_35(fn, arg, head, ...) fn(head, arg), _C2_MAP_34(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_34(fn, arg, head, ...) fn(head, arg), _C2_MAP_33(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_33(fn, arg, head, ...) fn(head, arg), _C2_MAP_32(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_32(fn, arg, head, ...) fn(head, arg), _C2_MAP_31(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_31(fn, arg, head, ...) fn(head, arg), _C2_MAP_30(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_30(fn, arg, head, ...) fn(head, arg), _C2_MAP_29(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_29(fn, arg, head, ...) fn(head, arg), _C2_MAP_28(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_28(fn, arg, head, ...) fn(head, arg), _C2_MAP_27(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_27(fn, arg, head, ...) fn(head, arg), _C2_MAP_26(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_26(fn, arg, head, ...) fn(head, arg), _C2_MAP_25(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_25(fn, arg, head, ...) fn(head, arg), _C2_MAP_24(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_24(fn, arg, head, ...) fn(head, arg), _C2_MAP_23(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_23(fn, arg, head, ...) fn(head, arg), _C2_MAP_22(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_22(fn, arg, head, ...) fn(head, arg), _C2_MAP_21(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_21(fn, arg, head, ...) fn(head, arg), _C2_MAP_20(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_20(fn, arg, head, ...) fn(head, arg), _C2_MAP_19(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_19(fn, arg, head, ...) fn(head, arg), _C2_MAP_18(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_18(fn, arg, head, ...) fn(head, arg), _C2_MAP_17(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_17(fn, arg, head, ...) fn(head, arg), _C2_MAP_16(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_16(fn, arg, head, ...) fn(head, arg), _C2_MAP_15(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_15(fn, arg, head, ...) fn(head, arg), _C2_MAP_14(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_14(fn, arg, head, ...) fn(head, arg), _C2_MAP_13(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_13(fn, arg, head, ...) fn(head, arg), _C2_MAP_12(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_12(fn, arg, head, ...) fn(head, arg), _C2_MAP_11(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_11(fn, arg, head, ...) fn(head, arg), _C2_MAP_10(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_10(fn, arg, head, ...) fn(head, arg), _C2_MAP_9(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_9(fn, arg, head, ...) fn(head, arg), _C2_MAP_8(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_8(fn, arg, head, ...) fn(head, arg), _C2_MAP_7(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_7(fn, arg, head, ...) fn(head, arg), _C2_MAP_6(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_6(fn, arg, head, ...) fn(head, arg), _C2_MAP_5(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_5(fn, arg, head, ...) fn(head, arg), _C2_MAP_4(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_4(fn, arg, head, ...) fn(head, arg), _C2_MAP_3(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_3(fn, arg, head, ...) fn(head, arg), _C2_MAP_2(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_2(fn, arg, head, ...) fn(head, arg), _C2_MAP_1(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_1(fn, arg, head, ...) fn(head, arg)
+
+/// \endif
+
+/**
+ * Maps each argument using another macro x -> fn(x, arg)
+ */
+// use wrapper to call the proper mapper based on the number of arguments
+#define _C2_MAP(fn, arg, ...) _C2_MAP__(_C2_ARGC(__VA_ARGS__), fn, arg, ##__VA_ARGS__)
+
+/// \if 0
+
+// evaluate _n so it becomes a number
+#define _C2_MAP__(_n, fn, arg, ...) _C2_MAP_(_n, fn, arg, __VA_ARGS__)
+// call the proper mapper
+#define _C2_MAP_(_n, fn, arg, ...) _C2_MAP_##_n (fn, arg, __VA_ARGS__)
+
+/// \endif
+
+#endif  // C2UTILS_MACRO_UTILS_H_
diff --git a/media/codec2/core/include/android-C2Buffer.h b/media/codec2/core/include/android-C2Buffer.h
new file mode 100644
index 0000000..26beab7
--- /dev/null
+++ b/media/codec2/core/include/android-C2Buffer.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_C2BUFFER_H_
+#define ANDROID_C2BUFFER_H_
+
+#include <cutils/native_handle.h>
+#include <hardware/gralloc.h>
+
+/* Use android native handle for C2Handle */
+typedef ::native_handle_t C2Handle;
+
+namespace android {
+
+/**
+ * Android platform buffer/memory usage bits.
+ */
+struct C2AndroidMemoryUsage : public C2MemoryUsage {
+    inline C2AndroidMemoryUsage(const C2MemoryUsage &usage) : C2MemoryUsage(usage) { }
+
+// public:
+    /**
+     * Reuse gralloc flags where possible, as Codec 2.0 API only uses bits 0 and 1.
+     */
+    enum consumer_t : uint64_t {
+        RENDERSCRIPT_READ = GRALLOC_USAGE_RENDERSCRIPT,
+        HW_TEXTURE_READ   = GRALLOC_USAGE_HW_TEXTURE,
+        HW_COMPOSER_READ  = GRALLOC_USAGE_HW_COMPOSER,
+        // gralloc does not define a video decoder read usage flag, so use encoder for
+        // now
+        HW_CODEC_READ     = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+        READ_PROTECTED    = GRALLOC_USAGE_PROTECTED,
+    };
+
+    enum producer_t : uint64_t {
+        RENDERSCRIPT_WRITE = GRALLOC_USAGE_RENDERSCRIPT,
+        HW_TEXTURE_WRITE   = GRALLOC_USAGE_HW_RENDER,
+        HW_COMPOSER_WRITE  = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER,
+        HW_CODEC_WRITE     = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+        // gralloc does not define a write protected usage flag, so use read protected
+        // now
+        WRITE_PROTECTED    = GRALLOC_USAGE_PROTECTED,
+    };
+
+    /**
+     * Convert from gralloc usage.
+     */
+    static C2MemoryUsage FromGrallocUsage(uint64_t usage);
+
+    /**
+     * Convert to gralloc usage.
+     */
+    uint64_t asGrallocUsage() const;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_C2BUFFER_H_
diff --git a/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurface.h b/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurface.h
new file mode 100644
index 0000000..0a82a68
--- /dev/null
+++ b/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurface.h
@@ -0,0 +1,70 @@
+/*
+ * 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 ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_H
+#define ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_H
+
+#include <memory>
+
+#include <C2Component.h>
+#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
+
+namespace android {
+
+class GraphicBufferSource;
+
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+        HGraphicBufferProducer;
+typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
+
+// TODO: ::android::TWGraphicBufferProducer<IInputSurface>
+typedef ::android::TWGraphicBufferProducer<HGraphicBufferProducer> InputSurfaceBase;
+
+class InputSurface : public InputSurfaceBase {
+public:
+    virtual ~InputSurface() = default;
+
+    // Methods from IInputSurface
+    sp<InputSurfaceConnection> connectToComponent(
+            const std::shared_ptr<::C2Component> &comp);
+    // TODO: intf()
+
+    static sp<InputSurface> Create();
+
+private:
+    InputSurface(
+            const sp<BGraphicBufferProducer> &base,
+            const sp<::android::GraphicBufferSource> &source);
+
+    sp<::android::GraphicBufferSource> mSource;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_H
diff --git a/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h b/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
new file mode 100644
index 0000000..5eae3af
--- /dev/null
+++ b/media/codec2/core/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_CONNECTION_H
+#define ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_CONNECTION_H
+
+#include <memory>
+
+#include <C2Component.h>
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+// TODO: inherit from IInputSurfaceConnection
+class InputSurfaceConnection : public RefBase {
+public:
+    virtual ~InputSurfaceConnection();
+
+    // From IInputSurfaceConnection
+    void disconnect();
+
+private:
+    friend class InputSurface;
+
+    // For InputSurface
+    InputSurfaceConnection(
+            const sp<GraphicBufferSource> &source, const std::shared_ptr<C2Component> &comp);
+    bool init();
+
+    InputSurfaceConnection() = delete;
+
+    class Impl;
+
+    sp<GraphicBufferSource> mSource;
+    sp<Impl> mImpl;
+
+    DISALLOW_EVIL_CONSTRUCTORS(InputSurfaceConnection);
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_CONNECTION_H
diff --git a/media/codec2/docs/doxyfilter.sh b/media/codec2/docs/doxyfilter.sh
new file mode 100755
index 0000000..d813153
--- /dev/null
+++ b/media/codec2/docs/doxyfilter.sh
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+import re, sys
+
+global in_comment, current, indent, hold
+in_comment, current, indent, hold = False, None, '', []
+
+class ChangeCStyleCommentsToDoxy:
+    def dump_hold():
+        global hold
+        for h in hold:
+            print(h, end='')
+        hold[:] = []
+
+    def doxy_hold():
+        global current, hold
+        if current == '//':
+            for h in hold:
+                print(re.sub(r'^( *//(?!/))', r'\1/', h), end='')
+        else:
+            first = True
+            for h in hold:
+                if first:
+                    h = re.sub(r'^( */[*](?![*]))', r'\1*', h)
+                    first = False
+                print(h, end='')
+        hold[:] = []
+
+    def process_comment(t, ind, line):
+        global current, indent, hold
+        if t != current or ind not in (indent, indent + ' '):
+            dump_hold()
+            current, indent = t, ind
+        hold.append(line)
+
+    def process_line(ind, line):
+        global current, indent
+        if ind in (indent, ''):
+            doxy_hold()
+        else:
+            dump_hold()
+        current, indent = None, None
+        print(line, end='')
+
+    def process(self, input, path):
+        for line in input:
+            ind = re.match(r'^( *)', line).group(1)
+            if in_comment:
+                # TODO: this is not quite right, but good enough
+                m = re.match(r'^ *[*]/', line)
+                if m:
+                    process_comment('/*', ind, line)
+                    in_comment = False
+                else:
+                    process_comment('/*', ind, line)
+                continue
+            m = re.match(r'^ *//', line)
+            if m:
+                # one-line comment
+                process_comment('//', ind, line)
+                continue
+            m = re.match(r'^ */[*]', line)
+            if m:
+                # multi-line comment
+                process_comment('/*', ind, line)
+                # TODO: this is not quite right, but good enough
+                in_comment = not re.match(r'^ *[*]/', line)
+                continue
+            process_line(ind, line)
+
+class AutoGroup:
+    def process(self, input, path):
+        if '/codec2/include/' in path:
+            group = 'API Codec2 API'
+        elif False:
+            return
+        elif '/codec2/vndk/' in path:
+            group = 'VNDK Platform provided glue'
+        elif '/codec2/tests/' in path:
+            group = 'Tests Unit tests'
+        else:
+            group = 'Random Misc. sandbox'
+
+        print('#undef __APPLE__')
+
+        for line in input:
+            if re.match(r'^namespace android {', line):
+                print(line, end='')
+                print()
+                print(r'/// \addtogroup {}'.format(group))
+                print(r'/// @{')
+                continue
+            elif re.match(r'^} +// +namespace', line):
+                print(r'/// @}')
+                print()
+            print(line, end='')
+
+P = AutoGroup()
+for path in sys.argv[1:]:
+    with open(path, 'rt') as input:
+        P.process(input, path)
diff --git a/media/codec2/docs/doxygen.config b/media/codec2/docs/doxygen.config
new file mode 100644
index 0000000..5c3bea3
--- /dev/null
+++ b/media/codec2/docs/doxygen.config
@@ -0,0 +1,2446 @@
+# Doxyfile 1.8.11
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = Codec2
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         = 
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = 
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO           = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = out/target/common/docs/codec2/api
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = YES
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        = frameworks/av/media/libstagefright/codec2
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                = 
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      = 
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = YES
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = YES
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       = INTERNAL
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    = 
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            = 
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR          = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = frameworks/av/media/libstagefright/codec2/
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl,
+# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js.
+
+FILE_PATTERNS          = C2*.c \
+                         C2*.cpp \
+                         C2*.h
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = ._*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER           = frameworks/av/media/libstagefright/codec2/docs/doxyfilter.sh
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = YES
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS = 
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = YES
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS          = -std=c++14
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        = 
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  = 
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       = 
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = YES
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               = 
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           = 
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     = 
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               = 
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   = 
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  = 
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  = 
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           = 
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     = 
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       = 
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       = 
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     = 
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           = 
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           = 
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET = 
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    = 
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             = 
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = YES
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           = /Volumes/A/aosp/prebuilts/clang/darwin-x86/host/3.6/lib/clang/3.6/include \
+                         /Volumes/A/aosp/external/libcxx/include \
+                         /Volumes/A/aosp/bionic/libc/include \
+                         /Volumes/A/aosp/bionic/libc/kernel/uapi \
+                         /Volumes/A/aosp/bionic/libc/kernel/uapi/asm-arm64 \
+                         /Volumes/A/aosp/external/gtest
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = __APPLE__= \
+                         __ANDROID__=1 \
+                         ANDROID:=1 \
+			 __unused=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      = DEFINE_FLEXIBLE_METHODS \
+                         DEFINE_CAST_OPERATORS
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            = 
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               = 
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           = 
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           = 
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           = 
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           = 
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      = 
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH  = 
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/media/codec2/faultinjection/Android.bp b/media/codec2/faultinjection/Android.bp
new file mode 100644
index 0000000..a0ad3ce
--- /dev/null
+++ b/media/codec2/faultinjection/Android.bp
@@ -0,0 +1,29 @@
+cc_library_shared {
+    name: "libcodec2_component_wrapper",
+    vendor_available: true,
+
+    srcs: [
+        "C2ComponentWrapper.cpp",
+        "SimpleMethodState.cpp",
+    ],
+
+    shared_libs: [
+        "libcodec2",
+        "libcodec2_vndk",
+        "libcutils",
+        "liblog",
+        "libstagefright_foundation",
+        "libutils",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+    },
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
diff --git a/media/codec2/faultinjection/C2ComponentWrapper.cpp b/media/codec2/faultinjection/C2ComponentWrapper.cpp
new file mode 100644
index 0000000..c45f8bf
--- /dev/null
+++ b/media/codec2/faultinjection/C2ComponentWrapper.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 "C2ComponentWrapper"
+
+#include <chrono>
+#include <functional>
+#include <thread>
+
+#include <C2ComponentWrapper.h>
+#include <C2Config.h>
+#include <C2PlatformSupport.h>
+
+namespace android {
+
+namespace {
+
+using namespace std::chrono_literals;
+
+c2_status_t WrapSimpleMethod(
+        std::function<c2_status_t(void)> op, const SimpleMethodState &state) {
+    c2_status_t result = C2_OK;
+    switch (state.getMode()) {
+        case SimpleMethodState::EXECUTE:
+            result = op();
+            break;
+        case SimpleMethodState::NO_OP:
+            break;
+        case SimpleMethodState::HANG:
+            while (true) {
+                std::this_thread::sleep_for(1s);
+            }
+            break;
+    }
+    (void)state.overrideResult(&result);
+    return result;
+}
+
+}  // namespace
+
+C2ComponentWrapper::Injecter::Injecter(C2ComponentWrapper *thiz) : mThiz(thiz) {}
+
+SimpleMethodState::Injecter C2ComponentWrapper::Injecter::start() {
+    return SimpleMethodState::Injecter(&mThiz->mStartState);
+}
+
+C2ComponentWrapper::Listener::Listener(
+        const std::shared_ptr<C2Component::Listener> &listener) : mListener(listener) {}
+
+void C2ComponentWrapper::Listener::onWorkDone_nb(std::weak_ptr<C2Component> component,
+        std::list<std::unique_ptr<C2Work>> workItems) {
+    mListener->onWorkDone_nb(component, std::move(workItems));
+}
+
+void C2ComponentWrapper::Listener::onTripped_nb(std::weak_ptr<C2Component> component,
+        std::vector<std::shared_ptr<C2SettingResult>> settingResult) {
+    mListener->onTripped_nb(component,settingResult);
+}
+
+void C2ComponentWrapper::Listener::onError_nb(
+        std::weak_ptr<C2Component> component, uint32_t errorCode) {
+    mListener->onError_nb(component, errorCode);
+}
+
+C2ComponentWrapper::C2ComponentWrapper(
+        const std::shared_ptr<C2Component> &comp) : mComp(comp) {}
+
+c2_status_t C2ComponentWrapper::setListener_vb(
+        const std::shared_ptr<C2Component::Listener> &listener, c2_blocking_t mayBlock) {
+    mListener = std::make_shared<Listener>(listener);
+    return mComp->setListener_vb(mListener, mayBlock);
+}
+
+c2_status_t C2ComponentWrapper::queue_nb(std::list<std::unique_ptr<C2Work>>* const items) {
+    return mComp->queue_nb(items);
+}
+
+c2_status_t C2ComponentWrapper::announce_nb(const std::vector<C2WorkOutline> &items) {
+    return mComp->announce_nb(items);
+}
+
+c2_status_t C2ComponentWrapper::flush_sm(
+        C2Component::flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+    return mComp->flush_sm(mode, flushedWork);
+}
+
+c2_status_t C2ComponentWrapper::drain_nb(C2Component::drain_mode_t mode) {
+    return mComp->drain_nb(mode);
+}
+
+c2_status_t C2ComponentWrapper::start() {
+    return WrapSimpleMethod([this] { return mComp->start(); }, mStartState);
+}
+
+c2_status_t C2ComponentWrapper::stop() {
+    return mComp->stop();
+}
+
+c2_status_t C2ComponentWrapper::reset() {
+    return mComp->reset();
+}
+
+c2_status_t C2ComponentWrapper::release() {
+    return mComp->release();
+}
+
+std::shared_ptr<C2ComponentInterface> C2ComponentWrapper::intf(){
+    return mComp->intf();
+}
+
+C2ComponentWrapper::Injecter C2ComponentWrapper::inject() {
+    return Injecter(this);
+}
+
+}  // namespace android
diff --git a/media/codec2/faultinjection/C2ComponentWrapper.h b/media/codec2/faultinjection/C2ComponentWrapper.h
new file mode 100644
index 0000000..737350d
--- /dev/null
+++ b/media/codec2/faultinjection/C2ComponentWrapper.h
@@ -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.
+ */
+
+#ifndef C2_COMPONENT_WRAPPER_H_
+#define C2_COMPONENT_WRAPPER_H_
+
+#include <C2Component.h>
+
+#include "SimpleMethodState.h"
+
+namespace android {
+
+/**
+ * Creates a Wrapper around the class C2Component and its methods. The wrapper is used to
+ * simulate errors in the android media components by fault injection technique.
+ * This is done to check how the framework handles the error situation.
+ */
+class C2ComponentWrapper
+        : public C2Component, public std::enable_shared_from_this<C2ComponentWrapper> {
+public:
+    class Injecter {
+    public:
+        explicit Injecter(C2ComponentWrapper *thiz);
+
+        SimpleMethodState::Injecter start();
+    private:
+        C2ComponentWrapper *const mThiz;
+    };
+
+    /**
+     * A wrapper around the listener class inside C2Component class.
+     */
+    class Listener : public C2Component::Listener {
+    public:
+        explicit Listener(const std::shared_ptr<C2Component::Listener> &listener);
+        virtual ~Listener() = default;
+
+        void onWorkDone_nb(std::weak_ptr<C2Component> component,
+                           std::list<std::unique_ptr<C2Work>> workItems) override;
+        void onTripped_nb(std::weak_ptr<C2Component> component,
+                          std::vector<std::shared_ptr<C2SettingResult>> settingResult) override;
+        void onError_nb(std::weak_ptr<C2Component> component, uint32_t errorCode) override;
+
+    private:
+        std::shared_ptr<C2Component::Listener> mListener;
+    };
+
+    explicit C2ComponentWrapper(const std::shared_ptr<C2Component> &comp);
+    virtual ~C2ComponentWrapper() = default;
+
+    virtual c2_status_t setListener_vb(
+            const std::shared_ptr<C2Component::Listener> &listener,
+            c2_blocking_t mayBlock) override;
+    virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
+    virtual c2_status_t announce_nb(const std::vector<C2WorkOutline> &items) override;
+    virtual c2_status_t flush_sm(
+            flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
+    virtual c2_status_t drain_nb(drain_mode_t mode) override;
+    virtual c2_status_t start() override;
+    virtual c2_status_t stop() override;
+    virtual c2_status_t reset() override;
+    virtual c2_status_t release() override;
+    virtual std::shared_ptr<C2ComponentInterface> intf() override;
+
+    Injecter inject();
+
+private:
+    std::shared_ptr<Listener> mListener;
+    std::shared_ptr<C2Component> mComp;
+
+    SimpleMethodState mStartState;
+};
+
+}  // namespace android
+
+#endif // C2_COMPONENT_WRAPPER_H_
diff --git a/media/codec2/faultinjection/SimpleMethodState.cpp b/media/codec2/faultinjection/SimpleMethodState.cpp
new file mode 100644
index 0000000..179d64e
--- /dev/null
+++ b/media/codec2/faultinjection/SimpleMethodState.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 "SimpleMethodState"
+#include <log/log.h>
+
+#include "SimpleMethodState.h"
+
+namespace android {
+
+SimpleMethodState::Injecter::Injecter(SimpleMethodState *thiz) : mThiz(thiz) {}
+
+void SimpleMethodState::Injecter::hang() {
+    mThiz->mMode = HANG;
+}
+
+void SimpleMethodState::Injecter::fail(c2_status_t err, bool execute) {
+    mThiz->mMode = execute ? EXECUTE : NO_OP;
+    mThiz->mOverride = true;
+    mThiz->mResultOverride = err;
+}
+
+SimpleMethodState::SimpleMethodState()
+    : mMode(EXECUTE), mOverride(false), mResultOverride(C2_OK) {}
+
+SimpleMethodState::Mode SimpleMethodState::getMode() const {
+    return mMode;
+}
+
+bool SimpleMethodState::overrideResult(c2_status_t *result) const {
+    if (!mOverride) {
+        return false;
+    }
+    *result = mResultOverride;
+    return true;
+}
+
+}  // namespace android
diff --git a/media/codec2/faultinjection/SimpleMethodState.h b/media/codec2/faultinjection/SimpleMethodState.h
new file mode 100644
index 0000000..dc0459d
--- /dev/null
+++ b/media/codec2/faultinjection/SimpleMethodState.h
@@ -0,0 +1,82 @@
+/*
+ * 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 SIMPLE_METHOD_STATE_H_
+#define SIMPLE_METHOD_STATE_H_
+
+#include <C2.h>
+
+namespace android {
+
+/**
+ * State for a simple method which returns c2_status_t and takes no parameters.
+ */
+class SimpleMethodState {
+public:
+    enum Mode {
+        // Execute the normal operation
+        EXECUTE,
+        // Don't do anything
+        NO_OP,
+        // Hang; never return
+        HANG,
+    };
+
+    /**
+     * Injecter class that modifies the internal states of this class.
+     */
+    class Injecter {
+    public:
+        explicit Injecter(SimpleMethodState *thiz);
+
+        /**
+         * Hang the operation.
+         */
+        void hang();
+
+        /**
+         * Fail the operation with given params.
+         *
+         * \param err       error code to replace the actual return value
+         * \param execute   whether the wrapper should execute the operation
+         */
+        void fail(c2_status_t err, bool execute = false);
+
+    private:
+        SimpleMethodState *const mThiz;
+    };
+
+    SimpleMethodState();
+
+    /**
+     * Get execution mode.
+     */
+    Mode getMode() const;
+
+    /**
+     * Override result from running the operation if configured so.
+     */
+    bool overrideResult(c2_status_t *result) const;
+
+private:
+    Mode mMode;
+    bool mOverride;
+    c2_status_t mResultOverride;
+};
+
+}  // namespace android
+
+#endif // SIMPLE_METHOD_STATE_H_
diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp
new file mode 100644
index 0000000..63fe36b
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/Android.bp
@@ -0,0 +1,126 @@
+// DO NOT DEPEND ON THIS DIRECTLY
+// use libcodec2-hidl-client-defaults instead
+cc_library {
+    name: "libcodec2_hidl_client@1.0",
+
+    defaults: ["hidl_defaults"],
+
+    srcs: [
+        "ClientBlockHelper.cpp",
+        "types.cpp",
+    ],
+
+    header_libs: [
+        "libcodec2_internal", // private
+    ],
+
+    shared_libs: [
+        "android.hardware.media.bufferpool@2.0",
+        "android.hardware.media.c2@1.0",
+        "libbase",
+        "libcodec2",
+        "libcodec2_vndk",
+        "libcutils",
+        "libgui",
+        "libhidlbase",
+        "liblog",
+        "libstagefright_bufferpool@2.0",
+        "libui",
+        "libutils",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "android.hardware.media.c2@1.0",
+        "libcodec2",
+        "libgui",
+        "libstagefright_bufferpool@2.0",
+        "libui",
+    ],
+}
+
+
+// DO NOT DEPEND ON THIS DIRECTLY
+// use libcodec2-hidl-defaults instead
+cc_library {
+    name: "libcodec2_hidl@1.0",
+    vendor_available: true,
+
+    defaults: ["hidl_defaults"],
+
+    srcs: [
+        "Component.cpp",
+        "ComponentInterface.cpp",
+        "ComponentStore.cpp",
+        "Configurable.cpp",
+        "InputBufferManager.cpp",
+        "InputSurface.cpp",
+        "InputSurfaceConnection.cpp",
+        "types.cpp",
+    ],
+
+    header_libs: [
+        "libsystem_headers",
+        "libcodec2_internal", // private
+    ],
+
+    shared_libs: [
+        "android.hardware.graphics.bufferqueue@1.0",
+        "android.hardware.graphics.bufferqueue@2.0",
+        "android.hardware.graphics.common@1.0",
+        "android.hardware.media@1.0",
+        "android.hardware.media.bufferpool@2.0",
+        "android.hardware.media.c2@1.0",
+        "android.hardware.media.omx@1.0",
+        "libbase",
+        "libcodec2",
+        "libcodec2_vndk",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libstagefright_bufferpool@2.0",
+        "libstagefright_bufferqueue_helper",
+        "libui",
+        "libutils",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "android.hardware.media.c2@1.0",
+        "libcodec2",
+        "libcodec2_vndk",
+        "libhidlbase",
+        "libstagefright_bufferpool@2.0",
+        "libui",
+    ],
+}
+
+// public dependency for Codec 2.0 HAL service implementations
+cc_defaults {
+    name: "libcodec2-hidl-defaults",
+    defaults: ["libcodec2-impl-defaults"],
+
+    shared_libs: [
+        "android.hardware.media.c2@1.0",
+        "libcodec2_hidl@1.0",
+    ],
+}
+
+// public dependency for Codec 2.0 HAL client
+cc_defaults {
+    name: "libcodec2-hidl-client-defaults",
+    defaults: ["libcodec2-impl-defaults"],
+
+    shared_libs: [
+        "android.hardware.media.c2@1.0",
+        "libcodec2_hidl_client@1.0",
+    ],
+}
diff --git a/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
new file mode 100644
index 0000000..50790bc
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/ClientBlockHelper.cpp
@@ -0,0 +1,371 @@
+/*
+ * 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 "Codec2-block_helper"
+#include <android-base/logging.h>
+
+#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
+#include <codec2/hidl/1.0/ClientBlockHelper.h>
+#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2BlockInternal.h>
+#include <C2Buffer.h>
+#include <C2PlatformSupport.h>
+
+#include <iomanip>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::
+        V2_0::IGraphicBufferProducer;
+using B2HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::
+        V2_0::utils::B2HGraphicBufferProducer;
+
+namespace /* unnamed */ {
+
+// Create a GraphicBuffer object from a graphic block.
+sp<GraphicBuffer> createGraphicBuffer(const C2ConstGraphicBlock& block) {
+    uint32_t width;
+    uint32_t height;
+    uint32_t format;
+    uint64_t usage;
+    uint32_t stride;
+    uint32_t generation;
+    uint64_t bqId;
+    int32_t bqSlot;
+    _UnwrapNativeCodec2GrallocMetadata(
+            block.handle(), &width, &height, &format, &usage,
+            &stride, &generation, &bqId, reinterpret_cast<uint32_t*>(&bqSlot));
+    native_handle_t *grallocHandle =
+            UnwrapNativeCodec2GrallocHandle(block.handle());
+    sp<GraphicBuffer> graphicBuffer =
+            new GraphicBuffer(grallocHandle,
+                              GraphicBuffer::CLONE_HANDLE,
+                              width, height, format,
+                              1, usage, stride);
+    native_handle_delete(grallocHandle);
+    return graphicBuffer;
+}
+
+template <typename BlockProcessor>
+void forEachBlock(C2FrameData& frameData,
+                  BlockProcessor process) {
+    for (const std::shared_ptr<C2Buffer>& buffer : frameData.buffers) {
+        if (buffer) {
+            for (const C2ConstGraphicBlock& block :
+                    buffer->data().graphicBlocks()) {
+                process(block);
+            }
+        }
+    }
+}
+
+template <typename BlockProcessor>
+void forEachBlock(const std::list<std::unique_ptr<C2Work>>& workList,
+                  BlockProcessor process) {
+    for (const std::unique_ptr<C2Work>& work : workList) {
+        if (!work) {
+            continue;
+        }
+        for (const std::unique_ptr<C2Worklet>& worklet : work->worklets) {
+            if (worklet) {
+                forEachBlock(worklet->output, process);
+            }
+        }
+    }
+}
+
+sp<HGraphicBufferProducer> getHgbp(const sp<IGraphicBufferProducer>& igbp) {
+    sp<HGraphicBufferProducer> hgbp =
+            igbp->getHalInterface<HGraphicBufferProducer>();
+    return hgbp ? hgbp :
+            new B2HGraphicBufferProducer(igbp);
+}
+
+status_t attachToBufferQueue(const C2ConstGraphicBlock& block,
+                             const sp<IGraphicBufferProducer>& igbp,
+                             uint32_t generation,
+                             int32_t* bqSlot) {
+    if (!igbp) {
+        LOG(WARNING) << "attachToBufferQueue -- null producer.";
+        return NO_INIT;
+    }
+
+    sp<GraphicBuffer> graphicBuffer = createGraphicBuffer(block);
+    graphicBuffer->setGenerationNumber(generation);
+
+    LOG(VERBOSE) << "attachToBufferQueue -- attaching buffer:"
+            << " block dimension " << block.width() << "x"
+                                   << block.height()
+            << ", graphicBuffer dimension " << graphicBuffer->getWidth() << "x"
+                                           << graphicBuffer->getHeight()
+            << std::hex << std::setfill('0')
+            << ", format 0x" << std::setw(8) << graphicBuffer->getPixelFormat()
+            << ", usage 0x" << std::setw(16) << graphicBuffer->getUsage()
+            << std::dec << std::setfill(' ')
+            << ", stride " << graphicBuffer->getStride()
+            << ", generation " << graphicBuffer->getGenerationNumber();
+
+    status_t result = igbp->attachBuffer(bqSlot, graphicBuffer);
+    if (result != OK) {
+        LOG(WARNING) << "attachToBufferQueue -- attachBuffer failed: "
+                        "status = " << result << ".";
+        return result;
+    }
+    LOG(VERBOSE) << "attachToBufferQueue -- attachBuffer returned slot #"
+                 << *bqSlot << ".";
+    return OK;
+}
+
+bool getBufferQueueAssignment(const C2ConstGraphicBlock& block,
+                              uint32_t* generation,
+                              uint64_t* bqId,
+                              int32_t* bqSlot) {
+    return _C2BlockFactory::GetBufferQueueData(
+            _C2BlockFactory::GetGraphicBlockPoolData(block),
+            generation, bqId, bqSlot);
+}
+} // unnamed namespace
+
+class OutputBufferQueue::Impl {
+    std::mutex mMutex;
+    sp<IGraphicBufferProducer> mIgbp;
+    uint32_t mGeneration;
+    uint64_t mBqId;
+    std::shared_ptr<int> mOwner;
+    // To migrate existing buffers
+    sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; // find a better way
+    std::weak_ptr<_C2BlockPoolData>
+                    mPoolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
+public:
+    Impl(): mGeneration(0), mBqId(0) {}
+
+    bool configure(const sp<IGraphicBufferProducer>& igbp,
+                   uint32_t generation,
+                   uint64_t bqId) {
+        size_t tryNum = 0;
+        size_t success = 0;
+        sp<GraphicBuffer> buffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+        std::weak_ptr<_C2BlockPoolData>
+                poolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS];
+        {
+            std::scoped_lock<std::mutex> l(mMutex);
+            if (generation == mGeneration) {
+                return false;
+            }
+            mIgbp = igbp;
+            mGeneration = generation;
+            mBqId = bqId;
+            mOwner = std::make_shared<int>(0);
+            for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
+                if (mBqId == 0 || !mBuffers[i]) {
+                    continue;
+                }
+                std::shared_ptr<_C2BlockPoolData> data = mPoolDatas[i].lock();
+                if (!data ||
+                    !_C2BlockFactory::BeginAttachBlockToBufferQueue(data)) {
+                    continue;
+                }
+                ++tryNum;
+                int bqSlot;
+                mBuffers[i]->setGenerationNumber(generation);
+                status_t result = igbp->attachBuffer(&bqSlot, mBuffers[i]);
+                if (result != OK) {
+                    continue;
+                }
+                bool attach =
+                        _C2BlockFactory::EndAttachBlockToBufferQueue(
+                                data, mOwner, getHgbp(mIgbp),
+                                generation, bqId, bqSlot);
+                if (!attach) {
+                    igbp->cancelBuffer(bqSlot, Fence::NO_FENCE);
+                    continue;
+                }
+                buffers[bqSlot] = mBuffers[i];
+                poolDatas[bqSlot] = data;
+                ++success;
+            }
+            for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) {
+                mBuffers[i] = buffers[i];
+                mPoolDatas[i] = poolDatas[i];
+            }
+        }
+        ALOGD("remote graphic buffer migration %zu/%zu", success, tryNum);
+        return true;
+    }
+
+    bool registerBuffer(const C2ConstGraphicBlock& block) {
+        std::shared_ptr<_C2BlockPoolData> data =
+                _C2BlockFactory::GetGraphicBlockPoolData(block);
+        if (!data) {
+            return false;
+        }
+        std::scoped_lock<std::mutex> l(mMutex);
+
+        if (!mIgbp) {
+            return false;
+        }
+
+        uint32_t oldGeneration;
+        uint64_t oldId;
+        int32_t oldSlot;
+        // If the block is not bufferqueue-based, do nothing.
+        if (!_C2BlockFactory::GetBufferQueueData(
+                data, &oldGeneration, &oldId, &oldSlot) || (oldId == 0)) {
+            return false;
+        }
+        // If the block's bqId is the same as the desired bqId, just hold.
+        if ((oldId == mBqId) && (oldGeneration == mGeneration)) {
+            LOG(VERBOSE) << "holdBufferQueueBlock -- import without attaching:"
+                         << " bqId " << oldId
+                         << ", bqSlot " << oldSlot
+                         << ", generation " << mGeneration
+                         << ".";
+            _C2BlockFactory::HoldBlockFromBufferQueue(data, mOwner, getHgbp(mIgbp));
+            mPoolDatas[oldSlot] = data;
+            mBuffers[oldSlot] = createGraphicBuffer(block);
+            mBuffers[oldSlot]->setGenerationNumber(mGeneration);
+            return true;
+        }
+        int32_t d = (int32_t) mGeneration - (int32_t) oldGeneration;
+        LOG(WARNING) << "receiving stale buffer: generation "
+                     << mGeneration << " , diff " << d  << " : slot "
+                     << oldSlot;
+        return false;
+    }
+
+    status_t outputBuffer(
+            const C2ConstGraphicBlock& block,
+            const BnGraphicBufferProducer::QueueBufferInput& input,
+            BnGraphicBufferProducer::QueueBufferOutput* output) {
+        uint32_t generation;
+        uint64_t bqId;
+        int32_t bqSlot;
+        bool display = displayBufferQueueBlock(block);
+        if (!getBufferQueueAssignment(block, &generation, &bqId, &bqSlot) ||
+            bqId == 0) {
+            // Block not from bufferqueue -- it must be attached before queuing.
+
+            mMutex.lock();
+            sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+            uint32_t outputGeneration = mGeneration;
+            mMutex.unlock();
+
+            status_t status = attachToBufferQueue(
+                    block, outputIgbp, outputGeneration, &bqSlot);
+            if (status != OK) {
+                LOG(WARNING) << "outputBuffer -- attaching failed.";
+                return INVALID_OPERATION;
+            }
+
+            status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+                                         input, output);
+            if (status != OK) {
+                LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
+                           "on non-bufferqueue-based block. "
+                           "Error = " << status << ".";
+                return status;
+            }
+            return OK;
+        }
+
+        mMutex.lock();
+        sp<IGraphicBufferProducer> outputIgbp = mIgbp;
+        uint32_t outputGeneration = mGeneration;
+        uint64_t outputBqId = mBqId;
+        mMutex.unlock();
+
+        if (!outputIgbp) {
+            LOG(VERBOSE) << "outputBuffer -- output surface is null.";
+            return NO_INIT;
+        }
+
+        if (!display) {
+            LOG(WARNING) << "outputBuffer -- cannot display "
+                         "bufferqueue-based block to the bufferqueue.";
+            return UNKNOWN_ERROR;
+        }
+        if (bqId != outputBqId || generation != outputGeneration) {
+            int32_t diff = (int32_t) outputGeneration - (int32_t) generation;
+            LOG(WARNING) << "outputBuffer -- buffers from old generation to "
+                         << outputGeneration << " , diff: " << diff
+                         << " , slot: " << bqSlot;
+            return DEAD_OBJECT;
+        }
+
+        status_t status = outputIgbp->queueBuffer(static_cast<int>(bqSlot),
+                                              input, output);
+        if (status != OK) {
+            LOG(ERROR) << "outputBuffer -- queueBuffer() failed "
+                       "on bufferqueue-based block. "
+                       "Error = " << status << ".";
+            return status;
+        }
+        return OK;
+    }
+
+    Impl *getPtr() {
+        return this;
+    }
+
+    ~Impl() {}
+};
+
+OutputBufferQueue::OutputBufferQueue(): mImpl(new Impl()) {}
+
+OutputBufferQueue::~OutputBufferQueue() {}
+
+bool OutputBufferQueue::configure(const sp<IGraphicBufferProducer>& igbp,
+                                  uint32_t generation,
+                                  uint64_t bqId) {
+    return mImpl && mImpl->configure(igbp, generation, bqId);
+}
+
+status_t OutputBufferQueue::outputBuffer(
+    const C2ConstGraphicBlock& block,
+    const BnGraphicBufferProducer::QueueBufferInput& input,
+    BnGraphicBufferProducer::QueueBufferOutput* output) {
+    if (mImpl) {
+        return mImpl->outputBuffer(block, input, output);
+    }
+    return DEAD_OBJECT;
+}
+
+void OutputBufferQueue::holdBufferQueueBlocks(
+        const std::list<std::unique_ptr<C2Work>>& workList) {
+    if (!mImpl) {
+        return;
+    }
+    forEachBlock(workList,
+                 std::bind(&OutputBufferQueue::Impl::registerBuffer,
+                           mImpl->getPtr(), std::placeholders::_1));
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
diff --git a/media/codec2/hidl/1.0/utils/Component.cpp b/media/codec2/hidl/1.0/utils/Component.cpp
new file mode 100644
index 0000000..a9f20a4
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/Component.cpp
@@ -0,0 +1,486 @@
+/*
+ * 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 "Codec2-Component"
+#include <android-base/logging.h>
+
+#include <codec2/hidl/1.0/Component.h>
+#include <codec2/hidl/1.0/ComponentStore.h>
+#include <codec2/hidl/1.0/InputBufferManager.h>
+
+#include <hidl/HidlBinderSupport.h>
+#include <utils/Timers.h>
+
+#include <C2BqBufferPriv.h>
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using namespace ::android;
+
+// ComponentListener wrapper
+struct Component::Listener : public C2Component::Listener {
+
+    Listener(const sp<Component>& component) :
+        mComponent(component),
+        mListener(component->mListener) {
+    }
+
+    virtual void onError_nb(
+            std::weak_ptr<C2Component> /* c2component */,
+            uint32_t errorCode) override {
+        sp<IComponentListener> listener = mListener.promote();
+        if (listener) {
+            Return<void> transStatus = listener->onError(Status::OK, errorCode);
+            if (!transStatus.isOk()) {
+                LOG(ERROR) << "Component::Listener::onError_nb -- "
+                           << "transaction failed.";
+            }
+        }
+    }
+
+    virtual void onTripped_nb(
+            std::weak_ptr<C2Component> /* c2component */,
+            std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+            ) override {
+        sp<IComponentListener> listener = mListener.promote();
+        if (listener) {
+            hidl_vec<SettingResult> settingResults(c2settingResult.size());
+            size_t ix = 0;
+            for (const std::shared_ptr<C2SettingResult> &c2result :
+                    c2settingResult) {
+                if (c2result) {
+                    if (!objcpy(&settingResults[ix++], *c2result)) {
+                        break;
+                    }
+                }
+            }
+            settingResults.resize(ix);
+            Return<void> transStatus = listener->onTripped(settingResults);
+            if (!transStatus.isOk()) {
+                LOG(ERROR) << "Component::Listener::onTripped_nb -- "
+                           << "transaction failed.";
+            }
+        }
+    }
+
+    virtual void onWorkDone_nb(
+            std::weak_ptr<C2Component> /* c2component */,
+            std::list<std::unique_ptr<C2Work>> c2workItems) override {
+        for (const std::unique_ptr<C2Work>& work : c2workItems) {
+            if (work) {
+                if (work->worklets.empty()
+                        || !work->worklets.back()
+                        || (work->worklets.back()->output.flags &
+                            C2FrameData::FLAG_INCOMPLETE) == 0) {
+                    InputBufferManager::
+                            unregisterFrameData(mListener, work->input);
+                }
+            }
+        }
+
+        sp<IComponentListener> listener = mListener.promote();
+        if (listener) {
+            WorkBundle workBundle;
+
+            sp<Component> strongComponent = mComponent.promote();
+            beginTransferBufferQueueBlocks(c2workItems, true);
+            if (!objcpy(&workBundle, c2workItems, strongComponent ?
+                    &strongComponent->mBufferPoolSender : nullptr)) {
+                LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
+                           << "received corrupted work items.";
+                endTransferBufferQueueBlocks(c2workItems, false, true);
+                return;
+            }
+            Return<void> transStatus = listener->onWorkDone(workBundle);
+            if (!transStatus.isOk()) {
+                LOG(ERROR) << "Component::Listener::onWorkDone_nb -- "
+                           << "transaction failed.";
+                endTransferBufferQueueBlocks(c2workItems, false, true);
+                return;
+            }
+            endTransferBufferQueueBlocks(c2workItems, true, true);
+        }
+    }
+
+protected:
+    wp<Component> mComponent;
+    wp<IComponentListener> mListener;
+};
+
+// Component::Sink
+struct Component::Sink : public IInputSink {
+    std::shared_ptr<Component> mComponent;
+    sp<IConfigurable> mConfigurable;
+
+    virtual Return<Status> queue(const WorkBundle& workBundle) override {
+        return mComponent->queue(workBundle);
+    }
+
+    virtual Return<sp<IConfigurable>> getConfigurable() override {
+        return mConfigurable;
+    }
+
+    Sink(const std::shared_ptr<Component>& component);
+    virtual ~Sink() override;
+
+    // Process-wide map: Component::Sink -> C2Component.
+    static std::mutex sSink2ComponentMutex;
+    static std::map<IInputSink*, std::weak_ptr<C2Component>> sSink2Component;
+
+    static std::shared_ptr<C2Component> findLocalComponent(
+            const sp<IInputSink>& sink);
+};
+
+std::mutex
+        Component::Sink::sSink2ComponentMutex{};
+std::map<IInputSink*, std::weak_ptr<C2Component>>
+        Component::Sink::sSink2Component{};
+
+Component::Sink::Sink(const std::shared_ptr<Component>& component)
+        : mComponent{component},
+          mConfigurable{[&component]() -> sp<IConfigurable> {
+              Return<sp<IComponentInterface>> ret1 = component->getInterface();
+              if (!ret1.isOk()) {
+                  LOG(ERROR) << "Sink::Sink -- component's transaction failed.";
+                  return nullptr;
+              }
+              Return<sp<IConfigurable>> ret2 =
+                      static_cast<sp<IComponentInterface>>(ret1)->
+                      getConfigurable();
+              if (!ret2.isOk()) {
+                  LOG(ERROR) << "Sink::Sink -- interface's transaction failed.";
+                  return nullptr;
+              }
+              return static_cast<sp<IConfigurable>>(ret2);
+          }()} {
+    std::lock_guard<std::mutex> lock(sSink2ComponentMutex);
+    sSink2Component.emplace(this, component->mComponent);
+}
+
+Component::Sink::~Sink() {
+    std::lock_guard<std::mutex> lock(sSink2ComponentMutex);
+    sSink2Component.erase(this);
+}
+
+std::shared_ptr<C2Component> Component::Sink::findLocalComponent(
+        const sp<IInputSink>& sink) {
+    std::lock_guard<std::mutex> lock(sSink2ComponentMutex);
+    auto i = sSink2Component.find(sink.get());
+    if (i == sSink2Component.end()) {
+        return nullptr;
+    }
+    return i->second.lock();
+}
+
+// Component
+Component::Component(
+        const std::shared_ptr<C2Component>& component,
+        const sp<IComponentListener>& listener,
+        const sp<ComponentStore>& store,
+        const sp<::android::hardware::media::bufferpool::V2_0::
+        IClientManager>& clientPoolManager)
+      : mComponent{component},
+        mInterface{new ComponentInterface(component->intf(), store.get())},
+        mListener{listener},
+        mStore{store},
+        mBufferPoolSender{clientPoolManager} {
+    // Retrieve supported parameters from store
+    // TODO: We could cache this per component/interface type
+    mInit = mInterface->status();
+}
+
+c2_status_t Component::status() const {
+    return mInit;
+}
+
+// Methods from ::android::hardware::media::c2::V1_0::IComponent
+Return<Status> Component::queue(const WorkBundle& workBundle) {
+    std::list<std::unique_ptr<C2Work>> c2works;
+
+    if (!objcpy(&c2works, workBundle)) {
+        return Status::CORRUPTED;
+    }
+
+    // Register input buffers.
+    for (const std::unique_ptr<C2Work>& work : c2works) {
+        if (work) {
+            InputBufferManager::
+                    registerFrameData(mListener, work->input);
+        }
+    }
+
+    return static_cast<Status>(mComponent->queue_nb(&c2works));
+}
+
+Return<void> Component::flush(flush_cb _hidl_cb) {
+    std::list<std::unique_ptr<C2Work>> c2flushedWorks;
+    c2_status_t c2res = mComponent->flush_sm(
+            C2Component::FLUSH_COMPONENT,
+            &c2flushedWorks);
+
+    // Unregister input buffers.
+    for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
+        if (work) {
+            if (work->worklets.empty()
+                    || !work->worklets.back()
+                    || (work->worklets.back()->output.flags &
+                        C2FrameData::FLAG_INCOMPLETE) == 0) {
+                InputBufferManager::
+                        unregisterFrameData(mListener, work->input);
+            }
+        }
+    }
+
+    WorkBundle flushedWorkBundle;
+    Status res = static_cast<Status>(c2res);
+    beginTransferBufferQueueBlocks(c2flushedWorks, true);
+    if (c2res == C2_OK) {
+        if (!objcpy(&flushedWorkBundle, c2flushedWorks, &mBufferPoolSender)) {
+            res = Status::CORRUPTED;
+        }
+    }
+    _hidl_cb(res, flushedWorkBundle);
+    endTransferBufferQueueBlocks(c2flushedWorks, true, true);
+    return Void();
+}
+
+Return<Status> Component::drain(bool withEos) {
+    return static_cast<Status>(mComponent->drain_nb(withEos ?
+            C2Component::DRAIN_COMPONENT_WITH_EOS :
+            C2Component::DRAIN_COMPONENT_NO_EOS));
+}
+
+Return<Status> Component::setOutputSurface(
+        uint64_t blockPoolId,
+        const sp<HGraphicBufferProducer2>& surface) {
+    std::shared_ptr<C2BlockPool> pool;
+    GetCodec2BlockPool(blockPoolId, mComponent, &pool);
+    if (pool && pool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
+        std::shared_ptr<C2BufferQueueBlockPool> bqPool =
+                std::static_pointer_cast<C2BufferQueueBlockPool>(pool);
+        C2BufferQueueBlockPool::OnRenderCallback cb =
+            [this](uint64_t producer, int32_t slot, int64_t nsecs) {
+                // TODO: batch this
+                hidl_vec<IComponentListener::RenderedFrame> rendered;
+                rendered.resize(1);
+                rendered[0] = { producer, slot, nsecs };
+                (void)mListener->onFramesRendered(rendered).isOk();
+        };
+        if (bqPool) {
+            bqPool->setRenderCallback(cb);
+            bqPool->configureProducer(surface);
+        }
+    }
+    return Status::OK;
+}
+
+Return<void> Component::connectToInputSurface(
+        const sp<IInputSurface>& inputSurface,
+        connectToInputSurface_cb _hidl_cb) {
+    Status status;
+    sp<IInputSurfaceConnection> connection;
+    auto transStatus = inputSurface->connect(
+            asInputSink(),
+            [&status, &connection](
+                    Status s, const sp<IInputSurfaceConnection>& c) {
+                status = s;
+                connection = c;
+            }
+        );
+    _hidl_cb(status, connection);
+    return Void();
+}
+
+Return<void> Component::connectToOmxInputSurface(
+        const sp<HGraphicBufferProducer1>& producer,
+        const sp<::android::hardware::media::omx::V1_0::
+        IGraphicBufferSource>& source,
+        connectToOmxInputSurface_cb _hidl_cb) {
+    (void)producer;
+    (void)source;
+    (void)_hidl_cb;
+    return Void();
+}
+
+Return<Status> Component::disconnectFromInputSurface() {
+    // TODO implement
+    return Status::OK;
+}
+
+namespace /* unnamed */ {
+
+struct BlockPoolIntf : public ConfigurableC2Intf {
+    BlockPoolIntf(const std::shared_ptr<C2BlockPool>& pool)
+          : ConfigurableC2Intf{
+                "C2BlockPool:" +
+                    (pool ? std::to_string(pool->getLocalId()) : "null"),
+                0},
+            mPool{pool} {
+    }
+
+    virtual c2_status_t config(
+            const std::vector<C2Param*>& params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures
+            ) override {
+        (void)params;
+        (void)mayBlock;
+        (void)failures;
+        return C2_OK;
+    }
+
+    virtual c2_status_t query(
+            const std::vector<C2Param::Index>& indices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const params
+            ) const override {
+        (void)indices;
+        (void)mayBlock;
+        (void)params;
+        return C2_OK;
+    }
+
+    virtual c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
+            ) const override {
+        (void)params;
+        return C2_OK;
+    }
+
+    virtual c2_status_t querySupportedValues(
+            std::vector<C2FieldSupportedValuesQuery>& fields,
+            c2_blocking_t mayBlock) const override {
+        (void)fields;
+        (void)mayBlock;
+        return C2_OK;
+    }
+
+protected:
+    std::shared_ptr<C2BlockPool> mPool;
+};
+
+} // unnamed namespace
+
+Return<void> Component::createBlockPool(
+        uint32_t allocatorId,
+        createBlockPool_cb _hidl_cb) {
+    std::shared_ptr<C2BlockPool> blockPool;
+    c2_status_t status = CreateCodec2BlockPool(
+            static_cast<C2PlatformAllocatorStore::id_t>(allocatorId),
+            mComponent,
+            &blockPool);
+    if (status != C2_OK) {
+        blockPool = nullptr;
+    }
+    if (blockPool) {
+        mBlockPoolsMutex.lock();
+        mBlockPools.emplace(blockPool->getLocalId(), blockPool);
+        mBlockPoolsMutex.unlock();
+    } else if (status == C2_OK) {
+        status = C2_CORRUPTED;
+    }
+
+    _hidl_cb(static_cast<Status>(status),
+            blockPool ? blockPool->getLocalId() : 0,
+            new CachedConfigurable(
+            std::make_unique<BlockPoolIntf>(blockPool)));
+    return Void();
+}
+
+Return<Status> Component::destroyBlockPool(uint64_t blockPoolId) {
+    std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+    return mBlockPools.erase(blockPoolId) == 1 ?
+            Status::OK : Status::CORRUPTED;
+}
+
+Return<Status> Component::start() {
+    return static_cast<Status>(mComponent->start());
+}
+
+Return<Status> Component::stop() {
+    InputBufferManager::unregisterFrameData(mListener);
+    return static_cast<Status>(mComponent->stop());
+}
+
+Return<Status> Component::reset() {
+    Status status = static_cast<Status>(mComponent->reset());
+    {
+        std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+        mBlockPools.clear();
+    }
+    InputBufferManager::unregisterFrameData(mListener);
+    return status;
+}
+
+Return<Status> Component::release() {
+    Status status = static_cast<Status>(mComponent->release());
+    {
+        std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
+        mBlockPools.clear();
+    }
+    InputBufferManager::unregisterFrameData(mListener);
+    return status;
+}
+
+Return<sp<IComponentInterface>> Component::getInterface() {
+    return sp<IComponentInterface>(mInterface);
+}
+
+Return<sp<IInputSink>> Component::asInputSink() {
+    std::lock_guard<std::mutex> lock(mSinkMutex);
+    if (!mSink) {
+        mSink = new Sink(shared_from_this());
+    }
+    return {mSink};
+}
+
+std::shared_ptr<C2Component> Component::findLocalComponent(
+        const sp<IInputSink>& sink) {
+    return Component::Sink::findLocalComponent(sink);
+}
+
+void Component::initListener(const sp<Component>& self) {
+    std::shared_ptr<C2Component::Listener> c2listener =
+            std::make_shared<Listener>(self);
+    c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
+    if (res != C2_OK) {
+        mInit = res;
+    }
+}
+
+Component::~Component() {
+    InputBufferManager::unregisterFrameData(mListener);
+    mStore->reportComponentDeath(this);
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
diff --git a/media/codec2/hidl/1.0/utils/ComponentInterface.cpp b/media/codec2/hidl/1.0/utils/ComponentInterface.cpp
new file mode 100644
index 0000000..39e5357
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/ComponentInterface.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 "Codec2-ComponentInterface"
+#include <android-base/logging.h>
+
+#include <codec2/hidl/1.0/Component.h>
+#include <codec2/hidl/1.0/ComponentInterface.h>
+#include <codec2/hidl/1.0/ComponentStore.h>
+
+#include <hidl/HidlBinderSupport.h>
+#include <utils/Timers.h>
+
+#include <C2BqBufferPriv.h>
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using namespace ::android;
+
+namespace /* unnamed */ {
+
+// Implementation of ConfigurableC2Intf based on C2ComponentInterface
+struct CompIntf : public ConfigurableC2Intf {
+    CompIntf(const std::shared_ptr<C2ComponentInterface>& intf) :
+        ConfigurableC2Intf{intf->getName(), intf->getId()},
+        mIntf{intf} {
+    }
+
+    virtual c2_status_t config(
+            const std::vector<C2Param*>& params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures
+            ) override {
+        return mIntf->config_vb(params, mayBlock, failures);
+    }
+
+    virtual c2_status_t query(
+            const std::vector<C2Param::Index>& indices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const params
+            ) const override {
+        return mIntf->query_vb({}, indices, mayBlock, params);
+    }
+
+    virtual c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
+            ) const override {
+        return mIntf->querySupportedParams_nb(params);
+    }
+
+    virtual c2_status_t querySupportedValues(
+            std::vector<C2FieldSupportedValuesQuery>& fields,
+            c2_blocking_t mayBlock) const override {
+        return mIntf->querySupportedValues_vb(fields, mayBlock);
+    }
+
+protected:
+    std::shared_ptr<C2ComponentInterface> mIntf;
+};
+
+} // unnamed namespace
+
+// ComponentInterface
+ComponentInterface::ComponentInterface(
+        const std::shared_ptr<C2ComponentInterface>& intf,
+        ComponentStore* store)
+      : mInterface{intf},
+        mConfigurable{new CachedConfigurable(std::make_unique<CompIntf>(intf))} {
+    mInit = mConfigurable->init(store);
+}
+
+c2_status_t ComponentInterface::status() const {
+    return mInit;
+}
+
+Return<sp<IConfigurable>> ComponentInterface::getConfigurable() {
+    return mConfigurable;
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
diff --git a/media/codec2/hidl/1.0/utils/ComponentStore.cpp b/media/codec2/hidl/1.0/utils/ComponentStore.cpp
new file mode 100644
index 0000000..1e0a190
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/ComponentStore.cpp
@@ -0,0 +1,437 @@
+/*
+ * 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 "Codec2-ComponentStore"
+#include <android-base/logging.h>
+
+#include <codec2/hidl/1.0/ComponentStore.h>
+#include <codec2/hidl/1.0/InputSurface.h>
+#include <codec2/hidl/1.0/types.h>
+
+#include <android-base/file.h>
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+#include <utils/Errors.h>
+
+#include <C2PlatformSupport.h>
+#include <util/C2InterfaceHelper.h>
+
+#include <chrono>
+#include <ctime>
+#include <iomanip>
+#include <ostream>
+#include <sstream>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using namespace ::android;
+using ::android::GraphicBufferSource;
+using namespace ::android::hardware::media::bufferpool::V2_0::implementation;
+
+namespace /* unnamed */ {
+
+struct StoreIntf : public ConfigurableC2Intf {
+    StoreIntf(const std::shared_ptr<C2ComponentStore>& store)
+          : ConfigurableC2Intf{store ? store->getName() : "", 0},
+            mStore{store} {
+    }
+
+    virtual c2_status_t config(
+            const std::vector<C2Param*> &params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>> *const failures
+            ) override {
+        // Assume all params are blocking
+        // TODO: Filter for supported params
+        if (mayBlock == C2_DONT_BLOCK && params.size() != 0) {
+            return C2_BLOCKING;
+        }
+        return mStore->config_sm(params, failures);
+    }
+
+    virtual c2_status_t query(
+            const std::vector<C2Param::Index> &indices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>> *const params) const override {
+        // Assume all params are blocking
+        // TODO: Filter for supported params
+        if (mayBlock == C2_DONT_BLOCK && indices.size() != 0) {
+            return C2_BLOCKING;
+        }
+        return mStore->query_sm({}, indices, params);
+    }
+
+    virtual c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> *const params
+            ) const override {
+        return mStore->querySupportedParams_nb(params);
+    }
+
+    virtual c2_status_t querySupportedValues(
+            std::vector<C2FieldSupportedValuesQuery> &fields,
+            c2_blocking_t mayBlock) const override {
+        // Assume all params are blocking
+        // TODO: Filter for supported params
+        if (mayBlock == C2_DONT_BLOCK && fields.size() != 0) {
+            return C2_BLOCKING;
+        }
+        return mStore->querySupportedValues_sm(fields);
+    }
+
+protected:
+    std::shared_ptr<C2ComponentStore> mStore;
+};
+
+} // unnamed namespace
+
+ComponentStore::ComponentStore(const std::shared_ptr<C2ComponentStore>& store)
+      : mConfigurable{new CachedConfigurable(std::make_unique<StoreIntf>(store))},
+        mStore{store} {
+
+    std::shared_ptr<C2ComponentStore> platformStore = android::GetCodec2PlatformComponentStore();
+    SetPreferredCodec2ComponentStore(store);
+
+    // Retrieve struct descriptors
+    mParamReflector = mStore->getParamReflector();
+
+    // Retrieve supported parameters from store
+    mInit = mConfigurable->init(this);
+}
+
+c2_status_t ComponentStore::status() const {
+    return mInit;
+}
+
+c2_status_t ComponentStore::validateSupportedParams(
+        const std::vector<std::shared_ptr<C2ParamDescriptor>>& params) {
+    c2_status_t res = C2_OK;
+
+    for (const std::shared_ptr<C2ParamDescriptor> &desc : params) {
+        if (!desc) {
+            // All descriptors should be valid
+            res = res ? res : C2_BAD_VALUE;
+            continue;
+        }
+        C2Param::CoreIndex coreIndex = desc->index().coreIndex();
+        std::lock_guard<std::mutex> lock(mStructDescriptorsMutex);
+        auto it = mStructDescriptors.find(coreIndex);
+        if (it == mStructDescriptors.end()) {
+            std::shared_ptr<C2StructDescriptor> structDesc =
+                    mParamReflector->describe(coreIndex);
+            if (!structDesc) {
+                // All supported params must be described
+                res = C2_BAD_INDEX;
+            }
+            mStructDescriptors.insert({ coreIndex, structDesc });
+        }
+    }
+    return res;
+}
+
+// Methods from ::android::hardware::media::c2::V1_0::IComponentStore
+Return<void> ComponentStore::createComponent(
+        const hidl_string& name,
+        const sp<IComponentListener>& listener,
+        const sp<IClientManager>& pool,
+        createComponent_cb _hidl_cb) {
+
+    sp<Component> component;
+    std::shared_ptr<C2Component> c2component;
+    Status status = static_cast<Status>(
+            mStore->createComponent(name, &c2component));
+
+    if (status == Status::OK) {
+        onInterfaceLoaded(c2component->intf());
+        component = new Component(c2component, listener, this, pool);
+        if (!component) {
+            status = Status::CORRUPTED;
+        } else {
+            reportComponentBirth(component.get());
+            if (component->status() != C2_OK) {
+                status = static_cast<Status>(component->status());
+            } else {
+                component->initListener(component);
+                if (component->status() != C2_OK) {
+                    status = static_cast<Status>(component->status());
+                }
+            }
+        }
+    }
+    _hidl_cb(status, component);
+    return Void();
+}
+
+Return<void> ComponentStore::createInterface(
+        const hidl_string& name,
+        createInterface_cb _hidl_cb) {
+    std::shared_ptr<C2ComponentInterface> c2interface;
+    c2_status_t res = mStore->createInterface(name, &c2interface);
+    sp<IComponentInterface> interface;
+    if (res == C2_OK) {
+        onInterfaceLoaded(c2interface);
+        interface = new ComponentInterface(c2interface, this);
+    }
+    _hidl_cb(static_cast<Status>(res), interface);
+    return Void();
+}
+
+Return<void> ComponentStore::listComponents(listComponents_cb _hidl_cb) {
+    std::vector<std::shared_ptr<const C2Component::Traits>> c2traits =
+            mStore->listComponents();
+    hidl_vec<IComponentStore::ComponentTraits> traits(c2traits.size());
+    size_t ix = 0;
+    for (const std::shared_ptr<const C2Component::Traits> &c2trait : c2traits) {
+        if (c2trait) {
+            if (objcpy(&traits[ix], *c2trait)) {
+                ++ix;
+            } else {
+                break;
+            }
+        }
+    }
+    traits.resize(ix);
+    _hidl_cb(Status::OK, traits);
+    return Void();
+}
+
+Return<void> ComponentStore::createInputSurface(createInputSurface_cb _hidl_cb) {
+    sp<GraphicBufferSource> source = new GraphicBufferSource();
+    if (source->initCheck() != OK) {
+        _hidl_cb(Status::CORRUPTED, nullptr);
+        return Void();
+    }
+    sp<InputSurface> inputSurface = new InputSurface(
+            this,
+            std::make_shared<C2ReflectorHelper>(),
+            source->getHGraphicBufferProducer(),
+            source);
+    _hidl_cb(inputSurface ? Status::OK : Status::NO_MEMORY,
+             inputSurface);
+    return Void();
+}
+
+void ComponentStore::onInterfaceLoaded(const std::shared_ptr<C2ComponentInterface> &intf) {
+    // invalidate unsupported struct descriptors if a new interface is loaded as it may have
+    // exposed new descriptors
+    std::lock_guard<std::mutex> lock(mStructDescriptorsMutex);
+    if (!mLoadedInterfaces.count(intf->getName())) {
+        mUnsupportedStructDescriptors.clear();
+        mLoadedInterfaces.emplace(intf->getName());
+    }
+}
+
+Return<void> ComponentStore::getStructDescriptors(
+        const hidl_vec<uint32_t>& indices,
+        getStructDescriptors_cb _hidl_cb) {
+    hidl_vec<StructDescriptor> descriptors(indices.size());
+    size_t dstIx = 0;
+    Status res = Status::OK;
+    for (size_t srcIx = 0; srcIx < indices.size(); ++srcIx) {
+        std::lock_guard<std::mutex> lock(mStructDescriptorsMutex);
+        const C2Param::CoreIndex coreIndex = C2Param::CoreIndex(indices[srcIx]).coreIndex();
+        const auto item = mStructDescriptors.find(coreIndex);
+        if (item == mStructDescriptors.end()) {
+            // not in the cache, and not known to be unsupported, query local reflector
+            if (!mUnsupportedStructDescriptors.count(coreIndex)) {
+                std::shared_ptr<C2StructDescriptor> structDesc =
+                    mParamReflector->describe(coreIndex);
+                if (!structDesc) {
+                    mUnsupportedStructDescriptors.emplace(coreIndex);
+                } else {
+                    mStructDescriptors.insert({ coreIndex, structDesc });
+                    if (objcpy(&descriptors[dstIx], *structDesc)) {
+                        ++dstIx;
+                        continue;
+                    }
+                    res = Status::CORRUPTED;
+                    break;
+                }
+            }
+            res = Status::NOT_FOUND;
+        } else if (item->second) {
+            if (objcpy(&descriptors[dstIx], *item->second)) {
+                ++dstIx;
+                continue;
+            }
+            res = Status::CORRUPTED;
+            break;
+        } else {
+            res = Status::NO_MEMORY;
+            break;
+        }
+    }
+    descriptors.resize(dstIx);
+    _hidl_cb(res, descriptors);
+    return Void();
+}
+
+Return<sp<IClientManager>> ComponentStore::getPoolClientManager() {
+    return ClientManager::getInstance();
+}
+
+Return<Status> ComponentStore::copyBuffer(const Buffer& src, const Buffer& dst) {
+    // TODO implement
+    (void)src;
+    (void)dst;
+    return Status::OMITTED;
+}
+
+Return<sp<IConfigurable>> ComponentStore::getConfigurable() {
+    return mConfigurable;
+}
+
+// Called from createComponent() after a successful creation of `component`.
+void ComponentStore::reportComponentBirth(Component* component) {
+    ComponentStatus componentStatus;
+    componentStatus.c2Component = component->mComponent;
+    componentStatus.birthTime = std::chrono::system_clock::now();
+
+    std::lock_guard<std::mutex> lock(mComponentRosterMutex);
+    mComponentRoster.emplace(component, componentStatus);
+}
+
+// Called from within the destructor of `component`. No virtual function calls
+// are made on `component` here.
+void ComponentStore::reportComponentDeath(Component* component) {
+    std::lock_guard<std::mutex> lock(mComponentRosterMutex);
+    mComponentRoster.erase(component);
+}
+
+// Dumps component traits.
+std::ostream& ComponentStore::dump(
+        std::ostream& out,
+        const std::shared_ptr<const C2Component::Traits>& comp) {
+
+    constexpr const char indent[] = "    ";
+
+    out << indent << "name: " << comp->name << std::endl;
+    out << indent << "domain: " << comp->domain << std::endl;
+    out << indent << "kind: " << comp->kind << std::endl;
+    out << indent << "rank: " << comp->rank << std::endl;
+    out << indent << "mediaType: " << comp->mediaType << std::endl;
+    out << indent << "aliases:";
+    for (const auto& alias : comp->aliases) {
+        out << ' ' << alias;
+    }
+    out << std::endl;
+
+    return out;
+}
+
+// Dumps component status.
+std::ostream& ComponentStore::dump(
+        std::ostream& out,
+        ComponentStatus& compStatus) {
+
+    constexpr const char indent[] = "    ";
+
+    // Print birth time.
+    std::chrono::milliseconds ms =
+            std::chrono::duration_cast<std::chrono::milliseconds>(
+                compStatus.birthTime.time_since_epoch());
+    std::time_t birthTime = std::chrono::system_clock::to_time_t(
+            compStatus.birthTime);
+    std::tm tm = *std::localtime(&birthTime);
+    out << indent << "Creation time: "
+        << std::put_time(&tm, "%Y-%m-%d %H:%M:%S")
+        << '.' << std::setfill('0') << std::setw(3) << ms.count() % 1000
+        << std::endl;
+
+    // Print name and id.
+    std::shared_ptr<C2ComponentInterface> intf = compStatus.c2Component->intf();
+    if (!intf) {
+        out << indent << "Unknown component -- null interface" << std::endl;
+        return out;
+    }
+    out << indent << "Name: " << intf->getName() << std::endl;
+    out << indent << "Id: " << intf->getId() << std::endl;
+
+    return out;
+}
+
+// Dumps information when lshal is called.
+Return<void> ComponentStore::debug(
+        const hidl_handle& handle,
+        const hidl_vec<hidl_string>& /* args */) {
+    LOG(INFO) << "debug -- dumping...";
+    const native_handle_t *h = handle.getNativeHandle();
+    if (!h || h->numFds != 1) {
+       LOG(ERROR) << "debug -- dumping failed -- "
+               "invalid file descriptor to dump to";
+       return Void();
+    }
+    std::ostringstream out;
+
+    { // Populate "out".
+
+        constexpr const char indent[] = "  ";
+
+        // Show name.
+        out << "Beginning of dump -- C2ComponentStore: "
+                << mStore->getName() << std::endl << std::endl;
+
+        // Retrieve the list of supported components.
+        std::vector<std::shared_ptr<const C2Component::Traits>> traitsList =
+                mStore->listComponents();
+
+        // Dump the traits of supported components.
+        out << indent << "Supported components:" << std::endl << std::endl;
+        if (traitsList.size() == 0) {
+            out << indent << indent << "NONE" << std::endl << std::endl;
+        } else {
+            for (const auto& traits : traitsList) {
+                dump(out, traits) << std::endl;
+            }
+        }
+
+        // Dump active components.
+        {
+            out << indent << "Active components:" << std::endl << std::endl;
+            std::lock_guard<std::mutex> lock(mComponentRosterMutex);
+            if (mComponentRoster.size() == 0) {
+                out << indent << indent << "NONE" << std::endl << std::endl;
+            } else {
+                for (auto& pair : mComponentRoster) {
+                    dump(out, pair.second) << std::endl;
+                }
+            }
+        }
+
+        out << "End of dump -- C2ComponentStore: "
+                << mStore->getName() << std::endl;
+    }
+
+    if (!android::base::WriteStringToFd(out.str(), h->data[0])) {
+        PLOG(WARNING) << "debug -- dumping failed -- write()";
+    } else {
+        LOG(INFO) << "debug -- dumping succeeded";
+    }
+    return Void();
+}
+
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
diff --git a/media/codec2/hidl/1.0/utils/Configurable.cpp b/media/codec2/hidl/1.0/utils/Configurable.cpp
new file mode 100644
index 0000000..ec9c170
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/Configurable.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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 "Codec2-Configurable"
+#include <android-base/logging.h>
+
+#include <codec2/hidl/1.0/Configurable.h>
+#include <codec2/hidl/1.0/ComponentStore.h>
+#include <codec2/hidl/1.0/types.h>
+
+#include <C2ParamInternal.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using namespace ::android;
+
+CachedConfigurable::CachedConfigurable(
+        std::unique_ptr<ConfigurableC2Intf>&& intf)
+      : mIntf{std::move(intf)} {
+}
+
+c2_status_t CachedConfigurable::init(ComponentStore* store) {
+    // Retrieve supported parameters from store
+    c2_status_t init = mIntf->querySupportedParams(&mSupportedParams);
+    c2_status_t validate = store->validateSupportedParams(mSupportedParams);
+    return init == C2_OK ? C2_OK : validate;
+}
+
+// Methods from ::android::hardware::media::c2::V1_0::IConfigurable follow.
+Return<uint32_t> CachedConfigurable::getId() {
+    return mIntf->getId();
+}
+
+Return<void> CachedConfigurable::getName(getName_cb _hidl_cb) {
+    _hidl_cb(mIntf->getName());
+    return Void();
+}
+
+Return<void> CachedConfigurable::query(
+        const hidl_vec<uint32_t>& indices,
+        bool mayBlock,
+        query_cb _hidl_cb) {
+    typedef C2Param::Index Index;
+    std::vector<Index> c2heapParamIndices(
+            (Index*)indices.data(),
+            (Index*)indices.data() + indices.size());
+    std::vector<std::unique_ptr<C2Param>> c2heapParams;
+    c2_status_t c2res = mIntf->query(
+            c2heapParamIndices,
+            mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK,
+            &c2heapParams);
+
+    hidl_vec<uint8_t> params;
+    if (!createParamsBlob(&params, c2heapParams)) {
+        LOG(WARNING) << "query -- invalid output params.";
+    }
+    _hidl_cb(static_cast<Status>(c2res), params);
+    return Void();
+}
+
+Return<void> CachedConfigurable::config(
+        const hidl_vec<uint8_t>& inParams,
+        bool mayBlock,
+        config_cb _hidl_cb) {
+    // inParams is not writable, so create a copy as config modifies the parameters
+    hidl_vec<uint8_t> inParamsCopy = inParams;
+    std::vector<C2Param*> c2params;
+    if (!parseParamsBlob(&c2params, inParamsCopy)) {
+        LOG(WARNING) << "config -- invalid input params.";
+        _hidl_cb(Status::CORRUPTED,
+                hidl_vec<SettingResult>(),
+                hidl_vec<uint8_t>());
+        return Void();
+    }
+    // TODO: check if blob was invalid
+    std::vector<std::unique_ptr<C2SettingResult>> c2failures;
+    c2_status_t c2res = mIntf->config(
+            c2params,
+            mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK,
+            &c2failures);
+    hidl_vec<SettingResult> failures(c2failures.size());
+    {
+        size_t ix = 0;
+        for (const std::unique_ptr<C2SettingResult>& c2result : c2failures) {
+            if (c2result) {
+                if (objcpy(&failures[ix], *c2result)) {
+                    ++ix;
+                } else {
+                    LOG(DEBUG) << "config -- invalid setting results.";
+                    break;
+                }
+            }
+        }
+        failures.resize(ix);
+    }
+    hidl_vec<uint8_t> outParams;
+    if (!createParamsBlob(&outParams, c2params)) {
+        LOG(DEBUG) << "config -- invalid output params.";
+    }
+    _hidl_cb((Status)c2res, failures, outParams);
+    return Void();
+}
+
+Return<void> CachedConfigurable::querySupportedParams(
+        uint32_t start,
+        uint32_t count,
+        querySupportedParams_cb _hidl_cb) {
+    C2LinearRange request = C2LinearCapacity(mSupportedParams.size()).range(
+            start, count);
+    hidl_vec<ParamDescriptor> params(request.size());
+    Status res = Status::OK;
+    size_t dstIx = 0;
+    for (size_t srcIx = request.offset(); srcIx < request.endOffset(); ++srcIx) {
+        if (mSupportedParams[srcIx]) {
+            if (objcpy(&params[dstIx], *mSupportedParams[srcIx])) {
+                ++dstIx;
+            } else {
+                res = Status::CORRUPTED;
+                LOG(WARNING) << "querySupportedParams -- invalid output params.";
+                break;
+            }
+        } else {
+            res = Status::BAD_INDEX;
+        }
+    }
+    params.resize(dstIx);
+    _hidl_cb(res, params);
+    return Void();
+}
+
+Return<void> CachedConfigurable::querySupportedValues(
+        const hidl_vec<FieldSupportedValuesQuery>& inFields,
+        bool mayBlock,
+        querySupportedValues_cb _hidl_cb) {
+    std::vector<C2FieldSupportedValuesQuery> c2fields;
+    {
+        // C2FieldSupportedValuesQuery objects are restricted in that some
+        // members are const.
+        // C2ParamField - required for its constructor - has no constructors
+        // from fields. Use C2ParamInspector.
+        for (const FieldSupportedValuesQuery &query : inFields) {
+            c2fields.emplace_back(_C2ParamInspector::CreateParamField(
+                    query.field.index,
+                    query.field.fieldId.offset,
+                    query.field.fieldId.size),
+                    query.type == FieldSupportedValuesQuery::Type::POSSIBLE ?
+                    C2FieldSupportedValuesQuery::POSSIBLE :
+                    C2FieldSupportedValuesQuery::CURRENT);
+        }
+    }
+    c2_status_t c2res = mIntf->querySupportedValues(
+            c2fields,
+            mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK);
+    hidl_vec<FieldSupportedValuesQueryResult> outFields(inFields.size());
+    size_t dstIx = 0;
+    for (const C2FieldSupportedValuesQuery &result : c2fields) {
+        if (objcpy(&outFields[dstIx], result)) {
+            ++dstIx;
+        } else {
+            outFields.resize(dstIx);
+            c2res = C2_CORRUPTED;
+            LOG(WARNING) << "querySupportedValues -- invalid output params.";
+            break;
+        }
+    }
+    _hidl_cb((Status)c2res, outFields);
+    return Void();
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
diff --git a/media/codec2/hidl/1.0/utils/InputBufferManager.cpp b/media/codec2/hidl/1.0/utils/InputBufferManager.cpp
new file mode 100644
index 0000000..a023a05
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/InputBufferManager.cpp
@@ -0,0 +1,461 @@
+/*
+ * 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 "Codec2-InputBufferManager"
+#include <android-base/logging.h>
+
+#include <codec2/hidl/1.0/InputBufferManager.h>
+#include <codec2/hidl/1.0/types.h>
+
+#include <android/hardware/media/c2/1.0/IComponentListener.h>
+#include <android-base/logging.h>
+
+#include <C2Buffer.h>
+#include <C2Work.h>
+
+#include <chrono>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using namespace ::android;
+
+void InputBufferManager::registerFrameData(
+        const sp<IComponentListener>& listener,
+        const C2FrameData& input) {
+    getInstance()._registerFrameData(listener, input);
+}
+
+void InputBufferManager::unregisterFrameData(
+        const wp<IComponentListener>& listener,
+        const C2FrameData& input) {
+    getInstance()._unregisterFrameData(listener, input);
+}
+
+void InputBufferManager::unregisterFrameData(
+        const wp<IComponentListener>& listener) {
+    getInstance()._unregisterFrameData(listener);
+}
+
+void InputBufferManager::setNotificationInterval(
+        nsecs_t notificationIntervalNs) {
+    getInstance()._setNotificationInterval(notificationIntervalNs);
+}
+
+void InputBufferManager::_registerFrameData(
+        const sp<IComponentListener>& listener,
+        const C2FrameData& input) {
+    uint64_t frameIndex = input.ordinal.frameIndex.peeku();
+    LOG(VERBOSE) << "InputBufferManager::_registerFrameData -- called with "
+                 << "listener @ 0x" << std::hex << listener.get()
+                 << ", frameIndex = " << std::dec << frameIndex
+                 << ".";
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    std::set<TrackedBuffer> &bufferIds =
+            mTrackedBuffersMap[listener][frameIndex];
+
+    for (size_t i = 0; i < input.buffers.size(); ++i) {
+        if (!input.buffers[i]) {
+            LOG(VERBOSE) << "InputBufferManager::_registerFrameData -- "
+                         << "Input buffer at index " << i << " is null.";
+            continue;
+        }
+        const TrackedBuffer &bufferId =
+                *bufferIds.emplace(listener, frameIndex, i, input.buffers[i]).
+                first;
+
+        c2_status_t status = input.buffers[i]->registerOnDestroyNotify(
+                onBufferDestroyed,
+                const_cast<void*>(reinterpret_cast<const void*>(&bufferId)));
+        if (status != C2_OK) {
+            LOG(DEBUG) << "InputBufferManager::_registerFrameData -- "
+                       << "registerOnDestroyNotify() failed "
+                       << "(listener @ 0x" << std::hex << listener.get()
+                       << ", frameIndex = " << std::dec << frameIndex
+                       << ", bufferIndex = " << i
+                       << ") => status = " << status
+                       << ".";
+        }
+    }
+
+    mDeathNotifications.emplace(
+            listener,
+            DeathNotifications(
+                mNotificationIntervalNs.load(std::memory_order_relaxed)));
+}
+
+// Remove a pair (listener, frameIndex) from mTrackedBuffersMap and
+// mDeathNotifications. This implies all bufferIndices are removed.
+//
+// This is called from onWorkDone() and flush().
+void InputBufferManager::_unregisterFrameData(
+        const wp<IComponentListener>& listener,
+        const C2FrameData& input) {
+    uint64_t frameIndex = input.ordinal.frameIndex.peeku();
+    LOG(VERBOSE) << "InputBufferManager::_unregisterFrameData -- called with "
+                 << "listener @ 0x" << std::hex << listener.unsafe_get()
+                 << ", frameIndex = " << std::dec << frameIndex
+                 << ".";
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    auto findListener = mTrackedBuffersMap.find(listener);
+    if (findListener != mTrackedBuffersMap.end()) {
+        std::map<uint64_t, std::set<TrackedBuffer>> &frameIndex2BufferIds
+                = findListener->second;
+        auto findFrameIndex = frameIndex2BufferIds.find(frameIndex);
+        if (findFrameIndex != frameIndex2BufferIds.end()) {
+            std::set<TrackedBuffer> &bufferIds = findFrameIndex->second;
+            for (const TrackedBuffer& bufferId : bufferIds) {
+                std::shared_ptr<C2Buffer> buffer = bufferId.buffer.lock();
+                if (buffer) {
+                    c2_status_t status = buffer->unregisterOnDestroyNotify(
+                            onBufferDestroyed,
+                            const_cast<void*>(
+                            reinterpret_cast<const void*>(&bufferId)));
+                    if (status != C2_OK) {
+                        LOG(DEBUG) << "InputBufferManager::_unregisterFrameData "
+                                   << "-- unregisterOnDestroyNotify() failed "
+                                   << "(listener @ 0x"
+                                        << std::hex
+                                        << bufferId.listener.unsafe_get()
+                                   << ", frameIndex = "
+                                        << std::dec << bufferId.frameIndex
+                                   << ", bufferIndex = " << bufferId.bufferIndex
+                                   << ") => status = " << status
+                                   << ".";
+                    }
+                }
+            }
+
+            frameIndex2BufferIds.erase(findFrameIndex);
+            if (frameIndex2BufferIds.empty()) {
+                mTrackedBuffersMap.erase(findListener);
+            }
+        }
+    }
+
+    auto findListenerD = mDeathNotifications.find(listener);
+    if (findListenerD != mDeathNotifications.end()) {
+        DeathNotifications &deathNotifications = findListenerD->second;
+        auto findFrameIndex = deathNotifications.indices.find(frameIndex);
+        if (findFrameIndex != deathNotifications.indices.end()) {
+            std::vector<size_t> &bufferIndices = findFrameIndex->second;
+            deathNotifications.count -= bufferIndices.size();
+            deathNotifications.indices.erase(findFrameIndex);
+        }
+    }
+}
+
+// Remove listener from mTrackedBuffersMap and mDeathNotifications. This implies
+// all frameIndices and bufferIndices are removed.
+//
+// This is called when the component cleans up all input buffers, i.e., when
+// reset(), release(), stop() or ~Component() is called.
+void InputBufferManager::_unregisterFrameData(
+        const wp<IComponentListener>& listener) {
+    LOG(VERBOSE) << "InputBufferManager::_unregisterFrameData -- called with "
+                 << "listener @ 0x" << std::hex << listener.unsafe_get()
+                 << std::dec << ".";
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    auto findListener = mTrackedBuffersMap.find(listener);
+    if (findListener != mTrackedBuffersMap.end()) {
+        std::map<uint64_t, std::set<TrackedBuffer>> &frameIndex2BufferIds =
+                findListener->second;
+        for (auto findFrameIndex = frameIndex2BufferIds.begin();
+                findFrameIndex != frameIndex2BufferIds.end();
+                ++findFrameIndex) {
+            std::set<TrackedBuffer> &bufferIds = findFrameIndex->second;
+            for (const TrackedBuffer& bufferId : bufferIds) {
+                std::shared_ptr<C2Buffer> buffer = bufferId.buffer.lock();
+                if (buffer) {
+                    c2_status_t status = buffer->unregisterOnDestroyNotify(
+                            onBufferDestroyed,
+                            const_cast<void*>(
+                            reinterpret_cast<const void*>(&bufferId)));
+                    if (status != C2_OK) {
+                        LOG(DEBUG) << "InputBufferManager::_unregisterFrameData "
+                                   << "-- unregisterOnDestroyNotify() failed "
+                                   << "(listener @ 0x"
+                                        << std::hex
+                                        << bufferId.listener.unsafe_get()
+                                   << ", frameIndex = "
+                                        << std::dec << bufferId.frameIndex
+                                   << ", bufferIndex = " << bufferId.bufferIndex
+                                   << ") => status = " << status
+                                   << ".";
+                    }
+                }
+            }
+        }
+        mTrackedBuffersMap.erase(findListener);
+    }
+
+    mDeathNotifications.erase(listener);
+}
+
+// Set mNotificationIntervalNs.
+void InputBufferManager::_setNotificationInterval(
+        nsecs_t notificationIntervalNs) {
+    mNotificationIntervalNs.store(
+            notificationIntervalNs,
+            std::memory_order_relaxed);
+}
+
+// Move a buffer from mTrackedBuffersMap to mDeathNotifications.
+// This is called when a registered C2Buffer object is destroyed.
+void InputBufferManager::onBufferDestroyed(const C2Buffer* buf, void* arg) {
+    getInstance()._onBufferDestroyed(buf, arg);
+}
+
+void InputBufferManager::_onBufferDestroyed(const C2Buffer* buf, void* arg) {
+    if (!buf || !arg) {
+        LOG(WARNING) << "InputBufferManager::_onBufferDestroyed -- called with "
+                     << "null argument (s): "
+                     << "buf @ 0x" << std::hex << buf
+                     << ", arg @ 0x" << std::hex << arg
+                     << std::dec << ".";
+        return;
+    }
+    TrackedBuffer id(*reinterpret_cast<TrackedBuffer*>(arg));
+    LOG(VERBOSE) << "InputBufferManager::_onBufferDestroyed -- called with "
+                 << "buf @ 0x" << std::hex << buf
+                 << ", arg @ 0x" << std::hex << arg
+                 << std::dec << " -- "
+                 << "listener @ 0x" << std::hex << id.listener.unsafe_get()
+                 << ", frameIndex = " << std::dec << id.frameIndex
+                 << ", bufferIndex = " << id.bufferIndex
+                 << ".";
+
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    auto findListener = mTrackedBuffersMap.find(id.listener);
+    if (findListener == mTrackedBuffersMap.end()) {
+        LOG(DEBUG) << "InputBufferManager::_onBufferDestroyed -- "
+                   << "received invalid listener: "
+                   << "listener @ 0x" << std::hex << id.listener.unsafe_get()
+                   << " (frameIndex = " << std::dec << id.frameIndex
+                   << ", bufferIndex = " << id.bufferIndex
+                   << ").";
+        return;
+    }
+
+    std::map<uint64_t, std::set<TrackedBuffer>> &frameIndex2BufferIds
+            = findListener->second;
+    auto findFrameIndex = frameIndex2BufferIds.find(id.frameIndex);
+    if (findFrameIndex == frameIndex2BufferIds.end()) {
+        LOG(DEBUG) << "InputBufferManager::_onBufferDestroyed -- "
+                   << "received invalid frame index: "
+                   << "frameIndex = " << id.frameIndex
+                   << " (listener @ 0x" << std::hex << id.listener.unsafe_get()
+                   << ", bufferIndex = " << std::dec << id.bufferIndex
+                   << ").";
+        return;
+    }
+
+    std::set<TrackedBuffer> &bufferIds = findFrameIndex->second;
+    auto findBufferId = bufferIds.find(id);
+    if (findBufferId == bufferIds.end()) {
+        LOG(DEBUG) << "InputBufferManager::_onBufferDestroyed -- "
+                   << "received invalid buffer index: "
+                   << "bufferIndex = " << id.bufferIndex
+                   << " (frameIndex = " << id.frameIndex
+                   << ", listener @ 0x" << std::hex << id.listener.unsafe_get()
+                   << std::dec << ").";
+        return;
+    }
+
+    bufferIds.erase(findBufferId);
+    if (bufferIds.empty()) {
+        frameIndex2BufferIds.erase(findFrameIndex);
+        if (frameIndex2BufferIds.empty()) {
+            mTrackedBuffersMap.erase(findListener);
+        }
+    }
+
+    DeathNotifications &deathNotifications = mDeathNotifications[id.listener];
+    deathNotifications.indices[id.frameIndex].emplace_back(id.bufferIndex);
+    ++deathNotifications.count;
+    mOnBufferDestroyed.notify_one();
+}
+
+// Notify the clients about buffer destructions.
+// Return false if all destructions have been notified.
+// Return true and set timeToRetry to the time point to wait for before
+// retrying if some destructions have not been notified.
+bool InputBufferManager::processNotifications(nsecs_t* timeToRetryNs) {
+
+    struct Notification {
+        sp<IComponentListener> listener;
+        hidl_vec<IComponentListener::InputBuffer> inputBuffers;
+        Notification(const sp<IComponentListener>& l, size_t s)
+              : listener(l), inputBuffers(s) {}
+    };
+    std::list<Notification> notifications;
+    nsecs_t notificationIntervalNs =
+            mNotificationIntervalNs.load(std::memory_order_relaxed);
+
+    bool retry = false;
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        *timeToRetryNs = notificationIntervalNs;
+        nsecs_t timeNowNs = systemTime();
+        for (auto it = mDeathNotifications.begin();
+                it != mDeathNotifications.end(); ) {
+            sp<IComponentListener> listener = it->first.promote();
+            if (!listener) {
+                ++it;
+                continue;
+            }
+            DeathNotifications &deathNotifications = it->second;
+
+            nsecs_t timeSinceLastNotifiedNs =
+                    timeNowNs - deathNotifications.lastSentNs;
+            // If not enough time has passed since the last callback, leave the
+            // notifications for this listener untouched for now and retry
+            // later.
+            if (timeSinceLastNotifiedNs < notificationIntervalNs) {
+                retry = true;
+                *timeToRetryNs = std::min(*timeToRetryNs,
+                        notificationIntervalNs - timeSinceLastNotifiedNs);
+                LOG(VERBOSE) << "InputBufferManager::processNotifications -- "
+                             << "Notifications for listener @ "
+                                 << std::hex << listener.get()
+                             << " will be postponed.";
+                ++it;
+                continue;
+            }
+
+            // If enough time has passed since the last notification to this
+            // listener but there are currently no pending notifications, the
+            // listener can be removed from mDeathNotifications---there is no
+            // need to keep track of the last notification time anymore.
+            if (deathNotifications.count == 0) {
+                it = mDeathNotifications.erase(it);
+                continue;
+            }
+
+            // Create the argument for the callback.
+            notifications.emplace_back(listener, deathNotifications.count);
+            hidl_vec<IComponentListener::InputBuffer> &inputBuffers =
+                    notifications.back().inputBuffers;
+            size_t i = 0;
+            for (std::pair<const uint64_t, std::vector<size_t>>& p :
+                    deathNotifications.indices) {
+                uint64_t frameIndex = p.first;
+                const std::vector<size_t> &bufferIndices = p.second;
+                for (const size_t& bufferIndex : bufferIndices) {
+                    IComponentListener::InputBuffer &inputBuffer
+                            = inputBuffers[i++];
+                    inputBuffer.arrayIndex = bufferIndex;
+                    inputBuffer.frameIndex = frameIndex;
+                }
+            }
+
+            // Clear deathNotifications for this listener and set retry to true
+            // so processNotifications will be called again. This will
+            // guarantee that a listener with no pending notifications will
+            // eventually be removed from mDeathNotifications after
+            // mNotificationIntervalNs nanoseconds has passed.
+            retry = true;
+            deathNotifications.indices.clear();
+            deathNotifications.count = 0;
+            deathNotifications.lastSentNs = timeNowNs;
+            ++it;
+        }
+    }
+
+    // Call onInputBuffersReleased() outside the lock to avoid deadlock.
+    for (const Notification& notification : notifications) {
+        if (!notification.listener->onInputBuffersReleased(
+                notification.inputBuffers).isOk()) {
+            // This may trigger if the client has died.
+            LOG(DEBUG) << "InputBufferManager::processNotifications -- "
+                       << "failed to send death notifications to "
+                       << "listener @ 0x" << std::hex
+                                          << notification.listener.get()
+                       << std::dec << ".";
+        } else {
+#if LOG_NDEBUG == 0
+            std::stringstream inputBufferLog;
+            for (const IComponentListener::InputBuffer& inputBuffer :
+                    notification.inputBuffers) {
+                inputBufferLog << " (" << inputBuffer.frameIndex
+                               << ", " << inputBuffer.arrayIndex
+                               << ")";
+            }
+            LOG(VERBOSE) << "InputBufferManager::processNotifications -- "
+                         << "death notifications sent to "
+                         << "listener @ 0x" << std::hex
+                                            << notification.listener.get()
+                                            << std::dec
+                         << " with these (frameIndex, bufferIndex) pairs:"
+                         << inputBufferLog.str();
+#endif
+        }
+    }
+#if LOG_NDEBUG == 0
+    if (retry) {
+        LOG(VERBOSE) << "InputBufferManager::processNotifications -- "
+                     << "will retry again in " << *timeToRetryNs << "ns.";
+    } else {
+        LOG(VERBOSE) << "InputBufferManager::processNotifications -- "
+                     << "no pending death notifications.";
+    }
+#endif
+    return retry;
+}
+
+void InputBufferManager::main() {
+    LOG(VERBOSE) << "InputBufferManager main -- started.";
+    nsecs_t timeToRetryNs;
+    while (true) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        while (mDeathNotifications.empty()) {
+            mOnBufferDestroyed.wait(lock);
+        }
+        lock.unlock();
+        while (processNotifications(&timeToRetryNs)) {
+            std::this_thread::sleep_for(
+                    std::chrono::nanoseconds(timeToRetryNs));
+        }
+    }
+}
+
+InputBufferManager::InputBufferManager()
+      : mMainThread{&InputBufferManager::main, this} {
+}
+
+InputBufferManager& InputBufferManager::getInstance() {
+    static InputBufferManager instance{};
+    return instance;
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+
+
diff --git a/media/codec2/hidl/1.0/utils/InputSurface.cpp b/media/codec2/hidl/1.0/utils/InputSurface.cpp
new file mode 100644
index 0000000..2b4ca85
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/InputSurface.cpp
@@ -0,0 +1,177 @@
+/*
+ * 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 "Codec2-InputSurface"
+#include <android-base/logging.h>
+
+#include <codec2/hidl/1.0/InputSurface.h>
+#include <codec2/hidl/1.0/InputSurfaceConnection.h>
+
+#include <C2Component.h>
+#include <C2Config.h>
+
+#include <memory>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using namespace ::android;
+
+// Derived class of C2InterfaceHelper
+class InputSurface::Interface : public C2InterfaceHelper {
+public:
+    explicit Interface(
+            const std::shared_ptr<C2ReflectorHelper> &helper)
+        : C2InterfaceHelper(helper) {
+
+        setDerivedInstance(this);
+
+        addParameter(
+                DefineParam(mEos, C2_PARAMKEY_INPUT_SURFACE_EOS)
+                .withDefault(new C2InputSurfaceEosTuning(false))
+                .withFields({C2F(mEos, value).oneOf({true, false})})
+                .withSetter(EosSetter)
+                .build());
+    }
+
+    static C2R EosSetter(bool mayBlock, C2P<C2InputSurfaceEosTuning> &me) {
+        (void)mayBlock;
+        return me.F(me.v.value).validatePossible(me.v.value);
+    }
+
+    bool eos() const { return mEos->value; }
+
+private:
+    std::shared_ptr<C2InputSurfaceEosTuning> mEos;
+};
+
+// Derived class of ConfigurableC2Intf
+class InputSurface::ConfigurableIntf : public ConfigurableC2Intf {
+public:
+    ConfigurableIntf(
+            const std::shared_ptr<InputSurface::Interface> &intf,
+            const sp<GraphicBufferSource> &source)
+        : ConfigurableC2Intf("input-surface", 0),
+          mIntf(intf),
+          mSource(source) {
+    }
+
+    virtual ~ConfigurableIntf() override = default;
+
+    virtual c2_status_t query(
+            const std::vector<C2Param::Index> &indices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const params
+            ) const override {
+        return mIntf->query({}, indices, mayBlock, params);
+    }
+
+    virtual c2_status_t config(
+            const std::vector<C2Param*> &params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures
+            ) override {
+        c2_status_t err = mIntf->config(params, mayBlock, failures);
+        if (mIntf->eos()) {
+            sp<GraphicBufferSource> source = mSource.promote();
+            if (source == nullptr || source->signalEndOfInputStream() != OK) {
+                // TODO: put something in |failures|
+                err = C2_BAD_VALUE;
+            }
+            // TODO: reset eos?
+        }
+        return err;
+    }
+
+    virtual c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
+            ) const override {
+        return mIntf->querySupportedParams(params);
+    }
+
+    virtual c2_status_t querySupportedValues(
+            std::vector<C2FieldSupportedValuesQuery>& fields,
+            c2_blocking_t mayBlock) const override {
+        return mIntf->querySupportedValues(fields, mayBlock);
+    }
+
+private:
+    const std::shared_ptr<InputSurface::Interface> mIntf;
+    wp<GraphicBufferSource> mSource;
+};
+
+Return<sp<InputSurface::HGraphicBufferProducer>> InputSurface::getGraphicBufferProducer() {
+    return mProducer;
+}
+
+Return<sp<IConfigurable>> InputSurface::getConfigurable() {
+    return mConfigurable;
+}
+
+Return<void> InputSurface::connect(
+        const sp<IInputSink>& sink,
+        connect_cb _hidl_cb) {
+    Status status;
+    sp<InputSurfaceConnection> connection;
+    if (!sink) {
+        _hidl_cb(Status::BAD_VALUE, nullptr);
+        return Void();
+    }
+    std::shared_ptr<C2Component> comp = Component::findLocalComponent(sink);
+    if (comp) {
+        connection = new InputSurfaceConnection(mSource, comp, mStore);
+    } else {
+        connection = new InputSurfaceConnection(mSource, sink, mStore);
+    }
+    if (!connection->init()) {
+        connection = nullptr;
+        status = Status::BAD_VALUE;
+    } else {
+        status = Status::OK;
+    }
+    _hidl_cb(status, connection);
+    return Void();
+}
+
+// Constructor is exclusive to ComponentStore.
+InputSurface::InputSurface(
+        const sp<ComponentStore>& store,
+        const std::shared_ptr<C2ReflectorHelper>& reflector,
+        const sp<HGraphicBufferProducer>& producer,
+        const sp<GraphicBufferSource>& source)
+      : mStore{store},
+        mProducer{producer},
+        mSource{source},
+        mIntf{std::make_shared<Interface>(reflector)},
+        mConfigurable{new CachedConfigurable(
+                std::make_unique<ConfigurableIntf>(
+                    mIntf, source))} {
+
+    mConfigurable->init(store.get());
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
diff --git a/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
new file mode 100644
index 0000000..c9932ef
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/InputSurfaceConnection.cpp
@@ -0,0 +1,529 @@
+/*
+ * 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 "Codec2-InputSurfaceConnection"
+#include <android-base/logging.h>
+
+#include <codec2/hidl/1.0/InputSurfaceConnection.h>
+#include <codec2/hidl/1.0/InputSurfaceConnection.h>
+
+#include <memory>
+#include <list>
+#include <mutex>
+#include <atomic>
+
+#include <hidl/HidlSupport.h>
+#include <media/stagefright/bqhelper/ComponentWrapper.h>
+#include <system/graphics.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
+
+#include <C2.h>
+#include <C2AllocatorGralloc.h>
+#include <C2BlockInternal.h>
+#include <C2Buffer.h>
+#include <C2Component.h>
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+#include <C2Work.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+constexpr int32_t kBufferCount = 16;
+
+using namespace ::android;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+
+namespace /* unnamed */ {
+
+class Buffer2D : public C2Buffer {
+public:
+    explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {
+    }
+};
+
+} // unnamed namespace
+
+// Derived class of ComponentWrapper for use with
+// GraphicBufferSource::configure().
+//
+struct InputSurfaceConnection::Impl : public ComponentWrapper {
+
+    Impl(const sp<GraphicBufferSource>& source,
+         const std::shared_ptr<C2Component>& localComp)
+          : mSource{source}, mLocalComp{localComp}, mSink{}, mFrameIndex{0} {
+        std::shared_ptr<C2ComponentInterface> intf = localComp->intf();
+        mSinkName = intf ? intf->getName() : "";
+    }
+
+    Impl(const sp<GraphicBufferSource>& source,
+         const sp<IInputSink>& sink)
+          : mSource{source}, mLocalComp{}, mSink{sink}, mFrameIndex{0} {
+        Return<sp<IConfigurable>> transResult = sink->getConfigurable();
+        if (!transResult.isOk()) {
+            LOG(ERROR) << "Remote sink is dead.";
+            return;
+        }
+        mSinkConfigurable =
+                static_cast<sp<IConfigurable>>(transResult);
+        if (!mSinkConfigurable) {
+            LOG(ERROR) << "Remote sink is not configurable.";
+            mSinkName = "";
+            return;
+        }
+
+        hidl_string name;
+        Return<void> transStatus = mSinkConfigurable->getName(
+                [&name](const hidl_string& n) {
+                    name = n;
+                });
+        if (!transStatus.isOk()) {
+            LOG(ERROR) << "Remote sink's configurable is dead.";
+            mSinkName = "";
+            return;
+        }
+        mSinkName = name.c_str();
+    }
+
+    virtual ~Impl() {
+        mSource->stop();
+        mSource->release();
+    }
+
+    bool init() {
+        if (mSource == nullptr) {
+            return false;
+        }
+        status_t err = mSource->initCheck();
+        if (err != OK) {
+            LOG(WARNING) << "Impl::init -- GraphicBufferSource init failed: "
+                         << "status = " << err << ".";
+            return false;
+        }
+
+        // TODO: read settings properly from the interface
+        C2StreamPictureSizeInfo::input inputSize;
+        C2StreamUsageTuning::input usage;
+        c2_status_t c2Status = queryFromSink({ &inputSize, &usage },
+                                         {},
+                                         C2_MAY_BLOCK,
+                                         nullptr);
+        if (c2Status != C2_OK) {
+            LOG(WARNING) << "Impl::init -- cannot query information from "
+                            "the component interface: "
+                         << "status = " << asString(c2Status) << ".";
+            return false;
+        }
+
+        // TODO: proper color aspect & dataspace
+        android_dataspace dataSpace = HAL_DATASPACE_BT709;
+
+        // TODO: use the usage read from intf
+        // uint32_t grallocUsage =
+        //         C2AndroidMemoryUsage(C2MemoryUsage(usage.value)).
+        //         asGrallocUsage();
+
+        uint32_t grallocUsage =
+                mSinkName.compare(0, 11, "c2.android.") == 0 ?
+                GRALLOC_USAGE_SW_READ_OFTEN :
+                GRALLOC_USAGE_HW_VIDEO_ENCODER;
+
+        err = mSource->configure(
+                this, dataSpace, kBufferCount,
+                inputSize.width, inputSize.height,
+                grallocUsage);
+        if (err != OK) {
+            LOG(WARNING) << "Impl::init -- GBS configure failed: "
+                         << "status = " << err << ".";
+            return false;
+        }
+        for (int32_t i = 0; i < kBufferCount; ++i) {
+            if (!mSource->onInputBufferAdded(i).isOk()) {
+                LOG(WARNING) << "Impl::init: failed to populate GBS slots.";
+                return false;
+            }
+        }
+        if (!mSource->start().isOk()) {
+            LOG(WARNING) << "Impl::init -- GBS failed to start.";
+            return false;
+        }
+        mAllocatorMutex.lock();
+        c2_status_t c2err = GetCodec2PlatformAllocatorStore()->fetchAllocator(
+                C2AllocatorStore::PLATFORM_START + 1,  // GRALLOC
+                &mAllocator);
+        mAllocatorMutex.unlock();
+        if (c2err != OK) {
+            LOG(WARNING) << "Impl::init -- failed to fetch gralloc allocator: "
+                         << "status = " << asString(c2err) << ".";
+            return false;
+        }
+        return true;
+    }
+
+    // From ComponentWrapper
+    virtual status_t submitBuffer(
+            int32_t bufferId,
+            const sp<GraphicBuffer>& buffer,
+            int64_t timestamp,
+            int fenceFd) override {
+        LOG(VERBOSE) << "Impl::submitBuffer -- bufferId = " << bufferId << ".";
+        // TODO: Use fd to construct fence
+        (void)fenceFd;
+
+        std::shared_ptr<C2GraphicAllocation> alloc;
+        C2Handle* handle = WrapNativeCodec2GrallocHandle(
+                buffer->handle,
+                buffer->width, buffer->height,
+                buffer->format, buffer->usage, buffer->stride);
+        mAllocatorMutex.lock();
+        c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc);
+        mAllocatorMutex.unlock();
+        if (err != OK) {
+            return UNKNOWN_ERROR;
+        }
+        std::shared_ptr<C2GraphicBlock> block =
+                _C2BlockFactory::CreateGraphicBlock(alloc);
+
+        std::unique_ptr<C2Work> work(new C2Work);
+        work->input.flags = (C2FrameData::flags_t)0;
+        work->input.ordinal.timestamp = timestamp;
+        work->input.ordinal.frameIndex = mFrameIndex.fetch_add(
+                1, std::memory_order_relaxed);
+        work->input.buffers.clear();
+        std::shared_ptr<C2Buffer> c2Buffer(
+                // TODO: fence
+                new Buffer2D(block->share(
+                        C2Rect(block->width(), block->height()), ::C2Fence())),
+                [bufferId, source = mSource](C2Buffer* ptr) {
+                    delete ptr;
+                    if (source != nullptr) {
+                        // TODO: fence
+                        (void)source->onInputBufferEmptied(bufferId, -1);
+                    }
+                });
+        work->input.buffers.push_back(c2Buffer);
+        work->worklets.clear();
+        work->worklets.emplace_back(new C2Worklet);
+        std::list<std::unique_ptr<C2Work>> items;
+        items.push_back(std::move(work));
+
+        err = queueToSink(&items);
+        return (err == C2_OK) ? OK : UNKNOWN_ERROR;
+    }
+
+    virtual status_t submitEos(int32_t bufferId) override {
+        LOG(VERBOSE) << "Impl::submitEos -- bufferId = " << bufferId << ".";
+        (void)bufferId;
+
+        std::unique_ptr<C2Work> work(new C2Work);
+        work->input.flags = (C2FrameData::flags_t)0;
+        work->input.ordinal.frameIndex = mFrameIndex.fetch_add(
+                1, std::memory_order_relaxed);
+        work->input.buffers.clear();
+        work->worklets.clear();
+        work->worklets.emplace_back(new C2Worklet);
+        std::list<std::unique_ptr<C2Work>> items;
+        items.push_back(std::move(work));
+
+        c2_status_t err = queueToSink(&items);
+        return (err == C2_OK) ? OK : UNKNOWN_ERROR;
+    }
+
+    virtual void dispatchDataSpaceChanged(
+            int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
+        // TODO
+        (void)dataSpace;
+        (void)aspects;
+        (void)pixelFormat;
+    }
+
+    // Configurable interface for InputSurfaceConnection::Impl.
+    //
+    // This class is declared as an inner class so that it will have access to
+    // all Impl's members.
+    struct ConfigurableIntf : public ConfigurableC2Intf {
+        sp<Impl> mConnection;
+        ConfigurableIntf(const sp<Impl>& connection)
+              : ConfigurableC2Intf{"input-surface-connection", 0},
+                mConnection{connection} {}
+        virtual c2_status_t config(
+                const std::vector<C2Param*> &params,
+                c2_blocking_t mayBlock,
+                std::vector<std::unique_ptr<C2SettingResult>> *const failures
+                ) override;
+        virtual c2_status_t query(
+                const std::vector<C2Param::Index> &indices,
+                c2_blocking_t mayBlock,
+                std::vector<std::unique_ptr<C2Param>> *const params) const override;
+        virtual c2_status_t querySupportedParams(
+                std::vector<std::shared_ptr<C2ParamDescriptor>> *const params
+                ) const override;
+        virtual c2_status_t querySupportedValues(
+                std::vector<C2FieldSupportedValuesQuery> &fields,
+                c2_blocking_t mayBlock) const override;
+    };
+
+private:
+    c2_status_t queryFromSink(
+            const std::vector<C2Param*> &stackParams,
+            const std::vector<C2Param::Index> &heapParamIndices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const heapParams) {
+        if (mLocalComp) {
+            std::shared_ptr<C2ComponentInterface> intf = mLocalComp->intf();
+            if (intf) {
+                return intf->query_vb(stackParams,
+                                      heapParamIndices,
+                                      mayBlock,
+                                      heapParams);
+            } else {
+                LOG(ERROR) << "queryFromSink -- "
+                           << "component does not have an interface.";
+                return C2_BAD_STATE;
+            }
+        }
+
+        CHECK(mSink) << "-- queryFromSink "
+                     << "-- connection has no sink.";
+        CHECK(mSinkConfigurable) << "-- queryFromSink "
+                                 << "-- sink has no configurable.";
+
+        hidl_vec<ParamIndex> indices(
+                stackParams.size() + heapParamIndices.size());
+        size_t numIndices = 0;
+        for (C2Param* const& stackParam : stackParams) {
+            if (!stackParam) {
+                LOG(DEBUG) << "queryFromSink -- null stack param encountered.";
+                continue;
+            }
+            indices[numIndices++] = static_cast<ParamIndex>(stackParam->index());
+        }
+        size_t numStackIndices = numIndices;
+        for (const C2Param::Index& index : heapParamIndices) {
+            indices[numIndices++] =
+                    static_cast<ParamIndex>(static_cast<uint32_t>(index));
+        }
+        indices.resize(numIndices);
+        if (heapParams) {
+            heapParams->reserve(heapParams->size() + numIndices);
+        }
+        c2_status_t status;
+        Return<void> transStatus = mSinkConfigurable->query(
+                indices,
+                mayBlock == C2_MAY_BLOCK,
+                [&status, &numStackIndices, &stackParams, heapParams](
+                        Status s, const Params& p) {
+                    status = static_cast<c2_status_t>(s);
+                    if (status != C2_OK && status != C2_BAD_INDEX) {
+                        LOG(DEBUG) << "queryFromSink -- call failed: "
+                                   << "status = " << asString(status) << ".";
+                        return;
+                    }
+                    std::vector<C2Param*> paramPointers;
+                    if (!parseParamsBlob(&paramPointers, p)) {
+                        LOG(DEBUG) << "queryFromSink -- error while "
+                                   << "parsing params.";
+                        status = C2_CORRUPTED;
+                        return;
+                    }
+                    size_t i = 0;
+                    for (auto it = paramPointers.begin();
+                            it != paramPointers.end(); ) {
+                        C2Param* paramPointer = *it;
+                        if (numStackIndices > 0) {
+                            --numStackIndices;
+                            if (!paramPointer) {
+                                LOG(DEBUG) << "queryFromSink -- "
+                                              "null stack param.";
+                                ++it;
+                                continue;
+                            }
+                            for (; i < stackParams.size() &&
+                                    !stackParams[i]; ) {
+                                ++i;
+                            }
+                            CHECK(i < stackParams.size());
+                            if (stackParams[i]->index() !=
+                                    paramPointer->index()) {
+                                LOG(DEBUG) << "queryFromSink -- "
+                                              "param skipped (index = "
+                                           << stackParams[i]->index() << ").";
+                                stackParams[i++]->invalidate();
+                                continue;
+                            }
+                            if (!stackParams[i++]->updateFrom(*paramPointer)) {
+                                LOG(DEBUG) << "queryFromSink -- "
+                                              "param update failed (index = "
+                                           << paramPointer->index() << ").";
+                            }
+                        } else {
+                            if (!paramPointer) {
+                                LOG(DEBUG) << "queryFromSink -- "
+                                              "null heap param.";
+                                ++it;
+                                continue;
+                            }
+                            if (!heapParams) {
+                                LOG(WARNING) << "queryFromSink -- "
+                                                "too many stack params.";
+                                break;
+                            }
+                            heapParams->emplace_back(C2Param::Copy(*paramPointer));
+                        }
+                        ++it;
+                    }
+                });
+        if (!transStatus.isOk()) {
+            LOG(ERROR) << "queryFromSink -- transaction failed.";
+            return C2_CORRUPTED;
+        }
+        return status;
+    }
+
+    c2_status_t queueToSink(std::list<std::unique_ptr<C2Work>>* const items) {
+        if (mLocalComp) {
+            return mLocalComp->queue_nb(items);
+        }
+
+        CHECK(mSink) << "-- queueToSink "
+                     << "-- connection has no sink.";
+
+        WorkBundle workBundle;
+        if (!objcpy(&workBundle, *items, nullptr)) {
+            LOG(ERROR) << "queueToSink -- bad input.";
+            return C2_CORRUPTED;
+        }
+        Return<Status> transStatus = mSink->queue(workBundle);
+        if (!transStatus.isOk()) {
+            LOG(ERROR) << "queueToSink -- transaction failed.";
+            return C2_CORRUPTED;
+        }
+        c2_status_t status =
+                static_cast<c2_status_t>(static_cast<Status>(transStatus));
+        if (status != C2_OK) {
+            LOG(DEBUG) << "queueToSink -- call failed: "
+                         << asString(status);
+        }
+        return status;
+    }
+
+    sp<GraphicBufferSource> mSource;
+    std::shared_ptr<C2Component> mLocalComp;
+    sp<IInputSink> mSink;
+    sp<IConfigurable> mSinkConfigurable;
+    std::string mSinkName;
+
+    // Needed for ComponentWrapper implementation
+    std::mutex mAllocatorMutex;
+    std::shared_ptr<C2Allocator> mAllocator;
+    std::atomic_uint64_t mFrameIndex;
+
+};
+
+InputSurfaceConnection::InputSurfaceConnection(
+        const sp<GraphicBufferSource>& source,
+        const std::shared_ptr<C2Component>& comp,
+        const sp<ComponentStore>& store)
+      : mImpl{new Impl(source, comp)},
+        mConfigurable{new CachedConfigurable(
+            std::make_unique<Impl::ConfigurableIntf>(mImpl))} {
+    mConfigurable->init(store.get());
+}
+
+InputSurfaceConnection::InputSurfaceConnection(
+        const sp<GraphicBufferSource>& source,
+        const sp<IInputSink>& sink,
+        const sp<ComponentStore>& store)
+      : mImpl{new Impl(source, sink)},
+        mConfigurable{new CachedConfigurable(
+            std::make_unique<Impl::ConfigurableIntf>(mImpl))} {
+    mConfigurable->init(store.get());
+}
+
+Return<Status> InputSurfaceConnection::disconnect() {
+    std::lock_guard<std::mutex> lock(mImplMutex);
+    mImpl = nullptr;
+    return Status::OK;
+}
+
+InputSurfaceConnection::~InputSurfaceConnection() {
+    mImpl = nullptr;
+}
+
+bool InputSurfaceConnection::init() {
+    std::lock_guard<std::mutex> lock(mImplMutex);
+    return mImpl->init();
+}
+
+Return<sp<IConfigurable>> InputSurfaceConnection::getConfigurable() {
+    return mConfigurable;
+}
+
+// Configurable interface for InputSurfaceConnection::Impl
+c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::config(
+        const std::vector<C2Param*> &params,
+        c2_blocking_t mayBlock,
+        std::vector<std::unique_ptr<C2SettingResult>> *const failures) {
+    // TODO: implement
+    (void)params;
+    (void)mayBlock;
+    (void)failures;
+    return C2_OK;
+}
+
+c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::query(
+        const std::vector<C2Param::Index> &indices,
+        c2_blocking_t mayBlock,
+        std::vector<std::unique_ptr<C2Param>> *const params) const {
+    // TODO: implement
+    (void)indices;
+    (void)mayBlock;
+    (void)params;
+    return C2_OK;
+}
+
+c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::querySupportedParams(
+        std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const {
+    // TODO: implement
+    (void)params;
+    return C2_OK;
+}
+
+c2_status_t InputSurfaceConnection::Impl::ConfigurableIntf::querySupportedValues(
+        std::vector<C2FieldSupportedValuesQuery> &fields,
+        c2_blocking_t mayBlock) const {
+    // TODO: implement
+    (void)fields;
+    (void)mayBlock;
+    return C2_OK;
+}
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
new file mode 100644
index 0000000..0a2298c
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ClientBlockHelper.h
@@ -0,0 +1,75 @@
+/*
+ * 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 CLIENT_BLOCK_HELPER_H
+#define CLIENT_BLOCK_HELPER_H
+
+#include <gui/IGraphicBufferProducer.h>
+#include <codec2/hidl/1.0/types.h>
+#include <C2Work.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+// BufferQueue-Based Block Operations
+// ==================================
+
+// Manage BufferQueue and graphic blocks for both component and codec.
+// Manage graphic blocks ownership consistently during surface change.
+struct OutputBufferQueue {
+
+    OutputBufferQueue();
+
+    ~OutputBufferQueue();
+
+    // Configure a new surface to render graphic blocks.
+    // Graphic blocks from older surface will be migrated to new surface.
+    bool configure(const sp<IGraphicBufferProducer>& igbp,
+                   uint32_t generation,
+                   uint64_t bqId);
+
+    // Render a graphic block to current surface.
+    status_t outputBuffer(
+            const C2ConstGraphicBlock& block,
+            const BnGraphicBufferProducer::QueueBufferInput& input,
+            BnGraphicBufferProducer::QueueBufferOutput* output);
+
+    // Call holdBufferQueueBlock() on output blocks in the given workList.
+    // The OutputBufferQueue will take the ownership of output blocks.
+    //
+    // Note: This function should be called after WorkBundle has been received
+    // from another process.
+    void holdBufferQueueBlocks(
+            const std::list<std::unique_ptr<C2Work>>& workList);
+
+private:
+
+    class Impl;
+    std::unique_ptr<Impl> mImpl;
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // CLIENT_BLOCK_HELPER_H
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
new file mode 100644
index 0000000..86dccd0
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
@@ -0,0 +1,144 @@
+/*
+ * 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 CODEC2_HIDL_V1_0_UTILS_COMPONENT_H
+#define CODEC2_HIDL_V1_0_UTILS_COMPONENT_H
+
+#include <codec2/hidl/1.0/ComponentInterface.h>
+#include <codec2/hidl/1.0/Configurable.h>
+#include <codec2/hidl/1.0/types.h>
+
+#include <android/hardware/media/bufferpool/2.0/IClientManager.h>
+#include <android/hardware/media/c2/1.0/IComponent.h>
+#include <android/hardware/media/c2/1.0/IComponentInterface.h>
+#include <android/hardware/media/c2/1.0/IComponentListener.h>
+#include <android/hardware/media/c2/1.0/IComponentStore.h>
+#include <android/hardware/media/c2/1.0/IInputSink.h>
+#include <hidl/Status.h>
+#include <hwbinder/IBinder.h>
+
+#include <C2Component.h>
+#include <C2Buffer.h>
+#include <C2.h>
+
+#include <map>
+#include <memory>
+#include <mutex>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+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::hardware::IBinder;
+using ::android::sp;
+using ::android::wp;
+
+struct ComponentStore;
+
+struct Component : public IComponent,
+                   public std::enable_shared_from_this<Component> {
+    Component(
+            const std::shared_ptr<C2Component>&,
+            const sp<IComponentListener>& listener,
+            const sp<ComponentStore>& store,
+            const sp<::android::hardware::media::bufferpool::V2_0::
+                IClientManager>& clientPoolManager);
+    c2_status_t status() const;
+
+    typedef ::android::hardware::graphics::bufferqueue::V1_0::
+            IGraphicBufferProducer HGraphicBufferProducer1;
+    typedef ::android::hardware::graphics::bufferqueue::V2_0::
+            IGraphicBufferProducer HGraphicBufferProducer2;
+
+    // Methods from IComponent follow.
+    virtual Return<Status> queue(const WorkBundle& workBundle) override;
+    virtual Return<void> flush(flush_cb _hidl_cb) override;
+    virtual Return<Status> drain(bool withEos) override;
+    virtual Return<Status> setOutputSurface(
+            uint64_t blockPoolId,
+            const sp<HGraphicBufferProducer2>& surface) override;
+    virtual Return<void> connectToInputSurface(
+            const sp<IInputSurface>& inputSurface,
+            connectToInputSurface_cb _hidl_cb) override;
+    virtual Return<void> connectToOmxInputSurface(
+            const sp<HGraphicBufferProducer1>& producer,
+            const sp<::android::hardware::media::omx::V1_0::
+            IGraphicBufferSource>& source,
+            connectToOmxInputSurface_cb _hidl_cb) override;
+    virtual Return<Status> disconnectFromInputSurface() override;
+    virtual Return<void> createBlockPool(
+            uint32_t allocatorId,
+            createBlockPool_cb _hidl_cb) override;
+    virtual Return<Status> destroyBlockPool(uint64_t blockPoolId) override;
+    virtual Return<Status> start() override;
+    virtual Return<Status> stop() override;
+    virtual Return<Status> reset() override;
+    virtual Return<Status> release() override;
+    virtual Return<sp<IComponentInterface>> getInterface() override;
+    virtual Return<sp<IInputSink>> asInputSink() override;
+
+    // Returns a C2Component associated to the given sink if the sink is indeed
+    // a local component. Returns nullptr otherwise.
+    //
+    // This function is used by InputSurface::connect().
+    static std::shared_ptr<C2Component> findLocalComponent(
+            const sp<IInputSink>& sink);
+
+protected:
+    c2_status_t mInit;
+    std::shared_ptr<C2Component> mComponent;
+    sp<ComponentInterface> mInterface;
+    sp<IComponentListener> mListener;
+    sp<ComponentStore> mStore;
+    ::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender
+            mBufferPoolSender;
+
+    struct Sink;
+    std::mutex mSinkMutex;
+    sp<Sink> mSink;
+
+    std::mutex mBlockPoolsMutex;
+    // This map keeps C2BlockPool objects that are created by createBlockPool()
+    // alive. These C2BlockPool objects can be deleted by calling
+    // destroyBlockPool(), reset() or release(), or by destroying the component.
+    std::map<uint64_t, std::shared_ptr<C2BlockPool>> mBlockPools;
+
+    void initListener(const sp<Component>& self);
+
+    virtual ~Component() override;
+
+    friend struct ComponentStore;
+
+    struct Listener;
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // CODEC2_HIDL_V1_0_UTILS_COMPONENT_H
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
new file mode 100644
index 0000000..a5d235e
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
@@ -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.
+ */
+
+#ifndef CODEC2_HIDL_V1_0_UTILS_COMPONENT_INTERFACE_H
+#define CODEC2_HIDL_V1_0_UTILS_COMPONENT_INTERFACE_H
+
+#include <codec2/hidl/1.0/Configurable.h>
+#include <codec2/hidl/1.0/types.h>
+
+#include <android/hardware/media/c2/1.0/IComponentInterface.h>
+#include <hidl/Status.h>
+
+#include <C2Component.h>
+#include <C2Buffer.h>
+#include <C2.h>
+
+#include <memory>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ComponentStore;
+
+struct ComponentInterface : public IComponentInterface {
+    ComponentInterface(
+            const std::shared_ptr<C2ComponentInterface>& interface,
+            ComponentStore* store);
+    c2_status_t status() const;
+    virtual Return<sp<IConfigurable>> getConfigurable() override;
+
+protected:
+    std::shared_ptr<C2ComponentInterface> mInterface;
+    sp<CachedConfigurable> mConfigurable;
+    c2_status_t mInit;
+};
+
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // CODEC2_HIDL_V1_0_UTILS_COMPONENT_INTERFACE_H
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h
new file mode 100644
index 0000000..be80c62
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentStore.h
@@ -0,0 +1,150 @@
+/*
+ * 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 CODEC2_HIDL_V1_0_UTILS_COMPONENTSTORE_H
+#define CODEC2_HIDL_V1_0_UTILS_COMPONENTSTORE_H
+
+#include <codec2/hidl/1.0/Component.h>
+#include <codec2/hidl/1.0/ComponentInterface.h>
+#include <codec2/hidl/1.0/Configurable.h>
+
+#include <android/hardware/media/bufferpool/2.0/IClientManager.h>
+#include <android/hardware/media/c2/1.0/IComponentStore.h>
+#include <hidl/Status.h>
+
+#include <C2Component.h>
+#include <C2Param.h>
+#include <C2.h>
+
+#include <chrono>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hardware::media::bufferpool::V2_0::IClientManager;
+
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ComponentStore : public IComponentStore {
+    ComponentStore(const std::shared_ptr<C2ComponentStore>& store);
+    virtual ~ComponentStore() = default;
+
+    /**
+     * Returns the status of the construction of this object.
+     */
+    c2_status_t status() const;
+
+    /**
+     * This function is called by CachedConfigurable::init() to validate
+     * supported parameters.
+     */
+    c2_status_t validateSupportedParams(
+            const std::vector<std::shared_ptr<C2ParamDescriptor>>& params);
+
+    // Methods from ::android::hardware::media::c2::V1_0::IComponentStore.
+    virtual Return<void> createComponent(
+            const hidl_string& name,
+            const sp<IComponentListener>& listener,
+            const sp<IClientManager>& pool,
+            createComponent_cb _hidl_cb) override;
+    virtual Return<void> createInterface(
+            const hidl_string& name,
+            createInterface_cb _hidl_cb) override;
+    virtual Return<void> listComponents(listComponents_cb _hidl_cb) override;
+    virtual Return<void> createInputSurface(
+            createInputSurface_cb _hidl_cb) override;
+    virtual Return<void> getStructDescriptors(
+            const hidl_vec<uint32_t>& indices,
+            getStructDescriptors_cb _hidl_cb) override;
+    virtual Return<sp<IClientManager>> getPoolClientManager() override;
+    virtual Return<Status> copyBuffer(
+            const Buffer& src,
+            const Buffer& dst) override;
+    virtual Return<sp<IConfigurable>> getConfigurable() override;
+
+    /**
+     * Dumps information when lshal is called.
+     */
+    virtual Return<void> debug(
+            const hidl_handle& handle,
+            const hidl_vec<hidl_string>& args) override;
+
+protected:
+    sp<CachedConfigurable> mConfigurable;
+
+    // Does bookkeeping for an interface that has been loaded.
+    void onInterfaceLoaded(const std::shared_ptr<C2ComponentInterface> &intf);
+
+    c2_status_t mInit;
+    std::shared_ptr<C2ComponentStore> mStore;
+    std::shared_ptr<C2ParamReflector> mParamReflector;
+
+    std::map<C2Param::CoreIndex, std::shared_ptr<C2StructDescriptor>> mStructDescriptors;
+    std::set<C2Param::CoreIndex> mUnsupportedStructDescriptors;
+    std::set<C2String> mLoadedInterfaces;
+    mutable std::mutex mStructDescriptorsMutex;
+
+    // ComponentStore keeps track of live Components.
+
+    struct ComponentStatus {
+        std::shared_ptr<C2Component> c2Component;
+        std::chrono::system_clock::time_point birthTime;
+    };
+
+    mutable std::mutex mComponentRosterMutex;
+    std::map<Component*, ComponentStatus> mComponentRoster;
+
+    // Called whenever Component is created.
+    void reportComponentBirth(Component* component);
+    // Called only from the destructor of Component.
+    void reportComponentDeath(Component* component);
+
+    friend Component;
+
+    // Helper functions for dumping.
+
+    std::ostream& dump(
+            std::ostream& out,
+            const std::shared_ptr<const C2Component::Traits>& comp);
+
+    std::ostream& dump(
+            std::ostream& out,
+            ComponentStatus& compStatus);
+
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // CODEC2_HIDL_V1_0_UTILS_COMPONENTSTORE_H
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Configurable.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Configurable.h
new file mode 100644
index 0000000..8095185
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/Configurable.h
@@ -0,0 +1,138 @@
+/*
+ * 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 CODEC2_HIDL_V1_0_UTILS_CONFIGURABLE_H
+#define CODEC2_HIDL_V1_0_UTILS_CONFIGURABLE_H
+
+#include <android/hardware/media/c2/1.0/IConfigurable.h>
+#include <hidl/Status.h>
+
+#include <C2Component.h>
+#include <C2Param.h>
+#include <C2.h>
+
+#include <memory>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ComponentStore;
+
+/**
+ * Codec2 objects of different types may have different querying and configuring
+ * functions, but across the Treble boundary, they share the same HIDL
+ * interface, IConfigurable.
+ *
+ * ConfigurableC2Intf is an abstract class that a Codec2 object can implement to
+ * easily expose an IConfigurable instance. See CachedConfigurable below.
+ */
+struct ConfigurableC2Intf {
+    C2String getName() const { return mName; }
+    uint32_t getId() const { return mId; }
+    /** C2ComponentInterface::query_vb sans stack params */
+    virtual c2_status_t query(
+            const std::vector<C2Param::Index> &indices,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2Param>>* const params) const = 0;
+    /** C2ComponentInterface::config_vb */
+    virtual c2_status_t config(
+            const std::vector<C2Param*> &params,
+            c2_blocking_t mayBlock,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) = 0;
+    /** C2ComponentInterface::querySupportedParams_nb */
+    virtual c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const = 0;
+    /** C2ComponentInterface::querySupportedParams_nb */
+    virtual c2_status_t querySupportedValues(
+            std::vector<C2FieldSupportedValuesQuery>& fields, c2_blocking_t mayBlock) const = 0;
+
+    virtual ~ConfigurableC2Intf() = default;
+
+    ConfigurableC2Intf(const C2String& name, uint32_t id)
+          : mName{name}, mId{id} {}
+
+protected:
+    C2String mName; /* cached component name */
+    uint32_t mId;
+};
+
+/**
+ * Implementation of the IConfigurable interface that supports caching of
+ * supported parameters from a supplied ComponentStore.
+ *
+ * CachedConfigurable essentially converts a ConfigurableC2Intf into HIDL's
+ * IConfigurable. A Codec2 object generally implements ConfigurableC2Intf and
+ * passes the implementation to the constructor of CachedConfigurable.
+ *
+ * Note that caching happens
+ */
+struct CachedConfigurable : public IConfigurable {
+    CachedConfigurable(std::unique_ptr<ConfigurableC2Intf>&& intf);
+
+    c2_status_t init(ComponentStore* store);
+
+    // Methods from ::android::hardware::media::c2::V1_0::IConfigurable
+
+    virtual Return<uint32_t> getId() override;
+
+    virtual Return<void> getName(getName_cb _hidl_cb) override;
+
+    virtual Return<void> query(
+            const hidl_vec<uint32_t>& indices,
+            bool mayBlock,
+            query_cb _hidl_cb) override;
+
+    virtual Return<void> config(
+            const hidl_vec<uint8_t>& inParams,
+            bool mayBlock,
+            config_cb _hidl_cb) override;
+
+    virtual Return<void> querySupportedParams(
+            uint32_t start,
+            uint32_t count,
+            querySupportedParams_cb _hidl_cb) override;
+
+    virtual Return<void> querySupportedValues(
+            const hidl_vec<FieldSupportedValuesQuery>& inFields,
+            bool mayBlock,
+            querySupportedValues_cb _hidl_cb) override;
+
+protected:
+    // Common Codec2.0 interface wrapper
+    std::unique_ptr<ConfigurableC2Intf> mIntf;
+
+    // Cached supported params
+    std::vector<std::shared_ptr<C2ParamDescriptor>> mSupportedParams;
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // CODEC2_HIDL_V1_0_UTILS_CONFIGURABLE_H
+
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputBufferManager.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputBufferManager.h
new file mode 100644
index 0000000..b6857d5
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputBufferManager.h
@@ -0,0 +1,294 @@
+/*
+ * 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 CODEC2_HIDL_V1_0_UTILS_INPUT_BUFFER_MANAGER_H
+#define CODEC2_HIDL_V1_0_UTILS_INPUT_BUFFER_MANAGER_H
+
+#include <android/hardware/media/c2/1.0/IComponentListener.h>
+#include <utils/Timers.h>
+
+#include <C2Buffer.h>
+#include <C2Work.h>
+
+#include <set>
+#include <map>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using namespace ::android;
+
+/**
+ * InputBufferManager
+ * ==================
+ *
+ * InputBufferManager presents a way to track and untrack input buffers in this
+ * (codec) process and send a notification to a listener, possibly in a
+ * different process, when a tracked buffer no longer has any references in this
+ * process.
+ *
+ * InputBufferManager holds a collection of records representing tracked buffers
+ * and their callback listeners. Conceptually, one record is a triple (listener,
+ * frameIndex, bufferIndex) where
+ *
+ * - (frameIndex, bufferIndex) is a pair of indices used to identify the buffer.
+ * - listener is of type IComponentListener. Its onInputBuffersReleased()
+ *   function will be called after the associated buffer dies. The argument of
+ *   onInputBuffersReleased() is a list of InputBuffer objects, each of which
+ *   has the following members:
+ *
+ *     uint64_t frameIndex
+ *     uint32_t arrayIndex
+ *
+ * When a tracked buffer associated to the triple (listener, frameIndex,
+ * bufferIndex) goes out of scope, listener->onInputBuffersReleased() will be
+ * called with an InputBuffer object whose members are set as follows:
+ *
+ *     inputBuffer.frameIndex = frameIndex
+ *     inputBuffer.arrayIndex = bufferIndex
+ *
+ * IPC Optimization
+ * ----------------
+ *
+ * Since onInputBuffersReleased() is an IPC call, InputBufferManager tries not
+ * to call it too often. Any two calls to the same listener are at least
+ * mNotificationIntervalNs nanoseconds apart, where mNotificationIntervalNs is
+ * configurable via calling setNotificationInterval(). The default value of
+ * mNotificationIntervalNs is kDefaultNotificationInternalNs.
+ *
+ * Public Member Functions
+ * -----------------------
+ *
+ * InputBufferManager is a singleton class. Its only instance is accessible via
+ * the following public functions:
+ *
+ * - registerFrameData(const sp<IComponentListener>& listener,
+ *                     const C2FrameData& input)
+ *
+ * - unregisterFrameData(const sp<IComponentListener>& listener,
+ *                       const C2FrameData& input)
+ *
+ * - unregisterFrameData(const sp<IComponentListener>& listener)
+ *
+ * - setNotificationInterval(nsecs_t notificationIntervalNs)
+ *
+ */
+
+struct InputBufferManager {
+
+    /**
+     * The default value for the time interval between 2 subsequent IPCs.
+     */
+    static constexpr nsecs_t kDefaultNotificationIntervalNs = 1000000; /* 1ms */
+
+    /**
+     * Track all buffers in a C2FrameData object.
+     *
+     * input (C2FrameData) has the following two members that are of interest:
+     *
+     *   C2WorkOrdinal                ordinal
+     *   vector<shared_ptr<C2Buffer>> buffers
+     *
+     * Calling registerFrameData(listener, input) will register multiple
+     * triples (listener, frameIndex, bufferIndex) where frameIndex is equal to
+     * input.ordinal.frameIndex and bufferIndex runs through the indices of
+     * input.buffers such that input.buffers[bufferIndex] is not null.
+     *
+     * This should be called from queue().
+     *
+     * \param listener Listener of death notifications.
+     * \param input Input frame data whose input buffers are to be tracked.
+     */
+    static void registerFrameData(
+            const sp<IComponentListener>& listener,
+            const C2FrameData& input);
+
+    /**
+     * Untrack all buffers in a C2FrameData object.
+     *
+     * Calling unregisterFrameData(listener, input) will unregister and remove
+     * pending notifications for all triples (l, fi, bufferIndex) such that
+     * l = listener and fi = input.ordinal.frameIndex.
+     *
+     * This should be called from onWorkDone() and flush().
+     *
+     * \param listener Previously registered listener.
+     * \param input Previously registered frame data.
+     */
+    static void unregisterFrameData(
+            const wp<IComponentListener>& listener,
+            const C2FrameData& input);
+
+    /**
+     * Untrack all buffers associated to a given listener.
+     *
+     * Calling unregisterFrameData(listener) will unregister and remove
+     * pending notifications for all triples (l, frameIndex, bufferIndex) such
+     * that l = listener.
+     *
+     * This should be called when the component cleans up all input buffers,
+     * i.e., when reset(), release(), stop() or ~Component() is called.
+     *
+     * \param listener Previously registered listener.
+     */
+    static void unregisterFrameData(
+            const wp<IComponentListener>& listener);
+
+    /**
+     * Set the notification interval.
+     *
+     * \param notificationIntervalNs New notification interval, in nanoseconds.
+     */
+    static void setNotificationInterval(nsecs_t notificationIntervalNs);
+
+private:
+    void _registerFrameData(
+            const sp<IComponentListener>& listener,
+            const C2FrameData& input);
+    void _unregisterFrameData(
+            const wp<IComponentListener>& listener,
+            const C2FrameData& input);
+    void _unregisterFrameData(
+            const wp<IComponentListener>& listener);
+    void _setNotificationInterval(nsecs_t notificationIntervalNs);
+
+    // The callback function tied to C2Buffer objects.
+    //
+    // Note: This function assumes that sInstance is the only instance of this
+    //       class.
+    static void onBufferDestroyed(const C2Buffer* buf, void* arg);
+    void _onBufferDestroyed(const C2Buffer* buf, void* arg);
+
+    // Persistent data to be passed as "arg" in onBufferDestroyed().
+    // This is essentially the triple (listener, frameIndex, bufferIndex) plus a
+    // weak pointer to the C2Buffer object.
+    //
+    // Note that the "key" is bufferIndex according to operator<(). This is
+    // designed to work with TrackedBuffersMap defined below.
+    struct TrackedBuffer {
+        wp<IComponentListener> listener;
+        uint64_t frameIndex;
+        size_t bufferIndex;
+        std::weak_ptr<C2Buffer> buffer;
+        TrackedBuffer(const wp<IComponentListener>& listener,
+                      uint64_t frameIndex,
+                      size_t bufferIndex,
+                      const std::shared_ptr<C2Buffer>& buffer)
+              : listener(listener),
+                frameIndex(frameIndex),
+                bufferIndex(bufferIndex),
+                buffer(buffer) {}
+        TrackedBuffer(const TrackedBuffer&) = default;
+        bool operator<(const TrackedBuffer& other) const {
+            return bufferIndex < other.bufferIndex;
+        }
+    };
+
+    // Map: listener -> frameIndex -> set<TrackedBuffer>.
+    // Essentially, this is used to store triples (listener, frameIndex,
+    // bufferIndex) that's searchable by listener and (listener, frameIndex).
+    // However, the value of the innermost map is TrackedBuffer, which also
+    // contains an extra copy of listener and frameIndex. This is needed
+    // because onBufferDestroyed() needs to know listener and frameIndex too.
+    typedef std::map<wp<IComponentListener>,
+                     std::map<uint64_t,
+                              std::set<TrackedBuffer>>> TrackedBuffersMap;
+
+    // Storage for pending (unsent) death notifications for one listener.
+    // Each pair in member named "indices" are (frameIndex, bufferIndex) from
+    // the (listener, frameIndex, bufferIndex) triple.
+    struct DeathNotifications {
+
+        // The number of pending notifications for this listener.
+        // count may be 0, in which case the DeathNotifications object will
+        // remain valid for only a small period (specified
+        // nanoseconds).
+        size_t count;
+
+        // The timestamp of the most recent callback on this listener. This is
+        // used to guarantee that callbacks do not occur too frequently, and
+        // also to trigger expiration of a DeathNotifications object that has
+        // count = 0.
+        nsecs_t lastSentNs;
+
+        // Map: frameIndex -> vector of bufferIndices
+        // This is essentially a collection of (framdeIndex, bufferIndex).
+        std::map<uint64_t, std::vector<size_t>> indices;
+
+        DeathNotifications(
+                nsecs_t notificationIntervalNs = kDefaultNotificationIntervalNs)
+              : count(0),
+                lastSentNs(systemTime() - notificationIntervalNs),
+                indices() {}
+    };
+
+    // The minimum time period between IPC calls to notify the client about the
+    // destruction of input buffers.
+    std::atomic<nsecs_t> mNotificationIntervalNs{kDefaultNotificationIntervalNs};
+
+    // Mutex for the management of all input buffers.
+    std::mutex mMutex;
+
+    // Tracked input buffers.
+    TrackedBuffersMap mTrackedBuffersMap;
+
+    // Death notifications to be sent.
+    //
+    // A DeathNotifications object is associated to each listener. An entry in
+    // this map will be removed if its associated DeathNotifications has count =
+    // 0 and lastSentNs < systemTime() - mNotificationIntervalNs.
+    std::map<wp<IComponentListener>, DeathNotifications> mDeathNotifications;
+
+    // Condition variable signaled when an entry is added to mDeathNotifications.
+    std::condition_variable mOnBufferDestroyed;
+
+    // Notify the clients about buffer destructions.
+    // Return false if all destructions have been notified.
+    // Return true and set timeToRetry to the duration to wait for before
+    // retrying if some destructions have not been notified.
+    bool processNotifications(nsecs_t* timeToRetryNs);
+
+    // Main function for the input buffer manager thread.
+    void main();
+
+    // The thread that manages notifications.
+    //
+    // Note: This variable is declared last so its initialization will happen
+    // after all other member variables have been initialized.
+    std::thread mMainThread;
+
+    // Private constructor.
+    InputBufferManager();
+
+    // The only instance of this class.
+    static InputBufferManager& getInstance();
+
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // CODEC2_HIDL_V1_0_UTILS_INPUT_BUFFER_MANAGER_H
+
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h
new file mode 100644
index 0000000..29ed7ff
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurface.h
@@ -0,0 +1,91 @@
+/*
+ * 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 CODEC2_HIDL_V1_0_UTILS_INPUTSURFACE_H
+#define CODEC2_HIDL_V1_0_UTILS_INPUTSURFACE_H
+
+#include <codec2/hidl/1.0/ComponentStore.h>
+
+#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
+#include <android/hardware/media/c2/1.0/IInputSink.h>
+#include <android/hardware/media/c2/1.0/IInputSurface.h>
+#include <hidl/Status.h>
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+
+#include <util/C2InterfaceHelper.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct InputSurface : public IInputSurface {
+
+    typedef ::android::hardware::graphics::bufferqueue::V2_0::
+            IGraphicBufferProducer HGraphicBufferProducer;
+
+    typedef ::android::
+            GraphicBufferSource GraphicBufferSource;
+
+    virtual Return<sp<HGraphicBufferProducer>> getGraphicBufferProducer() override;
+
+    virtual Return<sp<IConfigurable>> getConfigurable() override;
+
+    virtual Return<void> connect(
+            const sp<IInputSink>& sink,
+            connect_cb _hidl_cb) override;
+
+protected:
+
+    class Interface;
+    class ConfigurableIntf;
+
+    sp<ComponentStore> mStore;
+    sp<HGraphicBufferProducer> mProducer;
+    sp<GraphicBufferSource> mSource;
+    std::shared_ptr<Interface> mIntf;
+    sp<CachedConfigurable> mConfigurable;
+
+    InputSurface(
+            const sp<ComponentStore>& store,
+            const std::shared_ptr<C2ReflectorHelper>& reflector,
+            const sp<HGraphicBufferProducer>& base,
+            const sp<GraphicBufferSource>& source);
+
+    virtual ~InputSurface() override = default;
+
+    friend struct ComponentStore;
+
+};
+
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // CODEC2_HIDL_V1_0_UTILS_INPUTSURFACE_H
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurfaceConnection.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurfaceConnection.h
new file mode 100644
index 0000000..758b6b2
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/InputSurfaceConnection.h
@@ -0,0 +1,96 @@
+/*
+ * 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 CODEC2_HIDL_V1_0_UTILS_INPUTSURFACECONNECTION_H
+#define CODEC2_HIDL_V1_0_UTILS_INPUTSURFACECONNECTION_H
+
+#include <codec2/hidl/1.0/Component.h>
+#include <codec2/hidl/1.0/Configurable.h>
+
+#include <android/hardware/media/c2/1.0/IComponent.h>
+#include <android/hardware/media/c2/1.0/IConfigurable.h>
+#include <android/hardware/media/c2/1.0/IInputSurfaceConnection.h>
+
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+#include <C2Component.h>
+
+#include <memory>
+#include <mutex>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+using ::android::GraphicBufferSource;
+
+// An InputSurfaceConnection connects an InputSurface to a sink, which may be an
+// IInputSink or a local C2Component. This can be specified by choosing the
+// corresponding constructor. The reason for distinguishing these two cases is
+// that when an InputSurfaceConnection lives in the same process as the
+// component that processes the buffers, data parceling is not needed.
+struct InputSurfaceConnection : public IInputSurfaceConnection {
+
+    virtual Return<Status> disconnect() override;
+
+    virtual Return<sp<IConfigurable>> getConfigurable() override;
+
+protected:
+
+    InputSurfaceConnection(
+            const sp<GraphicBufferSource>& source,
+            const std::shared_ptr<C2Component>& comp,
+            const sp<ComponentStore>& store);
+
+    InputSurfaceConnection(
+            const sp<GraphicBufferSource>& source,
+            const sp<IInputSink>& sink,
+            const sp<ComponentStore>& store);
+
+    bool init();
+
+    friend struct InputSurface;
+
+    InputSurfaceConnection() = delete;
+    InputSurfaceConnection(const InputSurfaceConnection&) = delete;
+    void operator=(const InputSurfaceConnection&) = delete;
+
+    struct Impl;
+
+    std::mutex mImplMutex;
+    sp<Impl> mImpl;
+    sp<CachedConfigurable> mConfigurable;
+
+    virtual ~InputSurfaceConnection() override;
+};
+
+}  // namespace utils
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // CODEC2_HIDL_V1_0_UTILS_INPUTSURFACECONNECTION_H
diff --git a/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
new file mode 100644
index 0000000..a9928b3
--- /dev/null
+++ b/media/codec2/hidl/1.0/utils/include/codec2/hidl/1.0/types.h
@@ -0,0 +1,350 @@
+/*
+ * 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 CODEC2_HIDL_V1_0_UTILS_TYPES_H
+#define CODEC2_HIDL_V1_0_UTILS_TYPES_H
+
+#include <bufferpool/ClientManager.h>
+#include <android/hardware/media/bufferpool/2.0/IClientManager.h>
+#include <android/hardware/media/bufferpool/2.0/types.h>
+#include <android/hardware/media/c2/1.0/IComponentStore.h>
+#include <android/hardware/media/c2/1.0/types.h>
+#include <android/hidl/safe_union/1.0/types.h>
+
+#include <C2Component.h>
+#include <C2Param.h>
+#include <C2ParamDef.h>
+#include <C2Work.h>
+#include <util/C2Debug-base.h>
+
+#include <chrono>
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace utils {
+
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::status_t;
+using ::android::sp;
+using ::android::hardware::media::bufferpool::V2_0::implementation::
+        ConnectionId;
+
+// Types of metadata for Blocks.
+struct C2Hidl_Range {
+    uint32_t offset;
+    uint32_t length; // Do not use "size" because the name collides with C2Info::size().
+};
+typedef C2GlobalParam<C2Info, C2Hidl_Range, 0> C2Hidl_RangeInfo;
+
+struct C2Hidl_Rect {
+    uint32_t left;
+    uint32_t top;
+    uint32_t width;
+    uint32_t height;
+};
+typedef C2GlobalParam<C2Info, C2Hidl_Rect, 1> C2Hidl_RectInfo;
+
+// Make asString() and operator<< work with Status as well as c2_status_t.
+C2_DECLARE_AS_STRING_AND_DEFINE_STREAM_OUT(Status);
+
+/**
+ * All objcpy() functions will return a boolean value indicating whether the
+ * conversion succeeds or not.
+ */
+
+// C2SettingResult -> SettingResult
+bool objcpy(
+        SettingResult* d,
+        const C2SettingResult& s);
+
+// SettingResult -> std::unique_ptr<C2SettingResult>
+bool objcpy(
+        std::unique_ptr<C2SettingResult>* d,
+        const SettingResult& s);
+
+// C2ParamDescriptor -> ParamDescriptor
+bool objcpy(
+        ParamDescriptor* d,
+        const C2ParamDescriptor& s);
+
+// ParamDescriptor -> std::shared_ptr<C2ParamDescriptor>
+bool objcpy(
+        std::shared_ptr<C2ParamDescriptor>* d,
+        const ParamDescriptor& s);
+
+// C2FieldSupportedValuesQuery -> FieldSupportedValuesQuery
+bool objcpy(
+        FieldSupportedValuesQuery* d,
+        const C2FieldSupportedValuesQuery& s);
+
+// FieldSupportedValuesQuery -> C2FieldSupportedValuesQuery
+bool objcpy(
+        C2FieldSupportedValuesQuery* d,
+        const FieldSupportedValuesQuery& s);
+
+// C2FieldSupportedValuesQuery -> FieldSupportedValuesQueryResult
+bool objcpy(
+        FieldSupportedValuesQueryResult* d,
+        const C2FieldSupportedValuesQuery& s);
+
+// FieldSupportedValuesQuery, FieldSupportedValuesQueryResult -> C2FieldSupportedValuesQuery
+bool objcpy(
+        C2FieldSupportedValuesQuery* d,
+        const FieldSupportedValuesQuery& sq,
+