Merge "Add and update VHAL Properties and permissions"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 0900eea..1af439a 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -69,3 +69,6 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/seccomp_policy/configstore@1.1.policy)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/bin/hw/android.hardware.configstore@1.1-service)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/init/android.hardware.configstore@1.1-service.rc)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/bin/hw/android.hardware.cas@1.0*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/init/android.hardware.cas@1.0*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/vintf/manifest/android.hardware.cas@1.0*)
diff --git a/OWNERS b/OWNERS
index 9fbcb47..1d7a8e1 100644
--- a/OWNERS
+++ b/OWNERS
@@ -3,3 +3,4 @@
 malchev@google.com
 smoreland@google.com
 yim@google.com  # vts tests
+guangzhu@google.com # vts tests
diff --git a/audio/5.0/Android.bp b/audio/5.0/Android.bp
index 3586b8e..7c2db1d 100644
--- a/audio/5.0/Android.bp
+++ b/audio/5.0/Android.bp
@@ -30,6 +30,7 @@
         "AudioMicrophoneDirectionality",
         "AudioMicrophoneLocation",
         "MessageQueueFlagBits",
+        "MicrophoneDirection",
         "MicrophoneInfo",
         "MmapBufferFlag",
         "MmapBufferInfo",
diff --git a/audio/common/5.0/types.hal b/audio/common/5.0/types.hal
index ffe4506..8f8a888 100644
--- a/audio/common/5.0/types.hal
+++ b/audio/common/5.0/types.hal
@@ -643,8 +643,10 @@
             int32_t device;
         } alsa;             // used for USB_*
     } address;
-    string busAddress;      // used for BUS
-    string rSubmixAddress;  // used for REMOTE_SUBMIX
+    /** Arbitrary BUS device unique address. Should not be interpreted by the framework. */
+    string busAddress;
+    /** Arbitrary REMOTE_SUBMIX device unique address. Should not be interpreted by the HAL. */
+    string rSubmixAddress;
 };
 
 /**
diff --git a/audio/common/all-versions/default/service/Android.mk b/audio/common/all-versions/default/service/Android.mk
index 58987c7..b57a1ae 100644
--- a/audio/common/all-versions/default/service/Android.mk
+++ b/audio/common/all-versions/default/service/Android.mk
@@ -49,6 +49,7 @@
     android.hardware.audio.effect@4.0 \
     android.hardware.audio.effect@5.0 \
     android.hardware.bluetooth.a2dp@1.0 \
+    android.hardware.bluetooth.audio@2.0 \
     android.hardware.soundtrigger@2.0 \
     android.hardware.soundtrigger@2.1 \
     android.hardware.soundtrigger@2.2
diff --git a/audio/common/all-versions/default/service/service.cpp b/audio/common/all-versions/default/service/service.cpp
index 15ce5e0..8a7b2ea 100644
--- a/audio/common/all-versions/default/service/service.cpp
+++ b/audio/common/all-versions/default/service/service.cpp
@@ -23,6 +23,7 @@
 #include <android/hardware/audio/effect/4.0/IEffectsFactory.h>
 #include <android/hardware/audio/effect/5.0/IEffectsFactory.h>
 #include <android/hardware/bluetooth/a2dp/1.0/IBluetoothAudioOffload.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvidersFactory.h>
 #include <android/hardware/soundtrigger/2.0/ISoundTriggerHw.h>
 #include <android/hardware/soundtrigger/2.1/ISoundTriggerHw.h>
 #include <android/hardware/soundtrigger/2.2/ISoundTriggerHw.h>
@@ -64,6 +65,11 @@
            registerPassthroughServiceImplementation<soundtrigger::V2_0::ISoundTriggerHw>() != OK,
     ALOGW_IF(fail, "Could not register soundtrigger API 2.0, 2.1 nor 2.2");
 
+    fail = registerPassthroughServiceImplementation<
+                   bluetooth::audio::V2_0::IBluetoothAudioProvidersFactory>() != OK;
+    ALOGW_IF(fail, "Could not register Bluetooth Audio API 2.0");
+
+    // remove the old HIDL when Bluetooth Audio Hal V2 has offloading supported
     fail =
         registerPassthroughServiceImplementation<bluetooth::a2dp::V1_0::IBluetoothAudioOffload>() !=
         OK;
diff --git a/audio/core/all-versions/vts/functional/5.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/5.0/AudioPrimaryHidlHalTest.cpp
new file mode 100644
index 0000000..bdb17cd
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/5.0/AudioPrimaryHidlHalTest.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+#include <math.h>
+
+// pull in all the <= 4.0 tests
+#include "4.0/AudioPrimaryHidlHalTest.cpp"
+
+TEST_P(InputStreamTest, SetMicrophoneDirection) {
+    doc::test("Make sure setMicrophoneDirection correctly handles valid & invalid arguments");
+
+    // MicrophoneDirection dir = MicrophoneDirection::FRONT;
+    for (MicrophoneDirection dir : android::hardware::hidl_enum_range<MicrophoneDirection>()) {
+        ASSERT_RESULT(okOrNotSupported, stream->setMicrophoneDirection(dir));
+    }
+
+    // Bogus values
+    for (auto dir : {42, -1, 4}) {
+        ASSERT_RESULT(invalidArgsOrNotSupported,
+                      stream->setMicrophoneDirection(MicrophoneDirection(dir)));
+    }
+}
+
+TEST_P(InputStreamTest, SetMicrophoneFieldDimension) {
+    doc::test("Make sure setMicrophoneFieldDimension correctly handles valid & invalid arguments");
+
+    // Valid zoom values -1.0 -> 1.0
+    float incr = 0.1f;
+    for (float val = -1.0f; val <= 1.0; val += incr) {
+        ASSERT_RESULT(okOrNotSupported, stream->setMicrophoneFieldDimension(val));
+    }
+
+    // Bogus values
+    for (float val = 1.0f + incr; val <= 10.0f; val += incr) {
+        ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(val));
+        ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(-val));
+    }
+    // Some extremes
+    ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(NAN));
+    ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(-NAN));
+    ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(INFINITY));
+    ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(-INFINITY));
+}
diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp
index 6498289..88fdb5a 100644
--- a/audio/core/all-versions/vts/functional/Android.bp
+++ b/audio/core/all-versions/vts/functional/Android.bp
@@ -20,9 +20,6 @@
     static_libs: [
         "android.hardware.audio.common.test.utility",
         "libaudiopolicycomponents",
-        "libicuuc",
-        "libicuuc_stubdata",
-        "libandroidicu",
         "libmedia_helper",
         "libxml2",
     ],
@@ -73,8 +70,7 @@
     name: "VtsHalAudioV5_0TargetTest",
     defaults: ["VtsHalAudioTargetTest_defaults"],
     srcs: [
-        // for now the tests are the same as V4
-        "4.0/AudioPrimaryHidlHalTest.cpp",
+        "5.0/AudioPrimaryHidlHalTest.cpp",
     ],
     static_libs: [
         "android.hardware.audio@5.0",
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index a22cc1c..7f8b6a0 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -142,14 +142,13 @@
     android::DeviceVector availableOutputDevices;
     android::DeviceVector availableInputDevices;
     sp<android::DeviceDescriptor> defaultOutputDevice;
-    android::VolumeCurvesCollection volumes;
 };
 
 class PolicyConfig : private PolicyConfigData, public AudioPolicyConfig {
    public:
     PolicyConfig()
         : AudioPolicyConfig(hwModules, availableOutputDevices, availableInputDevices,
-                            defaultOutputDevice, &volumes) {
+                            defaultOutputDevice) {
         for (const char* location : kConfigLocations) {
             std::string path = std::string(location) + '/' + kConfigFileName;
             if (access(path.c_str(), F_OK) == 0) {
diff --git a/audio/effect/5.0/xml/api/current.txt b/audio/effect/5.0/xml/api/current.txt
index 473bb10..ef467c9 100644
--- a/audio/effect/5.0/xml/api/current.txt
+++ b/audio/effect/5.0/xml/api/current.txt
@@ -1,27 +1,27 @@
 // Signature format: 2.0
 package audio.effects.V5_0 {
 
-  public class Audioeffectsconf {
-    ctor public Audioeffectsconf();
+  public class AudioEffectsConf {
+    ctor public AudioEffectsConf();
     method public audio.effects.V5_0.EffectsType getEffects();
     method public audio.effects.V5_0.LibrariesType getLibraries();
-    method public audio.effects.V5_0.Audioeffectsconf.Postprocess getPostprocess();
-    method public audio.effects.V5_0.Audioeffectsconf.Preprocess getPreprocess();
+    method public audio.effects.V5_0.AudioEffectsConf.Postprocess getPostprocess();
+    method public audio.effects.V5_0.AudioEffectsConf.Preprocess getPreprocess();
     method public audio.effects.V5_0.VersionType getVersion();
     method public void setEffects(audio.effects.V5_0.EffectsType);
     method public void setLibraries(audio.effects.V5_0.LibrariesType);
-    method public void setPostprocess(audio.effects.V5_0.Audioeffectsconf.Postprocess);
-    method public void setPreprocess(audio.effects.V5_0.Audioeffectsconf.Preprocess);
+    method public void setPostprocess(audio.effects.V5_0.AudioEffectsConf.Postprocess);
+    method public void setPreprocess(audio.effects.V5_0.AudioEffectsConf.Preprocess);
     method public void setVersion(audio.effects.V5_0.VersionType);
   }
 
-  public static class Audioeffectsconf.Postprocess {
-    ctor public Audioeffectsconf.Postprocess();
+  public static class AudioEffectsConf.Postprocess {
+    ctor public AudioEffectsConf.Postprocess();
     method public java.util.List<audio.effects.V5_0.StreamPostprocessType> getStream();
   }
 
-  public static class Audioeffectsconf.Preprocess {
-    ctor public Audioeffectsconf.Preprocess();
+  public static class AudioEffectsConf.Preprocess {
+    ctor public AudioEffectsConf.Preprocess();
     method public java.util.List<audio.effects.V5_0.StreamPreprocessType> getStream();
   }
 
@@ -123,7 +123,7 @@
 
   public class XmlParser {
     ctor public XmlParser();
-    method public static audio.effects.V5_0.Audioeffectsconf read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static audio.effects.V5_0.AudioEffectsConf read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
   }
diff --git a/audio/effect/all-versions/vts/functional/Android.bp b/audio/effect/all-versions/vts/functional/Android.bp
index de6cad9..cccb5c8 100644
--- a/audio/effect/all-versions/vts/functional/Android.bp
+++ b/audio/effect/all-versions/vts/functional/Android.bp
@@ -26,9 +26,6 @@
         "android.hidl.allocator@1.0",
         "android.hidl.memory@1.0",
         "libeffectsconfig",
-        "libicuuc",
-        "libicuuc_stubdata",
-        "libandroidicu",
         "libxml2",
     ],
     header_libs: [
diff --git a/automotive/evs/1.0/IEvsEnumerator.hal b/automotive/evs/1.0/IEvsEnumerator.hal
index e1193d0..ee51e7e 100644
--- a/automotive/evs/1.0/IEvsEnumerator.hal
+++ b/automotive/evs/1.0/IEvsEnumerator.hal
@@ -57,7 +57,8 @@
      *
      * There can be at most one EVS display object for the system and this function
      * requests access to it. If the EVS display is not available or is already in use,
-     * a null pointer is returned.
+     * the old instance shall be closed and give the new caller exclusive
+     * access.
      * When done using the display, the caller may release it by calling closeDisplay().
      * TODO(b/36122635) Reliance on the sp<> going out of scope is not recommended because the
      * resources may not be released right away due to asynchronos behavior in the hardware binder.
diff --git a/automotive/evs/OWNERS b/automotive/evs/OWNERS
new file mode 100644
index 0000000..fec2a3a
--- /dev/null
+++ b/automotive/evs/OWNERS
@@ -0,0 +1,3 @@
+changyeon@google.com
+haoxiangl@google.com
+swan@google.com
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 7614cad..08cdffa 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -198,6 +198,15 @@
 
     {.config =
          {
+             .prop = toInt(VehicleProperty::INFO_DRIVER_SEAT),
+             .access = VehiclePropertyAccess::READ,
+             .changeMode = VehiclePropertyChangeMode::STATIC,
+             .areaConfigs = {VehicleAreaConfig{.areaId = (0)}},
+         },
+     .initialValue = {.int32Values = {SEAT_1_LEFT}}},
+
+    {.config =
+         {
              .prop = toInt(VehicleProperty::INFO_FUEL_DOOR_LOCATION),
              .access = VehiclePropertyAccess::READ,
              .changeMode = VehiclePropertyChangeMode::STATIC,
@@ -321,6 +330,8 @@
              .access = VehiclePropertyAccess::READ,
              .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
              .areaConfigs = {VehicleAreaConfig{.areaId = (0)}},
+             .minSampleRate = 1.0f,
+             .maxSampleRate = 2.0f,
          },
      .initialValue = {.floatValues = {100.0f}}},  // units in meters
 
@@ -328,6 +339,8 @@
          {.prop = toInt(VehicleProperty::TIRE_PRESSURE),
           .access = VehiclePropertyAccess::READ,
           .changeMode = VehiclePropertyChangeMode::CONTINUOUS,
+          .minSampleRate = 1.0f,
+          .maxSampleRate = 2.0f,
           .areaConfigs =
               {VehicleAreaConfig{
                    .areaId = WHEEL_FRONT_LEFT, .minFloatValue = 100.0f, .maxFloatValue = 300.0f,
diff --git a/biometrics/face/1.0/Android.bp b/biometrics/face/1.0/Android.bp
index 45dbad9..0f8c6e6 100644
--- a/biometrics/face/1.0/Android.bp
+++ b/biometrics/face/1.0/Android.bp
@@ -17,6 +17,7 @@
     types: [
         "FaceAcquiredInfo",
         "FaceError",
+        "Feature",
         "OptionalBool",
         "OptionalUint64",
         "Status",
diff --git a/biometrics/face/1.0/IBiometricsFace.hal b/biometrics/face/1.0/IBiometricsFace.hal
index e3c256a..0499c5d 100644
--- a/biometrics/face/1.0/IBiometricsFace.hal
+++ b/biometrics/face/1.0/IBiometricsFace.hal
@@ -18,9 +18,6 @@
 
 import IBiometricsFaceClientCallback;
 
-// TODO(b/78538290): Update comments with state machine transitions when ready.
-// TODO(b/78537981): Update comments with callback interaction contract.
-// TODO(b/79496983): Update comments with status returns fully enumerated.
 /**
  * The HAL interface for biometric face authentication.
  */
@@ -245,8 +242,10 @@
      * Reset lockout for the current user.
      *
      * @param hat A valid Hardware Authentication Token, generated when the
-     *     user authenticates with Pin/Pattern/Pass.
-     * @return true if lockout was reset, false otherwise.
+     *     user authenticates with Pin/Pattern/Pass. When the Hardware
+     *     Authentication Token is verified, lockout must be reset and
+     *     onLockoutChanged must be called with duration 0.
+     * @return status The status of this method call.
      */
-    resetLockout(vec<uint8_t> hat) generates (bool success);
+    resetLockout(vec<uint8_t> hat) generates (Status status);
 };
diff --git a/biometrics/face/1.0/IBiometricsFaceClientCallback.hal b/biometrics/face/1.0/IBiometricsFaceClientCallback.hal
index 93848c5..c9dd0e2 100644
--- a/biometrics/face/1.0/IBiometricsFaceClientCallback.hal
+++ b/biometrics/face/1.0/IBiometricsFaceClientCallback.hal
@@ -113,4 +113,19 @@
      */
     oneway onEnumerate(uint64_t deviceId, vec<uint32_t> faceIds,
         int32_t userId);
+
+    /**
+     * A callback invoked when the lockout state changes.
+     *
+     * This method must only be invoked when setActiveUser() is called,
+     * when lockout starts, and when lockout ends. When lockout starts,
+     * duration must be greater than 0, and when lockout ends, duration must
+     * be 0. This must be called before calling onError() with parameters
+     * LOCKOUT or LOCKOUT_PERMANENT. If the user is permanently locked out,
+     * the duration must be MAX_UINT64.
+     *
+     * @param duration the remaining lockout duration in milliseconds, or 0
+     *     if the user is not locked out.
+     */
+    oneway onLockoutChanged(uint64_t duration);
 };
diff --git a/biometrics/face/1.0/types.hal b/biometrics/face/1.0/types.hal
index 89e809b..b5db966 100644
--- a/biometrics/face/1.0/types.hal
+++ b/biometrics/face/1.0/types.hal
@@ -297,7 +297,7 @@
      * capture plane is too high.
      *
      * The tilt angle is defined as the angle swept out by the user’s face looking up
-     * and down. The pan angle would be zero if the user faced the camera directly.
+     * and down. The tilt angle would be zero if the user faced the camera directly.
      *
      * The user should be informed to look more directly at the camera.
      */
@@ -308,8 +308,8 @@
      * capture plane is too high.
      *
      * The roll angle is defined as the angle swept out by the user’s face tilting their head
-     * towards their shoulders to the left and right. The pan angle would be zero if the user
-     * faced the camera directly.
+     * towards their shoulders to the left and right. The roll angle would be zero if the user's
+     * head is vertically aligned with the camera.
      *
      * The user should be informed to look more directly at the camera.
      */
diff --git a/bluetooth/audio/2.0/Android.bp b/bluetooth/audio/2.0/Android.bp
index 5d67f75..e72b6ab 100644
--- a/bluetooth/audio/2.0/Android.bp
+++ b/bluetooth/audio/2.0/Android.bp
@@ -1,3 +1,5 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
 hidl_interface {
     name: "android.hardware.bluetooth.audio@2.0",
     root: "android.hardware",
@@ -11,8 +13,9 @@
         "IBluetoothAudioProvidersFactory.hal",
     ],
     interfaces: [
-        "android.hidl.base@1.0",
         "android.hardware.audio.common@5.0",
+        "android.hidl.base@1.0",
+        "android.hidl.safe_union@1.0",
     ],
     types: [
         "AacObjectType",
@@ -34,6 +37,7 @@
         "SbcAllocMethod",
         "SbcBlockLength",
         "SbcChannelMode",
+        "SbcNumSubbands",
         "SbcParameters",
         "SessionType",
         "Status",
diff --git a/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.cpp
new file mode 100644
index 0000000..84fba34
--- /dev/null
+++ b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderA2dpOffload"
+
+#include <android-base/logging.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "A2dpOffloadAudioProvider.h"
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Void;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+A2dpOffloadAudioProvider::A2dpOffloadAudioProvider()
+    : BluetoothAudioProvider() {
+  session_type_ = SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
+}
+
+bool A2dpOffloadAudioProvider::isValid(const SessionType& sessionType) {
+  return (sessionType == session_type_);
+}
+
+Return<void> A2dpOffloadAudioProvider::startSession(
+    const sp<IBluetoothAudioPort>& hostIf,
+    const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  /**
+   * Initialize the audio platform if audioConfiguration is supported.
+   * Save the the IBluetoothAudioPort interface, so that it can be used
+   * later to send stream control commands to the HAL client, based on
+   * interaction with Audio framework.
+   */
+  if (audioConfig.getDiscriminator() !=
+      AudioConfiguration::hidl_discriminator::codecConfig) {
+    LOG(WARNING) << __func__
+                 << " - Invalid Audio Configuration=" << toString(audioConfig);
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  } else if (!android::bluetooth::audio::IsOffloadCodecConfigurationValid(
+                 session_type_, audioConfig.codecConfig())) {
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  }
+
+  return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> A2dpOffloadAudioProvider::onSessionReady(
+    startSession_cb _hidl_cb) {
+  BluetoothAudioSessionReport::OnSessionStarted(session_type_, stack_iface_,
+                                                nullptr, audio_config_);
+  _hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.h b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.h
new file mode 100644
index 0000000..9f40278
--- /dev/null
+++ b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+class A2dpOffloadAudioProvider : public BluetoothAudioProvider {
+ public:
+  A2dpOffloadAudioProvider();
+
+  bool isValid(const SessionType& sessionType) override;
+
+  Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+                            const AudioConfiguration& audioConfig,
+                            startSession_cb _hidl_cb) override;
+
+ private:
+  Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.cpp b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.cpp
new file mode 100644
index 0000000..f71a73e
--- /dev/null
+++ b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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 "BTAudioProviderA2dpSoftware"
+
+#include <android-base/logging.h>
+
+#include "A2dpSoftwareAudioProvider.h"
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::hardware::Void;
+
+static constexpr uint32_t kPcmFrameSize = 4;  // 16 bits per sample / stereo
+static constexpr uint32_t kPcmFrameCount = 128;
+static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
+static constexpr uint32_t kRtpFrameCount = 7;  // max counts by 1 tick (20ms)
+static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
+static constexpr uint32_t kBufferCount = 2;  // double buffer
+static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
+
+A2dpSoftwareAudioProvider::A2dpSoftwareAudioProvider()
+    : BluetoothAudioProvider(), mDataMQ(nullptr) {
+  LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
+            << " byte(s)";
+  std::unique_ptr<DataMQ> tempDataMQ(
+      new DataMQ(kDataMqSize, /* EventFlag */ true));
+  if (tempDataMQ && tempDataMQ->isValid()) {
+    mDataMQ = std::move(tempDataMQ);
+    session_type_ = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
+  } else {
+    ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
+    ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
+  }
+}
+
+bool A2dpSoftwareAudioProvider::isValid(const SessionType& sessionType) {
+  return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
+}
+
+Return<void> A2dpSoftwareAudioProvider::startSession(
+    const sp<IBluetoothAudioPort>& hostIf,
+    const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  /**
+   * Initialize the audio platform if audioConfiguration is supported.
+   * Save the the IBluetoothAudioPort interface, so that it can be used
+   * later to send stream control commands to the HAL client, based on
+   * interaction with Audio framework.
+   */
+  if (audioConfig.getDiscriminator() !=
+      AudioConfiguration::hidl_discriminator::pcmConfig) {
+    LOG(WARNING) << __func__
+                 << " - Invalid Audio Configuration=" << toString(audioConfig);
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid(
+                 audioConfig.pcmConfig())) {
+    LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
+                 << toString(audioConfig.pcmConfig());
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  }
+
+  return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> A2dpSoftwareAudioProvider::onSessionReady(
+    startSession_cb _hidl_cb) {
+  if (mDataMQ && mDataMQ->isValid()) {
+    BluetoothAudioSessionReport::OnSessionStarted(
+        session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
+    _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
+  } else {
+    _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+  }
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.h b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.h
new file mode 100644
index 0000000..228a928
--- /dev/null
+++ b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class A2dpSoftwareAudioProvider : public BluetoothAudioProvider {
+ public:
+  A2dpSoftwareAudioProvider();
+
+  bool isValid(const SessionType& sessionType) override;
+
+  Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+                            const AudioConfiguration& audioConfig,
+                            startSession_cb _hidl_cb) override;
+
+ private:
+  // audio data queue for software encoding
+  std::unique_ptr<DataMQ> mDataMQ;
+
+  Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/Android.bp b/bluetooth/audio/2.0/default/Android.bp
new file mode 100644
index 0000000..1dfc05d
--- /dev/null
+++ b/bluetooth/audio/2.0/default/Android.bp
@@ -0,0 +1,48 @@
+cc_library_shared {
+    name: "android.hardware.bluetooth.audio@2.0-impl",
+    defaults: ["hidl_defaults"],
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: [
+        "BluetoothAudioProvidersFactory.cpp",
+        "BluetoothAudioProvider.cpp",
+        "A2dpOffloadAudioProvider.cpp",
+        "A2dpSoftwareAudioProvider.cpp",
+        "HearingAidAudioProvider.cpp",
+    ],
+    header_libs: ["libhardware_headers"],
+    shared_libs: [
+        "android.hardware.audio.common@5.0",
+        "android.hardware.bluetooth.audio@2.0",
+        "libbase",
+        "libbluetooth_audio_session",
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+    ],
+}
+
+cc_library_shared {
+    name: "libbluetooth_audio_session",
+    defaults: ["hidl_defaults"],
+    vendor: true,
+    srcs: [
+        "session/BluetoothAudioSession.cpp",
+        "session/BluetoothAudioSupportedCodecsDB.cpp",
+    ],
+    export_include_dirs: ["session/"],
+    header_libs: ["libhardware_headers"],
+    shared_libs: [
+        "android.hardware.bluetooth.audio@2.0",
+        "libbase",
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+    ],
+}
diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.0/default/BluetoothAudioProvider.cpp
new file mode 100644
index 0000000..ab8973e
--- /dev/null
+++ b/bluetooth/audio/2.0/default/BluetoothAudioProvider.cpp
@@ -0,0 +1,143 @@
+/*
+ * 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 "BTAudioProviderStub"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioProvider.h"
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Void;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+void BluetoothAudioDeathRecipient::serviceDied(
+    uint64_t cookie __unused,
+    const wp<::android::hidl::base::V1_0::IBase>& who __unused) {
+  LOG(ERROR) << "BluetoothAudioDeathRecipient::" << __func__
+             << " - BluetoothAudio Service died";
+  provider_->endSession();
+}
+
+BluetoothAudioProvider::BluetoothAudioProvider()
+    : death_recipient_(new BluetoothAudioDeathRecipient(this)),
+      session_type_(SessionType::UNKNOWN),
+      audio_config_({}) {}
+
+Return<void> BluetoothAudioProvider::startSession(
+    const sp<IBluetoothAudioPort>& hostIf,
+    const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  if (hostIf == nullptr) {
+    _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+    return Void();
+  }
+
+  /**
+   * Initialize the audio platform if audioConfiguration is supported.
+   * Save the the IBluetoothAudioPort interface, so that it can be used
+   * later to send stream control commands to the HAL client, based on
+   * interaction with Audio framework.
+   */
+  audio_config_ = audioConfig;
+  stack_iface_ = hostIf;
+  stack_iface_->linkToDeath(death_recipient_, 0);
+
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+            << ", AudioConfiguration=[" << toString(audio_config_) << "]";
+
+  onSessionReady(_hidl_cb);
+  return Void();
+}
+
+Return<void> BluetoothAudioProvider::streamStarted(
+    BluetoothAudioStatus status) {
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+            << ", status=" << toString(status);
+
+  /**
+   * Streaming on control path has started,
+   * HAL server should start the streaming on data path.
+   */
+  if (stack_iface_) {
+    BluetoothAudioSessionReport::ReportControlStatus(session_type_, true,
+                                                     status);
+  } else {
+    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+                 << ", status=" << toString(status) << " has NO session";
+  }
+
+  return Void();
+}
+
+Return<void> BluetoothAudioProvider::streamSuspended(
+    BluetoothAudioStatus status) {
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+            << ", status=" << toString(status);
+
+  /**
+   * Streaming on control path has suspend,
+   * HAL server should suspend the streaming on data path.
+   */
+  if (stack_iface_) {
+    BluetoothAudioSessionReport::ReportControlStatus(session_type_, false,
+                                                     status);
+  } else {
+    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+                 << ", status=" << toString(status) << " has NO session";
+  }
+
+  return Void();
+}
+
+Return<void> BluetoothAudioProvider::endSession() {
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
+
+  if (stack_iface_) {
+    BluetoothAudioSessionReport::OnSessionEnded(session_type_);
+    stack_iface_->unlinkToDeath(death_recipient_);
+  } else {
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+              << " has NO session";
+  }
+
+  /**
+   * Clean up the audio platform as remote audio device is no
+   * longer active
+   */
+  stack_iface_ = nullptr;
+  audio_config_ = {};
+
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvider.h b/bluetooth/audio/2.0/default/BluetoothAudioProvider.h
new file mode 100644
index 0000000..02bf2b9
--- /dev/null
+++ b/bluetooth/audio/2.0/default/BluetoothAudioProvider.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvider.h>
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+using BluetoothAudioStatus =
+    ::android::hardware::bluetooth::audio::V2_0::Status;
+
+class BluetoothAudioDeathRecipient;
+
+class BluetoothAudioProvider : public IBluetoothAudioProvider {
+ public:
+  BluetoothAudioProvider();
+  ~BluetoothAudioProvider() = default;
+
+  virtual bool isValid(const SessionType& sessionType) = 0;
+
+  Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+                            const AudioConfiguration& audioConfig,
+                            startSession_cb _hidl_cb) override;
+  Return<void> streamStarted(BluetoothAudioStatus status) override;
+  Return<void> streamSuspended(BluetoothAudioStatus status) override;
+  Return<void> endSession() override;
+
+ protected:
+  sp<BluetoothAudioDeathRecipient> death_recipient_;
+
+  SessionType session_type_;
+  AudioConfiguration audio_config_;
+  sp<IBluetoothAudioPort> stack_iface_;
+
+  virtual Return<void> onSessionReady(startSession_cb _hidl_cb) = 0;
+};
+
+class BluetoothAudioDeathRecipient : public hidl_death_recipient {
+ public:
+  BluetoothAudioDeathRecipient(const sp<BluetoothAudioProvider> provider)
+      : provider_(provider) {}
+
+  virtual void serviceDied(
+      uint64_t cookie,
+      const wp<::android::hidl::base::V1_0::IBase>& who) override;
+
+ private:
+  sp<BluetoothAudioProvider> provider_;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp
new file mode 100644
index 0000000..df89cc8
--- /dev/null
+++ b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 "BTAudioProvidersFactory"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioProvidersFactory.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Void;
+
+A2dpSoftwareAudioProvider
+    BluetoothAudioProvidersFactory::a2dp_software_provider_instance_;
+A2dpOffloadAudioProvider
+    BluetoothAudioProvidersFactory::a2dp_offload_provider_instance_;
+HearingAidAudioProvider
+    BluetoothAudioProvidersFactory::hearing_aid_provider_instance_;
+
+Return<void> BluetoothAudioProvidersFactory::openProvider(
+    const SessionType sessionType, openProvider_cb _hidl_cb) {
+  LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType);
+  BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS;
+  BluetoothAudioProvider* provider = nullptr;
+  switch (sessionType) {
+    case SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH:
+      provider = &a2dp_software_provider_instance_;
+      break;
+    case SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH:
+      provider = &a2dp_offload_provider_instance_;
+      break;
+    case SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH:
+      provider = &hearing_aid_provider_instance_;
+      break;
+    default:
+      status = BluetoothAudioStatus::FAILURE;
+  }
+  if (provider == nullptr || !provider->isValid(sessionType)) {
+    provider = nullptr;
+    status = BluetoothAudioStatus::FAILURE;
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(sessionType)
+               << ", status=" << toString(status);
+  }
+  _hidl_cb(status, provider);
+  return Void();
+}
+
+Return<void> BluetoothAudioProvidersFactory::getProviderCapabilities(
+    const SessionType sessionType, getProviderCapabilities_cb _hidl_cb) {
+  hidl_vec<AudioCapabilities> audio_capabilities =
+      hidl_vec<AudioCapabilities>(0);
+  if (sessionType == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+    std::vector<CodecCapabilities> db_codec_capabilities =
+        android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType);
+    if (db_codec_capabilities.size()) {
+      audio_capabilities.resize(db_codec_capabilities.size());
+      for (int i = 0; i < db_codec_capabilities.size(); ++i) {
+        audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]);
+      }
+    }
+  } else if (sessionType != SessionType::UNKNOWN) {
+    std::vector<PcmParameters> db_pcm_capabilities =
+        android::bluetooth::audio::GetSoftwarePcmCapabilities();
+    if (db_pcm_capabilities.size() == 1) {
+      audio_capabilities.resize(1);
+      audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]);
+    }
+  }
+  LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType)
+            << " supports " << audio_capabilities.size() << " codecs";
+  _hidl_cb(audio_capabilities);
+  return Void();
+}
+
+IBluetoothAudioProvidersFactory* HIDL_FETCH_IBluetoothAudioProvidersFactory(
+    const char* /* name */) {
+  return new BluetoothAudioProvidersFactory();
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.h b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.h
new file mode 100644
index 0000000..0b51536
--- /dev/null
+++ b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvidersFactory.h>
+
+#include "A2dpOffloadAudioProvider.h"
+#include "A2dpSoftwareAudioProvider.h"
+#include "BluetoothAudioProvider.h"
+#include "HearingAidAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+class BluetoothAudioProvidersFactory : public IBluetoothAudioProvidersFactory {
+ public:
+  BluetoothAudioProvidersFactory() {}
+
+  Return<void> openProvider(const SessionType sessionType,
+                            openProvider_cb _hidl_cb) override;
+
+  Return<void> getProviderCapabilities(
+      const SessionType sessionType,
+      getProviderCapabilities_cb _hidl_cb) override;
+
+ private:
+  static A2dpSoftwareAudioProvider a2dp_software_provider_instance_;
+  static A2dpOffloadAudioProvider a2dp_offload_provider_instance_;
+  static HearingAidAudioProvider hearing_aid_provider_instance_;
+};
+
+extern "C" IBluetoothAudioProvidersFactory*
+HIDL_FETCH_IBluetoothAudioProvidersFactory(const char* name);
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/HearingAidAudioProvider.cpp b/bluetooth/audio/2.0/default/HearingAidAudioProvider.cpp
new file mode 100644
index 0000000..e91cf8a
--- /dev/null
+++ b/bluetooth/audio/2.0/default/HearingAidAudioProvider.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 "BTAudioProviderHearingAid"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+#include "HearingAidAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::hardware::Void;
+
+static constexpr uint32_t kPcmFrameSize = 4;  // 16 bits per sample / stereo
+static constexpr uint32_t kPcmFrameCount = 128;
+static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
+static constexpr uint32_t kRtpFrameCount = 7;  // max counts by 1 tick (20ms)
+static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
+static constexpr uint32_t kBufferCount = 1;  // single buffer
+static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
+
+HearingAidAudioProvider::HearingAidAudioProvider()
+    : BluetoothAudioProvider(), mDataMQ(nullptr) {
+  LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
+            << " byte(s)";
+  std::unique_ptr<DataMQ> tempDataMQ(
+      new DataMQ(kDataMqSize, /* EventFlag */ true));
+  if (tempDataMQ && tempDataMQ->isValid()) {
+    mDataMQ = std::move(tempDataMQ);
+    session_type_ = SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH;
+  } else {
+    ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
+    ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
+  }
+}
+
+bool HearingAidAudioProvider::isValid(const SessionType& sessionType) {
+  return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
+}
+
+Return<void> HearingAidAudioProvider::startSession(
+    const sp<IBluetoothAudioPort>& hostIf,
+    const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  /**
+   * Initialize the audio platform if audioConfiguration is supported.
+   * Save the the IBluetoothAudioPort interface, so that it can be used
+   * later to send stream control commands to the HAL client, based on
+   * interaction with Audio framework.
+   */
+  if (audioConfig.getDiscriminator() !=
+      AudioConfiguration::hidl_discriminator::pcmConfig) {
+    LOG(WARNING) << __func__
+                 << " - Invalid Audio Configuration=" << toString(audioConfig);
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid(
+                 audioConfig.pcmConfig())) {
+    LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
+                 << toString(audioConfig.pcmConfig());
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  }
+
+  return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> HearingAidAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
+  if (mDataMQ && mDataMQ->isValid()) {
+    BluetoothAudioSessionReport::OnSessionStarted(
+        session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
+    _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
+  } else {
+    _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+  }
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/HearingAidAudioProvider.h b/bluetooth/audio/2.0/default/HearingAidAudioProvider.h
new file mode 100644
index 0000000..117eb32
--- /dev/null
+++ b/bluetooth/audio/2.0/default/HearingAidAudioProvider.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class HearingAidAudioProvider : public BluetoothAudioProvider {
+ public:
+  HearingAidAudioProvider();
+
+  bool isValid(const SessionType& sessionType) override;
+
+  Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+                            const AudioConfiguration& audioConfig,
+                            startSession_cb _hidl_cb) override;
+
+ private:
+  // audio data queue for software encoding
+  std::unique_ptr<DataMQ> mDataMQ;
+
+  Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp
new file mode 100644
index 0000000..d60e732
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp
@@ -0,0 +1,427 @@
+/*
+ * 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 "BTAudioProviderSession"
+
+#include "BluetoothAudioSession.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::hardware::audio::common::V5_0::AudioContentType;
+using ::android::hardware::audio::common::V5_0::AudioUsage;
+using ::android::hardware::audio::common::V5_0::PlaybackTrackMetadata;
+using ::android::hardware::audio::common::V5_0::SourceMetadata;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::TimeSpec;
+
+const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {
+    .codecType = CodecType::UNKNOWN,
+    .encodedAudioBitrate = 0x00000000,
+    .peerMtu = 0xffff,
+    .isScmstEnabled = false,
+    .config = {}};
+AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration =
+    {};
+AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {};
+
+static constexpr int kFmqSendTimeoutMs = 1000;  // 1000 ms timeout for sending
+static constexpr int kWritePollMs = 1;          // polled non-blocking interval
+
+static inline timespec timespec_convert_from_hal(const TimeSpec& TS) {
+  return {.tv_sec = static_cast<long>(TS.tvSec),
+          .tv_nsec = static_cast<long>(TS.tvNSec)};
+}
+
+BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type)
+    : session_type_(session_type), stack_iface_(nullptr), mDataMQ(nullptr) {
+  invalidSoftwareAudioConfiguration.pcmConfig(kInvalidPcmParameters);
+  invalidOffloadAudioConfiguration.codecConfig(kInvalidCodecConfiguration);
+}
+
+// The report function is used to report that the Bluetooth stack has started
+// this session without any failure, and will invoke session_changed_cb_ to
+// notify those registered bluetooth_audio outputs
+void BluetoothAudioSession::OnSessionStarted(
+    const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
+    const AudioConfiguration& audio_config) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (stack_iface == nullptr) {
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+               << ", IBluetoothAudioPort Invalid";
+  } else if (!UpdateAudioConfig(audio_config)) {
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+               << ", AudioConfiguration=" << toString(audio_config)
+               << " Invalid";
+  } else if (!UpdateDataPath(dataMQ)) {
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+               << " DataMQ Invalid";
+    audio_config_ =
+        (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
+             ? kInvalidOffloadAudioConfiguration
+             : kInvalidSoftwareAudioConfiguration);
+  } else {
+    stack_iface_ = stack_iface;
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+              << ", AudioConfiguration=" << toString(audio_config);
+    ReportSessionStatus();
+  }
+}
+
+// The report function is used to report that the Bluetooth stack has ended the
+// session, and will invoke session_changed_cb_ to notify registered
+// bluetooth_audio outputs
+void BluetoothAudioSession::OnSessionEnded() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (IsSessionReady()) {
+    ReportSessionStatus();
+  }
+  audio_config_ = (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
+                       ? kInvalidOffloadAudioConfiguration
+                       : kInvalidSoftwareAudioConfiguration);
+  stack_iface_ = nullptr;
+  UpdateDataPath(nullptr);
+}
+
+// invoking the registered session_changed_cb_
+void BluetoothAudioSession::ReportSessionStatus() {
+  // This is locked already by OnSessionStarted / OnSessionEnded
+  if (observers_.empty()) {
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+              << " has NO port state observer";
+    return;
+  }
+  for (auto& observer : observers_) {
+    uint16_t cookie = observer.first;
+    std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+              << " notify to bluetooth_audio=0x"
+              << android::base::StringPrintf("%04x", cookie);
+    cb->session_changed_cb_(cookie);
+  }
+}
+
+// The report function is used to report that the Bluetooth stack has notified
+// the result of startStream or suspendStream, and will invoke
+// control_result_cb_ to notify registered bluetooth_audio outputs
+void BluetoothAudioSession::ReportControlStatus(
+    bool start_resp, const BluetoothAudioStatus& status) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (observers_.empty()) {
+    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+                 << " has NO port state observer";
+    return;
+  }
+  for (auto& observer : observers_) {
+    uint16_t cookie = observer.first;
+    std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
+    LOG(INFO) << __func__ << " - status=" << toString(status)
+              << " for SessionType=" << toString(session_type_)
+              << ", bluetooth_audio=0x"
+              << android::base::StringPrintf("%04x", cookie)
+              << (start_resp ? " started" : " suspended");
+    cb->control_result_cb_(cookie, start_resp, status);
+  }
+}
+
+// The function helps to check if this session is ready or not
+// @return: true if the Bluetooth stack has started the specified session
+bool BluetoothAudioSession::IsSessionReady() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  bool dataMQ_valid =
+      (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
+       (mDataMQ != nullptr && mDataMQ->isValid()));
+  return stack_iface_ != nullptr && dataMQ_valid;
+}
+
+bool BluetoothAudioSession::UpdateDataPath(const DataMQ::Descriptor* dataMQ) {
+  if (dataMQ == nullptr) {
+    // usecase of reset by nullptr
+    mDataMQ = nullptr;
+    return true;
+  }
+  std::unique_ptr<DataMQ> tempDataMQ;
+  tempDataMQ.reset(new DataMQ(*dataMQ));
+  if (!tempDataMQ || !tempDataMQ->isValid()) {
+    mDataMQ = nullptr;
+    return false;
+  }
+  mDataMQ = std::move(tempDataMQ);
+  return true;
+}
+
+bool BluetoothAudioSession::UpdateAudioConfig(
+    const AudioConfiguration& audio_config) {
+  bool is_software_session =
+      (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+       session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
+  bool is_offload_session =
+      (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  auto audio_config_discriminator = audio_config.getDiscriminator();
+  bool is_software_audio_config =
+      (is_software_session &&
+       audio_config_discriminator ==
+           AudioConfiguration::hidl_discriminator::pcmConfig);
+  bool is_offload_audio_config =
+      (is_offload_session &&
+       audio_config_discriminator ==
+           AudioConfiguration::hidl_discriminator::codecConfig);
+  if (!is_software_audio_config && !is_offload_audio_config) {
+    return false;
+  }
+  audio_config_ = audio_config;
+  return true;
+}
+
+// The control function helps the bluetooth_audio module to register
+// PortStatusCallbacks
+// @return: cookie - the assigned number to this bluetooth_audio output
+uint16_t BluetoothAudioSession::RegisterStatusCback(
+    const PortStatusCallbacks& cbacks) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  uint16_t cookie = ObserversCookieGetInitValue(session_type_);
+  uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_);
+
+  while (cookie < cookie_upper_bound) {
+    if (observers_.find(cookie) == observers_.end()) {
+      break;
+    }
+    ++cookie;
+  }
+  if (cookie >= cookie_upper_bound) {
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+               << " has " << observers_.size()
+               << " observers already (No Resource)";
+    return kObserversCookieUndefined;
+  }
+  std::shared_ptr<struct PortStatusCallbacks> cb =
+      std::make_shared<struct PortStatusCallbacks>();
+  *cb = cbacks;
+  observers_[cookie] = cb;
+  return cookie;
+}
+
+// The control function helps the bluetooth_audio module to unregister
+// PortStatusCallbacks
+// @param: cookie - indicates which bluetooth_audio output is
+void BluetoothAudioSession::UnregisterStatusCback(uint16_t cookie) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (observers_.erase(cookie) != 1) {
+    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+                 << " no such provider=0x"
+                 << android::base::StringPrintf("%04x", cookie);
+  }
+}
+
+// The control function is for the bluetooth_audio module to get the current
+// AudioConfiguration
+const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (IsSessionReady()) {
+    return audio_config_;
+  } else if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+    return kInvalidOffloadAudioConfiguration;
+  } else {
+    return kInvalidSoftwareAudioConfiguration;
+  }
+}
+
+// Those control functions are for the bluetooth_audio module to start, suspend,
+// stop stream, to check position, and to update metadata.
+bool BluetoothAudioSession::StartStream() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (!IsSessionReady()) {
+    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+               << " has NO session";
+    return false;
+  }
+  auto hal_retval = stack_iface_->startStream();
+  if (!hal_retval.isOk()) {
+    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+                 << toString(session_type_) << " failed";
+    return false;
+  }
+  return true;
+}
+
+bool BluetoothAudioSession::SuspendStream() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (!IsSessionReady()) {
+    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+               << " has NO session";
+    return false;
+  }
+  auto hal_retval = stack_iface_->suspendStream();
+  if (!hal_retval.isOk()) {
+    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+                 << toString(session_type_) << " failed";
+    return false;
+  }
+  return true;
+}
+
+void BluetoothAudioSession::StopStream() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (!IsSessionReady()) {
+    return;
+  }
+  auto hal_retval = stack_iface_->stopStream();
+  if (!hal_retval.isOk()) {
+    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+                 << toString(session_type_) << " failed";
+  }
+}
+
+bool BluetoothAudioSession::GetPresentationPosition(
+    uint64_t* remote_delay_report_ns, uint64_t* total_bytes_readed,
+    timespec* data_position) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (!IsSessionReady()) {
+    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+               << " has NO session";
+    return false;
+  }
+  bool retval = false;
+  auto hal_retval = stack_iface_->getPresentationPosition(
+      [&retval, &remote_delay_report_ns, &total_bytes_readed, &data_position](
+          BluetoothAudioStatus status,
+          const uint64_t& remoteDeviceAudioDelayNanos,
+          uint64_t transmittedOctets,
+          const TimeSpec& transmittedOctetsTimeStamp) {
+        if (status == BluetoothAudioStatus::SUCCESS) {
+          if (remote_delay_report_ns)
+            *remote_delay_report_ns = remoteDeviceAudioDelayNanos;
+          if (total_bytes_readed) *total_bytes_readed = transmittedOctets;
+          if (data_position)
+            *data_position =
+                timespec_convert_from_hal(transmittedOctetsTimeStamp);
+          retval = true;
+        }
+      });
+  if (!hal_retval.isOk()) {
+    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+                 << toString(session_type_) << " failed";
+    return false;
+  }
+  return retval;
+}
+
+void BluetoothAudioSession::UpdateTracksMetadata(
+    const struct source_metadata* source_metadata) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (!IsSessionReady()) {
+    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+               << " has NO session";
+    return;
+  }
+
+  ssize_t track_count = source_metadata->track_count;
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ", "
+            << track_count << " track(s)";
+  if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+      session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+    return;
+  }
+
+  struct playback_track_metadata* track = source_metadata->tracks;
+  SourceMetadata sourceMetadata;
+  PlaybackTrackMetadata* halMetadata;
+
+  sourceMetadata.tracks.resize(track_count);
+  halMetadata = sourceMetadata.tracks.data();
+  while (track_count && track) {
+    halMetadata->usage = static_cast<AudioUsage>(track->usage);
+    halMetadata->contentType =
+        static_cast<AudioContentType>(track->content_type);
+    halMetadata->gain = track->gain;
+    LOG(VERBOSE) << __func__ << " - SessionType=" << toString(session_type_)
+                 << ", usage=" << toString(halMetadata->usage)
+                 << ", content=" << toString(halMetadata->contentType)
+                 << ", gain=" << halMetadata->gain;
+    --track_count;
+    ++track;
+    ++halMetadata;
+  }
+  auto hal_retval = stack_iface_->updateMetadata(sourceMetadata);
+  if (!hal_retval.isOk()) {
+    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+                 << toString(session_type_) << " failed";
+  }
+}
+
+// The control function writes stream to FMQ
+size_t BluetoothAudioSession::OutWritePcmData(const void* buffer,
+                                              size_t bytes) {
+  if (buffer == nullptr || !bytes) return 0;
+  size_t totalWritten = 0;
+  int ms_timeout = kFmqSendTimeoutMs;
+  do {
+    std::unique_lock<std::recursive_mutex> lock(mutex_);
+    if (!IsSessionReady()) break;
+    size_t availableToWrite = mDataMQ->availableToWrite();
+    if (availableToWrite) {
+      if (availableToWrite > (bytes - totalWritten)) {
+        availableToWrite = bytes - totalWritten;
+      }
+
+      if (!mDataMQ->write(static_cast<const uint8_t*>(buffer) + totalWritten,
+                          availableToWrite)) {
+        ALOGE("FMQ datapath writting %zu/%zu failed", totalWritten, bytes);
+        return totalWritten;
+      }
+      totalWritten += availableToWrite;
+    } else if (ms_timeout >= kWritePollMs) {
+      lock.unlock();
+      usleep(kWritePollMs * 1000);
+      ms_timeout -= kWritePollMs;
+    } else {
+      ALOGD("data %zu/%zu overflow %d ms", totalWritten, bytes,
+            (kFmqSendTimeoutMs - ms_timeout));
+      return totalWritten;
+    }
+  } while (totalWritten < bytes);
+  return totalWritten;
+}
+
+std::unique_ptr<BluetoothAudioSessionInstance>
+    BluetoothAudioSessionInstance::instance_ptr =
+        std::unique_ptr<BluetoothAudioSessionInstance>(
+            new BluetoothAudioSessionInstance());
+
+// API to fetch the session of A2DP / Hearing Aid
+std::shared_ptr<BluetoothAudioSession>
+BluetoothAudioSessionInstance::GetSessionInstance(
+    const SessionType& session_type) {
+  std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
+  if (!instance_ptr->sessions_map_.empty()) {
+    auto entry = instance_ptr->sessions_map_.find(session_type);
+    if (entry != instance_ptr->sessions_map_.end()) {
+      return entry->second;
+    }
+  }
+  std::shared_ptr<BluetoothAudioSession> session_ptr =
+      std::make_shared<BluetoothAudioSession>(session_type);
+  instance_ptr->sessions_map_[session_type] = session_ptr;
+  return session_ptr;
+}
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h
new file mode 100644
index 0000000..85e8742
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <unordered_map>
+
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
+#include <fmq/MessageQueue.h>
+#include <hardware/audio.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::sp;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
+using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+using BluetoothAudioStatus =
+    ::android::hardware::bluetooth::audio::V2_0::Status;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+static constexpr uint16_t kObserversCookieSize = 0x0010;  // 0x0000 ~ 0x000f
+constexpr uint16_t kObserversCookieUndefined =
+    (static_cast<uint16_t>(SessionType::UNKNOWN) << 8 & 0xff00);
+inline SessionType ObserversCookieGetSessionType(uint16_t cookie) {
+  return static_cast<SessionType>(cookie >> 8 & 0x00ff);
+}
+inline uint16_t ObserversCookieGetInitValue(SessionType session_type) {
+  return (static_cast<uint16_t>(session_type) << 8 & 0xff00);
+}
+inline uint16_t ObserversCookieGetUpperBound(SessionType session_type) {
+  return (static_cast<uint16_t>(session_type) << 8 & 0xff00) +
+         kObserversCookieSize;
+}
+
+// This presents the callbacks of started / suspended and session changed,
+// and the bluetooth_audio module uses to receive the status notification
+struct PortStatusCallbacks {
+  // control_result_cb_ - when the Bluetooth stack reports results of
+  // streamStarted or streamSuspended, the BluetoothAudioProvider will invoke
+  // this callback to report to the bluetooth_audio module.
+  // @param: cookie - indicates which bluetooth_audio output should handle
+  // @param: start_resp - this report is for startStream or not
+  // @param: status - the result of startStream
+  std::function<void(uint16_t cookie, bool start_resp,
+                     const BluetoothAudioStatus& status)>
+      control_result_cb_;
+  // session_changed_cb_ - when the Bluetooth stack start / end session, the
+  // BluetoothAudioProvider will invoke this callback to notify to the
+  // bluetooth_audio module.
+  // @param: cookie - indicates which bluetooth_audio output should handle
+  std::function<void(uint16_t cookie)> session_changed_cb_;
+};
+
+class BluetoothAudioSession {
+ private:
+  // using recursive_mutex to allow hwbinder to re-enter agian.
+  std::recursive_mutex mutex_;
+  SessionType session_type_;
+
+  // audio control path to use for both software and offloading
+  sp<IBluetoothAudioPort> stack_iface_;
+  // audio data path (FMQ) for software encoding
+  std::unique_ptr<DataMQ> mDataMQ;
+  // audio data configuration for both software and offloading
+  AudioConfiguration audio_config_;
+
+  static AudioConfiguration invalidSoftwareAudioConfiguration;
+  static AudioConfiguration invalidOffloadAudioConfiguration;
+
+  // saving those registered bluetooth_audio's callbacks
+  std::unordered_map<uint16_t, std::shared_ptr<struct PortStatusCallbacks>>
+      observers_;
+
+  bool UpdateDataPath(const DataMQ::Descriptor* dataMQ);
+  bool UpdateAudioConfig(const AudioConfiguration& audio_config);
+  // invoking the registered session_changed_cb_
+  void ReportSessionStatus();
+
+ public:
+  BluetoothAudioSession(const SessionType& session_type);
+
+  // The function helps to check if this session is ready or not
+  // @return: true if the Bluetooth stack has started the specified session
+  bool IsSessionReady();
+
+  // The report function is used to report that the Bluetooth stack has started
+  // this session without any failure, and will invoke session_changed_cb_ to
+  // notify those registered bluetooth_audio outputs
+  void OnSessionStarted(const sp<IBluetoothAudioPort> stack_iface,
+                        const DataMQ::Descriptor* dataMQ,
+                        const AudioConfiguration& audio_config);
+
+  // The report function is used to report that the Bluetooth stack has ended
+  // the session, and will invoke session_changed_cb_ to notify registered
+  // bluetooth_audio outputs
+  void OnSessionEnded();
+
+  // The report function is used to report that the Bluetooth stack has notified
+  // the result of startStream or suspendStream, and will invoke
+  // control_result_cb_ to notify registered bluetooth_audio outputs
+  void ReportControlStatus(bool start_resp, const BluetoothAudioStatus& status);
+
+  // The control function helps the bluetooth_audio module to register
+  // PortStatusCallbacks
+  // @return: cookie - the assigned number to this bluetooth_audio output
+  uint16_t RegisterStatusCback(const PortStatusCallbacks& cbacks);
+
+  // The control function helps the bluetooth_audio module to unregister
+  // PortStatusCallbacks
+  // @param: cookie - indicates which bluetooth_audio output is
+  void UnregisterStatusCback(uint16_t cookie);
+
+  // The control function is for the bluetooth_audio module to get the current
+  // AudioConfiguration
+  const AudioConfiguration& GetAudioConfig();
+
+  // Those control functions are for the bluetooth_audio module to start,
+  // suspend, stop stream, to check position, and to update metadata.
+  bool StartStream();
+  bool SuspendStream();
+  void StopStream();
+  bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
+                               uint64_t* total_bytes_readed,
+                               timespec* data_position);
+  void UpdateTracksMetadata(const struct source_metadata* source_metadata);
+
+  // The control function writes stream to FMQ
+  size_t OutWritePcmData(const void* buffer, size_t bytes);
+
+  static constexpr PcmParameters kInvalidPcmParameters = {
+      .sampleRate = SampleRate::RATE_UNKNOWN,
+      .bitsPerSample = BitsPerSample::BITS_UNKNOWN,
+      .channelMode = ChannelMode::UNKNOWN};
+  // can't be constexpr because of non-literal type
+  static const CodecConfiguration kInvalidCodecConfiguration;
+
+  static constexpr AudioConfiguration& kInvalidSoftwareAudioConfiguration =
+      invalidSoftwareAudioConfiguration;
+  static constexpr AudioConfiguration& kInvalidOffloadAudioConfiguration =
+      invalidOffloadAudioConfiguration;
+};
+
+class BluetoothAudioSessionInstance {
+ public:
+  // The API is to fetch the specified session of A2DP / Hearing Aid
+  static std::shared_ptr<BluetoothAudioSession> GetSessionInstance(
+      const SessionType& session_type);
+
+ private:
+  static std::unique_ptr<BluetoothAudioSessionInstance> instance_ptr;
+  std::mutex mutex_;
+  std::unordered_map<SessionType, std::shared_ptr<BluetoothAudioSession>>
+      sessions_map_;
+};
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSessionControl.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionControl.h
new file mode 100644
index 0000000..6707765
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionControl.h
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "BluetoothAudioSession.h"
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothAudioSessionControl {
+ public:
+  // The control API helps to check if session is ready or not
+  // @return: true if the Bluetooth stack has started th specified session
+  static bool IsSessionReady(const SessionType& session_type) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->IsSessionReady();
+    }
+    return false;
+  }
+
+  // The control API helps the bluetooth_audio module to register
+  // PortStatusCallbacks
+  // @return: cookie - the assigned number to this bluetooth_audio output
+  static uint16_t RegisterControlResultCback(
+      const SessionType& session_type, const PortStatusCallbacks& cbacks) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->RegisterStatusCback(cbacks);
+    }
+    return kObserversCookieUndefined;
+  }
+
+  // The control API helps the bluetooth_audio module to unregister
+  // PortStatusCallbacks
+  // @param: cookie - indicates which bluetooth_audio output is
+  static void UnregisterControlResultCback(const SessionType& session_type,
+                                           uint16_t cookie) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->UnregisterStatusCback(cookie);
+    }
+  }
+
+  // The control API for the bluetooth_audio module to get current
+  // AudioConfiguration
+  static const AudioConfiguration& GetAudioConfig(
+      const SessionType& session_type) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->GetAudioConfig();
+    } else if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+      return BluetoothAudioSession::kInvalidOffloadAudioConfiguration;
+    } else {
+      return BluetoothAudioSession::kInvalidSoftwareAudioConfiguration;
+    }
+  }
+
+  // Those control APIs for the bluetooth_audio module to start / suspend / stop
+  // stream, to check position, and to update metadata.
+  static bool StartStream(const SessionType& session_type) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->StartStream();
+    }
+    return false;
+  }
+
+  static bool SuspendStream(const SessionType& session_type) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->SuspendStream();
+    }
+    return false;
+  }
+
+  static void StopStream(const SessionType& session_type) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->StopStream();
+    }
+  }
+
+  static bool GetPresentationPosition(const SessionType& session_type,
+                                      uint64_t* remote_delay_report_ns,
+                                      uint64_t* total_bytes_readed,
+                                      timespec* data_position) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->GetPresentationPosition(
+          remote_delay_report_ns, total_bytes_readed, data_position);
+    }
+    return false;
+  }
+
+  static void UpdateTracksMetadata(
+      const SessionType& session_type,
+      const struct source_metadata* source_metadata) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->UpdateTracksMetadata(source_metadata);
+    }
+  }
+
+  // The control API writes stream to FMQ
+  static size_t OutWritePcmData(const SessionType& session_type,
+                                const void* buffer, size_t bytes) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->OutWritePcmData(buffer, bytes);
+    }
+    return 0;
+  }
+};
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSessionReport.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionReport.h
new file mode 100644
index 0000000..5a83ae2
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionReport.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "BluetoothAudioSession.h"
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothAudioSessionReport {
+ public:
+  // The API reports the Bluetooth stack has started the session, and will
+  // inform registered bluetooth_audio outputs
+  static void OnSessionStarted(const SessionType& session_type,
+                               const sp<IBluetoothAudioPort> host_iface,
+                               const DataMQ::Descriptor* dataMQ,
+                               const AudioConfiguration& audio_config) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->OnSessionStarted(host_iface, dataMQ, audio_config);
+    }
+  }
+  // The API reports the Bluetooth stack has ended the session, and will
+  // inform registered bluetooth_audio outputs
+  static void OnSessionEnded(const SessionType& session_type) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->OnSessionEnded();
+    }
+  }
+  // The API reports the Bluetooth stack has replied the result of startStream
+  // or suspendStream, and will inform registered bluetooth_audio outputs
+  static void ReportControlStatus(const SessionType& session_type,
+                                  const bool& start_resp,
+                                  const BluetoothAudioStatus& status) {
+    std::shared_ptr<BluetoothAudioSession> session_ptr =
+        BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->ReportControlStatus(start_resp, status);
+    }
+  }
+};
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.cpp b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.cpp
new file mode 100644
index 0000000..292e28b
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.cpp
@@ -0,0 +1,413 @@
+/*
+ * 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 "BTAudioProviderSessionCodecsDB"
+
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
+using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
+using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
+using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
+using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
+using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
+using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
+
+// Default Supported PCM Parameters
+static const PcmParameters kDefaultSoftwarePcmCapabilities = {
+    .sampleRate = static_cast<SampleRate>(
+        SampleRate::RATE_44100 | SampleRate::RATE_48000 |
+        SampleRate::RATE_88200 | SampleRate::RATE_96000 |
+        SampleRate::RATE_16000 | SampleRate::RATE_24000),
+    .channelMode =
+        static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
+    .bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
+                                                BitsPerSample::BITS_24 |
+                                                BitsPerSample::BITS_32)};
+
+// Default Supported Codecs
+// SBC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(MONO|STEREO)
+//      all blocks | subbands 8 | Loudness
+static const SbcParameters kDefaultOffloadSbcCapability = {
+    .sampleRate = SampleRate::RATE_44100,
+    .channelMode = static_cast<SbcChannelMode>(SbcChannelMode::MONO |
+                                               SbcChannelMode::JOINT_STEREO),
+    .blockLength = static_cast<SbcBlockLength>(
+        SbcBlockLength::BLOCKS_4 | SbcBlockLength::BLOCKS_8 |
+        SbcBlockLength::BLOCKS_12 | SbcBlockLength::BLOCKS_16),
+    .numSubbands = SbcNumSubbands::SUBBAND_8,
+    .allocMethod = SbcAllocMethod::ALLOC_MD_L,
+    .bitsPerSample = BitsPerSample::BITS_16,
+    .minBitpool = 2,
+    .maxBitpool = 53};
+
+// AAC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(STEREO)
+static const AacParameters kDefaultOffloadAacCapability = {
+    .objectType = AacObjectType::MPEG2_LC,
+    .sampleRate = SampleRate::RATE_44100,
+    .channelMode = ChannelMode::STEREO,
+    .variableBitRateEnabled = AacVariableBitRate::DISABLED,
+    .bitsPerSample = BitsPerSample::BITS_16};
+
+// LDAC: mSampleRate:(44100|48000|88200|96000), mBitsPerSample:(16|24|32),
+//       mChannelMode:(DUAL|STEREO)
+static const LdacParameters kDefaultOffloadLdacCapability = {
+    .sampleRate = static_cast<SampleRate>(
+        SampleRate::RATE_44100 | SampleRate::RATE_48000 |
+        SampleRate::RATE_88200 | SampleRate::RATE_96000),
+    .channelMode = static_cast<LdacChannelMode>(LdacChannelMode::DUAL |
+                                                LdacChannelMode::STEREO),
+    .qualityIndex = LdacQualityIndex::QUALITY_HIGH,
+    .bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
+                                                BitsPerSample::BITS_24 |
+                                                BitsPerSample::BITS_32)};
+
+// aptX: mSampleRate:(44100|48000), mBitsPerSample:(16), mChannelMode:(STEREO)
+static const AptxParameters kDefaultOffloadAptxCapability = {
+    .sampleRate = static_cast<SampleRate>(SampleRate::RATE_44100 |
+                                          SampleRate::RATE_48000),
+    .bitsPerSample = BitsPerSample::BITS_16,
+    .channelMode = ChannelMode::STEREO};
+
+// aptX HD: mSampleRate:(44100|48000), mBitsPerSample:(24),
+//          mChannelMode:(STEREO)
+static const AptxParameters kDefaultOffloadAptxHdCapability = {
+    .sampleRate = static_cast<SampleRate>(SampleRate::RATE_44100 |
+                                          SampleRate::RATE_48000),
+    .bitsPerSample = BitsPerSample::BITS_24,
+    .channelMode = ChannelMode::STEREO};
+
+const std::vector<CodecCapabilities> kDefaultOffloadA2dpCodecCapabilities = {
+    {.codecType = CodecType::SBC, .capabilities = {}},
+    {.codecType = CodecType::AAC, .capabilities = {}},
+    {.codecType = CodecType::LDAC, .capabilities = {}},
+    {.codecType = CodecType::APTX, .capabilities = {}},
+    {.codecType = CodecType::APTX_HD, .capabilities = {}}};
+
+static bool IsSingleBit(uint32_t bitmasks, uint32_t bitfield) {
+  bool single = false;
+  uint32_t test_bit = 0x00000001;
+  while (test_bit <= bitmasks && test_bit <= bitfield) {
+    if (bitfield & test_bit && bitmasks & test_bit) {
+      if (single) return false;
+      single = true;
+    }
+    if (test_bit == 0x80000000) break;
+    test_bit <<= 1;
+  }
+  return single;
+}
+
+static bool IsOffloadSbcConfigurationValid(
+    const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadAacConfigurationValid(
+    const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadLdacConfigurationValid(
+    const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadAptxConfigurationValid(
+    const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadAptxHdConfigurationValid(
+    const CodecConfiguration::CodecSpecific& codec_specific);
+
+static bool IsOffloadSbcConfigurationValid(
+    const CodecConfiguration::CodecSpecific& codec_specific) {
+  if (codec_specific.getDiscriminator() !=
+      CodecConfiguration::CodecSpecific::hidl_discriminator::sbcConfig) {
+    LOG(WARNING) << __func__
+                 << ": Invalid CodecSpecific=" << toString(codec_specific);
+    return false;
+  }
+  const SbcParameters sbc_data = codec_specific.sbcConfig();
+  if (!IsSingleBit(static_cast<uint32_t>(sbc_data.sampleRate), 0xff) ||
+      !IsSingleBit(static_cast<uint32_t>(sbc_data.channelMode), 0x0f) ||
+      !IsSingleBit(static_cast<uint32_t>(sbc_data.blockLength), 0xf0) ||
+      !IsSingleBit(static_cast<uint32_t>(sbc_data.numSubbands), 0x0c) ||
+      !IsSingleBit(static_cast<uint32_t>(sbc_data.allocMethod), 0x03) ||
+      !IsSingleBit(static_cast<uint32_t>(sbc_data.bitsPerSample), 0x07) ||
+      sbc_data.minBitpool > sbc_data.maxBitpool) {
+    LOG(WARNING) << __func__
+                 << ": Invalid CodecSpecific=" << toString(codec_specific);
+    return false;
+  } else if ((sbc_data.sampleRate & kDefaultOffloadSbcCapability.sampleRate) &&
+             (sbc_data.channelMode &
+              kDefaultOffloadSbcCapability.channelMode) &&
+             (sbc_data.blockLength &
+              kDefaultOffloadSbcCapability.blockLength) &&
+             (sbc_data.numSubbands &
+              kDefaultOffloadSbcCapability.numSubbands) &&
+             (sbc_data.allocMethod &
+              kDefaultOffloadSbcCapability.allocMethod) &&
+             (sbc_data.bitsPerSample &
+              kDefaultOffloadSbcCapability.bitsPerSample) &&
+             (kDefaultOffloadSbcCapability.minBitpool <= sbc_data.minBitpool &&
+              sbc_data.maxBitpool <= kDefaultOffloadSbcCapability.maxBitpool)) {
+    return true;
+  }
+  LOG(WARNING) << __func__
+               << ": Unsupported CodecSpecific=" << toString(codec_specific);
+  return false;
+}
+
+static bool IsOffloadAacConfigurationValid(
+    const CodecConfiguration::CodecSpecific& codec_specific) {
+  if (codec_specific.getDiscriminator() !=
+      CodecConfiguration::CodecSpecific::hidl_discriminator::aacConfig) {
+    LOG(WARNING) << __func__
+                 << ": Invalid CodecSpecific=" << toString(codec_specific);
+    return false;
+  }
+  const AacParameters aac_data = codec_specific.aacConfig();
+  if (!IsSingleBit(static_cast<uint32_t>(aac_data.objectType), 0xf0) ||
+      !IsSingleBit(static_cast<uint32_t>(aac_data.sampleRate), 0xff) ||
+      !IsSingleBit(static_cast<uint32_t>(aac_data.channelMode), 0x03) ||
+      !IsSingleBit(static_cast<uint32_t>(aac_data.bitsPerSample), 0x07)) {
+    LOG(WARNING) << __func__
+                 << ": Invalid CodecSpecific=" << toString(codec_specific);
+    return false;
+  } else if ((aac_data.objectType & kDefaultOffloadAacCapability.objectType) &&
+             (aac_data.sampleRate & kDefaultOffloadAacCapability.sampleRate) &&
+             (aac_data.channelMode &
+              kDefaultOffloadAacCapability.channelMode) &&
+             (aac_data.variableBitRateEnabled == AacVariableBitRate::DISABLED ||
+              kDefaultOffloadAacCapability.variableBitRateEnabled ==
+                  AacVariableBitRate::ENABLED) &&
+             (aac_data.bitsPerSample &
+              kDefaultOffloadAacCapability.bitsPerSample)) {
+    return true;
+  }
+  LOG(WARNING) << __func__
+               << ": Unsupported CodecSpecific=" << toString(codec_specific);
+  return false;
+}
+
+static bool IsOffloadLdacConfigurationValid(
+    const CodecConfiguration::CodecSpecific& codec_specific) {
+  if (codec_specific.getDiscriminator() !=
+      CodecConfiguration::CodecSpecific::hidl_discriminator::ldacConfig) {
+    LOG(WARNING) << __func__
+                 << ": Invalid CodecSpecific=" << toString(codec_specific);
+    return false;
+  }
+  const LdacParameters ldac_data = codec_specific.ldacConfig();
+  if (!IsSingleBit(static_cast<uint32_t>(ldac_data.sampleRate), 0xff) ||
+      !IsSingleBit(static_cast<uint32_t>(ldac_data.channelMode), 0x07) ||
+      (ldac_data.qualityIndex > LdacQualityIndex::QUALITY_LOW &&
+       ldac_data.qualityIndex != LdacQualityIndex::QUALITY_ABR) ||
+      !IsSingleBit(static_cast<uint32_t>(ldac_data.bitsPerSample), 0x07)) {
+    LOG(WARNING) << __func__
+                 << ": Invalid CodecSpecific=" << toString(codec_specific);
+    return false;
+  } else if ((ldac_data.sampleRate &
+              kDefaultOffloadLdacCapability.sampleRate) &&
+             (ldac_data.channelMode &
+              kDefaultOffloadLdacCapability.channelMode) &&
+             (ldac_data.bitsPerSample &
+              kDefaultOffloadLdacCapability.bitsPerSample)) {
+    return true;
+  }
+  LOG(WARNING) << __func__
+               << ": Unsupported CodecSpecific=" << toString(codec_specific);
+  return false;
+}
+
+static bool IsOffloadAptxConfigurationValid(
+    const CodecConfiguration::CodecSpecific& codec_specific) {
+  if (codec_specific.getDiscriminator() !=
+      CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
+    LOG(WARNING) << __func__
+                 << ": Invalid CodecSpecific=" << toString(codec_specific);
+    return false;
+  }
+  const AptxParameters aptx_data = codec_specific.aptxConfig();
+  if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
+      !IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
+      !IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
+    LOG(WARNING) << __func__
+                 << ": Invalid CodecSpecific=" << toString(codec_specific);
+    return false;
+  } else if ((aptx_data.sampleRate &
+              kDefaultOffloadAptxCapability.sampleRate) &&
+             (aptx_data.channelMode &
+              kDefaultOffloadAptxCapability.channelMode) &&
+             (aptx_data.bitsPerSample &
+              kDefaultOffloadAptxCapability.bitsPerSample)) {
+    return true;
+  }
+  LOG(WARNING) << __func__
+               << ": Unsupported CodecSpecific=" << toString(codec_specific);
+  return false;
+}
+
+static bool IsOffloadAptxHdConfigurationValid(
+    const CodecConfiguration::CodecSpecific& codec_specific) {
+  if (codec_specific.getDiscriminator() !=
+      CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
+    LOG(WARNING) << __func__
+                 << ": Invalid CodecSpecific=" << toString(codec_specific);
+    return false;
+  }
+  const AptxParameters aptx_data = codec_specific.aptxConfig();
+  if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
+      !IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
+      !IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
+    LOG(WARNING) << __func__
+                 << ": Invalid CodecSpecific=" << toString(codec_specific);
+    return false;
+  } else if ((aptx_data.sampleRate &
+              kDefaultOffloadAptxHdCapability.sampleRate) &&
+             (aptx_data.channelMode &
+              kDefaultOffloadAptxHdCapability.channelMode) &&
+             (aptx_data.bitsPerSample &
+              kDefaultOffloadAptxHdCapability.bitsPerSample)) {
+    return true;
+  }
+  LOG(WARNING) << __func__
+               << ": Unsupported CodecSpecific=" << toString(codec_specific);
+  return false;
+}
+
+std::vector<PcmParameters> GetSoftwarePcmCapabilities() {
+  return std::vector<PcmParameters>(1, kDefaultSoftwarePcmCapabilities);
+}
+
+std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
+    const SessionType& session_type) {
+  if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+    return std::vector<CodecCapabilities>(0);
+  }
+  std::vector<CodecCapabilities> offload_a2dp_codec_capabilities =
+      kDefaultOffloadA2dpCodecCapabilities;
+  for (auto& codec_capability : offload_a2dp_codec_capabilities) {
+    switch (codec_capability.codecType) {
+      case CodecType::SBC:
+        codec_capability.capabilities.sbcCapabilities(
+            kDefaultOffloadSbcCapability);
+        break;
+      case CodecType::AAC:
+        codec_capability.capabilities.aacCapabilities(
+            kDefaultOffloadAacCapability);
+        break;
+      case CodecType::LDAC:
+        codec_capability.capabilities.ldacCapabilities(
+            kDefaultOffloadLdacCapability);
+        break;
+      case CodecType::APTX:
+        codec_capability.capabilities.aptxCapabilities(
+            kDefaultOffloadAptxCapability);
+        break;
+      case CodecType::APTX_HD:
+        codec_capability.capabilities.aptxCapabilities(
+            kDefaultOffloadAptxHdCapability);
+        break;
+      case CodecType::UNKNOWN:
+        codec_capability = {};
+        break;
+    }
+  }
+  return offload_a2dp_codec_capabilities;
+}
+
+bool IsSoftwarePcmConfigurationValid(const PcmParameters& pcm_config) {
+  if ((pcm_config.sampleRate != SampleRate::RATE_44100 &&
+       pcm_config.sampleRate != SampleRate::RATE_48000 &&
+       pcm_config.sampleRate != SampleRate::RATE_88200 &&
+       pcm_config.sampleRate != SampleRate::RATE_96000 &&
+       pcm_config.sampleRate != SampleRate::RATE_16000 &&
+       pcm_config.sampleRate != SampleRate::RATE_24000) ||
+      (pcm_config.bitsPerSample != BitsPerSample::BITS_16 &&
+       pcm_config.bitsPerSample != BitsPerSample::BITS_24 &&
+       pcm_config.bitsPerSample != BitsPerSample::BITS_32) ||
+      (pcm_config.channelMode != ChannelMode::MONO &&
+       pcm_config.channelMode != ChannelMode::STEREO)) {
+    LOG(WARNING) << __func__
+                 << ": Invalid PCM Configuration=" << toString(pcm_config);
+    return false;
+  } else if (pcm_config.sampleRate &
+                 kDefaultSoftwarePcmCapabilities.sampleRate &&
+             pcm_config.bitsPerSample &
+                 kDefaultSoftwarePcmCapabilities.bitsPerSample &&
+             pcm_config.channelMode &
+                 kDefaultSoftwarePcmCapabilities.channelMode) {
+    return true;
+  }
+  LOG(WARNING) << __func__
+               << ": Unsupported PCM Configuration=" << toString(pcm_config);
+  return false;
+}
+
+bool IsOffloadCodecConfigurationValid(const SessionType& session_type,
+                                      const CodecConfiguration& codec_config) {
+  if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+    LOG(ERROR) << __func__
+               << ": Invalid SessionType=" << toString(session_type);
+    return false;
+  } else if (codec_config.encodedAudioBitrate < 0x00000001 ||
+             0x00ffffff < codec_config.encodedAudioBitrate) {
+    LOG(ERROR) << __func__ << ": Unsupported Codec Configuration="
+               << toString(codec_config);
+    return false;
+  }
+  const CodecConfiguration::CodecSpecific& codec_specific = codec_config.config;
+  switch (codec_config.codecType) {
+    case CodecType::SBC:
+      if (IsOffloadSbcConfigurationValid(codec_specific)) {
+        return true;
+      }
+      return false;
+    case CodecType::AAC:
+      if (IsOffloadAacConfigurationValid(codec_specific)) {
+        return true;
+      }
+      return false;
+    case CodecType::LDAC:
+      if (IsOffloadLdacConfigurationValid(codec_specific)) {
+        return true;
+      }
+      return false;
+    case CodecType::APTX:
+      if (IsOffloadAptxConfigurationValid(codec_specific)) {
+        return true;
+      }
+      return false;
+    case CodecType::APTX_HD:
+      if (IsOffloadAptxHdConfigurationValid(codec_specific)) {
+        return true;
+      }
+      return false;
+    case CodecType::UNKNOWN:
+      return false;
+  }
+  return false;
+}
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.h
new file mode 100644
index 0000000..e71dc8a
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.0/types.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+std::vector<PcmParameters> GetSoftwarePcmCapabilities();
+std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
+    const SessionType& session_type);
+
+bool IsSoftwarePcmConfigurationValid(const PcmParameters& pcm_config);
+bool IsOffloadCodecConfigurationValid(const SessionType& session_type,
+                                      const CodecConfiguration& codec_config);
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/2.0/vts/OWNERS b/bluetooth/audio/2.0/vts/OWNERS
new file mode 100644
index 0000000..b6c0813
--- /dev/null
+++ b/bluetooth/audio/2.0/vts/OWNERS
@@ -0,0 +1,3 @@
+include platform/system/bt:/OWNERS
+
+cheneyni@google.com
diff --git a/bluetooth/audio/2.0/vts/functional/Android.bp b/bluetooth/audio/2.0/vts/functional/Android.bp
new file mode 100644
index 0000000..b672fe4
--- /dev/null
+++ b/bluetooth/audio/2.0/vts/functional/Android.bp
@@ -0,0 +1,12 @@
+cc_test {
+    name: "VtsHalBluetoothAudioV2_0TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalBluetoothAudioV2_0TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.audio.common@5.0",
+        "android.hardware.bluetooth.audio@2.0",
+    ],
+    shared_libs: [
+        "libfmq",
+    ],
+}
diff --git a/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp b/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp
new file mode 100644
index 0000000..9572d3f
--- /dev/null
+++ b/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp
@@ -0,0 +1,915 @@
+/*
+ * 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 "bluetooth_audio_hidl_hal_test"
+
+#include <android-base/logging.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvider.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvidersFactory.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <utils/Log.h>
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::audio::common::V5_0::SourceMetadata;
+using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
+using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
+using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AudioCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioProvider;
+using ::android::hardware::bluetooth::audio::V2_0::
+    IBluetoothAudioProvidersFactory;
+using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
+using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
+using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
+using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
+using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using BluetoothAudioStatus =
+    ::android::hardware::bluetooth::audio::V2_0::Status;
+using CodecSpecificConfig = ::android::hardware::bluetooth::audio::V2_0::
+    CodecConfiguration::CodecSpecific;
+
+namespace {
+constexpr SampleRate a2dp_sample_rates[5] = {
+    SampleRate::RATE_UNKNOWN, SampleRate::RATE_44100, SampleRate::RATE_48000,
+    SampleRate::RATE_88200, SampleRate::RATE_96000};
+constexpr BitsPerSample a2dp_bits_per_samples[4] = {
+    BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16, BitsPerSample::BITS_24,
+    BitsPerSample::BITS_32};
+constexpr ChannelMode a2dp_channel_modes[3] = {
+    ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+constexpr CodecType a2dp_codec_types[6] = {CodecType::UNKNOWN, CodecType::SBC,
+                                           CodecType::AAC,     CodecType::APTX,
+                                           CodecType::APTX_HD, CodecType::LDAC};
+
+template <typename T>
+std::vector<T> ExtractValuesFromBitmask(T bitmasks, uint32_t bitfield,
+                                        bool supported) {
+  std::vector<T> retval;
+  if (!supported) {
+    retval.push_back(static_cast<T>(bitfield));
+  }
+  uint32_t test_bit = 0x00000001;
+  while (test_bit <= static_cast<uint32_t>(bitmasks) && test_bit <= bitfield) {
+    if ((bitfield & test_bit)) {
+      if ((!(bitmasks & test_bit) && !supported) ||
+          ((bitmasks & test_bit) && supported)) {
+        retval.push_back(static_cast<T>(test_bit));
+      }
+    }
+    if (test_bit == 0x80000000) {
+      break;
+    }
+    test_bit <<= 1;
+  }
+  return retval;
+}
+}  // namespace
+
+// Test environment for Bluetooth Audio HAL.
+class BluetoothAudioHidlEnvironment
+    : public ::testing::VtsHalHidlTargetTestEnvBase {
+ public:
+  // get the test environment singleton
+  static BluetoothAudioHidlEnvironment* Instance() {
+    static BluetoothAudioHidlEnvironment* instance =
+        new BluetoothAudioHidlEnvironment;
+    return instance;
+  }
+
+  virtual void registerTestServices() override {
+    registerTestService<IBluetoothAudioProvidersFactory>();
+  }
+
+ private:
+  BluetoothAudioHidlEnvironment() {}
+};
+
+// The base test class for Bluetooth Audio HAL.
+class BluetoothAudioProvidersFactoryHidlTest
+    : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+  virtual void SetUp() override {
+    providers_factory_ = ::testing::VtsHalHidlTargetTestBase::getService<
+        IBluetoothAudioProvidersFactory>(
+        BluetoothAudioHidlEnvironment::Instance()
+            ->getServiceName<IBluetoothAudioProvidersFactory>());
+    ASSERT_NE(providers_factory_, nullptr);
+  }
+
+  virtual void TearDown() override { providers_factory_ = nullptr; }
+
+  // A simple test implementation of IBluetoothAudioPort.
+  class BluetoothAudioPort : public ::testing::VtsHalHidlTargetCallbackBase<
+                                 BluetoothAudioProvidersFactoryHidlTest>,
+                             public IBluetoothAudioPort {
+    BluetoothAudioProvidersFactoryHidlTest& parent_;
+
+   public:
+    BluetoothAudioPort(BluetoothAudioProvidersFactoryHidlTest& parent)
+        : parent_(parent) {}
+    virtual ~BluetoothAudioPort() = default;
+
+    Return<void> startStream() override {
+      parent_.audio_provider_->streamStarted(BluetoothAudioStatus::SUCCESS);
+      return Void();
+    }
+
+    Return<void> suspendStream() override {
+      parent_.audio_provider_->streamSuspended(BluetoothAudioStatus::SUCCESS);
+      return Void();
+    }
+
+    Return<void> stopStream() override { return Void(); }
+
+    Return<void> getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
+      _hidl_cb(BluetoothAudioStatus::SUCCESS, 0, 0, {.tvSec = 0, .tvNSec = 0});
+      return Void();
+    }
+
+    Return<void> updateMetadata(const SourceMetadata& sourceMetadata __unused) {
+      return Void();
+    }
+  };
+
+  void GetProviderCapabilitiesHelper(const SessionType& session_type) {
+    temp_provider_capabilities_.clear();
+    auto hidl_cb = [& temp_capabilities = this->temp_provider_capabilities_](
+                       const hidl_vec<AudioCapabilities>& audioCapabilities) {
+      for (auto audioCapability : audioCapabilities)
+        temp_capabilities.push_back(audioCapability);
+    };
+    auto hidl_retval =
+        providers_factory_->getProviderCapabilities(session_type, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+    if (session_type == SessionType::UNKNOWN) {
+      ASSERT_TRUE(temp_provider_capabilities_.empty());
+    } else if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+      // All software paths are mandatory and must have exact 1 "PcmParameters"
+      ASSERT_EQ(temp_provider_capabilities_.size(), 1);
+      ASSERT_EQ(temp_provider_capabilities_[0].getDiscriminator(),
+                AudioCapabilities::hidl_discriminator::pcmCapabilities);
+    } else {
+      uint32_t codec_type_bitmask = 0x00000000;
+      // empty capability means offload is unsupported
+      for (auto audio_capability : temp_provider_capabilities_) {
+        ASSERT_EQ(audio_capability.getDiscriminator(),
+                  AudioCapabilities::hidl_discriminator::codecCapabilities);
+        const CodecCapabilities& codec_capabilities =
+            audio_capability.codecCapabilities();
+        // Every codec can present once at most
+        ASSERT_EQ(codec_type_bitmask &
+                      static_cast<uint32_t>(codec_capabilities.codecType),
+                  0);
+        switch (codec_capabilities.codecType) {
+          case CodecType::SBC:
+            ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+                      CodecCapabilities::Capabilities::hidl_discriminator::
+                          sbcCapabilities);
+            break;
+          case CodecType::AAC:
+            ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+                      CodecCapabilities::Capabilities::hidl_discriminator::
+                          aacCapabilities);
+            break;
+          case CodecType::APTX:
+            FALLTHROUGH_INTENDED;
+          case CodecType::APTX_HD:
+            ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+                      CodecCapabilities::Capabilities::hidl_discriminator::
+                          aptxCapabilities);
+            break;
+          case CodecType::LDAC:
+            ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+                      CodecCapabilities::Capabilities::hidl_discriminator::
+                          ldacCapabilities);
+            break;
+          case CodecType::UNKNOWN:
+            break;
+        }
+        codec_type_bitmask |= codec_capabilities.codecType;
+      }
+    }
+  }
+
+  // This helps to open the specified provider and check the openProvider()
+  // has corruct return values. BUT, to keep it simple, it does not consider
+  // the capability, and please do so at the SetUp of each session's test.
+  void OpenProviderHelper(const SessionType& session_type) {
+    BluetoothAudioStatus cb_status;
+    auto hidl_cb = [&cb_status, &local_provider = this->audio_provider_](
+                       BluetoothAudioStatus status,
+                       const sp<IBluetoothAudioProvider>& provider) {
+      cb_status = status;
+      local_provider = provider;
+    };
+    auto hidl_retval = providers_factory_->openProvider(session_type, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+    if (cb_status == BluetoothAudioStatus::SUCCESS) {
+      ASSERT_NE(session_type, SessionType::UNKNOWN);
+      ASSERT_NE(audio_provider_, nullptr);
+      audio_port_ = new BluetoothAudioPort(*this);
+    } else {
+      // A2DP_HARDWARE_OFFLOAD_DATAPATH is optional
+      ASSERT_TRUE(session_type == SessionType::UNKNOWN ||
+                  session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+      ASSERT_EQ(cb_status, BluetoothAudioStatus::FAILURE);
+      ASSERT_EQ(audio_provider_, nullptr);
+    }
+  }
+
+  bool IsPcmParametersSupported(const PcmParameters& pcm_parameters) {
+    if (temp_provider_capabilities_.size() != 1 ||
+        temp_provider_capabilities_[0].getDiscriminator() !=
+            AudioCapabilities::hidl_discriminator::pcmCapabilities) {
+      return false;
+    }
+    auto pcm_capability = temp_provider_capabilities_[0].pcmCapabilities();
+    bool is_parameter_valid =
+        (pcm_parameters.sampleRate != SampleRate::RATE_UNKNOWN &&
+         pcm_parameters.channelMode != ChannelMode::UNKNOWN &&
+         pcm_parameters.bitsPerSample != BitsPerSample::BITS_UNKNOWN);
+    bool is_parameter_in_capability =
+        (pcm_capability.sampleRate & pcm_parameters.sampleRate &&
+         pcm_capability.channelMode & pcm_parameters.channelMode &&
+         pcm_capability.bitsPerSample & pcm_parameters.bitsPerSample);
+    return is_parameter_valid && is_parameter_in_capability;
+  }
+
+  sp<IBluetoothAudioProvidersFactory> providers_factory_;
+
+  // temp storage saves the specified provider capability by
+  // GetProviderCapabilitiesHelper()
+  std::vector<AudioCapabilities> temp_provider_capabilities_;
+
+  // audio_provider_ is for the Bluetooth stack to report session started/ended
+  // and handled audio stream started / suspended
+  sp<IBluetoothAudioProvider> audio_provider_;
+
+  // audio_port_ is for the Audio HAL to send stream start/suspend/stop commands
+  // to Bluetooth stack
+  sp<IBluetoothAudioPort> audio_port_;
+
+  static constexpr SessionType session_types_[4] = {
+      SessionType::UNKNOWN, SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH,
+      SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH,
+      SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH};
+};
+
+/**
+ * Test whether we can get the FactoryService from HIDL
+ */
+TEST_F(BluetoothAudioProvidersFactoryHidlTest, GetProvidersFactoryService) {}
+
+/**
+ * Test whether we can open a provider for each provider returned by
+ * getProviderCapabilities() with non-empty capabalities
+ */
+TEST_F(BluetoothAudioProvidersFactoryHidlTest,
+       OpenProviderAndCheckCapabilitiesBySession) {
+  for (auto session_type : session_types_) {
+    GetProviderCapabilitiesHelper(session_type);
+    OpenProviderHelper(session_type);
+    // We must be able to open a provider if its getProviderCapabilities()
+    // returns non-empty list.
+    EXPECT_TRUE(temp_provider_capabilities_.empty() ||
+                audio_provider_ != nullptr);
+  }
+}
+
+/**
+ * openProvider A2DP_SOFTWARE_ENCODING_DATAPATH
+ */
+class BluetoothAudioProviderA2dpSoftwareHidlTest
+    : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+  virtual void SetUp() override {
+    BluetoothAudioProvidersFactoryHidlTest::SetUp();
+    GetProviderCapabilitiesHelper(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
+    OpenProviderHelper(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
+    ASSERT_NE(audio_provider_, nullptr);
+  }
+
+  virtual void TearDown() override {
+    audio_port_ = nullptr;
+    audio_provider_ = nullptr;
+    BluetoothAudioProvidersFactoryHidlTest::TearDown();
+  }
+};
+
+/**
+ * Test whether we can open a provider of type
+ */
+TEST_F(BluetoothAudioProviderA2dpSoftwareHidlTest, OpenA2dpSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH can be started and stopped with
+ * different PCM config
+ */
+TEST_F(BluetoothAudioProviderA2dpSoftwareHidlTest,
+       StartAndEndA2dpSoftwareSessionWithPossiblePcmConfig) {
+  bool is_codec_config_valid;
+  std::unique_ptr<DataMQ> tempDataMQ;
+  auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+                     BluetoothAudioStatus status,
+                     const DataMQ::Descriptor& dataMQ) {
+    if (is_codec_config_valid) {
+      ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+      ASSERT_TRUE(dataMQ.isHandleValid());
+      tempDataMQ.reset(new DataMQ(dataMQ));
+    } else {
+      EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+      EXPECT_FALSE(dataMQ.isHandleValid());
+    }
+  };
+  AudioConfiguration audio_config = {};
+  PcmParameters pcm_parameters = {};
+  for (auto sample_rate : a2dp_sample_rates) {
+    pcm_parameters.sampleRate = sample_rate;
+    for (auto bits_per_sample : a2dp_bits_per_samples) {
+      pcm_parameters.bitsPerSample = bits_per_sample;
+      for (auto channel_mode : a2dp_channel_modes) {
+        pcm_parameters.channelMode = channel_mode;
+        is_codec_config_valid = IsPcmParametersSupported(pcm_parameters);
+        audio_config.pcmConfig(pcm_parameters);
+        auto hidl_retval =
+            audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+        // HIDL calls should not be failed and callback has to be executed
+        ASSERT_TRUE(hidl_retval.isOk());
+        if (is_codec_config_valid) {
+          EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+        }
+        EXPECT_TRUE(audio_provider_->endSession().isOk());
+      }  // ChannelMode
+    }    // BitsPerSampple
+  }      // SampleRate
+}
+
+/**
+ * openProvider A2DP_HARDWARE_OFFLOAD_DATAPATH
+ */
+class BluetoothAudioProviderA2dpHardwareHidlTest
+    : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+  virtual void SetUp() override {
+    BluetoothAudioProvidersFactoryHidlTest::SetUp();
+    GetProviderCapabilitiesHelper(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+    OpenProviderHelper(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+    ASSERT_TRUE(temp_provider_capabilities_.empty() ||
+                audio_provider_ != nullptr);
+  }
+
+  virtual void TearDown() override {
+    audio_port_ = nullptr;
+    audio_provider_ = nullptr;
+    BluetoothAudioProvidersFactoryHidlTest::TearDown();
+  }
+
+  bool IsOffloadSupported() { return (temp_provider_capabilities_.size() > 0); }
+
+  void GetOffloadCodecCapabilityHelper(const CodecType& codec_type) {
+    temp_codec_capabilities_ = {};
+    for (auto codec_capability : temp_provider_capabilities_) {
+      if (codec_capability.codecCapabilities().codecType != codec_type) {
+        continue;
+      }
+      temp_codec_capabilities_ = codec_capability.codecCapabilities();
+    }
+  }
+
+  std::vector<CodecSpecificConfig> GetSbcCodecSpecificSupportedList(
+      bool supported) {
+    std::vector<CodecSpecificConfig> sbc_codec_specifics;
+    GetOffloadCodecCapabilityHelper(CodecType::SBC);
+    if (temp_codec_capabilities_.codecType != CodecType::SBC) {
+      return sbc_codec_specifics;
+    }
+    // parse the capability
+    SbcParameters sbc_capability =
+        temp_codec_capabilities_.capabilities.sbcCapabilities();
+    if (sbc_capability.minBitpool > sbc_capability.maxBitpool) {
+      return sbc_codec_specifics;
+    }
+    std::vector<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+        sbc_capability.sampleRate, 0xff, supported);
+    std::vector<SbcChannelMode> channel_modes =
+        ExtractValuesFromBitmask<SbcChannelMode>(sbc_capability.channelMode,
+                                                 0x0f, supported);
+    std::vector<SbcBlockLength> block_lengths =
+        ExtractValuesFromBitmask<SbcBlockLength>(sbc_capability.blockLength,
+                                                 0xf0, supported);
+    std::vector<SbcNumSubbands> num_subbandss =
+        ExtractValuesFromBitmask<SbcNumSubbands>(sbc_capability.numSubbands,
+                                                 0x0c, supported);
+    std::vector<SbcAllocMethod> alloc_methods =
+        ExtractValuesFromBitmask<SbcAllocMethod>(sbc_capability.allocMethod,
+                                                 0x03, supported);
+    std::vector<BitsPerSample> bits_per_samples =
+        ExtractValuesFromBitmask<BitsPerSample>(sbc_capability.bitsPerSample,
+                                                0x07, supported);
+    // combine those parameters into one list of
+    // CodecConfiguration::CodecSpecific
+    CodecSpecificConfig codec_specific = {};
+    SbcParameters sbc_data;
+    for (auto sample_rate : sample_rates) {
+      for (auto channel_mode : channel_modes) {
+        for (auto block_length : block_lengths) {
+          for (auto num_subbands : num_subbandss) {
+            for (auto alloc_method : alloc_methods) {
+              for (auto bits_per_sample : bits_per_samples) {
+                sbc_data = {.sampleRate = sample_rate,
+                            .channelMode = channel_mode,
+                            .blockLength = block_length,
+                            .numSubbands = num_subbands,
+                            .allocMethod = alloc_method,
+                            .bitsPerSample = bits_per_sample,
+                            .minBitpool = sbc_capability.minBitpool,
+                            .maxBitpool = sbc_capability.maxBitpool};
+                codec_specific.sbcConfig(sbc_data);
+                sbc_codec_specifics.push_back(codec_specific);
+              }
+            }
+          }
+        }
+      }
+    }
+    return sbc_codec_specifics;
+  }
+
+  std::vector<CodecSpecificConfig> GetAacCodecSpecificSupportedList(
+      bool supported) {
+    std::vector<CodecSpecificConfig> aac_codec_specifics;
+    GetOffloadCodecCapabilityHelper(CodecType::AAC);
+    if (temp_codec_capabilities_.codecType != CodecType::AAC) {
+      return aac_codec_specifics;
+    }
+    // parse the capability
+    AacParameters aac_capability =
+        temp_codec_capabilities_.capabilities.aacCapabilities();
+    std::vector<AacObjectType> object_types =
+        ExtractValuesFromBitmask<AacObjectType>(aac_capability.objectType, 0xf0,
+                                                supported);
+    std::vector<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+        aac_capability.sampleRate, 0xff, supported);
+    std::vector<ChannelMode> channel_modes =
+        ExtractValuesFromBitmask<ChannelMode>(aac_capability.channelMode, 0x03,
+                                              supported);
+    std::vector<AacVariableBitRate> variable_bit_rate_enableds = {
+        AacVariableBitRate::DISABLED};
+    if (aac_capability.variableBitRateEnabled == AacVariableBitRate::ENABLED) {
+      variable_bit_rate_enableds.push_back(AacVariableBitRate::ENABLED);
+    }
+    std::vector<BitsPerSample> bits_per_samples =
+        ExtractValuesFromBitmask<BitsPerSample>(aac_capability.bitsPerSample,
+                                                0x07, supported);
+    // combine those parameters into one list of
+    // CodecConfiguration::CodecSpecific
+    CodecSpecificConfig codec_specific = {};
+    AacParameters aac_data;
+    for (auto object_type : object_types) {
+      for (auto sample_rate : sample_rates) {
+        for (auto channel_mode : channel_modes) {
+          for (auto variable_bit_rate_enabled : variable_bit_rate_enableds) {
+            for (auto bits_per_sample : bits_per_samples) {
+              aac_data = {.objectType = object_type,
+                          .sampleRate = sample_rate,
+                          .channelMode = channel_mode,
+                          .variableBitRateEnabled = variable_bit_rate_enabled,
+                          .bitsPerSample = bits_per_sample};
+              codec_specific.aacConfig(aac_data);
+              aac_codec_specifics.push_back(codec_specific);
+            }
+          }
+        }
+      }
+    }
+    return aac_codec_specifics;
+  }
+
+  std::vector<CodecSpecificConfig> GetLdacCodecSpecificSupportedList(
+      bool supported) {
+    std::vector<CodecSpecificConfig> ldac_codec_specifics;
+    GetOffloadCodecCapabilityHelper(CodecType::LDAC);
+    if (temp_codec_capabilities_.codecType != CodecType::LDAC) {
+      return ldac_codec_specifics;
+    }
+    // parse the capability
+    LdacParameters ldac_capability =
+        temp_codec_capabilities_.capabilities.ldacCapabilities();
+    std::vector<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+        ldac_capability.sampleRate, 0xff, supported);
+    std::vector<LdacChannelMode> channel_modes =
+        ExtractValuesFromBitmask<LdacChannelMode>(ldac_capability.channelMode,
+                                                  0x07, supported);
+    std::vector<LdacQualityIndex> quality_indexes = {
+        LdacQualityIndex::QUALITY_HIGH, LdacQualityIndex::QUALITY_MID,
+        LdacQualityIndex::QUALITY_LOW, LdacQualityIndex::QUALITY_ABR};
+    std::vector<BitsPerSample> bits_per_samples =
+        ExtractValuesFromBitmask<BitsPerSample>(ldac_capability.bitsPerSample,
+                                                0x07, supported);
+    // combine those parameters into one list of
+    // CodecConfiguration::CodecSpecific
+    CodecSpecificConfig codec_specific = {};
+    LdacParameters ldac_data;
+    for (auto sample_rate : sample_rates) {
+      for (auto channel_mode : channel_modes) {
+        for (auto quality_index : quality_indexes) {
+          for (auto bits_per_sample : bits_per_samples) {
+            ldac_data = {.sampleRate = sample_rate,
+                         .channelMode = channel_mode,
+                         .qualityIndex = quality_index,
+                         .bitsPerSample = bits_per_sample};
+            codec_specific.ldacConfig(ldac_data);
+            ldac_codec_specifics.push_back(codec_specific);
+          }
+        }
+      }
+    }
+    return ldac_codec_specifics;
+  }
+
+  std::vector<CodecSpecificConfig> GetAptxCodecSpecificSupportedList(
+      bool is_hd, bool supported) {
+    std::vector<CodecSpecificConfig> aptx_codec_specifics;
+    GetOffloadCodecCapabilityHelper(
+        (is_hd ? CodecType::APTX_HD : CodecType::APTX));
+    if ((is_hd && temp_codec_capabilities_.codecType != CodecType::APTX_HD) ||
+        (!is_hd && temp_codec_capabilities_.codecType != CodecType::APTX)) {
+      return aptx_codec_specifics;
+    }
+    // parse the capability
+    AptxParameters aptx_capability =
+        temp_codec_capabilities_.capabilities.aptxCapabilities();
+    std::vector<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+        aptx_capability.sampleRate, 0xff, supported);
+    std::vector<ChannelMode> channel_modes =
+        ExtractValuesFromBitmask<ChannelMode>(aptx_capability.channelMode, 0x03,
+                                              supported);
+    std::vector<BitsPerSample> bits_per_samples =
+        ExtractValuesFromBitmask<BitsPerSample>(aptx_capability.bitsPerSample,
+                                                0x07, supported);
+    // combine those parameters into one list of
+    // CodecConfiguration::CodecSpecific
+    CodecSpecificConfig codec_specific = {};
+    AptxParameters aptx_data;
+    for (auto sample_rate : sample_rates) {
+      for (auto channel_mode : channel_modes) {
+        for (auto bits_per_sample : bits_per_samples) {
+          aptx_data = {.sampleRate = sample_rate,
+                       .channelMode = channel_mode,
+                       .bitsPerSample = bits_per_sample};
+          codec_specific.aptxConfig(aptx_data);
+          aptx_codec_specifics.push_back(codec_specific);
+        }
+      }
+    }
+    return aptx_codec_specifics;
+  }
+
+  // temp storage saves the specified codec capability by
+  // GetOffloadCodecCapabilityHelper()
+  CodecCapabilities temp_codec_capabilities_;
+};
+
+/**
+ * Test whether we can open a provider of type
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest, OpenA2dpHardwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * SBC hardware encoding config
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest,
+       StartAndEndA2dpSbcHardwareSession) {
+  if (!IsOffloadSupported()) {
+    return;
+  }
+
+  CodecConfiguration codec_config = {};
+  codec_config.codecType = CodecType::SBC;
+  codec_config.encodedAudioBitrate = 328000;
+  codec_config.peerMtu = 1005;
+  codec_config.isScmstEnabled = false;
+  AudioConfiguration audio_config = {};
+  std::vector<CodecSpecificConfig> sbc_codec_specifics =
+      GetSbcCodecSpecificSupportedList(true);
+  auto hidl_cb = [](BluetoothAudioStatus status,
+                    const DataMQ::Descriptor& dataMQ) {
+    EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+    EXPECT_FALSE(dataMQ.isHandleValid());
+  };
+  for (auto codec_specific : sbc_codec_specifics) {
+    codec_config.config = codec_specific;
+    audio_config.codecConfig(codec_config);
+    auto hidl_retval =
+        audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+    EXPECT_TRUE(audio_provider_->endSession().isOk());
+  }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * AAC hardware encoding config
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest,
+       StartAndEndA2dpAacHardwareSession) {
+  if (!IsOffloadSupported()) {
+    return;
+  }
+
+  CodecConfiguration codec_config = {};
+  codec_config.codecType = CodecType::AAC;
+  codec_config.encodedAudioBitrate = 320000;
+  codec_config.peerMtu = 1005;
+  codec_config.isScmstEnabled = false;
+  AudioConfiguration audio_config = {};
+  std::vector<CodecSpecificConfig> aac_codec_specifics =
+      GetAacCodecSpecificSupportedList(true);
+  auto hidl_cb = [](BluetoothAudioStatus status,
+                    const DataMQ::Descriptor& dataMQ) {
+    EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+    EXPECT_FALSE(dataMQ.isHandleValid());
+  };
+  for (auto codec_specific : aac_codec_specifics) {
+    codec_config.config = codec_specific;
+    audio_config.codecConfig(codec_config);
+    auto hidl_retval =
+        audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+    EXPECT_TRUE(audio_provider_->endSession().isOk());
+  }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * LDAC hardware encoding config
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest,
+       StartAndEndA2dpLdacHardwareSession) {
+  if (!IsOffloadSupported()) {
+    return;
+  }
+
+  CodecConfiguration codec_config = {};
+  codec_config.codecType = CodecType::LDAC;
+  codec_config.encodedAudioBitrate = 990000;
+  codec_config.peerMtu = 1005;
+  codec_config.isScmstEnabled = false;
+  AudioConfiguration audio_config = {};
+  std::vector<CodecSpecificConfig> ldac_codec_specifics =
+      GetLdacCodecSpecificSupportedList(true);
+  auto hidl_cb = [](BluetoothAudioStatus status,
+                    const DataMQ::Descriptor& dataMQ) {
+    EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+    EXPECT_FALSE(dataMQ.isHandleValid());
+  };
+  for (auto codec_specific : ldac_codec_specifics) {
+    codec_config.config = codec_specific;
+    audio_config.codecConfig(codec_config);
+    auto hidl_retval =
+        audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+    EXPECT_TRUE(audio_provider_->endSession().isOk());
+  }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * AptX hardware encoding config
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest,
+       StartAndEndA2dpAptxHardwareSession) {
+  if (!IsOffloadSupported()) {
+    return;
+  }
+
+  for (auto codec_type : {CodecType::APTX, CodecType::APTX_HD}) {
+    CodecConfiguration codec_config = {};
+    codec_config.codecType = codec_type;
+    codec_config.encodedAudioBitrate =
+        (codec_type == CodecType::APTX ? 352000 : 576000);
+    codec_config.peerMtu = 1005;
+    codec_config.isScmstEnabled = false;
+    AudioConfiguration audio_config = {};
+    std::vector<CodecSpecificConfig> aptx_codec_specifics =
+        GetAptxCodecSpecificSupportedList(
+            (codec_type == CodecType::APTX_HD ? true : false), true);
+    auto hidl_cb = [](BluetoothAudioStatus status,
+                      const DataMQ::Descriptor& dataMQ) {
+      EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+      EXPECT_FALSE(dataMQ.isHandleValid());
+    };
+    for (auto codec_specific : aptx_codec_specifics) {
+      codec_config.config = codec_specific;
+      audio_config.codecConfig(codec_config);
+      auto hidl_retval =
+          audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+      // HIDL calls should not be failed and callback has to be executed
+      ASSERT_TRUE(hidl_retval.isOk());
+      EXPECT_TRUE(audio_provider_->endSession().isOk());
+    }
+  }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * an invalid codec config
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest,
+       StartAndEndA2dpHardwareSessionInvalidCodecConfig) {
+  if (!IsOffloadSupported()) {
+    return;
+  }
+  ASSERT_NE(audio_provider_, nullptr);
+
+  std::vector<CodecSpecificConfig> codec_specifics;
+  for (auto codec_type : a2dp_codec_types) {
+    switch (codec_type) {
+      case CodecType::SBC:
+        codec_specifics = GetSbcCodecSpecificSupportedList(false);
+        break;
+      case CodecType::AAC:
+        codec_specifics = GetAacCodecSpecificSupportedList(false);
+        break;
+      case CodecType::LDAC:
+        codec_specifics = GetLdacCodecSpecificSupportedList(false);
+        break;
+      case CodecType::APTX:
+        codec_specifics = GetAptxCodecSpecificSupportedList(false, false);
+        break;
+      case CodecType::APTX_HD:
+        codec_specifics = GetAptxCodecSpecificSupportedList(true, false);
+        break;
+      case CodecType::UNKNOWN:
+        codec_specifics.clear();
+        break;
+    }
+    if (codec_specifics.empty()) {
+      continue;
+    }
+
+    CodecConfiguration codec_config = {};
+    codec_config.codecType = codec_type;
+    codec_config.encodedAudioBitrate = 328000;
+    codec_config.peerMtu = 1005;
+    codec_config.isScmstEnabled = false;
+    AudioConfiguration audio_config = {};
+    auto hidl_cb = [](BluetoothAudioStatus status,
+                      const DataMQ::Descriptor& dataMQ) {
+      EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+      EXPECT_FALSE(dataMQ.isHandleValid());
+    };
+    for (auto codec_specific : codec_specifics) {
+      codec_config.config = codec_specific;
+      audio_config.codecConfig(codec_config);
+      auto hidl_retval =
+          audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+      // HIDL calls should not be failed and callback has to be executed
+      ASSERT_TRUE(hidl_retval.isOk());
+      EXPECT_TRUE(audio_provider_->endSession().isOk());
+    }
+  }
+}
+
+/**
+ * openProvider HEARING_AID_SOFTWARE_ENCODING_DATAPATH
+ */
+class BluetoothAudioProviderHearingAidSoftwareHidlTest
+    : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+  virtual void SetUp() override {
+    BluetoothAudioProvidersFactoryHidlTest::SetUp();
+    GetProviderCapabilitiesHelper(
+        SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
+    OpenProviderHelper(SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
+    ASSERT_NE(audio_provider_, nullptr);
+  }
+
+  virtual void TearDown() override {
+    audio_port_ = nullptr;
+    audio_provider_ = nullptr;
+    BluetoothAudioProvidersFactoryHidlTest::TearDown();
+  }
+
+  static constexpr SampleRate hearing_aid_sample_rates_[3] = {
+      SampleRate::RATE_UNKNOWN, SampleRate::RATE_16000, SampleRate::RATE_24000};
+  static constexpr BitsPerSample hearing_aid_bits_per_samples_[3] = {
+      BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16,
+      BitsPerSample::BITS_24};
+  static constexpr ChannelMode hearing_aid_channel_modes_[3] = {
+      ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+};
+
+/**
+ * Test whether each provider of type
+ * SessionType::HEARING_AID_HARDWARE_ENCODING_DATAPATH can be started and
+ * stopped with SBC hardware encoding config
+ */
+TEST_F(BluetoothAudioProviderHearingAidSoftwareHidlTest,
+       OpenHearingAidSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH can be started and
+ * stopped with different PCM config
+ */
+TEST_F(BluetoothAudioProviderHearingAidSoftwareHidlTest,
+       StartAndEndHearingAidSessionWithPossiblePcmConfig) {
+  bool is_codec_config_valid;
+  std::unique_ptr<DataMQ> tempDataMQ;
+  auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+                     BluetoothAudioStatus status,
+                     const DataMQ::Descriptor& dataMQ) {
+    if (is_codec_config_valid) {
+      ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+      ASSERT_TRUE(dataMQ.isHandleValid());
+      tempDataMQ.reset(new DataMQ(dataMQ));
+    } else {
+      EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+      EXPECT_FALSE(dataMQ.isHandleValid());
+    }
+  };
+  AudioConfiguration audio_config = {};
+  PcmParameters pcm_parameters = {};
+  for (auto sample_rate : hearing_aid_sample_rates_) {
+    pcm_parameters.sampleRate = sample_rate;
+    for (auto bits_per_sample : hearing_aid_bits_per_samples_) {
+      pcm_parameters.bitsPerSample = bits_per_sample;
+      for (auto channel_mode : hearing_aid_channel_modes_) {
+        pcm_parameters.channelMode = channel_mode;
+        is_codec_config_valid = IsPcmParametersSupported(pcm_parameters);
+        audio_config.pcmConfig(pcm_parameters);
+        auto hidl_retval =
+            audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+        // HIDL calls should not be failed and callback has to be executed
+        ASSERT_TRUE(hidl_retval.isOk());
+        if (is_codec_config_valid) {
+          EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+        }
+        EXPECT_TRUE(audio_provider_->endSession().isOk());
+      }  // ChannelMode
+    }    // BitsPerSampple
+  }      // SampleRate
+}
+
+int main(int argc, char** argv) {
+  ::testing::AddGlobalTestEnvironment(
+      BluetoothAudioHidlEnvironment::Instance());
+  ::testing::InitGoogleTest(&argc, argv);
+  BluetoothAudioHidlEnvironment::Instance()->init(&argc, argv);
+  int status = RUN_ALL_TESTS();
+  LOG(INFO) << "Test result = " << status;
+  return status;
+}
diff --git a/camera/common/1.0/default/CameraModule.cpp b/camera/common/1.0/default/CameraModule.cpp
index 08354b3..467c121 100644
--- a/camera/common/1.0/default/CameraModule.cpp
+++ b/camera/common/1.0/default/CameraModule.cpp
@@ -253,6 +253,14 @@
         }
         mCameraInfoMap.removeItemsAt(0);
     }
+
+    while (mPhysicalCameraInfoMap.size() > 0) {
+        camera_metadata_t* metadata = mPhysicalCameraInfoMap.editValueAt(0);
+        if (metadata != NULL) {
+            free_camera_metadata(metadata);
+        }
+        mPhysicalCameraInfoMap.removeItemsAt(0);
+    }
 }
 
 int CameraModule::init() {
@@ -351,7 +359,14 @@
             return ret;
         }
 
-        index = mPhysicalCameraInfoMap.add(physicalCameraId, info);
+        // The camera_metadata_t returned by get_physical_camera_info could be using
+        // more memory than necessary due to unused reserved space. Reduce the
+        // size by appending it to a new CameraMetadata object, which internally
+        // calls resizeIfNeeded.
+        CameraMetadata m;
+        m.append(info);
+        camera_metadata_t* derivedMetadata = m.release();
+        index = mPhysicalCameraInfoMap.add(physicalCameraId, derivedMetadata);
     }
 
     assert(index != NAME_NOT_FOUND);
@@ -462,6 +477,54 @@
     return res;
 }
 
+void CameraModule::notifyDeviceStateChange(uint64_t deviceState) {
+   if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_5 &&
+           mModule->notify_device_state_change != NULL) {
+       ATRACE_BEGIN("camera_module->notify_device_state_change");
+       ALOGI("%s: calling notify_device_state_change with state %" PRId64, __FUNCTION__,
+               deviceState);
+       mModule->notify_device_state_change(deviceState);
+       ATRACE_END();
+   }
+}
+
+bool CameraModule::isLogicalMultiCamera(
+        const common::V1_0::helper::CameraMetadata& metadata,
+        std::unordered_set<std::string>* physicalCameraIds) {
+    if (physicalCameraIds == nullptr) {
+        ALOGE("%s: physicalCameraIds must not be null", __FUNCTION__);
+        return false;
+    }
+
+    bool isLogicalMultiCamera = false;
+    camera_metadata_ro_entry_t capabilities =
+            metadata.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+    for (size_t i = 0; i < capabilities.count; i++) {
+        if (capabilities.data.u8[i] ==
+                ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
+            isLogicalMultiCamera = true;
+            break;
+        }
+    }
+
+    if (isLogicalMultiCamera) {
+        camera_metadata_ro_entry_t entry =
+                metadata.find(ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
+        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) {
+                    const char* physicalId = reinterpret_cast<const char*>(ids+start);
+                    physicalCameraIds->emplace(physicalId);
+                }
+                start = i + 1;
+            }
+        }
+    }
+    return isLogicalMultiCamera;
+}
+
 status_t CameraModule::filterOpenErrorCode(status_t err) {
     switch(err) {
         case NO_ERROR:
@@ -476,8 +539,24 @@
 }
 
 void CameraModule::removeCamera(int cameraId) {
-    free_camera_metadata(const_cast<camera_metadata_t*>(
-        mCameraInfoMap.valueFor(cameraId).static_camera_characteristics));
+    std::unordered_set<std::string> physicalIds;
+    camera_metadata_t *metadata = const_cast<camera_metadata_t*>(
+            mCameraInfoMap.valueFor(cameraId).static_camera_characteristics);
+    common::V1_0::helper::CameraMetadata hidlMetadata(metadata);
+
+    if (isLogicalMultiCamera(hidlMetadata, &physicalIds)) {
+        for (const auto& id : physicalIds) {
+            int idInt = std::stoi(id);
+            if (mPhysicalCameraInfoMap.indexOfKey(idInt) >= 0) {
+                free_camera_metadata(mPhysicalCameraInfoMap[idInt]);
+                mPhysicalCameraInfoMap.removeItem(idInt);
+            } else {
+                ALOGE("%s: Cannot find corresponding static metadata for physical id %s",
+                        __FUNCTION__, id.c_str());
+            }
+        }
+    }
+    free_camera_metadata(metadata);
     mCameraInfoMap.removeItem(cameraId);
     mDeviceVersionMap.removeItem(cameraId);
 }
diff --git a/camera/common/1.0/default/Exif.cpp b/camera/common/1.0/default/Exif.cpp
index 6054999..4de05c5 100644
--- a/camera/common/1.0/default/Exif.cpp
+++ b/camera/common/1.0/default/Exif.cpp
@@ -18,7 +18,7 @@
 #define ATRACE_TAG ATRACE_TAG_CAMERA
 //#define LOG_NDEBUG 0
 
-#include <cutils/log.h>
+#include <android/log.h>
 
 #include <inttypes.h>
 #include <math.h>
diff --git a/camera/common/1.0/default/include/CameraModule.h b/camera/common/1.0/default/include/CameraModule.h
index ee75e72..c89e934 100644
--- a/camera/common/1.0/default/include/CameraModule.h
+++ b/camera/common/1.0/default/include/CameraModule.h
@@ -17,6 +17,9 @@
 #ifndef CAMERA_COMMON_1_0_CAMERAMODULE_H
 #define CAMERA_COMMON_1_0_CAMERAMODULE_H
 
+#include <string>
+#include <unordered_set>
+
 #include <hardware/camera.h>
 #include <utils/Mutex.h>
 #include <utils/KeyedVector.h>
@@ -67,6 +70,11 @@
     void removeCamera(int cameraId);
     int getPhysicalCameraInfo(int physicalCameraId, camera_metadata_t **physicalInfo);
     int isStreamCombinationSupported(int cameraId, camera_stream_combination_t *streams);
+    void notifyDeviceStateChange(uint64_t deviceState);
+
+    static bool isLogicalMultiCamera(
+            const common::V1_0::helper::CameraMetadata& metadata,
+            std::unordered_set<std::string>* physicalCameraIds);
 
 private:
     // Derive camera characteristics keys defined after HAL device version
diff --git a/camera/device/3.2/default/CameraDeviceSession.cpp b/camera/device/3.2/default/CameraDeviceSession.cpp
index fd785df..f2d7a47 100644
--- a/camera/device/3.2/default/CameraDeviceSession.cpp
+++ b/camera/device/3.2/default/CameraDeviceSession.cpp
@@ -44,13 +44,15 @@
 static constexpr int METADATA_SHRINK_REL_THRESHOLD = 2;
 
 HandleImporter CameraDeviceSession::sHandleImporter;
+buffer_handle_t CameraDeviceSession::sEmptyBuffer = nullptr;
+
 const int CameraDeviceSession::ResultBatcher::NOT_BATCHED;
 
 CameraDeviceSession::CameraDeviceSession(
     camera3_device_t* device,
     const camera_metadata_t* deviceInfo,
     const sp<ICameraDeviceCallback>& callback) :
-        camera3_callback_ops({&sProcessCaptureResult, &sNotify}),
+        camera3_callback_ops({&sProcessCaptureResult, &sNotify, nullptr, nullptr}),
         mDevice(device),
         mDeviceVersion(device->common.version),
         mFreeBufEarly(shouldFreeBufEarly()),
@@ -246,10 +248,50 @@
     }
 }
 
+Status CameraDeviceSession::importBuffer(int32_t streamId,
+        uint64_t bufId, buffer_handle_t buf,
+        /*out*/buffer_handle_t** outBufPtr,
+        bool allowEmptyBuf) {
+
+    if (buf == nullptr && bufId == BUFFER_ID_NO_BUFFER) {
+        if (allowEmptyBuf) {
+            *outBufPtr = &sEmptyBuffer;
+            return Status::OK;
+        } else {
+            ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
+            return Status::ILLEGAL_ARGUMENT;
+        }
+    }
+
+    Mutex::Autolock _l(mInflightLock);
+    CirculatingBuffers& cbs = mCirculatingBuffers[streamId];
+    if (cbs.count(bufId) == 0) {
+        // Register a newly seen buffer
+        buffer_handle_t importedBuf = buf;
+        sHandleImporter.importBuffer(importedBuf);
+        if (importedBuf == nullptr) {
+            ALOGE("%s: output buffer for stream %d is invalid!", __FUNCTION__, streamId);
+            return Status::INTERNAL_ERROR;
+        } else {
+            cbs[bufId] = importedBuf;
+        }
+    }
+    *outBufPtr = &cbs[bufId];
+    return Status::OK;
+}
+
 Status CameraDeviceSession::importRequest(
         const CaptureRequest& request,
         hidl_vec<buffer_handle_t*>& allBufPtrs,
         hidl_vec<int>& allFences) {
+    return importRequestImpl(request, allBufPtrs, allFences);
+}
+
+Status CameraDeviceSession::importRequestImpl(
+        const CaptureRequest& request,
+        hidl_vec<buffer_handle_t*>& allBufPtrs,
+        hidl_vec<int>& allFences,
+        bool allowEmptyBuf) {
     bool hasInputBuf = (request.inputBuffer.streamId != -1 &&
             request.inputBuffer.bufferId != 0);
     size_t numOutputBufs = request.outputBuffers.size();
@@ -277,25 +319,15 @@
     }
 
     for (size_t i = 0; i < numBufs; i++) {
-        buffer_handle_t buf = allBufs[i];
-        uint64_t bufId = allBufIds[i];
-        CirculatingBuffers& cbs = mCirculatingBuffers[streamIds[i]];
-        if (cbs.count(bufId) == 0) {
-            if (buf == nullptr) {
-                ALOGE("%s: bufferId %" PRIu64 " has null buffer handle!", __FUNCTION__, bufId);
-                return Status::ILLEGAL_ARGUMENT;
-            }
-            // Register a newly seen buffer
-            buffer_handle_t importedBuf = buf;
-            sHandleImporter.importBuffer(importedBuf);
-            if (importedBuf == nullptr) {
-                ALOGE("%s: output buffer %zu is invalid!", __FUNCTION__, i);
-                return Status::INTERNAL_ERROR;
-            } else {
-                cbs[bufId] = importedBuf;
-            }
+        Status st = importBuffer(
+                streamIds[i], allBufIds[i], allBufs[i], &allBufPtrs[i],
+                // Disallow empty buf for input stream, otherwise follow
+                // the allowEmptyBuf argument.
+                (hasInputBuf && i == numOutputBufs) ? false : allowEmptyBuf);
+        if (st != Status::OK) {
+            // Detailed error logs printed in importBuffer
+            return st;
         }
-        allBufPtrs[i] = &cbs[bufId];
     }
 
     // All buffers are imported. Now validate output buffer acquire fences
@@ -1271,18 +1303,26 @@
         ATRACE_END();
 
         // free all imported buffers
+        Mutex::Autolock _l(mInflightLock);
         for(auto& pair : mCirculatingBuffers) {
             CirculatingBuffers& buffers = pair.second;
             for (auto& p2 : buffers) {
                 sHandleImporter.freeBuffer(p2.second);
             }
+            buffers.clear();
         }
+        mCirculatingBuffers.clear();
 
         mClosed = true;
     }
     return Void();
 }
 
+uint64_t CameraDeviceSession::getCapResultBufferId(const buffer_handle_t&, int) {
+    // No need to fill in bufferId by default
+    return BUFFER_ID_NO_BUFFER;
+}
+
 status_t CameraDeviceSession::constructCaptureResult(CaptureResult& result,
                                                  const camera3_capture_result *hal_result) {
     uint32_t frameNumber = hal_result->frame_number;
@@ -1396,6 +1436,14 @@
         result.outputBuffers[i].streamId =
                 static_cast<Camera3Stream*>(hal_result->output_buffers[i].stream)->mId;
         result.outputBuffers[i].buffer = nullptr;
+        if (hal_result->output_buffers[i].buffer != nullptr) {
+            result.outputBuffers[i].bufferId = getCapResultBufferId(
+                    *(hal_result->output_buffers[i].buffer),
+                    result.outputBuffers[i].streamId);
+        } else {
+            result.outputBuffers[i].bufferId = 0;
+        }
+
         result.outputBuffers[i].status = (BufferStatus) hal_result->output_buffers[i].status;
         // skip acquire fence since it's of no use to camera service
         if (hal_result->output_buffers[i].release_fence != -1) {
diff --git a/camera/device/3.2/default/CameraDeviceSession.h b/camera/device/3.2/default/CameraDeviceSession.h
index bcee259..a96c245 100644
--- a/camera/device/3.2/default/CameraDeviceSession.h
+++ b/camera/device/3.2/default/CameraDeviceSession.h
@@ -161,6 +161,7 @@
     std::map<uint32_t, bool> mInflightRawBoostPresent;
     ::android::hardware::camera::common::V1_0::helper::CameraMetadata mOverridenRequest;
 
+    static const uint64_t BUFFER_ID_NO_BUFFER = 0;
     // buffers currently ciculating between HAL and camera service
     // key: bufferId sent via HIDL interface
     // value: imported buffer_handle_t
@@ -171,6 +172,7 @@
     std::map<int, CirculatingBuffers> mCirculatingBuffers;
 
     static HandleImporter sHandleImporter;
+    static buffer_handle_t sEmptyBuffer;
 
     bool mInitFail;
     bool mFirstRequest = false;
@@ -301,11 +303,23 @@
     Status initStatus() const;
 
     // Validate and import request's input buffer and acquire fence
-    Status importRequest(
+    virtual Status importRequest(
             const CaptureRequest& request,
             hidl_vec<buffer_handle_t*>& allBufPtrs,
             hidl_vec<int>& allFences);
 
+    Status importRequestImpl(
+            const CaptureRequest& request,
+            hidl_vec<buffer_handle_t*>& allBufPtrs,
+            hidl_vec<int>& allFences,
+            // Optional argument for ICameraDeviceSession@3.5 impl
+            bool allowEmptyBuf = false);
+
+    Status importBuffer(int32_t streamId,
+            uint64_t bufId, buffer_handle_t buf,
+            /*out*/buffer_handle_t** outBufPtr,
+            bool allowEmptyBuf);
+
     static void cleanupInflightFences(
             hidl_vec<int>& allFences, size_t numFences);
 
@@ -332,6 +346,11 @@
     static callbacks_process_capture_result_t sProcessCaptureResult;
     static callbacks_notify_t sNotify;
 
+    // By default camera service uses frameNumber/streamId pair to retrieve the buffer that
+    // was sent to HAL. Override this implementation if HAL is using buffers from buffer management
+    // APIs to send output buffer.
+    virtual uint64_t getCapResultBufferId(const buffer_handle_t& buf, int streamId);
+
     status_t constructCaptureResult(CaptureResult& result,
                                 const camera3_capture_result *hal_result);
 
diff --git a/camera/device/3.4/default/CameraDeviceSession.cpp b/camera/device/3.4/default/CameraDeviceSession.cpp
index f2e031c..e00b3f8 100644
--- a/camera/device/3.4/default/CameraDeviceSession.cpp
+++ b/camera/device/3.4/default/CameraDeviceSession.cpp
@@ -22,6 +22,7 @@
 #include <hardware/gralloc.h>
 #include <hardware/gralloc1.h>
 #include "CameraDeviceSession.h"
+#include "CameraModule.h"
 
 namespace android {
 namespace hardware {
@@ -30,6 +31,8 @@
 namespace V3_4 {
 namespace implementation {
 
+using ::android::hardware::camera::common::V1_0::helper::CameraModule;
+
 CameraDeviceSession::CameraDeviceSession(
     camera3_device_t* device,
     const camera_metadata_t* deviceInfo,
@@ -54,31 +57,9 @@
 
     mResultBatcher_3_4.setNumPartialResults(mNumPartialResults);
 
-    camera_metadata_entry_t capabilities =
-            mDeviceInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
-    bool isLogicalMultiCamera = false;
-    for (size_t i = 0; i < capabilities.count; i++) {
-        if (capabilities.data.u8[i] ==
-                ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
-            isLogicalMultiCamera = true;
-            break;
-        }
-    }
-    if (isLogicalMultiCamera) {
-        camera_metadata_entry entry =
-                mDeviceInfo.find(ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
-        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) {
-                    const char* physicalId = reinterpret_cast<const char*>(ids+start);
-                    mPhysicalCameraIds.emplace(physicalId);
-                }
-                start = i + 1;
-            }
-        }
-    }
+    // Parse and store current logical camera's physical ids.
+    (void)CameraModule::isLogicalMultiCamera(mDeviceInfo, &mPhysicalCameraIds);
+
 }
 
 CameraDeviceSession::~CameraDeviceSession() {
@@ -87,6 +68,14 @@
 Return<void> CameraDeviceSession::configureStreams_3_4(
         const StreamConfiguration& requestedConfiguration,
         ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb)  {
+    configureStreams_3_4_Impl(requestedConfiguration, _hidl_cb);
+    return Void();
+}
+
+void CameraDeviceSession::configureStreams_3_4_Impl(
+        const StreamConfiguration& requestedConfiguration,
+        ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb,
+        uint32_t streamConfigCounter)  {
     Status status = initStatus();
     HalStreamConfiguration outStreams;
 
@@ -97,7 +86,7 @@
                 ALOGE("%s: trying to configureStreams with physical camera id with V3.2 callback",
                         __FUNCTION__);
                 _hidl_cb(Status::INTERNAL_ERROR, outStreams);
-                return Void();
+                return;
             }
         }
     }
@@ -109,7 +98,7 @@
         ALOGE("%s: trying to configureStreams while there are still %zu inflight buffers!",
                 __FUNCTION__, mInflightBuffers.size());
         _hidl_cb(Status::INTERNAL_ERROR, outStreams);
-        return Void();
+        return;
     }
 
     if (!mInflightAETriggerOverrides.empty()) {
@@ -117,7 +106,7 @@
                 " trigger overrides!", __FUNCTION__,
                 mInflightAETriggerOverrides.size());
         _hidl_cb(Status::INTERNAL_ERROR, outStreams);
-        return Void();
+        return;
     }
 
     if (!mInflightRawBoostPresent.empty()) {
@@ -125,12 +114,12 @@
                 " boost overrides!", __FUNCTION__,
                 mInflightRawBoostPresent.size());
         _hidl_cb(Status::INTERNAL_ERROR, outStreams);
-        return Void();
+        return;
     }
 
     if (status != Status::OK) {
         _hidl_cb(status, outStreams);
-        return Void();
+        return;
     }
 
     const camera_metadata_t *paramBuffer = nullptr;
@@ -139,11 +128,14 @@
     }
 
     camera3_stream_configuration_t stream_list{};
+    // Block reading mStreamConfigCounter until configureStream returns
+    Mutex::Autolock _sccl(mStreamConfigCounterLock);
+    mStreamConfigCounter = streamConfigCounter;
     hidl_vec<camera3_stream_t*> streams;
     stream_list.session_parameters = paramBuffer;
     if (!preProcessConfigurationLocked_3_4(requestedConfiguration, &stream_list, &streams)) {
         _hidl_cb(Status::INTERNAL_ERROR, outStreams);
-        return Void();
+        return;
     }
 
     ATRACE_BEGIN("camera3->configure_streams");
@@ -168,7 +160,7 @@
     }
 
     _hidl_cb(status, outStreams);
-    return Void();
+    return;
 }
 
 bool CameraDeviceSession::preProcessConfigurationLocked_3_4(
diff --git a/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h b/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
index fdc8a5a..1db7b41 100644
--- a/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
+++ b/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
@@ -87,6 +87,12 @@
     void postProcessConfigurationFailureLocked_3_4(
             const StreamConfiguration& requestedConfiguration);
 
+    void configureStreams_3_4_Impl(
+            const StreamConfiguration& requestedConfiguration,
+            ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb,
+            // Optional argument for ICameraDeviceSession@3.5 impl
+            uint32_t streamConfigCounter = 0);
+
     Return<void> processCaptureRequest_3_4(
             const hidl_vec<V3_4::CaptureRequest>& requests,
             const hidl_vec<V3_2::BufferCache>& cachesToRemove,
@@ -117,6 +123,10 @@
     // Physical camera ids for the logical multi-camera. Empty if this
     // is not a logical multi-camera.
     std::unordered_set<std::string> mPhysicalCameraIds;
+
+    Mutex    mStreamConfigCounterLock;
+    uint32_t mStreamConfigCounter = 1;
+
 private:
 
     struct TrampolineSessionInterface_3_4 : public ICameraDeviceSession {
diff --git a/camera/device/3.5/Android.bp b/camera/device/3.5/Android.bp
index 2a9ba05..6d18367 100644
--- a/camera/device/3.5/Android.bp
+++ b/camera/device/3.5/Android.bp
@@ -23,6 +23,8 @@
     types: [
         "BufferRequest",
         "BufferRequestStatus",
+        "CameraBlob",
+        "CameraBlobId",
         "StreamBufferRequestError",
         "StreamBufferRet",
         "StreamBuffersVal",
diff --git a/camera/device/3.5/ICameraDeviceSession.hal b/camera/device/3.5/ICameraDeviceSession.hal
index b2b71cd..d0cfe39 100644
--- a/camera/device/3.5/ICameraDeviceSession.hal
+++ b/camera/device/3.5/ICameraDeviceSession.hal
@@ -17,6 +17,7 @@
 package android.hardware.camera.device@3.5;
 
 import android.hardware.camera.common@1.0::Status;
+import @3.2::CameraMetadata;
 import @3.4::ICameraDeviceSession;
 import @3.4::HalStreamConfiguration;
 
@@ -99,4 +100,49 @@
         vec<int32_t> streamIds,
         uint32_t streamConfigCounter
     );
+
+    /**
+     * isReconfigurationRequired:
+     *
+     * Check whether complete stream reconfiguration is required for possible new session
+     * parameter values.
+     *
+     * This method must be called by the camera framework in case the client changes
+     * the value of any advertised session parameters. Depending on the specific values
+     * the HAL can decide whether a complete stream reconfiguration is required. In case
+     * the HAL returns false, the camera framework must skip the internal reconfiguration.
+     * In case Hal returns true, the framework must reconfigure the streams and pass the
+     * new session parameter values accordingly.
+     * This call may be done by the framework some time before the request with new parameters
+     * is submitted to the HAL, and the request may be cancelled before it ever gets submitted.
+     * Therefore, the HAL must not use this query as an indication to change its behavior in any
+     * way.
+     * ------------------------------------------------------------------------
+     *
+     * Preconditions:
+     *
+     * The framework can call this method at any time after active
+     * session configuration. There must be no impact on the performance of
+     * pending camera requests in any way. In particular there must not be
+     * any glitches or delays during normal camera streaming.
+     *
+     * Performance requirements:
+     * HW and SW camera settings must not be changed and there must not be
+     * a user-visible impact on camera performance.
+     *
+     * @param oldSessionParams Before session parameters, usually the current session parameters.
+     * @param newSessionParams The new session parameters which may be set by client.
+     *
+     * @return Status Status code for the operation, one of:
+     *     OK:
+     *          On successful reconfiguration required query.
+     *     METHOD_NOT_SUPPORTED:
+     *          The camera device does not support the reconfiguration query.
+     *     INTERNAL_ERROR:
+     *          The reconfiguration query cannot complete due to internal
+     *          error.
+     * @return true in case the stream reconfiguration is required, false otherwise.
+     */
+    isReconfigurationRequired(CameraMetadata oldSessionParams, CameraMetadata newSessionParams)
+            generates(Status status, bool reconfigurationNeeded);
 };
diff --git a/camera/device/3.5/default/CameraDeviceSession.cpp b/camera/device/3.5/default/CameraDeviceSession.cpp
index 963893a..bea1be6 100644
--- a/camera/device/3.5/default/CameraDeviceSession.cpp
+++ b/camera/device/3.5/default/CameraDeviceSession.cpp
@@ -15,8 +15,10 @@
  */
 
 #define LOG_TAG "CamDevSession@3.5-impl"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
 #include <android/log.h>
 
+#include <vector>
 #include <utils/Trace.h>
 #include "CameraDeviceSession.h"
 
@@ -33,13 +35,26 @@
     const sp<V3_2::ICameraDeviceCallback>& callback) :
         V3_4::implementation::CameraDeviceSession(device, deviceInfo, callback) {
 
-    mHasCallback_3_5 = false;
+    mCallback_3_5 = nullptr;
 
     auto castResult = ICameraDeviceCallback::castFrom(callback);
     if (castResult.isOk()) {
         sp<ICameraDeviceCallback> callback3_5 = castResult;
         if (callback3_5 != nullptr) {
-            mHasCallback_3_5 = true;
+            mCallback_3_5 = callback3_5;
+        }
+    }
+
+    if (mCallback_3_5 != nullptr) {
+        camera_metadata_entry bufMgrVersion = mDeviceInfo.find(
+                ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION);
+        if (bufMgrVersion.count > 0) {
+            mSupportBufMgr = (bufMgrVersion.data.u8[0] ==
+                    ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+            if (mSupportBufMgr) {
+                request_stream_buffers = sRequestStreamBuffers;
+                return_stream_buffers = sReturnStreamBuffers;
+            }
         }
     }
 }
@@ -50,11 +65,344 @@
 Return<void> CameraDeviceSession::configureStreams_3_5(
         const StreamConfiguration& requestedConfiguration,
         ICameraDeviceSession::configureStreams_3_5_cb _hidl_cb)  {
-    return configureStreams_3_4(requestedConfiguration.v3_4, _hidl_cb);
+    configureStreams_3_4_Impl(requestedConfiguration.v3_4, _hidl_cb,
+            requestedConfiguration.streamConfigCounter);
+    return Void();
 }
 
 Return<void> CameraDeviceSession::signalStreamFlush(
-        const hidl_vec<int32_t>& /*requests*/, uint32_t /*streamConfigCounter*/) {
+        const hidl_vec<int32_t>& streamIds, uint32_t streamConfigCounter) {
+    if (mDevice->ops->signal_stream_flush == nullptr) {
+        return Void();
+    }
+
+    uint32_t currentCounter = 0;
+    {
+        Mutex::Autolock _l(mStreamConfigCounterLock);
+        currentCounter = mStreamConfigCounter;
+    }
+
+    if (streamConfigCounter < currentCounter) {
+        ALOGV("%s: streamConfigCounter %d is stale (current %d), skipping signal_stream_flush call",
+                __FUNCTION__, streamConfigCounter, mStreamConfigCounter);
+        return Void();
+    }
+
+    std::vector<camera3_stream_t*> streams(streamIds.size());
+    {
+        Mutex::Autolock _l(mInflightLock);
+        for (size_t i = 0; i < streamIds.size(); i++) {
+            int32_t id = streamIds[i];
+            if (mStreamMap.count(id) == 0) {
+                ALOGE("%s: unknown streamId %d", __FUNCTION__, id);
+                return Void();
+            }
+            streams[i] = &mStreamMap[id];
+        }
+    }
+
+    mDevice->ops->signal_stream_flush(mDevice, streams.size(), streams.data());
+    return Void();
+}
+
+Status CameraDeviceSession::importRequest(
+        const CaptureRequest& request,
+        hidl_vec<buffer_handle_t*>& allBufPtrs,
+        hidl_vec<int>& allFences) {
+    if (mSupportBufMgr) {
+        return importRequestImpl(request, allBufPtrs, allFences, /*allowEmptyBuf*/ true);
+    }
+    return importRequestImpl(request, allBufPtrs, allFences, /*allowEmptyBuf*/ false);
+}
+
+void CameraDeviceSession::pushBufferId(
+        const buffer_handle_t& buf, uint64_t bufferId, int streamId) {
+    std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+
+    // emplace will return existing entry if there is one.
+    auto pair = mBufferIdMaps.emplace(streamId, BufferIdMap{});
+    BufferIdMap& bIdMap = pair.first->second;
+    bIdMap[buf] = bufferId;
+}
+
+uint64_t CameraDeviceSession::popBufferId(
+        const buffer_handle_t& buf, int streamId) {
+    std::lock_guard<std::mutex> lock(mBufferIdMapLock);
+
+    auto streamIt = mBufferIdMaps.find(streamId);
+    if (streamIt == mBufferIdMaps.end()) {
+        return BUFFER_ID_NO_BUFFER;
+    }
+    BufferIdMap& bIdMap = streamIt->second;
+    auto it = bIdMap.find(buf);
+    if (it == bIdMap.end()) {
+        return BUFFER_ID_NO_BUFFER;
+    }
+    uint64_t bufId = it->second;
+    bIdMap.erase(it);
+    if (bIdMap.empty()) {
+        mBufferIdMaps.erase(streamIt);
+    }
+    return bufId;
+}
+
+uint64_t CameraDeviceSession::getCapResultBufferId(const buffer_handle_t& buf, int streamId) {
+    if (mSupportBufMgr) {
+        return popBufferId(buf, streamId);
+    }
+    return BUFFER_ID_NO_BUFFER;
+}
+
+Camera3Stream* CameraDeviceSession::getStreamPointer(int32_t streamId) {
+    Mutex::Autolock _l(mInflightLock);
+    if (mStreamMap.count(streamId) == 0) {
+        ALOGE("%s: unknown streamId %d", __FUNCTION__, streamId);
+        return nullptr;
+    }
+    return &mStreamMap[streamId];
+}
+
+void CameraDeviceSession::cleanupInflightBufferFences(
+        std::vector<int>& fences, std::vector<std::pair<buffer_handle_t, int>>& bufs) {
+    hidl_vec<int> hFences = fences;
+    cleanupInflightFences(hFences, fences.size());
+    for (auto& p : bufs) {
+        popBufferId(p.first, p.second);
+    }
+}
+
+camera3_buffer_request_status_t CameraDeviceSession::requestStreamBuffers(
+        uint32_t num_buffer_reqs,
+        const camera3_buffer_request_t *buffer_reqs,
+        /*out*/uint32_t *num_returned_buf_reqs,
+        /*out*/camera3_stream_buffer_ret_t *returned_buf_reqs) {
+    ATRACE_CALL();
+    *num_returned_buf_reqs = 0;
+    hidl_vec<BufferRequest> hBufReqs(num_buffer_reqs);
+    for (size_t i = 0; i < num_buffer_reqs; i++) {
+        hBufReqs[i].streamId =
+                static_cast<Camera3Stream*>(buffer_reqs[i].stream)->mId;
+        hBufReqs[i].numBuffersRequested = buffer_reqs[i].num_buffers_requested;
+    }
+
+    ATRACE_BEGIN("HIDL requestStreamBuffers");
+    BufferRequestStatus status;
+    hidl_vec<StreamBufferRet> bufRets;
+    auto err = mCallback_3_5->requestStreamBuffers(hBufReqs,
+            [&status, &bufRets]
+            (BufferRequestStatus s, const hidl_vec<StreamBufferRet>& rets) {
+                status = s;
+                bufRets = std::move(rets);
+            });
+    if (!err.isOk()) {
+        ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+        return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
+    }
+    ATRACE_END();
+
+    switch (status) {
+        case BufferRequestStatus::FAILED_CONFIGURING:
+            return CAMERA3_BUF_REQ_FAILED_CONFIGURING;
+        case BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS:
+            return CAMERA3_BUF_REQ_FAILED_ILLEGAL_ARGUMENTS;
+        default:
+            break; // Other status Handled by following code
+    }
+
+    if (status != BufferRequestStatus::OK && status != BufferRequestStatus::FAILED_PARTIAL &&
+            status != BufferRequestStatus::FAILED_UNKNOWN) {
+        ALOGE("%s: unknown buffer request error code %d", __FUNCTION__, status);
+        return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
+    }
+
+    // Only OK, FAILED_PARTIAL and FAILED_UNKNOWN reaches here
+    if (bufRets.size() != num_buffer_reqs) {
+        ALOGE("%s: expect %d buffer requests returned, only got %zu",
+                __FUNCTION__, num_buffer_reqs, bufRets.size());
+        return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
+    }
+
+    *num_returned_buf_reqs = num_buffer_reqs;
+    for (size_t i = 0; i < num_buffer_reqs; i++) {
+        // maybe we can query all streams in one call to avoid frequent locking device here?
+        Camera3Stream* stream = getStreamPointer(bufRets[i].streamId);
+        if (stream == nullptr) {
+            ALOGE("%s: unknown streamId %d", __FUNCTION__, bufRets[i].streamId);
+            return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
+        }
+        returned_buf_reqs[i].stream = stream;
+    }
+
+    // Handle failed streams
+    for (size_t i = 0; i < num_buffer_reqs; i++) {
+        if (bufRets[i].val.getDiscriminator() == StreamBuffersVal::hidl_discriminator::error) {
+            returned_buf_reqs[i].num_output_buffers = 0;
+            switch (bufRets[i].val.error()) {
+                case StreamBufferRequestError::NO_BUFFER_AVAILABLE:
+                    returned_buf_reqs[i].status = CAMERA3_PS_BUF_REQ_NO_BUFFER_AVAILABLE;
+                    break;
+                case StreamBufferRequestError::MAX_BUFFER_EXCEEDED:
+                    returned_buf_reqs[i].status = CAMERA3_PS_BUF_REQ_MAX_BUFFER_EXCEEDED;
+                    break;
+                case StreamBufferRequestError::STREAM_DISCONNECTED:
+                    returned_buf_reqs[i].status = CAMERA3_PS_BUF_REQ_STREAM_DISCONNECTED;
+                    break;
+                case StreamBufferRequestError::UNKNOWN_ERROR:
+                    returned_buf_reqs[i].status = CAMERA3_PS_BUF_REQ_UNKNOWN_ERROR;
+                    break;
+                default:
+                    ALOGE("%s: Unknown StreamBufferRequestError %d",
+                            __FUNCTION__, bufRets[i].val.error());
+                    return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
+            }
+        }
+    }
+
+    if (status == BufferRequestStatus::FAILED_UNKNOWN) {
+        return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
+    }
+
+    // Only BufferRequestStatus::OK and BufferRequestStatus::FAILED_PARTIAL reaches here
+    std::vector<int> importedFences;
+    std::vector<std::pair<buffer_handle_t, int>> importedBuffers;
+    for (size_t i = 0; i < num_buffer_reqs; i++) {
+        if (bufRets[i].val.getDiscriminator() !=
+                StreamBuffersVal::hidl_discriminator::buffers) {
+            continue;
+        }
+        int streamId = bufRets[i].streamId;
+        const hidl_vec<StreamBuffer>& hBufs = bufRets[i].val.buffers();
+        camera3_stream_buffer_t* outBufs = returned_buf_reqs[i].output_buffers;
+        for (size_t b = 0; b < hBufs.size(); b++) {
+            const StreamBuffer& hBuf = hBufs[b];
+            camera3_stream_buffer_t& outBuf = outBufs[b];
+            // maybe add importBuffers API to avoid frequent locking device?
+            Status s = importBuffer(streamId,
+                    hBuf.bufferId, hBuf.buffer.getNativeHandle(),
+                    /*out*/&(outBuf.buffer),
+                    /*allowEmptyBuf*/false);
+            if (s != Status::OK) {
+                ALOGE("%s: import stream %d bufferId %" PRIu64 " failed!",
+                        __FUNCTION__, streamId, hBuf.bufferId);
+                cleanupInflightBufferFences(importedFences, importedBuffers);
+                // Buffer import should never fail - restart HAL since something is very
+                // wrong.
+                assert(false);
+                return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
+            }
+
+            pushBufferId(*(outBuf.buffer), hBuf.bufferId, streamId);
+            importedBuffers.push_back(std::make_pair(*(outBuf.buffer), streamId));
+
+            if (!sHandleImporter.importFence(
+                    hBuf.acquireFence,
+                    outBuf.acquire_fence)) {
+                ALOGE("%s: stream %d bufferId %" PRIu64 "acquire fence is invalid",
+                        __FUNCTION__, streamId, hBuf.bufferId);
+                cleanupInflightBufferFences(importedFences, importedBuffers);
+                return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
+            }
+            importedFences.push_back(outBuf.acquire_fence);
+            outBuf.stream = returned_buf_reqs[i].stream;
+            outBuf.status = CAMERA3_BUFFER_STATUS_OK;
+            outBuf.release_fence = -1;
+        }
+        returned_buf_reqs[i].status = CAMERA3_PS_BUF_REQ_OK;
+    }
+
+    return (status == BufferRequestStatus::OK) ?
+            CAMERA3_BUF_REQ_OK : CAMERA3_BUF_REQ_FAILED_PARTIAL;
+}
+
+void CameraDeviceSession::returnStreamBuffers(
+        uint32_t num_buffers,
+        const camera3_stream_buffer_t* const* buffers) {
+    ATRACE_CALL();
+    hidl_vec<StreamBuffer> hBufs(num_buffers);
+
+    for (size_t i = 0; i < num_buffers; i++) {
+        hBufs[i].streamId =
+                static_cast<Camera3Stream*>(buffers[i]->stream)->mId;
+        hBufs[i].buffer = nullptr; // use bufferId
+        hBufs[i].bufferId = popBufferId(*(buffers[i]->buffer), hBufs[i].streamId);
+        if (hBufs[i].bufferId == BUFFER_ID_NO_BUFFER) {
+            ALOGE("%s: unknown buffer is returned to stream %d",
+                    __FUNCTION__, hBufs[i].streamId);
+        }
+        // ERROR since the buffer is not for application to consume
+        hBufs[i].status = BufferStatus::ERROR;
+        // skip acquire fence since it's of no use to camera service
+        if (buffers[i]->release_fence != -1) {
+            native_handle_t* handle = native_handle_create(/*numFds*/1, /*numInts*/0);
+            handle->data[0] = buffers[i]->release_fence;
+            hBufs[i].releaseFence.setTo(handle, /*shouldOwn*/true);
+        }
+    }
+
+    mCallback_3_5->returnStreamBuffers(hBufs);
+    return;
+}
+
+/**
+ * Static callback forwarding methods from HAL to instance
+ */
+camera3_buffer_request_status_t CameraDeviceSession::sRequestStreamBuffers(
+        const struct camera3_callback_ops *cb,
+        uint32_t num_buffer_reqs,
+        const camera3_buffer_request_t *buffer_reqs,
+        /*out*/uint32_t *num_returned_buf_reqs,
+        /*out*/camera3_stream_buffer_ret_t *returned_buf_reqs) {
+    CameraDeviceSession *d =
+            const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
+
+    if (num_buffer_reqs == 0 || buffer_reqs == nullptr || num_returned_buf_reqs == nullptr ||
+            returned_buf_reqs == nullptr) {
+        ALOGE("%s: bad argument: numBufReq %d, bufReqs %p, numRetBufReq %p, retBufReqs %p",
+                __FUNCTION__, num_buffer_reqs, buffer_reqs,
+                num_returned_buf_reqs, returned_buf_reqs);
+        return CAMERA3_BUF_REQ_FAILED_ILLEGAL_ARGUMENTS;
+    }
+
+    return d->requestStreamBuffers(num_buffer_reqs, buffer_reqs,
+            num_returned_buf_reqs, returned_buf_reqs);
+}
+
+void CameraDeviceSession::sReturnStreamBuffers(
+        const struct camera3_callback_ops *cb,
+        uint32_t num_buffers,
+        const camera3_stream_buffer_t* const* buffers) {
+    CameraDeviceSession *d =
+            const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
+
+    d->returnStreamBuffers(num_buffers, buffers);
+}
+
+Return<void> CameraDeviceSession::isReconfigurationRequired(
+        const V3_2::CameraMetadata& oldSessionParams, const V3_2::CameraMetadata& newSessionParams,
+        ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb) {
+    if (mDevice->ops->is_reconfiguration_required != nullptr) {
+        const camera_metadata_t *oldParams, *newParams;
+        V3_2::implementation::convertFromHidl(oldSessionParams, &oldParams);
+        V3_2::implementation::convertFromHidl(newSessionParams, &newParams);
+        auto ret = mDevice->ops->is_reconfiguration_required(mDevice, oldParams, newParams);
+        switch (ret) {
+            case 0:
+                _hidl_cb(Status::OK, true);
+                break;
+            case -EINVAL:
+                _hidl_cb(Status::OK, false);
+                break;
+            case -ENOSYS:
+                _hidl_cb(Status::METHOD_NOT_SUPPORTED, true);
+                break;
+            default:
+                _hidl_cb(Status::INTERNAL_ERROR, true);
+                break;
+        };
+    } else {
+        _hidl_cb(Status::METHOD_NOT_SUPPORTED, true);
+    }
+
     return Void();
 }
 
diff --git a/camera/device/3.5/default/ExternalCameraDeviceSession.cpp b/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
index ae7c45e..00c1d0d 100644
--- a/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
@@ -295,6 +295,15 @@
     return 0;
 }
 
+Return<void> ExternalCameraDeviceSession::isReconfigurationRequired(
+        const V3_2::CameraMetadata& /*oldSessionParams*/,
+        const V3_2::CameraMetadata& /*newSessionParams*/,
+        ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb) {
+    //Stub implementation
+    _hidl_cb(Status::OK, true);
+    return Void();
+}
+
 } // namespace implementation
 }  // namespace V3_5
 }  // namespace device
diff --git a/camera/device/3.5/default/include/device_v3_5_impl/CameraDeviceSession.h b/camera/device/3.5/default/include/device_v3_5_impl/CameraDeviceSession.h
index ec34769..87d616c 100644
--- a/camera/device/3.5/default/include/device_v3_5_impl/CameraDeviceSession.h
+++ b/camera/device/3.5/default/include/device_v3_5_impl/CameraDeviceSession.h
@@ -21,6 +21,7 @@
 #include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
 #include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
 #include <../../3.4/default/include/device_v3_4_impl/CameraDeviceSession.h>
+#include <unordered_map>
 
 namespace android {
 namespace hardware {
@@ -30,11 +31,14 @@
 namespace implementation {
 
 using namespace ::android::hardware::camera::device;
+using ::android::hardware::camera::device::V3_2::BufferStatus;
 using ::android::hardware::camera::device::V3_2::CaptureRequest;
+using ::android::hardware::camera::device::V3_2::StreamBuffer;
 using ::android::hardware::camera::device::V3_5::StreamConfiguration;
 using ::android::hardware::camera::device::V3_4::HalStreamConfiguration;
 using ::android::hardware::camera::device::V3_5::ICameraDeviceSession;
 using ::android::hardware::camera::device::V3_5::ICameraDeviceCallback;
+using ::android::hardware::camera::device::V3_2::implementation::Camera3Stream;
 using ::android::hardware::camera::common::V1_0::Status;
 using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
 using ::android::hardware::Return;
@@ -44,6 +48,25 @@
 using ::android::sp;
 using ::android::Mutex;
 
+
+/**
+ * Function pointer types with C calling convention to
+ * use for HAL callback functions.
+ */
+extern "C" {
+    typedef camera3_buffer_request_status_t (callbacks_request_stream_buffer_t)(
+            const struct camera3_callback_ops *,
+            uint32_t num_buffer_reqs,
+            const camera3_buffer_request_t *buffer_reqs,
+            /*out*/uint32_t *num_returned_buf_reqs,
+            /*out*/camera3_stream_buffer_ret_t *returned_buf_reqs);
+
+    typedef void (callbacks_return_stream_buffer_t)(
+            const struct camera3_callback_ops *,
+            uint32_t num_buffers,
+            const camera3_stream_buffer_t* const* buffers);
+}
+
 struct CameraDeviceSession : public V3_4::implementation::CameraDeviceSession {
 
     CameraDeviceSession(camera3_device_t*,
@@ -62,12 +85,88 @@
             ICameraDeviceSession::configureStreams_3_5_cb _hidl_cb);
 
     Return<void> signalStreamFlush(
-            const hidl_vec<int32_t>& requests,
+            const hidl_vec<int32_t>& streamIds,
             uint32_t streamConfigCounter);
 
+    virtual Status importRequest(
+            const CaptureRequest& request,
+            hidl_vec<buffer_handle_t*>& allBufPtrs,
+            hidl_vec<int>& allFences) override;
 
-    // Whether this camera device session is created with version 3.5 callback.
-    bool mHasCallback_3_5;
+    Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams,
+            const V3_2::CameraMetadata& newSessionParams,
+            ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb);
+    /**
+     * Static callback forwarding methods from HAL to instance
+     */
+    static callbacks_request_stream_buffer_t sRequestStreamBuffers;
+    static callbacks_return_stream_buffer_t sReturnStreamBuffers;
+
+    camera3_buffer_request_status_t requestStreamBuffers(
+            uint32_t num_buffer_reqs,
+            const camera3_buffer_request_t *buffer_reqs,
+            /*out*/uint32_t *num_returned_buf_reqs,
+            /*out*/camera3_stream_buffer_ret_t *returned_buf_reqs);
+
+    void returnStreamBuffers(
+            uint32_t num_buffers,
+            const camera3_stream_buffer_t* const* buffers);
+
+    struct BufferHasher {
+        size_t operator()(const buffer_handle_t& buf) const {
+            if (buf == nullptr)
+                return 0;
+
+            size_t result = 1;
+            result = 31 * result + buf->numFds;
+            for (int i = 0; i < buf->numFds; i++) {
+                result = 31 * result + buf->data[i];
+            }
+            return result;
+        }
+    };
+
+    struct BufferComparator {
+        bool operator()(const buffer_handle_t& buf1, const buffer_handle_t& buf2) const {
+            if (buf1->numFds == buf2->numFds) {
+                for (int i = 0; i < buf1->numFds; i++) {
+                    if (buf1->data[i] != buf2->data[i]) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+            return false;
+        }
+    };
+
+    Camera3Stream* getStreamPointer(int32_t streamId);
+
+    // Register buffer to mBufferIdMaps so we can find corresponding bufferId
+    // when the buffer is returned to camera service
+    void pushBufferId(const buffer_handle_t& buf, uint64_t bufferId, int streamId);
+
+    // Method to pop buffer's bufferId from mBufferIdMaps
+    // BUFFER_ID_NO_BUFFER is returned if no matching buffer is found
+    uint64_t popBufferId(const buffer_handle_t& buf, int streamId);
+
+    // Method to cleanup imported buffer/fences if requestStreamBuffers fails half way
+    void cleanupInflightBufferFences(
+            std::vector<int>& fences, std::vector<std::pair<buffer_handle_t, int>>& bufs);
+
+    // Overrides the default constructCaptureResult behavior for buffer management APIs
+    virtual uint64_t getCapResultBufferId(const buffer_handle_t& buf, int streamId) override;
+
+    std::mutex mBufferIdMapLock; // protecting mBufferIdMaps and mNextBufferId
+    typedef std::unordered_map<const buffer_handle_t, uint64_t,
+            BufferHasher, BufferComparator> BufferIdMap;
+    // stream ID -> per stream buffer ID map for buffers coming from requestStreamBuffers API
+    // Entries are created during requestStreamBuffers when a stream first request a buffer, and
+    // deleted in returnStreamBuffers/processCaptureResult* when all buffers are returned
+    std::unordered_map<int, BufferIdMap> mBufferIdMaps;
+
+    sp<ICameraDeviceCallback> mCallback_3_5;
+    bool mSupportBufMgr;
 
 private:
 
@@ -142,6 +241,11 @@
             return mParent->signalStreamFlush(requests, streamConfigCounter);
         }
 
+        virtual Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams,
+                const V3_2::CameraMetadata& newSessionParams,
+                ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb) override {
+            return mParent->isReconfigurationRequired(oldSessionParams, newSessionParams, _hidl_cb);
+        }
     private:
         sp<CameraDeviceSession> mParent;
     };
diff --git a/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h b/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
index aa119fc..d2b5e89 100644
--- a/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
@@ -106,6 +106,10 @@
             const hidl_vec<int32_t>& requests,
             uint32_t streamConfigCounter);
 
+    Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams,
+            const V3_2::CameraMetadata& newSessionParams,
+            ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb);
+
     virtual void initOutputThread() override;
     virtual void closeOutputThread() override;
     void closeOutputThreadImpl();
@@ -247,6 +251,12 @@
             return mParent->signalStreamFlush(requests, streamConfigCounter);
         }
 
+        virtual Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams,
+                const V3_2::CameraMetadata& newSessionParams,
+                ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb) override {
+            return mParent->isReconfigurationRequired(oldSessionParams, newSessionParams, _hidl_cb);
+        }
+
     private:
         sp<ExternalCameraDeviceSession> mParent;
     };
diff --git a/camera/device/3.5/types.hal b/camera/device/3.5/types.hal
index 613187d..e3c2350 100644
--- a/camera/device/3.5/types.hal
+++ b/camera/device/3.5/types.hal
@@ -18,6 +18,7 @@
 
 import @3.2::StreamBuffer;
 import @3.4::StreamConfiguration;
+import @3.2::CameraBlobId;
 
 /**
  * StreamConfiguration:
@@ -119,7 +120,9 @@
 
     /**
      * Method call failed for all streams and no buffers are returned at all.
-     * Failure due to unknown reason.
+     * Failure due to unknown reason, or all streams has individual failing
+     * reason. For the latter case, check per stream status for each returned
+     * StreamBufferRet.
      */
     FAILED_UNKNOWN = 4,
 };
@@ -129,3 +132,35 @@
     uint32_t numBuffersRequested;
 };
 
+/**
+ * CameraBlob:
+ *
+ * Identical to @3.2::CameraBlob, except that it also supports transport of JPEG
+ * APP segments blob, which contains JPEG APP1 to APPn (Application Marker)
+ * segments as specified in JEITA CP-3451.
+ *
+ * To capture a JPEG APP segments blob, a stream is created using the pixel format
+ * HAL_PIXEL_FORMAT_BLOB and dataspace HAL_DATASPACE_JPEG_APP_SEGMENTS. The buffer
+ * size for the stream is calculated by the framework, based on the static
+ * metadata field android.heic.maxAppSegmentsCount.
+ *
+ * Similar to JPEG image, the JPEG APP segment images can be of variable size,
+ * so the HAL needs to include the final size of all APP segments using this
+ * structure inside the output stream buffer. The camera blob ID field must be
+ * set to CameraBlobId::JPEG_APP_SEGMENTS.
+ *
+ * The transport header must be at the end of the JPEG APP segments output stream
+ * buffer. That means the blobId must start at byte[buffer_size -
+ * sizeof(CameraBlob)], where the buffer_size is the size of gralloc
+ * buffer. The JPEG APP segments data itself starts at the beginning of the
+ * buffer and must be blobSize bytes long.
+ */
+enum CameraBlobId : @3.2::CameraBlobId {
+    JPEG_APP_SEGMENTS = 0x100,
+};
+
+struct CameraBlob {
+    CameraBlobId blobId;
+    uint32_t blobSize;
+};
+
diff --git a/camera/metadata/3.4/Android.bp b/camera/metadata/3.4/Android.bp
index 388df68..d1cba14 100644
--- a/camera/metadata/3.4/Android.bp
+++ b/camera/metadata/3.4/Android.bp
@@ -14,7 +14,16 @@
         "android.hardware.camera.metadata@3.3",
     ],
     types: [
+        "CameraMetadataEnumAndroidDepthAvailableDynamicDepthStreamConfigurations",
+        "CameraMetadataEnumAndroidHeicAvailableHeicStreamConfigurations",
+        "CameraMetadataEnumAndroidHeicInfoSupported",
         "CameraMetadataEnumAndroidInfoSupportedBufferManagementVersion",
+        "CameraMetadataEnumAndroidRequestAvailableCapabilities",
+        "CameraMetadataEnumAndroidScalerAvailableFormats",
+        "CameraMetadataEnumAndroidScalerAvailableRecommendedStreamConfigurations",
+        "CameraMetadataEnumAndroidSensorInfoColorFilterArrangement",
+        "CameraMetadataSection",
+        "CameraMetadataSectionStart",
         "CameraMetadataTag",
     ],
     gen_java: true,
diff --git a/camera/metadata/3.4/types.hal b/camera/metadata/3.4/types.hal
index 9bbc90d..bb630f1 100644
--- a/camera/metadata/3.4/types.hal
+++ b/camera/metadata/3.4/types.hal
@@ -26,7 +26,34 @@
 import android.hardware.camera.metadata@3.2;
 import android.hardware.camera.metadata@3.3;
 
-// No new metadata sections added in this revision
+/**
+ * Top level hierarchy definitions for camera metadata. *_INFO sections are for
+ * the static metadata that can be retrived without opening the camera device.
+ */
+enum CameraMetadataSection : @3.3::CameraMetadataSection {
+    ANDROID_HEIC =
+        android.hardware.camera.metadata@3.3::CameraMetadataSection:ANDROID_SECTION_COUNT,
+
+    ANDROID_HEIC_INFO,
+
+    ANDROID_SECTION_COUNT_3_4,
+
+    VENDOR_SECTION_3_4 = 0x8000,
+
+};
+
+/**
+ * Hierarchy positions in enum space. All vendor extension sections must be
+ * defined with tag >= VENDOR_SECTION_START
+ */
+enum CameraMetadataSectionStart : android.hardware.camera.metadata@3.3::CameraMetadataSectionStart {
+    ANDROID_HEIC_START = CameraMetadataSection:ANDROID_HEIC << 16,
+
+    ANDROID_HEIC_INFO_START = CameraMetadataSection:ANDROID_HEIC_INFO << 16,
+
+    VENDOR_SECTION_START_3_4 = CameraMetadataSection:VENDOR_SECTION_3_4 << 16,
+
+};
 
 /**
  * Main enumeration for defining camera metadata tags added in this revision
@@ -105,12 +132,61 @@
 
     ANDROID_LOGICAL_MULTI_CAMERA_END_3_4,
 
+    /** android.heic.availableHeicStreamConfigurations [static, enum[], ndk_public]
+     *
+     * <p>The available HEIC (ISO/IEC 23008-12) stream
+     * configurations that this camera device supports
+     * (i.e. format, width, height, output/input stream).</p>
+     */
+    ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS = CameraMetadataSectionStart:ANDROID_HEIC_START,
+
+    /** android.heic.availableHeicMinFrameDurations [static, int64[], ndk_public]
+     *
+     * <p>This lists the minimum frame duration for each
+     * format/size combination for HEIC output formats.</p>
+     */
+    ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS,
+
+    /** android.heic.availableHeicStallDurations [static, int64[], ndk_public]
+     *
+     * <p>This lists the maximum stall duration for each
+     * output format/size combination for HEIC streams.</p>
+     */
+    ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS,
+
+    ANDROID_HEIC_END_3_4,
+
+    /** android.heic.info.supported [static, enum, system]
+     *
+     * <p>Whether this camera device can support identical set of stream combinations
+     * involving HEIC image format, compared to the
+     * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#createCaptureSession">table of combinations</a>
+     * involving JPEG image format required for the device's hardware level and capabilities.</p>
+     */
+    ANDROID_HEIC_INFO_SUPPORTED = CameraMetadataSectionStart:ANDROID_HEIC_INFO_START,
+
+    /** android.heic.info.maxJpegAppSegmentsCount [static, byte, system]
+     *
+     * <p>The maximum number of Jpeg APP segments supported by the camera HAL device.</p>
+     */
+    ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT,
+
+    ANDROID_HEIC_INFO_END_3_4,
+
 };
 
 /*
  * Enumeration definitions for the various entries that need them
  */
 
+/** android.request.availableCapabilities enumeration values added since v3.3
+ * @see ANDROID_REQUEST_AVAILABLE_CAPABILITIES
+ */
+enum CameraMetadataEnumAndroidRequestAvailableCapabilities :
+        @3.3::CameraMetadataEnumAndroidRequestAvailableCapabilities {
+    ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA,
+};
+
 /** android.scaler.availableFormats enumeration values added since v3.2
  * @see ANDROID_SCALER_AVAILABLE_FORMATS
  */
@@ -166,3 +242,19 @@
     ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_OUTPUT,
     ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_INPUT,
 };
+
+/** android.heic.availableHeicStreamConfigurations enumeration values
+ * @see ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS
+ */
+enum CameraMetadataEnumAndroidHeicAvailableHeicStreamConfigurations : uint32_t {
+    ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_OUTPUT,
+    ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_INPUT,
+};
+
+/** android.heic.info.supported enumeration values
+ * @see ANDROID_HEIC_INFO_SUPPORTED
+ */
+enum CameraMetadataEnumAndroidHeicInfoSupported : uint32_t {
+    ANDROID_HEIC_INFO_SUPPORTED_FALSE,
+    ANDROID_HEIC_INFO_SUPPORTED_TRUE,
+};
diff --git a/camera/provider/2.4/default/Android.bp b/camera/provider/2.4/default/Android.bp
index 81e5738..9d73934 100644
--- a/camera/provider/2.4/default/Android.bp
+++ b/camera/provider/2.4/default/Android.bp
@@ -1,46 +1,126 @@
 cc_library_shared {
-    name: "android.hardware.camera.provider@2.4-impl",
+    name: "android.hardware.camera.provider@2.4-legacy",
     defaults: ["hidl_defaults"],
     proprietary: true,
-    relative_install_path: "hw",
-    srcs: ["CameraProvider.cpp",
-           "ExternalCameraProvider.cpp"],
+    srcs: ["LegacyCameraProviderImpl_2_4.cpp"],
     shared_libs: [
-        "libhidlbase",
-        "libhidltransport",
-        "libutils",
-        "libcutils",
+        "android.hardware.camera.common@1.0",
         "android.hardware.camera.device@1.0",
         "android.hardware.camera.device@3.2",
         "android.hardware.camera.device@3.3",
         "android.hardware.camera.device@3.4",
         "android.hardware.camera.device@3.5",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
         "camera.device@1.0-impl",
         "camera.device@3.2-impl",
         "camera.device@3.3-impl",
         "camera.device@3.4-impl",
         "camera.device@3.5-impl",
-        "camera.device@3.4-external-impl",
-        "camera.device@3.5-external-impl",
-        "android.hardware.camera.provider@2.4",
-        "android.hardware.camera.common@1.0",
-        "android.hardware.graphics.mapper@2.0",
-        "android.hidl.allocator@1.0",
-        "android.hidl.memory@1.0",
-        "liblog",
-        "libhardware",
         "libcamera_metadata",
-        "libtinyxml2"
-    ],
-    header_libs: [
-        "camera.device@3.4-impl_headers",
-        "camera.device@3.5-impl_headers",
-        "camera.device@3.4-external-impl_headers",
-        "camera.device@3.5-external-impl_headers"
+        "libcutils",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
     ],
     static_libs: [
         "android.hardware.camera.common@1.0-helper",
     ],
+    header_libs: [
+        "camera.device@3.4-impl_headers",
+        "camera.device@3.5-impl_headers",
+    ],
+    export_include_dirs: ["."],
+}
+
+cc_library_shared {
+    name: "android.hardware.camera.provider@2.4-external",
+    proprietary: true,
+    srcs: ["ExternalCameraProviderImpl_2_4.cpp"],
+    shared_libs: [
+        "android.hardware.camera.common@1.0",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
+        "android.hardware.camera.device@3.4",
+        "android.hardware.camera.device@3.5",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "camera.device@3.3-impl",
+        "camera.device@3.4-external-impl",
+        "camera.device@3.4-impl",
+        "camera.device@3.5-external-impl",
+        "camera.device@3.5-impl",
+        "libcamera_metadata",
+        "libcutils",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libtinyxml2",
+        "libutils",
+    ],
+    static_libs: [
+        "android.hardware.camera.common@1.0-helper",
+    ],
+    header_libs: [
+        "camera.device@3.4-external-impl_headers",
+        "camera.device@3.5-external-impl_headers"
+    ],
+    export_include_dirs: ["."],
+}
+
+cc_library_shared {
+    name: "android.hardware.camera.provider@2.4-impl",
+    defaults: ["hidl_defaults"],
+    proprietary: true,
+    relative_install_path: "hw",
+    srcs: ["CameraProvider_2_4.cpp"],
+    shared_libs: [
+        "android.hardware.camera.common@1.0",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
+        "android.hardware.camera.device@3.4",
+        "android.hardware.camera.device@3.5",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.camera.provider@2.4-external",
+        "android.hardware.camera.provider@2.4-legacy",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "camera.device@1.0-impl",
+        "camera.device@3.2-impl",
+        "camera.device@3.3-impl",
+        "camera.device@3.4-external-impl",
+        "camera.device@3.4-impl",
+        "camera.device@3.5-external-impl",
+        "camera.device@3.5-impl",
+        "libcamera_metadata",
+        "libcutils",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libtinyxml2",
+        "libutils",
+    ],
+    header_libs: [
+        "camera.device@3.4-external-impl_headers",
+        "camera.device@3.4-impl_headers",
+        "camera.device@3.5-external-impl_headers",
+        "camera.device@3.5-impl_headers",
+    ],
+    static_libs: [
+        "android.hardware.camera.common@1.0-helper",
+    ],
+    export_include_dirs: ["."],
 }
 
 cc_defaults {
@@ -50,18 +130,32 @@
     relative_install_path: "hw",
     srcs: ["service.cpp"],
     shared_libs: [
-        "libhidlbase",
-        "libhidltransport",
-        "libbinder",
-        "liblog",
-        "libutils",
+        "android.hardware.camera.common@1.0",
         "android.hardware.camera.device@1.0",
         "android.hardware.camera.device@3.2",
         "android.hardware.camera.device@3.3",
         "android.hardware.camera.device@3.4",
         "android.hardware.camera.device@3.5",
         "android.hardware.camera.provider@2.4",
-        "android.hardware.camera.common@1.0",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libbinder",
+        "libcamera_metadata",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "android.hardware.camera.common@1.0-helper",
+    ],
+    header_libs: [
+        "camera.device@3.4-external-impl_headers",
+        "camera.device@3.4-impl_headers",
+        "camera.device@3.5-external-impl_headers",
+        "camera.device@3.5-impl_headers",
     ],
 }
 
@@ -106,17 +200,24 @@
     compile_multilib: "32",
     init_rc: ["android.hardware.camera.provider@2.4-external-service.rc"],
     shared_libs: [
-        "libhidlbase",
-        "libhidltransport",
-        "libbinder",
-        "liblog",
-        "libutils",
+        "android.hardware.camera.common@1.0",
         "android.hardware.camera.device@1.0",
         "android.hardware.camera.device@3.2",
         "android.hardware.camera.device@3.3",
         "android.hardware.camera.device@3.4",
         "android.hardware.camera.device@3.5",
         "android.hardware.camera.provider@2.4",
-        "android.hardware.camera.common@1.0",
+        "libbinder",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libtinyxml2",
+        "libutils",
+    ],
+    header_libs: [
+        "camera.device@3.4-external-impl_headers",
+        "camera.device@3.4-impl_headers",
+        "camera.device@3.5-external-impl_headers",
+        "camera.device@3.5-impl_headers",
     ],
 }
diff --git a/camera/provider/2.4/default/CameraProvider_2_4.cpp b/camera/provider/2.4/default/CameraProvider_2_4.cpp
new file mode 100644
index 0000000..15fc702
--- /dev/null
+++ b/camera/provider/2.4/default/CameraProvider_2_4.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 "CameraProvider_2_4.h"
+#include "LegacyCameraProviderImpl_2_4.h"
+#include "ExternalCameraProviderImpl_2_4.h"
+
+const char *kLegacyProviderName = "legacy/0";
+const char *kExternalProviderName = "external/0";
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace provider {
+namespace V2_4 {
+namespace implementation {
+
+using android::hardware::camera::provider::V2_4::ICameraProvider;
+
+extern "C" ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name);
+
+template<typename IMPL>
+CameraProvider<IMPL>* getProviderImpl() {
+    CameraProvider<IMPL> *provider = new CameraProvider<IMPL>();
+    if (provider == nullptr) {
+        ALOGE("%s: cannot allocate camera provider!", __FUNCTION__);
+        return nullptr;
+    }
+    if (provider->isInitFailed()) {
+        ALOGE("%s: camera provider init failed!", __FUNCTION__);
+        delete provider;
+        return nullptr;
+    }
+    return provider;
+}
+
+ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name) {
+    using namespace android::hardware::camera::provider::V2_4::implementation;
+    ICameraProvider* provider = nullptr;
+    if (strcmp(name, kLegacyProviderName) == 0) {
+        provider = getProviderImpl<LegacyCameraProviderImpl_2_4>();
+    } else if (strcmp(name, kExternalProviderName) == 0) {
+        provider = getProviderImpl<ExternalCameraProviderImpl_2_4>();
+    } else {
+        ALOGE("%s: unknown instance name: %s", __FUNCTION__, name);
+    }
+
+    return provider;
+}
+
+}  // namespace implementation
+}  // namespace V2_4
+}  // namespace provider
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
diff --git a/camera/provider/2.4/default/CameraProvider_2_4.h b/camera/provider/2.4/default/CameraProvider_2_4.h
new file mode 100644
index 0000000..d2e5b94
--- /dev/null
+++ b/camera/provider/2.4/default/CameraProvider_2_4.h
@@ -0,0 +1,86 @@
+/*
+ * 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_HARDWARE_CAMERA_PROVIDER_V2_4_CAMERAPROVIDER_H
+#define ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_CAMERAPROVIDER_H
+
+#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
+#include <hidl/Status.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace provider {
+namespace V2_4 {
+namespace implementation {
+
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::provider::V2_4::ICameraProvider;
+using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_string;
+using ::android::sp;
+
+template<typename IMPL>
+struct CameraProvider : public ICameraProvider {
+    CameraProvider() : impl() {}
+    ~CameraProvider() {}
+
+    // Caller must use this method to check if CameraProvider ctor failed
+    bool isInitFailed() { return impl.isInitFailed(); }
+
+    // Methods from ::android::hardware::camera::provider::V2_4::ICameraProvider follow.
+    Return<Status> setCallback(const sp<ICameraProviderCallback>& callback) override {
+        return impl.setCallback(callback);
+    }
+
+    Return<void> getVendorTags(getVendorTags_cb _hidl_cb) override {
+        return impl.getVendorTags(_hidl_cb);
+    }
+
+    Return<void> getCameraIdList(getCameraIdList_cb _hidl_cb) override {
+        return impl.getCameraIdList(_hidl_cb);
+    }
+
+    Return<void> isSetTorchModeSupported(isSetTorchModeSupported_cb _hidl_cb) override {
+        return impl.isSetTorchModeSupported(_hidl_cb);
+    }
+
+    Return<void> getCameraDeviceInterface_V1_x(
+            const hidl_string& cameraDeviceName,
+            getCameraDeviceInterface_V1_x_cb _hidl_cb) override {
+        return impl.getCameraDeviceInterface_V1_x(cameraDeviceName, _hidl_cb);
+    }
+
+    Return<void> getCameraDeviceInterface_V3_x(
+            const hidl_string& cameraDeviceName,
+            getCameraDeviceInterface_V3_x_cb _hidl_cb) override {
+        return impl.getCameraDeviceInterface_V3_x(cameraDeviceName, _hidl_cb);
+    }
+
+private:
+    IMPL impl;
+};
+
+}  // namespace implementation
+}  // namespace V2_4
+}  // namespace provider
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_CAMERAPROVIDER_H
diff --git a/camera/provider/2.4/default/ExternalCameraProvider.cpp b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
similarity index 87%
rename from camera/provider/2.4/default/ExternalCameraProvider.cpp
rename to camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
index 604df5c..a6fd288 100644
--- a/camera/provider/2.4/default/ExternalCameraProvider.cpp
+++ b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "CamPvdr@2.4-external"
+#define LOG_TAG "CamPrvdr@2.4-external"
 //#define LOG_NDEBUG 0
 #include <log/log.h>
 
@@ -22,10 +22,10 @@
 #include <sys/inotify.h>
 #include <errno.h>
 #include <linux/videodev2.h>
-#include "ExternalCameraProvider.h"
+#include <cutils/properties.h>
+#include "ExternalCameraProviderImpl_2_4.h"
 #include "ExternalCameraDevice_3_4.h"
 #include "ExternalCameraDevice_3_5.h"
-#include <cutils/properties.h>
 
 namespace android {
 namespace hardware {
@@ -34,6 +34,8 @@
 namespace V2_4 {
 namespace implementation {
 
+template struct CameraProvider<ExternalCameraProviderImpl_2_4>;
+
 namespace {
 // "device@<version>/external/<id>"
 const std::regex kDeviceNameRE("device@([0-9]+\\.[0-9]+)/external/(.+)");
@@ -60,7 +62,7 @@
 
 } // anonymous namespace
 
-ExternalCameraProvider::ExternalCameraProvider() :
+ExternalCameraProviderImpl_2_4::ExternalCameraProviderImpl_2_4() :
         mCfg(ExternalCameraConfig::loadFromCfg()),
         mHotPlugThread(this) {
     mHotPlugThread.run("ExtCamHotPlug", PRIORITY_BACKGROUND);
@@ -81,12 +83,12 @@
     }
 }
 
-ExternalCameraProvider::~ExternalCameraProvider() {
+ExternalCameraProviderImpl_2_4::~ExternalCameraProviderImpl_2_4() {
     mHotPlugThread.requestExit();
 }
 
 
-Return<Status> ExternalCameraProvider::setCallback(
+Return<Status> ExternalCameraProviderImpl_2_4::setCallback(
         const sp<ICameraProviderCallback>& callback) {
     {
         Mutex::Autolock _l(mLock);
@@ -105,14 +107,16 @@
     return Status::OK;
 }
 
-Return<void> ExternalCameraProvider::getVendorTags(getVendorTags_cb _hidl_cb) {
+Return<void> ExternalCameraProviderImpl_2_4::getVendorTags(
+        ICameraProvider::getVendorTags_cb _hidl_cb) {
     // No vendor tag support for USB camera
     hidl_vec<VendorTagSection> zeroSections;
     _hidl_cb(Status::OK, zeroSections);
     return Void();
 }
 
-Return<void> ExternalCameraProvider::getCameraIdList(getCameraIdList_cb _hidl_cb) {
+Return<void> ExternalCameraProviderImpl_2_4::getCameraIdList(
+        ICameraProvider::getCameraIdList_cb _hidl_cb) {
     // External camera HAL always report 0 camera, and extra cameras
     // are just reported via cameraDeviceStatusChange callbacks
     hidl_vec<hidl_string> hidlDeviceNameList;
@@ -120,25 +124,25 @@
     return Void();
 }
 
-Return<void> ExternalCameraProvider::isSetTorchModeSupported(
-        isSetTorchModeSupported_cb _hidl_cb) {
+Return<void> ExternalCameraProviderImpl_2_4::isSetTorchModeSupported(
+        ICameraProvider::isSetTorchModeSupported_cb _hidl_cb) {
     // setTorchMode API is supported, though right now no external camera device
     // has a flash unit.
     _hidl_cb (Status::OK, true);
     return Void();
 }
 
-Return<void> ExternalCameraProvider::getCameraDeviceInterface_V1_x(
+Return<void> ExternalCameraProviderImpl_2_4::getCameraDeviceInterface_V1_x(
         const hidl_string&,
-        getCameraDeviceInterface_V1_x_cb _hidl_cb) {
+        ICameraProvider::getCameraDeviceInterface_V1_x_cb _hidl_cb) {
     // External Camera HAL does not support HAL1
     _hidl_cb(Status::OPERATION_NOT_SUPPORTED, nullptr);
     return Void();
 }
 
-Return<void> ExternalCameraProvider::getCameraDeviceInterface_V3_x(
+Return<void> ExternalCameraProviderImpl_2_4::getCameraDeviceInterface_V3_x(
         const hidl_string& cameraDeviceName,
-        getCameraDeviceInterface_V3_x_cb _hidl_cb) {
+        ICameraProvider::getCameraDeviceInterface_V3_x_cb _hidl_cb) {
 
     std::string cameraId, deviceVersion;
     bool match = matchDeviceName(cameraDeviceName, &deviceVersion, &cameraId);
@@ -194,7 +198,7 @@
     return Void();
 }
 
-void ExternalCameraProvider::addExternalCamera(const char* devName) {
+void ExternalCameraProviderImpl_2_4::addExternalCamera(const char* devName) {
     ALOGI("ExtCam: adding %s to External Camera HAL!", devName);
     Mutex::Autolock _l(mLock);
     std::string deviceName;
@@ -209,7 +213,7 @@
     }
 }
 
-void ExternalCameraProvider::deviceAdded(const char* devName) {
+void ExternalCameraProviderImpl_2_4::deviceAdded(const char* devName) {
     {
         base::unique_fd fd(::open(devName, O_RDWR));
         if (fd.get() < 0) {
@@ -242,7 +246,7 @@
     return;
 }
 
-void ExternalCameraProvider::deviceRemoved(const char* devName) {
+void ExternalCameraProviderImpl_2_4::deviceRemoved(const char* devName) {
     Mutex::Autolock _l(mLock);
     std::string deviceName;
     if (mPreferredHal3MinorVersion == 5) {
@@ -260,14 +264,15 @@
     }
 }
 
-ExternalCameraProvider::HotplugThread::HotplugThread(ExternalCameraProvider* parent) :
+ExternalCameraProviderImpl_2_4::HotplugThread::HotplugThread(
+        ExternalCameraProviderImpl_2_4* parent) :
         Thread(/*canCallJava*/false),
         mParent(parent),
         mInternalDevices(parent->mCfg.mInternalDevices) {}
 
-ExternalCameraProvider::HotplugThread::~HotplugThread() {}
+ExternalCameraProviderImpl_2_4::HotplugThread::~HotplugThread() {}
 
-bool ExternalCameraProvider::HotplugThread::threadLoop() {
+bool ExternalCameraProviderImpl_2_4::HotplugThread::threadLoop() {
     // Find existing /dev/video* devices
     DIR* devdir = opendir(kDevicePath);
     if(devdir == 0) {
diff --git a/camera/provider/2.4/default/ExternalCameraProvider.h b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.h
similarity index 72%
rename from camera/provider/2.4/default/ExternalCameraProvider.h
rename to camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.h
index a69cf8b..8c79f68 100644
--- a/camera/provider/2.4/default/ExternalCameraProvider.h
+++ b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.h
@@ -20,13 +20,14 @@
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
-#include "utils/Mutex.h"
-#include "utils/Thread.h"
-#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
+#include <utils/Mutex.h>
+#include <utils/Thread.h>
 #include <hidl/Status.h>
 #include <hidl/MQDescriptor.h>
 #include "ExternalCameraUtils.h"
 
+#include "CameraProvider_2_4.h"
+
 namespace android {
 namespace hardware {
 namespace camera {
@@ -47,25 +48,32 @@
 using ::android::sp;
 using ::android::Mutex;
 
-struct ExternalCameraProvider : public ICameraProvider {
-    ExternalCameraProvider();
-    ~ExternalCameraProvider();
+/**
+ * The implementation of external webcam CameraProvider 2.4, separated
+ * from the HIDL interface layer to allow for implementation reuse by later
+ * provider versions.
+ *
+ * This camera provider supports standard UVC webcameras via the Linux V4L2
+ * UVC driver.
+ */
+struct ExternalCameraProviderImpl_2_4 {
+    ExternalCameraProviderImpl_2_4();
+    ~ExternalCameraProviderImpl_2_4();
+
+    // Caller must use this method to check if CameraProvider ctor failed
+    bool isInitFailed() { return false;}
 
     // Methods from ::android::hardware::camera::provider::V2_4::ICameraProvider follow.
-    Return<Status> setCallback(const sp<ICameraProviderCallback>& callback) override;
-
-    Return<void> getVendorTags(getVendorTags_cb _hidl_cb) override;
-
-    Return<void> getCameraIdList(getCameraIdList_cb _hidl_cb) override;
-
-    Return<void> isSetTorchModeSupported(isSetTorchModeSupported_cb _hidl_cb) override;
-
+    Return<Status> setCallback(const sp<ICameraProviderCallback>& callback);
+    Return<void> getVendorTags(ICameraProvider::getVendorTags_cb _hidl_cb);
+    Return<void> getCameraIdList(ICameraProvider::getCameraIdList_cb _hidl_cb);
+    Return<void> isSetTorchModeSupported(ICameraProvider::isSetTorchModeSupported_cb _hidl_cb);
     Return<void> getCameraDeviceInterface_V1_x(
             const hidl_string&,
-            getCameraDeviceInterface_V1_x_cb) override;
+            ICameraProvider::getCameraDeviceInterface_V1_x_cb);
     Return<void> getCameraDeviceInterface_V3_x(
             const hidl_string&,
-            getCameraDeviceInterface_V3_x_cb) override;
+            ICameraProvider::getCameraDeviceInterface_V3_x_cb);
 
 private:
 
@@ -77,13 +85,13 @@
 
     class HotplugThread : public android::Thread {
     public:
-        HotplugThread(ExternalCameraProvider* parent);
+        HotplugThread(ExternalCameraProviderImpl_2_4* parent);
         ~HotplugThread();
 
         virtual bool threadLoop() override;
 
     private:
-        ExternalCameraProvider* mParent = nullptr;
+        ExternalCameraProviderImpl_2_4* mParent = nullptr;
         const std::unordered_set<std::string> mInternalDevices;
 
         int mINotifyFD = -1;
diff --git a/camera/provider/2.4/default/CameraProvider.cpp b/camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.cpp
similarity index 85%
rename from camera/provider/2.4/default/CameraProvider.cpp
rename to camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.cpp
index f143dd7..4cff1b7 100644
--- a/camera/provider/2.4/default/CameraProvider.cpp
+++ b/camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.cpp
@@ -14,21 +14,21 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "CamProvider@2.4-impl"
+#define LOG_TAG "CamPrvdr@2.4-legacy"
 //#define LOG_NDEBUG 0
 #include <android/log.h>
 
-#include "CameraProvider.h"
-#include "ExternalCameraProvider.h"
+#include "LegacyCameraProviderImpl_2_4.h"
 #include "CameraDevice_1_0.h"
 #include "CameraDevice_3_3.h"
 #include "CameraDevice_3_4.h"
 #include "CameraDevice_3_5.h"
+#include "CameraProvider_2_4.h"
 #include <cutils/properties.h>
+#include <regex>
 #include <string.h>
 #include <utils/Trace.h>
 
-
 namespace android {
 namespace hardware {
 namespace camera {
@@ -36,16 +36,13 @@
 namespace V2_4 {
 namespace implementation {
 
+template struct CameraProvider<LegacyCameraProviderImpl_2_4>;
+
 namespace {
-const char *kLegacyProviderName = "legacy/0";
-const char *kExternalProviderName = "external/0";
 // "device@<version>/legacy/<id>"
 const std::regex kDeviceNameRE("device@([0-9]+\\.[0-9]+)/legacy/(.+)");
-const char *kHAL3_2 = "3.2";
-const char *kHAL3_3 = "3.3";
 const char *kHAL3_4 = "3.4";
 const char *kHAL3_5 = "3.5";
-const char *kHAL1_0 = "1.0";
 const int kMaxCameraDeviceNameLen = 128;
 const int kMaxCameraIdLen = 16;
 
@@ -70,7 +67,7 @@
 using ::android::hardware::camera::common::V1_0::CameraMetadataType;
 using ::android::hardware::camera::common::V1_0::Status;
 
-void CameraProvider::addDeviceNames(int camera_id, CameraDeviceStatus status, bool cam_new)
+void LegacyCameraProviderImpl_2_4::addDeviceNames(int camera_id, CameraDeviceStatus status, bool cam_new)
 {
     char cameraId[kMaxCameraIdLen];
     snprintf(cameraId, sizeof(cameraId), "%d", camera_id);
@@ -110,7 +107,7 @@
     }
 }
 
-void CameraProvider::removeDeviceNames(int camera_id)
+void LegacyCameraProviderImpl_2_4::removeDeviceNames(int camera_id)
 {
     std::string cameraIdStr = std::to_string(camera_id);
 
@@ -137,13 +134,12 @@
 /**
  * static callback forwarding methods from HAL to instance
  */
-void CameraProvider::sCameraDeviceStatusChange(
+void LegacyCameraProviderImpl_2_4::sCameraDeviceStatusChange(
         const struct camera_module_callbacks* callbacks,
         int camera_id,
         int new_status) {
-    CameraProvider* cp = const_cast<CameraProvider*>(
-            static_cast<const CameraProvider*>(callbacks));
-
+    LegacyCameraProviderImpl_2_4* cp = const_cast<LegacyCameraProviderImpl_2_4*>(
+            static_cast<const LegacyCameraProviderImpl_2_4*>(callbacks));
     if (cp == nullptr) {
         ALOGE("%s: callback ops is null", __FUNCTION__);
         return;
@@ -184,12 +180,12 @@
     }
 }
 
-void CameraProvider::sTorchModeStatusChange(
+void LegacyCameraProviderImpl_2_4::sTorchModeStatusChange(
         const struct camera_module_callbacks* callbacks,
         const char* camera_id,
         int new_status) {
-    CameraProvider* cp = const_cast<CameraProvider*>(
-            static_cast<const CameraProvider*>(callbacks));
+    LegacyCameraProviderImpl_2_4* cp = const_cast<LegacyCameraProviderImpl_2_4*>(
+            static_cast<const LegacyCameraProviderImpl_2_4*>(callbacks));
 
     if (cp == nullptr) {
         ALOGE("%s: callback ops is null", __FUNCTION__);
@@ -209,7 +205,7 @@
     }
 }
 
-Status CameraProvider::getHidlStatus(int status) {
+Status LegacyCameraProviderImpl_2_4::getHidlStatus(int status) {
     switch (status) {
         case 0: return Status::OK;
         case -ENODEV: return Status::INTERNAL_ERROR;
@@ -220,38 +216,29 @@
     }
 }
 
-std::string CameraProvider::getLegacyCameraId(const hidl_string& deviceName) {
+std::string LegacyCameraProviderImpl_2_4::getLegacyCameraId(const hidl_string& deviceName) {
     std::string cameraId;
     matchDeviceName(deviceName, nullptr, &cameraId);
     return cameraId;
 }
 
-int CameraProvider::getCameraDeviceVersion(const hidl_string& deviceName) {
-    std::string deviceVersion;
-    bool match = matchDeviceName(deviceName, &deviceVersion, nullptr);
-    if (!match) {
-        return -1;
-    }
-    if (deviceVersion == kHAL3_3) {
-        return CAMERA_DEVICE_API_VERSION_3_3;
-    } else if (deviceVersion == kHAL3_2) {
-        return CAMERA_DEVICE_API_VERSION_3_2;
-    } else if (deviceVersion == kHAL1_0) {
-        return CAMERA_DEVICE_API_VERSION_1_0;
-    }
-    return 0;
-}
-
-std::string CameraProvider::getHidlDeviceName(
+std::string LegacyCameraProviderImpl_2_4::getHidlDeviceName(
         std::string cameraId, int deviceVersion) {
     // Maybe consider create a version check method and SortedVec to speed up?
     if (deviceVersion != CAMERA_DEVICE_API_VERSION_1_0 &&
             deviceVersion != CAMERA_DEVICE_API_VERSION_3_2 &&
             deviceVersion != CAMERA_DEVICE_API_VERSION_3_3 &&
             deviceVersion != CAMERA_DEVICE_API_VERSION_3_4 &&
-            deviceVersion != CAMERA_DEVICE_API_VERSION_3_5) {
+            deviceVersion != CAMERA_DEVICE_API_VERSION_3_5 &&
+            deviceVersion != CAMERA_DEVICE_API_VERSION_3_6) {
         return hidl_string("");
     }
+
+    // Supported combinations:
+    // CAMERA_DEVICE_API_VERSION_1_0 -> ICameraDevice@1.0
+    // CAMERA_DEVICE_API_VERSION_3_[2-4] -> ICameraDevice@[3.2|3.3]
+    // CAMERA_DEVICE_API_VERSION_3_5 + CAMERA_MODULE_API_VERSION_2_4 -> ICameraDevice@3.4
+    // CAMERA_DEVICE_API_VERSION_3_[5-6] + CAMERA_MODULE_API_VERSION_2_5 -> ICameraDevice@3.5
     bool isV1 = deviceVersion == CAMERA_DEVICE_API_VERSION_1_0;
     int versionMajor = isV1 ? 1 : 3;
     int versionMinor = isV1 ? 0 : mPreferredHal3MinorVersion;
@@ -261,6 +248,8 @@
         } else {
             versionMinor = 4;
         }
+    } else if (deviceVersion == CAMERA_DEVICE_API_VERSION_3_6) {
+        versionMinor = 5;
     }
     char deviceName[kMaxCameraDeviceNameLen];
     snprintf(deviceName, sizeof(deviceName), "device@%d.%d/legacy/%s",
@@ -268,15 +257,15 @@
     return deviceName;
 }
 
-CameraProvider::CameraProvider() :
+LegacyCameraProviderImpl_2_4::LegacyCameraProviderImpl_2_4() :
         camera_module_callbacks_t({sCameraDeviceStatusChange,
                                    sTorchModeStatusChange}) {
     mInitFailed = initialize();
 }
 
-CameraProvider::~CameraProvider() {}
+LegacyCameraProviderImpl_2_4::~LegacyCameraProviderImpl_2_4() {}
 
-bool CameraProvider::initialize() {
+bool LegacyCameraProviderImpl_2_4::initialize() {
     camera_module_t *rawModule;
     int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
             (const hw_module_t **)&rawModule);
@@ -353,14 +342,15 @@
 /**
  * Check that the device HAL version is still in supported.
  */
-int CameraProvider::checkCameraVersion(int id, camera_info info) {
+int LegacyCameraProviderImpl_2_4::checkCameraVersion(int id, camera_info info) {
     if (mModule == nullptr) {
         return NO_INIT;
     }
 
     // device_version undefined in CAMERA_MODULE_API_VERSION_1_0,
     // All CAMERA_MODULE_API_VERSION_1_0 devices are backward-compatible
-    if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_0) {
+    uint16_t moduleVersion = mModule->getModuleApiVersion();
+    if (moduleVersion >= CAMERA_MODULE_API_VERSION_2_0) {
         // Verify the device version is in the supported range
         switch (info.device_version) {
             case CAMERA_DEVICE_API_VERSION_1_0:
@@ -370,6 +360,20 @@
             case CAMERA_DEVICE_API_VERSION_3_5:
                 // in support
                 break;
+            case CAMERA_DEVICE_API_VERSION_3_6:
+                /**
+                 * ICameraDevice@3.5 contains APIs from both
+                 * CAMERA_DEVICE_API_VERSION_3_6 and CAMERA_MODULE_API_VERSION_2_5
+                 * so we require HALs to uprev both for simplified supported combinations.
+                 * HAL can still opt in individual new APIs indepedently.
+                 */
+                if (moduleVersion < CAMERA_MODULE_API_VERSION_2_5) {
+                    ALOGE("%s: Device %d has unsupported version combination:"
+                            "HAL version %x and module version %x",
+                            __FUNCTION__, id, info.device_version, moduleVersion);
+                    return NO_INIT;
+                }
+                break;
             case CAMERA_DEVICE_API_VERSION_2_0:
             case CAMERA_DEVICE_API_VERSION_2_1:
             case CAMERA_DEVICE_API_VERSION_3_0:
@@ -385,7 +389,7 @@
     return OK;
 }
 
-bool CameraProvider::setUpVendorTags() {
+bool LegacyCameraProviderImpl_2_4::setUpVendorTags() {
     ATRACE_CALL();
     vendor_tag_ops_t vOps = vendor_tag_ops_t();
 
@@ -442,11 +446,8 @@
 }
 
 // Methods from ::android::hardware::camera::provider::V2_4::ICameraProvider follow.
-Return<Status> CameraProvider::setCallback(const sp<ICameraProviderCallback>& callback)  {
-    if (callback == nullptr) {
-        return Status::ILLEGAL_ARGUMENT;
-    }
-
+Return<Status> LegacyCameraProviderImpl_2_4::setCallback(
+        const sp<ICameraProviderCallback>& callback) {
     Mutex::Autolock _l(mCbLock);
     mCallbacks = callback;
 
@@ -462,12 +463,14 @@
     return Status::OK;
 }
 
-Return<void> CameraProvider::getVendorTags(getVendorTags_cb _hidl_cb)  {
+Return<void> LegacyCameraProviderImpl_2_4::getVendorTags(
+        ICameraProvider::getVendorTags_cb _hidl_cb) {
     _hidl_cb(Status::OK, mVendorTagSections);
     return Void();
 }
 
-Return<void> CameraProvider::getCameraIdList(getCameraIdList_cb _hidl_cb)  {
+Return<void> LegacyCameraProviderImpl_2_4::getCameraIdList(
+        ICameraProvider::getCameraIdList_cb _hidl_cb) {
     std::vector<hidl_string> deviceNameList;
     for (auto const& deviceNamePair : mCameraDeviceNames) {
         if (std::stoi(deviceNamePair.first) >= mNumberOfLegacyCameras) {
@@ -484,14 +487,16 @@
     return Void();
 }
 
-Return<void> CameraProvider::isSetTorchModeSupported(isSetTorchModeSupported_cb _hidl_cb) {
+Return<void> LegacyCameraProviderImpl_2_4::isSetTorchModeSupported(
+        ICameraProvider::isSetTorchModeSupported_cb _hidl_cb) {
     bool support = mModule->isSetTorchModeSupported();
     _hidl_cb (Status::OK, support);
     return Void();
 }
 
-Return<void> CameraProvider::getCameraDeviceInterface_V1_x(
-        const hidl_string& cameraDeviceName, getCameraDeviceInterface_V1_x_cb _hidl_cb)  {
+Return<void> LegacyCameraProviderImpl_2_4::getCameraDeviceInterface_V1_x(
+        const hidl_string& cameraDeviceName,
+        ICameraProvider::getCameraDeviceInterface_V1_x_cb _hidl_cb)  {
     std::string cameraId, deviceVersion;
     bool match = matchDeviceName(cameraDeviceName, &deviceVersion, &cameraId);
     if (!match) {
@@ -543,8 +548,9 @@
     return Void();
 }
 
-Return<void> CameraProvider::getCameraDeviceInterface_V3_x(
-        const hidl_string& cameraDeviceName, getCameraDeviceInterface_V3_x_cb _hidl_cb)  {
+Return<void> LegacyCameraProviderImpl_2_4::getCameraDeviceInterface_V3_x(
+        const hidl_string& cameraDeviceName,
+        ICameraProvider::getCameraDeviceInterface_V3_x_cb _hidl_cb)  {
     std::string cameraId, deviceVersion;
     bool match = matchDeviceName(cameraDeviceName, &deviceVersion, &cameraId);
     if (!match) {
@@ -575,10 +581,11 @@
         return Void();
     }
 
-    // ICameraDevice 3.4 or upper
     sp<android::hardware::camera::device::V3_2::implementation::CameraDevice> deviceImpl;
+
+    // ICameraDevice 3.4 or upper
     if (deviceVersion >= kHAL3_4) {
-        ALOGV("Constructing v3.4 camera device");
+        ALOGV("Constructing v3.4+ camera device");
         if (deviceVersion == kHAL3_4) {
             deviceImpl = new android::hardware::camera::device::V3_4::implementation::CameraDevice(
                     mModule, cameraId, mCameraDeviceNames);
@@ -641,27 +648,6 @@
     return Void();
 }
 
-ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name) {
-    if (strcmp(name, kLegacyProviderName) == 0) {
-        CameraProvider* provider = new CameraProvider();
-        if (provider == nullptr) {
-            ALOGE("%s: cannot allocate camera provider!", __FUNCTION__);
-            return nullptr;
-        }
-        if (provider->isInitFailed()) {
-            ALOGE("%s: camera provider init failed!", __FUNCTION__);
-            delete provider;
-            return nullptr;
-        }
-        return provider;
-    } else if (strcmp(name, kExternalProviderName) == 0) {
-        ExternalCameraProvider* provider = new ExternalCameraProvider();
-        return provider;
-    }
-    ALOGE("%s: unknown instance name: %s", __FUNCTION__, name);
-    return nullptr;
-}
-
 } // namespace implementation
 }  // namespace V2_4
 }  // namespace provider
diff --git a/camera/provider/2.4/default/CameraProvider.h b/camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.h
similarity index 79%
rename from camera/provider/2.4/default/CameraProvider.h
rename to camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.h
index 0f0959f..b4914b3 100644
--- a/camera/provider/2.4/default/CameraProvider.h
+++ b/camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.h
@@ -14,16 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_CAMERAPROVIDER_H
-#define ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_CAMERAPROVIDER_H
+#ifndef ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_LEGACYCAMERAPROVIDER_H
+#define ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_LEGACYCAMERAPROVIDER_H
 
-#include <regex>
+#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
 #include "hardware/camera_common.h"
 #include "utils/Mutex.h"
 #include "utils/SortedVector.h"
-#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
-#include <hidl/Status.h>
-#include <hidl/MQDescriptor.h>
+
 #include "CameraModule.h"
 #include "VendorTagDescriptor.h"
 
@@ -50,26 +48,34 @@
 using ::android::sp;
 using ::android::Mutex;
 
-struct CameraProvider : public ICameraProvider, public camera_module_callbacks_t {
-    CameraProvider();
-    ~CameraProvider();
+/**
+ * The implementation of legacy wrapper CameraProvider 2.4, separated
+ * from the HIDL interface layer to allow for implementation reuse by later
+ * provider versions.
+ *
+ * This implementation supports cameras implemented via the legacy libhardware
+ * camera HAL definitions.
+ */
+struct LegacyCameraProviderImpl_2_4 : public camera_module_callbacks_t {
+    LegacyCameraProviderImpl_2_4();
+    ~LegacyCameraProviderImpl_2_4();
 
     // Caller must use this method to check if CameraProvider ctor failed
     bool isInitFailed() { return mInitFailed; }
 
     // Methods from ::android::hardware::camera::provider::V2_4::ICameraProvider follow.
-    Return<Status> setCallback(const sp<ICameraProviderCallback>& callback) override;
-    Return<void> getVendorTags(getVendorTags_cb _hidl_cb) override;
-    Return<void> getCameraIdList(getCameraIdList_cb _hidl_cb) override;
-    Return<void> isSetTorchModeSupported(isSetTorchModeSupported_cb _hidl_cb) override;
+    Return<Status> setCallback(const sp<ICameraProviderCallback>& callback);
+    Return<void> getVendorTags(ICameraProvider::getVendorTags_cb _hidl_cb);
+    Return<void> getCameraIdList(ICameraProvider::getCameraIdList_cb _hidl_cb);
+    Return<void> isSetTorchModeSupported(ICameraProvider::isSetTorchModeSupported_cb _hidl_cb);
     Return<void> getCameraDeviceInterface_V1_x(
             const hidl_string& cameraDeviceName,
-            getCameraDeviceInterface_V1_x_cb _hidl_cb) override;
+            ICameraProvider::getCameraDeviceInterface_V1_x_cb _hidl_cb);
     Return<void> getCameraDeviceInterface_V3_x(
             const hidl_string& cameraDeviceName,
-            getCameraDeviceInterface_V3_x_cb _hidl_cb) override;
+            ICameraProvider::getCameraDeviceInterface_V3_x_cb _hidl_cb);
 
-private:
+protected:
     Mutex mCbLock;
     sp<ICameraProviderCallback> mCallbacks = nullptr;
 
@@ -98,7 +104,6 @@
 
     // extract legacy camera ID/device version from a HIDL device name
     static std::string getLegacyCameraId(const hidl_string& deviceName);
-    static int getCameraDeviceVersion(const hidl_string& deviceName);
 
     // convert conventional HAL status to HIDL Status
     static Status getHidlStatus(int);
@@ -116,9 +121,8 @@
     void addDeviceNames(int camera_id, CameraDeviceStatus status = CameraDeviceStatus::PRESENT,
                         bool cam_new = false);
     void removeDeviceNames(int camera_id);
-};
 
-extern "C" ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name);
+};
 
 }  // namespace implementation
 }  // namespace V2_4
@@ -127,4 +131,4 @@
 }  // namespace hardware
 }  // namespace android
 
-#endif  // ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_CAMERAPROVIDER_H
+#endif  // ANDROID_HARDWARE_CAMERA_PROVIDER_V2_4_LEGACYCAMERAPROVIDER_H
diff --git a/camera/provider/2.4/default/service.cpp b/camera/provider/2.4/default/service.cpp
index 15d0ea6..4475f7d 100644
--- a/camera/provider/2.4/default/service.cpp
+++ b/camera/provider/2.4/default/service.cpp
@@ -21,9 +21,8 @@
 #endif
 
 #include <android/hardware/camera/provider/2.4/ICameraProvider.h>
-#include <hidl/LegacySupport.h>
-
 #include <binder/ProcessState.h>
+#include <hidl/LegacySupport.h>
 
 using android::status_t;
 using android::hardware::defaultLazyPassthroughServiceImplementation;
@@ -38,7 +37,7 @@
 
 int main()
 {
-    ALOGI("Camera provider Service is starting.");
+    ALOGI("CameraProvider@2.4 legacy service is starting.");
     // The camera HAL may communicate to other vendor components via
     // /dev/vndbinder
     android::ProcessState::initWithDriver("/dev/vndbinder");
diff --git a/camera/provider/2.4/vts/functional/Android.bp b/camera/provider/2.4/vts/functional/Android.bp
index f8f058e..5fb1fd8 100644
--- a/camera/provider/2.4/vts/functional/Android.bp
+++ b/camera/provider/2.4/vts/functional/Android.bp
@@ -40,6 +40,7 @@
         "android.hardware.camera.device@3.5",
 	"android.hardware.camera.metadata@3.4",
         "android.hardware.camera.provider@2.4",
+        "android.hardware.camera.provider@2.5",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.common@1.0",
         "android.hardware.graphics.mapper@2.0",
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 211240a..3949346 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -35,6 +35,7 @@
 #include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h>
 #include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
 #include <android/hardware/camera/provider/2.4/ICameraProvider.h>
+#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
 #include <android/hardware/camera/metadata/3.4/types.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <binder/MemoryHeapBase.h>
@@ -65,6 +66,7 @@
 using namespace ::android::hardware::camera::device;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
+using ::android::hardware::hidl_bitfield;
 using ::android::hardware::hidl_handle;
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
@@ -543,6 +545,9 @@
 
      uint32_t id;
      ASSERT_TRUE(parseProviderName(service_name, &mProviderType, &id));
+
+     castProvider(mProvider, &mProvider2_5);
+     notifyDeviceState(provider::V2_5::DeviceState::NORMAL);
  }
  virtual void TearDown() override {}
 
@@ -680,6 +685,8 @@
         CameraHidlTest *mParent;               // Parent object
     };
 
+    void notifyDeviceState(::android::hardware::camera::provider::V2_5::DeviceState newState);
+
     void openCameraDevice(const std::string &name, sp<ICameraProvider> provider,
             sp<::android::hardware::camera::device::V1_0::ICameraDevice> *device /*out*/);
     void setupPreviewWindow(
@@ -709,6 +716,8 @@
             sp<ICameraDeviceSession> *session /*out*/,
             camera_metadata_t **staticMeta /*out*/,
             ::android::sp<ICameraDevice> *device = nullptr/*out*/);
+    void castProvider(const sp<provider::V2_4::ICameraProvider> &provider,
+            sp<provider::V2_5::ICameraProvider> *provider2_5 /*out*/);
     void castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion,
             sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/,
             sp<device::V3_4::ICameraDeviceSession> *session3_4 /*out*/,
@@ -770,6 +779,9 @@
             hidl_vec<int32_t> streamIds, sp<DeviceCb> cb,
             uint32_t streamConfigCounter = 0);
 
+    void verifySessionReconfigurationQuery(sp<device::V3_5::ICameraDeviceSession> session3_5,
+            camera_metadata* oldSessionParams, camera_metadata* newSessionParams);
+
     static Status getAvailableOutputStreams(camera_metadata_t *staticMeta,
             std::vector<AvailableStream> &outputStreams,
             const AvailableStream *threshold = nullptr);
@@ -898,6 +910,8 @@
 
     // Camera provider service
     sp<ICameraProvider> mProvider;
+    sp<::android::hardware::camera::provider::V2_5::ICameraProvider> mProvider2_5;
+
     // Camera provider type.
     std::string mProviderType;
 };
@@ -3292,7 +3306,8 @@
         }
 
         android::hardware::camera::common::V1_0::helper::CameraMetadata previewRequestSettings;
-        android::hardware::camera::common::V1_0::helper::CameraMetadata sessionParams;
+        android::hardware::camera::common::V1_0::helper::CameraMetadata sessionParams,
+                modifiedSessionParams;
         constructFilteredSettings(session, availableSessionKeys, RequestTemplate::PREVIEW,
                 &previewRequestSettings, &sessionParams);
         if (sessionParams.isEmpty()) {
@@ -3323,13 +3338,28 @@
         ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
         config.streams = streams;
         config.operationMode = StreamConfigurationMode::NORMAL_MODE;
-        const camera_metadata_t *sessionParamsBuffer = sessionParams.getAndLock();
-        config.sessionParams.setToExternal(
-                reinterpret_cast<uint8_t *> (const_cast<camera_metadata_t *> (sessionParamsBuffer)),
+        modifiedSessionParams = sessionParams;
+        auto sessionParamsBuffer = sessionParams.release();
+        config.sessionParams.setToExternal(reinterpret_cast<uint8_t *> (sessionParamsBuffer),
                 get_camera_metadata_size(sessionParamsBuffer));
         config3_5.v3_4 = config;
         config3_5.streamConfigCounter = 0;
         if (session3_5 != nullptr) {
+            bool newSessionParamsAvailable = false;
+            for (const auto& it : availableSessionKeys) {
+                if (modifiedSessionParams.exists(it)) {
+                    modifiedSessionParams.erase(it);
+                    newSessionParamsAvailable = true;
+                    break;
+                }
+            }
+            if (newSessionParamsAvailable) {
+                auto modifiedSessionParamsBuffer = modifiedSessionParams.release();
+                verifySessionReconfigurationQuery(session3_5, sessionParamsBuffer,
+                        modifiedSessionParamsBuffer);
+                modifiedSessionParams.acquire(modifiedSessionParamsBuffer);
+            }
+
             ret = session3_5->configureStreams_3_5(config3_5,
                     [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                         ASSERT_EQ(Status::OK, s);
@@ -3342,7 +3372,7 @@
                         ASSERT_EQ(1u, halConfig.streams.size());
                     });
         }
-
+        sessionParams.acquire(sessionParamsBuffer);
         ASSERT_TRUE(ret.isOk());
 
         free_camera_metadata(staticMetaBuffer);
@@ -4681,6 +4711,13 @@
     }
 }
 
+// Test camera provider@2.5 notify method
+TEST_F(CameraHidlTest, providerDeviceStateNotification) {
+
+    notifyDeviceState(provider::V2_5::DeviceState::BACK_COVERED);
+    notifyDeviceState(provider::V2_5::DeviceState::NORMAL);
+}
+
 // Retrieve all valid output stream resolutions from the camera
 // static characteristics.
 Status CameraHidlTest::getAvailableOutputStreams(camera_metadata_t *staticMeta,
@@ -5362,6 +5399,16 @@
     }
 }
 
+//Cast camera provider to corresponding version if available
+void CameraHidlTest::castProvider(const sp<ICameraProvider> &provider,
+        sp<provider::V2_5::ICameraProvider> *provider2_5 /*out*/) {
+    ASSERT_NE(nullptr, provider2_5);
+    auto castResult = provider::V2_5::ICameraProvider::castFrom(provider);
+    if (castResult.isOk()) {
+        *provider2_5 = castResult;
+    }
+}
+
 //Cast camera device session to corresponding version
 void CameraHidlTest::castSession(const sp<ICameraDeviceSession> &session, int32_t deviceVersion,
         sp<device::V3_3::ICameraDeviceSession> *session3_3 /*out*/,
@@ -5539,6 +5586,41 @@
         ADD_FAILURE() << "ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS"
             << " per API contract should never be set by Hal!";
     }
+
+    retcode = find_camera_metadata_ro_entry(metadata,
+            ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, &entry);
+    if (0 == retcode || entry.count > 0) {
+        ADD_FAILURE() << "ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS "
+            << " per API contract should never be set by Hal!";
+    }
+
+    retcode = find_camera_metadata_ro_entry(metadata,
+            ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS, &entry);
+    if (0 == retcode || entry.count > 0) {
+        ADD_FAILURE() << "ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS "
+            << " per API contract should never be set by Hal!";
+    }
+
+    retcode = find_camera_metadata_ro_entry(metadata,
+            ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS, &entry);
+    if (0 == retcode || entry.count > 0) {
+        ADD_FAILURE() << "ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS "
+            << " per API contract should never be set by Hal!";
+    }
+
+    retcode = find_camera_metadata_ro_entry(metadata,
+            ANDROID_HEIC_INFO_SUPPORTED, &entry);
+    if (0 == retcode && entry.count > 0) {
+        retcode = find_camera_metadata_ro_entry(metadata,
+            ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT, &entry);
+        if (0 == retcode && entry.count > 0) {
+            uint8_t maxJpegAppSegmentsCount = entry.data.u8[0];
+            ASSERT_TRUE(maxJpegAppSegmentsCount >= 1 &&
+                    maxJpegAppSegmentsCount <= 16);
+        } else {
+            ADD_FAILURE() << "Get Heic maxJpegAppSegmentsCount failed!";
+        }
+    }
 }
 
 void CameraHidlTest::verifyMonochromeCharacteristics(const CameraMetadata& chars,
@@ -5774,6 +5856,13 @@
     ASSERT_TRUE(ret.isOk());
 }
 
+void CameraHidlTest::notifyDeviceState(provider::V2_5::DeviceState newState) {
+    if (mProvider2_5.get() == nullptr) return;
+
+    mProvider2_5->notifyDeviceStateChange(
+            static_cast<hidl_bitfield<provider::V2_5::DeviceState>>(newState));
+}
+
 // Open a particular camera device.
 void CameraHidlTest::openCameraDevice(const std::string &name,
         sp<ICameraProvider> provider,
@@ -6026,6 +6115,35 @@
     }
 }
 
+void CameraHidlTest::verifySessionReconfigurationQuery(
+        sp<device::V3_5::ICameraDeviceSession> session3_5, camera_metadata* oldSessionParams,
+        camera_metadata* newSessionParams) {
+    ASSERT_NE(nullptr, session3_5.get());
+    ASSERT_NE(nullptr, oldSessionParams);
+    ASSERT_NE(nullptr, newSessionParams);
+
+    android::hardware::hidl_vec<uint8_t> oldParams, newParams;
+    oldParams.setToExternal(reinterpret_cast<uint8_t*>(oldSessionParams),
+            get_camera_metadata_size(oldSessionParams));
+    newParams.setToExternal(reinterpret_cast<uint8_t*>(newSessionParams),
+            get_camera_metadata_size(newSessionParams));
+    android::hardware::camera::common::V1_0::Status callStatus;
+    auto hidlCb = [&callStatus] (android::hardware::camera::common::V1_0::Status s,
+            bool /*requiredFlag*/) {
+        callStatus = s;
+    };
+    auto ret = session3_5->isReconfigurationRequired(oldParams, newParams, hidlCb);
+    ASSERT_TRUE(ret.isOk());
+    switch (callStatus) {
+        case android::hardware::camera::common::V1_0::Status::OK:
+        case android::hardware::camera::common::V1_0::Status::METHOD_NOT_SUPPORTED:
+            break;
+        case android::hardware::camera::common::V1_0::Status::INTERNAL_ERROR:
+        default:
+            ADD_FAILURE() << "Query calllback failed";
+    }
+}
+
 int main(int argc, char **argv) {
   ::testing::AddGlobalTestEnvironment(CameraHidlEnvironment::Instance());
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/camera/provider/2.5/Android.bp b/camera/provider/2.5/Android.bp
new file mode 100644
index 0000000..3de010a
--- /dev/null
+++ b/camera/provider/2.5/Android.bp
@@ -0,0 +1,25 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.camera.provider@2.5",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "types.hal",
+        "ICameraProvider.hal",
+    ],
+    interfaces: [
+        "android.hardware.camera.common@1.0",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.provider@2.4",
+        "android.hidl.base@1.0",
+    ],
+    types: [
+        "DeviceState",
+    ],
+    gen_java: false,
+}
+
diff --git a/camera/provider/2.5/ICameraProvider.hal b/camera/provider/2.5/ICameraProvider.hal
new file mode 100644
index 0000000..b4cda6a
--- /dev/null
+++ b/camera/provider/2.5/ICameraProvider.hal
@@ -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.
+ */
+
+package android.hardware.camera.provider@2.5;
+
+import android.hardware.camera.common@1.0::Status;
+import android.hardware.camera.provider@2.4::ICameraProvider;
+
+/**
+ * Camera provider HAL
+ *
+ * Version 2.5 adds support for the notifyDeviceStateChange method
+ */
+interface ICameraProvider extends @2.4::ICameraProvider {
+
+    /**
+     * notifyDeviceStateChange:
+     *
+     * Notify the HAL provider that the state of the overall device has
+     * changed in some way that the HAL may want to know about.
+     *
+     * For example, a physical shutter may have been uncovered or covered,
+     * or a camera may have been covered or uncovered by an add-on keyboard
+     * or other accessory.
+     *
+     * The state is a bitfield of potential states, and some physical configurations
+     * could plausibly correspond to multiple different combinations of state bits.
+     * The HAL must ignore any state bits it is not actively using to determine
+     * the appropriate camera configuration.
+     *
+     * For example, on some devices the FOLDED state could mean that
+     * backward-facing cameras are covered by the fold, so FOLDED by itself implies
+     * BACK_COVERED. But other devices may support folding but not cover any cameras
+     * when folded, so for those FOLDED would not imply any of the other flags.
+     * Since these relationships are very device-specific, it is difficult to specify
+     * a comprehensive policy.  But as a recommendation, it is suggested that if a flag
+     * necessarily implies other flags are set as well, then those flags should be set.
+     * So even though FOLDED would be enough to infer BACK_COVERED on some devices, the
+     * BACK_COVERED flag should also be set for clarity.
+     *
+     * This method may be invoked by the HAL client at any time. It must not
+     * cause any active camera device sessions to be closed, but may dynamically
+     * change which physical camera a logical multi-camera is using for its
+     * active and future output.
+     *
+     * The method must be invoked by the HAL client at least once before the
+     * client calls ICameraDevice::open on any camera device interfaces listed
+     * by this provider, to establish the initial device state.
+     *
+     * @param newState
+     *    The new state of the device.
+     */
+    notifyDeviceStateChange(bitfield<DeviceState> newState);
+
+};
diff --git a/camera/provider/2.5/default/Android.bp b/camera/provider/2.5/default/Android.bp
new file mode 100644
index 0000000..cd1caeb
--- /dev/null
+++ b/camera/provider/2.5/default/Android.bp
@@ -0,0 +1,187 @@
+cc_library_shared {
+    name: "android.hardware.camera.provider@2.5-legacy",
+    proprietary: true,
+    srcs: ["LegacyCameraProviderImpl_2_5.cpp"],
+    shared_libs: [
+        "android.hardware.camera.common@1.0",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
+        "android.hardware.camera.device@3.4",
+        "android.hardware.camera.device@3.5",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.camera.provider@2.4-legacy",
+        "android.hardware.camera.provider@2.5",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "camera.device@1.0-impl",
+        "camera.device@3.2-impl",
+        "camera.device@3.3-impl",
+        "camera.device@3.4-impl",
+        "camera.device@3.5-impl",
+        "libcamera_metadata",
+        "libcutils",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "android.hardware.camera.common@1.0-helper",
+    ],
+    header_libs: [
+        "camera.device@3.4-impl_headers",
+        "camera.device@3.5-impl_headers",
+    ],
+    export_include_dirs: ["."],
+}
+
+cc_library_shared {
+    name: "android.hardware.camera.provider@2.5-external",
+    proprietary: true,
+    srcs: ["ExternalCameraProviderImpl_2_5.cpp"],
+    shared_libs: [
+        "android.hardware.camera.common@1.0",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
+        "android.hardware.camera.device@3.4",
+        "android.hardware.camera.device@3.5",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.camera.provider@2.4-external",
+        "android.hardware.camera.provider@2.5",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "camera.device@3.3-impl",
+        "camera.device@3.4-external-impl",
+        "camera.device@3.4-impl",
+        "camera.device@3.5-external-impl",
+        "camera.device@3.5-impl",
+        "libcamera_metadata",
+        "libcutils",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libtinyxml2",
+        "libutils",
+    ],
+    static_libs: [
+        "android.hardware.camera.common@1.0-helper",
+    ],
+    header_libs: [
+        "camera.device@3.4-external-impl_headers",
+        "camera.device@3.5-external-impl_headers"
+    ],
+    export_include_dirs: ["."],
+}
+
+cc_defaults {
+    name: "camera_service_2_5_defaults",
+    defaults: ["hidl_defaults"],
+    proprietary: true,
+    relative_install_path: "hw",
+    srcs: ["service.cpp"],
+    shared_libs: [
+        "android.hardware.camera.common@1.0",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
+        "android.hardware.camera.device@3.4",
+        "android.hardware.camera.device@3.5",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.camera.provider@2.4-legacy",
+        "android.hardware.camera.provider@2.5",
+        "android.hardware.camera.provider@2.5-legacy",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libbinder",
+        "libcamera_metadata",
+        "libhardware",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "android.hardware.camera.common@1.0-helper",
+    ],
+    header_libs: [
+        "camera.device@3.4-impl_headers",
+        "camera.device@3.5-impl_headers"
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.camera.provider@2.5-service",
+    defaults: ["camera_service_2_5_defaults"],
+    compile_multilib: "32",
+    init_rc: ["android.hardware.camera.provider@2.5-service.rc"],
+}
+
+cc_binary {
+    name: "android.hardware.camera.provider@2.5-service_64",
+    defaults: ["camera_service_2_5_defaults"],
+    compile_multilib: "64",
+    init_rc: ["android.hardware.camera.provider@2.5-service_64.rc"],
+}
+
+cc_binary {
+    name: "android.hardware.camera.provider@2.5-service-lazy",
+    overrides: ["android.hardware.camera.provider@2.5-service"],
+    defaults: ["camera_service_2_5_defaults"],
+    compile_multilib: "32",
+    init_rc: ["android.hardware.camera.provider@2.5-service-lazy.rc"],
+    cflags: ["-DLAZY_SERVICE"],
+}
+
+cc_binary {
+    name: "android.hardware.camera.provider@2.5-service-lazy_64",
+    overrides: ["android.hardware.camera.provider@2.5-service_64"],
+    defaults: ["camera_service_2_5_defaults"],
+    compile_multilib: "64",
+    init_rc: ["android.hardware.camera.provider@2.5-service-lazy_64.rc"],
+    cflags: ["-DLAZY_SERVICE"],
+}
+
+cc_binary {
+    name: "android.hardware.camera.provider@2.5-external-service",
+    defaults: ["hidl_defaults"],
+    proprietary: true,
+    relative_install_path: "hw",
+    srcs: ["external-service.cpp"],
+    compile_multilib: "32",
+    init_rc: ["android.hardware.camera.provider@2.5-external-service.rc"],
+    shared_libs: [
+        "android.hardware.camera.common@1.0",
+        "android.hardware.camera.device@1.0",
+        "android.hardware.camera.device@3.2",
+        "android.hardware.camera.device@3.3",
+        "android.hardware.camera.device@3.4",
+        "android.hardware.camera.device@3.5",
+        "android.hardware.camera.provider@2.4",
+        "android.hardware.camera.provider@2.4-external",
+        "android.hardware.camera.provider@2.5",
+        "android.hardware.camera.provider@2.5-external",
+        "android.hardware.graphics.mapper@2.0",
+        "libbinder",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libtinyxml2",
+        "libutils",
+    ],
+    static_libs: [
+        "android.hardware.camera.common@1.0-helper",
+    ],
+    header_libs: [
+        "camera.device@3.4-external-impl_headers",
+        "camera.device@3.4-impl_headers",
+        "camera.device@3.5-external-impl_headers",
+        "camera.device@3.5-impl_headers",
+    ],
+}
diff --git a/camera/provider/2.5/default/CameraProvider_2_5.h b/camera/provider/2.5/default/CameraProvider_2_5.h
new file mode 100644
index 0000000..d0f1dda
--- /dev/null
+++ b/camera/provider/2.5/default/CameraProvider_2_5.h
@@ -0,0 +1,94 @@
+/*
+ * 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_HARDWARE_CAMERA_PROVIDER_V2_5_CAMERAPROVIDER_H
+#define ANDROID_HARDWARE_CAMERA_PROVIDER_V2_5_CAMERAPROVIDER_H
+
+#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
+#include <hidl/Status.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace provider {
+namespace V2_5 {
+namespace implementation {
+
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::provider::V2_5::ICameraProvider;
+using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_string;
+using ::android::sp;
+
+// Default recommended RPC thread count for camera provider implementations
+const int HWBINDER_THREAD_COUNT = 6;
+
+template<typename IMPL>
+struct CameraProvider : public ICameraProvider {
+    CameraProvider() : impl() {}
+    ~CameraProvider() {}
+
+    // Caller must use this method to check if CameraProvider ctor failed
+    bool isInitFailed() { return impl.isInitFailed(); }
+
+    // Methods from ::android::hardware::camera::provider::V2_4::ICameraProvider follow.
+    Return<Status> setCallback(const sp<ICameraProviderCallback>& callback) override {
+        return impl.setCallback(callback);
+    }
+
+    Return<void> getVendorTags(getVendorTags_cb _hidl_cb) override {
+        return impl.getVendorTags(_hidl_cb);
+    }
+
+    Return<void> getCameraIdList(getCameraIdList_cb _hidl_cb) override {
+        return impl.getCameraIdList(_hidl_cb);
+    }
+
+    Return<void> isSetTorchModeSupported(isSetTorchModeSupported_cb _hidl_cb) override {
+        return impl.isSetTorchModeSupported(_hidl_cb);
+    }
+
+    Return<void> getCameraDeviceInterface_V1_x(
+            const hidl_string& cameraDeviceName,
+            getCameraDeviceInterface_V1_x_cb _hidl_cb) override {
+        return impl.getCameraDeviceInterface_V1_x(cameraDeviceName, _hidl_cb);
+    }
+
+    Return<void> getCameraDeviceInterface_V3_x(
+            const hidl_string& cameraDeviceName,
+            getCameraDeviceInterface_V3_x_cb _hidl_cb) override {
+        return impl.getCameraDeviceInterface_V3_x(cameraDeviceName, _hidl_cb);
+    }
+
+    // Methods from ::android::hardware::camera::provider::V2_5::ICameraProvider follow.
+    Return<void> notifyDeviceStateChange(hardware::hidl_bitfield<DeviceState> newState) override {
+        return impl.notifyDeviceStateChange(newState);
+    }
+
+private:
+    IMPL impl;
+};
+
+}  // namespace implementation
+}  // namespace V2_5
+}  // namespace provider
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAMERA_PROVIDER_V2_5_CAMERAPROVIDER_H
diff --git a/camera/provider/2.5/default/ExternalCameraProviderImpl_2_5.cpp b/camera/provider/2.5/default/ExternalCameraProviderImpl_2_5.cpp
new file mode 100644
index 0000000..87851cd
--- /dev/null
+++ b/camera/provider/2.5/default/ExternalCameraProviderImpl_2_5.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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 "CamPrvdr@2.5-external"
+//#define LOG_NDEBUG 0
+#include <log/log.h>
+
+#include "ExternalCameraProviderImpl_2_5.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace provider {
+namespace V2_5 {
+namespace implementation {
+
+ExternalCameraProviderImpl_2_5::ExternalCameraProviderImpl_2_5() :
+        ExternalCameraProviderImpl_2_4() {
+}
+
+ExternalCameraProviderImpl_2_5::~ExternalCameraProviderImpl_2_5() {
+}
+
+Return<void> ExternalCameraProviderImpl_2_5::notifyDeviceStateChange(
+        hidl_bitfield<DeviceState> /*newState*/) {
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_5
+}  // namespace provider
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
diff --git a/camera/provider/2.5/default/ExternalCameraProviderImpl_2_5.h b/camera/provider/2.5/default/ExternalCameraProviderImpl_2_5.h
new file mode 100644
index 0000000..eeaa7cc
--- /dev/null
+++ b/camera/provider/2.5/default/ExternalCameraProviderImpl_2_5.h
@@ -0,0 +1,62 @@
+/*
+ * 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_HARDWARE_CAMERA_PROVIDER_V2_5_EXTCAMERAPROVIDER_H
+#define ANDROID_HARDWARE_CAMERA_PROVIDER_V2_5_EXTCAMERAPROVIDER_H
+
+#include <ExternalCameraProviderImpl_2_4.h>
+
+#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
+#include <hidl/Status.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace provider {
+namespace V2_5 {
+namespace implementation {
+
+using namespace ::android::hardware::camera::provider;
+
+using ::android::hardware::camera::common::V1_0::CameraDeviceStatus;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::VendorTagSection;
+using ::android::hardware::camera::external::common::ExternalCameraConfig;
+using ::android::hardware::camera::provider::V2_5::ICameraProvider;
+using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_bitfield;
+
+struct ExternalCameraProviderImpl_2_5 :
+        public V2_4::implementation::ExternalCameraProviderImpl_2_4 {
+    ExternalCameraProviderImpl_2_5();
+    ~ExternalCameraProviderImpl_2_5();
+
+    // Methods from ::android::hardware::camera::provider::V2_5::ICameraProvider follow.
+    Return<void> notifyDeviceStateChange(hidl_bitfield<DeviceState> newState);
+private:
+};
+
+}  // namespace implementation
+}  // namespace V2_5
+}  // namespace provider
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAMERA_PROVIDER_V2_5_EXTCAMERAPROVIDER_H
diff --git a/camera/provider/2.5/default/LegacyCameraProviderImpl_2_5.cpp b/camera/provider/2.5/default/LegacyCameraProviderImpl_2_5.cpp
new file mode 100644
index 0000000..5233397
--- /dev/null
+++ b/camera/provider/2.5/default/LegacyCameraProviderImpl_2_5.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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_TAG "CamPrvdr@2.5-legacy"
+//#define LOG_NDEBUG 0
+#include <android/log.h>
+#include <inttypes.h>
+
+#include "LegacyCameraProviderImpl_2_5.h"
+#include "CameraProvider_2_5.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace provider {
+namespace V2_5 {
+namespace implementation {
+
+template struct CameraProvider<LegacyCameraProviderImpl_2_5>;
+
+LegacyCameraProviderImpl_2_5::LegacyCameraProviderImpl_2_5() :
+        LegacyCameraProviderImpl_2_4() {
+}
+
+LegacyCameraProviderImpl_2_5::~LegacyCameraProviderImpl_2_5() {}
+
+Return<void> LegacyCameraProviderImpl_2_5::notifyDeviceStateChange(
+        hidl_bitfield<DeviceState> newState) {
+    ALOGD("%s: New device state: 0x%" PRIx64, __FUNCTION__, newState);
+    uint64_t state = static_cast<uint64_t>(newState);
+    mModule->notifyDeviceStateChange(state);
+    return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_5
+}  // namespace provider
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
diff --git a/camera/provider/2.5/default/LegacyCameraProviderImpl_2_5.h b/camera/provider/2.5/default/LegacyCameraProviderImpl_2_5.h
new file mode 100644
index 0000000..62dd97f
--- /dev/null
+++ b/camera/provider/2.5/default/LegacyCameraProviderImpl_2_5.h
@@ -0,0 +1,62 @@
+/*
+ * 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_HARDWARE_CAMERA_PROVIDER_V2_5_LEGACYCAMERAPROVIDER_H
+#define ANDROID_HARDWARE_CAMERA_PROVIDER_V2_5_LEGACYCAMERAPROVIDER_H
+
+#include <LegacyCameraProviderImpl_2_4.h>
+
+#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
+#include <hidl/Status.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace provider {
+namespace V2_5 {
+namespace implementation {
+
+using namespace ::android::hardware::camera::provider;
+
+using ::android::hardware::camera::common::V1_0::CameraDeviceStatus;
+using ::android::hardware::camera::common::V1_0::Status;
+using ::android::hardware::camera::common::V1_0::TorchModeStatus;
+using ::android::hardware::camera::common::V1_0::VendorTag;
+using ::android::hardware::camera::common::V1_0::VendorTagSection;
+using ::android::hardware::camera::common::V1_0::helper::CameraModule;
+using ::android::hardware::camera::common::V1_0::helper::VendorTagDescriptor;
+using ::android::hardware::camera::provider::V2_5::DeviceState;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::Return;
+
+struct LegacyCameraProviderImpl_2_5 : public V2_4::implementation::LegacyCameraProviderImpl_2_4 {
+    LegacyCameraProviderImpl_2_5();
+    ~LegacyCameraProviderImpl_2_5();
+
+    // Methods from ::android::hardware::camera::provider::V2_5::ICameraProvider follow.
+    Return<void> notifyDeviceStateChange(hidl_bitfield<DeviceState> newState);
+private:
+};
+
+}  // namespace implementation
+}  // namespace V2_5
+}  // namespace provider
+}  // namespace camera
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAMERA_PROVIDER_V2_5_CAMERAPROVIDER_H
diff --git a/camera/provider/2.5/default/OWNERS b/camera/provider/2.5/default/OWNERS
new file mode 100644
index 0000000..369b204
--- /dev/null
+++ b/camera/provider/2.5/default/OWNERS
@@ -0,0 +1,7 @@
+cychen@google.com
+epeev@google.com
+etalvala@google.com
+jchowdhary@google.com
+shuzhenwang@google.com
+yinchiayeh@google.com
+zhijunhe@google.com
diff --git a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-external-service.rc b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-external-service.rc
new file mode 100644
index 0000000..107097e
--- /dev/null
+++ b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-external-service.rc
@@ -0,0 +1,9 @@
+service vendor.camera-provider-2-5-ext /vendor/bin/hw/android.hardware.camera.provider@2.5-external-service
+    interface android.hardware.camera.provider@2.5::ICameraProvider external/0
+    interface android.hardware.camera.provider@2.4::ICameraProvider external/0
+    class hal
+    user cameraserver
+    group audio camera input drmrpc usb
+    ioprio rt 4
+    capabilities SYS_NICE
+    writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
diff --git a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy.rc b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy.rc
new file mode 100644
index 0000000..b45158a
--- /dev/null
+++ b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy.rc
@@ -0,0 +1,11 @@
+service vendor.camera-provider-2-5 /vendor/bin/hw/android.hardware.camera.provider@2.5-service-lazy
+    interface android.hardware.camera.provider@2.5::ICameraProvider legacy/0
+    interface android.hardware.camera.provider@2.4::ICameraProvider legacy/0
+    oneshot
+    disabled
+    class hal
+    user cameraserver
+    group audio camera input drmrpc
+    ioprio rt 4
+    capabilities SYS_NICE
+    writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
diff --git a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy_64.rc b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy_64.rc
new file mode 100644
index 0000000..aa070d9
--- /dev/null
+++ b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy_64.rc
@@ -0,0 +1,11 @@
+service vendor.camera-provider-2-5 /vendor/bin/hw/android.hardware.camera.provider@2.5-service-lazy_64
+    interface android.hardware.camera.provider@2.5::ICameraProvider legacy/0
+    interface android.hardware.camera.provider@2.5::ICameraProvider legacy/0
+    oneshot
+    disabled
+    class hal
+    user cameraserver
+    group audio camera input drmrpc
+    ioprio rt 4
+    capabilities SYS_NICE
+    writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
diff --git a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service.rc b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service.rc
new file mode 100644
index 0000000..c065815
--- /dev/null
+++ b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service.rc
@@ -0,0 +1,9 @@
+service vendor.camera-provider-2-5 /vendor/bin/hw/android.hardware.camera.provider@2.5-service
+    interface android.hardware.camera.provider@2.5::ICameraProvider legacy/0
+    interface android.hardware.camera.provider@2.4::ICameraProvider legacy/0
+    class hal
+    user cameraserver
+    group audio camera input drmrpc
+    ioprio rt 4
+    capabilities SYS_NICE
+    writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
diff --git a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service_64.rc b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service_64.rc
new file mode 100644
index 0000000..63dd11d
--- /dev/null
+++ b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service_64.rc
@@ -0,0 +1,9 @@
+service vendor.camera-provider-2-5 /vendor/bin/hw/android.hardware.camera.provider@2.5-service_64
+    interface android.hardware.camera.provider@2.5::ICameraProvider legacy/0
+    interface android.hardware.camera.provider@2.4::ICameraProvider legacy/0
+    class hal
+    user cameraserver
+    group audio camera input drmrpc
+    ioprio rt 4
+    capabilities SYS_NICE
+    writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
diff --git a/camera/provider/2.5/default/external-service.cpp b/camera/provider/2.5/default/external-service.cpp
new file mode 100644
index 0000000..8788916
--- /dev/null
+++ b/camera/provider/2.5/default/external-service.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.camera.provider@2.5-external-service"
+
+#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
+#include <binder/ProcessState.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "CameraProvider_2_5.h"
+#include "ExternalCameraProviderImpl_2_5.h"
+
+using android::status_t;
+using android::hardware::camera::provider::V2_5::ICameraProvider;
+
+int main()
+{
+    using namespace android::hardware::camera::provider::V2_5::implementation;
+
+    ALOGI("CameraProvider@2.5 external webcam service is starting.");
+
+    ::android::hardware::configureRpcThreadpool(/*threads*/ HWBINDER_THREAD_COUNT, /*willJoin*/ true);
+
+    ::android::sp<ICameraProvider> provider = new CameraProvider<ExternalCameraProviderImpl_2_5>();
+
+    status_t status = provider->registerAsService("external/0");
+    LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering provider service: %d",
+            status);
+
+    ::android::hardware::joinRpcThreadpool();
+
+    return 0;
+}
diff --git a/camera/provider/2.5/default/service.cpp b/camera/provider/2.5/default/service.cpp
new file mode 100644
index 0000000..604215d
--- /dev/null
+++ b/camera/provider/2.5/default/service.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifdef LAZY_SERVICE
+#define LOG_TAG "android.hardware.camera.provider@2.5-service-lazy"
+#else
+#define LOG_TAG "android.hardware.camera.provider@2.5-service"
+#endif
+
+#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
+#include <binder/ProcessState.h>
+#include <hidl/HidlLazyUtils.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "CameraProvider_2_5.h"
+#include "LegacyCameraProviderImpl_2_5.h"
+
+using android::status_t;
+using android::hardware::camera::provider::V2_5::ICameraProvider;
+
+#ifdef LAZY_SERVICE
+const bool kLazyService = true;
+#else
+const bool kLazyService = false;
+#endif
+
+int main()
+{
+    using namespace android::hardware::camera::provider::V2_5::implementation;
+
+    ALOGI("CameraProvider@2.5 legacy service is starting.");
+
+    ::android::hardware::configureRpcThreadpool(/*threads*/ HWBINDER_THREAD_COUNT, /*willJoin*/ true);
+
+    ::android::sp<ICameraProvider> provider = new CameraProvider<LegacyCameraProviderImpl_2_5>();
+
+    status_t status;
+    if (kLazyService) {
+        auto serviceRegistrar = std::make_shared<::android::hardware::LazyServiceRegistrar>();
+        status = serviceRegistrar->registerService(provider, "legacy/0");
+    } else {
+        status = provider->registerAsService("legacy/0");
+    }
+    LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering provider service: %d",
+            status);
+
+    ::android::hardware::joinRpcThreadpool();
+
+    return 0;
+}
diff --git a/camera/provider/2.5/types.hal b/camera/provider/2.5/types.hal
new file mode 100644
index 0000000..6a8ae83
--- /dev/null
+++ b/camera/provider/2.5/types.hal
@@ -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.
+ */
+
+package android.hardware.camera.provider@2.5;
+
+/**
+ * DeviceState:
+ *
+ * Possible physical states of the overall device, for use with
+ * ICameraProvider::notifyDeviceStateChange.
+ *
+ */
+enum DeviceState : uint64_t {
+    /**
+     * The device is in its normal physical configuration. This is the default if the
+     * device does not support multiple different states.
+     */
+    NORMAL = 0,
+
+    /**
+     * Camera device(s) facing backward are covered.
+     */
+    BACK_COVERED = 1 << 0,
+
+    /**
+     * Camera device(s) facing foward are covered.
+     */
+    FRONT_COVERED = 1 << 1,
+
+    /**
+     * The device is folded.  If not set, the device is unfolded or does not
+     * support folding.
+     *
+     * The exact point when this status change happens during the folding
+     * operation is device-specific.
+     */
+    FOLDED = 1 << 2,
+
+};
diff --git a/cas/1.1/Android.bp b/cas/1.1/Android.bp
new file mode 100644
index 0000000..bb0edb9
--- /dev/null
+++ b/cas/1.1/Android.bp
@@ -0,0 +1,20 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.cas@1.1",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "ICas.hal",
+        "ICasListener.hal",
+        "IMediaCasService.hal",
+    ],
+    interfaces: [
+        "android.hardware.cas@1.0",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
+
diff --git a/cas/1.1/ICas.hal b/cas/1.1/ICas.hal
new file mode 100644
index 0000000..027968e
--- /dev/null
+++ b/cas/1.1/ICas.hal
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package android.hardware.cas@1.1;
+
+import @1.0::HidlCasSessionId;
+import @1.0::ICas;
+import @1.0::Status;
+
+/**
+ * ICas is the API to control the cas system and is accessible from both
+ * Java and native level. It is used to manage sessions, provision/refresh
+ * the cas system, and process the EMM/ECM messages. It also allows bi-directional,
+ * scheme-specific communications between the client and the cas system.
+ */
+
+interface ICas extends @1.0::ICas {
+   /**
+     * Send an scheme-specific session event to the CasPlugin.
+     *
+     * @param sessionId the id of an opened session.
+     * @param event an integer denoting a scheme-specific event to be sent.
+     * @param arg a scheme-specific integer argument for the event.
+     * @param data a byte array containing scheme-specific data for the event.
+     * @return status the status of the call.
+     */
+    sendSessionEvent(HidlCasSessionId sessionId, int32_t event, int32_t arg,
+                     vec<uint8_t> eventData)
+          generates (Status status);
+};
diff --git a/cas/1.1/ICasListener.hal b/cas/1.1/ICasListener.hal
new file mode 100644
index 0000000..5ec1154
--- /dev/null
+++ b/cas/1.1/ICasListener.hal
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+package android.hardware.cas@1.1;
+
+import @1.0::ICasListener;
+import @1.0::HidlCasSessionId;
+
+interface ICasListener extends @1.0::ICasListener{
+    /**
+      * Notify the listener of a scheme-specific session event from CA system.
+      *
+      * @param sessionId the id of an opened session.
+      * @param event an integer whose meaning is scheme-specific.
+      * @param arg an integer whose meaning is scheme-specific.
+      * @param data a byte array of data whose format and meaning are
+      * scheme-specific.
+      */
+    onSessionEvent(HidlCasSessionId sessionId, int32_t event, int32_t arg,
+                   vec<uint8_t> data);
+};
diff --git a/cas/1.1/IMediaCasService.hal b/cas/1.1/IMediaCasService.hal
new file mode 100644
index 0000000..e82b54c
--- /dev/null
+++ b/cas/1.1/IMediaCasService.hal
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+package android.hardware.cas@1.1;
+
+import @1.1::ICas;
+import @1.1::ICasListener;
+import @1.0::IMediaCasService;
+
+/**
+ * IMediaCasService is the main entry point for interacting with a vendor's
+ * cas HAL to create cas and descrambler plugin instances. A cas plugin instance
+ * opens cas sessions which are used to obtain keys for a descrambler session,
+ * which can in turn be used to descramble protected video content.
+ */
+
+interface IMediaCasService extends @1.0::IMediaCasService {
+    /**
+     * Construct a new instance of a @1.1 ICAS CasPlugin given a CA_system_id.
+     *
+     * @param caSystemId the id of the CA system.
+     * @param listener the event listener to receive events coming from the CasPlugin.
+     * @return cas the newly created CasPlugin interface.
+     */
+    createPluginExt(int32_t caSystemId, ICasListener listener) generates (ICas cas);
+};
diff --git a/cas/1.1/default/Android.bp b/cas/1.1/default/Android.bp
new file mode 100644
index 0000000..68a49cf
--- /dev/null
+++ b/cas/1.1/default/Android.bp
@@ -0,0 +1,49 @@
+cc_defaults {
+    name: "cas_service_defaults@1.1",
+    defaults: ["hidl_defaults"],
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: [
+      "CasImpl.cpp",
+      "DescramblerImpl.cpp",
+      "MediaCasService.cpp",
+      "service.cpp",
+      "SharedLibrary.cpp",
+      "TypeConvert.cpp",
+    ],
+
+    compile_multilib: "32",
+
+    shared_libs: [
+      "android.hardware.cas@1.0",
+      "android.hardware.cas@1.1",
+      "android.hardware.cas.native@1.0",
+      "android.hidl.memory@1.0",
+      "libbinder",
+      "libhidlbase",
+      "libhidlmemory",
+      "libhidltransport",
+      "liblog",
+      "libutils",
+    ],
+    header_libs: [
+      "libstagefright_foundation_headers",
+      "media_plugin_headers",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.cas@1.1-service",
+    vintf_fragments: ["android.hardware.cas@1.1-service.xml"],
+    defaults: ["cas_service_defaults@1.1"],
+    init_rc: ["android.hardware.cas@1.1-service.rc"],
+}
+
+cc_binary {
+    name: "android.hardware.cas@1.1-service-lazy",
+    vintf_fragments: ["android.hardware.cas@1.1-service-lazy.xml"],
+    overrides: ["android.hardware.cas@1.1-service"],
+    defaults: ["cas_service_defaults@1.1"],
+    init_rc: ["android.hardware.cas@1.1-service-lazy.rc"],
+    cflags: ["-DLAZY_SERVICE"],
+}
diff --git a/cas/1.1/default/CasImpl.cpp b/cas/1.1/default/CasImpl.cpp
new file mode 100644
index 0000000..4cc6017
--- /dev/null
+++ b/cas/1.1/default/CasImpl.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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 "android.hardware.cas@1.1-CasImpl"
+
+#include <android/hardware/cas/1.1/ICasListener.h>
+#include <media/cas/CasAPI.h>
+#include <utils/Log.h>
+
+#include "CasImpl.h"
+#include "SharedLibrary.h"
+#include "TypeConvert.h"
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+CasImpl::CasImpl(const sp<ICasListener>& listener) : mListener(listener) {
+    ALOGV("CTOR");
+}
+
+CasImpl::~CasImpl() {
+    ALOGV("DTOR");
+    release();
+}
+
+// static
+void CasImpl::OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size) {
+    if (appData == NULL) {
+        ALOGE("Invalid appData!");
+        return;
+    }
+    CasImpl* casImpl = static_cast<CasImpl*>(appData);
+    casImpl->onEvent(event, arg, data, size);
+}
+
+// static
+void CasImpl::CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size,
+                          const CasSessionId* sessionId) {
+    if (appData == NULL) {
+        ALOGE("Invalid appData!");
+        return;
+    }
+    CasImpl* casImpl = static_cast<CasImpl*>(appData);
+    casImpl->onEvent(sessionId, event, arg, data, size);
+}
+
+void CasImpl::init(const sp<SharedLibrary>& library, CasPlugin* plugin) {
+    mLibrary = library;
+    std::shared_ptr<CasPlugin> holder(plugin);
+    std::atomic_store(&mPluginHolder, holder);
+}
+
+void CasImpl::onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size) {
+    if (mListener == NULL) {
+        return;
+    }
+
+    HidlCasData eventData;
+    if (data != NULL) {
+        eventData.setToExternal(data, size);
+    }
+
+    mListener->onEvent(event, arg, eventData);
+}
+
+void CasImpl::onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data,
+                      size_t size) {
+    if (mListener == NULL) {
+        return;
+    }
+
+    HidlCasData eventData;
+    if (data != NULL) {
+        eventData.setToExternal(data, size);
+    }
+
+    if (sessionId != NULL) {
+        mListener->onSessionEvent(*sessionId, event, arg, eventData);
+    } else {
+        mListener->onEvent(event, arg, eventData);
+    }
+}
+
+Return<Status> CasImpl::setPrivateData(const HidlCasData& pvtData) {
+    ALOGV("%s", __FUNCTION__);
+    std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+    return toStatus(holder->setPrivateData(pvtData));
+}
+
+Return<void> CasImpl::openSession(openSession_cb _hidl_cb) {
+    ALOGV("%s", __FUNCTION__);
+    CasSessionId sessionId;
+
+    std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+    status_t err = INVALID_OPERATION;
+    if (holder.get() != nullptr) {
+        err = holder->openSession(&sessionId);
+        holder.reset();
+    }
+
+    _hidl_cb(toStatus(err), sessionId);
+
+    return Void();
+}
+
+Return<Status> CasImpl::setSessionPrivateData(const HidlCasSessionId& sessionId,
+                                              const HidlCasData& pvtData) {
+    ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+    std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+    return toStatus(holder->setSessionPrivateData(sessionId, pvtData));
+}
+
+Return<Status> CasImpl::closeSession(const HidlCasSessionId& sessionId) {
+    ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+    std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+    return toStatus(holder->closeSession(sessionId));
+}
+
+Return<Status> CasImpl::processEcm(const HidlCasSessionId& sessionId, const HidlCasData& ecm) {
+    ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+    std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    return toStatus(holder->processEcm(sessionId, ecm));
+}
+
+Return<Status> CasImpl::processEmm(const HidlCasData& emm) {
+    ALOGV("%s", __FUNCTION__);
+    std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    return toStatus(holder->processEmm(emm));
+}
+
+Return<Status> CasImpl::sendEvent(int32_t event, int32_t arg, const HidlCasData& eventData) {
+    ALOGV("%s", __FUNCTION__);
+    std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    status_t err = holder->sendEvent(event, arg, eventData);
+    return toStatus(err);
+}
+
+Return<Status> CasImpl::sendSessionEvent(const HidlCasSessionId& sessionId, int32_t event,
+                                         int32_t arg, const HidlCasData& eventData) {
+    ALOGV("%s", __FUNCTION__);
+    std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    status_t err = holder->sendSessionEvent(sessionId, event, arg, eventData);
+    return toStatus(err);
+}
+
+Return<Status> CasImpl::provision(const hidl_string& provisionString) {
+    ALOGV("%s: provisionString=%s", __FUNCTION__, provisionString.c_str());
+    std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    return toStatus(holder->provision(String8(provisionString.c_str())));
+}
+
+Return<Status> CasImpl::refreshEntitlements(int32_t refreshType, const HidlCasData& refreshData) {
+    ALOGV("%s", __FUNCTION__);
+    std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    status_t err = holder->refreshEntitlements(refreshType, refreshData);
+    return toStatus(err);
+}
+
+Return<Status> CasImpl::release() {
+    ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get());
+
+    std::shared_ptr<CasPlugin> holder(nullptr);
+    std::atomic_store(&mPluginHolder, holder);
+
+    return Status::OK;
+}
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
diff --git a/cas/1.1/default/CasImpl.h b/cas/1.1/default/CasImpl.h
new file mode 100644
index 0000000..18aee9e
--- /dev/null
+++ b/cas/1.1/default/CasImpl.h
@@ -0,0 +1,98 @@
+/*
+ * 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_HARDWARE_CAS_V1_1_CAS_IMPL_H_
+#define ANDROID_HARDWARE_CAS_V1_1_CAS_IMPL_H_
+
+#include <android/hardware/cas/1.1/ICas.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+struct CasPlugin;
+
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+struct ICasListener;
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasData;
+using ::android::hardware::cas::V1_0::HidlCasSessionId;
+using ::android::hardware::cas::V1_0::Status;
+
+class SharedLibrary;
+
+class CasImpl : public ICas {
+   public:
+    CasImpl(const sp<ICasListener>& listener);
+    virtual ~CasImpl();
+
+    static void OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size);
+
+    static void CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size,
+                            const CasSessionId* sessionId);
+
+    void init(const sp<SharedLibrary>& library, CasPlugin* plugin);
+    void onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size);
+
+    void onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data,
+                 size_t size);
+
+    // ICas inherits
+
+    virtual Return<Status> setPrivateData(const HidlCasData& pvtData) override;
+
+    virtual Return<void> openSession(openSession_cb _hidl_cb) override;
+
+    virtual Return<Status> closeSession(const HidlCasSessionId& sessionId) override;
+
+    virtual Return<Status> setSessionPrivateData(const HidlCasSessionId& sessionId,
+                                                 const HidlCasData& pvtData) override;
+
+    virtual Return<Status> processEcm(const HidlCasSessionId& sessionId,
+                                      const HidlCasData& ecm) override;
+
+    virtual Return<Status> processEmm(const HidlCasData& emm) override;
+
+    virtual Return<Status> sendEvent(int32_t event, int32_t arg,
+                                     const HidlCasData& eventData) override;
+
+    virtual Return<Status> sendSessionEvent(const HidlCasSessionId& sessionId, int32_t event,
+                                            int32_t arg, const HidlCasData& eventData) override;
+
+    virtual Return<Status> provision(const hidl_string& provisionString) override;
+
+    virtual Return<Status> refreshEntitlements(int32_t refreshType,
+                                               const HidlCasData& refreshData) override;
+
+    virtual Return<Status> release() override;
+
+   private:
+    struct PluginHolder;
+    sp<SharedLibrary> mLibrary;
+    std::shared_ptr<CasPlugin> mPluginHolder;
+    sp<ICasListener> mListener;
+
+    DISALLOW_EVIL_CONSTRUCTORS(CasImpl);
+};
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAS_V1_1_CAS_IMPL_H_
diff --git a/cas/1.1/default/DescramblerImpl.cpp b/cas/1.1/default/DescramblerImpl.cpp
new file mode 100644
index 0000000..36dc1a5
--- /dev/null
+++ b/cas/1.1/default/DescramblerImpl.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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 "android.hardware.cas@1.1-DescramblerImpl"
+
+#include <hidlmemory/mapping.h>
+#include <media/cas/DescramblerAPI.h>
+#include <media/hardware/CryptoAPI.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <utils/Log.h>
+
+#include "DescramblerImpl.h"
+#include "SharedLibrary.h"
+#include "TypeConvert.h"
+
+namespace android {
+using hidl::memory::V1_0::IMemory;
+
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+#define CHECK_SUBSAMPLE_DEF(type)                                                                 \
+    static_assert(sizeof(SubSample) == sizeof(type::SubSample), "SubSample: size doesn't match"); \
+    static_assert(offsetof(SubSample, numBytesOfClearData) ==                                     \
+                          offsetof(type::SubSample, mNumBytesOfClearData),                        \
+                  "SubSample: numBytesOfClearData offset doesn't match");                         \
+    static_assert(offsetof(SubSample, numBytesOfEncryptedData) ==                                 \
+                          offsetof(type::SubSample, mNumBytesOfEncryptedData),                    \
+                  "SubSample: numBytesOfEncryptedData offset doesn't match")
+
+CHECK_SUBSAMPLE_DEF(DescramblerPlugin);
+CHECK_SUBSAMPLE_DEF(CryptoPlugin);
+
+DescramblerImpl::DescramblerImpl(const sp<SharedLibrary>& library, DescramblerPlugin* plugin)
+    : mLibrary(library), mPluginHolder(plugin) {
+    ALOGV("CTOR: plugin=%p", mPluginHolder.get());
+}
+
+DescramblerImpl::~DescramblerImpl() {
+    ALOGV("DTOR: plugin=%p", mPluginHolder.get());
+    release();
+}
+
+Return<Status> DescramblerImpl::setMediaCasSession(const HidlCasSessionId& sessionId) {
+    ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+
+    std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return toStatus(INVALID_OPERATION);
+    }
+
+    return toStatus(holder->setMediaCasSession(sessionId));
+}
+
+Return<bool> DescramblerImpl::requiresSecureDecoderComponent(const hidl_string& mime) {
+    std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        return false;
+    }
+
+    return holder->requiresSecureDecoderComponent(String8(mime.c_str()));
+}
+
+static inline bool validateRangeForSize(uint64_t offset, uint64_t length, uint64_t size) {
+    return isInRange<uint64_t, uint64_t>(0, size, offset, length);
+}
+
+Return<void> DescramblerImpl::descramble(ScramblingControl scramblingControl,
+                                         const hidl_vec<SubSample>& subSamples,
+                                         const SharedBuffer& srcBuffer, uint64_t srcOffset,
+                                         const DestinationBuffer& dstBuffer, uint64_t dstOffset,
+                                         descramble_cb _hidl_cb) {
+    ALOGV("%s", __FUNCTION__);
+
+    // hidl_memory's size is stored in uint64_t, but mapMemory's mmap will map
+    // size in size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed
+    // but the mapped memory's actual size will be smaller than the reported size.
+    if (srcBuffer.heapBase.size() > SIZE_MAX) {
+        ALOGE("Invalid hidl_memory size: %llu", srcBuffer.heapBase.size());
+        android_errorWriteLog(0x534e4554, "79376389");
+        _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+        return Void();
+    }
+
+    sp<IMemory> srcMem = mapMemory(srcBuffer.heapBase);
+
+    // Validate if the offset and size in the SharedBuffer is consistent with the
+    // mapped ashmem, since the offset and size is controlled by client.
+    if (srcMem == NULL) {
+        ALOGE("Failed to map src buffer.");
+        _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+        return Void();
+    }
+    if (!validateRangeForSize(srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize())) {
+        ALOGE("Invalid src buffer range: offset %llu, size %llu, srcMem size %llu",
+              srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
+        android_errorWriteLog(0x534e4554, "67962232");
+        _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+        return Void();
+    }
+
+    // use 64-bit here to catch bad subsample size that might be overflowing.
+    uint64_t totalBytesInSubSamples = 0;
+    for (size_t i = 0; i < subSamples.size(); i++) {
+        totalBytesInSubSamples +=
+                (uint64_t)subSamples[i].numBytesOfClearData + subSamples[i].numBytesOfEncryptedData;
+    }
+    // Further validate if the specified srcOffset and requested total subsample size
+    // is consistent with the source shared buffer size.
+    if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) {
+        ALOGE("Invalid srcOffset and subsample size: "
+              "srcOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
+              srcOffset, totalBytesInSubSamples, srcBuffer.size);
+        android_errorWriteLog(0x534e4554, "67962232");
+        _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+        return Void();
+    }
+
+    void* srcPtr = (uint8_t*)(void*)srcMem->getPointer() + srcBuffer.offset;
+    void* dstPtr = NULL;
+    if (dstBuffer.type == BufferType::SHARED_MEMORY) {
+        // When using shared memory, src buffer is also used as dst,
+        // we don't map it again here.
+        dstPtr = srcPtr;
+
+        // In this case the dst and src would be the same buffer, need to validate
+        // dstOffset against the buffer size too.
+        if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) {
+            ALOGE("Invalid dstOffset and subsample size: "
+                  "dstOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
+                  dstOffset, totalBytesInSubSamples, srcBuffer.size);
+            android_errorWriteLog(0x534e4554, "67962232");
+            _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+            return Void();
+        }
+    } else {
+        native_handle_t* handle =
+                const_cast<native_handle_t*>(dstBuffer.secureMemory.getNativeHandle());
+        dstPtr = static_cast<void*>(handle);
+    }
+
+    // Get a local copy of the shared_ptr for the plugin. Note that before
+    // calling the HIDL callback, this shared_ptr must be manually reset,
+    // since the client side could proceed as soon as the callback is called
+    // without waiting for this method to go out of scope.
+    std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder);
+    if (holder.get() == nullptr) {
+        _hidl_cb(toStatus(INVALID_OPERATION), 0, NULL);
+        return Void();
+    }
+
+    // Casting hidl SubSample to DescramblerPlugin::SubSample, but need
+    // to ensure structs are actually idential
+
+    int32_t result =
+            holder->descramble(dstBuffer.type != BufferType::SHARED_MEMORY,
+                               (DescramblerPlugin::ScramblingControl)scramblingControl,
+                               subSamples.size(), (DescramblerPlugin::SubSample*)subSamples.data(),
+                               srcPtr, srcOffset, dstPtr, dstOffset, NULL);
+
+    holder.reset();
+    _hidl_cb(toStatus(result >= 0 ? OK : result), result, NULL);
+    return Void();
+}
+
+Return<Status> DescramblerImpl::release() {
+    ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get());
+
+    std::shared_ptr<DescramblerPlugin> holder(nullptr);
+    std::atomic_store(&mPluginHolder, holder);
+
+    return Status::OK;
+}
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
diff --git a/cas/1.1/default/DescramblerImpl.h b/cas/1.1/default/DescramblerImpl.h
new file mode 100644
index 0000000..a1f66ae
--- /dev/null
+++ b/cas/1.1/default/DescramblerImpl.h
@@ -0,0 +1,67 @@
+/*
+ * 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_HARDWARE_CAS_V1_1_DESCRAMBLER_IMPL_H_
+#define ANDROID_HARDWARE_CAS_V1_1_DESCRAMBLER_IMPL_H_
+
+#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+struct DescramblerPlugin;
+using namespace hardware::cas::native::V1_0;
+
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasSessionId;
+using ::android::hardware::cas::V1_0::Status;
+
+class SharedLibrary;
+
+class DescramblerImpl : public IDescrambler {
+   public:
+    DescramblerImpl(const sp<SharedLibrary>& library, DescramblerPlugin* plugin);
+    virtual ~DescramblerImpl();
+
+    virtual Return<Status> setMediaCasSession(const HidlCasSessionId& sessionId) override;
+
+    virtual Return<bool> requiresSecureDecoderComponent(const hidl_string& mime) override;
+
+    virtual Return<void> descramble(ScramblingControl scramblingControl,
+                                    const hidl_vec<SubSample>& subSamples,
+                                    const SharedBuffer& srcBuffer, uint64_t srcOffset,
+                                    const DestinationBuffer& dstBuffer, uint64_t dstOffset,
+                                    descramble_cb _hidl_cb) override;
+
+    virtual Return<Status> release() override;
+
+   private:
+    sp<SharedLibrary> mLibrary;
+    std::shared_ptr<DescramblerPlugin> mPluginHolder;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DescramblerImpl);
+};
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAS_V1_1_DESCRAMBLER_IMPL_H_
diff --git a/cas/1.1/default/FactoryLoader.h b/cas/1.1/default/FactoryLoader.h
new file mode 100644
index 0000000..c4a48e2
--- /dev/null
+++ b/cas/1.1/default/FactoryLoader.h
@@ -0,0 +1,217 @@
+/*
+ * 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_HARDWARE_CAS_V1_1_FACTORY_LOADER_H_
+#define ANDROID_HARDWARE_CAS_V1_1_FACTORY_LOADER_H_
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <media/cas/CasAPI.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include "SharedLibrary.h"
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasPluginDescriptor;
+
+template <class T>
+class FactoryLoader {
+   public:
+    FactoryLoader(const char* name) : mFactory(NULL), mCreateFactoryFuncName(name) {}
+
+    virtual ~FactoryLoader() { closeFactory(); }
+
+    bool findFactoryForScheme(int32_t CA_system_id, sp<SharedLibrary>* library = NULL,
+                              T** factory = NULL);
+
+    bool enumeratePlugins(vector<HidlCasPluginDescriptor>* results);
+
+   private:
+    typedef T* (*CreateFactoryFunc)();
+
+    Mutex mMapLock;
+    T* mFactory;
+    const char* mCreateFactoryFuncName;
+    sp<SharedLibrary> mLibrary;
+    KeyedVector<int32_t, String8> mCASystemIdToLibraryPathMap;
+    KeyedVector<String8, wp<SharedLibrary> > mLibraryPathToOpenLibraryMap;
+
+    bool loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
+                                      sp<SharedLibrary>* library, T** factory);
+
+    bool queryPluginsFromPath(const String8& path, vector<HidlCasPluginDescriptor>* results);
+
+    bool openFactory(const String8& path);
+    void closeFactory();
+};
+
+template <class T>
+bool FactoryLoader<T>::findFactoryForScheme(int32_t CA_system_id, sp<SharedLibrary>* library,
+                                            T** factory) {
+    if (library != NULL) {
+        library->clear();
+    }
+    if (factory != NULL) {
+        *factory = NULL;
+    }
+
+    Mutex::Autolock autoLock(mMapLock);
+
+    // first check cache
+    ssize_t index = mCASystemIdToLibraryPathMap.indexOfKey(CA_system_id);
+    if (index >= 0) {
+        return loadFactoryForSchemeFromPath(mCASystemIdToLibraryPathMap[index], CA_system_id,
+                                            library, factory);
+    }
+
+    // no luck, have to search
+    String8 dirPath("/vendor/lib/mediacas");
+    DIR* pDir = opendir(dirPath.string());
+
+    if (pDir == NULL) {
+        ALOGE("Failed to open plugin directory %s", dirPath.string());
+        return false;
+    }
+
+    struct dirent* pEntry;
+    while ((pEntry = readdir(pDir))) {
+        String8 pluginPath = dirPath + "/" + pEntry->d_name;
+        if (pluginPath.getPathExtension() == ".so") {
+            if (loadFactoryForSchemeFromPath(pluginPath, CA_system_id, library, factory)) {
+                mCASystemIdToLibraryPathMap.add(CA_system_id, pluginPath);
+                closedir(pDir);
+
+                return true;
+            }
+        }
+    }
+
+    closedir(pDir);
+
+    ALOGE("Failed to find plugin");
+    return false;
+}
+
+template <class T>
+bool FactoryLoader<T>::enumeratePlugins(vector<HidlCasPluginDescriptor>* results) {
+    ALOGI("enumeratePlugins");
+
+    results->clear();
+
+    String8 dirPath("/vendor/lib/mediacas");
+    DIR* pDir = opendir(dirPath.string());
+
+    if (pDir == NULL) {
+        ALOGE("Failed to open plugin directory %s", dirPath.string());
+        return false;
+    }
+
+    Mutex::Autolock autoLock(mMapLock);
+
+    struct dirent* pEntry;
+    while ((pEntry = readdir(pDir))) {
+        String8 pluginPath = dirPath + "/" + pEntry->d_name;
+        if (pluginPath.getPathExtension() == ".so") {
+            queryPluginsFromPath(pluginPath, results);
+        }
+    }
+    return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
+                                                    sp<SharedLibrary>* library, T** factory) {
+    closeFactory();
+
+    if (!openFactory(path) || !mFactory->isSystemIdSupported(CA_system_id)) {
+        closeFactory();
+        return false;
+    }
+
+    if (library != NULL) {
+        *library = mLibrary;
+    }
+    if (factory != NULL) {
+        *factory = mFactory;
+    }
+    return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::queryPluginsFromPath(const String8& path,
+                                            vector<HidlCasPluginDescriptor>* results) {
+    closeFactory();
+
+    vector<CasPluginDescriptor> descriptors;
+    if (!openFactory(path) || mFactory->queryPlugins(&descriptors) != OK) {
+        closeFactory();
+        return false;
+    }
+
+    for (auto it = descriptors.begin(); it != descriptors.end(); it++) {
+        results->push_back(
+                HidlCasPluginDescriptor{.caSystemId = it->CA_system_id, .name = it->name.c_str()});
+    }
+    return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::openFactory(const String8& path) {
+    // get strong pointer to open shared library
+    ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path);
+    if (index >= 0) {
+        mLibrary = mLibraryPathToOpenLibraryMap[index].promote();
+    } else {
+        index = mLibraryPathToOpenLibraryMap.add(path, NULL);
+    }
+
+    if (!mLibrary.get()) {
+        mLibrary = new SharedLibrary(path);
+        if (!*mLibrary) {
+            return false;
+        }
+
+        mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary);
+    }
+
+    CreateFactoryFunc createFactory = (CreateFactoryFunc)mLibrary->lookup(mCreateFactoryFuncName);
+    if (createFactory == NULL || (mFactory = createFactory()) == NULL) {
+        return false;
+    }
+    return true;
+}
+
+template <class T>
+void FactoryLoader<T>::closeFactory() {
+    delete mFactory;
+    mFactory = NULL;
+    mLibrary.clear();
+}
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAS_V1_1_FACTORY_LOADER_H_
diff --git a/cas/1.1/default/MediaCasService.cpp b/cas/1.1/default/MediaCasService.cpp
new file mode 100644
index 0000000..eb3fa6b
--- /dev/null
+++ b/cas/1.1/default/MediaCasService.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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 "android.hardware.cas@1.1-MediaCasService"
+
+#include <android/hardware/cas/1.1/ICasListener.h>
+#include <media/cas/CasAPI.h>
+#include <media/cas/DescramblerAPI.h>
+#include <utils/Log.h>
+
+#include "CasImpl.h"
+#include "DescramblerImpl.h"
+#include "MediaCasService.h"
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+class Wrapper : public V1_1::ICasListener {
+  public:
+    static sp<V1_1::ICasListener> wrap(sp<V1_0::ICasListener> impl) {
+        sp<V1_1::ICasListener> cast = V1_1::ICasListener::castFrom(impl);
+        if (cast == NULL) {
+            cast = new Wrapper(impl);
+        }
+        return cast;
+    }
+
+    virtual Return<void> onEvent(int32_t event, int32_t arg,
+                                 const hidl_vec<uint8_t>& data) override {
+        mImpl->onEvent(event, arg, data);
+        return Void();
+    }
+
+    virtual Return<void> onSessionEvent(const hidl_vec<uint8_t>& /* sessionId */,
+                                        int32_t /* event */, int32_t /* arg */,
+                                        const hidl_vec<uint8_t>& /*data*/) override {
+        ALOGV("Do nothing on Session Event for cas@1.0 client in cas@1.1");
+        return Void();
+    }
+
+  private:
+    Wrapper(sp<V1_0::ICasListener> impl) : mImpl(impl){};
+    sp<V1_0::ICasListener> mImpl;
+};
+
+MediaCasService::MediaCasService()
+    : mCasLoader("createCasFactory"), mDescramblerLoader("createDescramblerFactory") {}
+
+MediaCasService::~MediaCasService() {}
+
+Return<void> MediaCasService::enumeratePlugins(enumeratePlugins_cb _hidl_cb) {
+    ALOGV("%s", __FUNCTION__);
+
+    vector<HidlCasPluginDescriptor> results;
+    mCasLoader.enumeratePlugins(&results);
+
+    _hidl_cb(results);
+    return Void();
+}
+
+Return<bool> MediaCasService::isSystemIdSupported(int32_t CA_system_id) {
+    ALOGV("isSystemIdSupported: CA_system_id=%d", CA_system_id);
+
+    return mCasLoader.findFactoryForScheme(CA_system_id);
+}
+
+Return<sp<V1_0::ICas>> MediaCasService::createPlugin(int32_t CA_system_id,
+                                                     const sp<V1_0::ICasListener>& listener) {
+    ALOGV("%s:Use createPluginExt to create plugin in cas@1.1", __FUNCTION__);
+
+    sp<ICas> result;
+
+    sp<V1_1::ICasListener> listenerV1_1 = Wrapper::wrap(listener);
+
+    result = createPluginExt(CA_system_id, listenerV1_1);
+
+    return result;
+}
+
+Return<sp<ICas>> MediaCasService::createPluginExt(int32_t CA_system_id,
+                                                  const sp<ICasListener>& listener) {
+    ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+    if (listener == NULL) ALOGV("%s: Listener is NULL", __FUNCTION__);
+
+    sp<ICas> result;
+
+    CasFactory* factory;
+    sp<SharedLibrary> library;
+    if (mCasLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
+        CasPlugin* plugin = NULL;
+        sp<CasImpl> casImpl = new CasImpl(listener);
+        if (factory->createPlugin(CA_system_id, casImpl.get(), &CasImpl::CallBackExt, &plugin) ==
+                    OK &&
+            plugin != NULL) {
+            casImpl->init(library, plugin);
+            result = casImpl;
+        }
+    }
+
+    return result;
+}
+
+Return<bool> MediaCasService::isDescramblerSupported(int32_t CA_system_id) {
+    ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+    return mDescramblerLoader.findFactoryForScheme(CA_system_id);
+}
+
+Return<sp<IDescramblerBase>> MediaCasService::createDescrambler(int32_t CA_system_id) {
+    ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+    sp<IDescrambler> result;
+
+    DescramblerFactory* factory;
+    sp<SharedLibrary> library;
+    if (mDescramblerLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
+        DescramblerPlugin* plugin = NULL;
+        if (factory->createPlugin(CA_system_id, &plugin) == OK && plugin != NULL) {
+            result = new DescramblerImpl(library, plugin);
+        }
+    }
+
+    return result;
+}
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
diff --git a/cas/1.1/default/MediaCasService.h b/cas/1.1/default/MediaCasService.h
new file mode 100644
index 0000000..ec5a86d
--- /dev/null
+++ b/cas/1.1/default/MediaCasService.h
@@ -0,0 +1,66 @@
+/*
+ * 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_HARDWARE_CAS_V1_1_MEDIA_CAS_SERVICE_H_
+#define ANDROID_HARDWARE_CAS_V1_1_MEDIA_CAS_SERVICE_H_
+
+#include <android/hardware/cas/1.1/IMediaCasService.h>
+
+#include "FactoryLoader.h"
+
+namespace android {
+struct CasFactory;
+struct DescramblerFactory;
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasPluginDescriptor;
+using ::android::hardware::cas::V1_0::IDescramblerBase;
+
+class MediaCasService : public IMediaCasService {
+   public:
+    MediaCasService();
+
+    virtual Return<void> enumeratePlugins(enumeratePlugins_cb _hidl_cb) override;
+
+    virtual Return<bool> isSystemIdSupported(int32_t CA_system_id) override;
+
+    virtual Return<sp<V1_0::ICas>> createPlugin(int32_t CA_system_id,
+                                                const sp<V1_0::ICasListener>& listener) override;
+
+    virtual Return<sp<ICas>> createPluginExt(int32_t CA_system_id,
+                                             const sp<ICasListener>& listener) override;
+
+    virtual Return<bool> isDescramblerSupported(int32_t CA_system_id) override;
+
+    virtual Return<sp<IDescramblerBase>> createDescrambler(int32_t CA_system_id) override;
+
+   private:
+    FactoryLoader<CasFactory> mCasLoader;
+    FactoryLoader<DescramblerFactory> mDescramblerLoader;
+
+    virtual ~MediaCasService();
+};
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAS_V1_1_MEDIA_CAS_SERVICE_H_
diff --git a/cas/1.1/default/SharedLibrary.cpp b/cas/1.1/default/SharedLibrary.cpp
new file mode 100644
index 0000000..ffe4bb9
--- /dev/null
+++ b/cas/1.1/default/SharedLibrary.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 "android.hardware.cas@1.1-SharedLibrary"
+
+#include "SharedLibrary.h"
+#include <dlfcn.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+SharedLibrary::SharedLibrary(const String8& path) {
+    mLibHandle = dlopen(path.string(), RTLD_NOW);
+}
+
+SharedLibrary::~SharedLibrary() {
+    if (mLibHandle != NULL) {
+        dlclose(mLibHandle);
+        mLibHandle = NULL;
+    }
+}
+
+bool SharedLibrary::operator!() const {
+    return mLibHandle == NULL;
+}
+
+void* SharedLibrary::lookup(const char* symbol) const {
+    if (!mLibHandle) {
+        return NULL;
+    }
+    // Clear last error before we load the symbol again,
+    // in case the caller didn't retrieve it.
+    (void)dlerror();
+    return dlsym(mLibHandle, symbol);
+}
+
+const char* SharedLibrary::lastError() const {
+    const char* error = dlerror();
+    return error ? error : "No errors or unknown error";
+}
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
diff --git a/cas/1.1/default/SharedLibrary.h b/cas/1.1/default/SharedLibrary.h
new file mode 100644
index 0000000..f4d2ff6
--- /dev/null
+++ b/cas/1.1/default/SharedLibrary.h
@@ -0,0 +1,50 @@
+/*
+ * 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_HARDWARE_CAS_V1_1_SHARED_LIBRARY_H_
+#define ANDROID_HARDWARE_CAS_V1_1_SHARED_LIBRARY_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+class SharedLibrary : public RefBase {
+   public:
+    explicit SharedLibrary(const String8& path);
+    ~SharedLibrary();
+
+    bool operator!() const;
+    void* lookup(const char* symbol) const;
+    const char* lastError() const;
+
+   private:
+    void* mLibHandle;
+    DISALLOW_EVIL_CONSTRUCTORS(SharedLibrary);
+};
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAS_V1_1_SHARED_LIBRARY_H_
diff --git a/cas/1.1/default/TypeConvert.cpp b/cas/1.1/default/TypeConvert.cpp
new file mode 100644
index 0000000..09ef41a
--- /dev/null
+++ b/cas/1.1/default/TypeConvert.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 "android.hardware.cas@1.1-TypeConvert"
+
+#include "TypeConvert.h"
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+Status toStatus(status_t legacyStatus) {
+    Status status;
+    switch (legacyStatus) {
+        case android::OK:
+            status = Status::OK;
+            break;
+        case android::ERROR_CAS_NO_LICENSE:
+            status = Status::ERROR_CAS_NO_LICENSE;
+            break;
+        case android::ERROR_CAS_LICENSE_EXPIRED:
+            status = Status::ERROR_CAS_LICENSE_EXPIRED;
+            break;
+        case android::ERROR_CAS_SESSION_NOT_OPENED:
+            status = Status::ERROR_CAS_SESSION_NOT_OPENED;
+            break;
+        case android::ERROR_CAS_CANNOT_HANDLE:
+            status = Status::ERROR_CAS_CANNOT_HANDLE;
+            break;
+        case android::ERROR_CAS_TAMPER_DETECTED:
+            status = Status::ERROR_CAS_INVALID_STATE;
+            break;
+        case android::BAD_VALUE:
+            status = Status::BAD_VALUE;
+            break;
+        case android::ERROR_CAS_NOT_PROVISIONED:
+            status = Status::ERROR_CAS_NOT_PROVISIONED;
+            break;
+        case android::ERROR_CAS_RESOURCE_BUSY:
+            status = Status::ERROR_CAS_RESOURCE_BUSY;
+            break;
+        case android::ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION:
+            status = Status::ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION;
+            break;
+        case android::ERROR_CAS_DEVICE_REVOKED:
+            status = Status::ERROR_CAS_DEVICE_REVOKED;
+            break;
+        case android::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED:
+            status = Status::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED;
+            break;
+        case android::ERROR_CAS_DECRYPT:
+            status = Status::ERROR_CAS_DECRYPT;
+            break;
+        default:
+            ALOGW("Unable to convert legacy status: %d, defaulting to UNKNOWN", legacyStatus);
+            status = Status::ERROR_CAS_UNKNOWN;
+            break;
+    }
+    return status;
+}
+
+String8 sessionIdToString(const CasSessionId& sessionId) {
+    String8 result;
+    for (size_t i = 0; i < sessionId.size(); i++) {
+        result.appendFormat("%02x ", sessionId[i]);
+    }
+    if (result.isEmpty()) {
+        result.append("(null)");
+    }
+    return result;
+}
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
diff --git a/cas/1.1/default/TypeConvert.h b/cas/1.1/default/TypeConvert.h
new file mode 100644
index 0000000..c4a0119
--- /dev/null
+++ b/cas/1.1/default/TypeConvert.h
@@ -0,0 +1,43 @@
+/*
+ * 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_HARDWARE_CAS_V1_1_TYPE_CONVERT_H
+#define ANDROID_HARDWARE_CAS_V1_1_TYPE_CONVERT_H
+
+#include <android/hardware/cas/1.0/types.h>
+#include <media/cas/CasAPI.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::Status;
+
+Status toStatus(status_t legacyStatus);
+
+String8 sessionIdToString(const CasSessionId& sessionId);
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace cas
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_CAS_V1_1_TYPE_CONVERT_H
diff --git a/cas/1.1/default/android.hardware.cas@1.1-service-lazy.rc b/cas/1.1/default/android.hardware.cas@1.1-service-lazy.rc
new file mode 100644
index 0000000..9227b6f
--- /dev/null
+++ b/cas/1.1/default/android.hardware.cas@1.1-service-lazy.rc
@@ -0,0 +1,9 @@
+service vendor.cas-hal-1-1 /vendor/bin/hw/android.hardware.cas@1.1-service-lazy
+    interface android.hardware.cas@1.1::IMediaCasService default
+    oneshot
+    disabled
+    class hal
+    user media
+    group mediadrm drmrpc
+    ioprio rt 4
+    writepid /dev/cpuset/foreground/tasks
diff --git a/cas/1.1/default/android.hardware.cas@1.1-service-lazy.xml b/cas/1.1/default/android.hardware.cas@1.1-service-lazy.xml
new file mode 100644
index 0000000..c9f13ba
--- /dev/null
+++ b/cas/1.1/default/android.hardware.cas@1.1-service-lazy.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.cas</name>
+        <transport>hwbinder</transport>
+        <version>1.1</version>
+        <interface>
+            <name>IMediaCasService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/cas/1.1/default/android.hardware.cas@1.1-service.rc b/cas/1.1/default/android.hardware.cas@1.1-service.rc
new file mode 100644
index 0000000..4081fe1
--- /dev/null
+++ b/cas/1.1/default/android.hardware.cas@1.1-service.rc
@@ -0,0 +1,6 @@
+service vendor.cas-hal-1-1 /vendor/bin/hw/android.hardware.cas@1.1-service
+    class hal
+    user media
+    group mediadrm drmrpc
+    ioprio rt 4
+    writepid /dev/cpuset/foreground/tasks
diff --git a/cas/1.1/default/android.hardware.cas@1.1-service.xml b/cas/1.1/default/android.hardware.cas@1.1-service.xml
new file mode 100644
index 0000000..c9f13ba
--- /dev/null
+++ b/cas/1.1/default/android.hardware.cas@1.1-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.cas</name>
+        <transport>hwbinder</transport>
+        <version>1.1</version>
+        <interface>
+            <name>IMediaCasService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/cas/1.1/default/service.cpp b/cas/1.1/default/service.cpp
new file mode 100644
index 0000000..9625303
--- /dev/null
+++ b/cas/1.1/default/service.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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
+#ifdef LAZY_SERVICE
+#define LOG_TAG "android.hardware.cas@1.1-service-lazy"
+#else
+#define LOG_TAG "android.hardware.cas@1.1-service"
+#endif
+
+#include <binder/ProcessState.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+
+#include "MediaCasService.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::LazyServiceRegistrar;
+using android::hardware::cas::V1_1::IMediaCasService;
+using android::hardware::cas::V1_1::implementation::MediaCasService;
+
+#ifdef LAZY_SERVICE
+const bool kLazyService = true;
+#else
+const bool kLazyService = false;
+#endif
+
+int main() {
+    configureRpcThreadpool(8, true /* callerWillJoin */);
+
+    // Setup hwbinder service
+    android::sp<IMediaCasService> service = new MediaCasService();
+    android::status_t status;
+    if (kLazyService) {
+        auto serviceRegistrar = std::make_shared<LazyServiceRegistrar>();
+        status = serviceRegistrar->registerService(service);
+    } else {
+        status = service->registerAsService();
+    }
+    LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering cas service: %d", status);
+
+    joinRpcThreadpool();
+    return 0;
+}
diff --git a/cas/1.1/vts/functional/Android.bp b/cas/1.1/vts/functional/Android.bp
new file mode 100644
index 0000000..8afd19a
--- /dev/null
+++ b/cas/1.1/vts/functional/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "VtsHalCasV1_1TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalCasV1_1TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.cas@1.0",
+        "android.hardware.cas@1.1",
+        "android.hardware.cas.native@1.0",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libhidlallocatorutils",
+        "libhidlmemory",
+    ],
+    shared_libs: [
+        "libbinder",
+    ],
+    test_suites: ["general-tests"],
+}
+
diff --git a/cas/1.1/vts/functional/OWNERS b/cas/1.1/vts/functional/OWNERS
new file mode 100644
index 0000000..29246ed
--- /dev/null
+++ b/cas/1.1/vts/functional/OWNERS
@@ -0,0 +1,3 @@
+nchalko@google.com
+chz@google.com
+quxiangfang@google.com
diff --git a/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp b/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp
new file mode 100644
index 0000000..88f1fb0
--- /dev/null
+++ b/cas/1.1/vts/functional/VtsHalCasV1_1TargetTest.cpp
@@ -0,0 +1,571 @@
+/*
+ * 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_TAG "mediacas_hidl_hal_test"
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/cas/1.0/IDescramblerBase.h>
+#include <android/hardware/cas/1.0/types.h>
+#include <android/hardware/cas/1.1/ICas.h>
+#include <android/hardware/cas/1.1/ICasListener.h>
+#include <android/hardware/cas/1.1/IMediaCasService.h>
+#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <android/hardware/cas/native/1.0/types.h>
+#include <binder/MemoryDealer.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/Status.h>
+#include <hidlmemory/FrameworkUtils.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+#define CLEAR_KEY_SYSTEM_ID 0xF6D8
+#define INVALID_SYSTEM_ID 0
+#define WAIT_TIMEOUT 3000000000
+
+#define PROVISION_STR                                      \
+    "{                                                   " \
+    "  \"id\": 21140844,                                 " \
+    "  \"name\": \"Test Title\",                         " \
+    "  \"lowercase_organization_name\": \"Android\",     " \
+    "  \"asset_key\": {                                  " \
+    "  \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\"  " \
+    "  },                                                " \
+    "  \"cas_type\": 1,                                  " \
+    "  \"track_types\": [ ]                              " \
+    "}                                                   "
+
+using android::Condition;
+using android::IMemory;
+using android::IMemoryHeap;
+using android::MemoryDealer;
+using android::Mutex;
+using android::sp;
+using android::hardware::fromHeap;
+using android::hardware::hidl_string;
+using android::hardware::hidl_vec;
+using android::hardware::HidlMemory;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::cas::native::V1_0::BufferType;
+using android::hardware::cas::native::V1_0::DestinationBuffer;
+using android::hardware::cas::native::V1_0::IDescrambler;
+using android::hardware::cas::native::V1_0::ScramblingControl;
+using android::hardware::cas::native::V1_0::SharedBuffer;
+using android::hardware::cas::native::V1_0::SubSample;
+using android::hardware::cas::V1_0::HidlCasPluginDescriptor;
+using android::hardware::cas::V1_0::IDescramblerBase;
+using android::hardware::cas::V1_0::Status;
+using android::hardware::cas::V1_1::ICas;
+using android::hardware::cas::V1_1::ICasListener;
+using android::hardware::cas::V1_1::IMediaCasService;
+
+namespace {
+
+const uint8_t kEcmBinaryBuffer[] = {
+        0x00, 0x00, 0x01, 0xf0, 0x00, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x46, 0x00,
+        0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x27, 0x10, 0x02, 0x00,
+        0x01, 0x77, 0x01, 0x42, 0x95, 0x6c, 0x0e, 0xe3, 0x91, 0xbc, 0xfd, 0x05, 0xb1, 0x60, 0x4f,
+        0x17, 0x82, 0xa4, 0x86, 0x9b, 0x23, 0x56, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+        0x27, 0x10, 0x02, 0x00, 0x01, 0x77, 0x01, 0x42, 0x95, 0x6c, 0xd7, 0x43, 0x62, 0xf8, 0x1c,
+        0x62, 0x19, 0x05, 0xc7, 0x3a, 0x42, 0xcd, 0xfd, 0xd9, 0x13, 0x48,
+};
+
+const SubSample kSubSamples[] = {{162, 0}, {0, 184}, {0, 184}};
+
+const uint8_t kInBinaryBuffer[] = {
+        0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb,
+        0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03,
+        0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06,
+        0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8,
+        0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72,
+        0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d,
+        0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63,
+        0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30,
+        0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+        0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f,
+        0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x6e, 0x45, 0x21,
+        0x82, 0x38, 0xf0, 0x9d, 0x7d, 0x96, 0xe6, 0x94, 0xae, 0xe2, 0x87, 0x8f, 0x04, 0x49, 0xe5,
+        0xf6, 0x8c, 0x8b, 0x9a, 0x10, 0x18, 0xba, 0x94, 0xe9, 0x22, 0x31, 0x04, 0x7e, 0x60, 0x5b,
+        0xc4, 0x24, 0x00, 0x90, 0x62, 0x0d, 0xdc, 0x85, 0x74, 0x75, 0x78, 0xd0, 0x14, 0x08, 0xcb,
+        0x02, 0x1d, 0x7d, 0x9d, 0x34, 0xe8, 0x81, 0xb9, 0xf7, 0x09, 0x28, 0x79, 0x29, 0x8d, 0xe3,
+        0x14, 0xed, 0x5f, 0xca, 0xaf, 0xf4, 0x1c, 0x49, 0x15, 0xe1, 0x80, 0x29, 0x61, 0x76, 0x80,
+        0x43, 0xf8, 0x58, 0x53, 0x40, 0xd7, 0x31, 0x6d, 0x61, 0x81, 0x41, 0xe9, 0x77, 0x9f, 0x9c,
+        0xe1, 0x6d, 0xf2, 0xee, 0xd9, 0xc8, 0x67, 0xd2, 0x5f, 0x48, 0x73, 0xe3, 0x5c, 0xcd, 0xa7,
+        0x45, 0x58, 0xbb, 0xdd, 0x28, 0x1d, 0x68, 0xfc, 0xb4, 0xc6, 0xf6, 0x92, 0xf6, 0x30, 0x03,
+        0xaa, 0xe4, 0x32, 0xf6, 0x34, 0x51, 0x4b, 0x0f, 0x8c, 0xf9, 0xac, 0x98, 0x22, 0xfb, 0x49,
+        0xc8, 0xbf, 0xca, 0x8c, 0x80, 0x86, 0x5d, 0xd7, 0xa4, 0x52, 0xb1, 0xd9, 0xa6, 0x04, 0x4e,
+        0xb3, 0x2d, 0x1f, 0xb8, 0x35, 0xcc, 0x45, 0x6d, 0x9c, 0x20, 0xa7, 0xa4, 0x34, 0x59, 0x72,
+        0xe3, 0xae, 0xba, 0x49, 0xde, 0xd1, 0xaa, 0xee, 0x3d, 0x77, 0xfc, 0x5d, 0xc6, 0x1f, 0x9d,
+        0xac, 0xc2, 0x15, 0x66, 0xb8, 0xe1, 0x54, 0x4e, 0x74, 0x93, 0xdb, 0x9a, 0x24, 0x15, 0x6e,
+        0x20, 0xa3, 0x67, 0x3e, 0x5a, 0x24, 0x41, 0x5e, 0xb0, 0xe6, 0x35, 0x87, 0x1b, 0xc8, 0x7a,
+        0xf9, 0x77, 0x65, 0xe0, 0x01, 0xf2, 0x4c, 0xe4, 0x2b, 0xa9, 0x64, 0x96, 0x96, 0x0b, 0x46,
+        0xca, 0xea, 0x79, 0x0e, 0x78, 0xa3, 0x5f, 0x43, 0xfc, 0x47, 0x6a, 0x12, 0xfa, 0xc4, 0x33,
+        0x0e, 0x88, 0x1c, 0x19, 0x3a, 0x00, 0xc3, 0x4e, 0xb5, 0xd8, 0xfa, 0x8e, 0xf1, 0xbc, 0x3d,
+        0xb2, 0x7e, 0x50, 0x8d, 0x67, 0xc3, 0x6b, 0xed, 0xe2, 0xea, 0xa6, 0x1f, 0x25, 0x24, 0x7c,
+        0x94, 0x74, 0x50, 0x49, 0xe3, 0xc6, 0x58, 0x2e, 0xfd, 0x28, 0xb4, 0xc6, 0x73, 0xb1, 0x53,
+        0x74, 0x27, 0x94, 0x5c, 0xdf, 0x69, 0xb7, 0xa1, 0xd7, 0xf5, 0xd3, 0x8a, 0x2c, 0x2d, 0xb4,
+        0x5e, 0x8a, 0x16, 0x14, 0x54, 0x64, 0x6e, 0x00, 0x6b, 0x11, 0x59, 0x8a, 0x63, 0x38, 0x80,
+        0x76, 0xc3, 0xd5, 0x59, 0xf7, 0x3f, 0xd2, 0xfa, 0xa5, 0xca, 0x82, 0xff, 0x4a, 0x62, 0xf0,
+        0xe3, 0x42, 0xf9, 0x3b, 0x38, 0x27, 0x8a, 0x89, 0xaa, 0x50, 0x55, 0x4b, 0x29, 0xf1, 0x46,
+        0x7c, 0x75, 0xef, 0x65, 0xaf, 0x9b, 0x0d, 0x6d, 0xda, 0x25, 0x94, 0x14, 0xc1, 0x1b, 0xf0,
+        0xc5, 0x4c, 0x24, 0x0e, 0x65,
+};
+
+const uint8_t kOutRefBinaryBuffer[] = {
+        0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb,
+        0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03,
+        0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06,
+        0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8,
+        0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72,
+        0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d,
+        0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63,
+        0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30,
+        0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+        0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f,
+        0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x2d, 0x20,
+        0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d,
+        0x30, 0x20, 0x72, 0x65, 0x66, 0x3d, 0x32, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
+        0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x65, 0x3d,
+        0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20, 0x6d, 0x65, 0x3d, 0x68, 0x65,
+        0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, 0x3d, 0x37, 0x20, 0x70, 0x73, 0x79, 0x3d, 0x31,
+        0x20, 0x70, 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30, 0x30, 0x3a, 0x30, 0x2e,
+        0x30, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x3d, 0x31, 0x20,
+        0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72,
+        0x6f, 0x6d, 0x61, 0x5f, 0x6d, 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69,
+        0x73, 0x3d, 0x31, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x71,
+        0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, 0x6f, 0x6e, 0x65, 0x3d, 0x32, 0x31,
+        0x2c, 0x31, 0x31, 0x20, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x73, 0x6b, 0x69, 0x70, 0x3d,
+        0x31, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66,
+        0x73, 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d,
+        0x36, 0x30, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x68,
+        0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x35, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x64, 0x5f,
+        0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x30, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20,
+        0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x69, 0x6e, 0x74, 0x65,
+        0x72, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x3d, 0x30, 0x20, 0x62, 0x6c, 0x75, 0x72, 0x61, 0x79,
+        0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74,
+        0x72, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x3d, 0x30, 0x20,
+        0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x30, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68,
+        0x74, 0x70, 0x3d, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x3d, 0x32, 0x35, 0x30,
+        0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x3d, 0x32, 0x35, 0x20,
+        0x73, 0x63, 0x65, 0x6e, 0x65,
+};
+
+class MediaCasListener : public ICasListener {
+  public:
+    virtual Return<void> onEvent(int32_t event, int32_t arg,
+                                 const hidl_vec<uint8_t>& data) override {
+        android::Mutex::Autolock autoLock(mMsgLock);
+        mEvent = event;
+        mEventArg = arg;
+        mEventData = data;
+
+        mEventReceived = true;
+        mMsgCondition.signal();
+        return Void();
+    }
+
+    virtual Return<void> onSessionEvent(const hidl_vec<uint8_t>& sessionId, int32_t event,
+                                        int32_t arg, const hidl_vec<uint8_t>& data) override {
+        android::Mutex::Autolock autoLock(mMsgLock);
+        mSessionId = sessionId;
+        mEvent = event;
+        mEventArg = arg;
+        mEventData = data;
+
+        mEventReceived = true;
+        mMsgCondition.signal();
+        return Void();
+    }
+
+    void testEventEcho(sp<ICas>& mediaCas, int32_t& event, int32_t& eventArg,
+                       hidl_vec<uint8_t>& eventData);
+
+    void testSessionEventEcho(sp<ICas>& mediaCas, const hidl_vec<uint8_t>& sessionId,
+                              int32_t& event, int32_t& eventArg, hidl_vec<uint8_t>& eventData);
+
+  private:
+    int32_t mEvent = -1;
+    int32_t mEventArg = -1;
+    bool mEventReceived = false;
+    hidl_vec<uint8_t> mEventData;
+    hidl_vec<uint8_t> mSessionId;
+    android::Mutex mMsgLock;
+    android::Condition mMsgCondition;
+};
+
+void MediaCasListener::testEventEcho(sp<ICas>& mediaCas, int32_t& event, int32_t& eventArg,
+                                     hidl_vec<uint8_t>& eventData) {
+    mEventReceived = false;
+    auto returnStatus = mediaCas->sendEvent(event, eventArg, eventData);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    android::Mutex::Autolock autoLock(mMsgLock);
+    while (!mEventReceived) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            EXPECT_TRUE(false) << "event not received within timeout";
+            return;
+        }
+    }
+
+    EXPECT_EQ(mEvent, event);
+    EXPECT_EQ(mEventArg, eventArg);
+    EXPECT_TRUE(mEventData == eventData);
+}
+
+void MediaCasListener::testSessionEventEcho(sp<ICas>& mediaCas, const hidl_vec<uint8_t>& sessionId,
+                                            int32_t& event, int32_t& eventArg,
+                                            hidl_vec<uint8_t>& eventData) {
+    mEventReceived = false;
+    auto returnStatus = mediaCas->sendSessionEvent(sessionId, event, eventArg, eventData);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    android::Mutex::Autolock autoLock(mMsgLock);
+    while (!mEventReceived) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            EXPECT_TRUE(false) << "event not received within timeout";
+            return;
+        }
+    }
+
+    EXPECT_TRUE(mSessionId == sessionId);
+    EXPECT_EQ(mEvent, event);
+    EXPECT_EQ(mEventArg, eventArg);
+    EXPECT_TRUE(mEventData == eventData);
+}
+
+// Test environment for Cas HIDL HAL.
+class CasHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+  public:
+    // get the test environment singleton
+    static CasHidlEnvironment* Instance() {
+        static CasHidlEnvironment* instance = new CasHidlEnvironment;
+        return instance;
+    }
+
+    virtual void registerTestServices() override { registerTestService<IMediaCasService>(); }
+};
+
+class MediaCasHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+  public:
+    virtual void SetUp() override {
+        mService = ::testing::VtsHalHidlTargetTestBase::getService<IMediaCasService>(
+                CasHidlEnvironment::Instance()->getServiceName<IMediaCasService>());
+        ASSERT_NE(mService, nullptr);
+    }
+
+    sp<IMediaCasService> mService;
+
+  protected:
+    static void description(const std::string& description) {
+        RecordProperty("description", description);
+    }
+
+    sp<ICas> mMediaCas;
+    sp<IDescramblerBase> mDescramblerBase;
+    sp<MediaCasListener> mCasListener;
+    typedef struct _OobInputTestParams {
+        const SubSample* subSamples;
+        uint32_t numSubSamples;
+        size_t imemSizeActual;
+        uint64_t imemOffset;
+        uint64_t imemSize;
+        uint64_t srcOffset;
+        uint64_t dstOffset;
+    } OobInputTestParams;
+
+    ::testing::AssertionResult createCasPlugin(int32_t caSystemId);
+    ::testing::AssertionResult openCasSession(std::vector<uint8_t>* sessionId);
+    ::testing::AssertionResult descrambleTestInputBuffer(const sp<IDescrambler>& descrambler,
+                                                         Status* descrambleStatus,
+                                                         sp<IMemory>* hidlInMemory);
+    ::testing::AssertionResult descrambleTestOobInput(const sp<IDescrambler>& descrambler,
+                                                      Status* descrambleStatus,
+                                                      const OobInputTestParams& params);
+};
+
+::testing::AssertionResult MediaCasHidlTest::createCasPlugin(int32_t caSystemId) {
+    auto status = mService->isSystemIdSupported(caSystemId);
+    if (!status.isOk() || !status) {
+        return ::testing::AssertionFailure();
+    }
+    status = mService->isDescramblerSupported(caSystemId);
+    if (!status.isOk() || !status) {
+        return ::testing::AssertionFailure();
+    }
+
+    mCasListener = new MediaCasListener();
+    auto pluginStatus = mService->createPluginExt(caSystemId, mCasListener);
+    if (!pluginStatus.isOk()) {
+        return ::testing::AssertionFailure();
+    }
+    mMediaCas = pluginStatus;
+    if (mMediaCas == nullptr) {
+        return ::testing::AssertionFailure();
+    }
+
+    auto descramblerStatus = mService->createDescrambler(caSystemId);
+    if (!descramblerStatus.isOk()) {
+        return ::testing::AssertionFailure();
+    }
+    mDescramblerBase = descramblerStatus;
+    return ::testing::AssertionResult(mDescramblerBase != nullptr);
+}
+
+::testing::AssertionResult MediaCasHidlTest::openCasSession(std::vector<uint8_t>* sessionId) {
+    Status sessionStatus;
+    auto returnVoid = mMediaCas->openSession([&](Status status, const hidl_vec<uint8_t>& id) {
+        sessionStatus = status;
+        *sessionId = id;
+    });
+    return ::testing::AssertionResult(returnVoid.isOk() && (Status::OK == sessionStatus));
+}
+
+::testing::AssertionResult MediaCasHidlTest::descrambleTestInputBuffer(
+        const sp<IDescrambler>& descrambler, Status* descrambleStatus, sp<IMemory>* inMemory) {
+    hidl_vec<SubSample> hidlSubSamples;
+    hidlSubSamples.setToExternal(const_cast<SubSample*>(kSubSamples),
+                                 (sizeof(kSubSamples) / sizeof(SubSample)), false /*own*/);
+
+    sp<MemoryDealer> dealer = new MemoryDealer(sizeof(kInBinaryBuffer), "vts-cas");
+    if (nullptr == dealer.get()) {
+        ALOGE("couldn't get MemoryDealer!");
+        return ::testing::AssertionFailure();
+    }
+
+    sp<IMemory> mem = dealer->allocate(sizeof(kInBinaryBuffer));
+    if (nullptr == mem.get()) {
+        ALOGE("couldn't allocate IMemory!");
+        return ::testing::AssertionFailure();
+    }
+    *inMemory = mem;
+
+    // build HidlMemory from memory heap
+    ssize_t offset;
+    size_t size;
+    sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+    if (nullptr == heap.get()) {
+        ALOGE("couldn't get memory heap!");
+        return ::testing::AssertionFailure();
+    }
+
+    uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mem->pointer()));
+    memcpy(ipBuffer, kInBinaryBuffer, sizeof(kInBinaryBuffer));
+
+    // hidlMemory is not to be passed out of scope!
+    sp<HidlMemory> hidlMemory = fromHeap(heap);
+
+    SharedBuffer srcBuffer = {
+            .heapBase = *hidlMemory, .offset = (uint64_t)offset, .size = (uint64_t)size};
+
+    DestinationBuffer dstBuffer;
+    dstBuffer.type = BufferType::SHARED_MEMORY;
+    dstBuffer.nonsecureMemory = srcBuffer;
+
+    uint32_t outBytes;
+    hidl_string detailedError;
+    auto returnVoid = descrambler->descramble(
+            ScramblingControl::EVENKEY /*2*/, hidlSubSamples, srcBuffer, 0, dstBuffer, 0,
+            [&](Status status, uint32_t bytesWritten, const hidl_string& detailedErr) {
+                *descrambleStatus = status;
+                outBytes = bytesWritten;
+                detailedError = detailedErr;
+            });
+    if (!returnVoid.isOk() || *descrambleStatus != Status::OK) {
+        ALOGI("descramble failed, trans=%s, status=%d, outBytes=%u, error=%s",
+              returnVoid.description().c_str(), *descrambleStatus, outBytes, detailedError.c_str());
+    }
+    return ::testing::AssertionResult(returnVoid.isOk());
+}
+
+::testing::AssertionResult MediaCasHidlTest::descrambleTestOobInput(
+        const sp<IDescrambler>& descrambler, Status* descrambleStatus,
+        const OobInputTestParams& params) {
+    hidl_vec<SubSample> hidlSubSamples;
+    hidlSubSamples.setToExternal(const_cast<SubSample*>(params.subSamples), params.numSubSamples,
+                                 false /*own*/);
+
+    sp<MemoryDealer> dealer = new MemoryDealer(params.imemSizeActual, "vts-cas");
+    if (nullptr == dealer.get()) {
+        ALOGE("couldn't get MemoryDealer!");
+        return ::testing::AssertionFailure();
+    }
+
+    sp<IMemory> mem = dealer->allocate(params.imemSizeActual);
+    if (nullptr == mem.get()) {
+        ALOGE("couldn't allocate IMemory!");
+        return ::testing::AssertionFailure();
+    }
+
+    // build HidlMemory from memory heap
+    ssize_t offset;
+    size_t size;
+    sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+    if (nullptr == heap.get()) {
+        ALOGE("couldn't get memory heap!");
+        return ::testing::AssertionFailure();
+    }
+
+    // hidlMemory is not to be passed out of scope!
+    sp<HidlMemory> hidlMemory = fromHeap(heap);
+
+    SharedBuffer srcBuffer = {
+            .heapBase = *hidlMemory,
+            .offset = (uint64_t)offset + params.imemOffset,
+            .size = (uint64_t)params.imemSize,
+    };
+
+    DestinationBuffer dstBuffer;
+    dstBuffer.type = BufferType::SHARED_MEMORY;
+    dstBuffer.nonsecureMemory = srcBuffer;
+
+    uint32_t outBytes;
+    hidl_string detailedError;
+    auto returnVoid = descrambler->descramble(
+            ScramblingControl::EVENKEY /*2*/, hidlSubSamples, srcBuffer, params.srcOffset,
+            dstBuffer, params.dstOffset,
+            [&](Status status, uint32_t bytesWritten, const hidl_string& detailedErr) {
+                *descrambleStatus = status;
+                outBytes = bytesWritten;
+                detailedError = detailedErr;
+            });
+    if (!returnVoid.isOk() || *descrambleStatus != Status::OK) {
+        ALOGI("descramble failed, trans=%s, status=%d, outBytes=%u, error=%s",
+              returnVoid.description().c_str(), *descrambleStatus, outBytes, detailedError.c_str());
+    }
+    return ::testing::AssertionResult(returnVoid.isOk());
+}
+
+TEST_F(MediaCasHidlTest, TestClearKeyApisWithSession) {
+    description("Test that valid call sequences with SessionEvent send and receive");
+
+    ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID));
+
+    auto returnStatus = mMediaCas->provision(hidl_string(PROVISION_STR));
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    hidl_vec<uint8_t> hidlPvtData;
+    hidlPvtData.resize(256);
+    returnStatus = mMediaCas->setPrivateData(hidlPvtData);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    std::vector<uint8_t> sessionId;
+    ASSERT_TRUE(openCasSession(&sessionId));
+    returnStatus = mMediaCas->setSessionPrivateData(sessionId, hidlPvtData);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    std::vector<uint8_t> streamSessionId;
+    ASSERT_TRUE(openCasSession(&streamSessionId));
+    returnStatus = mMediaCas->setSessionPrivateData(streamSessionId, hidlPvtData);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    returnStatus = mDescramblerBase->setMediaCasSession(sessionId);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    returnStatus = mDescramblerBase->setMediaCasSession(streamSessionId);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    hidl_vec<uint8_t> hidlNullPtr;
+    hidlNullPtr.setToExternal(static_cast<uint8_t*>(nullptr), 0);
+    returnStatus = mMediaCas->refreshEntitlements(3, hidlNullPtr);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    uint8_t refreshData[] = {0, 1, 2, 3};
+    hidl_vec<uint8_t> hidlRefreshData;
+    hidlRefreshData.setToExternal(static_cast<uint8_t*>(refreshData), sizeof(refreshData));
+    returnStatus = mMediaCas->refreshEntitlements(10, hidlRefreshData);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    int32_t eventID = 1;
+    int32_t eventArg = 2;
+    mCasListener->testEventEcho(mMediaCas, eventID, eventArg, hidlNullPtr);
+    mCasListener->testSessionEventEcho(mMediaCas, sessionId, eventID, eventArg, hidlNullPtr);
+
+    eventID = 3;
+    eventArg = 4;
+    uint8_t eventData[] = {'e', 'v', 'e', 'n', 't', 'd', 'a', 't', 'a'};
+    hidl_vec<uint8_t> hidlEventData;
+    hidlEventData.setToExternal(static_cast<uint8_t*>(eventData), sizeof(eventData));
+    mCasListener->testEventEcho(mMediaCas, eventID, eventArg, hidlEventData);
+    mCasListener->testSessionEventEcho(mMediaCas, sessionId, eventID, eventArg, hidlEventData);
+
+    uint8_t clearKeyEmmData[] = {'c', 'l', 'e', 'a', 'r', 'k', 'e', 'y', 'e', 'm', 'm'};
+    hidl_vec<uint8_t> hidlClearKeyEmm;
+    hidlClearKeyEmm.setToExternal(static_cast<uint8_t*>(clearKeyEmmData), sizeof(clearKeyEmmData));
+    returnStatus = mMediaCas->processEmm(hidlClearKeyEmm);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    hidl_vec<uint8_t> hidlEcm;
+    hidlEcm.setToExternal(const_cast<uint8_t*>(kEcmBinaryBuffer), sizeof(kEcmBinaryBuffer));
+    returnStatus = mMediaCas->processEcm(sessionId, hidlEcm);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+    returnStatus = mMediaCas->processEcm(streamSessionId, hidlEcm);
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    EXPECT_FALSE(mDescramblerBase->requiresSecureDecoderComponent("video/avc"));
+
+    sp<IDescrambler> descrambler;
+    descrambler = IDescrambler::castFrom(mDescramblerBase);
+    ASSERT_NE(descrambler, nullptr);
+
+    Status descrambleStatus = Status::OK;
+    sp<IMemory> dataMemory;
+
+    ASSERT_TRUE(descrambleTestInputBuffer(descrambler, &descrambleStatus, &dataMemory));
+    EXPECT_EQ(Status::OK, descrambleStatus);
+
+    ASSERT_NE(nullptr, dataMemory.get());
+    uint8_t* opBuffer = static_cast<uint8_t*>(static_cast<void*>(dataMemory->pointer()));
+
+    int compareResult =
+            memcmp(static_cast<const void*>(opBuffer),
+                   static_cast<const void*>(kOutRefBinaryBuffer), sizeof(kOutRefBinaryBuffer));
+    EXPECT_EQ(0, compareResult);
+
+    returnStatus = mDescramblerBase->release();
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+
+    returnStatus = mMediaCas->release();
+    EXPECT_TRUE(returnStatus.isOk());
+    EXPECT_EQ(Status::OK, returnStatus);
+}
+
+}  // anonymous namespace
+
+int main(int argc, char** argv) {
+    ::testing::AddGlobalTestEnvironment(CasHidlEnvironment::Instance());
+    ::testing::InitGoogleTest(&argc, argv);
+    CasHidlEnvironment::Instance()->init(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    LOG(INFO) << "Test result = " << status;
+    return status;
+}
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 94ffe80..244d7a0 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -1,5 +1,5 @@
 <compatibility-matrix version="1.0" type="framework" level="4">
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.atrace</name>
         <version>1.0</version>
         <interface>
@@ -113,7 +113,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.camera.provider</name>
-        <version>2.4</version>
+        <version>2.4-5</version>
         <interface>
             <name>ICameraProvider</name>
             <regex-instance>[^/]+/[0-9]+</regex-instance>
@@ -121,7 +121,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.cas</name>
-        <version>1.0</version>
+        <version>1.1</version>
         <interface>
             <name>IMediaCasService</name>
             <instance>default</instance>
@@ -153,19 +153,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.drm</name>
-        <version>1.0-1</version>
-        <interface>
-            <name>ICryptoFactory</name>
-            <regex-instance>.*</regex-instance>
-        </interface>
-        <interface>
-            <name>IDrmFactory</name>
-            <regex-instance>.*</regex-instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.drm</name>
-        <version>1.2</version>
+        <version>1.0-2</version>
         <interface>
             <name>ICryptoFactory</name>
             <regex-instance>.*</regex-instance>
diff --git a/configstore/utils/ConfigStoreUtils.cpp b/configstore/utils/ConfigStoreUtils.cpp
index 5a1fb42..8cb61a9 100644
--- a/configstore/utils/ConfigStoreUtils.cpp
+++ b/configstore/utils/ConfigStoreUtils.cpp
@@ -23,12 +23,12 @@
 namespace hardware {
 namespace details {
 
-bool wouldLogInfo() {
-    return WOULD_LOG(INFO);
+bool wouldLogVerbose() {
+    return WOULD_LOG(VERBOSE);
 }
 
-void logAlwaysInfo(const std::string& message) {
-    LOG(INFO) << message;
+void logAlwaysVerbose(const std::string& message) {
+    LOG(VERBOSE) << message;
 }
 
 void logAlwaysError(const std::string& message) {
diff --git a/configstore/utils/include/configstore/Utils.h b/configstore/utils/include/configstore/Utils.h
index e04f57d..d65f41f 100644
--- a/configstore/utils/include/configstore/Utils.h
+++ b/configstore/utils/include/configstore/Utils.h
@@ -29,8 +29,8 @@
 namespace details {
 // Templated classes can use the below method
 // to avoid creating dependencies on liblog.
-bool wouldLogInfo();
-void logAlwaysInfo(const std::string& message);
+bool wouldLogVerbose();
+void logAlwaysVerbose(const std::string& message);
 void logAlwaysError(const std::string& message);
 }  // namespace details
 
@@ -92,7 +92,7 @@
     };
     static V cachedValue = getHelper();
 
-    if (wouldLogInfo()) {
+    if (wouldLogVerbose()) {
         std::string iname = __PRETTY_FUNCTION__;
         // func name starts with "func = " in __PRETTY_FUNCTION__
         auto pos = iname.find("func = ");
@@ -107,7 +107,7 @@
         oss << iname << " retrieved: "
             << (cachedValue.specified ? cachedValue.value : defValue)
             << (cachedValue.specified ? "" : " (default)");
-        logAlwaysInfo(oss.str());
+        logAlwaysVerbose(oss.str());
     }
 
     return cachedValue.specified ? cachedValue.value : defValue;
diff --git a/current.txt b/current.txt
index 3b87306..ce8e05f 100644
--- a/current.txt
+++ b/current.txt
@@ -399,9 +399,157 @@
 417ab60fe1ef786778047e4486f3d868ebce570d91addd8fe4251515213072de android.hardware.neuralnetworks@1.0::types
 e22e8135d061d0e9c4c1a70c25c19fdba10f4d3cda9795ef25b6392fc520317c android.hardware.neuralnetworks@1.1::types
 1d4a5776614c08b5d794a5ec5ab04697260cbd4b3441d5935cd53ee71d19da02 android.hardware.radio@1.0::IRadioResponse
-271187e261b30c01a33011aea257c07a2d2f05b72943ebee89e973e997849973 android.hardware.radio@1.0::types
+ed9da80ec0c96991fd03f0a46107815d0e50f764656e49dba4980fa5c31d5bc3 android.hardware.radio@1.0::types
 1d19720d4fd38b1095f0f555a4bd92b3b12c9b1d0f560b0e9a474cd6dcc20db6 android.hardware.radio@1.2::IRadio
+cd1757867a5e3a3faa362e785239515870d1a3c9ce756c6f0cf0f0fd8aac2547 android.hardware.radio@1.2::types
 e78cf871f9fd1c072874e481e06e18e2681763cf2aa38c1fd777d53bab4eb69b android.hardware.sensors@1.0::types
 3d01e29e8129186f7567c4f9c8bee7480a0768e587b1be9b28adb0a6cbec6bf2 android.hardware.tv.cec@1.0::types
 1722ad002317b1fae1400de709e90f442d94ef22864e05f7a12af48c32e8edc8 android.hardware.usb@1.1::types
 29c8da7a13c40d488f569c812441d5754ee45bdcdb8ce6564f524b708d10a057 android.hardware.vibrator@1.1::types
+
+# HALs released in Android Q
+438dc52ab820befb7a11e953e82110f0d8c91cdf96ef62be921efc64f5a3d580 android.hardware.atrace@1.0::IAtraceDevice
+20b9f81bb0b1f812f150ec94d42648b01087f2344ea91df0416bce0fb6cdfbd4 android.hardware.atrace@1.0::types
+dfdb4d04b65dc363e5621c85bfdf3023c277b75c31d821d8e71b3f44f198e214 android.hardware.audio@5.0::IDevice
+0a911297821854985cfcdb17b63d7948af0f0f51ce8c68cc86367c185bbc772e android.hardware.audio@5.0::IDevicesFactory
+ce2e8c6c8559fd42bd69e0dee27b4d9c93cd9b2eff487b4e6b6395b6a1a993d6 android.hardware.audio@5.0::IPrimaryDevice
+4a4e5e5d9357004a1256bde8d36010ee00c51cea811a1c1e0dd969a9fc0bf862 android.hardware.audio@5.0::IStream
+e05e48c583de14c1e5a6fa9d48ea50244e3e0924b76b342374e7471dc8007ba9 android.hardware.audio@5.0::IStreamIn
+9471b12b1c255bb530695720bc4174bd74987b75b1f820854af8944bc8c215c9 android.hardware.audio@5.0::IStreamOut
+1b0500367ed2b32a841667ac3200edf3d3a164e8004aca445ff1b085ac831e93 android.hardware.audio@5.0::IStreamOutCallback
+83e365479cc77d8717c155e1787ee668cd2ae4c557b467cf75b8e7cd53697ad8 android.hardware.audio@5.0::types
+894af04bebfe7da5b6791eefeb6eb3627da63d5efea735f16876d11d8ca4f61d android.hardware.audio.common@5.0::types
+f269297866765b95ddd1825676cc8a772f0c7c9863286df596fc302781a42ff5 android.hardware.audio.effect@5.0::IAcousticEchoCancelerEffect
+fa187b602d8939644ef708ed7627f2e3deac97899a4bda1de07f2ff126abe243 android.hardware.audio.effect@5.0::IAutomaticGainControlEffect
+e1bf864ccb8458c0da1dcc74a2e748b1dca8ac360df590591cf82d98292d7981 android.hardware.audio.effect@5.0::IBassBoostEffect
+d3867dcf1f46f1962d258c56151b5afb2ef26e9835fef73f01115492d58da5b7 android.hardware.audio.effect@5.0::IDownmixEffect
+445a65ea2edd1ccba8a8d8e1c88d24c6ea67e57d355fa446ab390e20f0db14aa android.hardware.audio.effect@5.0::IEffect
+0ac2e951f359e87644dcf642f488194f6b02563f89cfe8dd668ab7c422be89b1 android.hardware.audio.effect@5.0::IEffectBufferProviderCallback
+9019e7ac057b8105a9b9ac8974b13e1ded46d1006e5a544aec510dfaf92755c6 android.hardware.audio.effect@5.0::IEffectsFactory
+c8e5a7e450a5b201c286814d484d04c7b9c6cab71b4982b34b84177e818909db android.hardware.audio.effect@5.0::IEnvironmentalReverbEffect
+cf356f430a1bb42fd91af65f105e07fa84a5927dc45c36d8ffce5b6c132c1f3c android.hardware.audio.effect@5.0::IEqualizerEffect
+8c31b0cad211968dc6008dcb2b116bf5f12079c8dce920e79242e7839104138e android.hardware.audio.effect@5.0::ILoudnessEnhancerEffect
+6f0f86b549e30382619877784857c40d4c64978c1e9eef3c034bdd367aac7468 android.hardware.audio.effect@5.0::INoiseSuppressionEffect
+ca15a738dedc2f4981925f7d7ff29c22bc3f8a848403dcf0c592c167de09d9af android.hardware.audio.effect@5.0::IPresetReverbEffect
+443659bb9e27221e5da0d16c7a0ecb2dc3a9a03acc8a0b2196b47c50735e2d2e android.hardware.audio.effect@5.0::IVirtualizerEffect
+78fed26a781cdca1b3bcb37520bff705d7764ee81db9cfd37014953c7ad2596e android.hardware.audio.effect@5.0::IVisualizerEffect
+6385b6accab8a544e2ee54ba7bf5aa55dff6153bcedd80fdaae16fe9e0be7050 android.hardware.audio.effect@5.0::types
+2f11e4c10ebe2b600426e0695f3c720d21663501c1c9449537055f13f37600d3 android.hardware.biometrics.face@1.0::IBiometricsFace
+dfb0666af59eb306c82a6f576c65a160e6829d3324211a10429fd63768df70df android.hardware.biometrics.face@1.0::IBiometricsFaceClientCallback
+cc40d308f38b6a218fcf99f264ebb49544fce670a6abdf294c617357a3d83dad android.hardware.biometrics.face@1.0::types
+ecedc58dbcdb13503c19c0ab160ac1dd0530bb1471164149282dd1463c684185 android.hardware.bluetooth.audio@2.0::IBluetoothAudioPort
+fb9c40e4deab40be5476477078fe3d8a4a4495fd9deef4321878d169d675c633 android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvider
+f7431f3e3e4e3387fc6f27a6cf423eddcd824a395dc4349d302c995ab44a9895 android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvidersFactory
+447a5c9bb0f1a5ed3f1dfe5216afe4be2f4362111d95479670eec4cae4d7d5f7 android.hardware.bluetooth.audio@2.0::types
+09ab9b24994429d9bb32a3fb420b6f6be3e47eb655139a2c08c4e80d3f33ff95 android.hardware.camera.device@3.5::ICameraDevice
+06237de53c42890029e3f8fe7d1480d078469c0d07608e51c37b4d485d342992 android.hardware.camera.device@3.5::ICameraDeviceCallback
+08c68b196e2fc4e5ba67ba0d0917bde828a87cbe2cffec19d04733972da9eb49 android.hardware.camera.device@3.5::ICameraDeviceSession
+acaba39216973e58949f50978762bcda1c29f5f7e0bca3e08db21f0767356130 android.hardware.camera.device@3.5::types
+74ec7732fdacb22292c907b49f8f933510851ea1b3ed195c4dcdff35a20387f5 android.hardware.camera.metadata@3.4::types
+0fb39a7809ad1c52b3efbbed5ef4749b06c2a4f1f19cdc3efa2e3d9b28f1205c android.hardware.camera.provider@2.5::ICameraProvider
+f5777403d65135a5407723671bc7a864cdca83aea13ee3ce2894b95e6588ca3a android.hardware.camera.provider@2.5::types
+44c88954b3c201b26f64fcdb6f278024ab3aae864a9e1ec70e8a74274ae9d6aa android.hardware.cas@1.1::ICas
+25012d1778f7396f967bbc0231397d544bde421ba5b98706c9e48ac790612683 android.hardware.cas@1.1::ICasListener
+dffacdbe0bcf8443013de5bdc56a83479ad979d4919ed15a5585539f46091f07 android.hardware.cas@1.1::IMediaCasService
+bc742c6b17c834010f90c06ce2939c845198eecd2706be18da99e14bceb2acc0 android.hardware.configstore@1.2::ISurfaceFlingerConfigs
+aadac6b9fcf660384d73cbb97f3732ff9eeb8e8c67001e3fdbf4add108e5b1dc android.hardware.configstore@1.2::types
+5b1f4a4fb88c239e07d76026467a1f2ee0d08f4d52c1805bd93bd7c05e3fe69c android.hardware.drm@1.2::ICryptoFactory
+4895f98e9ef210e9acb01982f5d07b654538377e1404b8db5e19e7858835e9d8 android.hardware.drm@1.2::ICryptoPlugin
+976116b9033b2c222b940109fdf0ffcc29b77cbe631ef6b4fcc2ad5ce8e605f7 android.hardware.drm@1.2::IDrmFactory
+b2efccc6425085f84795a2ca15a09d9a81ffd02f9dc3d30ba21d1a59bdfa253f android.hardware.drm@1.2::IDrmPlugin
+39ca9e88404b6c090f7650455a7ed3fdee9cce4e3a356c9d547f8ff02f2e7fc8 android.hardware.drm@1.2::IDrmPluginListener
+f27baaa587bc3dd9b740cb6928ab812b9b7d105b5187663938aee578105f3c39 android.hardware.drm@1.2::types
+44480c912e4ab90b9ed17e56569cd5ca98413a8a2372efb028f4181204b6b73e android.hardware.fastboot@1.0::IFastboot
+7b2989744e3c555292d4b5b829acd09a7b40f96ead62ce54174cd959503b64bb android.hardware.fastboot@1.0::types
+7f460e795f5d1ed5e378935f98c6db4d39497de988aef1b4c2a4a07a6c400392 android.hardware.gnss@2.0::IAGnss
+2e5ad983734069e84a760004b32da0d09e4170c05380abe27e6eb80e4aa70d5a android.hardware.gnss@2.0::IAGnssCallback
+1f4ac068a88a72360280d94a7f6fd7c63813c1eea4891a0eb01394d3e7e775f2 android.hardware.gnss@2.0::IAGnssRil
+6e2f9a44375a0ae0b49ca7d711cb88945189d398535078408269e1e85889061d android.hardware.gnss@2.0::IGnss
+54d253b10c7da2a4a708d11acda3118b283df1540bc10323a5a3773c94cc8e71 android.hardware.gnss@2.0::IGnssCallback
+ecc966c68bddbd95c8dae782b84204cf01c75734675e8769963f3b5106ec128b android.hardware.gnss@2.0::IGnssConfiguration
+c67759f5d6387d273b66729180d03690e827f0b6b8d4e13ce2ff42d31b224065 android.hardware.gnss@2.0::IGnssMeasurement
+089338944c45f66f25ba4ee958c161c42fefeb73ec60e4451f3535a1b3fd10c7 android.hardware.gnss@2.0::IGnssMeasurementCallback
+9e66234e65bcde75733d75d8b5d5cc094c2a5e14b074a25cd3f9ad141dc56f60 android.hardware.gnss@2.0::types
+50623a69a88b1c8a05738e4af7d5f78e905f415ccb0e84c99d0a71ea182e9393 android.hardware.gnss.measurement_corrections@1.0::IMeasurementCorrections
+6ef12cd95df73f8f80c25eb035d98ca4594f9cee571fdabea838a0b6016dd908 android.hardware.gnss.measurement_corrections@1.0::types
+0d278956d7fc6fdf9ca9c42962ff2d73967bbb1c9f0b3e0b58d71b7095c286bc android.hardware.gnss.visibility_control@1.0::IGnssVisibilityControl
+0d99e34500cfc2d40b684cb4dea7ebd89d4aff9f5315ed36b33442a7a88c138c android.hardware.gnss.visibility_control@1.0::IGnssVisibilityControlCallback
+6b2d8dfa3db505c34a3a19082d8737c86bd859ec00f0e6c5fd19cce3c1ef95d1 android.hardware.graphics.allocator@3.0::IAllocator
+2a81d3132df91614a30ca45dfe088d67ddbb30240ab8c25feb6b8110d7ec3800 android.hardware.graphics.bufferqueue@2.0::IGraphicBufferProducer
+b826892686850a9cf2b60ca5845db7185c2196ea4dd765cd80cd163169678a78 android.hardware.graphics.bufferqueue@2.0::IProducerListener
+01c6398c90fc6be0640810e2c5d8a4863b457280132bb3f97dd5682e19632b62 android.hardware.graphics.bufferqueue@2.0::types
+7a2d64095252f85781b2d521f4f11d04ce774544feececcec2088c568656e93c android.hardware.graphics.common@1.2::types
+3dff04a36b86660b5807414587e530bb0c294ed56fdff06f8915ba0a9b73f974 android.hardware.graphics.composer@2.3::IComposer
+ed0cf91532e88dff073cf7f45b9c65ee1c88553ebcb97892f7b7072abb9b822a android.hardware.graphics.composer@2.3::IComposerClient
+5c8bf8e1af9efe225a4661db8c08ff1b7e13fdc8ed49f35291bd0b6c9436b8f2 android.hardware.graphics.mapper@3.0::IMapper
+7183d9d9acfa41a61a64bdfed548e98299265a7bb1821a3ed204173b5c2cfd4a android.hardware.graphics.mapper@3.0::types
+c3f831a66d5815baf74f5b82fe79cf099542ddae4dfab3f388e1d41828e794fc android.hardware.health.storage@1.0::IGarbageCollectCallback
+dd1ec219f5d2e2b33c6c0bcb92e63bbedb36f7c716413462848f6b6ae74fc864 android.hardware.health.storage@1.0::IStorage
+2b4a14661e6a38617b7dd0c6ebb66a56a90e564674ac7697a14cb8a0cab92b2f android.hardware.health.storage@1.0::types
+30006fde4cb1f255f2530208728bff692100411b20af5b66fa31a9196d51f00b android.hardware.input.classifier@1.0::IInputClassifier
+0300c7667030da36c3de585f176ce18ff4b0d2615446d4930f331097378c06ef android.hardware.input.common@1.0::types
+24ae089981d58bc4cc74d75a6055bf357338ae6744ce1b467c5b4a9c470aba6d android.hardware.media.bufferpool@2.0::IAccessor
+897f45ee7db24ef227dea83ca3e4de72d53ff6bb7adc7983c90a650a1a6ff576 android.hardware.media.bufferpool@2.0::IClientManager
+aee53b2865b4f7939fb3df6fae758d9750c14f93dd454b479fc74aa7978fda4f android.hardware.media.bufferpool@2.0::IConnection
+0bf3758eeeb05767830ea87041214be80968c4679fb73577ac5b3091841ee71f android.hardware.media.bufferpool@2.0::IObserver
+82255e252ae215382473ad2e5ac7a2814a439a24f0092551aad7a2f89c6e9546 android.hardware.media.bufferpool@2.0::types
+fd4725732511b8ae124b868fd0b2090386663dffc5e67dfd0d0b4a0b49c335db android.hardware.media.c2@1.0::IComponent
+389d06e4a4ecf60f828a260045b0c327a5ae883ee0856a3c054556dd22b1f450 android.hardware.media.c2@1.0::IComponentInterface
+5ee0c02265c5505ade189796bef46697df4e0563e3544bb0c934855b34694b07 android.hardware.media.c2@1.0::IComponentListener
+43d70bcdc63b3d042bac3c3297f5d941dfabbd08f3ceb96b6016cc14f6e34ba3 android.hardware.media.c2@1.0::IComponentStore
+d36f747f9c9a8f2f21db2f8323c2d755dd08b34ce813932d7339979f7d490dab android.hardware.media.c2@1.0::IConfigurable
+cd2bd9f424515aeb0c2b91f3bb879797defb5b846b8660784b68d225285bcdff android.hardware.media.c2@1.0::IInputSink
+c3dfaa1fcd452c6cfc26d8dcbb67b71b08a1fe6dad5f87427e93d11a95372b05 android.hardware.media.c2@1.0::IInputSurface
+0a786a19e6753f9774a7ca7781c2a2edfe5c0b5fa112355dfa0e50ebedeb08b9 android.hardware.media.c2@1.0::IInputSurfaceConnection
+4cb139f729c29d8d6f4ecdab149c4feb571dad8a06e56cd57fcb52e70208bab4 android.hardware.media.c2@1.0::types
+4880af120fc1640225abdc2c60bda6d79617d73484d5124913c7278af3b11e2d android.hardware.neuralnetworks@1.2::IBurstCallback
+19877e466ad8c6ed42b38050b77bd010cf7800ff365fdc8574f45bbfda03a758 android.hardware.neuralnetworks@1.2::IBurstContext
+96249c852dabeefa3a9496ecdfc44681a071c665bfbf88527bf775c88bf1ab1b android.hardware.neuralnetworks@1.2::IDevice
+92714960d1a53fc2ec557302b41c7cc93d2636d8364a44bd0f85be0c92927ff8 android.hardware.neuralnetworks@1.2::IExecutionCallback
+83885d366f22ada42c00d8854f0b7e7ba4cf73ddf80bb0d8e168ce132cec57ea android.hardware.neuralnetworks@1.2::IPreparedModel
+e1c734d1545e1a4ae749ff1dd9704a8e594c59aea7c8363159dc258e93e0df3b android.hardware.neuralnetworks@1.2::IPreparedModelCallback
+769f8650631eef7a3ceedc8cf130f4b99eb52fe698a11609d55de32985a3dddf android.hardware.neuralnetworks@1.2::types
+cf7a4ba516a638f9b82a249c91fb603042c2d9ca43fd5aad9cf6c0401ed2a5d7 android.hardware.nfc@1.2::INfc
+abf98c2ae08bf765db54edc8068e36d52eb558cff6706b6fd7c18c65a1f3fc18 android.hardware.nfc@1.2::types
+4cb252dc6372a874aef666b92a6e9529915aa187521a700f0789065c3c702ead android.hardware.power.stats@1.0::IPowerStats
+2043037d5baaff604805757d06979aa861013a1e87430db745265339a8681f79 android.hardware.power.stats@1.0::types
+11620ce020b6ef8f5b63eb2a39390de4a2fbbccc0a5e558b5b1a0e22e33f63cf android.hardware.radio@1.3::IRadio
+e9d0f11a52715f5a29d89e2d8e2e21db1e16a43174af6b9d51a62d705cda1455 android.hardware.radio@1.3::IRadioIndication
+d233f0da44f55fdef0a95db5229231412787bb67695cd1ea197ce89a3c2908b9 android.hardware.radio@1.3::IRadioResponse
+750a363c8cec70baa1aac19e275c15233c5898e93c6bb5155fa2ca7f365490dc android.hardware.radio@1.3::types
+4ac73ec1e4cfa535209e5e22547f08beb20ef812b4a29d0824780f52cbe2324d android.hardware.radio@1.4::IRadio
+33d9e6895cca98aa56296bb01720d18b8acd0e4de4960beb712e63ad147438a5 android.hardware.radio@1.4::IRadioIndication
+0cc0dd87c634aad36d7df22b2832839ef7ded71909dbcde11cfdd69dc0dc52b8 android.hardware.radio@1.4::IRadioResponse
+29d34232cc3974626b08759e039fe788bded7695cdeb098458e3e11e4c7d3603 android.hardware.radio@1.4::types
+51e696c0ceff30f74da8ff8d02fe4522ffd2f4a04cdfdbca0c68bfa64fcd306b android.hardware.radio.config@1.1::IRadioConfig
+7fcf167f593b10c67b59ab70321781c26a5575eab60803e7cbb1c14c71085a3b android.hardware.radio.config@1.1::IRadioConfigIndication
+b42eb3bbd5e7b519e28362340c2205aa75356de6b30f4fd09ec2ea784f250ab0 android.hardware.radio.config@1.1::IRadioConfigResponse
+989ffce9105bb21626fd7ef51330ad47a3292a77bef77ac59badd9da40316ca7 android.hardware.radio.config@1.1::types
+b0d452f9a2e45f80bdb672b1e4cb649fff50293bdf208099be41738f11cd2ead android.hardware.radio.config@1.2::IRadioConfigIndication
+d8e7717e8187dd7453d4142f8f331e7c325e7a6f9e8d44ac0d52b3be502bfe83 android.hardware.radio.config@1.2::IRadioConfigResponse
+93b8102078e25057ae347ac9704e87529eb26121c2a1b419b362dd36eccefc4d android.hardware.radio.config@1.2::types
+08d439c463e4044fa78874037d8e8379aa3cabecde32f08a775897eea5a538af android.hardware.secure_element@1.1::ISecureElement
+b53ac9d61c24efb16a2d63a861cef20680f6d57adb244a03b9778c675550628b android.hardware.secure_element@1.1::ISecureElementHalCallback
+3702b1c52c0bb3427244618e9e7975c05228bf4ceb8720da7a93603a71cb0368 android.hardware.sensors@2.0::ISensors
+ae5faa38538a9f50eb71eb7f9b998271124d2c64b761cb11c4d820c7732b4ddc android.hardware.sensors@2.0::ISensorsCallback
+3a98242a57d0820dacaca0f7db52bec433eae1f21c498763c6f1ece611c3967b android.hardware.sensors@2.0::types
+ce4b98211959449361146d4b1e5554dc841ceb4d4577154d7b2fb6d1eb504f76 android.hardware.soundtrigger@2.2::ISoundTriggerHw
+bd88b48639cae30982021024e22371076c76faa8466e38ca598529452b618eae android.hardware.thermal@2.0::IThermal
+cc4d2ef36da776c475ad054f0f3416d8a8865def9d9e2129f10074b28e36d203 android.hardware.thermal@2.0::IThermalChangedCallback
+b47f90302595874dfddb19bd05a054727bf18b3a930bc810ea14957b859ae8bf android.hardware.thermal@2.0::types
+61bc302e7c974c59b25898c585c6e9685e8a81021b1bed3eedf5224198f2785a android.hardware.usb@1.2::IUsb
+46996cd2a1c66261a75a1f6ecada77eeb5861eb264fa39b996548fe0a7f22dd3 android.hardware.usb@1.2::IUsbCallback
+3bbaa8cbc5d6b1da21f5509b2b641e05fc7eeca1354751eb1bb3cf37f89aa32f android.hardware.usb@1.2::types
+92c1a726c80970d623b891f7c2f9a989a40a15ee1244092b49f4eb6adcdce4e9 android.hardware.vibrator@1.3::IVibrator
+f19832856a3f53ced5ef91d3cc630a57fb7f4d4ce15f364dbed09099b89f6830 android.hardware.wifi@1.3::IWifi
+7c6799c19bfdb3dec016b751556fe246cf7d37191ee7bb82a0091ab9fbf6f2fb android.hardware.wifi@1.3::IWifiChip
+3bef30e8b61ab050c0f6fd26572712be5ebb7707d624c9aa6c74bbb9d6a5b4a9 android.hardware.wifi@1.3::IWifiStaIface
+f3dbd8dd0d6333c005610288a4785d0ef79a72a7bbe6d0a46d46fa89fc886f1e android.hardware.wifi@1.3::types
+2fae61e962f68091335f7ff4581fcfe2e28ce7f6132d7a712fa13d7965543e4d android.hardware.wifi.hostapd@1.1::IHostapd
+913e66d8790c4e494950f1cbc259173b45d9e7bf9f1e8fc0c6a3623128290f4d android.hardware.wifi.hostapd@1.1::IHostapdCallback
+067b22efc50529a88d650fe7400603429d1164a47ee96a17476fdb0aadd6b4d3 android.hardware.wifi.supplicant@1.2::ISupplicant
+120211371fdd29fb134837071d432a302d7b60e9b95af611dd8dde86bd1f77ee android.hardware.wifi.supplicant@1.2::ISupplicantP2pIface
+7efe2b057e9f9387b3500e67af97942aa7c8008e6ee7d8dcaae4107fda84016b android.hardware.wifi.supplicant@1.2::ISupplicantStaIface
+09e08b5d12b109562ecdd8882532fd1f2c4639588e07769d5c7396b7c5b9f34f android.hardware.wifi.supplicant@1.2::ISupplicantStaIfaceCallback
+efbb061c969fa9553d243da6ee23b83fe5d4aa663a7b8896adc52e2b015bc2f3 android.hardware.wifi.supplicant@1.2::ISupplicantStaNetwork
+cfa81f229b69f9011c58f48264fcb552447430fe68610eac514e811e65bc306a android.hardware.wifi.supplicant@1.2::types
diff --git a/drm/1.2/Android.bp b/drm/1.2/Android.bp
index fa2962a..4e0e8ba 100644
--- a/drm/1.2/Android.bp
+++ b/drm/1.2/Android.bp
@@ -20,6 +20,7 @@
         "android.hidl.base@1.0",
     ],
     types: [
+        "HdcpLevel",
         "OfflineLicenseState",
         "Status",
     ],
diff --git a/drm/1.2/IDrmPlugin.hal b/drm/1.2/IDrmPlugin.hal
index 3c21815..7d266f4 100644
--- a/drm/1.2/IDrmPlugin.hal
+++ b/drm/1.2/IDrmPlugin.hal
@@ -74,7 +74,7 @@
      * @return status the status of the call. Must be one of OK on
      *     success, BAD_VALUE if the license is not found or
      *     ERROR_DRM_INVALID_STATE if the HAL is in a state where the
-     *     KeySetIds can't be returned.
+     *     KeySetIds can't be removed.
      */
     removeOfflineLicense(KeySetId keySetId) generates (@1.0::Status status);
 
diff --git a/gnss/1.0/default/GnssBatching.cpp b/gnss/1.0/default/GnssBatching.cpp
index 02b38cb..f2e2e45 100644
--- a/gnss/1.0/default/GnssBatching.cpp
+++ b/gnss/1.0/default/GnssBatching.cpp
@@ -20,7 +20,7 @@
 #include <Gnss.h> // for wakelock consolidation
 #include <GnssUtils.h>
 
-#include <cutils/log.h>  // for ALOGE
+#include <android/log.h>  // for ALOGE
 #include <vector>
 
 namespace android {
diff --git a/gnss/1.0/default/ThreadCreationWrapper.h b/gnss/1.0/default/ThreadCreationWrapper.h
index df0a9e4..f401ce2 100644
--- a/gnss/1.0/default/ThreadCreationWrapper.h
+++ b/gnss/1.0/default/ThreadCreationWrapper.h
@@ -17,9 +17,9 @@
 #ifndef ANDROID_HARDWARE_GNSS_THREADCREATIONWRAPPER_H
 #define ANDROID_HARDWARE_GNSS_THREADCREATIONWRAPPER_H
 
+#include <log/log.h>
 #include <pthread.h>
 #include <vector>
-#include <cutils/log.h>
 
 typedef void (*threadEntryFunc)(void* ret);
 
diff --git a/gnss/1.1/default/Android.bp b/gnss/1.1/default/Android.bp
index 44aed2b..8c3aac4 100644
--- a/gnss/1.1/default/Android.bp
+++ b/gnss/1.1/default/Android.bp
@@ -18,4 +18,7 @@
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@1.0",
     ],
+    static_libs: [
+        "android.hardware.gnss@common-default-lib",
+    ],
 }
diff --git a/gnss/1.1/default/Gnss.cpp b/gnss/1.1/default/Gnss.cpp
index bbf1cd3..4abe707 100644
--- a/gnss/1.1/default/Gnss.cpp
+++ b/gnss/1.1/default/Gnss.cpp
@@ -4,9 +4,9 @@
 #include <log/log.h>
 
 #include "Gnss.h"
-#include "GnssConstants.h"
 #include "GnssDebug.h"
 #include "GnssMeasurement.h"
+#include "Utils.h"
 
 namespace android {
 namespace hardware {
@@ -14,6 +14,7 @@
 namespace V1_1 {
 namespace implementation {
 
+using ::android::hardware::gnss::common::Utils;
 using GnssSvFlags = IGnssCallback::GnssSvFlags;
 
 const uint32_t MIN_INTERVAL_MILLIS = 100;
@@ -43,7 +44,7 @@
             auto svStatus = this->getMockSvStatus();
             this->reportSvStatus(svStatus);
 
-            auto location = this->getMockLocation();
+            auto location = Utils::getMockLocation();
             this->reportLocation(location);
 
             std::this_thread::sleep_for(std::chrono::milliseconds(mMinIntervalMs));
@@ -193,44 +194,17 @@
     return true;
 }
 
-Return<GnssLocation> Gnss::getMockLocation() const {
-    GnssLocation location = {.gnssLocationFlags = 0xFF,
-                             .latitudeDegrees = kMockLatitudeDegrees,
-                             .longitudeDegrees = kMockLongitudeDegrees,
-                             .altitudeMeters = kMockAltitudeMeters,
-                             .speedMetersPerSec = kMockSpeedMetersPerSec,
-                             .bearingDegrees = kMockBearingDegrees,
-                             .horizontalAccuracyMeters = kMockHorizontalAccuracyMeters,
-                             .verticalAccuracyMeters = kMockVerticalAccuracyMeters,
-                             .speedAccuracyMetersPerSecond = kMockSpeedAccuracyMetersPerSecond,
-                             .bearingAccuracyDegrees = kMockBearingAccuracyDegrees,
-                             .timestamp = kMockTimestamp};
-    return location;
-}
-
-Return<GnssSvInfo> Gnss::getSvInfo(int16_t svid, GnssConstellationType type, float cN0DbHz,
-                                   float elevationDegrees, float azimuthDegrees) const {
-    GnssSvInfo svInfo = {.svid = svid,
-                         .constellation = type,
-                         .cN0Dbhz = cN0DbHz,
-                         .elevationDegrees = elevationDegrees,
-                         .azimuthDegrees = azimuthDegrees,
-                         .svFlag = GnssSvFlags::USED_IN_FIX | GnssSvFlags::HAS_EPHEMERIS_DATA |
-                                   GnssSvFlags::HAS_ALMANAC_DATA};
-    return svInfo;
-}
-
 Return<GnssSvStatus> Gnss::getMockSvStatus() const {
     std::unique_lock<std::recursive_mutex> lock(mGnssConfiguration->getMutex());
     GnssSvInfo mockGnssSvInfoList[] = {
-        getSvInfo(3, GnssConstellationType::GPS, 32.5, 59.1, 166.5),
-        getSvInfo(5, GnssConstellationType::GPS, 27.0, 29.0, 56.5),
-        getSvInfo(17, GnssConstellationType::GPS, 30.5, 71.0, 77.0),
-        getSvInfo(26, GnssConstellationType::GPS, 24.1, 28.0, 253.0),
-        getSvInfo(5, GnssConstellationType::GLONASS, 20.5, 11.5, 116.0),
-        getSvInfo(17, GnssConstellationType::GLONASS, 21.5, 28.5, 186.0),
-        getSvInfo(18, GnssConstellationType::GLONASS, 28.3, 38.8, 69.0),
-        getSvInfo(10, GnssConstellationType::GLONASS, 25.0, 66.0, 247.0)};
+            Utils::getSvInfo(3, GnssConstellationType::GPS, 32.5, 59.1, 166.5),
+            Utils::getSvInfo(5, GnssConstellationType::GPS, 27.0, 29.0, 56.5),
+            Utils::getSvInfo(17, GnssConstellationType::GPS, 30.5, 71.0, 77.0),
+            Utils::getSvInfo(26, GnssConstellationType::GPS, 24.1, 28.0, 253.0),
+            Utils::getSvInfo(5, GnssConstellationType::GLONASS, 20.5, 11.5, 116.0),
+            Utils::getSvInfo(17, GnssConstellationType::GLONASS, 21.5, 28.5, 186.0),
+            Utils::getSvInfo(18, GnssConstellationType::GLONASS, 28.3, 38.8, 69.0),
+            Utils::getSvInfo(10, GnssConstellationType::GLONASS, 25.0, 66.0, 247.0)};
 
     GnssSvStatus svStatus = {.numSvs = sizeof(mockGnssSvInfoList) / sizeof(GnssSvInfo)};
     for (uint32_t i = 0; i < svStatus.numSvs; i++) {
diff --git a/gnss/1.1/default/Gnss.h b/gnss/1.1/default/Gnss.h
index 99af34c..21b66a2 100644
--- a/gnss/1.1/default/Gnss.h
+++ b/gnss/1.1/default/Gnss.h
@@ -84,10 +84,7 @@
 
     // Methods from ::android::hidl::base::V1_0::IBase follow.
    private:
-    Return<GnssLocation> getMockLocation() const;
     Return<GnssSvStatus> getMockSvStatus() const;
-    Return<GnssSvInfo> getSvInfo(int16_t svid, GnssConstellationType type, float cN0DbHz,
-                                 float elevationDegress, float azimuthDegress) const;
     Return<void> reportLocation(const GnssLocation&) const;
     Return<void> reportSvStatus(const GnssSvStatus&) const;
 
diff --git a/gnss/1.1/default/GnssDebug.cpp b/gnss/1.1/default/GnssDebug.cpp
index 62870e4..471ed24 100644
--- a/gnss/1.1/default/GnssDebug.cpp
+++ b/gnss/1.1/default/GnssDebug.cpp
@@ -18,9 +18,11 @@
 
 #include <log/log.h>
 
-#include "GnssConstants.h"
+#include "Constants.h"
 #include "GnssDebug.h"
 
+using namespace ::android::hardware::gnss::common;
+
 namespace android {
 namespace hardware {
 namespace gnss {
diff --git a/gnss/1.1/vts/functional/Android.bp b/gnss/1.1/vts/functional/Android.bp
index 9892eca..147a470 100644
--- a/gnss/1.1/vts/functional/Android.bp
+++ b/gnss/1.1/vts/functional/Android.bp
@@ -25,6 +25,7 @@
     static_libs: [
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
+        "android.hardware.gnss@common-vts-lib",
     ],
     test_suites: ["general-tests"],
 }
diff --git a/gnss/1.1/vts/functional/gnss_hal_test.cpp b/gnss/1.1/vts/functional/gnss_hal_test.cpp
index 433f5cb..381ac1d 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test.cpp
@@ -17,8 +17,10 @@
 #define LOG_TAG "GnssHalTest"
 
 #include <gnss_hal_test.h>
-
 #include <chrono>
+#include "Utils.h"
+
+using ::android::hardware::gnss::common::Utils;
 
 // Implementations for the main test class for GNSS HAL
 GnssHalTest::GnssHalTest()
@@ -124,69 +126,7 @@
 void GnssHalTest::CheckLocation(GnssLocation& location, bool check_speed) {
     bool check_more_accuracies = (info_called_count_ > 0 && last_info_.yearOfHw >= 2017);
 
-    EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_LAT_LONG);
-    EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_ALTITUDE);
-    if (check_speed) {
-        EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_SPEED);
-    }
-    EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_HORIZONTAL_ACCURACY);
-    // New uncertainties available in O must be provided,
-    // at least when paired with modern hardware (2017+)
-    if (check_more_accuracies) {
-        EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_VERTICAL_ACCURACY);
-        if (check_speed) {
-            EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_SPEED_ACCURACY);
-            if (location.gnssLocationFlags & GnssLocationFlags::HAS_BEARING) {
-                EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_BEARING_ACCURACY);
-            }
-        }
-    }
-    EXPECT_GE(location.latitudeDegrees, -90.0);
-    EXPECT_LE(location.latitudeDegrees, 90.0);
-    EXPECT_GE(location.longitudeDegrees, -180.0);
-    EXPECT_LE(location.longitudeDegrees, 180.0);
-    EXPECT_GE(location.altitudeMeters, -1000.0);
-    EXPECT_LE(location.altitudeMeters, 30000.0);
-    if (check_speed) {
-        EXPECT_GE(location.speedMetersPerSec, 0.0);
-        EXPECT_LE(location.speedMetersPerSec, 5.0);  // VTS tests are stationary.
-
-        // Non-zero speeds must be reported with an associated bearing
-        if (location.speedMetersPerSec > 0.0) {
-            EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_BEARING);
-        }
-    }
-
-    /*
-     * Tolerating some especially high values for accuracy estimate, in case of
-     * first fix with especially poor geometry (happens occasionally)
-     */
-    EXPECT_GT(location.horizontalAccuracyMeters, 0.0);
-    EXPECT_LE(location.horizontalAccuracyMeters, 250.0);
-
-    /*
-     * Some devices may define bearing as -180 to +180, others as 0 to 360.
-     * Both are okay & understandable.
-     */
-    if (location.gnssLocationFlags & GnssLocationFlags::HAS_BEARING) {
-        EXPECT_GE(location.bearingDegrees, -180.0);
-        EXPECT_LE(location.bearingDegrees, 360.0);
-    }
-    if (location.gnssLocationFlags & GnssLocationFlags::HAS_VERTICAL_ACCURACY) {
-        EXPECT_GT(location.verticalAccuracyMeters, 0.0);
-        EXPECT_LE(location.verticalAccuracyMeters, 500.0);
-    }
-    if (location.gnssLocationFlags & GnssLocationFlags::HAS_SPEED_ACCURACY) {
-        EXPECT_GT(location.speedAccuracyMetersPerSecond, 0.0);
-        EXPECT_LE(location.speedAccuracyMetersPerSecond, 50.0);
-    }
-    if (location.gnssLocationFlags & GnssLocationFlags::HAS_BEARING_ACCURACY) {
-        EXPECT_GT(location.bearingAccuracyDegrees, 0.0);
-        EXPECT_LE(location.bearingAccuracyDegrees, 360.0);
-    }
-
-    // Check timestamp > 1.48e12 (47 years in msec - 1970->2017+)
-    EXPECT_GT(location.timestamp, 1.48e12);
+    Utils::checkLocation(location, check_speed, check_more_accuracies);
 }
 
 void GnssHalTest::StartAndCheckLocations(int count) {
diff --git a/gnss/2.0/Android.bp b/gnss/2.0/Android.bp
index 9b04be0..230dd90 100644
--- a/gnss/2.0/Android.bp
+++ b/gnss/2.0/Android.bp
@@ -25,6 +25,9 @@
         "android.hidl.base@1.0",
     ],
     types: [
+        "ElapsedRealtime",
+        "ElapsedRealtimeFlags",
+        "GnssLocation",
     ],
     gen_java: true,
     gen_java_constants: true,
diff --git a/gnss/2.0/IGnss.hal b/gnss/2.0/IGnss.hal
index 1f1858e..2c149b7 100644
--- a/gnss/2.0/IGnss.hal
+++ b/gnss/2.0/IGnss.hal
@@ -20,6 +20,7 @@
 import android.hardware.gnss.visibility_control@1.0::IGnssVisibilityControl;
 import @1.1::IGnss;
 
+import GnssLocation;
 import IGnssCallback;
 import IGnssConfiguration;
 import IGnssMeasurement;
@@ -94,4 +95,16 @@
      * @return visibilityControlIface Handle to the IGnssVisibilityControl interface.
      */
     getExtensionVisibilityControl() generates (IGnssVisibilityControl visibilityControlIface);
+
+    /**
+     * Injects current location from the best available location provider.
+     *
+     * Unlike injectLocation, this method may inject a recent GNSS location from the HAL
+     * implementation, if that is the best available location known to the framework.
+     *
+     * @param location Location information from the best available location provider.
+     *
+     * @return success Returns true if successful.
+     */
+    injectBestLocation_2_0(GnssLocation location) generates (bool success);
 };
\ No newline at end of file
diff --git a/gnss/2.0/IGnssCallback.hal b/gnss/2.0/IGnssCallback.hal
index 6baff91..e2ac02a 100644
--- a/gnss/2.0/IGnssCallback.hal
+++ b/gnss/2.0/IGnssCallback.hal
@@ -18,6 +18,7 @@
 
 import @1.0::IGnssCallback;
 import @1.1::IGnssCallback;
+import GnssLocation;
 
 /**
  * The interface is required for the HAL to communicate certain information
@@ -44,4 +45,11 @@
      */
     gnssSetCapabilitiesCb_2_0(bitfield<Capabilities> capabilities);
 
+   /**
+     * Called when a GNSS location is available.
+     *
+     * @param location Location information from HAL.
+     */
+    gnssLocationCb_2_0(GnssLocation location);
+
 };
\ No newline at end of file
diff --git a/gnss/2.0/IGnssMeasurementCallback.hal b/gnss/2.0/IGnssMeasurementCallback.hal
index 226934e..d5dc038 100644
--- a/gnss/2.0/IGnssMeasurementCallback.hal
+++ b/gnss/2.0/IGnssMeasurementCallback.hal
@@ -18,53 +18,54 @@
 
 import @1.0::IGnssMeasurementCallback;
 import @1.1::IGnssMeasurementCallback;
+import ElapsedRealtime;
 
 /** The callback interface to report measurements from the HAL. */
 interface IGnssMeasurementCallback extends @1.1::IGnssMeasurementCallback {
     /**
      * Enumeration of available values for the GNSS Measurement's code type. Similar to the
-     * Attribute field described in Rinex 3.03, e.g., in Tables 4-10, and Table A2 at the Rinex 3.03
+     * Attribute field described in RINEX 3.03, e.g., in Tables 4-10, and Table A2 at the RINEX 3.03
      * Update 1 Document.
      */
     enum GnssMeasurementCodeType : uint8_t {
         /** GALILEO E1A, GALILEO E6A, IRNSS L5A, IRNSS SA. */
-        CODE_TYPE_A = 0,
+        A = 0,
 
         /** GALILEO E1B, GALILEO E6B, IRNSS L5B, IRNSS SB. */
-        CODE_TYPE_B = 1,
+        B = 1,
 
         /**
          *  GPS L1 C/A,  GPS L2 C/A, GLONASS G1 C/A, GLONASS G2 C/A, GALILEO E1C, GALILEO E6C, SBAS
          *  L1 C/A, QZSS L1 C/A, IRNSS L5C.
          */
-        CODE_TYPE_C = 2,
+        C = 2,
 
         /**
          * GPS L5 I, GLONASS G3 I, GALILEO E5a I, GALILEO E5b I, GALILEO E5a+b I, SBAS L5 I, QZSS L5
          * I, BDS B1 I, BDS B2 I, BDS B3 I.
          */
-        CODE_TYPE_I = 3,
+        I = 3,
 
         /** GPS L1C (P), GPS L2C (L), QZSS L1C (P), QZSS L2C (L), LEX(6) L. */
-        CODE_TYPE_L = 4,
+        L = 4,
 
         /** GPS L1M, GPS L2M. */
-        CODE_TYPE_M = 5,
+        M = 5,
 
         /** GPS L1P, GPS L2P, GLONASS G1P, GLONASS G2P. */
-        CODE_TYPE_P = 6,
+        P = 6,
 
         /**
          * GPS L5 Q, GLONASS G3 Q, GALILEO E5a Q, GALILEO E5b Q, GALILEO E5a+b Q, SBAS L5 Q, QZSS L5
          * Q, BDS B1 Q, BDS B2 Q, BDS B3 Q.
          */
-        CODE_TYPE_Q = 7,
+        Q = 7,
 
         /** GPS L1C (D), GPS L2C (M), QZSS L1C (D), QZSS L2C (M), LEX(6) S. */
-        CODE_TYPE_S = 8,
+        S = 8,
 
         /** GPS L1 Z-tracking, GPS L2 Z-tracking. */
-        CODE_TYPE_W = 9,
+        W = 9,
 
         /**
          * GPS L1C (D+P), GPS L2C (M+L), GPS L5 (I+Q), GLONASS G3 (I+Q), GALILEO E1 (B+C), GALILEO
@@ -72,16 +73,366 @@
          * L1C (D+P), QZSS L2C (M+L), QZSS L5 (I+Q), LEX(6) (S+L), BDS B1 (I+Q), BDS B2 (I+Q), BDS
          * B3 (I+Q), IRNSS L5 (B+C).
          */
-        CODE_TYPE_X = 10,
+        X = 10,
 
         /** GPS L1Y, GPS L2Y. */
-        CODE_TYPE_Y = 11,
+        Y = 11,
 
         /** GALILEO E1 (A+B+C), GALILEO E6 (A+B+C), QZSS L1-SAIF. */
-        CODE_TYPE_Z = 12,
+        Z = 12,
 
         /** GPS L1 codeless, GPS L2 codeless. */
-        CODE_TYPE_CODELESS = 13
+        N = 13,
+
+        /**
+         * Other code type that does not belong to any of the above code types.
+         *
+         * This code type is used in the case that the above code types do not cover all the code
+         * types introduced in a new version of RINEX standard. When this code type is set, the
+         * field GnssMeasurement.otherCodeTypeName must specify the new code type.
+         */
+        OTHER = 255
+    };
+
+    /**
+     * Flags indicating the GNSS measurement state.
+     *
+     * <p>The expected behavior here is for GNSS HAL to set all the flags that apply. For example,
+     * if the state for a satellite is only C/A code locked and bit synchronized, and there is still
+     * millisecond ambiguity, the state must be set as:
+     *
+     * STATE_CODE_LOCK | STATE_BIT_SYNC |  STATE_MSEC_AMBIGUOUS
+     *
+     * <p>If GNSS is still searching for a satellite, the corresponding state must be set to
+     * STATE_UNKNOWN(0).
+     *
+     * <p>In @2.0::IGnssMeasurementCallback.GnssMeasurement, v1_1.v1_0.receivedSvTimeInNs, the
+     * received satellite time, is relative to the beginning of the system week for all
+     * constellations except for Glonass where it is relative to the beginning of the Glonass system
+     * day.
+     *
+     * <p>The table below indicates the valid range of the received GNSS satellite time.  These
+     * ranges depend on the constellation and code being tracked and the state of the tracking
+     * algorithms given by the getState method. If the state flag is set, then the valid measurement
+     * range is zero to the value in the table. The state flag with the widest range indicates the
+     * range of the received GNSS satellite time value.
+     *
+     * <table>
+     *   <thead>
+     *     <tr>
+     *       <td />
+     *       <td colspan="3"><strong>GPS/QZSS</strong></td>
+     *       <td><strong>GLNS</strong></td>
+     *       <td colspan="2"><strong>BDS</strong></td>
+     *       <td colspan="3"><strong>GAL</strong></td>
+     *       <td><strong>SBAS</strong></td>
+     *     </tr>
+     *     <tr>
+     *       <td><strong>State Flag</strong></td>
+     *       <td><strong>L1 C/A</strong></td>
+     *       <td><strong>L5I</strong></td>
+     *       <td><strong>L5Q</strong></td>
+     *       <td><strong>L1OF</strong></td>
+     *       <td><strong>B1I (D1)</strong></td>
+     *       <td><strong>B1I &nbsp;(D2)</strong></td>
+     *       <td><strong>E1B</strong></td>
+     *       <td><strong>E1C</strong></td>
+     *       <td><strong>E5AQ</strong></td>
+     *       <td><strong>L1 C/A</strong></td>
+     *     </tr>
+     *   </thead>
+     *   <tbody>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_UNKNOWN</strong>
+     *       </td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *       <td>0</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_CODE_LOCK</strong>
+     *       </td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>1 ms</td>
+     *       <td>1 ms</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_SYMBOL_SYNC</strong>
+     *       </td>
+     *       <td>20 ms (optional)</td>
+     *       <td>10 ms</td>
+     *       <td>1 ms (optional)</td>
+     *       <td>10 ms</td>
+     *       <td>20 ms (optional)</td>
+     *       <td>2 ms</td>
+     *       <td>4 ms (optional)</td>
+     *       <td>4 ms (optional)</td>
+     *       <td>1 ms (optional)</td>
+     *       <td>2 ms</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_BIT_SYNC</strong>
+     *       </td>
+     *       <td>20 ms</td>
+     *       <td>20 ms</td>
+     *       <td>1 ms (optional)</td>
+     *       <td>20 ms</td>
+     *       <td>20 ms</td>
+     *       <td>-</td>
+     *       <td>8 ms</td>
+     *       <td>-</td>
+     *       <td>1 ms (optional)</td>
+     *       <td>4 ms</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_SUBFRAME_SYNC</strong>
+     *       </td>
+     *       <td>6s</td>
+     *       <td>6s</td>
+     *       <td>-</td>
+     *       <td>2 s</td>
+     *       <td>6 s</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>100 ms</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_TOW_DECODED</strong>
+     *       </td>
+     *       <td colspan="2">1 week</td>
+     *       <td>-</td>
+     *       <td>1 day</td>
+     *       <td colspan="2">1 week</td>
+     *       <td colspan="2">1 week</td>
+     *       <td>-</td>
+     *       <td>1 week</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_TOW_KNOWN</strong>
+     *       </td>
+     *       <td colspan="3">1 week</td>
+     *       <td>1 day</td>
+     *       <td colspan="2">1 week</td>
+     *       <td colspan="3">1 week</td>
+     *       <td>1 week</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GLO_STRING_SYNC</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>2 s</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GLO_TOD_DECODED</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>1 day</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GLO_TOD_KNOWN</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>1 day</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_BDS_D2_BIT_SYNC</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>2 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_BDS_D2_SUBFRAME_SYNC</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>600 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GAL_E1BC_CODE_LOCK</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>4 ms</td>
+     *       <td>4 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GAL_E1C_2ND_CODE_LOCK</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>100 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_2ND_CODE_LOCK</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>10 ms (optional)</td>
+     *       <td>20 ms</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>100 ms (optional)</td>
+     *       <td>100 ms</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_GAL_E1B_PAGE_SYNC</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>2 s</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *     </tr>
+     *     <tr>
+     *       <td>
+     *         <strong>STATE_SBAS_SYNC</strong>
+     *       </td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>-</td>
+     *       <td>1 s</td>
+     *     </tr>
+     *   </tbody>
+     * </table>
+     *
+     * <p>Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
+     * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
+     *
+     * <p>Note well: if there is any ambiguity in integer millisecond, STATE_MSEC_AMBIGUOUS must be
+     * set accordingly, in the 'state' field.  This value must be populated if 'state' !=
+     * STATE_UNKNOWN.
+     *
+     * <p>Note on optional flags:
+     * <ul>
+     *     <li> For L1 C/A and B1I, STATE_SYMBOL_SYNC is optional since the symbol length is the
+     *     same as the bit length.
+     *     <li> For L5Q and E5aQ, STATE_BIT_SYNC and STATE_SYMBOL_SYNC are optional since they are
+     *     implied by STATE_CODE_LOCK.
+     *     <li> STATE_2ND_CODE_LOCK for L5I is optional since it is implied by STATE_SYMBOL_SYNC.
+     *     <li> STATE_2ND_CODE_LOCK for E1C is optional since it is implied by
+     *     STATE_GAL_E1C_2ND_CODE_LOCK.
+     *     <li> For E1B and E1C, STATE_SYMBOL_SYNC is optional, because it is implied by
+     *     STATE_GAL_E1BC_CODE_LOCK.
+     * </ul>
+     *
+     */
+    @export(name="", value_prefix="GNSS_MEASUREMENT_")
+    enum GnssMeasurementState : uint32_t {
+        STATE_UNKNOWN                = 0,
+        STATE_CODE_LOCK              = 1 << 0,
+        STATE_BIT_SYNC               = 1 << 1,
+        STATE_SUBFRAME_SYNC          = 1 << 2,
+        STATE_TOW_DECODED            = 1 << 3,
+        STATE_MSEC_AMBIGUOUS         = 1 << 4,
+        STATE_SYMBOL_SYNC            = 1 << 5,
+        STATE_GLO_STRING_SYNC        = 1 << 6,
+        STATE_GLO_TOD_DECODED        = 1 << 7,
+        STATE_BDS_D2_BIT_SYNC        = 1 << 8,
+        STATE_BDS_D2_SUBFRAME_SYNC   = 1 << 9,
+        STATE_GAL_E1BC_CODE_LOCK     = 1 << 10,
+        STATE_GAL_E1C_2ND_CODE_LOCK  = 1 << 11,
+        STATE_GAL_E1B_PAGE_SYNC      = 1 << 12,
+        STATE_SBAS_SYNC              = 1 << 13,
+        STATE_TOW_KNOWN              = 1 << 14,
+        STATE_GLO_TOD_KNOWN          = 1 << 15,
+        STATE_2ND_CODE_LOCK          = 1 << 16,
     };
 
     /**
@@ -96,6 +447,10 @@
          * quarter cycle offset as they do when transmitted from the satellites. If the measurement
          * is from a combination of the in-phase and quadrature phase components, then the alignment
          * of the phase measurement will be aligned to the in-phase component.
+         *
+         * In this version of the HAL, the field 'state' in the v1_1.v1_0 struct is deprecated, and
+         * is no longer used by the framework. The satellite sync state is instead reported in
+         * @2.0::IGnssMeasurementCallback.GnssMeasurement.state.
          */
         @1.1::IGnssMeasurementCallback.GnssMeasurement v1_1;
 
@@ -106,6 +461,27 @@
          * in-order to properly apply code specific corrections to the psuedorange measurements.
          */
         GnssMeasurementCodeType codeType;
+
+        /**
+         * The name of the code type when codeType is OTHER.
+         *
+         * This is used to specify the observation descriptor defined in GNSS Observation Data File
+         * Header Section Description in the RINEX standard (Version 3.XX). In RINEX Version 3.03,
+         * in Appendix Table A2 Attributes are listed as uppercase letters (for instance, "A" for
+         * "A channel"). In the future, if for instance a code "G" was added in the official RINEX
+         * standard, "G" could be specified here.
+         */
+        string otherCodeTypeName;
+
+        /**
+         * Per satellite sync state. It represents the current sync state for the associated
+         * satellite.
+         *
+         * Based on the sync state, the receivedSvTimeInNs field must be interpreted accordingly.
+         *
+         * This value is mandatory.
+         */
+        bitfield<GnssMeasurementState> state;
     };
 
     /**
@@ -117,6 +493,12 @@
 
         /** The GNSS clock time reading. */
         GnssClock clock;
+
+        /**
+         * Timing information of the GNSS data synchronized with SystemClock.elapsedRealtimeNanos()
+         * clock.
+         */
+        ElapsedRealtime elapsedRealtime;
     };
 
     /**
diff --git a/gnss/2.0/default/Android.bp b/gnss/2.0/default/Android.bp
index 985aa2b..f327197 100644
--- a/gnss/2.0/default/Android.bp
+++ b/gnss/2.0/default/Android.bp
@@ -40,4 +40,7 @@
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
     ],
+    static_libs: [
+        "android.hardware.gnss@common-default-lib",
+    ],
 }
diff --git a/gnss/2.0/default/Gnss.cpp b/gnss/2.0/default/Gnss.cpp
index 217f0f3..599681b 100644
--- a/gnss/2.0/default/Gnss.cpp
+++ b/gnss/2.0/default/Gnss.cpp
@@ -17,14 +17,19 @@
 #define LOG_TAG "Gnss"
 
 #include "Gnss.h"
+
 #include <log/log.h>
+#include <utils/SystemClock.h>
+
 #include "AGnss.h"
 #include "AGnssRil.h"
 #include "GnssConfiguration.h"
 #include "GnssMeasurement.h"
 #include "GnssVisibilityControl.h"
+#include "Utils.h"
 
 using ::android::hardware::Status;
+using ::android::hardware::gnss::common::Utils;
 using ::android::hardware::gnss::visibility_control::V1_0::implementation::GnssVisibilityControl;
 
 namespace android {
@@ -33,64 +38,106 @@
 namespace V2_0 {
 namespace implementation {
 
+using GnssSvFlags = IGnssCallback::GnssSvFlags;
+
 sp<V2_0::IGnssCallback> Gnss::sGnssCallback_2_0 = nullptr;
 sp<V1_1::IGnssCallback> Gnss::sGnssCallback_1_1 = nullptr;
 
+namespace {
+
+V2_0::GnssLocation getMockLocationV2_0() {
+    const ElapsedRealtime timestamp = {
+            .flags = ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
+                     ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS,
+            .timestampNs = static_cast<uint64_t>(::android::elapsedRealtimeNano()),
+            // This is an hardcoded value indicating a 1ms of uncertainty between the two clocks.
+            // In an actual implementation provide an estimate of the synchronization uncertainty
+            // or don't set the field.
+            .timeUncertaintyNs = 1000000};
+
+    V2_0::GnssLocation location = {.v1_0 = Utils::getMockLocation(), .elapsedRealtime = timestamp};
+    return location;
+}
+
+}  // namespace
+
+Gnss::Gnss() : mMinIntervalMs(1000) {}
+
+Gnss::~Gnss() {
+    stop();
+}
+
 // Methods from V1_0::IGnss follow.
 Return<bool> Gnss::setCallback(const sp<V1_0::IGnssCallback>&) {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return bool{};
 }
 
 Return<bool> Gnss::start() {
-    // TODO implement
-    return bool{};
+    if (mIsActive) {
+        ALOGW("Gnss has started. Restarting...");
+        stop();
+    }
+
+    mIsActive = true;
+    mThread = std::thread([this]() {
+        while (mIsActive == true) {
+            const auto location = getMockLocationV2_0();
+            this->reportLocation(location);
+
+            std::this_thread::sleep_for(std::chrono::milliseconds(mMinIntervalMs));
+        }
+    });
+    return true;
 }
 
 Return<bool> Gnss::stop() {
-    // TODO implement
-    return bool{};
+    mIsActive = false;
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+    return true;
 }
 
 Return<void> Gnss::cleanup() {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return Void();
 }
 
 Return<bool> Gnss::injectTime(int64_t, int64_t, int32_t) {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return bool{};
 }
 
 Return<bool> Gnss::injectLocation(double, double, float) {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return bool{};
 }
 
 Return<void> Gnss::deleteAidingData(V1_0::IGnss::GnssAidingData) {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return Void();
 }
 
 Return<bool> Gnss::setPositionMode(V1_0::IGnss::GnssPositionMode,
                                    V1_0::IGnss::GnssPositionRecurrence, uint32_t, uint32_t,
                                    uint32_t) {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return bool{};
 }
 
 Return<sp<V1_0::IAGnssRil>> Gnss::getExtensionAGnssRil() {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return sp<V1_0::IAGnssRil>{};
 }
 
 Return<sp<V1_0::IGnssGeofencing>> Gnss::getExtensionGnssGeofencing() {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return sp<V1_0::IGnssGeofencing>{};
 }
 
 Return<sp<V1_0::IAGnss>> Gnss::getExtensionAGnss() {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return sp<V1_0::IAGnss>{};
 }
 
@@ -105,27 +152,27 @@
 }
 
 Return<sp<V1_0::IGnssNavigationMessage>> Gnss::getExtensionGnssNavigationMessage() {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return sp<V1_0::IGnssNavigationMessage>{};
 }
 
 Return<sp<V1_0::IGnssXtra>> Gnss::getExtensionXtra() {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return sp<V1_0::IGnssXtra>{};
 }
 
 Return<sp<V1_0::IGnssConfiguration>> Gnss::getExtensionGnssConfiguration() {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return sp<V1_0::IGnssConfiguration>{};
 }
 
 Return<sp<V1_0::IGnssDebug>> Gnss::getExtensionGnssDebug() {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return sp<V1_0::IGnssDebug>{};
 }
 
 Return<sp<V1_0::IGnssBatching>> Gnss::getExtensionGnssBatching() {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return sp<V1_0::IGnssBatching>{};
 }
 
@@ -164,12 +211,11 @@
 Return<bool> Gnss::setPositionMode_1_1(V1_0::IGnss::GnssPositionMode,
                                        V1_0::IGnss::GnssPositionRecurrence, uint32_t, uint32_t,
                                        uint32_t, bool) {
-    // TODO implement
-    return bool{};
+    return true;
 }
 
 Return<sp<V1_1::IGnssConfiguration>> Gnss::getExtensionGnssConfiguration_1_1() {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return sp<V1_1::IGnssConfiguration>{};
 }
 
@@ -179,7 +225,7 @@
 }
 
 Return<bool> Gnss::injectBestLocation(const V1_0::GnssLocation&) {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return bool{};
 }
 
@@ -203,7 +249,7 @@
 
 Return<sp<measurement_corrections::V1_0::IMeasurementCorrections>>
 Gnss::getExtensionMeasurementCorrections() {
-    // TODO implement
+    // TODO(b/124012850): Implement function.
     return sp<measurement_corrections::V1_0::IMeasurementCorrections>{};
 }
 
@@ -243,6 +289,21 @@
     return true;
 }
 
+Return<void> Gnss::reportLocation(const V2_0::GnssLocation& location) const {
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (sGnssCallback_2_0 == nullptr) {
+        ALOGE("%s: sGnssCallback 2.0 is null.", __func__);
+        return Void();
+    }
+    sGnssCallback_2_0->gnssLocationCb_2_0(location);
+    return Void();
+}
+
+Return<bool> Gnss::injectBestLocation_2_0(const V2_0::GnssLocation&) {
+    // TODO(b/124012850): Implement function.
+    return bool{};
+}
+
 }  // namespace implementation
 }  // namespace V2_0
 }  // namespace gnss
diff --git a/gnss/2.0/default/Gnss.h b/gnss/2.0/default/Gnss.h
index 890b026..f02ab0a 100644
--- a/gnss/2.0/default/Gnss.h
+++ b/gnss/2.0/default/Gnss.h
@@ -20,6 +20,9 @@
 #include <android/hardware/gnss/2.0/IGnss.h>
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
+#include <atomic>
+#include <mutex>
+#include <thread>
 
 namespace android {
 namespace hardware {
@@ -35,7 +38,14 @@
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 
+using GnssConstellationType = V1_0::GnssConstellationType;
+using GnssLocation = V1_0::GnssLocation;
+using GnssSvInfo = V1_0::IGnssCallback::GnssSvInfo;
+using GnssSvStatus = V1_0::IGnssCallback::GnssSvStatus;
+
 struct Gnss : public IGnss {
+    Gnss();
+    ~Gnss();
     // Methods from V1_0::IGnss follow.
     Return<bool> setCallback(const sp<V1_0::IGnssCallback>& callback) override;
     Return<bool> start() override;
@@ -69,7 +79,7 @@
                                      uint32_t preferredTimeMs, bool lowPowerMode) override;
     Return<sp<V1_1::IGnssConfiguration>> getExtensionGnssConfiguration_1_1() override;
     Return<sp<V1_1::IGnssMeasurement>> getExtensionGnssMeasurement_1_1() override;
-    Return<bool> injectBestLocation(const V1_0::GnssLocation& location) override;
+    Return<bool> injectBestLocation(const GnssLocation& location) override;
 
     // Methods from V2_0::IGnss follow.
     Return<sp<V2_0::IGnssConfiguration>> getExtensionGnssConfiguration_2_0() override;
@@ -81,10 +91,16 @@
     getExtensionMeasurementCorrections() override;
     Return<sp<visibility_control::V1_0::IGnssVisibilityControl>> getExtensionVisibilityControl()
             override;
+    Return<bool> injectBestLocation_2_0(const V2_0::GnssLocation& location) override;
 
-   private:
+  private:
+    Return<void> reportLocation(const V2_0::GnssLocation&) const;
     static sp<V2_0::IGnssCallback> sGnssCallback_2_0;
     static sp<V1_1::IGnssCallback> sGnssCallback_1_1;
+    std::atomic<long> mMinIntervalMs;
+    std::atomic<bool> mIsActive;
+    std::thread mThread;
+    mutable std::mutex mMutex;
 };
 
 }  // namespace implementation
diff --git a/gnss/2.0/default/GnssMeasurement.cpp b/gnss/2.0/default/GnssMeasurement.cpp
index dc23db3..702c9e2 100644
--- a/gnss/2.0/default/GnssMeasurement.cpp
+++ b/gnss/2.0/default/GnssMeasurement.cpp
@@ -16,7 +16,9 @@
 #define LOG_TAG "GnssMeasurement"
 
 #include "GnssMeasurement.h"
+
 #include <log/log.h>
+#include <utils/SystemClock.h>
 
 namespace android {
 namespace hardware {
@@ -26,7 +28,7 @@
 
 using GnssConstellationType = V1_0::GnssConstellationType;
 using GnssMeasurementFlags = V1_0::IGnssMeasurementCallback::GnssMeasurementFlags;
-using GnssMeasurementState = V1_0::IGnssMeasurementCallback::GnssMeasurementState;
+using GnssMeasurementState = V2_0::IGnssMeasurementCallback::GnssMeasurementState;
 
 sp<V2_0::IGnssMeasurementCallback> GnssMeasurement::sCallback = nullptr;
 
@@ -97,11 +99,6 @@
         .svid = (int16_t)6,
         .constellation = GnssConstellationType::GLONASS,
         .timeOffsetNs = 0.0,
-        .state = GnssMeasurementState::STATE_CODE_LOCK | GnssMeasurementState::STATE_BIT_SYNC |
-                 GnssMeasurementState::STATE_SUBFRAME_SYNC |
-                 GnssMeasurementState::STATE_TOW_DECODED |
-                 GnssMeasurementState::STATE_GLO_STRING_SYNC |
-                 GnssMeasurementState::STATE_GLO_TOD_DECODED,
         .receivedSvTimeInNs = 8195997131077,
         .receivedSvTimeUncertaintyInNs = 15,
         .cN0DbHz = 30.0,
@@ -116,8 +113,14 @@
             V1_0::IGnssMeasurementCallback::GnssMultipathIndicator::INDICATOR_UNKNOWN};
     V1_1::IGnssMeasurementCallback::GnssMeasurement measurement_1_1 = {.v1_0 = measurement_1_0};
     V2_0::IGnssMeasurementCallback::GnssMeasurement measurement_2_0 = {
-        .v1_1 = measurement_1_1,
-        .codeType = IGnssMeasurementCallback::GnssMeasurementCodeType::CODE_TYPE_C};
+            .v1_1 = measurement_1_1,
+            .codeType = IGnssMeasurementCallback::GnssMeasurementCodeType::C,
+            .otherCodeTypeName = "",
+            .state = GnssMeasurementState::STATE_CODE_LOCK | GnssMeasurementState::STATE_BIT_SYNC |
+                     GnssMeasurementState::STATE_SUBFRAME_SYNC |
+                     GnssMeasurementState::STATE_TOW_DECODED |
+                     GnssMeasurementState::STATE_GLO_STRING_SYNC |
+                     GnssMeasurementState::STATE_GLO_TOD_DECODED};
 
     hidl_vec<IGnssMeasurementCallback::GnssMeasurement> measurements(1);
     measurements[0] = measurement_2_0;
@@ -128,7 +131,18 @@
                                                        .driftNsps = -51.757811607455452,
                                                        .driftUncertaintyNsps = 310.64968328491528,
                                                        .hwClockDiscontinuityCount = 1};
-    GnssData gnssData = {.measurements = measurements, .clock = clock};
+
+    ElapsedRealtime timestamp = {
+            .flags = ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
+                     ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS,
+            .timestampNs = static_cast<uint64_t>(::android::elapsedRealtimeNano()),
+            // This is an hardcoded value indicating a 1ms of uncertainty between the two clocks.
+            // In an actual implementation provide an estimate of the synchronization uncertainty
+            // or don't set the field.
+            .timeUncertaintyNs = 1000000};
+
+    GnssData gnssData = {
+            .measurements = measurements, .clock = clock, .elapsedRealtime = timestamp};
     return gnssData;
 }
 
diff --git a/gnss/2.0/types.hal b/gnss/2.0/types.hal
index 97c178f..4abb604 100644
--- a/gnss/2.0/types.hal
+++ b/gnss/2.0/types.hal
@@ -16,5 +16,60 @@
 
 package android.hardware.gnss@2.0;
 
+import @1.0::GnssLocation;
+
 /** Network handle type. */
 typedef uint64_t net_handle_t;
+
+/**
+ * Flags indicating the validity of the fields in ElapsedRealtime.
+ */
+@export(name="", value_prefix="ELAPSED_REALTIME_")
+enum ElapsedRealtimeFlags : uint16_t {
+    /** A valid timestampNs is stored in the data structure. */
+    HAS_TIMESTAMP_NS        = 1 << 0,
+    /** A valid timeUncertaintyNs is stored in the data structure. */
+    HAS_TIME_UNCERTAINTY_NS = 1 << 1,
+};
+
+/**
+ * Represents an estimate of elapsed time since boot of Android for a given event.
+ *
+ * This timestamp MUST represent the time the event happened and MUST be synchronized
+ * with the SystemClock.elapsedRealtimeNanos() clock.
+ */
+struct ElapsedRealtime {
+    /**
+     * A set of flags indicating the validity of each field in this data structure.
+     *
+     * Fields may have invalid information in them, if not marked as valid by the
+     * corresponding bit in flags.
+     */
+    bitfield<ElapsedRealtimeFlags> flags;
+
+    /**
+     * Estimate of the elapsed time since boot value for the corresponding event in nanoseconds.
+     */
+    uint64_t timestampNs;
+
+    /**
+     * Estimate of the relative precision of the alignment of this SystemClock
+     * timestamp, with the reported measurements in nanoseconds (68% confidence).
+     */
+    uint64_t timeUncertaintyNs;
+};
+
+/** Represents a location. */
+struct GnssLocation {
+    @1.0::GnssLocation v1_0;
+
+    /**
+     * Timing information of the GNSS location synchronized with SystemClock.elapsedRealtimeNanos()
+     * clock.
+     *
+     * This clock information can be obtained from SystemClock.elapsedRealtimeNanos(), when the GNSS
+     * is attached straight to the AP/SOC. When it is attached to a separate module the timestamp
+     * needs to be estimatedd by syncing the notion of time via PTP or some other mechanism. 
+     */
+    ElapsedRealtime elapsedRealtime;
+};
\ No newline at end of file
diff --git a/gnss/2.0/vts/functional/Android.bp b/gnss/2.0/vts/functional/Android.bp
index 684b381..278d87b 100644
--- a/gnss/2.0/vts/functional/Android.bp
+++ b/gnss/2.0/vts/functional/Android.bp
@@ -28,5 +28,6 @@
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@2.0",
+        "android.hardware.gnss@common-vts-lib",
     ],
 }
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.cpp b/gnss/2.0/vts/functional/gnss_hal_test.cpp
index 3a48c9e..c564f41 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test.cpp
@@ -17,8 +17,10 @@
 #define LOG_TAG "GnssHalTest"
 
 #include <gnss_hal_test.h>
-
 #include <chrono>
+#include "Utils.h"
+
+using ::android::hardware::gnss::common::Utils;
 
 // Implementations for the main test class for GNSS HAL
 GnssHalTest::GnssHalTest()
@@ -77,9 +79,88 @@
     EXPECT_EQ(name_called_count_, 1);
 }
 
+void GnssHalTest::StopAndClearLocations() {
+    const auto result = gnss_hal_->stop();
+
+    EXPECT_TRUE(result.isOk());
+    EXPECT_TRUE(result);
+
+    /*
+     * Clear notify/waiting counter, allowing up till the timeout after
+     * the last reply for final startup messages to arrive (esp. system
+     * info.)
+     */
+    while (wait(TIMEOUT_SEC) == std::cv_status::no_timeout) {
+    }
+    location_called_count_ = 0;
+}
+
+void GnssHalTest::SetPositionMode(const int min_interval_msec, const bool low_power_mode) {
+    const int kPreferredAccuracy = 0;  // Ideally perfect (matches GnssLocationProvider)
+    const int kPreferredTimeMsec = 0;  // Ideally immediate
+
+    const auto result = gnss_hal_->setPositionMode_1_1(
+            IGnss::GnssPositionMode::MS_BASED, IGnss::GnssPositionRecurrence::RECURRENCE_PERIODIC,
+            min_interval_msec, kPreferredAccuracy, kPreferredTimeMsec, low_power_mode);
+
+    ASSERT_TRUE(result.isOk());
+    EXPECT_TRUE(result);
+}
+
+bool GnssHalTest::StartAndCheckFirstLocation() {
+    const auto result = gnss_hal_->start();
+
+    EXPECT_TRUE(result.isOk());
+    EXPECT_TRUE(result);
+
+    /*
+     * GnssLocationProvider support of AGPS SUPL & XtraDownloader is not available in VTS,
+     * so allow time to demodulate ephemeris over the air.
+     */
+    const int kFirstGnssLocationTimeoutSeconds = 75;
+
+    wait(kFirstGnssLocationTimeoutSeconds);
+    EXPECT_EQ(location_called_count_, 1);
+
+    if (location_called_count_ > 0) {
+        // don't require speed on first fix
+        CheckLocation(last_location_, false);
+        return true;
+    }
+    return false;
+}
+
+void GnssHalTest::CheckLocation(const GnssLocation_2_0& location, bool check_speed) {
+    const bool check_more_accuracies = (info_called_count_ > 0 && last_info_.yearOfHw >= 2017);
+
+    Utils::checkLocation(location.v1_0, check_speed, check_more_accuracies);
+}
+
+void GnssHalTest::StartAndCheckLocations(int count) {
+    const int kMinIntervalMsec = 500;
+    const int kLocationTimeoutSubsequentSec = 2;
+    const bool kLowPowerMode = false;
+
+    SetPositionMode(kMinIntervalMsec, kLowPowerMode);
+
+    EXPECT_TRUE(StartAndCheckFirstLocation());
+
+    for (int i = 1; i < count; i++) {
+        EXPECT_EQ(std::cv_status::no_timeout, wait(kLocationTimeoutSubsequentSec));
+        EXPECT_EQ(location_called_count_, i + 1);
+        // Don't cause confusion by checking details if no location yet
+        if (location_called_count_ > 0) {
+            // Should be more than 1 location by now, but if not, still don't check first fix speed
+            CheckLocation(last_location_, location_called_count_ > 1);
+        }
+    }
+}
+
 void GnssHalTest::notify() {
-    std::unique_lock<std::mutex> lock(mtx_);
-    notify_count_++;
+    {
+        std::unique_lock<std::mutex> lock(mtx_);
+        notify_count_++;
+    }
     cv_.notify_one();
 }
 
@@ -112,6 +193,14 @@
     return Void();
 }
 
+Return<void> GnssHalTest::GnssCallback::gnssSetCapabilitiesCb_2_0(uint32_t capabilities) {
+    ALOGI("Capabilities (v2.0) received %d", capabilities);
+    parent_.capabilities_called_count_++;
+    parent_.last_capabilities_ = capabilities;
+    parent_.notify();
+    return Void();
+}
+
 Return<void> GnssHalTest::GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) {
     ALOGI("Name received: %s", name.c_str());
     parent_.name_called_count_++;
@@ -120,8 +209,19 @@
     return Void();
 }
 
-Return<void> GnssHalTest::GnssCallback::gnssLocationCb(const GnssLocation& location) {
+Return<void> GnssHalTest::GnssCallback::gnssLocationCb(const GnssLocation_1_0& location) {
     ALOGI("Location received");
+    GnssLocation_2_0 location_v2_0;
+    location_v2_0.v1_0 = location;
+    return gnssLocationCbImpl(location_v2_0);
+}
+
+Return<void> GnssHalTest::GnssCallback::gnssLocationCb_2_0(const GnssLocation_2_0& location) {
+    ALOGI("Location (v2.0) received");
+    return gnssLocationCbImpl(location);
+}
+
+Return<void> GnssHalTest::GnssCallback::gnssLocationCbImpl(const GnssLocation_2_0& location) {
     parent_.location_called_count_++;
     parent_.last_location_ = location;
     parent_.notify();
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.h b/gnss/2.0/vts/functional/gnss_hal_test.h
index 5649b45..7793eb7 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.h
+++ b/gnss/2.0/vts/functional/gnss_hal_test.h
@@ -18,7 +18,6 @@
 #define GNSS_HAL_TEST_H_
 
 #include <android/hardware/gnss/2.0/IGnss.h>
-
 #include <VtsHalHidlTargetTestBase.h>
 #include <VtsHalHidlTargetTestEnvBase.h>
 
@@ -29,11 +28,13 @@
 using android::hardware::Return;
 using android::hardware::Void;
 
-using android::hardware::gnss::V1_0::GnssLocation;
-
 using android::hardware::gnss::V1_0::GnssLocationFlags;
-using android::hardware::gnss::V1_1::IGnssCallback;
 using android::hardware::gnss::V2_0::IGnss;
+using android::hardware::gnss::V2_0::IGnssCallback;
+
+using GnssLocation_1_0 = android::hardware::gnss::V1_0::GnssLocation;
+using GnssLocation_2_0 = android::hardware::gnss::V2_0::GnssLocation;
+
 using IGnssMeasurementCallback_1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback;
 using IGnssMeasurementCallback_1_1 = android::hardware::gnss::V1_1::IGnssMeasurementCallback;
 using IGnssMeasurementCallback_2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback;
@@ -97,10 +98,15 @@
         Return<void> gnssRequestTimeCb() override { return Void(); }
         // Actual (test) callback handlers
         Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
-        Return<void> gnssLocationCb(const GnssLocation& location) override;
+        Return<void> gnssLocationCb(const GnssLocation_1_0& location) override;
+        Return<void> gnssLocationCb_2_0(const GnssLocation_2_0& location) override;
         Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override;
+        Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
         Return<void> gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override;
         Return<void> gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override;
+
+      private:
+        Return<void> gnssLocationCbImpl(const GnssLocation_2_0& location);
     };
 
     /* Callback class for GnssMeasurement. */
@@ -130,9 +136,51 @@
      */
     void SetUpGnssCallback();
 
+    /*
+     * StartAndCheckFirstLocation:
+     *   Helper function to start location, and check the first one.
+     *
+     *   <p> Note this leaves the Location request active, to enable Stop call vs. other call
+     *   reordering tests.
+     *
+     * returns  true if a location was successfully generated
+     */
+    bool StartAndCheckFirstLocation();
+
+    /*
+     * CheckLocation:
+     *   Helper function to vet Location fields
+     *
+     *   check_speed: true if speed related fields are also verified.
+     */
+    void CheckLocation(const GnssLocation_2_0& location, const bool check_speed);
+
+    /*
+     * StartAndCheckLocations:
+     *   Helper function to collect, and check a number of
+     *   normal ~1Hz locations.
+     *
+     *   Note this leaves the Location request active, to enable Stop call vs. other call
+     *   reordering tests.
+     */
+    void StartAndCheckLocations(int count);
+
+    /*
+     * StopAndClearLocations:
+     * Helper function to stop locations, and clear any remaining notifications
+     */
+    void StopAndClearLocations();
+
+    /*
+     * SetPositionMode:
+     * Helper function to set positioning mode and verify output
+     */
+    void SetPositionMode(const int min_interval_msec, const bool low_power_mode);
+
     sp<IGnss> gnss_hal_;         // GNSS HAL to call into
     sp<IGnssCallback> gnss_cb_;  // Primary callback interface
 
+    // TODO: make these variables thread-safe.
     /* Count of calls to set the following items, and the latest item (used by
      * test.)
      */
@@ -144,7 +192,7 @@
 
     IGnssCallback::GnssSystemInfo last_info_;
     uint32_t last_capabilities_;
-    GnssLocation last_location_;
+    GnssLocation_2_0 last_location_;
     IGnssMeasurementCallback_2_0::GnssData last_measurement_;
     android::hardware::hidl_string last_name_;
 
diff --git a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
index 552cf1b..b135dba 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
@@ -31,7 +31,9 @@
 using IAGnss_2_0 = android::hardware::gnss::V2_0::IAGnss;
 using IAGnss_1_0 = android::hardware::gnss::V1_0::IAGnss;
 using IAGnssCallback_2_0 = android::hardware::gnss::V2_0::IAGnssCallback;
+
 using android::hardware::gnss::V1_0::IGnssNi;
+using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
 using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
 
 /*
@@ -185,10 +187,16 @@
     ASSERT_TRUE(last_measurement_.measurements.size() > 0);
     for (auto measurement : last_measurement_.measurements) {
         ASSERT_TRUE(
-            (int)measurement.codeType >=
-                (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::CODE_TYPE_A &&
-            (int)measurement.codeType <=
-                (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::CODE_TYPE_CODELESS);
+                ((int)measurement.codeType >=
+                                (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::A &&
+                        (int)measurement.codeType <=
+                                (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::N) ||
+                (int)measurement.codeType ==
+                        (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::OTHER);
+        if ((int)measurement.codeType ==
+                (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::OTHER) {
+            ASSERT_NE(measurement.otherCodeTypeName, "");
+        }
     }
 
     iGnssMeasurement->close();
@@ -263,3 +271,64 @@
     ASSERT_TRUE(result.isOk());
     EXPECT_TRUE(result);
 }
+
+/*
+ * TestGnssDataElapsedRealtimeFlags:
+ * Sets a GnssMeasurementCallback, waits for a GnssData object, and verifies the flags in member
+ * elapsedRealitme are valid.
+ */
+TEST_F(GnssHalTest, TestGnssDataElapsedRealtimeFlags) {
+    const int kFirstGnssMeasurementTimeoutSeconds = 10;
+
+    auto gnssMeasurement = gnss_hal_->getExtensionGnssMeasurement_2_0();
+    if (!gnssMeasurement.isOk()) {
+        return;
+    }
+
+    sp<IGnssMeasurement_2_0> iGnssMeasurement = gnssMeasurement;
+    if (iGnssMeasurement == nullptr) {
+        return;
+    }
+
+    sp<IGnssMeasurementCallback_2_0> callback = new GnssMeasurementCallback(*this);
+
+    auto result = iGnssMeasurement->setCallback_2_0(callback, /* enableFullTracking= */ true);
+    ASSERT_TRUE(result.isOk());
+    EXPECT_EQ(result, IGnssMeasurement_1_0::GnssMeasurementStatus::SUCCESS);
+
+    wait(kFirstGnssMeasurementTimeoutSeconds);
+    EXPECT_EQ(measurement_called_count_, 1);
+
+    ASSERT_TRUE((int)last_measurement_.elapsedRealtime.flags >= 0 &&
+                (int)last_measurement_.elapsedRealtime.flags <=
+                        (int)ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS);
+
+    // We expect a non-zero timestamp when set.
+    if (last_measurement_.elapsedRealtime.flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
+        ASSERT_TRUE(last_measurement_.elapsedRealtime.timestampNs != 0);
+    }
+
+    iGnssMeasurement->close();
+}
+
+TEST_F(GnssHalTest, TestGnssLocationElapsedRealtime) {
+    StartAndCheckFirstLocation();
+
+    ASSERT_TRUE((int)last_location_.elapsedRealtime.flags >= 0 &&
+                (int)last_location_.elapsedRealtime.flags <=
+                        (int)ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS);
+
+    // We expect a non-zero timestamp when set.
+    if (last_location_.elapsedRealtime.flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
+        ASSERT_TRUE(last_location_.elapsedRealtime.timestampNs != 0);
+    }
+
+    StopAndClearLocations();
+}
+
+// This test only verify that injectBestLocation_2_0 does not crash.
+TEST_F(GnssHalTest, TestInjectBestLocation_2_0) {
+    StartAndCheckFirstLocation();
+    gnss_hal_->injectBestLocation_2_0(last_location_);
+    StopAndClearLocations();
+}
diff --git a/gnss/common/OWNERS b/gnss/common/OWNERS
new file mode 100644
index 0000000..3ed36da
--- /dev/null
+++ b/gnss/common/OWNERS
@@ -0,0 +1,7 @@
+wyattriley@google.com
+gomo@google.com
+smalkos@google.com
+yuhany@google.com
+
+# VTS team
+yim@google.com
diff --git a/gnss/common/utils/default/Android.bp b/gnss/common/utils/default/Android.bp
new file mode 100644
index 0000000..4ea97fa
--- /dev/null
+++ b/gnss/common/utils/default/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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.
+//
+
+cc_library_static {
+    name: "android.hardware.gnss@common-default-lib",
+    vendor_available: true,
+    relative_install_path: "hw",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        "Utils.cpp",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "android.hardware.gnss@1.0",
+    ],
+}
diff --git a/gnss/common/utils/default/Utils.cpp b/gnss/common/utils/default/Utils.cpp
new file mode 100644
index 0000000..b9a06e8
--- /dev/null
+++ b/gnss/common/utils/default/Utils.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#include <Constants.h>
+#include <Utils.h>
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace common {
+
+using GnssSvFlags = V1_0::IGnssCallback::GnssSvFlags;
+
+GnssLocation Utils::getMockLocation() {
+    GnssLocation location = {.gnssLocationFlags = 0xFF,
+                             .latitudeDegrees = kMockLatitudeDegrees,
+                             .longitudeDegrees = kMockLongitudeDegrees,
+                             .altitudeMeters = kMockAltitudeMeters,
+                             .speedMetersPerSec = kMockSpeedMetersPerSec,
+                             .bearingDegrees = kMockBearingDegrees,
+                             .horizontalAccuracyMeters = kMockHorizontalAccuracyMeters,
+                             .verticalAccuracyMeters = kMockVerticalAccuracyMeters,
+                             .speedAccuracyMetersPerSecond = kMockSpeedAccuracyMetersPerSecond,
+                             .bearingAccuracyDegrees = kMockBearingAccuracyDegrees,
+                             .timestamp = kMockTimestamp};
+    return location;
+}
+
+GnssSvInfo Utils::getSvInfo(int16_t svid, GnssConstellationType type, float cN0DbHz,
+                            float elevationDegrees, float azimuthDegrees) {
+    GnssSvInfo svInfo = {.svid = svid,
+                         .constellation = type,
+                         .cN0Dbhz = cN0DbHz,
+                         .elevationDegrees = elevationDegrees,
+                         .azimuthDegrees = azimuthDegrees,
+                         .svFlag = GnssSvFlags::USED_IN_FIX | GnssSvFlags::HAS_EPHEMERIS_DATA |
+                                   GnssSvFlags::HAS_ALMANAC_DATA};
+    return svInfo;
+}
+
+}  // namespace common
+}  // namespace gnss
+}  // namespace hardware
+}  // namespace android
diff --git a/gnss/1.1/default/GnssConstants.h b/gnss/common/utils/default/include/Constants.h
similarity index 78%
rename from gnss/1.1/default/GnssConstants.h
rename to gnss/common/utils/default/include/Constants.h
index 9ce1a12..000a9ec 100644
--- a/gnss/1.1/default/GnssConstants.h
+++ b/gnss/common/utils/default/include/Constants.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef android_hardware_gnss_V1_1_GnssConstants_H_
-#define android_hardware_gnss_V1_1_GnssConstants_H_
+#ifndef android_hardware_gnss_common_Constants_H_
+#define android_hardware_gnss_common_Constants_H_
+
+#include <cstdint>
 
 namespace android {
 namespace hardware {
 namespace gnss {
-namespace V1_1 {
-namespace implementation {
+namespace common {
 
 const float kMockLatitudeDegrees = 37.4219999;
 const float kMockLongitudeDegrees = -122.0840575;
@@ -34,10 +35,9 @@
 const float kMockBearingAccuracyDegrees = 90;
 const int64_t kMockTimestamp = 1519930775453L;
 
-}  // namespace implementation
-}  // namespace V1_1
+}  // namespace common
 }  // namespace gnss
 }  // namespace hardware
 }  // namespace android
 
-#endif  // android_hardware_gnss_V1_1_GnssConstants_H_
+#endif  // android_hardware_gnss_common_Constants_H_
diff --git a/gnss/common/utils/default/include/Utils.h b/gnss/common/utils/default/include/Utils.h
new file mode 100644
index 0000000..47c8812
--- /dev/null
+++ b/gnss/common/utils/default/include/Utils.h
@@ -0,0 +1,42 @@
+/*
+ * 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_hardware_gnss_common_default_Utils_H_
+#define android_hardware_gnss_common_default_Utils_H_
+
+#include <android/hardware/gnss/1.0/IGnss.h>
+
+using GnssConstellationType = ::android::hardware::gnss::V1_0::GnssConstellationType;
+using GnssLocation = ::android::hardware::gnss::V1_0::GnssLocation;
+using GnssSvInfo = ::android::hardware::gnss::V1_0::IGnssCallback::GnssSvInfo;
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace common {
+
+struct Utils {
+    static GnssLocation getMockLocation();
+    static GnssSvInfo getSvInfo(int16_t svid, GnssConstellationType type, float cN0DbHz,
+                                float elevationDegrees, float azimuthDegrees);
+};
+
+}  // namespace common
+}  // namespace gnss
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_gnss_common_default_Utils_H_
diff --git a/gnss/common/utils/vts/Android.bp b/gnss/common/utils/vts/Android.bp
new file mode 100644
index 0000000..99d8cf9
--- /dev/null
+++ b/gnss/common/utils/vts/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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.
+//
+
+cc_library_static {
+    name: "android.hardware.gnss@common-vts-lib",
+    vendor_available: true,
+    relative_install_path: "hw",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        "Utils.cpp",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "android.hardware.gnss@1.0",
+    ],
+    static_libs: [
+        "libgtest",
+    ],
+}
diff --git a/gnss/common/utils/vts/Utils.cpp b/gnss/common/utils/vts/Utils.cpp
new file mode 100644
index 0000000..24d6883
--- /dev/null
+++ b/gnss/common/utils/vts/Utils.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#include <Utils.h>
+#include "gtest/gtest.h"
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace common {
+
+using V1_0::GnssLocationFlags;
+
+void Utils::checkLocation(const GnssLocation& location, bool check_speed,
+                          bool check_more_accuracies) {
+    EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_LAT_LONG);
+    EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_ALTITUDE);
+    if (check_speed) {
+        EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_SPEED);
+    }
+    EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_HORIZONTAL_ACCURACY);
+    // New uncertainties available in O must be provided,
+    // at least when paired with modern hardware (2017+)
+    if (check_more_accuracies) {
+        EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_VERTICAL_ACCURACY);
+        if (check_speed) {
+            EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_SPEED_ACCURACY);
+            if (location.gnssLocationFlags & GnssLocationFlags::HAS_BEARING) {
+                EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_BEARING_ACCURACY);
+            }
+        }
+    }
+    EXPECT_GE(location.latitudeDegrees, -90.0);
+    EXPECT_LE(location.latitudeDegrees, 90.0);
+    EXPECT_GE(location.longitudeDegrees, -180.0);
+    EXPECT_LE(location.longitudeDegrees, 180.0);
+    EXPECT_GE(location.altitudeMeters, -1000.0);
+    EXPECT_LE(location.altitudeMeters, 30000.0);
+    if (check_speed) {
+        EXPECT_GE(location.speedMetersPerSec, 0.0);
+        EXPECT_LE(location.speedMetersPerSec, 5.0);  // VTS tests are stationary.
+
+        // Non-zero speeds must be reported with an associated bearing
+        if (location.speedMetersPerSec > 0.0) {
+            EXPECT_TRUE(location.gnssLocationFlags & GnssLocationFlags::HAS_BEARING);
+        }
+    }
+
+    /*
+     * Tolerating some especially high values for accuracy estimate, in case of
+     * first fix with especially poor geometry (happens occasionally)
+     */
+    EXPECT_GT(location.horizontalAccuracyMeters, 0.0);
+    EXPECT_LE(location.horizontalAccuracyMeters, 250.0);
+
+    /*
+     * Some devices may define bearing as -180 to +180, others as 0 to 360.
+     * Both are okay & understandable.
+     */
+    if (location.gnssLocationFlags & GnssLocationFlags::HAS_BEARING) {
+        EXPECT_GE(location.bearingDegrees, -180.0);
+        EXPECT_LE(location.bearingDegrees, 360.0);
+    }
+    if (location.gnssLocationFlags & GnssLocationFlags::HAS_VERTICAL_ACCURACY) {
+        EXPECT_GT(location.verticalAccuracyMeters, 0.0);
+        EXPECT_LE(location.verticalAccuracyMeters, 500.0);
+    }
+    if (location.gnssLocationFlags & GnssLocationFlags::HAS_SPEED_ACCURACY) {
+        EXPECT_GT(location.speedAccuracyMetersPerSecond, 0.0);
+        EXPECT_LE(location.speedAccuracyMetersPerSecond, 50.0);
+    }
+    if (location.gnssLocationFlags & GnssLocationFlags::HAS_BEARING_ACCURACY) {
+        EXPECT_GT(location.bearingAccuracyDegrees, 0.0);
+        EXPECT_LE(location.bearingAccuracyDegrees, 360.0);
+    }
+
+    // Check timestamp > 1.48e12 (47 years in msec - 1970->2017+)
+    EXPECT_GT(location.timestamp, 1.48e12);
+}
+
+}  // namespace common
+}  // namespace gnss
+}  // namespace hardware
+}  // namespace android
diff --git a/gnss/common/utils/vts/include/Utils.h b/gnss/common/utils/vts/include/Utils.h
new file mode 100644
index 0000000..f8eeff6
--- /dev/null
+++ b/gnss/common/utils/vts/include/Utils.h
@@ -0,0 +1,39 @@
+/*
+ * 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_hardware_gnss_common_vts_Utils_H_
+#define android_hardware_gnss_common_vts_Utils_H_
+
+#include <android/hardware/gnss/1.0/IGnss.h>
+
+using GnssLocation = ::android::hardware::gnss::V1_0::GnssLocation;
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace common {
+
+struct Utils {
+    static void checkLocation(const GnssLocation& location, bool check_speed,
+                              bool check_more_accuracies);
+};
+
+}  // namespace common
+}  // namespace gnss
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_gnss_common_vts_Utils_H_
diff --git a/gnss/measurement_corrections/1.0/types.hal b/gnss/measurement_corrections/1.0/types.hal
index 5f20734..edf26bf 100644
--- a/gnss/measurement_corrections/1.0/types.hal
+++ b/gnss/measurement_corrections/1.0/types.hal
@@ -79,18 +79,35 @@
  * toaGpsNanosecondsOfWeek
  */
 struct MeasurementCorrections {
-    /** Represents latitude in degrees. */
+    /** Represents latitude in degrees at which the corrections are computed.. */
     double latitudeDegrees;
 
-    /** Represents longitude in degrees. */
+    /** Represents longitude in degrees at which the corrections are computed.. */
     double longitudeDegrees;
 
     /**
-     * Represents altitude in meters above the WGS 84 reference ellipsoid.
+     * Represents altitude in meters above the WGS 84 reference ellipsoid at which the corrections
+     * are computed.
      */
     double altitudeMeters;
 
-    /** Time Of Applicability, GPS time of week */
+    /**
+     * Represents the horizontal uncertainty (68% confidence) in meters on the device position at
+     * which the corrections are provided.
+     *
+     * This value is useful for example to judge how accurate the provided corrections are.
+     */
+    double horizontalPositionUncertaintyMeters;
+
+    /**
+     * Represents the vertical uncertainty (68% confidence) in meters on the device position at
+     * which the corrections are provided.
+     *
+     * This value is useful for example to judge how accurate the provided corrections are.
+     */
+    double verticalPositionUncertaintyMeters;
+
+    /** Time Of Applicability, GPS time of week in nanoseconds. */
     uint64_t toaGpsNanosecondsOfWeek;
 
     /**
diff --git a/graphics/common/1.2/Android.bp b/graphics/common/1.2/Android.bp
index a3ebdbf..b6cd865 100644
--- a/graphics/common/1.2/Android.bp
+++ b/graphics/common/1.2/Android.bp
@@ -15,9 +15,12 @@
         "android.hardware.graphics.common@1.1",
     ],
     types: [
+        "BufferUsage",
         "ColorMode",
         "Dataspace",
         "HardwareBuffer",
+        "Hdr",
+        "PixelFormat",
     ],
     gen_java: true,
     gen_java_constants: true,
diff --git a/graphics/common/1.2/types.hal b/graphics/common/1.2/types.hal
index 3da6176..ebea1dc 100644
--- a/graphics/common/1.2/types.hal
+++ b/graphics/common/1.2/types.hal
@@ -49,6 +49,32 @@
      * Embedded depth metadata following the dynamic depth specification.
      */
     DYNAMIC_DEPTH = 0x1002,
+
+    /**
+     * JPEG APP segments format as specified by JEIDA spec
+     *
+     * The buffer must only contain APP1 (Application Marker) segment followed
+     * by zero or more APPn segments, as is specified by JEITA CP-3451C section 4.5.4.
+     * The APP1 segment optionally contains a thumbnail. The buffer will not
+     * contain main compressed image.
+     *
+     * This value is valid with formats:
+     *    HAL_PIXEL_FORMAT_BLOB: JPEG APP segments optionally containing thumbnail image
+     * in APP1. BLOB buffer with this dataspace is output by HAL, and used by
+     * camera framework to encode into a HEIC image.
+     */
+    JPEG_APP_SEGMENTS = 0x1003,
+
+    /**
+     * ISO/IEC 23008-12
+     *
+     * High Efficiency Image File Format (HEIF)
+     *
+     * This value is valid with formats:
+     *    HAL_PIXEL_FORMAT_BLOB: A HEIC image encoded by HEIC or HEVC encoder
+     * according to ISO/IEC 23008-12.
+     */
+    HEIF = 0x1004,
 };
 
 enum ColorMode : @1.1::ColorMode {
@@ -70,6 +96,18 @@
 };
 
 /**
+ * Buffer usage definitions.
+ */
+enum BufferUsage : @1.1::BufferUsage {
+    /**
+     * Buffer is used as input for HEIC encoder.
+     */
+    HW_IMAGE_ENCODER = 1ULL << 27,
+
+    /* bits 28 and 32-47 must be zero and are reserved for future versions */
+};
+
+/**
  * HIDL counterpart of `AHardwareBuffer_Desc`.
  *
  * An `AHardwareBuffer_Desc` object can be converted to and from a
diff --git a/health/storage/1.0/default/Storage.cpp b/health/storage/1.0/default/Storage.cpp
index 2e53c50..561deaa 100644
--- a/health/storage/1.0/default/Storage.cpp
+++ b/health/storage/1.0/default/Storage.cpp
@@ -36,27 +36,20 @@
 using base::Trim;
 using base::WriteStringToFd;
 using base::WriteStringToFile;
+using fs_mgr::Fstab;
+using fs_mgr::ReadDefaultFstab;
 
 std::string getGarbageCollectPath() {
-    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                               fs_mgr_free_fstab);
-    struct fstab_rec* rec = NULL;
+    Fstab fstab;
+    ReadDefaultFstab(&fstab);
 
-    for (int i = 0; i < fstab->num_entries; i++) {
-        if (fs_mgr_has_sysfs_path(&fstab->recs[i])) {
-            rec = &fstab->recs[i];
-            break;
+    for (const auto& entry : fstab) {
+        if (!entry.sysfs_path.empty()) {
+            return entry.sysfs_path + "/manual_gc";
         }
     }
-    if (!rec) {
-        return "";
-    }
 
-    std::string path;
-    path.append(rec->sysfs_path);
-    path = path + "/manual_gc";
-
-    return path;
+    return "";
 }
 
 Return<void> Storage::garbageCollect(uint64_t timeoutSeconds,
diff --git a/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp b/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
index 5ad561c..946e5f2 100644
--- a/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
+++ b/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
@@ -36,7 +36,7 @@
 const uint64_t kDevGcTimeoutSec = 120;
 const std::chrono::seconds kDevGcTimeout{kDevGcTimeoutSec};
 // Time accounted for RPC calls.
-const std::chrono::milliseconds kRpcTime{100};
+const std::chrono::milliseconds kRpcTime{1000};
 
 template <typename R>
 std::string toString(std::chrono::duration<R, std::milli> time) {
@@ -90,11 +90,8 @@
     template <typename R, typename P>
     void waitForResult(std::chrono::duration<R, P> timeout, Result expected) {
         std::unique_lock<std::mutex> lock(mMutex);
-        if (waitLocked(&lock, timeout)) {
-            EXPECT_EQ(expected, mResult);
-        } else {
-            LOG(INFO) << "timeout after " << toString(timeout);
-        }
+        ASSERT_TRUE(waitLocked(&lock, timeout)) << "timeout after " << toString(timeout);
+        EXPECT_EQ(expected, mResult);
     }
 
    private:
diff --git a/input/classifier/1.0/default/Android.bp b/input/classifier/1.0/default/Android.bp
index ddd883c..ceb2aca 100644
--- a/input/classifier/1.0/default/Android.bp
+++ b/input/classifier/1.0/default/Android.bp
@@ -1,6 +1,6 @@
 cc_binary {
-    name: "android.hardware.input.classifier@1.0-service-example",
-    init_rc: ["android.hardware.input.classifier@1.0-service-example.rc"],
+    name: "android.hardware.input.classifier@1.0-service.default",
+    init_rc: ["android.hardware.input.classifier@1.0-service.default.rc"],
     relative_install_path: "hw",
     vendor: true,
     vintf_fragments: ["manifest_input.classifier.xml"],
diff --git a/input/classifier/1.0/default/InputClassifier.cpp b/input/classifier/1.0/default/InputClassifier.cpp
index a78bbc5..cce9190 100644
--- a/input/classifier/1.0/default/InputClassifier.cpp
+++ b/input/classifier/1.0/default/InputClassifier.cpp
@@ -31,41 +31,24 @@
 namespace implementation {
 
 // Methods from ::android::hardware::input::classifier::V1_0::IInputClassifier follow.
-Return<Classification> InputClassifier::classify(const MotionEvent& event) {
+Return<Classification> InputClassifier::classify(const MotionEvent& /*event*/) {
     /**
-     * In this example implementation, we will see how many "pixels" inside the video frame
-     * exceed the value of 250. If more than 6 such pixels are present, then treat the event
-     * as a "DEEP_PRESS".
+     * The touchscreen data is highly device-dependent.
+     * As a result, the implementation of this method will likely be hardware-specific.
+     * Here we just report gesture as not having any classification, which means that the
+     * default action will be taken in the framework.
+     * This is equivalent to not having the InputClassifier HAL at all.
      */
-    if (event.frames.size() == 0) {
-        return Classification::NONE;
-    }
-    ALOGI("Frame(O) timestamp = %" PRIu64 ", received %zu frame(s)", event.frames[0].timestamp,
-          event.frames.size());
-    for (const VideoFrame& frame : event.frames) {
-        size_t count = 0;
-        for (size_t i = 0; i < frame.data.size(); i++) {
-            if (frame.data[i] > 250) {
-                count++;
-            }
-        }
-        if (count > 6) {
-            return Classification::DEEP_PRESS;
-        }
-    }
-
     return Classification::NONE;
 }
 
 Return<void> InputClassifier::reset() {
-    // We don't have any internal state in this example implementation,
-    // so no work needed here.
+    // We don't have any internal state, so no work needed here.
     return Void();
 }
 
 Return<void> InputClassifier::resetDevice(int32_t /*deviceId*/) {
-    // We don't have any internal per-device state in this example implementation,
-    // so no work needed here.
+    // We don't have any internal per-device state, so no work needed here.
     return Void();
 }
 
diff --git a/input/classifier/1.0/default/android.hardware.input.classifier@1.0-service-example.rc b/input/classifier/1.0/default/android.hardware.input.classifier@1.0-service.default.rc
similarity index 90%
rename from input/classifier/1.0/default/android.hardware.input.classifier@1.0-service-example.rc
rename to input/classifier/1.0/default/android.hardware.input.classifier@1.0-service.default.rc
index f799bf4..e7d16ae 100644
--- a/input/classifier/1.0/default/android.hardware.input.classifier@1.0-service-example.rc
+++ b/input/classifier/1.0/default/android.hardware.input.classifier@1.0-service.default.rc
@@ -1,4 +1,4 @@
-service vendor.input.classifier-1-0 /vendor/bin/hw/android.hardware.input.classifier@1.0-service-example
+service vendor.input.classifier-1-0 /vendor/bin/hw/android.hardware.input.classifier@1.0-service.default
     # Must be specified if "disabled" is set. This HAL will only start if requested via getService
     interface android.hardware.input.classifier@1.0::IInputClassifier default
     class hal
diff --git a/input/classifier/1.0/vts/OWNERS b/input/classifier/1.0/vts/OWNERS
new file mode 100644
index 0000000..447f3d9
--- /dev/null
+++ b/input/classifier/1.0/vts/OWNERS
@@ -0,0 +1,3 @@
+michaelwr@google.com
+pquinn@google.com
+svv@google.com
\ No newline at end of file
diff --git a/input/classifier/1.0/vts/functional/Android.bp b/input/classifier/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..ef49d70
--- /dev/null
+++ b/input/classifier/1.0/vts/functional/Android.bp
@@ -0,0 +1,27 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "VtsHalInputClassifierV1_0TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalInputClassifierV1_0TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.input.classifier@1.0",
+        "android.hardware.input.common@1.0",
+    ],
+    test_suites: ["general-tests"],
+}
+
diff --git a/input/classifier/1.0/vts/functional/VtsHalInputClassifierV1_0TargetTest.cpp b/input/classifier/1.0/vts/functional/VtsHalInputClassifierV1_0TargetTest.cpp
new file mode 100644
index 0000000..f033c2a
--- /dev/null
+++ b/input/classifier/1.0/vts/functional/VtsHalInputClassifierV1_0TargetTest.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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_TAG "input_classifier_hal_test"
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
+#include <android/hardware/input/common/1.0/types.h>
+#include <input/InputDevice.h>
+#include <unistd.h>
+
+using ::android::ReservedInputDeviceId;
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::input::classifier::V1_0::IInputClassifier;
+using ::android::hardware::input::common::V1_0::Action;
+using ::android::hardware::input::common::V1_0::Axis;
+using ::android::hardware::input::common::V1_0::Button;
+using ::android::hardware::input::common::V1_0::EdgeFlag;
+using ::android::hardware::input::common::V1_0::MotionEvent;
+using ::android::hardware::input::common::V1_0::PointerCoords;
+using ::android::hardware::input::common::V1_0::PointerProperties;
+using ::android::hardware::input::common::V1_0::Source;
+using ::android::hardware::input::common::V1_0::ToolType;
+using ::android::hardware::input::common::V1_0::VideoFrame;
+
+static MotionEvent getSimpleMotionEvent() {
+    MotionEvent event;
+    event.action = Action::DOWN;
+    event.actionButton = Button::NONE;
+    event.actionIndex = 0;
+    event.buttonState = 0;
+    event.deviceId = 0;
+    event.deviceTimestamp = 0;
+    event.displayId = 1;
+    event.downTime = 2;
+    event.edgeFlags = 0;
+    event.eventTime = 3;
+    event.flags = 0;
+    event.frames = {};
+    event.metaState = 0;
+    event.policyFlags = 0;
+    event.source = Source::TOUCHSCREEN;
+    event.xPrecision = 0;
+    event.yPrecision = 0;
+
+    PointerCoords coords;
+    coords.bits = Axis::X | Axis::Y;
+    coords.values = {1 /*X*/, 2 /*Y*/};
+    event.pointerCoords = {coords};
+
+    PointerProperties properties;
+    properties.id = 0;
+    properties.toolType = ToolType::FINGER;
+    event.pointerProperties = {properties};
+
+    return event;
+}
+
+// Test environment for Input Classifier HIDL HAL.
+class InputClassifierHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+  public:
+    // get the test environment singleton
+    static InputClassifierHidlEnvironment* Instance() {
+        static InputClassifierHidlEnvironment* instance = new InputClassifierHidlEnvironment;
+        return instance;
+    }
+
+    virtual void registerTestServices() override { registerTestService<IInputClassifier>(); }
+
+  private:
+    InputClassifierHidlEnvironment() {}
+};
+
+// The main test class for INPUT CLASSIFIER HIDL HAL 1.0.
+class InputClassifierHidlTest_1_0 : public ::testing::VtsHalHidlTargetTestBase {
+  public:
+    virtual void SetUp() override {
+        classifier = ::testing::VtsHalHidlTargetTestBase::getService<IInputClassifier>(
+                InputClassifierHidlEnvironment::Instance()->getServiceName<IInputClassifier>());
+        ASSERT_NE(classifier, nullptr);
+    }
+
+    virtual void TearDown() override {}
+
+    sp<IInputClassifier> classifier;
+};
+
+/**
+ * Call resetDevice(..) for a few common device id values, and make sure that the HAL
+ * can handle the resets gracefully.
+ */
+TEST_F(InputClassifierHidlTest_1_0, ResetDevice) {
+    EXPECT_TRUE(classifier->resetDevice(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID).isOk());
+    EXPECT_TRUE(classifier->resetDevice(ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID).isOk());
+    EXPECT_TRUE(classifier->resetDevice(1).isOk());
+    EXPECT_TRUE(classifier->resetDevice(2).isOk());
+}
+
+/**
+ * Call reset() on the HAL to ensure no fatal failure there.
+ */
+TEST_F(InputClassifierHidlTest_1_0, ResetHal) {
+    EXPECT_TRUE(classifier->reset().isOk());
+}
+
+/**
+ * Classify an event without any video frames.
+ */
+TEST_F(InputClassifierHidlTest_1_0, Classify_NoVideoFrame) {
+    // Create a MotionEvent that does not have any video data
+    MotionEvent event = getSimpleMotionEvent();
+
+    EXPECT_TRUE(classifier->classify(event).isOk());
+    // We are not checking the actual classification here,
+    // because the HAL operation is highly device-specific.
+
+    // Return HAL to a consistent state by doing a reset
+    classifier->reset();
+}
+
+/**
+ * Classify an event with one video frame. Should be the most common scenario.
+ */
+TEST_F(InputClassifierHidlTest_1_0, Classify_OneVideoFrame) {
+    MotionEvent event = getSimpleMotionEvent();
+    VideoFrame frame;
+    frame.data = {1, 2, 3, 4};
+    frame.height = 2;
+    frame.width = 2;
+    frame.timestamp = event.eventTime;
+    event.frames = {frame};
+
+    EXPECT_TRUE(classifier->classify(event).isOk());
+    // We are not checking the actual classification here,
+    // because the HAL operation is highly device-specific.
+
+    // Return HAL to a consistent state by doing a reset
+    classifier->reset();
+}
+
+/**
+ * Classify an event with 2 video frames. This could happen if there's slowness in the system,
+ * or if simply the video rate is somehow higher that the input event rate.
+ * The HAL should be able to handle events with more than 1 video frame.
+ *
+ * The frames should be in chronological order, but it is not guaranteed that they will have
+ * monotonically increasing timestamps. Still, we provide consistent timestamps here since that
+ * is the most realistic mode of operation.
+ */
+TEST_F(InputClassifierHidlTest_1_0, Classify_TwoVideoFrames) {
+    MotionEvent event = getSimpleMotionEvent();
+    VideoFrame frame1;
+    frame1.data = {1, 2, 3, 4};
+    frame1.height = 2;
+    frame1.width = 2;
+    frame1.timestamp = event.eventTime;
+    VideoFrame frame2 = frame1;
+    frame2.data = {5, 5, 5, -1};
+    frame2.timestamp += 1;
+    event.frames = {frame1, frame2};
+
+    EXPECT_TRUE(classifier->classify(event).isOk());
+    // We are not checking the actual classification here,
+    // because the HAL operation is highly device-specific.
+
+    // Return HAL to a consistent state by doing a reset
+    classifier->reset();
+}
+
+int main(int argc, char** argv) {
+    ::testing::AddGlobalTestEnvironment(InputClassifierHidlEnvironment::Instance());
+    ::testing::InitGoogleTest(&argc, argv);
+    InputClassifierHidlEnvironment::Instance()->init(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    LOG(INFO) << "Test result = " << status;
+    return status;
+}
diff --git a/input/common/1.0/Android.bp b/input/common/1.0/Android.bp
index 68a77f1..3de18b4 100644
--- a/input/common/1.0/Android.bp
+++ b/input/common/1.0/Android.bp
@@ -9,9 +9,6 @@
     srcs: [
         "types.hal",
     ],
-    interfaces: [
-        "android.hidl.base@1.0",
-    ],
     types: [
         "Action",
         "Axis",
diff --git a/input/common/1.0/types.hal b/input/common/1.0/types.hal
index 1a07f3b..9ad368b 100644
--- a/input/common/1.0/types.hal
+++ b/input/common/1.0/types.hal
@@ -685,20 +685,20 @@
  * Touch heatmap.
  *
  * The array is a 2-D row-major matrix with dimensions (height, width).
- * The heatmap data does not rotate when device orientation changes.
+ * The heatmap data is rotated when device orientation changes.
  *
  * Example:
  *
  * If the data in the array is:
  * data[i] = i for i in 0 .. 59,
- * then it can be represented as follows:
+ * then it can be represented as a 10 x 6 matrix:
  *
- *  <--    width   -- >
+ *  <--   width   -->
  *   0  1  2  3  4  5   ^
  *   6  7  8  9 10 11   |
- *  12 12 14 15 16 17   |
- *  18    ...      23   | height
- *  24    ...      29   |
+ *  12 13 14 15 16 17   |
+ *  18    ...      23   |
+ *  24    ...      29   | height
  *  30    ...      35   |
  *  36    ...      41   |
  *  42    ...      47   |
@@ -708,16 +708,33 @@
  * Looking at the device in standard portrait orientation,
  * the element "0" is the top left of the screen,
  * "5" is at the top right, and "59" is the bottom right.
- * Here width=6, and height=10.
+ * Here height=10 and width=6.
+ *
+ * If the screen orientation changes to landscape (a 90 degree orientation
+ * change), the frame's dimensions will become 6 x 10
+ * and the data will look as follows:
+ * 54 48 42 36 30 24 18 12  6  0     ^
+ * ...                  13  7  1     |
+ * ...                  14  8  2     | height
+ * ...                  15  9  3     |
+ * ...                  16 10  4     |
+ * 59 53 47 41 35 29 23 17 11  5     v
+ * <--        width          -->
+ *
+ * Here the element "0" is at the physical top left of the unrotated screen.
+ *
+ * Since the coordinates of a MotionEvent are also adjusted based on the
+ * orientation, the rotation of the video frame data ensures that
+ * the axes for MotionEvent and VideoFrame data are consistent.
  */
 struct VideoFrame {
     /**
      * Video frame data.
-     * Size of the data is width * height.
+     * Size of the data is height * width.
      */
     vec<int16_t> data;
-    uint32_t width;
     uint32_t height;
+    uint32_t width;
     /**
      * Time at which the frame was collected, in nanoseconds.
      * Measured with the same clock that is used to populate MotionEvent times.
diff --git a/keymaster/3.0/default/KeymasterDevice.cpp b/keymaster/3.0/default/KeymasterDevice.cpp
index 6fabbde..7d3e6f2 100644
--- a/keymaster/3.0/default/KeymasterDevice.cpp
+++ b/keymaster/3.0/default/KeymasterDevice.cpp
@@ -19,7 +19,7 @@
 
 #include "KeymasterDevice.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <AndroidKeymaster3Device.h>
 #include <hardware/keymaster0.h>
diff --git a/media/c2/1.0/Android.bp b/media/c2/1.0/Android.bp
index c37c22b..56c78b2 100644
--- a/media/c2/1.0/Android.bp
+++ b/media/c2/1.0/Android.bp
@@ -13,6 +13,7 @@
         "IComponentListener.hal",
         "IComponentStore.hal",
         "IConfigurable.hal",
+        "IInputSink.hal",
         "IInputSurface.hal",
         "IInputSurfaceConnection.hal",
     ],
diff --git a/media/c2/1.0/IComponent.hal b/media/c2/1.0/IComponent.hal
index deb9255..7fd551f 100644
--- a/media/c2/1.0/IComponent.hal
+++ b/media/c2/1.0/IComponent.hal
@@ -22,13 +22,19 @@
 import IConfigurable;
 import IComponentInterface;
 import IComponentListener;
+import IInputSurface;
+import IInputSurfaceConnection;
 
 /**
- * Interface for a Codec 2.0 component corresponding to API level 1.0 or
- * below. Components have two states: stopped and running. The running
- * state has three sub-states: executing, tripped and error.
+ * Interface for a Codec2 component corresponding to API level 1.0 or below.
+ * Components have two states: stopped and running. The running state has three
+ * sub-states: executing, tripped and error.
+ *
+ * All methods in `IComponent` must not block. If a method call cannot be
+ * completed in a timely manner, it must return `TIMED_OUT` in the return
+ * status.
  */
-interface IComponent extends IComponentInterface {
+interface IComponent {
 
     // METHODS AVAILABLE WHEN RUNNING
     // =========================================================================
@@ -38,44 +44,42 @@
      *
      * This method must be supported in running (including tripped) states.
      *
-     * This method must return within 1ms
+     * It is acceptable for this method to return `OK` and return an error value
+     * using the IComponentListener::onWorkDone() callback.
      *
-     * It is acceptable for this method to return OK and return an error value
-     * using the onWorkDone() callback.
-     *
-     * @param workBundle WorkBundle object containing Works to queue to the
-     * component.
+     * @param workBundle `WorkBundle` object containing a list of `Work` objects
+     *     to queue to the component.
      * @return status Status of the call, which may be
-     *   - OK        - Works in \p workBundle were successfully queued.
-     *   - BAD_INDEX - Some component(s) in some Work do(es) not exist.
-     *   - CANNOT_DO - The components are not tunneled.
-     *   - NO_MEMORY - Not enough memory to queue \p workBundle.
-     *   - CORRUPTED - Some unknown error prevented queuing the Works.
-     *                 (unexpected).
+     *   - `OK`        - Works in @p workBundle were successfully queued.
+     *   - `BAD_INDEX` - Some component id in some `Worklet` is not valid.
+     *   - `CANNOT_DO` - The components are not tunneled but some `Work` object
+     *                   contains tunneling information.
+     *   - `NO_MEMORY` - Not enough memory to queue @p workBundle.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
      */
     queue(WorkBundle workBundle) generates (Status status);
 
     /**
-     * Discards and abandons any pending work for the component.
+     * Discards and abandons any pending `Work` items for the component.
      *
      * This method must be supported in running (including tripped) states.
      *
-     * This method must return within 5ms.
+     * `Work` that could be immediately abandoned/discarded must be returned in
+     * @p flushedWorkBundle. The order in which queued `Work` items are
+     * discarded can be arbitrary.
      *
-     * Work that could be immediately abandoned/discarded must be returned in
-     * \p flushedWorks; this can be done in an arbitrary order.
-     *
-     * Work that could not be abandoned or discarded immediately must be marked
-     * to be discarded at the earliest opportunity, and must be returned via
-     * the onWorkDone() callback. This must be completed within 500ms.
+     * `Work` that could not be abandoned or discarded immediately must be
+     * marked to be discarded at the earliest opportunity, and must be returned
+     * via IComponentListener::onWorkDone(). This must be completed within
+     * 500ms.
      *
      * @return status Status of the call, which may be
-     *   - OK        - The component has been successfully flushed.
-     *   - TIMED_OUT - The flush could not be completed within the time limit.
-     *                 (unexpected)
-     *   - CORRUPTED - Some unknown error prevented flushing from
-     *                 completion. (unexpected)
-     * @return flushedWorkBundle WorkBundle object containing flushed Works.
+     *   - `OK`        - The component has been successfully flushed.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return flushedWorkBundle `WorkBundle` object containing flushed `Work`
+     *     items.
      */
     flush(
         ) generates (
@@ -87,42 +91,39 @@
      * 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 be 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.
+     * The last `Work` item is marked as "drain-till-here", so the component is
+     * notified not to wait for further `Work` before it processes what is
+     * already queued. This method can also be 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 return within 1ms.
-     *
-     * Work that is completed must be returned via the onWorkDone() callback.
+     * `Work` that is completed must be returned via
+     * IComponentListener::onWorkDone().
      *
      * @param withEos Whether to drain the component with marking end-of-stream.
      * @return status Status of the call, which may be
-     *   - OK        - The drain request has been successfully recorded.
-     *   - TIMED_OUT - The flush could not be completed within the time limit.
-     *                 (unexpected)
-     *   - CORRUPTED - Some unknown error prevented flushing from completion.
-     *                 (unexpected)
+     *   - `OK`        - The drain request has been successfully recorded.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
      */
     drain(bool withEos) generates (Status status);
 
     /**
      * Starts using a surface for output.
      *
-     * @param blockPoolId The id of the BlockPool to be associated with the
-     * output surface.
-     * @param surface A surface to use for codec output.
+     * This method must not block.
+     *
+     * @param blockPoolId Id of the `C2BlockPool` to be associated with the
+     *     output surface.
+     * @param surface Output surface.
      * @return status Status of the call, which may be
-     *   - OK        - The operation completed successfully.
-     *   - CANNOT_DO - The component does not support an output surface.
-     *   - REFUSED   - The output surface cannot be accessed.
-     *   - TIMED_OUT - The component could not be connected within the time
-     *                 limit. (unexpected)
-     *   - CORRUPTED - Some unknown error prevented connecting the component.
-     *                 (unexpected)
+     *   - `OK`        - The operation completed successfully.
+     *   - `CANNOT_DO` - The component does not support an output surface.
+     *   - `REFUSED`   - The output surface cannot be accessed.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
      */
     setOutputSurface(
             uint64_t blockPoolId,
@@ -132,65 +133,101 @@
         );
 
     /**
-     * Starts using a persistent OMX input surface for a component.
+     * Starts using an input surface.
      *
      * The component must be in running state.
      *
-     * @param producer Producer component of an OMX persistent input surface.
-     * @param source Source component of an OMX persistent input surface.
+     * @param inputSurface Input surface to connect to.
      * @return status Status of the call, which may be
-     *   - OK        - The operation completed successfully.
-     *   - CANNOT_DO - The component does not support an input surface.
-     *   - BAD_STATE - Component is not in running state.
-     *   - DUPLICATE - The component is already connected to an input surface.
-     *   - REFUSED   - The input surface is already in use.
-     *   - NO_MEMORY - Not enough memory to start the component.
-     *   - TIMED_OUT - The component could not be connected within the time
-     *                 limit. (unexpected)
-     *   - CORRUPTED - Some unknown error prevented connecting the component.
-     *                 (unexpected)
+     *   - `OK`        - The operation completed successfully.
+     *   - `CANNOT_DO` - The component does not support an input surface.
+     *   - `BAD_STATE` - The component is not in running state.
+     *   - `DUPLICATE` - The component is already connected to an input surface.
+     *   - `REFUSED`   - The input surface is already in use.
+     *   - `NO_MEMORY` - Not enough memory to start the component.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return connection `IInputSurfaceConnection` object, which can be used to
+     *     query and configure properties of the connection. This cannot be
+     *     null.
+     */
+    connectToInputSurface(
+            IInputSurface inputSurface
+        ) generates (
+            Status status,
+            IInputSurfaceConnection connection
+        );
+
+    /**
+     * Starts using an OMX input surface.
+     *
+     * The component must be in running state.
+     *
+     * This method is similar to connectToInputSurface(), but it takes an OMX
+     * input surface (as a pair of `IGraphicBufferProducer` and
+     * `IGraphicBufferSource`) instead of Codec2's own `IInputSurface`.
+     *
+     * @param producer Producer component of an OMX input surface.
+     * @param source Source component of an OMX input surface.
+     * @return status Status of the call, which may be
+     *   - `OK`        - The operation completed successfully.
+     *   - `CANNOT_DO` - The component does not support an OMX input surface.
+     *   - `BAD_STATE` - The component is not in running state.
+     *   - `DUPLICATE` - The component is already connected to an input surface.
+     *   - `REFUSED`   - The input surface is already in use.
+     *   - `NO_MEMORY` - Not enough memory to start the component.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return connection `IInputSurfaceConnection` object, which can be used to
+     *     query and configure properties of the connection. This cannot be
+     *     null.
      */
     connectToOmxInputSurface(
             IGraphicBufferProducer producer,
             IGraphicBufferSource source
-        ) generates (Status status);
+        ) generates (
+            Status status,
+            IInputSurfaceConnection connection
+        );
 
     /**
      * Stops using an input surface.
      *
-     * This call is used for both Codec 2.0 and OMX input surfaces.
-     *
      * The component must be in running state.
      *
      * @return status Status of the call, which may be
-     *   - OK        - The operation completed successfully.
-     *   - CANNOT_DO - The component does not support an input surface.
-     *   - BAD_STATE - Component is not in running state.
-     *   - NOT_FOUND - The component is not connected to an input surface.
-     *   - TIMED_OUT - The component could not be connected within the time
-     *                 limit. (unexpected)
-     *   - CORRUPTED - Some unknown error prevented connecting the component.
-     *                 (unexpected)
+     *   - `OK`        - The operation completed successfully.
+     *   - `CANNOT_DO` - The component does not support an input surface.
+     *   - `BAD_STATE` - The component is not in running state.
+     *   - `NOT_FOUND` - The component is not connected to an input surface.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
      */
     disconnectFromInputSurface() generates (Status Status);
 
     /**
-     * Creates a local block pool backed by the given allocator and returns its
-     * identifier.
+     * Creates a local `C2BlockPool` backed by the given allocator and returns
+     * its id.
      *
-     * This call must return within 100 msec.
+     * The returned @p blockPoolId is the only way the client can refer to a
+     * `C2BlockPool` object in the component. The id can be passed to
+     * setOutputSurface() or used in some C2Param objects later.
      *
-     * @param allocatorId The Codec 2.0 allocator ID
+     * The created `C2BlockPool` object can be destroyed by calling
+     * destroyBlockPool(), reset() or release(). reset() and release() must
+     * destroy all `C2BlockPool` objects that have been created.
+     *
+     * @param allocatorId Id of a `C2Allocator`.
      * @return status Status of the call, which may be
-     *   - OK        - The operation completed successfully.
-     *   - NO_MEMORY - Not enough memory to create the pool.
-     *   - BAD_VALUE - Invalid allocator.
-     *   - TIMED_OUT - The pool could not be created within the time
-     *                 limit. (unexpected)
-     *   - CORRUPTED - Some unknown error prevented creating the pool.
-     *                 (unexpected)
-     * @return blockPoolId The Codec 2.0 blockpool ID for the created pool.
-     * @return configurable Configuration interface for the created pool.
+     *   - `OK`        - The operation completed successfully.
+     *   - `NO_MEMORY` - Not enough memory to create the pool.
+     *   - `BAD_VALUE` - @p allocatorId is not recognized.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return blockPoolId Id of the created C2BlockPool object. This may be
+     *     used in setOutputSurface() if the allocator
+     * @return configurable Configuration interface for the created pool. This
+     *     must not be null.
      */
     createBlockPool(uint32_t allocatorId) generates (
         Status status,
@@ -201,17 +238,13 @@
     /**
      * Destroys a local block pool previously created by createBlockPool().
      *
-     * This call must return within 100 msec.
-     *
-     * @param blockPoolId The block pool id previously returned by
+     * @param blockPoolId Id of a `C2BlockPool` that was previously returned by
      *      createBlockPool().
      * @return status Status of the call, which may be
-     *   - OK        - The operation completed successfully.
-     *   - NOT_FOUND - The supplied blockPoolId is not valid.
-     *   - TIMED_OUT - The pool could not be destroyedwithin the time limit.
-     *                 (unexpected)
-     *   - CORRUPTED - Some unknown error prevented destruction of the pool.
-     *                 (unexpected)
+     *   - `OK`        - The operation completed successfully.
+     *   - `NOT_FOUND` - The supplied blockPoolId is not valid.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
      */
     destroyBlockPool(uint64_t blockPoolId) generates (Status status);
 
@@ -223,28 +256,24 @@
      *
      * This method must be supported in stopped state as well as tripped state.
      *
-     * If the return value is OK, the component must be in the running state.
-     * If the return value is BAD_STATE or DUPLICATE, no state change is
-     * expected as a response to this call.
-     * Otherwise, the component must be in the stopped state.
+     * If the return value is `OK`, the component must be in the running state.
+     * If the return value is `BAD_STATE` or `DUPLICATE`, no state change is
+     * expected as a response to this call. Otherwise, the component must be in
+     * the stopped state.
      *
      * If a component is in the tripped state and start() is called while the
-     * component configuration still results in a trip, start must succeed and
-     * a new onTripped callback must be used to communicate the configuration
+     * component configuration still results in a trip, start() must succeed and
+     * a new onTripped() callback must be used to communicate the configuration
      * conflict that results in the new trip.
      *
-     * This method must return within 500ms.
-     *
      * @return status Status of the call, which may be
-     *   - OK        - The component has started successfully.
-     *   - BAD_STATE - Component is not in stopped or tripped state.
-     *   - DUPLICATE - When called during another start call from another
-     *                 thread.
-     *   - NO_MEMORY - Not enough memory to start the component.
-     *   - TIMED_OUT - The component could not be started within the time limit.
-     *                 (unexpected)
-     *   - CORRUPTED - Some unknown error prevented starting the component.
-     *                 (unexpected)
+     *   - `OK`        - The component has started successfully.
+     *   - `BAD_STATE` - Component is not in stopped or tripped state.
+     *   - `DUPLICATE` - When called during another start call from another
+     *                   thread.
+     *   - `NO_MEMORY` - Not enough memory to start the component.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
      */
     start() generates (Status status);
 
@@ -255,22 +284,22 @@
      *
      * This method must return withing 500ms.
      *
-     * Upon this call, all pending work must be abandoned.
-     * If the return value is BAD_STATE or DUPLICATE, no state change is
-     * expected as a response to this call.
-     * For all other return values, the component must be in the stopped state.
+     * Upon this call, all pending `Work` must be abandoned.
+     *
+     * If the return value is `BAD_STATE` or `DUPLICATE`, no state change is
+     * expected as a response to this call. For all other return values, the
+     * component must be in the stopped state.
      *
      * This does not alter any settings and tunings that may have resulted in a
      * tripped state.
      *
      * @return status Status of the call, which may be
-     *   - OK        - The component has stopped successfully.
-     *   - BAD_STATE - Component is not in running state.
-     *   - DUPLICATE - When called during another stop call from another thread.
-     *   - TIMED_OUT - The component could not be stopped within the time limit.
-     *                 (unexpected)
-     *   - CORRUPTED - Some unknown error prevented starting the component.
-     *                 (unexpected)
+     *   - `OK`        - The component has stopped successfully.
+     *   - `BAD_STATE` - Component is not in running state.
+     *   - `DUPLICATE` - When called during another stop call from another
+     *                   thread.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
      */
     stop() generates (Status status);
 
@@ -284,25 +313,24 @@
      *
      * This method must return withing 500ms.
      *
-     * After this call returns all work must have been abandoned, all references
-     * must have been released.
+     * When this call returns, if @p status is `OK`, all `Work` items must
+     * have been abandoned, and all resources (including `C2BlockPool` objects
+     * previously created by createBlockPool()) must have been released.
      *
-     * If the return value is BAD_STATE or 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.
+     * If the return value is `BAD_STATE` or `DUPLICATE`, no state change is
+     * expected as a response to this call. For all other return values, the
+     * component must be in the stopped state.
      *
-     * This brings settings back to their default - "guaranteeing" no tripped
+     * This brings settings back to their default, "guaranteeing" no tripped
      * state.
      *
      * @return status Status of the call, which may be
-     *   - OK        - The component has been reset.
-     *   - BAD_STATE - Component is in released state.
-     *   - DUPLICATE - When called during another reset call from another
-     *                 thread.
-     *   - TIMED_OUT - The component could not be reset within the time limit.
-     *                 (unexpected)
-     *   - CORRUPTED - Some unknown error prevented resetting the component.
-     *                 (unexpected)
+     *   - `OK`        - The component has been reset.
+     *   - `BAD_STATE` - Component is in released state.
+     *   - `DUPLICATE` - When called during another reset call from another
+     *                   thread.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
      */
     reset() generates (Status status);
 
@@ -311,19 +339,27 @@
      *
      * This method must be supported in stopped state.
      *
-     * This method must return withing 500ms. Upon return all references must
-     * be abandoned.
+     * This method destroys the component. Upon return, if @p status is `OK` or
+     * `DUPLICATE`, all resources must have been released.
      *
      * @return status Status of the call, which may be
-     *   - OK        - The component has been released.
-     *   - BAD_STATE - The component is running.
-     *   - DUPLICATE - The component is already released.
-     *   - TIMED_OUT - The component could not be released within the time
-     *                 limit. (unexpected)
-     *   - CORRUPTED - Some unknown error prevented releasing the component.
-     *                 (unexpected)
+     *   - `OK`        - The component has been released.
+     *   - `BAD_STATE` - The component is running.
+     *   - `DUPLICATE` - The component is already released.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
      */
     release() generates (Status status);
 
+    /**
+     * Returns the @ref IComponentInterface instance associated to this
+     * component.
+     *
+     * An @p IConfigurable instance for the component can be obtained by calling
+     * IComponentInterface::getConfigurable() on the returned @p intf.
+     *
+     * @return intf `IComponentInterface` instance. This must not be null.
+     */
+    getInterface() generates (IComponentInterface intf);
 };
 
diff --git a/media/c2/1.0/IComponentInterface.hal b/media/c2/1.0/IComponentInterface.hal
index d4b30b1..a007d02 100644
--- a/media/c2/1.0/IComponentInterface.hal
+++ b/media/c2/1.0/IComponentInterface.hal
@@ -19,21 +19,20 @@
 import IConfigurable;
 
 /**
- * Component interface object. This object contains all of the configuration of
+ * Component interface object. This object contains all of the configurations of
  * a potential or actual component. It can be created and used independently of
- * an actual Codec 2.0 component instance to query support and parameters for
- * various component settings and configurations for a potential component.
- * Actual components also expose this interface.
+ * an actual Codec2 component to query supported parameters for various
+ * component settings, and configurations for a potential component.
+ *
+ * An actual component exposes this interface via IComponent::getInterface().
  */
-interface IComponentInterface extends IConfigurable {
-    /*
-     * There are no additional methods to IConfigurable interface.
+interface IComponentInterface {
+    /**
+     * Returns the @ref IConfigurable instance associated to this component
+     * interface.
      *
-     * Component interfaces have no states.
-     *
-     * The name of the component or component interface object is a unique name
-     * for that component or component interface 'class'; however, multiple
-     * instances of that component must have the same name.
+     * @return configurable `IConfigurable` instance. This must not be null.
      */
+    getConfigurable() generates (IConfigurable configurable);
 };
 
diff --git a/media/c2/1.0/IComponentListener.hal b/media/c2/1.0/IComponentListener.hal
index eb71ecb..70d5fb2 100644
--- a/media/c2/1.0/IComponentListener.hal
+++ b/media/c2/1.0/IComponentListener.hal
@@ -17,54 +17,112 @@
 package android.hardware.media.c2@1.0;
 
 /**
- * This callback interface is used for handling notifications from IComponent.
+ * Callback interface for handling notifications from @ref IComponent.
  */
 interface IComponentListener {
 
     /**
-     * Notify the listener that some works have been completed.
+     * Notify the listener that some `Work` items have been completed.
+     *
+     * All the input buffers in the returned `Work` objects must not be used by
+     * the component after onWorkDone() is called.
+     *
+     * @param workBundle List of completed `Work` objects.
      */
     oneway onWorkDone(WorkBundle workBundle);
 
     /**
      * Notify the listener that the component is tripped.
+     *
+     * @param settingResults List of failures.
      */
     oneway onTripped(vec<SettingResult> settingResults);
 
     /**
      * Notify the listener of an error.
      *
-     * @param status Error type. \p status may be `OK`, which means that an
-     *     error has occurred, but the error type is unknown.
-     * @param errorCode Additional error code. The framework may not recognize
-     *     this.
+     * @param status Error type. @p status may be `OK`, which means that an
+     *     error has occurred, but the error type does not fit into the type
+     *     `Status`. In this case, additional information is provided by
+     *     @p errorCode.
+     * @param errorCode Additional error information. The framework may not
+     *     recognize the meaning of this value.
      */
     oneway onError(Status status, uint32_t errorCode);
 
     /**
-     * Information about rendering of a frame.
+     * Information about rendering of a frame to a `Surface`.
      */
     struct RenderedFrame {
         /**
-         * Id of the buffer queue containing the rendered buffer.
+         * Id of the `BufferQueue` containing the rendered buffer.
+         *
+         * This value must have been obtained by an earlier call to
+         * IGraphicBufferProducer::getUniqueId().
          */
         uint64_t bufferQueueId;
         /**
          * Id of the slot of the rendered buffer.
+         *
+         * This value must have been obtained by an earlier call to
+         * IGraphicBufferProducer::dequeueBuffer() or
+         * IGraphicBufferProducer::attachBuffer().
          */
         int32_t slotId;
         /**
-         * Timestamp of the rendering (consistent with timestamps in
-         * the associated BufferQueue).
+         * Timestamp the rendering happened.
+         *
+         * The reference point for the timestamp is determined by the
+         * `BufferQueue` that performed the rendering.
          */
         int64_t timestampNs;
     };
 
     /**
-     * Notify the listener that frames are rendered.
+     * Notify the listener that frames have been rendered.
      *
-     * @param renderedFrames List of information about renderings of frames.
+     * @param renderedFrames List of @ref RenderedFrame objects.
      */
     oneway onFramesRendered(vec<RenderedFrame> renderedFrames);
+
+    /**
+     * Identifying information for an input buffer previously queued to the
+     * component via IComponent::queue().
+     */
+    struct InputBuffer {
+        /**
+         * This value comes from `Work::input.ordinal.frameIndex` in a `Work`
+         * object that was previously queued.
+         */
+        uint64_t frameIndex;
+        /**
+         * This value is an index into `Work::input.buffers` (which is an array)
+         * in a `Work` object that was previously queued.
+         */
+        uint32_t arrayIndex;
+    };
+
+    /**
+     * Notify the listener that some input buffers are no longer needed by the
+     * component, and hence can be released or reused by the client.
+     *
+     * Input buffers that are contained in a `Work` object returned by an
+     * earlier onWorkDone() call are assumed released, so they must not appear
+     * in any onInputBuffersReleased() calls. That means
+     * onInputBuffersReleased() must only report input buffers that are released
+     * before the output in the same `Work` item is produced. However, it is
+     * possible for an input buffer to be returned by onWorkDone() after it has
+     * been reported by onInputBuffersReleased().
+     *
+     * @note onWorkDone() and onInputBuffersReleased() both notify the client
+     * that input buffers are no longer needed. However, in order to minimize
+     * IPC calls, onInputBuffersReleased() should be called only when
+     * onWorkDone() cannot be called, e.g., the component needs more input
+     * before an output can be produced.
+     *
+     * @param inputBuffers List of `InputBuffer` objects, identifying input
+     * buffers that are no longer needed by the component.
+     */
+    oneway onInputBuffersReleased(vec<InputBuffer> inputBuffers);
 };
 
diff --git a/media/c2/1.0/IComponentStore.hal b/media/c2/1.0/IComponentStore.hal
index 4bfa170..2aa6a70 100644
--- a/media/c2/1.0/IComponentStore.hal
+++ b/media/c2/1.0/IComponentStore.hal
@@ -23,27 +23,33 @@
 import IConfigurable;
 import IInputSurface;
 
-interface IComponentStore extends IConfigurable {
+/**
+ * Entry point for Codec2 HAL.
+ *
+ * All methods in `IComponentStore` must not block. If a method call cannot be
+ * completed in a timely manner, it must return `TIMED_OUT` in the return
+ * status. The only exceptions are getPoolClientManager() and getConfigurable(),
+ * which must always return immediately.
+ */
+interface IComponentStore {
 
     /**
      * Creates a component by name.
      *
-     * This method must return within 100ms.
-     *
-     * @param name Name of the component to create. This should match one of the
+     * @param name Name of the component to create. This must match one of the
      *     names returned by listComponents().
-     * @param listener The component listener to use for the component.
-     * @param pool The buffer pool client manager of the component listener.
-     *     This must be null if the listener process does not own a buffer pool.
+     * @param listener Callback receiver.
+     * @param pool `IClientManager` object of the BufferPool in the client
+     *     process. This may be null if the client does not own a BufferPool.
      * @return status Status of the call, which may be
-     *   - OK        - The component was created successfully.
-     *   - NOT_FOUND - There is no component with the given name.
-     *   - NO_MEMORY - Not enough memory to create the component.
-     *   - TIMED_OUT - The component could not be created within the time limit.
-     *                 (unexpected)
-     *   - CORRUPTED - Some unknown error prevented the creation of the
-     *                 component. (unexpected)
-     * @return comp The created component if `Status = OK`.
+     *   - `OK`        - The component was created successfully.
+     *   - `NOT_FOUND` - There is no component with the given name.
+     *   - `NO_MEMORY` - Not enough memory to create the component.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return comp The created component if @p status is `OK`.
+     *
+     * @sa IComponentListener.
      */
     createComponent(
             string name,
@@ -57,19 +63,15 @@
     /**
      * Creates a component interface by name.
      *
-     * This method must return within 100ms.
-     *
      * @param name Name of the component interface to create. This should match
      *     one of the names returned by listComponents().
      * @return status Status of the call, which may be
-     *   - OK        - The component interface was created successfully.
-     *   - NOT_FOUND - There is no component interface with the given name.
-     *   - NO_MEMORY - Not enough memory to create the component interface.
-     *   - TIMED_OUT - The component interface could not be created within the
-     *                 time limit. (unexpected)
-     *   - CORRUPTED - Some unknown error prevented the creation of the
-     *                 component interface. (unexpected)
-     * @return compIntf The created component interface if `Status = OK`.
+     *   - `OK`        - The component interface was created successfully.
+     *   - `NOT_FOUND` - There is no component interface with the given name.
+     *   - `NO_MEMORY` - Not enough memory to create the component interface.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return compIntf The created component interface if @p status is `OK`.
      */
     createInterface(
             string name
@@ -83,57 +85,49 @@
      */
     struct ComponentTraits {
         /**
-         * Name of the component.
+         * Name of the component. This must be unique for each component.
+         *
+         * This name is use to identify the component to create in
+         * createComponent() and createComponentInterface().
          */
         string name;
 
         enum Domain : uint32_t {
-            AUDIO,
+            OTHER = 0,
             VIDEO,
-            OTHER = 0xffffffff,
+            AUDIO,
+            IMAGE,
         };
         /**
-         * Component domain. The framework may not recognize `OTHER`.
+         * Component domain.
          */
         Domain domain;
-        /**
-         * If #domain is `OTHER`, #domainOther can be used to provide additional
-         * information. Otherwise, #domainOther is ignored. The framework may
-         * not inspect this value.
-         */
-        uint32_t domainOther;
 
         enum Kind : uint32_t {
+            OTHER = 0,
             DECODER,
             ENCODER,
-            OTHER = 0xffffffff,
         };
         /**
-         * Component kind. The framework may not recognize `OTHER`.
+         * Component kind.
          */
         Kind kind;
-        /**
-         * If #kind is `OTHER`, #kindOther can be used to provide additional
-         * information. Otherwise, #kindOther is ignored. The framework may not
-         * inspect this value.
-         */
-        uint32_t kindOther;
 
         /**
-         * Rank used by MediaCodecList to determine component ordering. Lower
+         * Rank used by `MediaCodecList` to determine component ordering. Lower
          * value means higher priority.
          */
         uint32_t rank;
 
         /**
-         * Media type.
+         * MIME type.
          */
         string mediaType;
 
         /**
          * Aliases for component name for backward compatibility.
          *
-         * \note Multiple components can have the same alias (but not the same
+         * Multiple components can have the same alias (but not the same
          * component name) as long as their media types differ.
          */
         vec<string> aliases;
@@ -142,36 +136,51 @@
     /**
      * Returns the list of components supported by this component store.
      *
-     * This method must return within 500ms.
-     *
-     * @return traits List of component traits for all components supported by this store in no
-     * particular order.
+     * @return status Status of the call, which may be
+     *   - `OK`        - The operation was successful.
+     *   - `NO_MEMORY` - Not enough memory to complete this method.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return traits List of component traits for all components supported by
+     *     this store (in no particular order).
      */
-    listComponents() generates (vec<ComponentTraits> traits);
+    listComponents() generates (
+            Status status,
+            vec<ComponentTraits> traits
+        );
 
     /**
      * Creates a persistent input surface that can be used as an input surface
      * for any IComponent instance
      *
-     * This method must return within 100ms.
-     *
-     * @return surface A persistent input surface
+     * @return status Status of the call, which may be
+     *   - `OK`        - The operation was successful.
+     *   - `NO_MEMORY` - Not enough memory to complete this method.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return surface A persistent input surface. This may be null to indicate
+     *     an error.
      */
-    createInputSurface() generates (IInputSurface surface);
+    createInputSurface() generates (
+            Status status,
+            IInputSurface surface
+        );
 
     /**
-     * Returns a list of StructDescriptor object for a set of requested
-     * structures that this store is aware of.
+     * Returns a list of `StructDescriptor` objects for a set of requested
+     * C2Param structure indices that this store is aware of.
      *
      * This operation must be performed at best effort, e.g. the component
      * store must simply ignore all struct indices that it is not aware of.
      *
-     * @param indices struct indices to return des
+     * @param indices Indices of C2Param structures to describe.
      * @return status Status of the call, which may be
-     *   - OK        - The operation completed successfully.
-     *   - NOT_FOUND - Some indices were not known.
-     *   - NO_MEMORY - Not enough memory to complete this method.
-     * @return structs List of StructDescriptor objects.
+     *   - `OK`        - The operation completed successfully.
+     *   - `NOT_FOUND` - Some indices were not known.
+     *   - `NO_MEMORY` - Not enough memory to complete this method.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return structs List of `StructDescriptor` objects.
      */
     getStructDescriptors(
             vec<ParamIndex> indices
@@ -181,33 +190,35 @@
         );
 
     /**
-     * Returns information required for using BufferPool API in buffer passing.
-     * If the returned pool is not null, the client can call registerSender() to
-     * register its IAccessor instance, hence allowing the client to send
-     * buffers to components hosted by this process.
-     *
-     * @return pool If the component store supports receiving buffers via
-     *     BufferPool API, \p pool must be a valid `IClientManager` instance.
-     *     Otherwise, \p pool must be null.
-     */
-    getPoolClientManager(
-        ) generates (
-            IClientManager pool
-        );
-
-    /**
-     * The store must copy the contents of \p src into \p dst without changing
-     * the format of \p dst.
+     * Copies the contents of @p src into @p dst without changing the format of
+     * @p dst.
      *
      * @param src Source buffer.
      * @param dst Destination buffer.
      * @return status Status of the call, which may be
-     *   - OK        - The copy is successful.
-     *   - CANNOT_DO - \p src and \p dst are not compatible.
-     *   - REFUSED   - No permission to copy.
-     *   - CORRUPTED - The copy cannot be done. (unexpected)
+     *   - `OK`        - The copy is successful.
+     *   - `CANNOT_DO` - @p src and @p dst are not compatible.
+     *   - `REFUSED`   - No permission to copy.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
      */
     copyBuffer(Buffer src, Buffer dst) generates (Status status);
 
+    /**
+     * Returns the `IClientManager` object for the component's BufferPool.
+     *
+     * @return pool If the component store supports receiving buffers via
+     *     BufferPool API, @p pool must be a valid `IClientManager` instance.
+     *     Otherwise, @p pool must be null.
+     */
+    getPoolClientManager() generates (IClientManager pool);
+
+    /**
+     * Returns the @ref IConfigurable instance associated to this component
+     * store.
+     *
+     * @return configurable `IConfigurable` instance. This must not be null.
+     */
+    getConfigurable() generates (IConfigurable configurable);
 };
 
diff --git a/media/c2/1.0/IConfigurable.hal b/media/c2/1.0/IConfigurable.hal
index cd4dd10..31dc4d3 100644
--- a/media/c2/1.0/IConfigurable.hal
+++ b/media/c2/1.0/IConfigurable.hal
@@ -17,43 +17,78 @@
 package android.hardware.media.c2@1.0;
 
 /**
- * Generic configuration interface used by all configurable Codec 2.0
- * components.
+ * Generic configuration interface presented by all configurable Codec2 objects.
  *
- * This interface must be supported in all states of the inheriting
- * object, and must not change the state of the inheriting object.
+ * This interface must be supported in all states of the owning object, and must
+ * not change the state of the owning object.
  */
 interface IConfigurable {
     /**
-     * Returns the name of this object. This must match the name that was
-     * supplied during the creation of the object.
+     * Returns the id of the object. This must be unique among all objects of
+     * the same type hosted by the same store.
      *
-     * @return name Name of this object.
+     * @return id Id of the object.
+     */
+    getId() generates (uint32_t id);
+
+    /**
+     * Returns the name of the object.
+     *
+     * This must match the name that was supplied during the creation of the
+     * object.
+     *
+     * @return name Name of the object.
      */
     getName() generates (string name);
 
     /**
-     * Queries a set of parameters from the object. Querying is performed at
-     * best effort: the object must query all supported parameters and skip
-     * unsupported ones, or parameters that could not be allocated. Any errors
-     * are communicated in the return value.
+     * Queries a set of parameters from the object.
      *
-     * \note Parameter values do not depend on the order of query.
+     * Querying is performed at best effort: the object must query all supported
+     * parameters and skip unsupported ones (which may include parameters that
+     * could not be allocated). Any errors are communicated in the return value.
      *
-     * This method must return within 1ms if \p mayBlock is DONT_BLOCK, and
-     * within 5ms otherwise.
+     * If @p mayBlock is false, this method must not block. All parameter
+     * queries that require blocking must be skipped.
      *
-     * @param indices List of param indices for params to be queried.
+     * If @p mayBlock is true, a query may block, but the whole method call
+     * has to complete in a timely manner, or `status = TIMED_OUT` is returned.
+     *
+     * If @p mayBlock is false, this method must not block. Otherwise, this
+     * method is allowed to block for a certain period of time before completing
+     * the operation. If the operation is not completed in a timely manner,
+     * `status = TIMED_OUT` is returned.
+     *
+     * @note The order of C2Param objects in @p param does not depend on the
+     *     order of C2Param structure indices in @p indices.
+     *
+     * \par For IComponent
+     *
+     * When the object type is @ref IComponent, this method must be supported in
+     * any state except released. This call must not change the state nor the
+     * internal configuration of the component.
+     *
+     * The blocking behavior of this method differs among states:
+     *   - In the stopped state, this must be non-blocking. @p mayBlock is
+     *     ignored. (The method operates as if @p mayBlock was false.)
+     *   - In any of the running states, this method may block momentarily if
+     *     @p mayBlock is true. However, if the call cannot be completed in a
+     *     timely manner, `status = TIMED_OUT` is returned.
+     *
+     * @param indices List of C2Param structure indices to query.
      * @param mayBlock Whether this call may block or not.
      * @return status Status of the call, which may be
-     *   - OK        - All parameters could be queried.
-     *   - BAD_INDEX - All supported parameters could be queried, but some
-     *                 parameters were not supported.
-     *   - NO_MEMORY - Could not allocate memory for a supported parameter.
-     *   - BLOCKING  - Querying some parameters requires blocking.
-     *   - CORRUPTED - Some unknown error prevented the querying of the
-     *                 parameters. (unexpected)
-     * @return params List of params queried corresponding to \p indices.
+     *   - `OK`        - All parameters could be queried.
+     *   - `BAD_INDEX` - All supported parameters could be queried, but some
+     *                   parameters were not supported.
+     *   - `NO_MEMORY` - Could not allocate memory for a supported parameter.
+     *   - `BLOCKING`  - Querying some parameters requires blocking, but
+     *                   @p mayBlock is false.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return params Flattened representation of C2Param objects.
+     *
+     * @sa Params.
      */
     query(
             vec<ParamIndex> indices,
@@ -64,31 +99,60 @@
         );
 
     /**
-     * Sets a set of parameters for the object. Tuning is performed at best
-     * effort: the object must update all supported configuration at best
-     * effort and skip unsupported parameters. Any errors are communicated in
-     * the return value and in \p failures.
+     * Sets a set of parameters for the object.
      *
-     * \note Parameter tuning DOES depend on the order of the tuning parameters.
-     * E.g. some parameter update may allow some subsequent parameter update.
+     * Tuning is performed at best effort: the object must update all supported
+     * configurations at best effort and skip unsupported parameters. Any errors
+     * are communicated in the return value and in @p failures.
      *
-     * This method must return within 1ms if \p mayBlock is false, and within
-     * 5ms otherwise.
+     * A non-strict parameter update with an unsupported value shall cause an
+     * update to the closest supported value. A strict parameter update with an
+     * unsupported value shall be skipped and a failure shall be returned.
+     *
+     * If @p mayBlock is false, this method must not block. An update that
+     * requires blocking shall be skipped and a failure shall be returned.
+     *
+     * If @p mayBlock is true, an update may block, but the whole method call
+     * has to complete in a timely manner, or `status = TIMED_OUT` is returned.
+     *
+     * The final values for all parameters set are propagated back to the caller
+     * in @p params.
+     *
+     * \par For IComponent
+     *
+     * When the object type is @ref IComponent, this method must be supported in
+     * any state except released.
+     *
+     * The blocking behavior of this method differs among states:
+     *   - In the stopped state, this must be non-blocking. @p mayBlock is
+     *     ignored. (The method operates as if @p mayBlock was false.)
+     *   - In any of the running states, this method may block momentarily if
+     *     @p mayBlock is true. However, if the call cannot be completed in a
+     *     timely manner, `status = TIMED_OUT` is returned.
+     *
+     * @note Parameter tuning @e does depend on the order of the tuning
+     * parameters, e.g., some parameter update may enable some subsequent
+     * parameter update.
      *
      * @param inParams Requested parameter updates.
      * @param mayBlock Whether this call may block or not.
      * @return status Status of the call, which may be
-     *   - OK        - All parameters could be updated successfully.
-     *   - BAD_INDEX - All supported parameters could be updated successfully,
-     *                 but some parameters were not supported.
-     *   - NO_MEMORY - Some supported parameters could not be updated
-     *                 successfully because they contained unsupported values.
-     *                 These are returned in \p failures.
-     *   - BLOCKING  - Setting some parameters requires blocking.
-     *   - CORRUPTED - Some unknown error prevented the update of the
-     *                 parameters. (unexpected)
-     * @return failures List of parameter failures.
-     * @return outParams Resulting values for the configured parameters.
+     *   - `OK`        - All parameters could be updated successfully.
+     *   - `BAD_INDEX` - All supported parameters could be updated successfully,
+     *                   but some parameters were not supported.
+     *   - `NO_MEMORY` - Some supported parameters could not be updated
+     *                   successfully because they contained unsupported values.
+     *                   These are returned in @p failures.
+     *   - `BLOCKING`  - Setting some parameters requires blocking, but
+     *                   @p mayBlock is false.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return failures List of update failures.
+     * @return outParams Flattened representation of configured parameters. The
+     *     order of parameters in @p outParams is based on the order of
+     *     requested updates in @p inParams.
+     *
+     * @sa SettingResult.
      */
     config(
             Params inParams,
@@ -103,22 +167,19 @@
     // =========================================================================
 
     /**
-     * Returns a selected range of the set of supported parameters.
+     * Returns a list of supported parameters within a selected range of C2Param
+     * structure indices.
      *
-     * The set of supported parameters are represented in a vector with a
-     * start index of 0, and the selected range are indices into this vector.
-     * Fewer than \p count parameters are returned if the selected range is
-     * not fully/not at all part of the available vector indices.
-     *
-     * This method must return within 1ms.
-     *
-     * @param start start index of selected range
-     * @param count size of the selected
+     * @param start The first index of the selected range.
+     * @param count The length of the selected range.
      * @return status Status of the call, which may be
-     *   - OK        - The operation completed successfully.
-     *   - NO_MEMORY - Not enough memory to complete this method.
-     * @return params Vector containing the selected range of supported
-     *     parameters.
+     *   - `OK`        - The operation completed successfully.
+     *   - `NO_MEMORY` - Not enough memory to complete this method.
+     * @return params List of supported parameters in the selected range. This
+     *     list may have fewer than @p count elements if some indices in the
+     *     range are not supported.
+     *
+     * @sa ParamDescriptor.
      */
     querySupportedParams(
             uint32_t start,
@@ -131,23 +192,42 @@
     /**
      * Retrieves the supported values for the queried fields.
      *
-     * Upon return the object must fill in the supported
-     * values for the fields listed as well as a status for each field.
-     * Object shall process all fields queried even if some queries fail.
+     * The object must process all fields queried even if some queries fail.
      *
-     * This method must return within 1ms if \p mayBlock is false, and within
-     * 5ms otherwise.
+     * If @p mayBlock is false, this method must not block. Otherwise, this
+     * method is allowed to block for a certain period of time before completing
+     * the operation. If the operation cannot be completed in a timely manner,
+     * `status = TIMED_OUT` is returned.
      *
-     * @param inFields Vector of field queries.
+     * \par For IComponent
+     *
+     * When the object type is @ref IComponent, this method must be supported in
+     * any state except released.
+     *
+     * The blocking behavior of this method differs among states:
+     *   - In the stopped state, this must be non-blocking. @p mayBlock is
+     *     ignored. (The method operates as if @p mayBlock was false.)
+     *   - In any of the running states, this method may block momentarily if
+     *     @p mayBlock is true. However, if the call cannot be completed in a
+     *     timely manner, `status = TIMED_OUT` is returned.
+     *
+     * @param inFields List of field queries.
      * @param mayBlock Whether this call may block or not.
      * @return status Status of the call, which may be
-     *   - OK        - The operation completed successfully.
-     *   - BLOCKING  - Querying some parameters requires blocking.
-     *   - NO_MEMORY - Not enough memory to complete this method.
-     *   - BAD_INDEX - At least one field was not recognized as a component
-     *                 field.
-     * @return outFields Vector containing supported values and query result
-     *     for the selected fields.
+     *   - `OK`        - The operation completed successfully.
+     *   - `BLOCKING`  - Querying some parameters requires blocking, but
+     *                   @p mayBlock is false.
+     *   - `NO_MEMORY` - Not enough memory to complete this method.
+     *   - `BAD_INDEX` - At least one field was not recognized as a component
+     *                   field.
+     *   - `BLOCKING`  - Querying some fields requires blocking, but @p mayblock
+     *                   is false.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return outFields List of supported values and results for the
+     *     supplied queries.
+     *
+     * @sa FieldSupportedValuesQuery, FieldSupportedValuesQueryResult.
      */
     querySupportedValues(
             vec<FieldSupportedValuesQuery> inFields,
diff --git a/media/c2/1.0/IInputSink.hal b/media/c2/1.0/IInputSink.hal
new file mode 100644
index 0000000..809c27a
--- /dev/null
+++ b/media/c2/1.0/IInputSink.hal
@@ -0,0 +1,55 @@
+/*
+ * 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.media.c2@1.0;
+
+import android.hardware.graphics.bufferqueue@1.0::IGraphicBufferProducer;
+
+import IConfigurable;
+
+/**
+ * An `IInputSink` is a receiver of work items.
+ *
+ * An @ref IComponent instance can present itself as an `IInputSink` via a thin
+ * wrapper.
+ *
+ * @sa IInputSurface, IComponent.
+ */
+interface IInputSink {
+    /**
+     * Feeds work to the sink.
+     *
+     * @param workBundle `WorkBundle` object containing a list of `Work` objects
+     *     to queue to the component.
+     * @return status Status of the call, which may be
+     *   - `OK`        - Works in @p workBundle were successfully queued.
+     *   - `BAD_INDEX` - Some component id in some `Worklet` is not valid.
+     *   - `CANNOT_DO` - Tunneling has not been set up for this sink, but some
+     *                   `Work` object contains tunneling information.
+     *   - `NO_MEMORY` - Not enough memory to queue @p workBundle.
+     *   - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     */
+    queue(WorkBundle workBundle) generates (Status status);
+
+    /**
+     * Returns the @ref IConfigurable instance associated to this sink.
+     *
+     * @return configurable `IConfigurable` instance. This must not be null.
+     */
+    getConfigurable() generates (IConfigurable configurable);
+};
+
diff --git a/media/c2/1.0/IInputSurface.hal b/media/c2/1.0/IInputSurface.hal
index c083a21..d11ce15 100644
--- a/media/c2/1.0/IInputSurface.hal
+++ b/media/c2/1.0/IInputSurface.hal
@@ -19,43 +19,57 @@
 import android.hardware.graphics.bufferqueue@1.0::IGraphicBufferProducer;
 
 import IConfigurable;
-import IComponent;
+import IInputSink;
 import IInputSurfaceConnection;
 
 /**
- * Input surface that can be configured for the IComponent.
+ * Input surface for a Codec2 component.
+ *
+ * An <em>input surface</em> is an instance of `IInputSurface`, which may be
+ * created by calling IComponentStore::createInputSurface(). Once created, the
+ * client may
+ *   1. write data to it via the `IGraphicBufferProducer` interface; and
+ *   2. use it as input to a Codec2 encoder.
+ *
+ * @sa IInputSurfaceConnection, IComponentStore::createInputSurface(),
+ *     IComponent::connectToInputSurface().
  */
-interface IInputSurface extends IGraphicBufferProducer {
+interface IInputSurface {
+    /**
+     * Returns the producer interface into the internal buffer queue.
+     *
+     * @return producer `IGraphicBufferProducer` instance. This must not be
+     * null.
+     */
+    getGraphicBufferProducer() generates (IGraphicBufferProducer producer);
 
     /**
-     * Connects this input surface to a component.
+     * Returns the @ref IConfigurable instance associated to this input surface.
      *
-     * This call must return within 100 ms.
-     *
-     * @param component The component to connect to. This must have type
-     *     IComponent.
-     * @return status Status of the call, which may be
-     *   - OK        - The operation succeeded.
-     *   - BAD_STATE - The component is in running state.
-     *   - DUPLICATE - The surface is already connected to a component.
-     *   - NO_MEMORY - Could not allocate memory to connect to the component.
-     *   - CORRUPTED - Some unknown error prevented the connection. (unexpected)
-     * @return connection Connection object that is used to disconnect
-     *     from the component.
+     * @return configurable `IConfigurable` instance. This must not be null.
      */
-    connectToComponent(
-            IComponent component
+    getConfigurable() generates (IConfigurable configurable);
+
+    /**
+     * Connects the input surface to an input sink.
+     *
+     * This function is generally called from inside the implementation of
+     * IComponent::connectToInputSurface(), where @p sink is a thin wrapper of
+     * the component that consumes buffers from this surface.
+     *
+     * @param sink Input sink. See `IInputSink` for more information.
+     * @return status Status of the call, which may be
+     *   - `OK`        - Configuration successful.
+     *   - `BAD_VALUE` - @p sink is invalid.
+     *   - `CORRUPTED` - Some unknown error occurred.
+     * @return connection `IInputSurfaceConnection` object. This must not be
+     *     null if @p status is `OK`.
+     */
+    connect(
+            IInputSink sink
         ) generates (
             Status status,
             IInputSurfaceConnection connection
         );
-
-    /**
-     * Returns the Codec 2.0 configuration object for this surface.
-     *
-     * @return configurable The configuration object for this surface.
-     */
-    getConfigurable() generates (IConfigurable configurable);
-
 };
 
diff --git a/media/c2/1.0/IInputSurfaceConnection.hal b/media/c2/1.0/IInputSurfaceConnection.hal
index 500091d..035b115 100644
--- a/media/c2/1.0/IInputSurfaceConnection.hal
+++ b/media/c2/1.0/IInputSurfaceConnection.hal
@@ -16,20 +16,33 @@
 
 package android.hardware.media.c2@1.0;
 
-interface IInputSurfaceConnection {
+import IConfigurable;
 
+/**
+ * Connection between a component and an input surface.
+ *
+ * An instance of `IInputSurfaceConnection` contains an `IConfigurable`
+ * interface for querying and configuring properties of the connection.
+ */
+interface IInputSurfaceConnection {
     /**
-     * Disconnects this input surface from the component.
-     *
-     * This call must return within 100 ms.
+     * Destroys the connection between an input surface and a component.
      *
      * @return status Status of the call, which may be
-     *   - OK        - The operation succeeded.
-     *   - BAD_STATE - The component is not in running state.
-     *   - NOT_FOUND - The surface is not connected to a component.
-     *   - CORRUPTED - Some unknown error prevented the connection. (unexpected)
+     *   - `OK`        - The disconnection succeeded.
+     *   - `BAD_STATE` - The component is not in running state.
+     *   - `NOT_FOUND` - The surface is not connected to a component.
+     *   - `CORRUPTED` - Some unknown error occurred.
      */
     disconnect() generates (Status status);
 
+    /**
+     * Returns the @ref IConfigurable instance associated to this connection.
+     *
+     * This can be used to customize the connection.
+     *
+     * @return configurable `IConfigurable` instance. This must not be null.
+     */
+    getConfigurable() generates (IConfigurable configurable);
 };
 
diff --git a/media/c2/1.0/types.hal b/media/c2/1.0/types.hal
index 252d781..ec422b1 100644
--- a/media/c2/1.0/types.hal
+++ b/media/c2/1.0/types.hal
@@ -18,220 +18,278 @@
 
 import android.hardware.media.bufferpool@2.0::BufferStatusMessage;
 
+/**
+ * Common return values for Codec2 operations.
+ */
 enum Status : int32_t {
-    /** operation completed successfully */
+    /** Operation completed successfully. */
     OK        = 0,
 
     // bad input
 
-    /** argument has invalid value (user error) */
+    /** Argument has invalid value (user error). */
     BAD_VALUE = -22,
-    /** argument uses invalid index (user error) */
+    /** Argument uses invalid index (user error). */
     BAD_INDEX = -75,
-    /** argument/index is valid but not possible */
+    /** Argument/Index is valid but not possible. */
     CANNOT_DO = -2147483646,
 
     // bad sequencing of events
 
-    /** object already exists */
+    /** Object already exists. */
     DUPLICATE = -17,
-    /** object not found */
+    /** Object not found. */
     NOT_FOUND = -2,
-    /** operation is not permitted in the current state */
+    /** Operation is not permitted in the current state. */
     BAD_STATE = -38,
-    /** operation would block but blocking is not permitted */
+    /** Operation would block but blocking is not permitted. */
     BLOCKING  = -9930,
 
     // bad environment
 
-    /** not enough memory to complete operation */
+    /** Not enough memory to complete operation. */
     NO_MEMORY = -12,
-    /** missing permission to complete operation */
+    /** Missing permission to complete operation. */
     REFUSED   = -1,
 
-    /** operation did not complete within timeout */
+    /** Operation did not complete within timeout. */
     TIMED_OUT = -110,
 
     // missing functionality
 
-    /** operation is not implemented/supported (optional only) */
+    /** Operation is not implemented/supported (optional only). */
     OMITTED   = -74,
 
     // unknown fatal
 
-    /** some unexpected error prevented the operation */
+    /** Some unexpected error prevented the operation. */
     CORRUPTED = -2147483648,
 
     // uninitialized
 
-    /** status has not been initialized */
+    /** Status has not been initialized. */
     NO_INIT   = -19,
 };
 
 /**
- * Codec 2.0 parameter index
+ * C2Param structure index.
+ *
+ * This is a number that is unique for each C2Param structure type.
+ *
+ * @sa Codec 2.0 standard.
  */
 typedef uint32_t ParamIndex;
 
 /**
- * Codec 2.0 parameter structure
+ * Flattened representation of C2Param objects.
  *
- * The description of a Params is provided by supplying a ParamIndex to
- * IComponentStore::getStructDescriptors().
+ * The `Params` type is an array of bytes made up by concatenating a list of
+ * C2Param objects. The start index (offset into @ref Params) of each C2Param
+ * object in the list is divisible by 8. Up to 7 padding bytes may be added
+ * after each C2Param object to achieve this 64-bit alignment.
+ *
+ * Each C2Param object has the following layout:
+ * - 4 bytes: C2Param structure index (of type @ref ParamIndex) identifying the
+ *   type of the C2Param object.
+ * - 4 bytes: size of the C2Param object (unsigned 4-byte integer).
+ * - (size - 8) bytes: data of the C2Param object.
+ *
+ * In order to interpret each C2Param object correctly, its structure must be
+ * described by IComponentStore::getStructDescriptors().
+ *
+ * @note Please refer to the Codec 2.0 standard for the list of standard
+ * parameter structures.
+ *
+ * @sa Codec 2.0 standard.
  */
 typedef vec<uint8_t> Params;
 
 /**
- * Struct uniquely specifying a field in an arbitrary parameter structure.
+ * Identifying information of a field relative to a known C2Param structure.
+ *
+ * Within a given C2Param structure, each field is uniquely identified by @ref
+ * FieldId.
  */
 struct FieldId {
-    /** Offset of the field in bytes */
+    /** Offset of the field in bytes. */
     uint32_t offset;
-    /** Size of the field in bytes */
+    /** Size of the field in bytes. */
     uint32_t size;
 };
 
 /**
- * Struct representing a location of a field in a parameter with a given index.
+ * Reference to a field in a C2Param structure.
  */
 struct ParamField {
-    /** Index of the parameter */
+    /** Index of the C2Param structure. */
     ParamIndex index;
-    /** Field identifier */
+    /** Identifier of the field inside the C2Param structure. */
     FieldId fieldId;
 };
 
 /**
- * Struct describing basic properties of a parameter with a given index.
+ * Usage description of a C2Param structure.
+ *
+ * @ref ParamDescriptor is returned by IConfigurable::querySupportedParams().
  */
 struct ParamDescriptor {
-    /** Parameter index */
+    /**
+     * Index of the C2Param structure being described.
+     */
     ParamIndex index;
 
     enum Attrib : uint32_t {
         /**
-         * Parameter is required to be specified.
+         * The parameter is required to be specified.
          */
         REQUIRED   = 1u << 0,
         /**
-         * Parameter retains its value.
+         * The parameter retains its value.
          */
         PERSISTENT = 1u << 1,
         /**
-         * Parameter is strict.
+         * The parameter is strict.
          */
         STRICT     = 1u << 2,
         /**
-         * Parameter is publicly read-only.
+         * The parameter is publicly read-only.
          */
         READ_ONLY  = 1u << 3,
         /**
-         * Parameter must not be visible to clients.
+         * The parameter must not be visible to clients.
          */
         HIDDEN     = 1u << 4,
         /**
-         * Parameter must not be used by framework (other than testing).
+         * The parameter must not be used by framework (other than testing).
          */
         INTERNAL   = 1u << 5,
         /**
-         * Parameter is publicly constant (hence read-only).
+         * The parameter is publicly constant (hence read-only).
          */
         CONST      = 1u << 6,
     };
-    /** Parameter attributes */
     bitfield<Attrib> attrib;
 
-    /** Parameter name */
+    /**
+     * Name of the structure. This must be unique for each structure.
+     */
     string name;
 
-    /** index of other parameters that this parameter depends on */
+    /**
+     * Indices of other C2Param structures that this C2Param structure depends
+     * on.
+     */
     vec<ParamIndex> dependencies;
 };
 
-// Generic way to describe supported numeric values for Codec 2.0 interfaces.
+// Generic way to describe supported numeric values for Codec2 interfaces.
 
 /**
- * An untyped value that can fit on 64 bits - the type of which is communicated
- * via a separate channel (FieldType).
+ * An untyped value that can fit in 64 bits, the type of which is communicated
+ * via a separate channel (@ref FieldSupportedValues.type).
  */
 typedef uint64_t PrimitiveValue;
 
 /*
- * Generic supported values for a field.
+ * Description of supported values for a field.
  *
- * This can be either a range or a set of values. The range can be linear or
- * geometric with clear minimum and maximum values, and can have an optional
- * step size or geometric ratio. Values can optionally represent flags.
+ * This can be a continuous range or a discrete set of values.
  */
 struct FieldSupportedValues {
+    /**
+     * Used if #type is `RANGE`.
+     *
+     * If the `step` member is 0, and `num` and `denom` are both 1, the `Range`
+     * structure represents a closed interval bounded by `min` and `max`.
+     *
+     * Otherwise, the #Range structure represents a finite sequence of numbers
+     * produced from the following recurrence relation:
+     *
+     * @code
+     * v[0] = min
+     * v[i] = v[i - 1] * num / denom + step ; i >= 1
+     * @endcode
+     *
+     * Both the ratio `num / denom` and the value `step` must be positive. The
+     * last number in the sequence described by this #Range structure is the
+     * largest number in the sequence that is smaller than or equal to `max`.
+     *
+     * @note
+     * The division in the formula may truncate the result if the data type of
+     * these values is an integral type.
+     */
     struct Range {
+        /**
+         * Lower end of the range (inclusive).
+         */
         PrimitiveValue min;
+        /**
+         * Upper end of the range (inclusive).
+         */
         PrimitiveValue max;
+        /**
+         * The non-homogeneous term in the recurrence relation.
+         */
         PrimitiveValue step;
+        /**
+         * The numerator of the scale coefficient in the recurrence relation.
+         */
         PrimitiveValue num;
+        /**
+         * The denominator of the scale coefficient in the recurrence relation.
+         */
         PrimitiveValue denom;
     };
 
     enum Type : int32_t {
         /** No supported values */
-        EMPTY,
-        /** Numeric range that can be continuous or discrete */
+        EMPTY = 0,
+        /** Numeric range, described in a #Range structure */
         RANGE,
         /** List of values */
         VALUES,
         /** List of flags that can be OR-ed */
         FLAGS,
-        /** Other representations */
-        OTHER = 0xffffffff,
     };
     /**
-     * Type of the supported values. The framework may not recognize `OTHER`.
+     * Type of the supported values.
      */
     Type type;
-    /**
-     * Codec2.0 type code of the supported values.
-     *   * If #type is `OTHER`, #typeOther can be used to give more information.
-     *     In this case, the interpretation of this structure is
-     *     implementation-defined.
-     *   * For all other values of #type, #typeOther is not used.
-     * The framework may not inspect this value.
-     */
-    int32_t typeOther;
-
-    /*
-     * If #type = EMPTY, #range and #value are unused.
-     */
 
     /**
-     * If #type = RANGE, #range will specify the range of possible values.
+     * When #type is #Type.RANGE, #range shall specify the range of possible
+     * values.
      *
-     * The intended type of members of #range will be clear in the context where
-     * FieldSupportedValues is used.
+     * The intended type of members of #range shall be clear in the context
+     * where `FieldSupportedValues` is used.
      */
     Range range;
 
     /**
-     * If #type is `VALUES` or `FLAGS`, #value will list supported values.
+     * When #type is #Type.VALUES or #Type.FLAGS, #value shall list supported
+     * values/flags.
      *
-     * The intended type of components of #value will be clear in the context
-     * where FieldSupportedValues is used.
+     * The intended type of components of #value shall be clear in the context
+     * where `FieldSupportedValues` is used.
      */
     vec<PrimitiveValue> values;
 };
 
 /**
- * Supported values for a specific field.
+ * Supported values for a 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 ParamFieldValues {
-    /** the field or parameter */
+    /**
+     * Reference to a field or a C2Param structure.
+     */
     ParamField paramOrField;
 
     /**
-     * optional supported values for the field if paramOrField specifies an
+     * 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
@@ -241,18 +299,18 @@
 };
 
 /**
- * Field descriptor.
+ * Description of a field inside a C2Param structure.
  */
 struct FieldDescriptor {
 
-    /** Field id */
+    /** Location of the field in the C2Param structure */
     FieldId fieldId;
 
     /**
-     * Possible types of a field.
+     * Possible types of the field.
      */
     enum Type : uint32_t {
-        NO_INIT,
+        NO_INIT = 0,
         INT32,
         UINT32,
         CNTR32,
@@ -261,186 +319,227 @@
         CNTR64,
         FLOAT,
         /**
-         * Fixed-size string (POD)
+         * Fixed-size string (POD).
          */
         STRING = 0x100,
         /**
-         * blobs have no sub-elements and can be thought of as byte arrays.
-         * However, bytes cannot be individually addressed by clients.
+         * A blob has no sub-elements and can be thought of as an array of
+         * bytes. However, bytes cannot be individually addressed by clients.
          */
         BLOB,
         /**
-         * Structs. Marked with this flag in addition to their coreIndex.
+         * The field is a structure that may contain other fields.
          */
-        STRUCT_FLAG = 0x20000,
+        STRUCT = 0x20000,
     };
     /**
      * Type of the field.
      */
     bitfield<Type> type;
 
-    /** Extent of the field */
-    uint32_t length;
-    /*
-     * Note: the last member of a param struct can be of arbitrary length (e.g.
-     * if it is T[] array, which extends to the last byte of the parameter.)
-     * This is marked with extent 0.
+    /**
+     * If #type is #Type.STRUCT, #structIndex is the C2Param structure index;
+     * otherwise, #structIndex is not used.
      */
+    ParamIndex structIndex;
 
-    /** Name of the field */
+    /**
+     * Extent of the field.
+     * - For a non-array field, #extent is 1.
+     * - For a fixed-length array field, #extent is the length. An array field
+     *   of length 1 is indistinguishable from a non-array field.
+     * - For a variable-length array field, #extent is 0. This can only occur as
+     *   the last member of a C2Param structure.
+     */
+    uint32_t extent;
+
+    /**
+     * Name of the field. This must be unique for each field in the same
+     * structure.
+     */
     string name;
-    /** Named value type */
+
+    /**
+     * Named value type. This is used for defining an enum value for a numeric
+     * type.
+     */
     struct NamedValue {
+        /**
+         * Name of the enum value. This must be unique for each enum value in
+         * the same field.
+         */
         string name;
+        /**
+         * Underlying value of the enum value. Multiple enum names may have the
+         * same underlying value.
+         */
         PrimitiveValue value;
     };
-    /** Named values for the field */
+    /**
+     * List of enum values. This is not used when #type is not one of the
+     * numeric types.
+     */
     vec<NamedValue> namedValues;
 };
 
 /**
- * Struct descriptor.
+ * Description of a C2Param structure. It consists of an index and a list of
+ * `FieldDescriptor`s.
  */
 struct StructDescriptor {
-    /** Struct type */
+    /**
+     * Index of the structure.
+     */
     ParamIndex type;
-    /** Field descriptors for each field */
+    /**
+     * List of fields in the structure.
+     *
+     * Fields are ordered by their offsets. A field that is a structure is
+     * ordered before its members.
+     */
     vec<FieldDescriptor> fields;
 };
 
 /**
- * Information describing the reason a parameter settings may fail, or
- * may be overriden.
+ * Information describing the reason the parameter settings may fail, or may be
+ * overridden.
  */
 struct SettingResult {
-    /** Failure code (of Codec 2.0 SettingResult failure type) */
+    /** Failure code */
     enum Failure : uint32_t {
-        /** Parameter is read-only and cannot be set. */
-        READ_ONLY,
-        /** Parameter mismatches input data. */
-        MISMATCH,
-        /** Parameter does not accept value. */
-        BAD_VALUE,
         /** Parameter is not supported. */
         BAD_TYPE,
         /** Parameter is not supported on the specific port. */
         BAD_PORT,
         /** Parameter is not supported on the specific stream. */
         BAD_INDEX,
-        /** Parameter is in conflict with an/other setting(s). */
+        /** Parameter is read-only and cannot be set. */
+        READ_ONLY,
+        /** Parameter mismatches input data. */
+        MISMATCH,
+        /** Strict parameter does not accept value for the field at all. */
+        BAD_VALUE,
+        /**
+         * Strict parameter field value is in conflict with an/other
+         * setting(s).
+         */
         CONFLICT,
         /**
-         * Parameter is out of range due to other settings. (This failure mode
-         * can only be used for strict parameters.)
+         * 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 suggestion to the client as to how to enable
-         * the requested parameter value. */
-        INFO_CONFLICT,
-        /**
-         * This failure mode is reported when all the above failure modes do not
-         * apply.
+         * 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.
          */
-        OTHER = 0xffffffff,
+        INFO_CONFLICT,
     };
-    /**
-     * The failure type. The framework might not recognize `OTHER`.
-     */
     Failure failure;
-    /**
-     * The failure code.
-     *   * If #failure is `OTHER`, #failureOther can be used to give more
-     *     information.
-     *   * For all other values of #failure, #failureOther is not used.
-     * The framework may not inspect this value.
-     */
-    uint32_t failureOther;
 
     /**
-     * Failing (or corrected) field. Currently supported values for the field.
-     * This is set if different from the globally supported values (e.g. due to
-     * restrictions by another param or input data)
+     * Failing (or corrected) field or parameter and 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 parameter
+     * or input data).
      */
     ParamFieldValues field;
 
     /**
-     * Conflicting parameters or fields with
-     * (optional) suggested values for any conflicting fields to avoid the conflict.
+     * Conflicting parameters or fields with (optional) suggested values for any
+     * conflicting fields to avoid the conflict. Values must only be set for
+     * `CONFLICT`, `UNSUPPORTED` or `INFO_CONFLICT` failure code.
      */
     vec<ParamFieldValues> conflicts;
 };
 
 /**
- * Data structure for ordering Work objects. Each member is used for comparing
- * urgency in the same fashion: a smaller value indicates that the associated
- * Work object is more urgent.
+ * Ordering information of @ref FrameData objects. Each member is used for
+ * comparing urgency: a smaller difference from a reference value indicates that
+ * the associated Work object is more urgent. The reference value for each
+ * member is initialized the first time it is communicated between the client
+ * and the codec, and it may be updated to later values that are communicated.
+ *
+ * Each member of `WorkOrdinal` is stored as an unsigned integer, but the actual
+ * order it represents is derived by subtracting the reference value, then
+ * interpreting the result as a signed number with the same storage size (using
+ * two's complement).
+ *
+ * @note `WorkOrdinal` is the HIDL counterpart of `C2WorkOrdinalStruct` in the
+ * Codec 2.0 standard.
  */
 struct WorkOrdinal {
     /**
-     * Timestamp in microseconds - can wrap around.
+     * Timestamp in microseconds.
      */
     uint64_t timestampUs;
     /**
-     * Frame index - can wrap around.
+     * Frame index.
      */
     uint64_t frameIndex;
     /**
-     * Component specific frame ordinal - can wrap around.
+     * Component specific frame ordinal.
      */
     uint64_t customOrdinal;
 };
 
 /**
- * A structure that holds information of a Block. There are two types of Blocks:
- * NATIVE and POOLED. Each type has its own way of identifying blocks.
+ * Storage type for `BaseBlock`.
+ *
+ * A `BaseBlock` is a representation of a codec memory block. Coded data,
+ * decoded data, codec-specific data, and other codec-related data are all sent
+ * in the form of BaseBlocks.
  */
-struct BaseBlock {
-    enum Type : int32_t {
-        NATIVE,
-        POOLED,
-    };
+safe_union BaseBlock {
     /**
-     * There are two types of blocks: NATIVE and POOLED.
-     */
-    Type type;
-
-    /**
-     * A "NATIVE" block is represented by a native handle.
+     * #nativeBlock is the opaque representation of a buffer.
      */
     handle nativeBlock;
-
-    /*
-     * A "POOLED" block is represented by `BufferStatusMessage`.
+    /**
+     * #pooledBlock is a reference to a buffer handled by a BufferPool.
      */
     BufferStatusMessage pooledBlock;
 };
 
 /**
- * A Block in transfer consists of an index into an array of BaseBlock plus some
- * extra information. One BaseBlock may occur in multiple blocks in one
- * `WorkBundle`.
+ * Reference to a @ref BaseBlock within a @ref WorkBundle.
+ *
+ * `Block` contains additional attributes that `BaseBlock` does not. These
+ * attributes may differ among `Block` objects that refer to the same
+ * `BaseBlock` in the same `WorkBundle`.
  */
 struct Block {
     /**
-     * Identity of the BaseBlock within a WorkBundle. This is an index into the
-     * `baseBlocks` array of a `WorkBundle` object.
+     * Identity of a `BaseBlock` within a `WorkBundle`. This is an index into
+     * #WorkBundle.baseBlocks.
      */
     uint32_t index;
     /**
-     * Metadata associated with the block.
+     * Metadata associated with this `Block`.
      */
     Params meta;
     /**
-     * Fence for synchronizing block access.
+     * Fence for synchronizing `Block` access.
      */
     handle fence;
 };
 
 /**
- * Type of buffers processed by a component.
+ * A codec buffer, which is a collection of @ref Block objects and metadata.
+ *
+ * This is a part of @ref FrameData.
  */
 struct Buffer {
     /**
@@ -454,23 +553,37 @@
 };
 
 /**
- * An extension of Buffer that also contains an index.
+ * An extension of @ref Buffer that also contains a C2Param structure index.
+ *
+ * This is a part of @ref FrameData.
  */
 struct InfoBuffer {
+    /**
+     * A C2Param structure index.
+     */
     ParamIndex index;
+    /**
+     * Associated @ref Buffer object.
+     */
     Buffer buffer;
 };
 
 /**
- * This structure represents a frame with its metadata. A frame consists of an
- * ordered set of buffers, configuration changes, and info buffers along with
- * some non-configuration metadata.
+ * Data for an input frame or an output frame.
+ *
+ * This structure represents a @e frame with its metadata. A @e frame consists
+ * of an ordered set of buffers, configuration changes, and info buffers along
+ * with some non-configuration metadata.
+ *
+ * @note `FrameData` is the HIDL counterpart of `C2FrameData` in the Codec 2.0
+ * standard.
  */
 struct FrameData {
     enum Flags : uint32_t {
         /**
-         * For input frames: no output frame will be generated when processing
+         * For input frames: no output frame shall be generated when processing
          * this frame, but metadata must still be processed.
+         *
          * For output frames: this frame must be discarded but metadata is still
          * valid.
          */
@@ -482,92 +595,178 @@
         END_OF_STREAM = (1 << 1),
         /**
          * This frame must be discarded with its metadata.
-         * This flag is only set by components - e.g. as a response to the flush
+         *
+         * This flag is only set by components, e.g. as a response to the flush
          * command.
          */
         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 frames are 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 the codec-specific
+         * @deprecated Pass codec configuration with the codec-specific
          * configuration info together with the access unit.
          */
         CODEC_CONFIG  = (1u << 31),
     };
 
     /**
-     * Frame flags.
+     * Frame flags, as described in #Flags.
      */
     bitfield<Flags> flags;
 
     /**
-     * Ordinal of the frame.
+     * @ref WorkOrdinal of the frame.
      */
     WorkOrdinal ordinal;
 
     /**
-     * Frame buffers.
+     * List of frame buffers.
      */
     vec<Buffer> buffers;
 
     /**
-     * Params determining a configuration update.
+     * List of configuration updates.
      */
     Params configUpdate;
 
     /**
-     * Info buffers.
+     * List of info buffers.
      */
     vec<InfoBuffer> infoBuffers;
 };
 
 /**
- * Struct for
+ * In/out structure containing some instructions for and results from output
+ * processing.
+ *
+ * This is a part of @ref Work. One `Worklet` corresponds to one output
+ * @ref FrameData. The client must construct an original `Worklet` object inside
+ * a @ref Work object for each expected output before calling
+ * IComponent::queue().
  */
 struct Worklet {
     /**
-     * List of Params describing tunings.
+     * Component id. (Input)
+     *
+     * This is used only when tunneling is enabled.
+     *
+     * When used, this must match the return value from IConfigurable::getId().
      */
-    vec<Params> tunings;
+    uint32_t componentId;
 
     /**
-     * List of failures.
+     * List of C2Param objects describing tunings to be applied before
+     * processing this `Worklet`. (Input)
+     */
+    Params tunings;
+
+    /**
+     * List of failures. (Output)
      */
     vec<SettingResult> failures;
 
     /**
-     * Output frame data.
+     * Output frame data. (Output)
      */
     FrameData output;
-
-    /* Note: Component id is not necessary as tunneling is not supported. */
 };
 
 /**
- * This structure holds information about a single work item. It must be passed
- * by the client to the component.
+ * A collection of input data to and output data from the component.
+ *
+ * A `Work` object holds information about a single work item. It is created by
+ * the client and passed to the component via IComponent::queue(). The component
+ * has two ways of returning a `Work` object to the client:
+ *   1. If the queued `Work` object has been successfully processed,
+ *      IComponentListener::onWorkDone() shall be called to notify the listener,
+ *      and the output shall be included in the returned `Work` object.
+ *   2. If the client calls IComponent::flush(), a `Work` object that has not
+ *      been processed shall be returned.
+ *
+ * `Work` is a part of @ref WorkBundle.
  */
 struct Work {
     /**
-     * FrameData for the input. Indices of Blocks inside #input refer to
-     * BaseBlocks in the member `blocks` of the containing `WorkBundle`.
+     * Additional work chain info not part of this work.
+     */
+    Params chainInfo;
+
+    /**
+     * @ref FrameData for the input.
      */
     FrameData input;
+
     /**
-     * Worklet. Indices of Blocks inside `worklet.output` refer to
-     * BaseBlocks in the member `blocks` of the containing `WorkBundle`.
+     * The chain of `Worklet`s.
+     *
+     * The length of #worklets is 1 when tunneling is not enabled.
+     *
+     * If #worklets has more than a single element, the tunnels between
+     * successive components of the work chain must have been successfully
+     * pre-registered at the time that the `Work` is submitted. Allocating the
+     * output buffers in the `Worklet`s is the responsibility of each component
+     * in the chain.
+     *
+     * Upon `Work` submission, #worklets must be an appropriately sized vector
+     * containing `Worklet`s with @ref Worklet.hasOutput set to `false`. After a
+     * successful processing, all but the final `Worklet` in the returned
+     * #worklets must have @ref Worklet.hasOutput set to `false`.
      */
-    Worklet worklet;
+    vec<Worklet> worklets;
+
     /**
-     * Whether the worklet was processed or not.
+     * The number of `Worklet`s successfully processed in this chain.
+     *
+     * This must be initialized to 0 by the client when the `Work` is submitted,
+     * and it must contain the number of `Worklet`s that were successfully
+     * processed when the `Work` is returned to the client.
+     *
+     * #workletsProcessed cannot exceed the length of #worklets. If
+     * #workletsProcessed is smaller than the length of #worklets, #result
+     * cannot be `OK`.
      */
-    bool workletProcessed;
+    uint32_t workletsProcessed;
+
+    /**
+     * The final outcome of the `Work` (corresponding to #workletsProcessed).
+     *
+     * The value of @ref Status.OK implies that all `Worklet`s have been
+     * successfully processed.
+     */
     Status result;
 };
 
 /**
- * This structure holds a list of Work objects and a list of BaseBlocks.
+ * List of `Work` objects.
+ *
+ * `WorkBundle` is used in IComponent::queue(), IComponent::flush() and
+ * IComponentListener::onWorkDone(). A `WorkBundle` object consists of a list of
+ * `Work` objects and a list of `BaseBlock` objects. Bundling multiple `Work`
+ * objects together provides two benefits:
+ *   1. Batching of `Work` objects can reduce the number of IPC calls.
+ *   2. If multiple `Work` objects contain `Block`s that refer to the same
+ *      `BaseBlock`, the number of `BaseBlock`s that is sent between processes
+ *      is also reduced.
+ *
+ * @note `WorkBundle` is the HIDL counterpart of the vector of `C2Work` in the
+ * Codec 2.0 standard. The presence of #baseBlocks helps with minimizing the
+ * data transferred over an IPC.
  */
 struct WorkBundle {
     /**
@@ -581,27 +780,48 @@
 };
 
 /**
- * This structure describes a query for supported values of a field. This is
- * used as input to IConfigurable::queryFieldSupportedValues().
+ * Query information for supported values of a field. This is used as input to
+ * IConfigurable::querySupportedValues().
  */
 struct FieldSupportedValuesQuery {
+    /**
+     * Identity of the field to query.
+     */
+    ParamField field;
+
     enum Type : uint32_t {
-        /** Query all possible values regardless of other settings */
+        /** Query all possible values regardless of other settings. */
         POSSIBLE,
-        /** Query currently possible values given dependent settings */
+        /** Query currently possible values given dependent settings. */
         CURRENT,
     };
-
-    ParamField field;
+    /**
+     * Type of the query. See #Type for more information.
+     */
     Type type;
 };
 
 /**
  * This structure is used to hold the result from
- * IConfigurable::queryFieldSupportedValues().
+ * IConfigurable::querySupportedValues().
  */
 struct FieldSupportedValuesQueryResult {
+    /**
+     * Result of the query. Possible values are
+     * - `OK`: The query was successful.
+     * - `BAD_STATE`: The query was requested when the `IConfigurable` instance
+     *   was in a bad state.
+     * - `BAD_INDEX`: The requested field was not recognized.
+     * - `TIMED_OUT`: The query could not be completed in a timely manner.
+     * - `BLOCKING`: The query must block, but the parameter `mayBlock` in the
+     *   call to `querySupportedValues()` was `false`.
+     * - `CORRUPTED`: Some unknown error occurred.
+     */
     Status status;
+
+    /**
+     * Supported values. This is meaningful only when #status is `OK`.
+     */
     FieldSupportedValues values;
 };
 
diff --git a/neuralnetworks/1.0/vts/functional/Android.bp b/neuralnetworks/1.0/vts/functional/Android.bp
index 52d6328..9057695 100644
--- a/neuralnetworks/1.0/vts/functional/Android.bp
+++ b/neuralnetworks/1.0/vts/functional/Android.bp
@@ -80,6 +80,7 @@
             },
         },
     },
+    test_suites: ["general-tests"],
 }
 
 cc_test {
@@ -89,5 +90,16 @@
         "BasicTests.cpp",
         "GeneratedTests.cpp",
     ],
-    test_suites: ["general-tests"],
+}
+
+cc_test {
+    name: "PresubmitHalNeuralnetworksV1_0TargetTest",
+    defaults: ["VtsHalNeuralNetworksTargetTestDefaults"],
+    srcs: [
+        "BasicTests.cpp",
+        "GeneratedTests.cpp",
+    ],
+    cflags: [
+        "-DPRESUBMIT_NOT_VTS",
+    ],
 }
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
index 2e13854..7eea7fc 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
@@ -72,7 +72,8 @@
     copy_back_(&dst->bool8Operands, ra, src);
     copy_back_(&dst->quant8ChannelOperands, ra, src);
     copy_back_(&dst->quant16AsymmOperands, ra, src);
-    static_assert(8 == MixedTyped::kNumTypes,
+    copy_back_(&dst->quant8SymmOperands, ra, src);
+    static_assert(9 == MixedTyped::kNumTypes,
                   "Number of types in MixedTyped changed, but copy_back function wasn't updated");
 }
 
@@ -548,7 +549,7 @@
         std::cout << "[          ]   Early termination of test because vendor service cannot "
                      "prepare model that it does not support."
                   << std::endl;
-        GTEST_SKIP();
+        return;
     }
     EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
     ASSERT_NE(nullptr, preparedModel->get());
@@ -561,6 +562,9 @@
     V1_2::Model model = create_model();
     sp<V1_2::IPreparedModel> preparedModel = nullptr;
     PrepareModel(device, model, &preparedModel);
+    if (preparedModel == nullptr) {
+        GTEST_SKIP();
+    }
     EvaluatePreparedModel(preparedModel, is_ignored, examples,
                           model.relaxComputationFloat32toFloat16, testDynamicOutputShape);
 }
diff --git a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp
index 1ff3b66..8883057 100644
--- a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.cpp
@@ -49,7 +49,17 @@
 void NeuralnetworksHidlTest::SetUp() {
     ::testing::VtsHalHidlTargetTestBase::SetUp();
     device = ::testing::VtsHalHidlTargetTestBase::getService<IDevice>(
-        NeuralnetworksHidlEnvironment::getInstance());
+            NeuralnetworksHidlEnvironment::getInstance());
+
+#ifdef PRESUBMIT_NOT_VTS
+    const std::string name =
+            NeuralnetworksHidlEnvironment::getInstance()->getServiceName<IDevice>();
+    const std::string sampleDriver = "sample-";
+    if (device == nullptr && name.substr(0, sampleDriver.size()) == sampleDriver) {
+        GTEST_SKIP();
+    }
+#endif  // PRESUBMIT_NOT_VTS
+
     ASSERT_NE(nullptr, device.get());
 }
 
@@ -60,6 +70,12 @@
 
 }  // namespace functional
 }  // namespace vts
+}  // namespace V1_0
+}  // namespace neuralnetworks
+}  // namespace hardware
+}  // namespace android
+
+namespace android::hardware::neuralnetworks::V1_0 {
 
 ::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
     return os << toString(errorStatus);
@@ -69,10 +85,7 @@
     return os << toString(deviceStatus);
 }
 
-}  // namespace V1_0
-}  // namespace neuralnetworks
-}  // namespace hardware
-}  // namespace android
+}  // namespace android::hardware::neuralnetworks::V1_0
 
 using android::hardware::neuralnetworks::V1_0::vts::functional::NeuralnetworksHidlEnvironment;
 
diff --git a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h
index e79129b..d4c114d 100644
--- a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h
@@ -72,14 +72,17 @@
 
 }  // namespace functional
 }  // namespace vts
+}  // namespace V1_0
+}  // namespace neuralnetworks
+}  // namespace hardware
+}  // namespace android
+
+namespace android::hardware::neuralnetworks::V1_0 {
 
 // pretty-print values for error messages
 ::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus);
 ::std::ostream& operator<<(::std::ostream& os, DeviceStatus deviceStatus);
 
-}  // namespace V1_0
-}  // namespace neuralnetworks
-}  // namespace hardware
-}  // namespace android
+}  // namespace android::hardware::neuralnetworks::V1_0
 
 #endif  // VTS_HAL_NEURALNETWORKS_V1_0_TARGET_TESTS_H
diff --git a/neuralnetworks/1.1/vts/functional/Android.bp b/neuralnetworks/1.1/vts/functional/Android.bp
index 3f0c256..4fbeac9 100644
--- a/neuralnetworks/1.1/vts/functional/Android.bp
+++ b/neuralnetworks/1.1/vts/functional/Android.bp
@@ -21,7 +21,6 @@
     srcs: [
         "GeneratedTestsV1_0.cpp",
     ],
-    test_suites: ["general-tests"],
 }
 
 // Tests for V1_1 models.
@@ -32,5 +31,16 @@
         "BasicTests.cpp",
         "GeneratedTests.cpp",
     ],
-    test_suites: ["general-tests"],
+}
+
+cc_test {
+    name: "PresubmitHalNeuralnetworksV1_1TargetTest",
+    defaults: ["VtsHalNeuralNetworksTargetTestDefaults"],
+    srcs: [
+        "BasicTests.cpp",
+        "GeneratedTests.cpp",
+    ],
+    cflags: [
+        "-DPRESUBMIT_NOT_VTS",
+    ],
 }
diff --git a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp
index 62381e6..224a51d 100644
--- a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.cpp
@@ -49,7 +49,17 @@
 void NeuralnetworksHidlTest::SetUp() {
     ::testing::VtsHalHidlTargetTestBase::SetUp();
     device = ::testing::VtsHalHidlTargetTestBase::getService<IDevice>(
-        NeuralnetworksHidlEnvironment::getInstance());
+            NeuralnetworksHidlEnvironment::getInstance());
+
+#ifdef PRESUBMIT_NOT_VTS
+    const std::string name =
+            NeuralnetworksHidlEnvironment::getInstance()->getServiceName<IDevice>();
+    const std::string sampleDriver = "sample-";
+    if (device == nullptr && name.substr(0, sampleDriver.size()) == sampleDriver) {
+        GTEST_SKIP();
+    }
+#endif  // PRESUBMIT_NOT_VTS
+
     ASSERT_NE(nullptr, device.get());
 }
 
@@ -60,6 +70,12 @@
 
 }  // namespace functional
 }  // namespace vts
+}  // namespace V1_1
+}  // namespace neuralnetworks
+}  // namespace hardware
+}  // namespace android
+
+namespace android::hardware::neuralnetworks::V1_0 {
 
 ::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
     return os << toString(errorStatus);
@@ -69,10 +85,7 @@
     return os << toString(deviceStatus);
 }
 
-}  // namespace V1_1
-}  // namespace neuralnetworks
-}  // namespace hardware
-}  // namespace android
+}  // namespace android::hardware::neuralnetworks::V1_0
 
 using android::hardware::neuralnetworks::V1_1::vts::functional::NeuralnetworksHidlEnvironment;
 
diff --git a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h
index 970e8b5..1c8c0e1 100644
--- a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h
@@ -81,14 +81,17 @@
 
 }  // namespace functional
 }  // namespace vts
+}  // namespace V1_1
+}  // namespace neuralnetworks
+}  // namespace hardware
+}  // namespace android
+
+namespace android::hardware::neuralnetworks::V1_0 {
 
 // pretty-print values for error messages
 ::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus);
 ::std::ostream& operator<<(::std::ostream& os, DeviceStatus deviceStatus);
 
-}  // namespace V1_1
-}  // namespace neuralnetworks
-}  // namespace hardware
-}  // namespace android
+}  // namespace android::hardware::neuralnetworks::V1_0
 
 #endif  // VTS_HAL_NEURALNETWORKS_V1_1_H
diff --git a/neuralnetworks/1.2/Android.bp b/neuralnetworks/1.2/Android.bp
index 9057b94..daf0c18 100644
--- a/neuralnetworks/1.2/Android.bp
+++ b/neuralnetworks/1.2/Android.bp
@@ -22,9 +22,12 @@
         "android.hidl.safe_union@1.0",
     ],
     types: [
+        "Constant",
         "DeviceType",
+        "Extension",
         "FmqRequestDatum",
         "FmqResultDatum",
+        "MeasureTiming",
         "Model",
         "Operand",
         "OperandType",
@@ -34,6 +37,7 @@
         "OperationTypeRange",
         "OutputShape",
         "SymmPerChannelQuantParams",
+        "Timing",
     ],
     gen_java: false,
 }
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 9e7d8f0..ab17598 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -38,6 +38,8 @@
      *
      * Values of this operand type are either true or false. A zero value
      * represents false; any other value represents true.
+     *
+     * Available since API level 29.
      */
     BOOL = 6,
     /**
@@ -47,42 +49,50 @@
      * used to convert the 16 bit number to a real value in the following way:
      * realValue = integerValue * scale.
      *
-     * scale is a 32 bit floating point with value greater then zero.
+     * scale is a 32 bit floating point with value greater than zero.
+     *
+     * Available since API level 29.
      */
     TENSOR_QUANT16_SYMM = 7,
-    /** A tensor of IEEE 754 16 bit floating point values. */
+    /**
+     * A tensor of IEEE 754 16 bit floating point values.
+     *
+     * Available since API level 29.
+     */
     TENSOR_FLOAT16 = 8,
     /**
      * A tensor of 8 bit boolean values.
      *
      * Values of this operand type are either true or false. A zero value
      * represents false; any other value represents true.
+     *
+     * Available since API level 29.
      */
     TENSOR_BOOL8 = 9,
-    /** An IEEE 754 16 bit floating point scalar value. */
+    /**
+     * An IEEE 754 16 bit floating point scalar value.
+     *
+     * Available since API level 29.
+     */
     FLOAT16 = 10,
     /**
      * A tensor of 8 bit signed integers that represent real numbers.
      *
-     * This tensor is associated with additional fields that are
-     * used to convert the 8 bit signed integer to the real value and vice versa.
+     * This tensor is associated with additional fields that can
+     * be used to convert the 8 bit signed integer to the real value and vice versa.
      * These fields are:
      * - channelDim: a 32 bit unsigned integer indicating channel dimension.
      * - scales: an array of positive 32 bit floating point values.
      * The size of the scales array must be equal to dimensions[channelDim].
-     * These fields are located inside Operand's extraParams union, inside the
-     * SymmPerChannelQuantParams struct.
      *
-     * An Operand of this type must use the 'channelQuant' variant of its
-     * extraParams field.
+     * The channel dimension of this tensor must not be unknown (dimensions[channelDim] != 0).
      *
-     * The channel dimension of this tensor must be known, i.e.
-     * dimensions[channelDim] must be non-zero.
-     *
-     * The formula for real values:
+     * The formula is:
      * realValue[..., C, ...] =
      *     integerValue[..., C, ...] * scales[C]
      * where C is an index in the Channel dimension.
+     *
+     * Available since API level 29.
      */
     TENSOR_QUANT8_SYMM_PER_CHANNEL = 11,
     /**
@@ -95,8 +105,36 @@
      *
      * The formula is:
      * real_value = (integer_value - zeroPoint) * scale.
+     *
+     * Available since API level 29.
      */
     TENSOR_QUANT16_ASYMM = 12,
+    /**
+     * A tensor of 8 bit signed integers that represent real numbers.
+     *
+     * Attached to this tensor is a number representing real value scale that is
+     * used to convert the 8 bit number to a real value in the following way:
+     * realValue = integerValue * scale.
+     *
+     * scale is a 32 bit floating point with value greater than zero.
+     *
+     * Available since API level 29.
+     */
+    TENSOR_QUANT8_SYMM = 13,
+    /*
+     * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
+     * OEM operation and data types.
+     *
+     * OEM specific scalar value.
+     * OEM                 = 10000,
+     */
+    /*
+     * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
+     * OEM operation and data types.
+     *
+     * A tensor of OEM specific values.
+     * TENSOR_OEM_BYTE     = 10001,
+     */
     /* ADDING A NEW FUNDAMENTAL TYPE REQUIRES UPDATING THE VALUE OF
      * OperandTypeRange::FUNDAMENTAL_MAX.
      */
@@ -111,7 +149,7 @@
 enum OperandTypeRange : uint32_t {
     BASE_MIN        = 0,
     FUNDAMENTAL_MIN = 0,
-    FUNDAMENTAL_MAX = 12,
+    FUNDAMENTAL_MAX = 13,
     OEM_MIN         = 10000,
     OEM_MAX         = 10001,
     BASE_MAX        = 0xFFFF,
@@ -122,64 +160,4367 @@
  *
  * The type of an operation in a model.
  */
-enum OperationType : @1.1::OperationType {
-    // TODO(b/116445845): Sync docs when all ops are implemented.
+enum OperationType : int32_t {
+    /**
+     * Adds two tensors, element-wise.
+     *
+     * Takes two input tensors of identical {@link OperandType} and compatible
+     * dimensions. The output is the sum of both input tensors, optionally
+     * modified by an activation function.
+     *
+     * Two dimensions are compatible when:
+     *     1. they are equal, or
+     *     2. one of them is 1
+     *
+     * The size of the output is the maximum size along each dimension of the
+     * input operands. It starts with the trailing dimensions, and works its
+     * way forward.
+     *
+     * Example:
+     *
+     *     input1.dimension = {4, 1, 2}
+     *     input2.dimension = {5, 4, 3, 1}
+     *     output.dimension = {5, 4, 3, 2}
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+     *      as input0.
+     * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     *
+     * Outputs:
+     * * 0: The sum, a tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 27.
+     */
+    ADD = @1.1::OperationType:ADD,
+
+    /**
+     * Performs a 2-D average pooling operation.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, channel] =
+     *         sum_{di, dj}(
+     *             input[b, strides[1] * i + di, strides[2] * j + dj, channel]
+     *         ) / sum(1)
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+     *       Set to true to specify NCHW data layout for input0 and output0.
+     *       Available since API level 29.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth].
+     *
+     * Available since API level 27.
+     */
+    AVERAGE_POOL_2D = @1.1::OperationType:AVERAGE_POOL_2D,
+
+    /**
+     * Concatenates the input tensors along the given dimension.
+     *
+     * The input tensors must have identical {@link OperandType} and the same
+     * dimensions except the dimension along the concatenation axis.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM} (full support since API
+     *   level 29, see the input section)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0 ~ n-1: The list of n input tensors, of shape
+     *            [D0, D1, ..., Daxis(i), ..., Dm].
+     *            Before API level 29, all input tensors of
+     *            {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *            must have the same scale and zeroPoint as the output tensor.
+     * * n: An {@link OperandType::INT32} scalar, specifying the
+     *      concatenation axis.
+     *
+     * Outputs:
+     * * 0: The output, a tensor of the same {@link OperandType} as the input
+     *      tensors. The output shape is [D0, D1, ..., sum(Daxis(i)), ..., Dm].
+     *
+     * Available since API level 27.
+     */
+    CONCATENATION = @1.1::OperationType:CONCATENATION,
+
+    /**
+     * Performs an 2-D convolution operation.
+     *
+     * The CONV_2D op sweeps a 2-D filter that can mix channels together over a
+     * batch of images, applying the filter to each window of each image of the
+     * appropriate size.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, channel] =
+     *         sum_{di, dj, k} (
+     *             input[b, strides[1] * i + di, strides[2] * j + dj, k] *
+     *             filter[channel, di, dj, k]
+     *         ) + bias[channel]
+     *
+     * Supported tensor {@link OperandType} configurations:
+     * * 32 bit Floating point :
+     * * * {@link OperandType::TENSOR_FLOAT32} for input, filter, output, and bias.
+     *
+     * * Quantized:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * Available since API level 29:
+     * * Quantized with symmetric per channel quantization for the filter:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * * 16 bit Floating point:
+     * * {@link OperandType::TENSOR_FLOAT16} for input, filter, output, and bias.
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_in], specifying the
+     *      filter. For tensor of type
+     *      {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} the channel
+     *      dimension (extraParams.channelQuant.channelDim) must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+     *      type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+     *      of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+     *      should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+     *      0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     * * 11: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on width dimension. If this input is set,
+     *      input 12 (dilation factor for height) must be specified as well.
+     *      Available since API level 29.
+     * * 12: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on height dimension. If this input is set,
+     *      input 11 (dilation factor for width) must be specified as well.
+     *      Available since API level 29.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_in], specifying the
+     *      filter. For tensor of type
+     *      {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} the channel
+     *      dimension (extraParams.channelQuant.channelDim) must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+     *      type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+     *      of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+     *      should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+     *      0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     * * 8: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on width dimension. If this input is set,
+     *      input 9 (dilation factor for height) must be specified as well.
+     *      Available since API level 29.
+     * * 9: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on height dimension. If this input is set,
+     *      input 8 (dilation factor for width) must be specified as well.
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth_out]. For output tensor of
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
+     *      must be satisfied: output_scale > input_scale * filter_scale (for
+     *      filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     *      this condition must be true for all filter scales).
+     *
+     * Available since API level 27.
+     */
+    CONV_2D = @1.1::OperationType:CONV_2D,
+
+    /**
+     * Performs a depthwise 2-D convolution operation.
+     *
+     * Given an input tensor of shape [batches, height, width, depth_in] and a
+     * filter tensor of shape [1, filter_height, filter_width, depth_out]
+     * containing depth_out convolutional filters of depth 1, DEPTHWISE_CONV
+     * applies a different filter to each input channel (expanding from 1
+     * channel to channel_multiplier channels for each), then concatenates the
+     * results together.
+     *
+     * The output has depth_out = depth_in * depth_multiplier channels.
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, k * channel_multiplier + q] =
+     *         sum_{di, dj} (
+     *             input[b, strides[1] * i + di, strides[2] * j + dj, k] *
+     *             filter[1, di, dj, k * channel_multiplier + q]
+     *         ) + bias[k * channel_multiplier + q]
+     *
+     * Supported tensor {@link OperandType} configurations:
+     * * 32 bit Floating point :
+     * * * {@link OperandType::TENSOR_FLOAT32} for input, filter, output, and bias.
+     *
+     * * Quantized:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * Available since API level 29:
+     * * Quantized with symmetric per channel quantization for the filter:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: A 4-D tensor, of shape [1, filter_height, filter_width, depth_out],
+     *      specifying the filter. For tensor of type
+     *      {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} the channel
+     *      dimension (extraParams.channelQuant.channelDim) must be set to 3.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+     *      type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+     *      of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+     *      should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+     *      0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 9: An {@link OperandType::INT32} scalar, specifying the depthwise
+     *      multiplier.
+     * * 10: An {@link OperandType::INT32} scalar, and has to be one of the
+     *       {@link FusedActivationFunc} values. Specifies the activation to
+     *       invoke on the result.
+     * * 11: An optional {@link OperandType::BOOL} scalar, default to false.
+     *       Set to true to specify NCHW data layout for input0 and output0.
+     *       Available since API level 29.
+     * * 12: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on width dimension. If this input is set,
+     *      input 13 (dilation factor for height) must be specified as well.
+     *      Available since API level 29.
+     * * 13: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on height dimension. If this input is set,
+     *      input 12 (dilation factor for width) must be specified as well.
+     *      Available since API level 29.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: A 4-D tensor, of shape [1, filter_height, filter_width, depth_out],
+     *      specifying the filter.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+     *      type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+     *      of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+     *      should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+     *      0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the depthwise
+     *      multiplier.
+     * * 7: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 8: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     * * 9: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on width dimension. If this input is set,
+     *      input 10 (dilation factor for height) must be specified as well.
+     *      Available since API level 29.
+     * * 10: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on height dimension. If this input is set,
+     *      input 9 (dilation factor for width) must be specified as well.
+     *      Available since API level 29.
+
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth_out]. For output tensor of
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
+     *      must be satisfied: output_scale > input_scale * filter_scale (for
+     *      filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     *      this condition must be true for all filter scales).
+     *
+     * Available since API level 27.
+     */
+    DEPTHWISE_CONV_2D = @1.1::OperationType:DEPTHWISE_CONV_2D,
+
+    /**
+     * Rearranges data from depth into blocks of spatial data.
+     *
+     * More specifically, this op outputs a copy of the input tensor where
+     * values from the depth dimension are moved in spatial blocks to the height
+     * and width dimensions. The value block_size indicates the input block size
+     * and how the data is moved.
+     *
+     * Chunks of data of size block_size * block_size from depth are rearranged
+     * into non-overlapping blocks of size block_size x block_size.
+     *
+     * The width of the output tensor is input_depth * block_size, whereas the
+     * height is input_height * block_size. The depth of the input tensor must
+     * be divisible by block_size * block_size
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the block_size.
+     *      block_size must be >=1 and block_size * block_size must be a divisor
+     *      of the input depth.
+     * * 2: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape [batch, height*block_size,
+     *      width*block_size, depth/(block_size*block_size)].
+     *
+     * Available since API level 27.
+     */
+    DEPTH_TO_SPACE = @1.1::OperationType:DEPTH_TO_SPACE,
+
+    /**
+     * Dequantizes the input tensor.
+     *
+     * The formula is:
+     *
+     *     output = (input - zeroPoint) * scale.
+     *
+     * Supported input tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_SYMM} (since API level 29)
+     * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} (since API level 29)
+     *
+     * Supported output tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}.
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: A tensor with the same shape as input0.
+     *
+     * Available since API level 27.
+     */
+    DEQUANTIZE = @1.1::OperationType:DEQUANTIZE,
+
+    /**
+     * Looks up sub-tensors in the input tensor.
+     *
+     * This operator takes for input a tensor of values (Values) and
+     * a one-dimensional tensor of selection indices (Lookups).
+     * The output tensor is the concatenation of sub-tensors of Values as
+     * selected by Lookups.
+     *
+     * Think of Values as being sliced along its first dimension:
+     * The entries in Lookups select which slices are concatenated together
+     * to create the output tensor.
+     *
+     * For example, if Values has shape of [40, 200, 300] and
+     * Lookups has shape of [3], all three values found in Lookups are
+     * expected to be between 0 and 39. The resulting tensor must
+     * have shape of [3, 200, 300].
+     *
+     * If a value in Lookups is out of bounds, the operation must fail
+     * and an error must be reported.
+     *
+     * Supported value tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported value tensor rank: from 2
+     *
+     * Inputs:
+     * * 0: Lookups. A 1-D tensor of {@link OperandType::TENSOR_INT32}.
+     *      The values are indices into the first dimension of Values.
+     * * 1: Values. An n-D tensor, where n >= 2, from which sub-tensors are
+     *      extracted.
+     *
+     * Output:
+     * * 0: A n-D tensor with the same rank and shape as the Values
+     *      tensor, except for the first dimension which has the same size
+     *      as Lookups' only dimension.
+     *
+     * Available since API level 27.
+     */
+    EMBEDDING_LOOKUP = @1.1::OperationType:EMBEDDING_LOOKUP,
+
+    /**
+     * Computes element-wise floor() on the input tensor.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor, of the same {@link OperandType} and dimensions as
+     *      the input tensor.
+     *
+     * Available since API level 27.
+     */
+    FLOOR = @1.1::OperationType:FLOOR,
+
+    /**
+     * Denotes a fully (densely) connected layer, which connects all elements
+     * in the input tensor with each element in the output tensor.
+     *
+     * This layer implements the operation:
+     *
+     *     outputs = activation(inputs * weights’ + bias)
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor of at least rank 2, specifying the input. If rank is
+     *      greater than 2, then it gets flattened to a 2-D Tensor. The
+     *      (flattened) 2-D Tensor is reshaped (if necessary) to
+     *      [batch_size, input_size], where "input_size" corresponds to the
+     *      number of inputs to the layer, matching the second dimension of
+     *      weights, and "batch_size" is calculated by dividing the number of
+     *      elements by "input_size".
+     * * 1: A 2-D tensor, specifying the weights, of shape
+     *      [num_units, input_size], where "num_units" corresponds to the number
+     *      of output nodes.
+     * * 2: A 1-D tensor, of shape [num_units], specifying the bias. For input
+     *      tensor of {@link OperandType::TENSOR_FLOAT32}, the bias should
+     *      also be of {@link OperandType::TENSOR_FLOAT32}. For input tensor
+     *      of {@link OperandType::TENSOR_QUANT8_ASYMM}, the bias should be
+     *      of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0 and
+     *      bias_scale == input_scale * filter_scale.
+     * * 3: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     *
+     * Outputs:
+     * * 0: The output tensor, of shape [batch_size, num_units]. For output
+     *      tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}, the following
+     *      condition must be satisfied:
+     *      output_scale > input_scale * filter_scale.
+     *
+     * Available since API level 27.
+     */
+    FULLY_CONNECTED = @1.1::OperationType:FULLY_CONNECTED,
+
+    /**
+     * Looks up sub-tensors in the input tensor using a key-value map.
+     *
+     * This operator takes for input a tensor of values (Values),
+     * a one-dimensional tensor of selection values (Lookups) and
+     * a one-dimensional tensor that maps these values to Values
+     * indexes. The output tensor is the concatenation of sub-tensors of
+     * Values as selected by Lookups via Keys.
+     *
+     * Think of Values as being sliced along its outer-most dimension.
+     * The output is a concatenation of selected slices, with one slice
+     * for each entry of Lookups. The slice selected is the one at the
+     * same index as the Maps entry that matches the value in Lookups.
+     *
+     * For a hit, the corresponding sub-tensor of Values is included
+     * in the Output tensor. For a miss, the corresponding sub-tensor in
+     * Output must have zero values.
+     *
+     * For example, if Values has shape of [40, 200, 300],
+     * Keys should have a shape of [40]. If Lookups tensor has shape
+     * of [3], three slices are being concatenated, so the resulting tensor
+     * must have the shape of [3, 200, 300]. If the first entry in Lookups
+     * has the value 123456, that value must be located in Keys tensor.
+     * If the sixth entry of Keys contains 123456, the sixth slice of Values
+     * must be selected. If no entry in Keys has 123456, a slice of zeroes
+     * must be concatenated.
+     *
+     * Supported value tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported value tensor rank: from 2
+     *
+     * Inputs:
+     * * 0: Lookups. A 1-D {@link OperandType::TENSOR_INT32} tensor with
+     *      shape [ k ].
+     * * 1: Keys. A 1-D {@link OperandType::TENSOR_INT32} tensor with shape
+     *      [ n ]; Keys and Values pair represent a map, i.e., the ith element
+     *      in Keys (Keys[i]) is the key to select the ith sub-tensor in Values
+     *      (Values[i]), where 0 <= i <= n-1. Keys tensor *MUST* be sorted in
+     *      ascending order.
+     * * 2: Values. A tensor with shape of [ n, … ]; i.e., the first dimension
+     *      must be n.
+     *
+     * Outputs:
+     * * 0: Output. A tensor with shape [ k …].
+     * * 1: Hits. A boolean tensor with shape [ k ] indicates whether the lookup
+     *      hits (True) or not (False).
+     *      Stored as {@link OperandType::TENSOR_QUANT8_ASYMM} with offset 0
+     *      and scale 1.0f.
+     *      A non-zero byte represents True, a hit. A zero indicates otherwise.
+     *
+     * Available since API level 27.
+     */
+    HASHTABLE_LOOKUP = @1.1::OperationType:HASHTABLE_LOOKUP,
+
+    /**
+     * Applies L2 normalization along the depth dimension.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[batch, row, col, channel] =
+     *         input[batch, row, col, channel] /
+     *         sqrt(sum_{c} pow(input[batch, row, col, c], 2))
+     *
+     * For input tensor with rank less than 4, independently normalizes each
+     * 1-D slice along dimension dim.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: up to 4
+     * Tensors with rank less than 4 are only supported since API level 29.
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be normalized.
+     * * 1: An optional {@link OperandType::INT32} scalar, default to -1,
+     *      specifying the dimension normalization would be performed on.
+     *      Negative index is used to specify axis from the end (e.g. -1 for
+     *      the last axis). Must be in the range [-n, n).
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} and same shape as input0.
+     *
+     * Available since API level 27.
+     */
+    L2_NORMALIZATION = @1.1::OperationType:L2_NORMALIZATION,
+
+    /**
+     * Performs an 2-D L2 pooling operation.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, c] =
+     *         sqrt(sum_{di, dj} pow(input[b, strides[1] * i + di, strides[2] * j + dj, c], 2) /
+     *              sum(1))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+     *       Set to true to specify NCHW data layout for input0 and output0.
+     *       Available since API level 29.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth].
+     *
+     * Available since API level 27.
+     */
+    L2_POOL_2D = @1.1::OperationType:L2_POOL_2D,
+
+    /**
+     * Applies Local Response Normalization along the depth dimension.
+     *
+     * The 4-D input tensor is treated as a 3-D array of 1-D vectors (along the
+     * last dimension), and each vector is normalized independently. Within a
+     * given vector, each component is divided by the weighted, squared sum of
+     * inputs within depth_radius.
+     *
+     * The output is calculated using this formula:
+     *
+     *     sqr_sum[a, b, c, d] = sum(
+     *         pow(input[a, b, c, d - depth_radius : d + depth_radius + 1], 2))
+     *     output = input / pow((bias + alpha * sqr_sum), beta)
+     *
+     * For input tensor with rank less than 4, independently normalizes each
+     * 1-D slice along specified dimension.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: up to 4
+     * Tensors with rank less than 4 are only supported since API level 29.
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the radius of
+     *      the normalization window.
+     * * 2: An {@link OperandType::FLOAT32} scalar, specifying the bias, must
+     *      not be zero.
+     * * 3: An {@link OperandType::FLOAT32} scalar, specifying the scale
+     *      factor, alpha.
+     * * 4: An {@link OperandType::FLOAT32} scalar, specifying the exponent,
+     *      beta.
+     * * 5: An optional {@link OperandType::INT32} scalar, default to -1,
+     *      specifying the dimension normalization would be performed on.
+     *      Negative index is used to specify axis from the end (e.g. -1 for
+     *      the last axis). Must be in the range [-n, n).
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 27.
+     */
+    LOCAL_RESPONSE_NORMALIZATION = @1.1::OperationType:LOCAL_RESPONSE_NORMALIZATION,
+
+    /**
+     * Computes sigmoid activation on the input tensor element-wise.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = 1 / (1 + exp(-input))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the scale must be 1.f / 256 and the zeroPoint must be 0.
+     *
+     * Available since API level 27.
+     */
+    LOGISTIC = @1.1::OperationType:LOGISTIC,
+
+    /**
+     * Projects an input to a bit vector via locality senstive hashing.
+     *
+     * Supported input tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported input tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: Hash functions. Dim.size == 2, DataType: Float.
+     *      Tensor[0].Dim[0]: Number of hash functions.
+     *      Tensor[0].Dim[1]: Number of projected output bits generated by each
+     *      hash function.
+     *      If the projection type is Sparse:
+     *      Tensor[0].Dim[1] + ceil(log2(Tensor[0].Dim[0])) <= 32
+     *
+     * * 1: Input. Dim.size >= 1, no restriction on DataType.
+     * * 2: Weight. Optional. Dim.size == 1, DataType: Float.
+     *      If not set, each input element is considered to have the same weight
+     *      of 1.0.
+     *      Tensor[1].Dim[0] == Tensor[2].Dim[0]
+     * * 3: Type:
+     *        Sparse:
+     *          Value LSHProjectionType_SPARSE(=3) (since API level 29).
+     *          Computed bit vector is considered to be sparse.
+     *          Each output element is an int32 made up of multiple bits
+     *          computed from hash functions.
+     *
+     *          NOTE: To avoid collisions across hash functions, an offset value
+     *          of k * (1 << Tensor[0].Dim[1]) will be added to each signature,
+     *          where k is the index of the hash function.
+     *
+     *          Value LSHProjectionType_SPARSE_DEPRECATED(=1).
+     *          Legacy behavior that does not include the offset value.
+     *
+     *        Dense:
+     *          Value LSHProjectionType_DENSE(=2).
+     *          Computed bit vector is considered to be dense. Each output
+     *          element represents a bit and can take the value of either
+     *          0 or 1.
+     *
+     * Outputs:
+     * * 0: If the projection type is Sparse:
+     *      Output.Dim == { Tensor[0].Dim[0] }
+     *      A tensor of int32 that represents hash signatures,
+     *
+     *      If the projection type is Dense:
+     *      Output.Dim == { Tensor[0].Dim[0] * Tensor[0].Dim[1] }
+     *      A flattened tensor that represents projected bit vectors.
+     *
+     * Available since API level 27.
+     * The offset value for sparse projections was added in API level 29.
+     */
+    LSH_PROJECTION = @1.1::OperationType:LSH_PROJECTION,
+
+    /**
+     * Performs a single time step in a Long Short-Term Memory (LSTM) layer
+     *
+     * The LSTM operation is described by the following equations.
+     *
+     * \f{eqnarray*}{
+     * i_t =& \sigma(W_{xi}x_t+W_{hi}h_{t-1}+W_{ci}C_{t-1}+b_i) & \\
+     * f_t =& \sigma(W_{xf}x_t+W_{hf}h_{t-1}+W_{cf}C_{t-1}+b_f) & \\
+     * C_t =& clip(f_t \odot C_{t-1} + i_t \odot
+     *        g(W_{xc}x_t+W_{hc}h_{t-1}+b_c),\ t_{cell}) & \\
+     * o_t =& \sigma(W_{xo}x_t+W_{ho}h_{t-1}+W_{co}C_t+b_o) & \\
+     *      & & \\
+     *      & clip(W_{proj}(o_t \odot g(C_t))+b_{proj},\ t_{proj})
+     *      & if\ there\ is\ a\ projection; \\
+     * h_t =& & \\
+     *      & o_t \odot g(C_t) & otherwise. \\
+     * \f}
+     * Where:
+     * * \f$x_t\f$ is the input,
+     * * \f$i_t\f$ is the input gate,
+     * * \f$f_t\f$ is the forget gate,
+     * * \f$C_t\f$ is the cell state,
+     * * \f$o_t\f$ is the output,
+     * * \f$h_t\f$ is the output state,
+     * * \f$\sigma\f$ is the logistic sigmoid function,
+     * * \f$g\f$ is the cell input and cell output activation function, usually
+     *   \f$tahn\f$,
+     * * \f$W_{xi}\f$ is the input-to-input weight matrix,
+     * * \f$W_{hi}\f$ is the recurrent to input weight matrix,
+     * * \f$W_{ci}\f$ is the cell-to-input weight matrix,
+     * * \f$b_i\f$ is the input gate bias,
+     * * \f$W_{xf}\f$ is the input-to-forget weight matrix,
+     * * \f$W_{hf}\f$ is the recurrent-to-forget weight matrix,
+     * * \f$W_{cf}\f$ is the cell-to-forget weight matrix,
+     * * \f$b_f\f$ is the forget gate bias,
+     * * \f$W_{xc}\f$ is the input-to-cell weight matrix,
+     * * \f$W_{hc}\f$ is the recurrent-to-cell weight matrix,
+     * * \f$b_c\f$ is the cell bias,
+     * * \f$W_{xo}\f$ is the input-to-output weight matrix,
+     * * \f$W_{ho}\f$ is the recurrent-to-output weight matrix,
+     * * \f$W_{co}\f$ is the cell-to-output weight matrix,
+     * * \f$b_o\f$ is the output gate bias,
+     * * \f$W_{proj}\f$ is the projection weight matrix,
+     * * \f$b_{proj}\f$ is the projection bias,
+     * * \f$t_{cell}\f$ is the threshold for clipping the cell state, and
+     * * \f$t_{proj}\f$ is the threshold for clipping the projected output.
+     * * \f$\odot\f$ is the
+     *   <a href="https://en.wikipedia.org/wiki/Hadamard_product_(matrices)">
+     *   Hadamard product</a> that takes two matrices and produces another
+     *   matrix, each element of which is the product of the corresponding
+     *   elements of the input matrices.
+     *
+     * Since API level 29 LSTM supports layer normalization.
+     * In case layer normalization is used, the inputs to internal activation
+     * functions (sigmoid and \f$g\f$) are normalized, rescaled and recentered
+     * following an approach from section 3.1 from
+     * https://arxiv.org/pdf/1607.06450.pdf
+     *
+     * The operation has the following independently optional inputs:
+     * * The input-to-input weights (\f$W_{xi}\f$), recurrent-to-input weights
+     *   (\f$W_{hi}\f$), cell-to-input (\f$W_{ci}\f$) weights, and input gate
+     *   bias (\f$b_i\f$) either all have values, or none of them have values
+     *   (i.e., all set to null). If they have no values, coupling of input and
+     *   forget gates (CIFG) is used, in which case the input gate (\f$i_t\f$)
+     *   is calculated using the following equation instead.
+     *   \f{eqnarray*}{
+     *   i_t = 1 - f_t
+     *   \f}
+     * * The cell-to-forget weights (\f$W_{cf}\f$) and cell-to-output weights
+     *   (\f$W_{co}\f$) either both have values or neither of them have values.
+     *   If they have values, the peephole optimization is used. Additionally,
+     *   if CIFG is not used, cell-to-input weights (\f$W_{ci}\f$) is also
+     *   required to have values for peephole optimization.
+     * * The projection weights (\f$W_{proj}\f$) is required only for the
+     *   recurrent projection layer, and should otherwise have no value.
+     * * The projection bias (\f$b_{proj}\f$) may (but not required to) have a
+     *   value if the recurrent projection layer exists, and should otherwise
+     *   have no value.
+     * * (API level >= 29) The four layer normalization weights either all have
+     *   values or none of them have values. Layer normalization is used when
+     *   values are present.
+     *
+     * References:
+     *
+     * The default non-peephole non-CIFG implementation is based on:
+     * http://www.bioinf.jku.at/publications/older/2604.pdf
+     * S. Hochreiter and J. Schmidhuber. "Long Short-Term Memory". Neural
+     * Computation, 9(8):1735-1780, 1997.
+     *
+     * The peephole implementation and projection layer is based on:
+     * https://research.google.com/pubs/archive/43905.pdf
+     * Hasim Sak, Andrew Senior, and Francoise Beaufays. "Long short-term memory
+     * recurrent neural network architectures for large scale acoustic
+     * modeling." INTERSPEECH, 2014.
+     * (However, the concept of peephole optimization was introduced in work
+     * prior to this paper.)
+     *
+     * The coupling of input and forget gate (CIFG) is based on:
+     * http://arxiv.org/pdf/1503.04069.pdf
+     * Greff et al. "LSTM: A Search Space Odyssey"
+     *
+     * The layer normalization is based on:
+     * https://arxiv.org/pdf/1607.06450.pdf
+     * Jimmy Ba et al. "Layer Normalization"
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * All input and output tensors must be of the same type.
+     *
+     * Inputs:
+     * * 0: The input (\f$x_t\f$).
+     *      A 2-D tensor of shape [batch_size, input_size], where “batch_size”
+     *      corresponds to the batching dimension, and “input_size” is the size
+     *      of the input.
+     * * 1: The input-to-input weights (\f$W_{xi}\f$). Optional.
+     *      A 2-D tensor of shape [num_units, input_size], where “num_units”
+     *      corresponds to the number of cell units.
+     * * 2: The input-to-forget weights (\f$W_{xf}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 3: The input-to-cell weights (\f$W_{xc}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 4: The input-to-output weights (\f$W_{xo}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 5: The recurrent-to-input weights (\f$W_{hi}\f$). Optional.
+     *      A 2-D tensor of shape [num_units, output_size], where “output_size”
+     *      corresponds to either the number of cell units (i.e., “num_units”),
+     *      or the second dimension of the “projection_weights”, if defined.
+     * * 6: The recurrent-to-forget weights (\f$W_{hf}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 7: The recurrent-to-cell weights (\f$W_{hc}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 8: The recurrent-to-output weights (\f$W_{ho}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 9: The cell-to-input weights (\f$W_{ci}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 10:The cell-to-forget weights (\f$W_{cf}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 11:The cell-to-output weights (\f$W_{co}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 12:The input gate bias (\f$b_i\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 13:The forget gate bias (\f$b_f\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 14:The cell bias (\f$b_c\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 15:The output gate bias (\f$b_o\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 16:The projection weights (\f$W_{proj}\f$). Optional.
+     *      A 2-D tensor of shape [output_size, num_units].
+     * * 17:The projection bias (\f$b_{proj}\f$). Optional.
+     *      A 1-D tensor of shape [output_size].
+     * * 18:The output state (in) (\f$h_{t-1}\f$).
+     *      A 2-D tensor of shape [batch_size, output_size].
+     * * 19:The cell state (in) (\f$C_{t-1}\f$).
+     *      A 2-D tensor of shape [batch_size, num_units].
+     * * 20:The activation function (\f$g\f$).
+     *      A value indicating the activation function:
+     *      <ul>
+     *      <li>0: None;
+     *      <li>1: Relu;
+     *      <li>3: Relu6;
+     *      <li>4: Tanh;
+     *      <li>6: Sigmoid.
+     *      </ul>
+     * * 21:The clipping threshold (\f$t_{cell}\f$) for the cell state, such
+     *      that values are bound within [-cell_clip, cell_clip]. If set to 0.0
+     *      then clipping is disabled.
+     *      Until API level 29 this scalar must be of type {@link
+     *      FLOAT32}. Since API level 29, if all the input
+     *      tensors have type {@link OperandType::TENSOR_FLOAT32}, this
+     *      scalar must be of the type {@link OperandType::FLOAT32},
+     *      otherwise if all the input tensors have the type {@link
+     *      TENSOR_FLOAT16}, this scalar must be of type {@link
+     *      FLOAT16}.
+     * * 22:The clipping threshold (\f$t_{proj}\f$) for the output from the
+     *      projection layer, such that values are bound within
+     *      [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+     *      Until API level 29 this scalar must be of type {@link
+     *      FLOAT32}. Since API level 29, if all the input
+     *      tensors have type {@link OperandType::TENSOR_FLOAT32}, this
+     *      scalar must be of the type {@link OperandType::FLOAT32},
+     *      otherwise if all the input tensors have the type {@link
+     *      TENSOR_FLOAT16}, this scalar must be of type {@link
+     *      FLOAT16}.
+     * Since API level 29 there are additional inputs to this op:
+     * * 23:The input layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at input gate.
+     * * 24:The forget layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at forget gate.
+     * * 25:The cell layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at cell gate.
+     * * 26:The output layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at output gate.
+     *
+     * Outputs:
+     * * 0: The scratch buffer.
+     *      A 2-D tensor of shape [batch_size, num_units * 3] with CIFG, or
+     *      [batch_size, num_units * 4] without CIFG.
+     * * 1: The output state (out) (\f$h_t\f$).
+     *      A 2-D tensor of shape [batch_size, output_size].
+     * * 2: The cell state (out) (\f$C_t\f$).
+     *      A 2-D tensor of shape [batch_size, num_units].
+     * * 3: The output (\f$o_t\f$).
+     *      A 2-D tensor of shape [batch_size, output_size]. This is effectively
+     *      the same as the current “output state (out)” value.
+     *
+     * Available since API level 27.
+     */
+    LSTM = @1.1::OperationType:LSTM,
+
+    /**
+     * Performs an 2-D max pooling operation.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, channel] =
+     *         max_{di, dj} (
+     *             input[b, strides[1] * i + di, strides[2] * j + dj, channel]
+     *         )
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+     *       Set to true to specify NCHW data layout for input0 and output0.
+     *       Available since API level 29.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth].
+     *
+     * Available since API level 27.
+     */
+    MAX_POOL_2D = @1.1::OperationType:MAX_POOL_2D,
+
+    /**
+     * Multiplies two tensors, element-wise.
+     *
+     * Takes two input tensors of identical {@link OperandType} and compatible
+     * dimensions. The output is the product of both input tensors, optionally
+     * modified by an activation function.
+     *
+     * Two dimensions are compatible when:
+     *     1. they are equal, or
+     *     2. one of them is 1
+     *
+     * The size of the resulting output is the maximum size along each dimension
+     * of the input operands. It starts with the trailing dimensions, and works
+     * its way forward.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+     *      as input0.
+     * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     *
+     * Outputs:
+     * * 0: The product, a tensor of the same {@link OperandType} as input0.
+     *      For output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the following condition must be satisfied:
+     *      output_scale > input1_scale * input2_scale.
+     *
+     * Available since API level 27.
+     */
+    MUL = @1.1::OperationType:MUL,
+
+    /**
+     * Computes rectified linear activation on the input tensor element-wise.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = max(0, input)
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 27.
+     */
+    RELU = @1.1::OperationType:RELU,
+
+    /**
+     * Computes rectified linear 1 activation on the input tensor element-wise.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = min(1.f, max(-1.f, input))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 27.
+     */
+    RELU1 = @1.1::OperationType:RELU1,
+
+    /**
+     * Computes rectified linear 6 activation on the input tensor element-wise.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = min(6, max(0, input))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 27.
+     */
+    RELU6 = @1.1::OperationType:RELU6,
+
+    /**
+     * Reshapes a tensor.
+     *
+     * Given tensor, this operation returns a tensor that has the same values as
+     * tensor, but with a newly specified shape.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the tensor to be reshaped.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}, defining the
+     *      shape of the output tensor. The number of elements implied by shape
+     *      must be the same as the number of elements in the input tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor, of shape specified by the input shape.
+     *
+     * Available since API level 27.
+     */
+    RESHAPE = @1.1::OperationType:RESHAPE,
+
+    /**
+     * Resizes images to given size using the bilinear interpretation.
+     *
+     * Resized images must be distorted if their output aspect ratio is not the
+     * same as input aspect ratio. The corner pixels of output may not be the
+     * same as corner pixels of input.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since API level 29)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the output
+     *      height of the output tensor.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the output
+     *      width of the output tensor.
+     * * 3: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, new_height, new_width, depth].
+     *
+     * Available since API level 27.
+     */
+    RESIZE_BILINEAR = @1.1::OperationType:RESIZE_BILINEAR,
+
+    /**
+     * A basic recurrent neural network layer.
+     *
+     * This layer implements the operation:
+     * outputs = state = activation(inputs * input_weights +
+     *                              state * recurrent_weights + bias)
+     *
+     * Where:
+     * * “input_weights” is a weight matrix that multiplies the inputs;
+     * * “recurrent_weights” is a weight matrix that multiplies the current
+     *    “state” which itself is the output from the previous time step
+     *    computation;
+     * * “bias” is a bias vector (added to each output vector in the batch);
+     * * “activation” is the function passed as the “fused_activation_function”
+     *   argument (if not “NONE”).
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * The input tensors must all be the same type.
+     *
+     * Inputs:
+     * * 0: input.
+     *      A 2-D tensor of shape [batch_size, input_size], where “batch_size”
+     *      corresponds to the batching dimension, and “input_size” is the size
+     *      of the input.
+     * * 1: weights.
+     *      A 2-D tensor of shape [num_units, input_size], where “num_units”
+     *      corresponds to the number of units.
+     * * 2: recurrent_weights.
+     *      A 2-D tensor of shape [num_units, num_units], with columns
+     *      corresponding to the weights from each unit.
+     * * 3: bias.
+     *      A 1-D tensor of shape [num_units].
+     * * 4: hidden state (in).
+     *      A 2-D tensor of shape [batch_size, num_units].
+     * * 5: fused_activation_function.
+     *      An optional {@link FusedActivationFunc} value indicating the
+     *      activation function. If “NONE” is specified then it results in a
+     *      linear activation.
+     *
+     * Outputs:
+     * * 0: hidden state (out).
+     *      A 2-D tensor of shape [batch_size, num_units].
+     *
+     * * 1: output.
+     *      A 2-D tensor of shape [batch_size, num_units]. This is effectively
+     *      the same as the current state value.
+     *
+     * Available since API level 27.
+     */
+    RNN = @1.1::OperationType:RNN,
+
+    /**
+     * Computes the softmax activation on the input tensor element-wise, per
+     * batch, by normalizing the input vector so the maximum coefficient is
+     * zero.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output[batch, i] =
+     *         exp((input[batch, i] - max(input[batch, :])) * beta) /
+     *         sum_{k}{exp((input[batch, k] - max(input[batch, :])) * beta)}
+     *
+     * For input tensor with rank other than 2, the activation will be applied
+     * independently on each 1-D slice along specified dimension.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4.
+     * Tensors with rank other than 2 or 4 are only supported since API level 29.
+     *
+     * Inputs:
+     * * 0: A 2-D or 4-D tensor, specifying the tensor to be reshaped.
+     * * 1: An {@link OperandType::FLOAT32} scalar, specifying the positive
+     *      scaling factor for the exponent, beta.
+     * * 2: An optional {@link OperandType::INT32} scalar, default to -1,
+     *      specifying the dimension the activation would be performed on.
+     *      Negative index is used to specify axis from the end (e.g. -1 for
+     *      the last axis). Must be in the range [-n, n).
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the scale must be 1.f / 256 and the zeroPoint must be 0.
+     *
+     * Available since API level 27.
+     */
+    SOFTMAX = @1.1::OperationType:SOFTMAX,
+
+    /**
+     * Rearranges blocks of spatial data, into depth.
+     *
+     * More specifically, this op outputs a copy of the input tensor where
+     * values from the height and width dimensions are moved to the depth
+     * dimension. The value block_size indicates the input block size and how
+     * the data is moved.
+     *
+     * Chunks of data of size block_size * block_size from depth are rearranged
+     * into non-overlapping blocks of size block_size x block_size.
+     *
+     * The depth of the output tensor is input_depth * block_size * block_size.
+     * The input tensor's height and width must be divisible by block_size.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the block_size.
+     *      block_size must be >=1 and block_size must be a divisor of both the
+     *      input height and width.
+     * * 2: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape [batches, height/block_size,
+     *      width/block_size, depth_in*block_size*block_size].
+     *
+     * Available since API level 27.
+     */
+    SPACE_TO_DEPTH = @1.1::OperationType:SPACE_TO_DEPTH,
+
+    /**
+     * SVDF op is a kind of stateful layer derived from the notion that a
+     * densely connected layer that's processing a sequence of input frames can
+     * be approximated by using a singular value decomposition of each of its
+     * nodes. The implementation is based on:
+     *
+     * https://research.google.com/pubs/archive/43813.pdf
+     *
+     * P. Nakkiran, R. Alvarez, R. Prabhavalkar, C. Parada.
+     * “Compressing Deep Neural Networks using a Rank-Constrained Topology”.
+     * INTERSPEECH, 2015.
+     *
+     * It processes the incoming input using a 2-stage filtering mechanism:
+     * * stage 1 performs filtering on the "features" dimension, whose outputs
+     *   get pushed into a memory of fixed-size memory_size.
+     * * stage 2 performs filtering on the "time" dimension of the memory_size
+     *   memoized outputs of stage 1.
+     *
+     * Specifically, for rank 1, this layer implements the operation:
+     *
+     *     memory = push(conv1d(inputs, weights_feature, feature_dim,
+     *                          "PADDING_VALID"));
+     *     outputs = activation(memory * weights_time + bias);
+     *
+     * Where:
+     * * “weights_feature” is a weights matrix that processes the inputs (by
+     *   convolving the input with every “feature filter”), and whose outputs
+     *   get pushed, stacked in order, into the fixed-size “memory” (the oldest
+     *   entry gets dropped);
+     * * “weights_time” is a weights matrix that processes the “memory” (by a
+     *   batched matrix multiplication on the num_units);
+     * * “bias” is an optional bias vector (added to each output vector in the
+     *   batch); and
+     * * “activation” is the function passed as the “fused_activation_function”
+     *   argument (if not “NONE”).
+     *
+     * Each rank adds a dimension to the weights matrices by means of stacking
+     * the filters.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * All input tensors must be the same type.
+     *
+     * Inputs:
+     * * 0: input.
+     *      A 2-D tensor of shape [batch_size, input_size], where “batch_size”
+     *      corresponds to the batching dimension, and “input_size” is the size
+     *      of the input.
+     * * 1: weights_feature.
+     *      A 2-D tensor of shape [num_units, input_size], where “num_units”
+     *      corresponds to the number of units.
+     * * 2: weights_time.
+     *      A 2-D tensor of shape [num_units, memory_size], where “memory_size”
+     *      corresponds to the fixed-size of the memory.
+     * * 3: bias.
+     *      An optional 1-D tensor of shape [num_units].
+     * * 4: state (in).
+     *      A 2-D tensor of shape [batch_size, (memory_size - 1) * num_units * rank].
+     * * 5: rank.
+     *      The rank of the SVD approximation.
+     * * 6: fused_activation_function.
+     *      An optional {@link FusedActivationFunc} value indicating the
+     *      activation function. If “NONE” is specified then it results in a
+     *      linear activation.
+     *
+     * Outputs:
+     * * 0: state (out).
+     *      A 2-D tensor of the same {@link OperandType} as the inputs, with shape
+     *      [batch_size, (memory_size - 1) * num_units * rank].
+     * * 1: output.
+     *      A 2-D tensor of the same {@link OperandType} as the inputs, with shape
+     *      [batch_size, num_units].
+     *
+     * Available since API level 27.
+     */
+    SVDF = @1.1::OperationType:SVDF,
+
+    /**
+     * Computes hyperbolic tangent of input tensor element-wise.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = tanh(input)
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since API level 29)
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the scale must be 1.f / 128 and the zeroPoint must be 128.
+     *
+     * Available since API level 27.
+     */
+    TANH = @1.1::OperationType:TANH,
+
+    /**
+     * BatchToSpace for N-dimensional tensors.
+     *
+     * This operation reshapes the batch dimension (dimension 0) into M + 1
+     * dimensions of shape block_shape + [batch], interleaves these blocks back
+     * into the grid defined by the spatial dimensions [1, ..., M], to obtain a
+     * result with the same rank as the input.
+     *
+     * This is the reverse of SpaceToBatch.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be reshaped
+     * * 1: A 1-D Tensor of {@link OperandType::TENSOR_INT32}, the block
+     *      sizes for each spatial dimension of the input tensor. All values
+     *      must be >= 1.
+     * * 2: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 28.
+     */
+    BATCH_TO_SPACE_ND = @1.1::OperationType:BATCH_TO_SPACE_ND,
+
+    /**
+     * Element-wise division of two tensors.
+     *
+     * Takes two input tensors of identical {@link OperandType} and compatible
+     * dimensions. The output is the result of dividing the first input tensor
+     * by the second, optionally modified by an activation function.
+     *
+     * Two dimensions are compatible when:
+     *     1. they are equal, or
+     *     2. one of them is 1
+     *
+     * The size of the output is the maximum size along each dimension of the
+     * input operands. It starts with the trailing dimensions, and works its way
+     * forward.
+     *
+     * Example:
+     *     input1.dimension =    {4, 1, 2}
+     *     input2.dimension = {5, 4, 3, 1}
+     *     output.dimension = {5, 4, 3, 2}
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the first input.
+     * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+     *      as input0.
+     * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 28.
+     */
+    DIV = @1.1::OperationType:DIV,
+
+    /**
+     * Computes the mean of elements across dimensions of a tensor.
+     *
+     * Reduces the input tensor along the given dimensions to reduce. Unless
+     * keep_dims is true, the rank of the tensor is reduced by 1 for each entry
+     * in axis. If keep_dims is true, the reduced dimensions are retained with
+     * length 1.
+     *
+     * If dimensions to reduce have no entries, all dimensions are reduced, and
+     * a tensor with a single element is returned.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     * * 1: A 1-D Tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. If None (the default), reduces all dimensions. Must be in
+     *      the range [-rank(input_tensor), rank(input_tensor)).
+     * * 2: An {@link OperandType::INT32} scalar, keep_dims. If positive,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 28.
+     */
+    MEAN = @1.1::OperationType:MEAN,
+
+    /**
+     * Pads a tensor with zeros.
+     *
+     * This operation pads a tensor according to the specified paddings.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be padded.
+     * * 1: A 2-D Tensor of {@link OperandType::TENSOR_INT32}, the paddings
+     *      for each spatial dimension of the input tensor. The shape of the
+     *      tensor must be {rank(input0), 2}.
+     *      padding[i, 0] specifies the number of elements to be padded in the
+     *      front of dimension i.
+     *      padding[i, 1] specifies the number of elements to be padded after the
+     *      end of dimension i.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0. The
+     *      output tensor has the same rank as input0, and each
+     *      dimension of the output tensor has the same size as the
+     *      corresponding dimension of the input tensor plus the size
+     *      of the padding:
+     *          output0.dimension[i] =
+     *              padding[i, 0] + input0.dimension[i] + padding[i, 1]
+     *
+     * Available since API level 28.
+     */
+    PAD = @1.1::OperationType:PAD,
+
+    /**
+     * SpaceToBatch for N-Dimensional tensors.
+     *
+     * This operation divides "spatial" dimensions [1, ..., M] of the input into
+     * a grid of blocks of shape block_shape, and interleaves these blocks with
+     * the "batch" dimension (0) such that in the output, the spatial dimensions
+     * [1, ..., M] correspond to the position within the grid, and the batch
+     * dimension combines both the position within a spatial block and the
+     * original batch position. Prior to division into blocks, the spatial
+     * dimensions of the input are optionally zero padded according to paddings.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the input.
+     * * 1: A 1-D Tensor of {@link OperandType::TENSOR_INT32}, the block
+     *      sizes for each spatial dimension of the input tensor. All values
+     *      must be >= 1.
+     * * 2: A 2-D Tensor of {@link OperandType::TENSOR_INT32}, the paddings
+     *      for each spatial dimension of the input tensor. All values must be
+     *      >= 0. The shape of the tensor must be {rank(input0), 2}.
+     *      padding[i, 0] specifies the number of element to be padded in the
+     *      front of dimension i.
+     *      padding[i, 1] specifies the number of element to be padded after the
+     *      end of dimension i.
+     * * 3: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 28.
+     */
+    SPACE_TO_BATCH_ND = @1.1::OperationType:SPACE_TO_BATCH_ND,
+
+    /**
+     * Removes dimensions of size 1 from the shape of a tensor.
+     *
+     * Given a tensor input, this operation returns a tensor of the same
+     * {@link OperandType} with all dimensions of size 1 removed. If you don't
+     * want to remove all size 1 dimensions, you can remove specific size 1
+     * dimensions by specifying the axes (input1).
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, the tensor to be squeezed.
+     * * 1: An optional 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+     *      dimensions to squeeze. If specified only squeezes the dimensions
+     *      listed. Otherwise, squeezes all dimensions. The dimension index
+     *      starts at 0. An error must be reported if squeezing a dimension that
+     *      is not 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0. Contains the
+     *      same data as input, but has one or more dimensions of size 1
+     *      removed.
+     *
+     * Available since API level 28.
+     */
+    SQUEEZE = @1.1::OperationType:SQUEEZE,
+
+    /**
+     * Extracts a strided slice of a tensor.
+     *
+     * Roughly speaking, this op extracts a slice of size (end - begin) / stride
+     * from the given input tensor. Starting at the location specified by begin
+     * the slice continues by adding stride to the index until all dimensions
+     * are not less than end. Note that a stride can be negative, which causes a
+     * reverse slice.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be sliced.
+     * * 1: begin, a 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+     *      starts of the dimensions of the input tensor to be sliced. The
+     *      length must be of rank(input0).
+     * * 2: end, a 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+     *      ends of the dimensions of the input tensor to be sliced. The length
+     *      must be of rank(input0).
+     * * 3: strides, a 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+     *      strides of the dimensions of the input tensor to be sliced. The
+     *      length must be of rank(input0). The entries must be non-zero.
+     * * 4: begin_mask, an {@link OperandType::INT32} scalar. If the ith bit
+     *      of begin_mask is set, begin[i] is ignored and the fullest possible
+     *      range in that dimension is used instead.
+     * * 5: end_mask, an {@link OperandType::INT32} scalar. If the ith bit of
+     *      end_mask is set, end[i] is ignored and the fullest possible range in
+     *      that dimension is used instead.
+     * * 6: shrink_axis_mask, an {@link OperandType::INT32} scalar. If the
+     *      ith bit of shrink_axis_mask is set, the ith dimension specification
+     *      shrinks the dimensionality by 1, taking on the value at index
+     *      begin[i]. In this case, the ith specification must define a
+     *      slice of size 1, e.g. begin[i] = x, end[i] = x + 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0 and rank (n - k),
+     *      where k is the number of bits set in shrink_axis_mask.
+     *
+     * Available since API level 28.
+     */
+    STRIDED_SLICE = @1.1::OperationType:STRIDED_SLICE,
+
+    /**
+     * Element-wise subtraction of two tensors.
+     *
+     * Takes two input tensors of identical {@link OperandType} and compatible
+     * dimensions. The output is the result of subtracting the second input
+     * tensor from the first one, optionally modified by an activation function.
+     *
+     * Two dimensions are compatible when:
+     *     1. they are equal, or
+     *     2. one of them is 1
+     *
+     * The size of the output is the maximum size along each dimension of the
+     * input operands. It starts with the trailing dimensions, and works its way
+     * forward.
+     *
+     * Example:
+     *     input1.dimension =    {4, 1, 2}
+     *     input2.dimension = {5, 4, 3, 1}
+     *     output.dimension = {5, 4, 3, 2}
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since API level 29)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the first input.
+     * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+     *      as input0.
+     * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 28.
+     */
+    SUB = @1.1::OperationType:SUB,
+
+    /**
+     * Transposes the input tensor, permuting the dimensions according to the
+     * perm tensor.
+     *
+     * The returned tensor's dimension i corresponds to the input dimension
+     * perm[i]. If perm is not given, it is set to (n-1...0), where n is the
+     * rank of the input tensor. Hence by default, this operation performs a
+     * regular matrix transpose on 2-D input Tensors.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be transposed.
+     * * 1: An optional 1-D Tensor of {@link OperandType::TENSOR_INT32},
+     *      the permutation of the dimensions of the input tensor.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 28.
+     */
+    TRANSPOSE = @1.1::OperationType:TRANSPOSE,
+
+    /**
+     * Computes the absolute value of a tensor, element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 29.
+     */
     ABS = 38,
+
+    /**
+     * Returns the index of the largest element along an axis.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor specifying the input. Must be non-empty.
+     * * 1: An {@link OperandType::INT32} scalar specifying the axis to
+     *      reduce across. Negative index is used to specify axis from the
+     *      end (e.g. -1 for the last axis). Must be in the range [-n, n).
+     *
+     * Outputs:
+     * * 0: An (n - 1)-D {@link OperandType::TENSOR_INT32} tensor.
+     *
+     * Available since API level 29.
+     */
+    // There is no underscore in ARG_MAX to avoid name conflict with
+    // the macro defined in libc/kernel/uapi/linux/limits.h.
     ARGMAX = 39,
-    ARGMIN = 40,
+
+    /**
+     * Returns the index of the smallest element along an axis.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor specifying the input. Must be non-empty.
+     * * 1: An {@link OperandType::INT32} scalar specifying the axis to
+     *      reduce across. Negative index is used to specify axis from the
+     *      end (e.g. -1 for the last axis). Must be in the range [-n, n).
+     *
+     * Outputs:
+     * * 0: An (n - 1)-D {@link OperandType::TENSOR_INT32} tensor.
+     *
+     * Available since API level 29.
+     */
+    ARGMIN = 40,  // See ARGMAX for naming discussion.
+
+    /**
+     * Transform axis-aligned bounding box proposals using bounding box deltas.
+     *
+     * Given the positions of bounding box proposals and the corresponding
+     * bounding box deltas for each class, return the refined bounding box
+     * regions. The resulting bounding boxes are cliped against the edges of
+     * the image.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT16_ASYMM}
+     *
+     * Inputs:
+     * * 0: A 2-D Tensor of shape [num_rois, 4], specifying the locations of the
+     *      bounding box proposals, each line with format [x1, y1, x2, y2].
+     *      For tensor of type {@link OperandType::TENSOR_QUANT16_ASYMM},
+     *      the zeroPoint must be 0 and the scale must be 0.125.
+     * * 1: A 2-D Tensor of shape [num_rois, num_classes * 4], specifying the
+     *      bounding box delta for each region of interest and each class. The
+     *      bounding box deltas are organized in the following order
+     *      [dx, dy, dw, dh], where dx and dy is the relative correction factor
+     *      for the center position of the bounding box with respect to the width
+     *      and height, dw and dh is the log-scale relative correction factor
+     *      for the width and height. For input0 of type
+     *      {@link OperandType::TENSOR_QUANT16_ASYMM}, this tensor should be
+     *      of {@link OperandType::TENSOR_QUANT8_ASYMM}.
+     * * 2: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_rois], specifying the batch index of each box. Boxes with
+     *      the same batch index are grouped together.
+     * * 3: A 2-D Tensor of shape [batches, 2], specifying the information of
+     *      each image in the batch, each line with format
+     *      [image_height, image_width].
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0, with shape
+     *      [num_rois, num_classes * 4], specifying the coordinates of each
+     *      output bounding box for each class, with format [x1, y1, x2, y2].
+     *
+     * Available since API level 29.
+     */
     AXIS_ALIGNED_BBOX_TRANSFORM = 41,
+
+    /**
+     * Performs a forward LSTM on the input followed by a backward LSTM.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: 3, either time-major or batch-major.
+     *
+     * All input and output tensors must be of the same type.
+     *
+     *
+     * Inputs:
+     * * 0: The input.
+     *      A 3-D tensor of shape:
+     *        If time-major: [max_time, batch_size, output_size]
+     *        If batch-major: [batch_size, max_time, output_size]
+     *      where "max_time" is the number of timesteps (sequence length),
+     *      "batch_size" corresponds to the batching dimension, and
+     *      "input_size" is the size of the input.
+     * * 1: The forward input-to-input weights. Optional.
+     *      A 2-D tensor of shape [num_units, input_size], where “num_units”
+     *      corresponds to the number of cell units.
+     * * 2: The forward input-to-forget weights.
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 3: The forward input-to-cell weights.
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 4: The forward input-to-output weights.
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 5: The forward recurrent-to-input weights. Optional.
+     *      A 2-D tensor of shape [num_units, output_size], where “output_size”
+     *      corresponds to either the number of cell units (i.e., “num_units”),
+     *      or the second dimension of the “projection_weights”, if defined.
+     * * 6: The forward recurrent-to-forget weights.
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 7: The forward recurrent-to-cell weights.
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 8: The forward recurrent-to-output weights.
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 9: The forward cell-to-input weights. Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 10: The forward cell-to-forget weights. Optional.
+     *       A 1-D tensor of shape [num_units].
+     * * 11: The forward cell-to-output weights. Optional.
+     *       A 1-D tensor of shape [num_units].
+     * * 12: The forward input gate bias. Optional.
+     *       A 1-D tensor of shape [num_units].
+     * * 13: The forward forget gate bias.
+     *       A 1-D tensor of shape [num_units].
+     * * 14: The forward cell gate bias.
+     *       A 1-D tensor of shape [num_units].
+     * * 15: The forward output gate bias.
+     *       A 1-D tensor of shape [num_units].
+     * * 16: The forward projection weights. Optional.
+     *       A 2-D tensor of shape [output_size, num_units].
+     * * 17: The forward projection bias. Optional.
+     *       A 1-D tensor of shape [output_size].
+     * * 18: The backward input-to-input weights. Optional.
+     *       A 2-D tensor of shape [num_units, input_size], where “num_units”
+     *       corresponds to the number of cell units.
+     * * 19: The backward input-to-forget weights.
+     *       A 2-D tensor of shape [num_units, input_size].
+     * * 20: The backward input-to-cell weights.
+     *       A 2-D tensor of shape [num_units, input_size].
+     * * 21: The backward input-to-output weights.
+     *       A 2-D tensor of shape [num_units, input_size].
+     * * 22: The backward recurrent-to-input weights. Optional.
+     *       A 2-D tensor of shape [num_units, output_size], where “output_size”
+     *       corresponds to either the number of cell units (i.e., “num_units”),
+     *       or the second dimension of the “projection_weights”, if defined.
+     * * 23: The backward recurrent-to-forget weights.
+     *       A 2-D tensor of shape [num_units, output_size].
+     * * 24: The backward recurrent-to-cell weights.
+     *       A 2-D tensor of shape [num_units, output_size].
+     * * 25: The backward recurrent-to-output weights.
+     *       A 2-D tensor of shape [num_units, output_size].
+     * * 26: The backward cell-to-input weights. Optional.
+     *       A 1-D tensor of shape [num_units].
+     * * 27: The backward cell-to-forget weights. Optional.
+     *       A 1-D tensor of shape [num_units].
+     * * 28: The backward cell-to-output weights. Optional.
+     *       A 1-D tensor of shape [num_units].
+     * * 29: The backward input gate bias. Optional.
+     *       A 1-D tensor of shape [num_units].
+     * * 30: The backward forget gate bias.
+     *       A 1-D tensor of shape [num_units].
+     * * 31: The backward cell gate bias.
+     *       A 1-D tensor of shape [num_units].
+     * * 32: The backward output gate bias.
+     *       A 1-D tensor of shape [num_units].
+     * * 33: The backward projection weights. Optional.
+     *       A 2-D tensor of shape [output_size, num_units].
+     * * 34: The backward projection bias. Optional.
+     *       A 1-D tensor of shape [output_size].
+     * * 35: The forward input activation state.
+     *       A 2-D tensor of shape [batch_size, output_size].
+     * * 36: The forward input cell state.
+     *       A 2-D tensor of shape [batch_size, num_units].
+     * * 37: The backward input activation state.
+     *       A 2-D tensor of shape [batch_size, output_size].
+     * * 38: The backward input cell state.
+     *       A 2-D tensor of shape [batch_size, num_units].
+     * * 39: The auxiliary input. Optional.
+     *       A 3-D tensor of shape [max_time, batch_size, input_size], where “batch_size”
+     *       corresponds to the batching dimension, and “input_size” is the size
+     *       of the input.
+     * * 40: The forward auxiliary input-to-input weights. Optional.
+     *       A 2-D tensor of shape [num_units, input_size].
+     * * 41: The forward auxiliary input-to-forget weights. Optional.
+     *       A 2-D tensor of shape [num_units, input_size].
+     * * 42: The forward auxiliary input-to-cell weights. Optional.
+     *       A 2-D tensor of shape [num_units, input_size].
+     * * 43: The forward auxiliary input-to-output weights. Optional.
+     *       A 2-D tensor of shape [num_units, input_size].
+     * * 44: The backward auxiliary input-to-input weights. Optional.
+     *       A 2-D tensor of shape [num_units, input_size].
+     * * 45: The backward auxiliary input-to-forget weights. Optional.
+     *       A 2-D tensor of shape [num_units, input_size].
+     * * 46: The backward auxiliary input-to-cell weights. Optional.
+     *       A 2-D tensor of shape [num_units, input_size].
+     * * 47: The backward auxiliary input-to-output weights. Optional.
+     *       A 2-D tensor of shape [num_units, input_size].
+     * * 48: The activation function.
+     *       A value indicating the activation function:
+     *       <ul>
+     *       <li>0: None;
+     *       <li>1: Relu;
+     *       <li>3: Relu6;
+     *       <li>4: Tanh;
+     *       <li>6: Sigmoid.
+     *       </ul>
+     * * 49: The clipping threshold for the cell state, such
+     *       that values are bound within [-cell_clip, cell_clip]. If set to 0.0
+     *       then clipping is disabled.
+     *       If all the input tensors have type {@link OperandType::TENSOR_FLOAT32},
+     *       this scalar must be of the type {@link OperandType::FLOAT32},
+     *       otherwise if all the input tensors have the type {@link
+     *       TENSOR_FLOAT16}, this scalar must be of type {@link
+     *       FLOAT16}.
+     * * 50: The clipping threshold for the output from the
+     *       projection layer, such that values are bound within
+     *       [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+     *       If all the input tensors have type {@link OperandType::TENSOR_FLOAT32},
+     *       this scalar must be of the type {@link OperandType::FLOAT32},
+     *       otherwise if all the input tensors have the type {@link
+     *       TENSOR_FLOAT16}, this scalar must be of type {@link
+     *       FLOAT16}.
+     * * 51: merge_outputs
+     *       An {@link OperandType::BOOL} scalar specifying if the outputs
+     *       from forward and backward cells should be merged.
+     * * 52: time_major
+     *       An {@link OperandType::BOOL} scalar specifying the shape format
+     *       of input and output tensors.
+     *
+     * Outputs:
+     * * 0: The forward output.
+     *      A 3-D tensor of shape:
+     *        If time-major: [max_time, batch_size, output_size]
+     *        If batch-major: [batch_size, max_time, output_size]
+     * * 1: The backward output.  Unused if merge_outputs is true.
+     *      A 3-D tensor of shape:
+     *        If time-major: [max_time, batch_size, output_size]
+     *        If batch-major: [batch_size, max_time, output_size]
+     *
+     * Available since API level 29.
+     */
     BIDIRECTIONAL_SEQUENCE_LSTM = 42,
+
+    /**
+     * A recurrent neural network layer that applies a basic RNN cell to a
+     * sequence of inputs in forward and backward directions.
+     *
+     * This Op unrolls the input along the sequence dimension, and implements
+     * the following operation for each element in the sequence s =
+     * 1...sequence_length:
+     *   fw_outputs[s] = fw_state = activation(inputs[s] * fw_input_weights’ +
+     *          fw_state * fw_recurrent_weights’ + fw_bias)
+     *
+     * And for each element in sequence t = sequence_length : 1
+     *   bw_outputs[t] = bw_state = activation(inputs[t] * bw_input_weights’ +
+     *          bw_state * bw_recurrent_weights’ + bw_bias)
+     *
+     * Where:
+     * * “{fw,bw}_input_weights” is a weight matrix that multiplies the inputs;
+     * * “{fw,bw}_recurrent_weights” is a weight matrix that multiplies the
+     *    current “state” which itself is the output from the previous time step
+     *    computation;
+     * * “{fw,bw}_bias” is a bias vector (added to each output vector in the
+     *    batch);
+     * * “activation” is the function passed as the “fused_activation_function”
+     *   argument (if not “NONE”).
+     *
+     * The op also supports an auxiliary input. Regular cell feeds one input
+     * into the two RNN cells in the following way:
+     *
+     *       INPUT  (INPUT_REVERSED)
+     *         |         |
+     *    ---------------------
+     *    | FW_RNN     BW_RNN |
+     *    ---------------------
+     *         |         |
+     *      FW_OUT     BW_OUT
+     *
+     * An op with an auxiliary input takes two inputs and feeds them into the
+     * RNN cells in the following way:
+     *
+     *       AUX_INPUT   (AUX_INPUT_REVERSED)
+     *           |             |
+     *     INPUT | (INPUT_R'D.)|
+     *       |   |       |     |
+     *    -----------------------
+     *    |  \  /        \    / |
+     *    | FW_RNN       BW_RNN |
+     *    -----------------------
+     *         |           |
+     *      FW_OUT      BW_OUT
+     *
+     * While stacking this op on top of itself, this allows to connect both
+     * forward and backward outputs from previous cell to the next cell's
+     * inputs.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * The input tensors must all be the same type.
+     *
+     * Inputs:
+     * * 0: input.
+     *      A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+     *      it is set to true, then the input has a shape [maxTime, batchSize,
+     *      inputSize], otherwise the input has a shape [batchSize, maxTime,
+     *      inputSize].
+     * * 1: fwWeights.
+     *      A 2-D tensor of shape [fwNumUnits, inputSize].
+     * * 2: fwRecurrentWeights.
+     *      A 2-D tensor of shape [fwNumUnits, fwNumUnits].
+     * * 3: fwBias.
+     *      A 1-D tensor of shape [fwNumUnits].
+     * * 4: fwHiddenState.
+     *      A 2-D tensor of shape [batchSize, fwNumUnits]. Specifies a hidden
+     *      state input for the first time step of the computation.
+     * * 5: bwWeights.
+     *      A 2-D tensor of shape [bwNumUnits, inputSize].
+     * * 6: bwRecurrentWeights.
+     *      A 2-D tensor of shape [bwNumUnits, bwNumUnits].
+     * * 7: bwBias.
+     *      A 1-D tensor of shape [bwNumUnits].
+     * * 8: bwHiddenState
+     *      A 2-D tensor of shape [batchSize, bwNumUnits]. Specifies a hidden
+     *      state input for the first time step of the computation.
+     * * 9: auxInput.
+     *      A 3-D tensor. The shape is the same as of the input 0.
+     * * 10:fwAuxWeights.
+     *      A 2-D tensor of shape [fwNumUnits, inputSize].
+     * * 11:bwAuxWeights.
+     *      A 2-D tensor of shape [bwNumUnits, inputSize].
+     * * 12:fusedActivationFunction.
+     *      A {@link FusedActivationFunc} value indicating the activation function. If
+     *      “NONE” is specified then it results in a linear activation.
+     * * 13:timeMajor
+     *      An {@link OperandType::BOOL} scalar specifying the shape format
+     *      of input and output tensors.
+     * * 14:mergeOutputs
+     *      An {@link OperandType::BOOL} scalar specifying if the outputs
+     *      from forward and backward cells are separate (if set to false) or
+     *      concatenated (if set to true).
+     * Outputs:
+     * * 0: fwOutput.
+     *      A 3-D tensor. The first two dimensions of the shape are defined by
+     *      the input 6 (timeMajor) and the third dimension is defined by the
+     *      input 14 (mergeOutputs). If timeMajor is set to true, then the first
+     *      two dimensions are [maxTime, batchSize], otherwise they are set to
+     *      [batchSize, maxTime]. If mergeOutputs is set to true, then the third
+     *      dimension is equal to (fwNumUnits + bwNumUnits), otherwise it is set
+     *      to fwNumUnits.
+     * * 1: bwOutput.
+     *      A 3-D tensor. If the input 14 (mergeOutputs) is set to true, then
+     *      this tensor is not produced. The shape is defined by the input 6
+     *      (timeMajor). If it is set to true, then the shape is set to
+     *      [maxTime, batchSize, bwNumUnits], otherwise the shape is set to
+     *      [batchSize, maxTime, bwNumUnits].
+     *
+     * Available since API level 29.
+     */
     BIDIRECTIONAL_SEQUENCE_RNN = 43,
+
+    /**
+     * Greedily selects a subset of bounding boxes in descending order of score.
+     *
+     * This op applies hard NMS algorithm to each class. In each loop of
+     * execution, the box with maximum score gets selected, and any boxes with
+     * the intersection-over-union (IOU) greater than a threshold are removed
+     * from the pending set.
+     *
+     * Axis-aligned bounding boxes are represented by its upper-left corner
+     * coordinate (x1,y1) and lower-right corner coordinate (x2,y2). A valid
+     * bounding box should satisfy x1 <= x2 and y1 <= y2.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Inputs:
+     * * 0: A 2-D Tensor of shape [num_rois, num_classes], specifying the score
+     *      of each bounding box proposal. The boxes are grouped by batches in the
+     *      first dimension.
+     * * 1: A 2-D Tensor specifying the bounding boxes of shape
+     *      [num_rois, num_classes * 4], organized in the order [x1, y1, x2, y2].
+     *      The boxes are grouped by batches in the first dimension. The sequential
+     *      order of the boxes corresponds with input0. For input0 of type
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, this tensor should be of
+     *      {@link OperandType::TENSOR_QUANT16_ASYMM}, with zeroPoint of 0 and
+     *      scale of 0.125.
+     * * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_rois], specifying the batch index of each box. Boxes with
+     *      the same batch index are grouped together.
+     * * 3: An {@link OperandType::FLOAT32} scalar, score_threshold. Boxes
+     *      with scores lower than the threshold are filtered before sending
+     *      to the NMS algorithm.
+     * * 4: An {@link OperandType::FLOAT32} scalar, specifying the IoU
+     *      threshold.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the maximum
+     *      number of selected bounding boxes for each image. Set to a negative
+     *      value for unlimited number of output bounding boxes.
+     *
+     * Outputs:
+     * * 0: A 1-D Tensor of the same {@link OperandType} as input0, with shape
+     *      [num_output_rois], specifying the score of each output box. The boxes
+     *      are grouped by batches, but the sequential order in each batch is not
+     *      guaranteed. For type of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the scale and zero point must be the same as input0.
+     * * 1: A 2-D Tensor of the same {@link OperandType} as input1, with shape
+     *      [num_output_rois, 4], specifying the coordinates of each
+     *      output bounding box with the same format as input1. The sequential
+     *      order of the boxes corresponds with output0. For type of
+     *      {@link OperandType::TENSOR_QUANT16_ASYMM}, the scale must be
+     *      0.125 and the zero point must be 0.
+     * * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_output_rois], specifying the class of each output box. The
+     *      sequential order of the boxes corresponds with output0.
+     * * 3: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_rois], specifying the batch index of each box. Boxes with
+     *      the same batch index are grouped together.
+     *
+     * Available since API level 29.
+     */
     BOX_WITH_NMS_LIMIT = 44,
+
+    /**
+     * Casts a tensor to a new type.
+     *
+     * This operation ignores the scale and zeroPoint of quanized tensors,
+     * e.g. it treats a {@link OperandType::TENSOR_QUANT8_ASYMM} input
+     * as a tensor of uint8 values.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: A tensor with the same shape as input0.
+     *
+     * Available since API level 29.
+     */
     CAST = 45,
+
+    /**
+     * Shuffle the channels of the input tensor.
+     *
+     * Given an input tensor and a integer value of num_groups, CHANNEL_SHUFFLE
+     * divide the channel dimension into num_groups groups, and reorganize the
+     * channels by grouping channels with the same index in each group.
+     *
+     * Along the channel dimension, the output is calculated using this formula:
+     *
+     *     output_channel[k * num_groups + g] = input_channel[g * group_size + k]
+     *
+     * where group_size = num_channels / num_groups
+     *
+     * The number of channels must be divisible by num_groups.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be shuffled.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the number of
+     *      groups.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the dimension
+     *      channel shuffle would be performed on. Negative index is used to
+     *      specify axis from the end (e.g. -1 for the last axis). Must be in
+     *      the range [-n, n).
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} and same shape as input0.
+     *
+     * Available since API level 29.
+     */
     CHANNEL_SHUFFLE = 46,
+
+    /**
+     * Apply postprocessing steps to bounding box detections.
+     *
+     * Bounding box detections are generated by applying transformation on a set
+     * of predefined anchors with the bounding box deltas from bounding box
+     * regression. A final step of hard NMS is applied to limit the number of
+     * returned boxes.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Inputs:
+     * * 0: A 3-D Tensor of shape [batches, num_anchors, num_classes], specifying
+     *      the score of each anchor with each class. Class 0 for each
+     *      [batches, num_anchors, 0] is background and will be ignored.
+     * * 1: A 3-D Tensor of shape [batches, num_anchors, length_box_encoding], with
+     *      the first four values in length_box_encoding specifying the bounding
+     *      box deltas. The box deltas are encoded in the order of [dy, dx, dh, dw],
+     *      where dy and dx is the linear-scale relative correction factor for the
+     *      center position of the bounding box with respect to the width and height,
+     *      dh and dw is the log-scale relative correction factor for the width and
+     *      height. All the entries in length_box_encoding beyond the first four
+     *      values are ignored in this operation.
+     * * 2: A 2-D Tensor of shape [num_anchors, 4], specifying the shape of each
+     *      predefined anchor, with format [ctr_y, ctr_x, h, w], where ctr_y and
+     *      ctr_x are the center position of the box, and h and w are the height
+     *      and the width.
+     * * 3: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+     *      factor for dy in bounding box deltas.
+     * * 4: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+     *      factor for dx in bounding box deltas.
+     * * 5: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+     *      factor for dh in bounding box deltas.
+     * * 6: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+     *      factor for dw in bounding box deltas.
+     * * 7: An {@link OperandType::BOOL} scalar, set to true to use regular
+     *      multi-class NMS algorithm that do NMS separately for each class,
+     *      set to false for a faster algorithm that only do one single NMS
+     *      using the highest class score..
+     * * 8: An {@link OperandType::INT32} scalar, max_num_detections, specifying
+     *      the maximum number of boxes for the output. Boxes with the lowest
+     *      scores are discarded to meet the limit.
+     * * 9: An {@link OperandType::INT32} scalar, only used when input7 is
+     *      set to false, specifying the maximum number of classes per detection.
+     * * 10: An {@link OperandType::INT32} scalar, only used when input7 is
+     *       set to true, specifying the maximum number of detections when
+     *       applying NMS algorithm for each single class.
+     * * 11: An {@link OperandType::FLOAT32} scalar, score_threshold. Boxes
+     *       with scores lower than the threshold are filtered before sending
+     *       to the NMS algorithm.
+     * * 12: An {@link OperandType::FLOAT32} scalar, specifying the IoU
+     *       threshold for hard NMS.
+     * * 13: An {@link OperandType::BOOL} scalar, set to true to include
+     *       background class in the list of label map for the output, set
+     *       to false to not include the background. When the background
+     *       class is included, it has label 0 and the output classes start
+     *       at 1 in the label map, otherwise, the output classes start at 0.
+     *
+     * Outputs:
+     * * 0: A 2-D tensor of the same {@link OperandType} as input0, with shape
+     *      [batches, max_num_detections], specifying the score of each output
+     *      detections.
+     * * 1: A 3-D tensor of shape [batches, max_num_detections, 4], specifying the
+     *      coordinates of each output bounding box, with format
+     *      [y1, x1, y2, x2].
+     * * 2: A 2-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [batches, max_num_detections], specifying the class label for each
+     *      output detection.
+     * * 3: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape [batches],
+     *      specifying the number of valid output detections for each batch.
+     *
+     * Available since API level 29.
+     */
     DETECTION_POSTPROCESSING = 47,
+
+    /**
+     * For input tensors x and y, computes x == y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     *
+     * Available since API level 29.
+     */
     EQUAL = 48,
+
+    /**
+     * Computes exponential of x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 29.
+     */
     EXP = 49,
+
+    /**
+     * Inserts a dimension of 1 into a tensor's shape.
+     *
+     * Given a tensor input, this operation inserts a dimension of 1 at the
+     * given dimension index of input's shape. The dimension index starts at
+     * zero; if you specify a negative dimension index, it is counted backward
+     * from the end.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: An {@link OperandType::INT32} scalar specifying the dimension
+     *      index to expand. Must be in the range [-(n + 1), (n + 1)).
+     *
+     * Outputs:
+     * * 0: An (n + 1)-D tensor with the same {@link OperandType} and data as
+     *      input0.
+     *
+     * Available since API level 29.
+     */
     EXPAND_DIMS = 50,
+
+    /**
+     * Gathers values along an axis.
+     *
+     * Produces an output tensor with shape
+     *     input0.dimension[:axis] + indices.dimension + input0.dimension[axis + 1:]
+     * where:
+     *     # Vector indices (output is rank(input0)).
+     *     output[a_0, ..., a_n, i, b_0, ..., b_n] =
+     *       input0[a_0, ..., a_n, indices[i], b_0, ..., b_n]
+     *
+     *     # Higher rank indices (output is rank(input0) + rank(indices) - 1).
+     *     output[a_0, ..., a_n, i, ..., j, b_0, ... b_n] =
+     *       input0[a_0, ..., a_n, indices[i, ..., j], b_0, ..., b_n]
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor from which to gather values.
+     * * 1: An {@link OperandType::INT32} scalar specifying the axis.
+     *      Negative index is used to specify axis from the end
+     *      (e.g. -1 for the last axis). Must be in the range [-n, n).
+     * * 2: A k-D tensor {@link OperandType::TENSOR_INT32} of indices.
+     *      The values must be in the bounds of the corresponding dimensions
+     *      of input0.
+     *
+     * Outputs:
+     * * 0: An (n + k - 1)-D tensor with the same {@link OperandType} as input0.
+     *
+     * Available since API level 29.
+     */
     GATHER = 51,
+
+    /**
+     * Generate aixs-aligned bounding box proposals.
+     *
+     * Bounding box proposals are generated by applying transformation on a set
+     * of predefined anchors with the bounding box deltas from bounding box
+     * regression. A final step of hard NMS is applied to limit the number of
+     * returned boxes.
+     *
+     * Axis-aligned bounding boxes are represented by its upper-left corner
+     * coordinate (x1,y1) and lower-right corner coordinate (x2,y2). A valid
+     * bounding box should satisfy x1 <= x2 and y1 <= y2.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Inputs:
+     * * 0: A 4-D Tensor specifying the score of each anchor at each
+     *      location. With "NHWC" data layout, the tensor shape is
+     *      [batches, height, width, num_anchors]. With "NCHW" data layout,
+     *      the tensor shape is [batches, num_anchors, height, width].
+     * * 1: A 4-D Tensor specifying the bounding box deltas. With "NHWC" data
+     *      layout, the tensor shape is [batches, height, width, num_anchors * 4].
+     *      With "NCHW" data layout, the tensor shape is
+     *      [batches, num_anchors * 4, height, width]. The box deltas are encoded
+     *      in the order of [dx, dy, dw, dh], where dx and dy is the linear-scale
+     *      relative correction factor for the center position of the bounding box
+     *      with respect to the width and height, dw and dh is the log-scale
+     *      relative correction factor for the width and height. The last
+     *      dimensions is the channel dimension.
+     * * 2: A 2-D Tensor of shape [num_anchors, 4], specifying the shape of each
+     *      predefined anchor, with format [x1, y1, x2, y2]. For input0 of type
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, this tensor should be of
+     *      {@link OperandType::TENSOR_QUANT16_SYMM}, with scale of 0.125.
+     * * 3: A 2-D Tensor of shape [batches, 2], specifying the size of
+     *      each image in the batch, with format [image_height, image_width].
+     *      For input0 of type {@link OperandType::TENSOR_QUANT8_ASYMM}, this
+     *      tensor should be of {@link OperandType::TENSOR_QUANT16_SYMM}, with
+     *      scale of 0.125.
+     * * 4: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the height of original image to the height of feature map.
+     * * 5: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the width of original image to the width of feature map.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the maximum
+     *      number of boxes before going into the hard NMS algorithm. Boxes
+     *      with the lowest scores are discarded to meet the limit. Set to
+     *      a non-positive value for unlimited number.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the maximum
+     *      number of boxes returning from the hard NMS algorithm. Boxes
+     *      with the lowest scores are discarded to meet the limit. Set to
+     *      a non-positive value for unlimited number.
+     * * 8: An {@link OperandType::FLOAT32} scalar, specifying the IoU
+     *      threshold for hard NMS.
+     * * 9: An {@link OperandType::FLOAT32} scalar, min_size. Boxes with
+     *      height or width lower than the absolute threshold are filtered out.
+     * * 10: An {@link OperandType::BOOL} scalar, set to true to specify
+     *       NCHW data layout for input0 and input1. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0, of shape
+     *      [num_output_rois], specifying the score of each output box.
+     *      The boxes are grouped by batches, but the sequential order in
+     *      each batch is not guaranteed. For type of
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, the scale and zero
+     *      point must be the same as input0.
+     * * 1: A tensor of the same {@link OperandType} as input3, of shape
+     *      [num_output_rois, 4], specifying the coordinates of each output
+     *      bounding box for each class, with format [x1, y1, x2, y2].
+     *      The sequential order of the boxes corresponds with output0.
+     *      For type of {@link OperandType::TENSOR_QUANT16_ASYMM}, the
+     *      scale must be 0.125 and the zero point must be 0.
+     * * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_rois], specifying the batch index of each box. Boxes with
+     *      the same batch index are grouped together.
+     *
+     * Available since API level 29.
+     */
     GENERATE_PROPOSALS = 52,
+
+    /**
+     * For input tensors x and y, computes x > y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     *
+     * Available since API level 29.
+     */
     GREATER = 53,
+    /**
+     * For input tensors x and y, computes x >= y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     *
+     * Available since API level 29.
+     */
     GREATER_EQUAL = 54,
+
+    /**
+     * Performs a grouped 2-D convolution operation.
+     *
+     * Given an input tensor of shape [batches, height, width, depth_in] and a
+     * filter tensor of shape [depth_out, filter_height, filter_width, depth_group]
+     * containing depth_out convolutional filters of depth depth_group, GROUPED_CONV
+     * applies a group of different filters to each input channel group, then
+     * concatenates the results together.
+     *
+     * Specifically, the input channels are divided into num_groups groups, each with
+     * depth depth_group, i.e. depth_in = num_groups * depth_group. The convolutional
+     * filters are also divided into num_groups groups, i.e. depth_out is divisible
+     * by num_groups. GROUPED_CONV applies each group of filters to the corresponding
+     * input channel group, and the result are concatenated together.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, g * channel_multiplier + q] =
+     *         sum_{di, dj, dk} (
+     *             input[b, strides[1] * i + di, strides[2] * j + dj,
+     *                   g * depth_group + dk] *
+     *             filter[g * channel_multiplier + q, di, dj, dk]
+     *         ) + bias[channel]
+     *
+     * where channel_multiplier = depth_out / num_groups
+     *
+     * Supported tensor {@link OperandType} configurations:
+     * * 32 bit Floating point :
+     * * * {@link OperandType::TENSOR_FLOAT32} for input, filter, output, and bias.
+     *
+     * * 16 bit Floating point:
+     * * {@link OperandType::TENSOR_FLOAT16} for input, filter, output, and bias.
+     *
+     * * Quantized:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * * Quantized with symmetric per channel quantization for the filter:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input, where depth_in = num_groups * depth_group.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_group], specifying
+     *      the filter, where depth_out must be divisible by num_groups.  For
+     *      tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     *      the channel dimension must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+     *      type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+     *      of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+     *      should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+     *      0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 9: An {@link OperandType::INT32} scalar, specifying the number of
+            groups.
+     * * 10: An {@link OperandType::INT32} scalar, and has to be one of the
+     *       {@link FusedActivationFunc} values. Specifies the activation to
+     *       invoke on the result.
+     * * 11: An {@link OperandType::BOOL} scalar, set to true to specify
+     *       NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input, where depth_in = num_groups * depth_group.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_group], specifying
+     *      the filter, where depth_out must be divisible by num_groups.  For
+     *      tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     *      the channel dimension must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+     *      type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+     *      of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+     *      should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+     *      0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the number of
+     *      groups.
+     * * 7: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 8: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth_out]. For output tensor of
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
+     *      must be satisfied: output_scale > input_scale * filter_scale (for
+     *      filter tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     *      this condition must be true for all filter scales).
+     *
+     * Available since API level 29.
+     */
     GROUPED_CONV_2D = 55,
+
+    /**
+     * Localize the maximum keypoints from heatmaps.
+     *
+     * This operation approximates the accurate maximum keypoint scores and
+     * indices after bicubic upscaling by using Taylor expansion up to the
+     * quadratic term.
+     *
+     * The bounding box is represented by its upper-left corner coordinate
+     * (x1,y1) and lower-right corner coordinate (x2,y2) in the original image.
+     * A valid bounding box should satisfy x1 <= x2 and y1 <= y2.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: A 4-D Tensor of shape
+     *      [num_boxes, heatmap_size, heatmap_size, num_keypoints],
+     *      specifying the heatmaps, the height and width of heatmaps should
+     *      be the same, and must be greater than or equal to 2.
+     * * 1: A 2-D Tensor of shape [num_boxes, 4], specifying the bounding boxes,
+     *      each with format [x1, y1, x2, y2]. For input0 of type
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, this tensor should
+     *      be of {@link OperandType::TENSOR_QUANT16_ASYMM}, with zeroPoint
+     *      of 0 and scale of 0.125.
+     * * 2: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0, with shape
+     *      [num_boxes, num_keypoints], specifying score of the keypoints.
+     * * 1: A tensor of the same {@link OperandType} as input1, with shape
+     *      [num_boxes, num_keypoints, 2], specifying the location of
+     *      the keypoints, the second dimension is organized as
+     *      [keypoint_x, keypoint_y].
+     *
+     * Available since API level 29.
+     */
     HEATMAP_MAX_KEYPOINT = 56,
+
+    /**
+     * Applies instance normalization to the input tensor.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, h, w, c] =
+     *         (input[b, h, w, c] - mean[b, c]) * gamma /
+     *         sqrt(var[b, c] + epsilon) + beta
+     *
+     * Where the mean and variance are computed across the spatial dimensions:
+     *
+     *     mean[b, c] =
+     *         sum_{h, w}(input[b, h, w, c]) / sum(1)
+     *
+     *     var[b, c] =
+     *         sum_{h, w}(pow(input[b, h, w, c] - mean[b, c], 2)) / sum(1)
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be normalized.
+     * * 1: An {@link OperandType::FLOAT32} scalar, specifying gamma, the
+     *      scale applied to the normalized tensor.
+     * * 2: An {@link OperandType::FLOAT32} scalar, specifying beta, the
+     *      offset applied to the normalized tensor.
+     * * 3: An {@link OperandType::FLOAT32} scalar, specifying epsilon, the
+     *      small value added to variance to avoid dividing by zero.
+     * * 4: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} and same shape as input0.
+     *
+     * Available since API level 29.
+     */
     INSTANCE_NORMALIZATION = 57,
+
+    /**
+     * For input tensors x and y, computes x < y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     *
+     * Available since API level 29.
+     */
     LESS = 58,
+
+    /**
+     * For input tensors x and y, computes x <= y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     *
+     * Available since API level 29.
+     */
     LESS_EQUAL = 59,
+
+    /**
+     * Computes natural logarithm of x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 29.
+     */
     LOG = 60,
+
+    /**
+     * Returns the truth value of x AND y element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     * * 1: A tensor of {@link OperandType::TENSOR_BOOL8} and dimensions
+     *      compatible with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     *
+     * Available since API level 29.
+     */
     LOGICAL_AND = 61,
+
+    /**
+     * Computes the truth value of NOT x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 29.
+     */
     LOGICAL_NOT = 62,
+
+    /**
+     * Returns the truth value of x OR y element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     * * 1: A tensor of {@link OperandType::TENSOR_BOOL8} and dimensions
+     *      compatible with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     *
+     * Available since API level 29.
+     */
     LOGICAL_OR = 63,
+
+    /**
+     * Computes the log softmax activations given logits.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = logits * beta - log(reduce_sum(exp(logits * beta), axis))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor specifying the input logits.
+     * * 1: An {@link OperandType::FLOAT32} scalar, specifying the positive
+     *      scaling factor for the exponent, beta.
+     * * 2: An {@link OperandType::INT32} scalar specifying the axis to
+     *      reduce across. Negative index is used to specify axis from the
+     *      end (e.g. -1 for the last axis). Must be in the range [-n, n).
+     *
+     * Outputs:
+     * * 0: The output tensor of the same {@link OperandType} and shape as
+     *      input0.
+     *
+     * Available since API level 29.
+     */
     LOG_SOFTMAX = 64,
+
+    /**
+     * Returns the element-wise maximum of two tensors.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and compatible dimensions
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: The sum, a tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 29.
+     */
     MAXIMUM = 65,
+
+    /**
+     * Returns the element-wise minimum of two tensors.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and compatible dimensions
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: The sum, a tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 29.
+     */
     MINIMUM = 66,
+
+    /**
+     * Computes numerical negative value element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 29.
+     */
     NEG = 67,
+
+    /**
+     * For input tensors x and y, computes x != y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     *
+     * Available since API level 29.
+     */
     NOT_EQUAL = 68,
+
+    /**
+     * Pads a tensor with the given constant value according to the specified
+     * paddings.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be padded.
+     * * 1: A 2-D Tensor of {@link OperandType::TENSOR_INT32}, the paddings
+     *      for each spatial dimension of the input tensor. The shape of the
+     *      tensor must be {rank(input0), 2}.
+     *      padding[i, 0] specifies the number of elements to be padded in the
+     *      front of dimension i.
+     *      padding[i, 1] specifies the number of elements to be padded after
+     *      the end of dimension i.
+     * * 2: An scalar specifying the value to use for padding input0.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
+     *      pad value should be of {@link OperandType::FLOAT32}.
+     *      For input tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the pad value should be of {@link OperandType::INT32}. The
+     *      scale and zeroPoint are assumed to be the same as in input0.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0. The
+     *      output tensor has the same rank as input0, and each
+     *      dimension of the output tensor has the same size as the
+     *      corresponding dimension of the input tensor plus the size
+     *      of the padding:
+     *          output0.dimension[i] =
+     *              padding[i, 0] + input0.dimension[i] + padding[i, 1]
+     *
+     * Available since API level 29.
+     */
     PAD_V2 = 69,
+
+    /**
+     * Computes the power of one value to another.
+     *
+     * Given a tensor base and a tensor exponent, this operation computes
+     * base^exponent elementwise.
+     *
+     * This operations supports broadcasting. The size of the output is the
+     * maximum size along each dimension of the input operands. It starts with
+     * the trailing dimensions, and works its way forward.
+     *
+     * For example:
+     *     base.dimension     =    {4, 1, 2}
+     *     exponent.dimension = {5, 4, 3, 1}
+     *     output.dimension   = {5, 4, 3, 2}
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: A tensor specifying the base.
+     * * 1: A tensor specifying the exponent.
+     *
+     * Outputs:
+     * * 0: An output tensor.
+     *
+     * Available since API level 29.
+     */
     POW = 70,
+
+    /**
+     * Parametric Rectified Linear Unit.
+     *
+     * It follows: f(x) = alpha * x for x < 0, f(x) = x for x >= 0, where alpha
+     * is a learned array with the same {@link OperandType} and compatible
+     * dimensions as input x.
+     *
+     * Two dimensions are compatible when:
+     *     1. they are equal, or
+     *     2. one of them is 1
+     *
+     * The size of the output is the maximum size along each dimension of the
+     * input operands. It starts with the trailing dimensions, and works its way
+     * forward.
+     *
+     * Example:
+     *     input.dimension  =    {4, 1, 2}
+     *     alpha.dimension  = {5, 4, 3, 1}
+     *     output.dimension = {5, 4, 3, 2}
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+     *      as input0, specifying the alpha.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 29.
+     */
     PRELU = 71,
+
+    /**
+     * Quantizes the input tensor.
+     *
+     * The formula is:
+     *
+     *     output = max(0, min(255, round(input / scale) + zeroPoint)
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0, but with
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}.
+     *
+     * Available since API level 29.
+     */
     QUANTIZE = 72,
+
+    /**
+     * A version of quantized LSTM, using 16 bit quantization for internal
+     * state.
+     *
+     * There is no projection layer, so cell state size is equal to the output
+     * size.
+     *
+     * Inputs:
+     * * 0: A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [numBatches, inputSize] specifying the input to the LSTM
+     *      cell. Tensor is quantized with a fixed quantization range of
+     *      [-1, 127/128] (scale = 1/128, zeroPoint = 128).
+     * * 1: The input-to-input weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying input-to-input part of
+     *      weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 2: The input-to-forget weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying input-to-forget part of
+     *      weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 3: The input-to-cell weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying input-to-cell part of
+     *      weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 4: The input-to-output weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying input-to-output part of
+     *      weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 5: The recurrent-to-input weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying recurrent-to-input part
+     *      of weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 6: The recurrent-to-forget weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying recurrent-to-forget
+     *      part of weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 7: The recurrent-to-cell weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying recurrent-to-cell part
+     *      of weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 8: The recurrent-to-output weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying recurrent-to-output
+     *      part of weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 9: The input gate bias.
+     *      A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+     *      [outputSize] specifying the bias for the fully-connected layer
+     *      inside the LSTM cell. Bias is quantized with scale being a product
+     *      of input and weights scales and zeroPoint equal to 0.
+     * * 10:The forget gate bias.
+     *      A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+     *      [outputSize] specifying the bias for the fully-connected layer
+     *      inside the LSTM cell. Bias is quantized with scale being a product
+     *      of input and weights scales and zeroPoint equal to 0.
+     * * 11:The cell bias.
+     *      A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+     *      [outputSize] specifying the bias for the fully-connected layer
+     *      inside the LSTM cell. Bias is quantized with scale being a product
+     *      of input and weights scales and zeroPoint equal to 0.
+     * * 12:The output gate bias.
+     *      A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+     *      [outputSize] specifying the bias for the fully-connected layer
+     *      inside the LSTM cell. Bias is quantized with scale being a product
+     *      of input and weights scales and zeroPoint equal to 0.
+     * * 13: A 2-D tensor of type {@link OperandType::TENSOR_QUANT16_SYMM}
+     *       and shape [numBatches, outputSize] specifying the cell state from the
+     *       previous time step of the LSTM cell. It is quantized using a
+     *       quantization range of [-2^4, 2^4 * 32767/32768] (scale = 2^4 /
+     *       32768, zeroPoint = 0).
+     * * 14: A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *       and shape [numBathes, outputSize] specifying the output of the LSTM
+     *       cell from previous time-step. Tensor is quantized with a fixed
+     *       quantization range of [-1, 127/128] (scale = 1/128, zeroPoint =
+     *       128).
+     *
+     *
+     * Outputs:
+     * * 0: A 2-D tensor of type {@link OperandType::TENSOR_QUANT16_SYMM}
+     *      and shape [numBatches, outputSize] which contains a cell state from
+     *      the current time step. Tensor is quantized using a quantization
+     *      range of [-2^4, 2^4 * 32767/32768] (scale = 2^4 / 32768, zeroPoint =
+     *      0).
+     * * 1: A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [numBathes, outputSize] which contains the output value.
+     *      Tensor is quantized with a fixed quantization range of [-1, 127/128]
+     *      (scale = 1/128, zeroPoint = 128).
+     */
     QUANTIZED_16BIT_LSTM = 73,
+
+    /**
+     * Draws samples from a multinomial distribution.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Inputs:
+     * * 0: A 2-D tensor with shape [batches, classes], specifying the
+     *      unnormalized log-probabilities for all classes.
+     * * 1: A scalar {@link OperandType::INT32}, specifying the number of
+     *      independent samples to draw for each row slice.
+     * * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor with shape [2],
+     *      specifying seeds used to initialize the random distribution.
+     * Outputs:
+     * * 0: A 2-D {@link OperandType::TENSOR_INT32} tensor with shape
+     *      [batches, samples], containing the drawn samples.
+     *
+     * Available since API level 29.
+     */
     RANDOM_MULTINOMIAL = 74,
+
+    /**
+     * Reduces a tensor by computing the "logical and" of elements along given
+     * dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 29.
+     */
     REDUCE_ALL = 75,
+
+    /**
+     * Reduces a tensor by computing the "logical or" of elements along given
+     * dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 29.
+     */
     REDUCE_ANY = 76,
+
+    /**
+     * Reduces a tensor by computing the maximum of elements along given
+     * dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 29.
+     */
     REDUCE_MAX = 77,
+
+    /**
+     * Reduces a tensor by computing the minimum of elements along given
+     * dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 29.
+     */
     REDUCE_MIN = 78,
+
+    /**
+     * Reduces a tensor by multiplying elements along given dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 29.
+     */
     REDUCE_PROD = 79,
+
+    /**
+     * Reduces a tensor by summing elements along given dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *
+     * Available since API level 29.
+     */
     REDUCE_SUM = 80,
+
+    /**
+     * Select and scale the feature map of each region of interest to a unified
+     * output size by average pooling sampling points from bilinear interpolation.
+     *
+     * The region of interest is represented by its upper-left corner coordinate
+     * (x1,y1) and lower-right corner coordinate (x2,y2) in the original image.
+     * A spatial scaling factor is applied to map into feature map coordinate.
+     * A valid region of interest should satisfy x1 <= x2 and y1 <= y2.
+     *
+     * No rounding is applied in this operation. The sampling points are unified
+     * distributed in the pooling bin and their values are calculated by bilinear
+     * interpolation.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, specifying the feature map.
+     * * 1: A 2-D Tensor of shape [num_rois, 4], specifying the locations of
+     *      the regions of interest, each line with format [x1, y1, x2, y2].
+     *      For input0 of type {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      this tensor should be of {@link OperandType::TENSOR_QUANT16_ASYMM},
+     *      with zeroPoint of 0 and scale of 0.125.
+     * * 2: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_rois], specifying the batch index of each box. Boxes with
+     *      the same batch index are grouped together.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the output
+     *      height of the output tensor.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the output
+     *      width of the output tensor.
+     * * 5: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the height of original image to the height of feature map.
+     * * 6: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the width of original image to the width of feature map.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the number of
+     *      sampling points in height dimension used to compute the output.
+     *      Set to 0 for adaptive value of ceil(roi_height/out_height).
+     * * 8: An {@link OperandType::INT32} scalar, specifying the number of
+     *      sampling points in width dimension used to compute the output.
+     *      Set to 0 for adaptive value of ceil(roi_width/out_width).
+     * * 9: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0. The output
+     *      shape is [num_rois, out_height, out_width, depth].
+     *
+     * Available since API level 29.
+     */
     ROI_ALIGN = 81,
+
+    /**
+     * Select and scale the feature map of each region of interest to a unified
+     * output size by max-pooling.
+     *
+     * The region of interest is represented by its upper-left corner coordinate
+     * (x1,y1) and lower-right corner coordinate (x2,y2) in the original image.
+     * A spatial scaling factor is applied to map into feature map coordinate.
+     * A valid region of interest should satisfy x1 <= x2 and y1 <= y2.
+     *
+     * Rounding is applied in this operation to ensure integer boundary for
+     * regions of interest and pooling bins.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, specifying the feature map.
+     * * 1: A 2-D Tensor of shape [num_rois, 4], specifying the locations of
+     *      the regions of interest, each line with format [x1, y1, x2, y2].
+     *      For input0 of type {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      this tensor should be of {@link OperandType::TENSOR_QUANT16_ASYMM},
+     *      with zeroPoint of 0 and scale of 0.125.
+     * * 2: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_rois], specifying the batch index of each box. Boxes with
+     *      the same batch index are grouped together.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the output
+     *      height of the output tensor.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the output
+     *      width of the output tensor.
+     * * 5: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the height of original image to the height of feature map.
+     * * 6: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the width of original image to the width of feature map.
+     * * 7: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0. The output
+     *      shape is [num_rois, out_height, out_width, depth].
+     *
+     * Available since API level 29.
+     */
     ROI_POOLING = 82,
+
+    /**
+     * Computes reciprocal of square root of x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 29.
+     */
     RSQRT = 83,
+
+    /**
+     * Using a tensor of booleans c and input tensors x and y select values
+     * elementwise from both input tensors:
+     *
+     * O[i] = C[i] ? x[i] : y[i].
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: A tensor of type {@link OperandType::TENSOR_BOOL8} acting as a
+     *      mask that chooses, based on the value at each element, whether the
+     *      corresponding element in the output should be taken from input1 (if
+     *      true) or input2 (if false).
+     * * 1: An input tensor of the same shape as input0.
+     * * 2: An input tensor of the same shape and type as input1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same type and shape as input1 and input2.
+     *
+     */
     SELECT = 84,
+
+    /**
+     * Computes sin of x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 29.
+     */
     SIN = 85,
+
+    /**
+     * Extracts a slice of specified size from the input tensor starting at a
+     * specified location.
+     *
+     * The starting location is specified as a 1-D tensor containing offsets
+     * for each dimension. The size is specified as a 1-D tensor containing
+     * either size of a slice along corresponding dimension or -1. In the latter
+     * case, all the remaining elements in dimension are included in the slice.
+     * Slice size in each dimension cannot be zero.
+     *
+     * A sum of begin offset and a size of a slice must not exceed size of a
+     * corresponding dimension.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor to take slice from.
+     * * 1: A 1-D tensor of type {@link OperandType::TENSOR_INT32} specifying
+     *      the beginning indices of the slice in each dimension.
+     * * 2: A 1-D tensor of type {@link OperandType::TENSOR_INT32} specifying
+     *      the size of the slice in each dimension.
+     *
+     * Outputs:
+     * * 0: An n-D tensor of the same type as the input containing the slice.
+     *
+     * Available since API level 29.
+     */
     SLICE = 86,
+
+    /**
+     * Splits a tensor along a given axis into num_splits subtensors.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor to split.
+     * * 1: An {@link OperandType::INT32} scalar specifying the axis along
+     *      which to split.
+     * * 2: An {@link OperandType::INT32} scalar indicating the number of
+     *      splits along given axis. Must evenly divide axis size.
+     *
+     * Outputs:
+     * * 0 ~ (num_splits - 1): Resulting subtensors.
+     *
+     * Available since API level 29.
+     */
     SPLIT = 87,
+
+    /**
+     * Computes square root of x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *
+     * Available since API level 29.
+     */
     SQRT = 88,
+
+    /**
+     * Constructs a tensor by tiling a given tensor.
+     *
+     * This operation creates a new tensor by replicating `input` `multiples`
+     * times. The output tensor's i-th dimension has `input.dims(i) * multiples[i]`
+     * elements, and the values of `input` are replicated `multiples[i]` times
+     * along the i-th dimension.
+     * For example, tiling `[a b c d]` by `[2]` produces `[a b c d a b c d]`.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: input, an n-D tensor specifying the input.
+     * * 1: multiples, a 1-D tensor of {@link OperandType::TENSOR_INT32}.
+     *      The length of multiples must be n.
+     *
+     * Outputs:
+     * * 0: A tiled tensor of the same {@link OperandType} and rank as `input`.
+     *
+     * Available since API level 29.
+     */
     TILE = 89,
+
+    /**
+     * Finds values and indices of the k largest entries for the last dimension.
+     *
+     * Resulting values in each dimensions are sorted in descending order. If
+     * two values are equal, the one with larger index appears first.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: input, an n-D tensor specifying the input.
+     * * 1: k, an {@link OperandType::INT32} scalar, specifying the number of
+     *      top elements to look for along the last dimension.
+     *
+     * Outputs:
+     * * 0: An n-D tensor of the same type as the input, containing the k
+     *      largest elements along each last dimensional slice.
+     * * 1: An n-D tensor of type {@link OperandType::TENSOR_INT32}
+     *      containing the indices of values within the last dimension of input.
+     *
+     * Available since API level 29.
+     */
     TOPK_V2 = 90,
+
+    /**
+     * Performs the tranpose of 2-D convolution operation.
+     *
+     * This operation is sometimes called "deconvolution" after Deconvolutional
+     * Networks, but is actually the transpose (gradient) of
+     * {@link OperandType::CONV_2D} rather than an actual deconvolution.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * Supported tensor {@link OperandCode} configurations:
+     * * 32 bit Floating point :
+     * * * {@link OperandType::TENSOR_FLOAT32} for input, filter, output, and bias.
+     *
+     * * Quantized:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * Available since API level 29:
+     * * Quantized with symmetric per channel quantization for the filter:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_in], specifying the
+     *      filter. For tensor of type
+     *      {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} the channel
+     *      dimension (extraParams.channelQuant.channelDim) must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias should be of the
+     *      same type. For input tensor of type
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, the bias should be
+     *      of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0 and
+     *      bias_scale == input_scale * filter_scale. For filter tensor of
+     *      {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+     *      must be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+     *      0 and bias_scale of 0. The actual scale of each value 'i' is equal
+     *      to bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 10: An {@link OperandType::BOOL} scalar, set to true to specify
+     *       NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_in], specifying the
+     *      filter. For tensor of type
+     *      {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} the channel
+     *      dimension (extraParams.channelQuant.channelDim) must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias should be of the
+     *      same type. For input tensor of type
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, the bias should be
+     *      of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0 and
+     *      bias_scale == input_scale * filter_scale. For filter tensor of
+     *      {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+     *      must be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+     *      0 and bias_scale of 0. The actual scale of each value 'i' is equal
+     *      to bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::TENSOR_INT32} tensor, specifying the output
+     *      tensor shape.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 8: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth_out]. For output tensor of
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
+     *      must be satisfied: output_scale > input_scale * filter_scale (for
+     *      filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     *      this condition must be true for all filter scales).
+     *
+     * Available since API level 29.
+     */
     TRANSPOSE_CONV_2D = 91,
+
+    /**
+     * A recurrent neural network specified by an LSTM cell.
+     *
+     * Performs (fully) dynamic unrolling of input.
+     *
+     * This Op unrolls the input along the time dimension, and implements the
+     * following operation for each element in the sequence
+     * s = 1...sequence_length:
+     *   outputs[s] = projection(state = activation(LSTMOp(inputs[s])))
+     *
+     * Where LSTMOp is the LSTM op as in {@link OperandType::LSTM},
+     * the "projection" is an optional projection layer from state and output
+     * and the “activation” is the function passed as the
+     * “fused_activation_function” argument (if not “NONE”).
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: 3, either time-major or batch-major.
+     *
+     * All input and output tensors must be of the same type.
+     *
+     * Inputs:
+     * * 0: The input (\f$x_t\f$).
+     *      A 3-D tensor of shape:
+     *        If time-major: [max_time, batch_size, output_size]
+     *        If batch-major: [batch_size, max_time, output_size]
+     *      where “max_size” is the number of timesteps (sequence length),
+     *      “batch_size” corresponds to the batching dimension, and
+     *      “input_size” is the size of the input.
+     * * 1: The input-to-input weights (\f$W_{xi}\f$). Optional.
+     *      A 2-D tensor of shape [num_units, input_size], where “num_units”
+     *      corresponds to the number of cell units.
+     * * 2: The input-to-forget weights (\f$W_{xf}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 3: The input-to-cell weights (\f$W_{xc}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 4: The input-to-output weights (\f$W_{xo}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 5: The recurrent-to-input weights (\f$W_{hi}\f$). Optional.
+     *      A 2-D tensor of shape [num_units, output_size], where “output_size”
+     *      corresponds to either the number of cell units (i.e., “num_units”),
+     *      or the second dimension of the “projection_weights”, if defined.
+     * * 6: The recurrent-to-forget weights (\f$W_{hf}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 7: The recurrent-to-cell weights (\f$W_{hc}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 8: The recurrent-to-output weights (\f$W_{ho}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 9: The cell-to-input weights (\f$W_{ci}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 10:The cell-to-forget weights (\f$W_{cf}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 11:The cell-to-output weights (\f$W_{co}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 12:The input gate bias (\f$b_i\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 13:The forget gate bias (\f$b_f\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 14:The cell bias (\f$b_c\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 15:The output gate bias (\f$b_o\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 16:The projection weights (\f$W_{proj}\f$). Optional.
+     *      A 2-D tensor of shape [output_size, num_units].
+     * * 17:The projection bias (\f$b_{proj}\f$). Optional.
+     *      A 1-D tensor of shape [output_size].
+     * * 18:The output state (in) (\f$h_{t-1}\f$).
+     *      A 2-D tensor of shape [batch_size, output_size].
+     * * 19:The cell state (in) (\f$C_{t-1}\f$).
+     *      A 2-D tensor of shape [batch_size, num_units].
+     * * 20:The activation function (\f$g\f$).
+     *      A value indicating the activation function:
+     *      <ul>
+     *      <li>0: None;
+     *      <li>1: Relu;
+     *      <li>3: Relu6;
+     *      <li>4: Tanh;
+     *      <li>6: Sigmoid.
+     *      </ul>
+     * * 21:The clipping threshold (\f$t_{cell}\f$) for the cell state, such
+     *      that values are bound within [-cell_clip, cell_clip]. If set to 0.0
+     *      then clipping is disabled.
+     * * 22:The clipping threshold (\f$t_{proj}\f$) for the output from the
+     *      projection layer, such that values are bound within
+     *      [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+     * * 23:Time-major if true, batch-major if false.
+     * * 24:The input layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at input gate.
+     * * 25:The forget layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at forget gate.
+     * * 26:The cell layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at cell gate.
+     * * 27:The output layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at output gate.
+     *
+     * Outputs:
+     * * 0: The output (\f$o_t\f$).
+     *      A 3-D tensor of shape:
+     *        If time-major: [max_time, batch_size, output_size]
+     *        If batch-major: [batch_size, max_time, output_size]
+     *
+     * Available since API level 29.
+     */
     UNIDIRECTIONAL_SEQUENCE_LSTM = 92,
+
+    /**
+     * A recurrent neural network layer that applies a basic RNN cell to a
+     * sequence of inputs.
+     *
+     * This layer unrolls the input along the sequence dimension, and implements
+     * the following operation
+     * for each element in the sequence s = 1...sequence_length:
+     *   outputs[s] = state = activation(inputs[s] * input_weights’ + state *
+     *   recurrent_weights’ + bias)
+     *
+     * Where:
+     * * “input_weights” is a weight matrix that multiplies the inputs;
+     * * “recurrent_weights” is a weight matrix that multiplies the current
+     *    “state” which itself is the output from the previous time step
+     *    computation;
+     * * “bias” is a bias vector (added to each output vector in the batch);
+     * * “activation” is the function passed as the “fused_activation_function”
+     *   argument (if not “NONE”).
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * The input tensors must all be the same type.
+     *
+     * Inputs:
+     * * 0: input.
+     *      A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+     *      it is set to 1, then the input has a shape [maxTime, batchSize,
+     *      inputSize], otherwise the input has a shape [batchSize, maxTime,
+     *      inputSize].
+     * * 1: weights.
+     *      A 2-D tensor of shape [numUnits, inputSize].
+     * * 2: recurrent_weights.
+     *      A 2-D tensor of shape [numUnits, numUnits].
+     * * 3: bias.
+     *      A 1-D tensor of shape [numUnits].
+     * * 4: hidden state
+     *      A 2-D tensor of shape [batchSize, numUnits]. Specifies a hidden
+     *      state input for the first time step of the computation.
+     * * 5: fusedActivationFunction.
+     *      A {@link FusedActivationFunc} value indicating the activation function. If
+     *      “NONE” is specified then it results in a linear activation.
+     * * 6: timeMajor
+     *      An {@link OperandType::INT32} scalar specifying the shape format
+     *      of input and output tensors. Must be set to either 0 or 1.
+     * Outputs:
+     * * 0: output.
+     *      A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+     *      it is set to 1, then the output has a shape [maxTime, batchSize,
+     *      numUnits], otherwise the output has a shape [batchSize, maxTime,
+     *      numUnits].
+     *
+     * Available since API level 29.
+     */
     UNIDIRECTIONAL_SEQUENCE_RNN = 93,
+
+    /**
+     * Resizes images to given size using the nearest neighbor interpretation.
+     *
+     * Resized images must be distorted if their output aspect ratio is not the
+     * same as input aspect ratio. The corner pixels of output may not be the
+     * same as corner pixels of input.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the output
+     *      height of the output tensor.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the output
+     *      width of the output tensor.
+     * * 3: An {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, new_height, new_width, depth].
+     *
+     * Available since API level 29.
+     */
+    RESIZE_NEAREST_NEIGHBOR = 94,
+
+    /**
+     * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
+     * OEM operation and data types.
+     *
+     * This operation is OEM specific. It should only be used for OEM
+     * applications.
+     */
+    OEM_OPERATION = @1.1::OperationType:OEM_OPERATION,
     /* ADDING A NEW FUNDAMENTAL OPERATION REQUIRES UPDATING THE VALUE OF
      * OperationTypeRange::FUNDAMENTAL_MAX.
      */
@@ -194,7 +4535,7 @@
 enum OperationTypeRange : uint32_t {
     BASE_MIN        = 0,
     FUNDAMENTAL_MIN = 0,
-    FUNDAMENTAL_MAX = 93,
+    FUNDAMENTAL_MAX = 94,
     OEM_MIN         = 10000,
     OEM_MAX         = 10000,
     BASE_MAX        = 0xFFFF,
@@ -449,7 +4790,7 @@
      * {@link OperationTypeRange::BASE_MAX} respectively should be interpreted
      * as an extension operand. The low
      * {@link Model::ExtensionTypeEncoding::LOW_BITS_TYPE} bits of the value
-     * correspond to the value within the extension and the high
+     * correspond to the type ID within the extension and the high
      * {@link Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX} bits encode
      * the "prefix", which maps uniquely to the extension name.
      *
diff --git a/neuralnetworks/1.2/vts/functional/Android.bp b/neuralnetworks/1.2/vts/functional/Android.bp
index 510a0d5..891b414 100644
--- a/neuralnetworks/1.2/vts/functional/Android.bp
+++ b/neuralnetworks/1.2/vts/functional/Android.bp
@@ -24,7 +24,6 @@
     cflags: [
         "-DNN_TEST_DYNAMIC_OUTPUT_SHAPE"
     ],
-    test_suites: ["general-tests"],
 }
 
 // Tests for V1_1 models using the V1_2 HAL.
@@ -37,7 +36,6 @@
     cflags: [
         "-DNN_TEST_DYNAMIC_OUTPUT_SHAPE"
     ],
-    test_suites: ["general-tests"],
 }
 
 // Tests for V1_2 models.
@@ -52,5 +50,17 @@
     cflags: [
         "-DNN_TEST_DYNAMIC_OUTPUT_SHAPE"
     ],
-    test_suites: ["general-tests"],
+}
+
+cc_test {
+    name: "PresubmitHalNeuralnetworksV1_2TargetTest",
+    defaults: ["VtsHalNeuralNetworksTargetTestDefaults"],
+    srcs: [
+        "BasicTests.cpp",
+        "GeneratedTests.cpp",
+    ],
+    cflags: [
+        "-DNN_TEST_DYNAMIC_OUTPUT_SHAPE",
+        "-DPRESUBMIT_NOT_VTS",
+    ],
 }
diff --git a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
index 454aa1f..00989e5 100644
--- a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
@@ -86,14 +86,22 @@
   protected:
     void SetUp() override {
         NeuralnetworksHidlTest::SetUp();
+        ASSERT_NE(device.get(), nullptr);
 
-        // Create cache directory.
+        // Create cache directory. The cache directory and cache files are always created to test
+        // the behavior of prepareModelFromCache, even when caching is not supported.
         char cacheDirTemp[] = "/data/local/tmp/TestCompilationCachingXXXXXX";
         char* cacheDir = mkdtemp(cacheDirTemp);
         ASSERT_NE(cacheDir, nullptr);
-        mCache1 = cacheDir + mCache1;
-        mCache2 = cacheDir + mCache2;
-        mCache3 = cacheDir + mCache3;
+        mCacheDir = cacheDir;
+
+        // Create empty cache files.
+        mCache1 = mCacheDir + "/cache1";
+        mCache2 = mCacheDir + "/cache2";
+        mCache3 = mCacheDir + "/cache3";
+        // A dummy handle, use AccessMode::WRITE_ONLY for createCacheHandle to create files.
+        hidl_handle handle;
+        createCacheHandle({mCache1, mCache2, mCache3}, AccessMode::WRITE_ONLY, &handle);
 
         // Check if caching is supported.
         bool isCachingSupported;
@@ -113,10 +121,18 @@
                       << std::endl;
             mIsCachingSupported = false;
         }
+    }
 
-        // Create empty cache files.
-        hidl_handle handle;
-        createCacheHandle({mCache1, mCache2, mCache3}, AccessMode::WRITE_ONLY, &handle);
+    void TearDown() override {
+        // The tmp directory is only removed when the driver reports caching not supported,
+        // otherwise it is kept for debugging purpose.
+        if (!mIsCachingSupported) {
+            remove(mCache1.c_str());
+            remove(mCache2.c_str());
+            remove(mCache3.c_str());
+            rmdir(mCacheDir.c_str());
+        }
+        NeuralnetworksHidlTest::TearDown();
     }
 
     void saveModelToCache(sp<IPreparedModel> preparedModel, const hidl_handle& cache1,
@@ -163,9 +179,10 @@
                                  .withDefault(nullptr);
     }
 
-    std::string mCache1 = "/cache1";
-    std::string mCache2 = "/cache2";
-    std::string mCache3 = "/cache3";
+    std::string mCacheDir;
+    std::string mCache1;
+    std::string mCache2;
+    std::string mCache3;
     uint8_t mToken[static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)] = {};
     bool mIsCachingSupported;
 };
diff --git a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
index 1bbb203..3b8e3dd 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
@@ -157,10 +157,12 @@
         case OperandType::UINT32:
         case OperandType::BOOL:
             return 1;
+        case OperandType::TENSOR_BOOL8:
         case OperandType::TENSOR_FLOAT16:
         case OperandType::TENSOR_FLOAT32:
         case OperandType::TENSOR_INT32:
         case OperandType::TENSOR_QUANT8_ASYMM:
+        case OperandType::TENSOR_QUANT8_SYMM:
         case OperandType::TENSOR_QUANT16_ASYMM:
         case OperandType::TENSOR_QUANT16_SYMM:
         case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
@@ -193,12 +195,14 @@
         case OperandType::INT32:
         case OperandType::UINT32:
         case OperandType::BOOL:
+        case OperandType::TENSOR_BOOL8:
         case OperandType::TENSOR_FLOAT16:
         case OperandType::TENSOR_FLOAT32:
         case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
             return 1.0f;
         case OperandType::TENSOR_INT32:
             return -1.0f;
+        case OperandType::TENSOR_QUANT8_SYMM:
         case OperandType::TENSOR_QUANT8_ASYMM:
         case OperandType::TENSOR_QUANT16_ASYMM:
         case OperandType::TENSOR_QUANT16_SYMM:
@@ -228,6 +232,7 @@
         case OperandType::INT32:
         case OperandType::UINT32:
         case OperandType::BOOL:
+        case OperandType::TENSOR_BOOL8:
         case OperandType::TENSOR_FLOAT16:
         case OperandType::TENSOR_FLOAT32:
         case OperandType::TENSOR_INT32:
@@ -235,6 +240,8 @@
             return {1};
         case OperandType::TENSOR_QUANT8_ASYMM:
             return {-1, 256};
+        case OperandType::TENSOR_QUANT8_SYMM:
+          return {-129, -1, 1, 128};
         case OperandType::TENSOR_QUANT16_ASYMM:
             return {-1, 65536};
         case OperandType::TENSOR_QUANT16_SYMM:
@@ -279,6 +286,7 @@
             newOperand.scale = 0.0f;
             newOperand.zeroPoint = 0;
             break;
+        case OperandType::TENSOR_BOOL8:
         case OperandType::TENSOR_FLOAT16:
         case OperandType::TENSOR_FLOAT32:
             newOperand.dimensions =
@@ -292,6 +300,7 @@
             newOperand.zeroPoint = 0;
             break;
         case OperandType::TENSOR_QUANT8_ASYMM:
+        case OperandType::TENSOR_QUANT8_SYMM:
         case OperandType::TENSOR_QUANT16_ASYMM:
         case OperandType::TENSOR_QUANT16_SYMM:
             newOperand.dimensions =
@@ -334,6 +343,10 @@
         // TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM).
         // - CAST's argument can be any of TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM).
         // - RANDOM_MULTINOMIAL's argument can be either TENSOR_FLOAT16 or TENSOR_FLOAT32.
+        // - DEQUANTIZE input can be any of
+        // TENSOR_(QUANT8_ASYMM|QUANT8_SYMM|QUANT8_SYMM_PER_CHANNEL), output can
+        // be of either TENSOR_FLOAT16 or TENSOR_FLOAT32.
+        // - QUANTIZE input can be either TENSOR_FLOAT16 or TENSOR_FLOAT32
         // - CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
         // - DEPTHWISE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
         // - GROUPED_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
@@ -352,8 +365,22 @@
                     return true;
                 }
             } break;
+            case OperationType::QUANTIZE:
             case OperationType::RANDOM_MULTINOMIAL: {
-                if (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32) {
+                if (operand == operation.inputs[0] &&
+                    (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
+                    return true;
+                }
+            } break;
+            case OperationType::DEQUANTIZE: {
+                if (operand == operation.inputs[0] &&
+                    (type == OperandType::TENSOR_QUANT8_ASYMM ||
+                     type == OperandType::TENSOR_QUANT8_SYMM ||
+                     type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL)) {
+                    return true;
+                }
+                if (operand == operation.outputs[0] &&
+                    (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
                     return true;
                 }
             } break;
@@ -392,7 +419,6 @@
 ///////////////////////// VALIDATE MODEL OPERATION TYPE /////////////////////////
 
 static const uint32_t invalidOperationTypes[] = {
-        static_cast<uint32_t>(OperationTypeRange::FUNDAMENTAL_MIN) - 1,
         static_cast<uint32_t>(OperationTypeRange::FUNDAMENTAL_MAX) + 1,
         static_cast<uint32_t>(OperationTypeRange::OEM_MIN) - 1,
         static_cast<uint32_t>(OperationTypeRange::OEM_MAX) + 1,
@@ -479,6 +505,15 @@
                 }
             }
         }
+        // BIDIRECTIONAL_SEQUENCE_RNN can have either on or two outputs
+        // depending on a mergeOutputs parameter
+        if (operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_RNN) {
+            for (const size_t outOprand : operation.outputs) {
+                if (operand == outOprand) {
+                    return true;
+                }
+            }
+        }
     }
     return false;
 }
diff --git a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
index 4eced82..4728c28 100644
--- a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.cpp
@@ -49,7 +49,17 @@
 void NeuralnetworksHidlTest::SetUp() {
     ::testing::VtsHalHidlTargetTestBase::SetUp();
     device = ::testing::VtsHalHidlTargetTestBase::getService<IDevice>(
-        NeuralnetworksHidlEnvironment::getInstance());
+            NeuralnetworksHidlEnvironment::getInstance());
+
+#ifdef PRESUBMIT_NOT_VTS
+    const std::string name =
+            NeuralnetworksHidlEnvironment::getInstance()->getServiceName<IDevice>();
+    const std::string sampleDriver = "sample-";
+    if (device == nullptr && name.substr(0, sampleDriver.size()) == sampleDriver) {
+        GTEST_SKIP();
+    }
+#endif  // PRESUBMIT_NOT_VTS
+
     ASSERT_NE(nullptr, device.get());
 }
 
@@ -66,6 +76,12 @@
 
 }  // namespace functional
 }  // namespace vts
+}  // namespace V1_2
+}  // namespace neuralnetworks
+}  // namespace hardware
+}  // namespace android
+
+namespace android::hardware::neuralnetworks::V1_0 {
 
 ::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
     return os << toString(errorStatus);
@@ -75,10 +91,7 @@
     return os << toString(deviceStatus);
 }
 
-}  // namespace V1_2
-}  // namespace neuralnetworks
-}  // namespace hardware
-}  // namespace android
+}  // namespace android::hardware::neuralnetworks::V1_0
 
 using android::hardware::neuralnetworks::V1_2::vts::functional::NeuralnetworksHidlEnvironment;
 
diff --git a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
index c0c21bd..404eec0 100644
--- a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
@@ -88,14 +88,17 @@
 
 }  // namespace functional
 }  // namespace vts
+}  // namespace V1_2
+}  // namespace neuralnetworks
+}  // namespace hardware
+}  // namespace android
+
+namespace android::hardware::neuralnetworks::V1_0 {
 
 // pretty-print values for error messages
 ::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus);
 ::std::ostream& operator<<(::std::ostream& os, DeviceStatus deviceStatus);
 
-}  // namespace V1_2
-}  // namespace neuralnetworks
-}  // namespace hardware
-}  // namespace android
+}  // namespace android::hardware::neuralnetworks::V1_0
 
 #endif  // VTS_HAL_NEURALNETWORKS_V1_2_H
diff --git a/neuralnetworks/TEST_MAPPING b/neuralnetworks/TEST_MAPPING
new file mode 100644
index 0000000..50b6c19
--- /dev/null
+++ b/neuralnetworks/TEST_MAPPING
@@ -0,0 +1,28 @@
+{
+  "presubmit": [
+    {
+      "name": "PresubmitHalNeuralnetworksV1_0TargetTest",
+      "options": [
+        {
+          "native-test-flag": "--hal_service_instance=android.hardware.neuralnetworks@1.0::IDevice/sample-all"
+        }
+      ]
+    },
+    {
+      "name": "PresubmitHalNeuralnetworksV1_1TargetTest",
+      "options": [
+        {
+          "native-test-flag": "--hal_service_instance=android.hardware.neuralnetworks@1.1::IDevice/sample-all"
+        }
+      ]
+    },
+    {
+      "name": "PresubmitHalNeuralnetworksV1_2TargetTest",
+      "options": [
+        {
+          "native-test-flag": "--hal_service_instance=android.hardware.neuralnetworks@1.2::IDevice/sample-all"
+        }
+      ]
+    }
+  ]
+}
diff --git a/power/stats/1.0/types.hal b/power/stats/1.0/types.hal
index 644224b..6051e3f 100644
--- a/power/stats/1.0/types.hal
+++ b/power/stats/1.0/types.hal
@@ -26,9 +26,9 @@
 struct RailInfo {
     /** Index corresponding to the rail */
     uint32_t index;
-    /** Name of the rail */
+    /** Name of the rail (opaque to the framework) */
     string railName;
-    /** Name of the subsystem to which this rail belongs */
+    /** Name of the subsystem to which this rail belongs (opaque to the framework) */
     string subsysName;
     /** Hardware sampling rate */
     uint32_t samplingRate;
@@ -71,7 +71,7 @@
 struct PowerEntityInfo {
     /** Unique ID corresponding to the PowerEntity */
     uint32_t powerEntityId;
-    /** Name of the PowerEntity */
+    /** Name of the PowerEntity (opaque to the framework) */
     string powerEntityName;
     /** Type of the PowerEntity */
     PowerEntityType type;
@@ -82,7 +82,7 @@
      * ID corresponding to the state. Unique for a given PowerEntityStateSpace
      */
     uint32_t powerEntityStateId;
-    /** Name of the state */
+    /** Name of the state (opaque to the framework) */
     string powerEntityStateName;
 };
 
diff --git a/radio/1.0/types.hal b/radio/1.0/types.hal
index 17718e0..8393cf5 100644
--- a/radio/1.0/types.hal
+++ b/radio/1.0/types.hal
@@ -1230,85 +1230,108 @@
 
 struct GsmSignalStrength {
     uint32_t signalStrength;              // Valid values are (0-61, 99) as defined in
-                                          // TS 27.007 8.69
-    uint32_t bitErrorRate;                // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+                                          // TS 27.007 8.69; INT_MAX means invalid/unreported.
+    uint32_t bitErrorRate;                // bit error rate (0-7, 99) as defined in TS 27.007 8.5;
+                                          // INT_MAX means invalid/unreported.
     int32_t timingAdvance;                // Timing Advance in bit periods. 1 bit period = 48/13 us.
-                                          // INT_MAX denotes invalid value
+                                          // INT_MAX means invalid/unreported.
 };
 
 struct WcdmaSignalStrength{
     int32_t signalStrength;               // Valid values are (0-31, 99) as defined in
-                                          // TS 27.007 8.5
-    int32_t bitErrorRate;                 // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+                                          // TS 27.007 8.5; INT_MAX means unreported.
+    int32_t bitErrorRate;                 // bit error rate (0-7, 99) as defined in TS 27.007 8.5;
+                                          // INT_MAX means invalid/unreported.
 };
 
 struct CdmaSignalStrength {
     uint32_t dbm;                         // This value is the actual RSSI
                                           // value multiplied by -1. Example: If the
                                           // actual RSSI is -75, then this response value will
-                                          // be 75.
+                                          // be 75. INT_MAX means invalid/unreported.
     uint32_t ecio;                        // This value is the actual
                                           // Ec/Io multiplied by -10. Example: If the
                                           // actual Ec/Io is -12.5 dB, then this response value
-                                          // will be 125.
+                                          // will be 125. INT_MAX means invalid/unreported.
 };
 
 struct EvdoSignalStrength {
     uint32_t dbm;                         // This value is the actual
                                           // RSSI value multiplied by -1.
                                           // Example: If the actual RSSI is -75,
-                                          // then this response value will be 75.
+                                          // then this response value will be 75; INT_MAX means
+                                          // invalid/unreported.
     uint32_t ecio;                        // This value is the actual
                                           // Ec/Io multiplied by -10. Example: If the
                                           // actual Ec/Io is -12.5 dB, then this response value
-                                          // will be 125.
+                                          // will be 125; INT_MAX means invalid/unreported.
     uint32_t signalNoiseRatio;            // Valid values are 0-8. 8 is the highest signal to
-                                          // noise ratio.
+                                          // noise ratio; INT_MAX means invalid/unreported.
 };
 
 struct LteSignalStrength {
     uint32_t signalStrength;              // Valid values are (0-31, 99) as defined in
-                                          // TS 27.007 8.5
+                                          // TS 27.007 8.5; INT_MAX means invalid/unreported.
     uint32_t rsrp;                        // The current Reference Signal Receive Power in dBm
                                           // multipled by -1.
-                                          // Range: 44 to 140 dBm
-                                          // INT_MAX: 0x7FFFFFFF denotes invalid value.
+                                          // Range: 44 to 140 dBm;
+                                          // INT_MAX: 0x7FFFFFFF denotes invalid/unreported value.
                                           // Reference: 3GPP TS 36.133 9.1.4
     uint32_t rsrq;                        // The current Reference Signal Receive Quality in dB
                                           // multiplied by -1.
-                                          // Range: 20 to 3 dB.
-                                          // INT_MAX: 0x7FFFFFFF denotes invalid value.
+                                          // Range: 20 to 3 dB;
+                                          // INT_MAX: 0x7FFFFFFF denotes invalid/unreported value.
                                           // Reference: 3GPP TS 36.133 9.1.7
     int32_t rssnr;                        // The current reference signal signal-to-noise ratio in
                                           // 0.1 dB units.
                                           // Range: -200 to +300 (-200 = -20.0 dB, +300 = 30dB).
-                                          // INT_MAX : 0x7FFFFFFF denotes invalid value.
+                                          // INT_MAX: 0x7FFFFFFF denotes invalid/unreported value.
                                           // Reference: 3GPP TS 36.101 8.1.1
     uint32_t cqi;                         // The current Channel Quality Indicator.
                                           // Range: 0 to 15.
-                                          // INT_MAX : 0x7FFFFFFF denotes invalid value.
+                                          // INT_MAX: 0x7FFFFFFF denotes invalid/unreported value.
                                           // Reference: 3GPP TS 36.101 9.2, 9.3, A.4
     uint32_t timingAdvance;               // timing advance in micro seconds for a one way trip
                                           // from cell to device.
                                           // Approximate distance is calculated using
                                           // 300m/us * timingAdvance.
                                           // Range: 0 to 1282 inclusive.
-                                          // INT_MAX : 0x7FFFFFFF denotes unknown value.
+                                          // INT_MAX: 0x7FFFFFFF denotes invalid/unreported value.
                                           // Reference: 3GPP 36.213 section 4.2.3
 };
 
 struct TdScdmaSignalStrength {
     uint32_t rscp;                        // The Received Signal Code Power in dBm multiplied by -1.
                                           // Range : 25 to 120
-                                          // INT_MAX: 0x7FFFFFFF denotes invalid value.
+                                          // INT_MAX: 0x7FFFFFFF denotes invalid/unreported value.
                                           // Reference: 3GPP TS 25.123, section 9.1.1.1
 };
 
 struct SignalStrength {
+    /**
+     * If GSM measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
     GsmSignalStrength gw;
+    /**
+     * If CDMA measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
     CdmaSignalStrength cdma;
+    /**
+     * If EvDO measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
     EvdoSignalStrength evdo;
+    /**
+     * If LTE measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
     LteSignalStrength lte;
+    /**
+     * If TD-SCDMA measurements are provided, this structure must contain valid measurements;
+     * otherwise all fields should be set to INT_MAX to mark them as invalid.
+     */
     TdScdmaSignalStrength tdScdma;
 };
 
diff --git a/radio/1.2/types.hal b/radio/1.2/types.hal
index 4715fac..2dceeb1 100644
--- a/radio/1.2/types.hal
+++ b/radio/1.2/types.hal
@@ -427,11 +427,13 @@
     /**
      * CPICH RSCP as defined in TS 25.215 5.1.1
      * Valid values are (0-96, 255) as defined in TS 27.007 8.69
+     * INT_MAX denotes that the value is invalid/unreported.
      */
     uint32_t rscp;
     /**
      * Ec/No value as defined in TS 25.215 5.1.5
      * Valid values are (0-49, 255) as defined in TS 27.007 8.69
+     * INT_MAX denotes that the value is invalid/unreported.
      */
     uint32_t ecno;
 
@@ -441,26 +443,53 @@
     /**
      * UTRA carrier RSSI as defined in TS 25.225 5.1.4
      * Valid values are (0-31, 99) as defined in TS 27.007 8.5
+     * INT_MAX denotes that the value is invalid/unreported.
      */
     uint32_t signalStrength;
     /**
      * Transport Channel BER as defined in TS 25.225 5.2.5
      * Valid values are (0-7, 99) as defined in TS 27.007 8.5
+     * INT_MAX denotes that the value is invalid/unreported.
      */
     uint32_t bitErrorRate;
     /**
      * P-CCPCH RSCP as defined in TS 25.225 5.1.1
      * Valid values are (0-96, 255) as defined in TS 27.007 8.69
+     * INT_MAX denotes that the value is invalid/unreported.
      */
     uint32_t rscp;
 };
 
 struct SignalStrength {
+    /**
+     * If GSM measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
     GsmSignalStrength gsm;
+    /**
+     * If CDMA measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
     CdmaSignalStrength cdma;
+    /**
+     * If EvDO measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
     EvdoSignalStrength evdo;
+    /**
+     * If LTE measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
     LteSignalStrength lte;
+    /**
+     * If TD-SCDMA measurements are provided, this structure must contain valid measurements;
+     * otherwise all fields should be set to INT_MAX to mark them as invalid.
+     */
     TdScdmaSignalStrength tdScdma;
+    /**
+     * If WCDMA measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
     WcdmaSignalStrength wcdma;
 };
 
diff --git a/radio/1.3/Android.bp b/radio/1.3/Android.bp
index 30b8edf..63056cc 100644
--- a/radio/1.3/Android.bp
+++ b/radio/1.3/Android.bp
@@ -7,6 +7,7 @@
         enabled: true,
     },
     srcs: [
+        "types.hal",
         "IRadio.hal",
         "IRadioIndication.hal",
         "IRadioResponse.hal",
@@ -17,6 +18,9 @@
         "android.hardware.radio@1.2",
         "android.hidl.base@1.0",
     ],
+    types: [
+        "RadioResponseInfoModem",
+    ],
     gen_java: true,
 }
 
diff --git a/radio/1.4/Android.bp b/radio/1.4/Android.bp
index 9fd0374..e8b8777 100644
--- a/radio/1.4/Android.bp
+++ b/radio/1.4/Android.bp
@@ -25,8 +25,10 @@
         "CardStatus",
         "CarrierRestrictionsWithPriority",
         "CellConfigLte",
+        "CellIdentityNr",
         "CellInfo",
         "CellInfoLte",
+        "CellInfoNr",
         "DataCallFailCause",
         "DataConnActiveStatus",
         "DataProfileInfo",
@@ -39,6 +41,7 @@
         "LteVopsInfo",
         "NetworkScanResult",
         "NrIndicators",
+        "NrSignalStrength",
         "PdpProtocolType",
         "PhysicalChannelConfig",
         "RadioAccessFamily",
@@ -46,6 +49,7 @@
         "RadioFrequencyInfo",
         "RadioTechnology",
         "SetupDataCallResult",
+        "SignalStrength",
         "SimLockMultiSimPolicy",
     ],
     gen_java: true,
diff --git a/radio/1.4/IRadio.hal b/radio/1.4/IRadio.hal
index 21c5097..046f074 100644
--- a/radio/1.4/IRadio.hal
+++ b/radio/1.4/IRadio.hal
@@ -115,11 +115,13 @@
      * PUK/PIN state of the subscription and the service state of the radio.
      *
      * Some countries or carriers require some emergency numbers that must be handled with normal
-     * call routing or emergency routing. If the 'routing' field is specified as
-     * @1.4::EmergencyNumberRouting#NORMAL, the implementation must use normal call routing to
-     * handle the call; if it is specified as @1.4::EmergencyNumberRouting#EMERGENCY, the
-     * implementation must use emergency routing to handle the call; if it is
-     * @1.4::EmergencyNumberRouting#UNKNOWN, Android does not know how to handle the call.
+     * call routing if possible or emergency routing. 1) if the 'routing' field is specified as
+     * @1.4::EmergencyNumberRouting#NORMAL, the implementation must try the full radio service to
+     * use normal call routing to handle the call; if service cannot support normal routing, the
+     * implementation must use emergency routing to handle the call. 2) if 'routing' is specified
+     * as @1.4::EmergencyNumberRouting#EMERGENCY, the implementation must use emergency routing to
+     * handle the call. 3) if 'routing' is specified as @1.4::EmergencyNumberRouting#UNKNOWN,
+     * Android does not know how to handle the call.
      *
      * If the dialed emergency number does not have a specified emergency service category, the
      * 'categories' field is set to @1.4::EmergencyServiceCategory#UNSPECIFIED; if the dialed
@@ -128,9 +130,11 @@
      * does not support the emergency service category or emergency uniform resource names, the
      * field 'categories' or 'urns' may be ignored.
      *
-     * 'fromEmergencyDialer' indicates if this request originated from emergency dialer/shortcut,
-     * which means an explicit intent from the user to dial an emergency number. The modem must
-     * treat this as an actual emergency dial and not try to disambiguate.
+     * In the scenarios that the 'address' in the 'dialInfo' field has other functions besides the
+     * emergency number function, if the 'hasKnownUserIntentEmergency' field is true, the user's
+     * intent for this dial request is emergency call, and the modem must treat this as an actual
+     * emergency dial; if the 'hasKnownUserIntentEmergency' field is false, Android does not know
+     * user's intent for this call.
      *
      * If 'isTesting' is true, this request is for testing purpose, and must not be sent to a real
      * emergency service; otherwise it's for a real emergency call request.
@@ -146,14 +150,15 @@
      *     of the call.
      * @param urns the emergency Uniform Resource Names (URN)
      * @param routing @1.4::EmergencyCallRouting the emergency call routing information.
-     * @param fromEmergencyDialer Flag indicating if this request originated from emergency dialer.
+     * @param hasKnownUserIntentEmergency Flag indicating if user's intent for the emergency call
+     *     is known.
      * @param isTesting Flag indicating if this request is for testing purpose.
      *
      * Response function is IRadioResponse.emergencyDialResponse()
      */
     oneway emergencyDial(int32_t serial, Dial dialInfo,
             bitfield<EmergencyServiceCategory> categories, vec<string> urns,
-            EmergencyCallRouting routing, bool fromEmergencyDialer, bool isTesting);
+            EmergencyCallRouting routing, bool hasKnownUserIntentEmergency, bool isTesting);
 
     /**
      * Starts a network scan
@@ -215,4 +220,13 @@
      * Response callback is IRadioResponse.getAllowedCarriersResponse_1_3()
      */
     oneway getAllowedCarriers_1_4(int32_t serial);
+
+    /**
+     * Requests current signal strength and associated information.  Must succeed if radio is on.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getSignalStrengthResponse_1_4()
+     */
+    oneway getSignalStrength_1_4(int32_t serial);
 };
diff --git a/radio/1.4/IRadioIndication.hal b/radio/1.4/IRadioIndication.hal
index 58b7b70..f4d3ba9 100644
--- a/radio/1.4/IRadioIndication.hal
+++ b/radio/1.4/IRadioIndication.hal
@@ -53,7 +53,7 @@
             vec<EmergencyNumber> emergencyNumberList);
 
     /**
-     * Request all of the current cell information known to the radio.
+     * Report all of the current cell information known to the radio.
      *
      * @param type Type of radio indication
      * @param records Current cell information
@@ -89,4 +89,12 @@
      *        3. Unsolicited disconnect from either modem or network side.
      */
     oneway dataCallListChanged_1_4(RadioIndicationType type, vec<SetupDataCallResult> dcList);
+
+    /**
+     * Indicates current signal strength of the radio.
+     *
+     * @param type Type of radio indication
+     * @param signalStrength SignalStrength information
+     */
+    oneway currentSignalStrength_1_4(RadioIndicationType type, SignalStrength signalStrength);
 };
diff --git a/radio/1.4/IRadioResponse.hal b/radio/1.4/IRadioResponse.hal
index 6b8977d..f43295a 100644
--- a/radio/1.4/IRadioResponse.hal
+++ b/radio/1.4/IRadioResponse.hal
@@ -24,6 +24,7 @@
 import @1.4::DataRegStateResult;
 import @1.4::RadioAccessFamily;
 import @1.4::SetupDataCallResult;
+import @1.4::SignalStrength;
 import @1.4::SimLockMultiSimPolicy;
 
 /**
@@ -200,4 +201,14 @@
      */
     oneway getAllowedCarriersResponse_1_4(RadioResponseInfo info,
             CarrierRestrictionsWithPriority carriers, SimLockMultiSimPolicy multiSimPolicy);
+
+    /**
+     * @param signalStrength Current signal strength
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getSignalStrengthResponse_1_4(RadioResponseInfo info, SignalStrength signalStrength);
 };
diff --git a/radio/1.4/types.hal b/radio/1.4/types.hal
index 6fa7ebd..dc3bba0 100644
--- a/radio/1.4/types.hal
+++ b/radio/1.4/types.hal
@@ -20,12 +20,16 @@
 import @1.0::ApnTypes;
 import @1.0::Carrier;
 import @1.0::CellInfoType;
+import @1.0::CdmaSignalStrength;
 import @1.0::DataCallFailCause;
 import @1.0::DataProfileId;
 import @1.0::DataProfileInfoType;
+import @1.0::EvdoSignalStrength;
+import @1.0::GsmSignalStrength;
+import @1.0::LteSignalStrength;
 import @1.0::RadioAccessFamily;
 import @1.0::RadioCapabilityPhase;
-import @1.0::RadioCapabilityStatus ;
+import @1.0::RadioCapabilityStatus;
 import @1.0::RadioError;
 import @1.0::RadioTechnology;
 import @1.0::RegState;
@@ -39,10 +43,17 @@
 import @1.2::CellInfoTdscdma;
 import @1.2::CellInfoWcdma;
 import @1.2::CardStatus;
-import @1.2::CellIdentity;
 import @1.2::CellConnectionStatus;
+import @1.2::CellIdentityCdma;
+import @1.2::CellIdentityGsm;
+import @1.2::CellIdentityLte;
+import @1.2::CellIdentityTdscdma;
+import @1.2::CellIdentityWcdma;
+import @1.2::CellIdentityOperatorNames;
 import @1.2::DataRegStateResult;
 import @1.2::PhysicalChannelConfig;
+import @1.2::TdscdmaSignalStrength;
+import @1.2::WcdmaSignalStrength;
 
 import android.hidl.safe_union@1.0::Monostate;
 
@@ -188,12 +199,12 @@
 };
 
 enum RadioTechnology : @1.0::RadioTechnology {
-    /** 5G NR. */
+    /** 5G NR. This is only use in 5G Standalone mode. */
     NR = 20,
 };
 
 enum RadioAccessFamily : @1.0::RadioAccessFamily {
-    /** 5G NR. */
+    /** 5G NR. This is only use in 5G Standalone mode. */
     NR = 1 << RadioTechnology:NR,
 };
 
@@ -1551,9 +1562,15 @@
         CellInfoWcdma wcdma;
         CellInfoTdscdma tdscdma;
         CellInfoLte lte;
+        CellInfoNr nr;
     } info;
 };
 
+struct CellInfoNr {
+    NrSignalStrength signalStrength;
+    CellIdentityNr cellidentity;
+};
+
 /** Overwritten from @1.2::NetworkScanResult in order to update the CellInfo to 1.4 version. */
 struct NetworkScanResult {
     /**
@@ -1770,3 +1787,141 @@
      */
     bool allowedCarriersPrioritized;
 };
+
+struct CellIdentityNr {
+    /** 3-digit Mobile Country Code, in range[0, 999]; This value must be valid for registered or
+     *  camped cells; INT_MAX means invalid/unreported.
+     */
+    string mcc;
+
+    /**
+     * 2 or 3-digit Mobile Network Code, in range [0, 999], This value must be valid for
+     * registered or camped cells; INT_MAX means invalid/unreported.
+     */
+    string mnc;
+
+    /**
+     * NR Cell Identity in range [0, 68719476735] (36 bits) described in 3GPP TS 38.331, which
+     * unambiguously identifies a cell within a PLMN. This value must be valid for registered or
+     * camped cells; LONG_MAX (2^63-1) means invalid/unreported.
+     */
+    uint64_t nci;
+
+    /**
+     * Physical cell id in range [0, 1007] described in 3GPP TS 38.331. This value must be valid.
+     */
+    uint32_t pci;
+
+    /** 16-bit tracking area code, INT_MAX means invalid/unreported. */
+    int32_t tac;
+
+    /**
+     * NR Absolute Radio Frequency Channel Number, in range [0, 3279165].
+     * Reference: 3GPP TS 38.101-1 and 3GPP TS 38.101-2 section 5.4.2.1.
+     * This value must be valid.
+     */
+    int32_t nrarfcn;
+
+    CellIdentityOperatorNames operatorNames;
+};
+
+struct NrSignalStrength {
+    /**
+     * SS reference signal received power, multipled by -1.
+     *
+     * Reference: 3GPP TS 38.215.
+     *
+     * Range [44, 140], INT_MAX means invalid/unreported.
+     */
+    int32_t ssRsrp;
+
+    /**
+     * SS reference signal received quality, multipled by -1.
+     *
+     * Reference: 3GPP TS 38.215.
+     *
+     * Range [3, 20], INT_MAX means invalid/unreported.
+     */
+    int32_t ssRsrq;
+
+    /**
+     * SS signal-to-noise and interference ratio.
+     *
+     * Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
+     *
+     * Range [-23, 40], INT_MAX means invalid/unreported.
+     */
+    int32_t ssSinr;
+
+    /**
+     * CSI reference signal received power, multipled by -1.
+     *
+     * Reference: 3GPP TS 38.215.
+     *
+     * Range [44, 140], INT_MAX means invalid/unreported.
+     */
+    int32_t csiRsrp;
+
+    /**
+     * CSI reference signal received quality, multipled by -1.
+     *
+     * Reference: 3GPP TS 38.215.
+     *
+     * Range [3, 20], INT_MAX means invalid/unreported.
+     */
+    int32_t csiRsrq;
+
+    /**
+     * CSI signal-to-noise and interference ratio.
+     *
+     * Reference: 3GPP TS 138.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
+     *
+     * Range [-23, 40], INT_MAX means invalid/unreported.
+     */
+    int32_t csiSinr;
+};
+
+/** Overwritten from @1.2::SignalStrength in order to add signal strength for NR.  */
+struct SignalStrength {
+    /**
+     * If GSM measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    GsmSignalStrength gsm;
+
+    /**
+     * If CDMA measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    CdmaSignalStrength cdma;
+
+    /**
+     * If EvDO measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    EvdoSignalStrength evdo;
+
+    /**
+     * If LTE measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    LteSignalStrength lte;
+
+    /**
+     * If TD-SCDMA measurements are provided, this structure must contain valid measurements;
+     * otherwise all fields should be set to INT_MAX to mark them as invalid.
+     */
+    TdscdmaSignalStrength tdscdma;
+
+    /**
+     * If WCDMA measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    WcdmaSignalStrength wcdma;
+
+    /**
+     * If NR 5G measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    NrSignalStrength nr;
+};
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
index 6b1f85e..c395df6 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
@@ -16,4 +16,500 @@
 
 #include <radio_hidl_hal_utils_v1_4.h>
 
-#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
\ No newline at end of file
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+
+/*
+ * Test IRadio.emergencyDial() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_4, emergencyDial) {
+    serial = GetRandomSerialNumber();
+
+    ::android::hardware::radio::V1_0::Dial dialInfo;
+    dialInfo.address = hidl_string("911");
+    int categories = static_cast<int>(
+            ::android::hardware::radio::V1_4::EmergencyServiceCategory::UNSPECIFIED);
+    std::vector<hidl_string> urns = {""};
+    ::android::hardware::radio::V1_4::EmergencyCallRouting routing =
+            ::android::hardware::radio::V1_4::EmergencyCallRouting::UNKNOWN;
+
+    Return<void> res =
+            radio_v1_4->emergencyDial(serial, dialInfo, categories, urns, routing, true, true);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("emergencyDial, rspInfo.error = %s\n", toString(radioRsp_v1_4->rspInfo.error).c_str());
+    EXPECT_EQ(RadioError::NONE, radioRsp_v1_4->rspInfo.error);
+}
+
+/*
+ * Test IRadio.emergencyDial() with specified service and its response returned.
+ */
+TEST_F(RadioHidlTest_v1_4, emergencyDial_withServices) {
+    serial = GetRandomSerialNumber();
+
+    ::android::hardware::radio::V1_0::Dial dialInfo;
+    dialInfo.address = hidl_string("911");
+    int categories =
+            static_cast<int>(::android::hardware::radio::V1_4::EmergencyServiceCategory::AMBULANCE);
+    std::vector<hidl_string> urns = {"urn:service:sos.ambulance"};
+    ::android::hardware::radio::V1_4::EmergencyCallRouting routing =
+            ::android::hardware::radio::V1_4::EmergencyCallRouting::UNKNOWN;
+
+    Return<void> res =
+            radio_v1_4->emergencyDial(serial, dialInfo, categories, urns, routing, true, true);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("emergencyDial_withServices, rspInfo.error = %s\n",
+          toString(radioRsp_v1_4->rspInfo.error).c_str());
+    EXPECT_EQ(RadioError::NONE, radioRsp_v1_4->rspInfo.error);
+}
+
+/*
+ * Test IRadio.emergencyDial() with known emergency call routing and its response returned.
+ */
+TEST_F(RadioHidlTest_v1_4, emergencyDial_withEmergencyRouting) {
+    serial = GetRandomSerialNumber();
+
+    ::android::hardware::radio::V1_0::Dial dialInfo;
+    dialInfo.address = hidl_string("911");
+    int categories = static_cast<int>(
+            ::android::hardware::radio::V1_4::EmergencyServiceCategory::UNSPECIFIED);
+    std::vector<hidl_string> urns = {""};
+    ::android::hardware::radio::V1_4::EmergencyCallRouting routing =
+            ::android::hardware::radio::V1_4::EmergencyCallRouting::EMERGENCY;
+
+    Return<void> res =
+            radio_v1_4->emergencyDial(serial, dialInfo, categories, urns, routing, true, true);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("emergencyDial_withEmergencyRouting, rspInfo.error = %s\n",
+          toString(radioRsp_v1_4->rspInfo.error).c_str());
+    EXPECT_EQ(RadioError::NONE, radioRsp_v1_4->rspInfo.error);
+}
+
+/*
+ * Test IRadio.startNetworkScan() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_4, startNetworkScan) {
+    serial = GetRandomSerialNumber();
+
+    RadioAccessSpecifier specifier = {.radioAccessNetwork = RadioAccessNetworks::GERAN,
+                                      .geranBands = {GeranBands::BAND_450, GeranBands::BAND_480},
+                                      .channels = {1, 2}};
+
+    ::android::hardware::radio::V1_2::NetworkScanRequest request = {
+            .type = ScanType::ONE_SHOT, .interval = 60, .specifiers = {specifier}};
+
+    Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("startNetworkScan, rspInfo.error = %s\n", toString(radioRsp_v1_4->rspInfo.error).c_str());
+
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error, {RadioError::SIM_ABSENT}));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        // OPERATION_NOT_ALLOWED should not be allowed; however, some vendors do not support the
+        // required manual GSM search functionality. This is tracked in b/112206766.
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::NONE, RadioError::OPERATION_NOT_ALLOWED}));
+    }
+}
+
+/*
+ * Test IRadio.startNetworkScan() with invalid specifier.
+ */
+TEST_F(RadioHidlTest_v1_4, startNetworkScan_InvalidArgument) {
+    serial = GetRandomSerialNumber();
+
+    ::android::hardware::radio::V1_2::NetworkScanRequest request = {.type = ScanType::ONE_SHOT,
+                                                                    .interval = 60};
+
+    Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
+
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("startNetworkScan_InvalidArgument, rspInfo.error = %s\n",
+          toString(radioRsp_v1_4->rspInfo.error).c_str());
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::SIM_ABSENT, RadioError::INVALID_ARGUMENTS}));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(
+                CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error, {RadioError::INVALID_ARGUMENTS}));
+    }
+}
+
+/*
+ * Test IRadio.startNetworkScan() with invalid interval (lower boundary).
+ */
+TEST_F(RadioHidlTest_v1_4, startNetworkScan_InvalidInterval1) {
+    serial = GetRandomSerialNumber();
+
+    RadioAccessSpecifier specifier = {.radioAccessNetwork = RadioAccessNetworks::GERAN,
+                                      .geranBands = {GeranBands::BAND_450, GeranBands::BAND_480},
+                                      .channels = {1, 2}};
+
+    ::android::hardware::radio::V1_2::NetworkScanRequest request = {
+            .type = ScanType::ONE_SHOT,
+            .interval = 4,
+            .specifiers = {specifier},
+            .maxSearchTime = 60,
+            .incrementalResults = false,
+            .incrementalResultsPeriodicity = 1};
+
+    Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
+
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("startNetworkScan_InvalidInterval1, rspInfo.error = %s\n",
+          toString(radioRsp_v1_4->rspInfo.error).c_str());
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::SIM_ABSENT, RadioError::INVALID_ARGUMENTS}));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(
+                CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error, {RadioError::INVALID_ARGUMENTS}));
+    }
+}
+
+/*
+ * Test IRadio.startNetworkScan() with invalid interval (upper boundary).
+ */
+TEST_F(RadioHidlTest_v1_4, startNetworkScan_InvalidInterval2) {
+    serial = GetRandomSerialNumber();
+
+    RadioAccessSpecifier specifier = {.radioAccessNetwork = RadioAccessNetworks::GERAN,
+                                      .geranBands = {GeranBands::BAND_450, GeranBands::BAND_480},
+                                      .channels = {1, 2}};
+
+    ::android::hardware::radio::V1_2::NetworkScanRequest request = {
+            .type = ScanType::ONE_SHOT,
+            .interval = 301,
+            .specifiers = {specifier},
+            .maxSearchTime = 60,
+            .incrementalResults = false,
+            .incrementalResultsPeriodicity = 1};
+
+    Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("startNetworkScan_InvalidInterval2, rspInfo.error = %s\n",
+          toString(radioRsp_v1_4->rspInfo.error).c_str());
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::SIM_ABSENT, RadioError::INVALID_ARGUMENTS}));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(
+                CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error, {RadioError::INVALID_ARGUMENTS}));
+    }
+}
+
+/*
+ * Test IRadio.startNetworkScan() with invalid max search time (lower boundary).
+ */
+TEST_F(RadioHidlTest_v1_4, startNetworkScan_InvalidMaxSearchTime1) {
+    serial = GetRandomSerialNumber();
+
+    RadioAccessSpecifier specifier = {.radioAccessNetwork = RadioAccessNetworks::GERAN,
+                                      .geranBands = {GeranBands::BAND_450, GeranBands::BAND_480},
+                                      .channels = {1, 2}};
+
+    ::android::hardware::radio::V1_2::NetworkScanRequest request = {
+            .type = ScanType::ONE_SHOT,
+            .interval = 60,
+            .specifiers = {specifier},
+            .maxSearchTime = 59,
+            .incrementalResults = false,
+            .incrementalResultsPeriodicity = 1};
+
+    Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("startNetworkScan_InvalidMaxSearchTime1, rspInfo.error = %s\n",
+          toString(radioRsp_v1_4->rspInfo.error).c_str());
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::SIM_ABSENT, RadioError::INVALID_ARGUMENTS}));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(
+                CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error, {RadioError::INVALID_ARGUMENTS}));
+    }
+}
+
+/*
+ * Test IRadio.startNetworkScan() with invalid max search time (upper boundary).
+ */
+TEST_F(RadioHidlTest_v1_4, startNetworkScan_InvalidMaxSearchTime2) {
+    serial = GetRandomSerialNumber();
+
+    RadioAccessSpecifier specifier = {.radioAccessNetwork = RadioAccessNetworks::GERAN,
+                                      .geranBands = {GeranBands::BAND_450, GeranBands::BAND_480},
+                                      .channels = {1, 2}};
+
+    ::android::hardware::radio::V1_2::NetworkScanRequest request = {
+            .type = ScanType::ONE_SHOT,
+            .interval = 60,
+            .specifiers = {specifier},
+            .maxSearchTime = 3601,
+            .incrementalResults = false,
+            .incrementalResultsPeriodicity = 1};
+
+    Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("startNetworkScan_InvalidMaxSearchTime2, rspInfo.error = %s\n",
+          toString(radioRsp_v1_4->rspInfo.error).c_str());
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::SIM_ABSENT, RadioError::INVALID_ARGUMENTS}));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(
+                CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error, {RadioError::INVALID_ARGUMENTS}));
+    }
+}
+
+/*
+ * Test IRadio.startNetworkScan() with invalid periodicity (lower boundary).
+ */
+TEST_F(RadioHidlTest_v1_4, startNetworkScan_InvalidPeriodicity1) {
+    serial = GetRandomSerialNumber();
+
+    RadioAccessSpecifier specifier = {.radioAccessNetwork = RadioAccessNetworks::GERAN,
+                                      .geranBands = {GeranBands::BAND_450, GeranBands::BAND_480},
+                                      .channels = {1, 2}};
+
+    ::android::hardware::radio::V1_2::NetworkScanRequest request = {
+            .type = ScanType::ONE_SHOT,
+            .interval = 60,
+            .specifiers = {specifier},
+            .maxSearchTime = 600,
+            .incrementalResults = false,
+            .incrementalResultsPeriodicity = 0};
+
+    Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("startNetworkScan_InvalidPeriodicity1, rspInfo.error = %s\n",
+          toString(radioRsp_v1_4->rspInfo.error).c_str());
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::SIM_ABSENT, RadioError::INVALID_ARGUMENTS}));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(
+                CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error, {RadioError::INVALID_ARGUMENTS}));
+    }
+}
+
+/*
+ * Test IRadio.startNetworkScan() with invalid periodicity (upper boundary).
+ */
+TEST_F(RadioHidlTest_v1_4, startNetworkScan_InvalidPeriodicity2) {
+    serial = GetRandomSerialNumber();
+
+    RadioAccessSpecifier specifier = {.radioAccessNetwork = RadioAccessNetworks::GERAN,
+                                      .geranBands = {GeranBands::BAND_450, GeranBands::BAND_480},
+                                      .channels = {1, 2}};
+
+    ::android::hardware::radio::V1_2::NetworkScanRequest request = {
+            .type = ScanType::ONE_SHOT,
+            .interval = 60,
+            .specifiers = {specifier},
+            .maxSearchTime = 600,
+            .incrementalResults = false,
+            .incrementalResultsPeriodicity = 11};
+
+    Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("startNetworkScan_InvalidPeriodicity2, rspInfo.error = %s\n",
+          toString(radioRsp_v1_4->rspInfo.error).c_str());
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::SIM_ABSENT, RadioError::INVALID_ARGUMENTS}));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(
+                CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error, {RadioError::INVALID_ARGUMENTS}));
+    }
+}
+
+/*
+ * Test IRadio.startNetworkScan() with valid periodicity
+ */
+TEST_F(RadioHidlTest_v1_4, startNetworkScan_GoodRequest1) {
+    serial = GetRandomSerialNumber();
+
+    RadioAccessSpecifier specifier = {.radioAccessNetwork = RadioAccessNetworks::GERAN,
+                                      .geranBands = {GeranBands::BAND_450, GeranBands::BAND_480},
+                                      .channels = {1, 2}};
+
+    ::android::hardware::radio::V1_2::NetworkScanRequest request = {
+            .type = ScanType::ONE_SHOT,
+            .interval = 60,
+            .specifiers = {specifier},
+            // Some vendor may not support max search time of 360s.
+            // This issue is tracked in b/112205669.
+            .maxSearchTime = 300,
+            .incrementalResults = false,
+            .incrementalResultsPeriodicity = 10};
+
+    Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("startNetworkScan_GoodRequest1, rspInfo.error = %s\n",
+          toString(radioRsp_v1_4->rspInfo.error).c_str());
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::NONE, RadioError::SIM_ABSENT}));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::NONE, RadioError::INVALID_ARGUMENTS}));
+    }
+}
+
+/*
+ * Test IRadio.startNetworkScan() with valid periodicity and plmns
+ */
+TEST_F(RadioHidlTest_v1_4, startNetworkScan_GoodRequest2) {
+    serial = GetRandomSerialNumber();
+
+    RadioAccessSpecifier specifier = {.radioAccessNetwork = RadioAccessNetworks::GERAN,
+                                      .geranBands = {GeranBands::BAND_450, GeranBands::BAND_480},
+                                      .channels = {1, 2}};
+
+    ::android::hardware::radio::V1_2::NetworkScanRequest request = {
+            .type = ScanType::ONE_SHOT,
+            .interval = 60,
+            .specifiers = {specifier},
+            // Some vendor may not support max search time of 360s.
+            // This issue is tracked in b/112205669.
+            .maxSearchTime = 300,
+            .incrementalResults = false,
+            .incrementalResultsPeriodicity = 10,
+            .mccMncs = {"310410"}};
+
+    Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
+
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    ALOGI("startNetworkScan_GoodRequest2, rspInfo.error = %s\n",
+          toString(radioRsp_v1_4->rspInfo.error).c_str());
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::NONE, RadioError::SIM_ABSENT}));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::NONE, RadioError::INVALID_ARGUMENTS}));
+    }
+}
+
+/*
+ * Test IRadio.getSignalStrength_1_4() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_4, getSignalStrength_1_4) {
+    serial = GetRandomSerialNumber();
+
+    radio_v1_4->getSignalStrength_1_4(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        EXPECT_EQ(RadioError::NONE, radioRsp_v1_4->rspInfo.error);
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE}));
+    }
+}
+
+/*
+ * Test IRadio.setupDataCall_1_4() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_4, setupDataCall_1_4) {
+    serial = GetRandomSerialNumber();
+
+    ::android::hardware::radio::V1_4::AccessNetwork accessNetwork =
+            ::android::hardware::radio::V1_4::AccessNetwork::EUTRAN;
+
+    android::hardware::radio::V1_4::DataProfileInfo dataProfileInfo;
+    memset(&dataProfileInfo, 0, sizeof(dataProfileInfo));
+    dataProfileInfo.profileId = DataProfileId::DEFAULT;
+    dataProfileInfo.apn = hidl_string("internet");
+    dataProfileInfo.protocol = PdpProtocolType::IPV4V6;
+    dataProfileInfo.roamingProtocol = PdpProtocolType::IPV4V6;
+    dataProfileInfo.authType = ApnAuthType::NO_PAP_NO_CHAP;
+    dataProfileInfo.user = hidl_string("username");
+    dataProfileInfo.password = hidl_string("password");
+    dataProfileInfo.type = DataProfileInfoType::THREE_GPP;
+    dataProfileInfo.maxConnsTime = 300;
+    dataProfileInfo.maxConns = 20;
+    dataProfileInfo.waitTime = 0;
+    dataProfileInfo.enabled = true;
+    dataProfileInfo.supportedApnTypesBitmap = 320;
+    dataProfileInfo.bearerBitmap = 161543;
+    dataProfileInfo.mtu = 0;
+    dataProfileInfo.preferred = true;
+    dataProfileInfo.persistent = false;
+
+    bool roamingAllowed = false;
+
+    ::android::hardware::radio::V1_2::DataRequestReason reason =
+            ::android::hardware::radio::V1_2::DataRequestReason::NORMAL;
+    std::vector<hidl_string> addresses = {""};
+    std::vector<hidl_string> dnses = {""};
+
+    Return<void> res = radio_v1_4->setupDataCall_1_4(serial, accessNetwork, dataProfileInfo,
+                                                     roamingAllowed, reason, addresses, dnses);
+    ASSERT_OK(res);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+    if (cardStatus.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::SIM_ABSENT, RadioError::RADIO_NOT_AVAILABLE,
+                                      RadioError::OP_NOT_ALLOWED_BEFORE_REG_TO_NW}));
+    } else if (cardStatus.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_4->rspInfo.error,
+                                     {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE,
+                                      RadioError::OP_NOT_ALLOWED_BEFORE_REG_TO_NW}));
+    }
+}
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h b/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h
index f290d5d..b77814f 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h
+++ b/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h
@@ -441,6 +441,9 @@
             const RadioResponseInfo& info,
             const ::android::hardware::radio::V1_2::SignalStrength& sig_strength);
 
+    Return<void> getSignalStrengthResponse_1_4(const RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_4::SignalStrength& sig_strength);
+
     Return<void> getCellInfoListResponse_1_2(
             const RadioResponseInfo& info,
             const ::android::hardware::hidl_vec<::android::hardware::radio::V1_2::CellInfo>&
@@ -559,6 +562,9 @@
             RadioIndicationType type,
             const ::android::hardware::radio::V1_2::SignalStrength& signalStrength);
 
+    Return<void> currentSignalStrength_1_4(RadioIndicationType type,
+            const ::android::hardware::radio::V1_4::SignalStrength& signalStrength);
+
     /* 1.1 Api */
     Return<void> carrierInfoForImsiEncryption(RadioIndicationType info);
 
@@ -730,4 +736,4 @@
 
     /* radio indication handle */
     sp<RadioIndication_v1_4> radioInd_v1_4;
-};
\ No newline at end of file
+};
diff --git a/radio/1.4/vts/functional/radio_indication.cpp b/radio/1.4/vts/functional/radio_indication.cpp
index f08b361..c3722b0 100644
--- a/radio/1.4/vts/functional/radio_indication.cpp
+++ b/radio/1.4/vts/functional/radio_indication.cpp
@@ -85,6 +85,11 @@
     return Void();
 }
 
+Return<void> RadioIndication_v1_4::currentSignalStrength_1_4(RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_4::SignalStrength& /*signalStrength*/) {
+    return Void();
+}
+
 /* 1.1 Apis */
 Return<void> RadioIndication_v1_4::carrierInfoForImsiEncryption(RadioIndicationType /*info*/) {
     return Void();
@@ -321,4 +326,4 @@
 Return<void> RadioIndication_v1_4::modemReset(RadioIndicationType /*type*/,
                                               const ::android::hardware::hidl_string& /*reason*/) {
     return Void();
-}
\ No newline at end of file
+}
diff --git a/radio/1.4/vts/functional/radio_response.cpp b/radio/1.4/vts/functional/radio_response.cpp
index 2ae5f7c..10ecead 100644
--- a/radio/1.4/vts/functional/radio_response.cpp
+++ b/radio/1.4/vts/functional/radio_response.cpp
@@ -739,6 +739,13 @@
     return Void();
 }
 
+Return<void> RadioResponse_v1_4::getSignalStrengthResponse_1_4(const RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_4::SignalStrength& /*sig_strength*/) {
+    rspInfo = info;
+    parent_v1_4.notify(info.serial);
+    return Void();
+}
+
 Return<void> RadioResponse_v1_4::getCellInfoListResponse_1_2(
         const RadioResponseInfo& /*info*/,
         const ::android::hardware::hidl_vec<
@@ -864,4 +871,4 @@
         const RadioResponseInfo& /*info*/, const CarrierRestrictionsWithPriority& /*carriers*/,
         SimLockMultiSimPolicy /*multiSimPolicy*/) {
     return Void();
-}
\ No newline at end of file
+}
diff --git a/sensors/2.0/Android.bp b/sensors/2.0/Android.bp
index 3b948a9..57d45ff 100644
--- a/sensors/2.0/Android.bp
+++ b/sensors/2.0/Android.bp
@@ -18,6 +18,7 @@
     types: [
         "EventQueueFlagBits",
         "SensorTimeout",
+        "WakeLockQueueFlagBits",
     ],
     gen_java: false,
 }
diff --git a/sensors/2.0/default/Android.bp b/sensors/2.0/default/Android.bp
index d83a9c3..d05634b 100644
--- a/sensors/2.0/default/Android.bp
+++ b/sensors/2.0/default/Android.bp
@@ -14,7 +14,7 @@
 // limitations under the License.
 
 cc_binary {
-    name: "android.hardware.sensors@2.0-service",
+    name: "android.hardware.sensors@2.0-service.mock",
     defaults: ["hidl_defaults"],
     vendor: true,
     relative_install_path: "hw",
diff --git a/sensors/2.0/default/Sensor.cpp b/sensors/2.0/default/Sensor.cpp
index 373ab12..c09173f 100644
--- a/sensors/2.0/default/Sensor.cpp
+++ b/sensors/2.0/default/Sensor.cpp
@@ -18,6 +18,8 @@
 
 #include <utils/SystemClock.h>
 
+#include <cmath>
+
 namespace android {
 namespace hardware {
 namespace sensors {
@@ -28,6 +30,8 @@
 using ::android::hardware::sensors::V1_0::SensorFlagBits;
 using ::android::hardware::sensors::V1_0::SensorStatus;
 
+static constexpr float kDefaultMaxDelayUs = 10 * 1000 * 1000;
+
 Sensor::Sensor(ISensorsEventCallback* callback)
     : mIsEnabled(false),
       mSamplingPeriodNs(0),
@@ -83,7 +87,7 @@
     // to the Event FMQ prior to writing the flush complete event.
     Event ev;
     ev.sensorHandle = mSensorInfo.sensorHandle;
-    ev.sensorType = SensorType::ADDITIONAL_INFO;
+    ev.sensorType = SensorType::META_DATA;
     ev.u.meta.what = MetaDataEventType::META_DATA_FLUSH_COMPLETE;
     std::vector<Event> evs{ev};
     mCallback->postEvents(evs, isWakeUpSensor());
@@ -202,7 +206,7 @@
     mSensorInfo.resolution = 1.52e-5;
     mSensorInfo.power = 0.001f;          // mA
     mSensorInfo.minDelay = 20 * 1000;    // microseconds
-    mSensorInfo.maxDelay = 1000 * 1000;  // microseconds
+    mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
     mSensorInfo.requiredPermission = "";
@@ -218,10 +222,10 @@
     mSensorInfo.type = SensorType::PRESSURE;
     mSensorInfo.typeAsString = "";
     mSensorInfo.maxRange = 1100.0f;   // hPa
-    mSensorInfo.resolution = 1.0f;    // hPa
+    mSensorInfo.resolution = 0.005f;  // hPa
     mSensorInfo.power = 0.001f;       // mA
-    mSensorInfo.minDelay = 28571.0f;  // microseconds
-    mSensorInfo.maxDelay = 0.0f;      // microseconds
+    mSensorInfo.minDelay = 100 * 1000;  // microseconds
+    mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
     mSensorInfo.requiredPermission = "";
@@ -236,11 +240,11 @@
     mSensorInfo.version = 1;
     mSensorInfo.type = SensorType::MAGNETIC_FIELD;
     mSensorInfo.typeAsString = "";
-    mSensorInfo.maxRange = 4911.0f;
-    mSensorInfo.resolution = 1.00f;
+    mSensorInfo.maxRange = 1300.0f;
+    mSensorInfo.resolution = 0.01f;
     mSensorInfo.power = 0.001f;       // mA
-    mSensorInfo.minDelay = 14284.0f;  // microseconds
-    mSensorInfo.maxDelay = 0.0f;      // microseconds
+    mSensorInfo.minDelay = 20 * 1000;  // microseconds
+    mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
     mSensorInfo.requiredPermission = "";
@@ -255,11 +259,11 @@
     mSensorInfo.version = 1;
     mSensorInfo.type = SensorType::LIGHT;
     mSensorInfo.typeAsString = "";
-    mSensorInfo.maxRange = 10000.0f;
+    mSensorInfo.maxRange = 43000.0f;
     mSensorInfo.resolution = 10.0f;
     mSensorInfo.power = 0.001f;           // mA
-    mSensorInfo.minDelay = 20.0f * 1000;  // microseconds
-    mSensorInfo.maxDelay = 0;             // microseconds
+    mSensorInfo.minDelay = 200 * 1000;    // microseconds
+    mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
     mSensorInfo.requiredPermission = "";
@@ -277,8 +281,8 @@
     mSensorInfo.maxRange = 5.0f;
     mSensorInfo.resolution = 1.0f;
     mSensorInfo.power = 0.012f;  // mA
-    mSensorInfo.minDelay = 500;  // microseconds
-    mSensorInfo.maxDelay = 2 * mSensorInfo.minDelay;
+    mSensorInfo.minDelay = 200 * 1000;  // microseconds
+    mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
     mSensorInfo.requiredPermission = "";
@@ -293,11 +297,11 @@
     mSensorInfo.version = 1;
     mSensorInfo.type = SensorType::GYROSCOPE;
     mSensorInfo.typeAsString = "";
-    mSensorInfo.maxRange = 8.726639f;
-    mSensorInfo.resolution = 1.0f;
+    mSensorInfo.maxRange = 1000.0f * M_PI / 180.0f;
+    mSensorInfo.resolution = 1000.0f * M_PI / (180.0f * 32768.0f);
     mSensorInfo.power = 0.001f;
-    mSensorInfo.minDelay = 4444;  // microseonds
-    mSensorInfo.maxDelay = 0;     // microseconds
+    mSensorInfo.minDelay = 2.5f * 1000;  // microseconds
+    mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
     mSensorInfo.requiredPermission = "";
@@ -313,10 +317,10 @@
     mSensorInfo.type = SensorType::AMBIENT_TEMPERATURE;
     mSensorInfo.typeAsString = "";
     mSensorInfo.maxRange = 80.0f;
-    mSensorInfo.resolution = 1.0f;
+    mSensorInfo.resolution = 0.01f;
     mSensorInfo.power = 0.001f;
-    mSensorInfo.minDelay = 4444;  // microseonds
-    mSensorInfo.maxDelay = 0;     // microseconds
+    mSensorInfo.minDelay = 40 * 1000;  // microseconds
+    mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
     mSensorInfo.requiredPermission = "";
@@ -332,10 +336,10 @@
     mSensorInfo.type = SensorType::TEMPERATURE;
     mSensorInfo.typeAsString = "";
     mSensorInfo.maxRange = 80.0f;
-    mSensorInfo.resolution = 1.0f;
+    mSensorInfo.resolution = 0.01f;
     mSensorInfo.power = 0.001f;
-    mSensorInfo.minDelay = 4444;  // microseonds
-    mSensorInfo.maxDelay = 0;     // microseconds
+    mSensorInfo.minDelay = 40 * 1000;  // microseconds
+    mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
     mSensorInfo.requiredPermission = "";
@@ -352,10 +356,10 @@
     mSensorInfo.type = SensorType::RELATIVE_HUMIDITY;
     mSensorInfo.typeAsString = "";
     mSensorInfo.maxRange = 100.0f;
-    mSensorInfo.resolution = 1.0f;
+    mSensorInfo.resolution = 0.1f;
     mSensorInfo.power = 0.001f;
-    mSensorInfo.minDelay = 4444;  // microseonds
-    mSensorInfo.maxDelay = 0;     // microseconds
+    mSensorInfo.minDelay = 40 * 1000;  // microseconds
+    mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
     mSensorInfo.requiredPermission = "";
diff --git a/sensors/2.0/default/android.hardware.sensors@2.0-service.rc b/sensors/2.0/default/android.hardware.sensors@2.0-service.rc
index 321d760..1fee4f2 100644
--- a/sensors/2.0/default/android.hardware.sensors@2.0-service.rc
+++ b/sensors/2.0/default/android.hardware.sensors@2.0-service.rc
@@ -1,4 +1,4 @@
-service vendor.sensors-hal-2-0 /vendor/bin/hw/android.hardware.sensors@2.0-service
+service vendor.sensors-hal-2-0-mock /vendor/bin/hw/android.hardware.sensors@2.0-service.mock
     class hal
     user system
     group system
diff --git a/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp b/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
index 4a1f8f1..39053fe 100644
--- a/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
+++ b/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
@@ -51,7 +51,7 @@
     }
 
     void onEvent(const ::android::hardware::sensors::V1_0::Event& event) override {
-        if (event.sensorType == SensorType::ADDITIONAL_INFO &&
+        if (event.sensorType == SensorType::META_DATA &&
             event.u.meta.what == MetaDataEventType::META_DATA_FLUSH_COMPLETE) {
             std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
             mFlushMap[event.sensorHandle]++;
diff --git a/tests/libhwbinder/1.0/default/Android.bp b/tests/libhwbinder/1.0/default/Android.bp
index 3d18720..aad1e31 100644
--- a/tests/libhwbinder/1.0/default/Android.bp
+++ b/tests/libhwbinder/1.0/default/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
     name: "android.hardware.tests.libhwbinder@1.0-impl",
     defaults: ["hidl_defaults"],
     relative_install_path: "hw",
diff --git a/thermal/2.0/IThermal.hal b/thermal/2.0/IThermal.hal
index f890694..3ea4590 100644
--- a/thermal/2.0/IThermal.hal
+++ b/thermal/2.0/IThermal.hal
@@ -56,22 +56,24 @@
      *    they go offline, if these devices exist on boot. The method
      *    always returns and never removes such temperatures. The thresholds
      *    are returned as static values and must not change across calls. The actual
-     *    throttling state is determined in driver and HAL and must not be simply
-     *    compared with these thresholds. To get accurate throttling status, use
-     *    getCurrentTemperatures or registerThermalChangedCallback and listen.
+     *    throttling state is determined in device thermal mitigation policy/agorithm
+     *    which might not be simple thresholds so these values Thermal HAL provided
+     *    may not be accurate to detemin the throttling status. To get accurate
+     *    throttling status, use getCurrentTemperatures or registerThermalChangedCallback
+     *    and listen to the callback.
      */
     getTemperatureThresholds(bool filterType, TemperatureType type)
         generates (ThermalStatus status, vec<TemperatureThreshold> temperatureThresholds);
 
    /**
     * Register an IThermalChangedCallback, used by the Thermal HAL
-    * to send thermal events when thermal mitigation status changed.
+    * to receive thermal events when thermal mitigation status changed.
     * Multiple registrations with different IThermalChangedCallback must be allowed.
     * Multiple registrations with same IThermalChangedCallback is not allowed, client
     * should unregister the given IThermalChangedCallback first.
     *
-    * @param callback the IThermalChangedCallback to use for sending
-    *    thermal events (cannot be nullptr).
+    * @param callback the IThermalChangedCallback to use for receiving
+    *    thermal events (nullptr callback will lead to failure with status code FAILURE).
     * @param filterType if filter for given sensor type.
     * @param type the type to be filtered.
     *
@@ -84,11 +86,11 @@
        generates (ThermalStatus status);
 
    /**
-    * Register an IThermalChangedCallback, used by the Thermal HAL
-    * to send thermal events when thermal mitigation status changed.
+    * Unregister an IThermalChangedCallback, used by the Thermal HAL
+    * to receive thermal events when thermal mitigation status changed.
     *
-    * @param callback the IThermalChangedCallback to use for sending
-    *    thermal events, or nullptr to set no callback.
+    * @param callback the IThermalChangedCallback used for receiving
+    *    thermal events (nullptr callback will lead to failure with status code FAILURE).
     *
     * @return status Status of the operation. If status code is FAILURE,
     *    the status.debugMessage must be populated with a human-readable error message.
diff --git a/thermal/2.0/default/Thermal.cpp b/thermal/2.0/default/Thermal.cpp
index 442af61..0ef4b63 100644
--- a/thermal/2.0/default/Thermal.cpp
+++ b/thermal/2.0/default/Thermal.cpp
@@ -155,7 +155,15 @@
                                                      bool filterType, TemperatureType type,
                                                      registerThermalChangedCallback_cb _hidl_cb) {
     ThermalStatus status;
-    status.code = ThermalStatusCode::SUCCESS;
+    if (callback == nullptr) {
+        status.code = ThermalStatusCode::FAILURE;
+        status.debugMessage = "Invalid nullptr callback";
+        LOG(ERROR) << status.debugMessage;
+        _hidl_cb(status);
+        return Void();
+    } else {
+        status.code = ThermalStatusCode::SUCCESS;
+    }
     std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
     if (std::any_of(callbacks_.begin(), callbacks_.end(), [&](const CallbackSetting& c) {
             return interfacesEqual(c.callback, callback);
@@ -175,7 +183,15 @@
 Return<void> Thermal::unregisterThermalChangedCallback(
     const sp<IThermalChangedCallback>& callback, unregisterThermalChangedCallback_cb _hidl_cb) {
     ThermalStatus status;
-    status.code = ThermalStatusCode::SUCCESS;
+    if (callback == nullptr) {
+        status.code = ThermalStatusCode::FAILURE;
+        status.debugMessage = "Invalid nullptr callback";
+        LOG(ERROR) << status.debugMessage;
+        _hidl_cb(status);
+        return Void();
+    } else {
+        status.code = ThermalStatusCode::SUCCESS;
+    }
     bool removed = false;
     std::lock_guard<std::mutex> _lock(thermal_callback_mutex_);
     callbacks_.erase(
diff --git a/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp b/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp
index 3893014..cc2f3df 100644
--- a/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp
+++ b/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp
@@ -132,8 +132,13 @@
 TEST_F(ThermalHidlTest, RegisterThermalChangedCallbackTest) {
     // Expect to fail with same callback
     auto ret = mThermal->registerThermalChangedCallback(
-        mThermalCallback, false, TemperatureType::SKIN,
-        [](ThermalStatus status) { EXPECT_NE(ThermalStatusCode::SUCCESS, status.code); });
+            mThermalCallback, false, TemperatureType::SKIN,
+            [](ThermalStatus status) { EXPECT_EQ(ThermalStatusCode::FAILURE, status.code); });
+    ASSERT_TRUE(ret.isOk());
+    // Expect to fail with null callback
+    ret = mThermal->registerThermalChangedCallback(
+            nullptr, false, TemperatureType::SKIN,
+            [](ThermalStatus status) { EXPECT_EQ(ThermalStatusCode::FAILURE, status.code); });
     ASSERT_TRUE(ret.isOk());
     sp<ThermalCallback> localThermalCallback = new (std::nothrow) ThermalCallback();
     // Expect to succeed with different callback
@@ -141,11 +146,16 @@
         localThermalCallback, false, TemperatureType::SKIN,
         [](ThermalStatus status) { EXPECT_EQ(ThermalStatusCode::SUCCESS, status.code); });
     ASSERT_TRUE(ret.isOk());
-    // Remove the local callback.
+    // Remove the local callback
     ret = mThermal->unregisterThermalChangedCallback(
         localThermalCallback,
         [](ThermalStatus status) { EXPECT_EQ(ThermalStatusCode::SUCCESS, status.code); });
     ASSERT_TRUE(ret.isOk());
+    // Expect to fail with null callback
+    ret = mThermal->unregisterThermalChangedCallback(nullptr, [](ThermalStatus status) {
+        EXPECT_EQ(ThermalStatusCode::FAILURE, status.code);
+    });
+    ASSERT_TRUE(ret.isOk());
 }
 
 // Test Thermal->unregisterThermalChangedCallback.
diff --git a/vibrator/1.3/example/Android.bp b/vibrator/1.3/example/Android.bp
new file mode 100644
index 0000000..36f2ff8
--- /dev/null
+++ b/vibrator/1.3/example/Android.bp
@@ -0,0 +1,34 @@
+//
+// 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.
+
+cc_binary {
+    name: "android.hardware.vibrator@1.3-service.example",
+    vendor: true,
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.vibrator@1.3-service.example.rc"],
+    vintf_fragments: ["android.hardware.vibrator@1.3-service.example.xml"],
+    srcs: ["service.cpp", "Vibrator.cpp"],
+    cflags: ["-Wall", "-Werror"],
+    shared_libs: [
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libutils",
+        "android.hardware.vibrator@1.0",
+        "android.hardware.vibrator@1.1",
+        "android.hardware.vibrator@1.2",
+        "android.hardware.vibrator@1.3",
+    ],
+}
diff --git a/vibrator/1.3/example/Vibrator.cpp b/vibrator/1.3/example/Vibrator.cpp
new file mode 100644
index 0000000..bb9a057
--- /dev/null
+++ b/vibrator/1.3/example/Vibrator.cpp
@@ -0,0 +1,242 @@
+/*
+ * 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_TAG "VibratorService"
+
+#include <log/log.h>
+
+#include "Vibrator.h"
+
+namespace android {
+namespace hardware {
+namespace vibrator {
+namespace V1_3 {
+namespace implementation {
+
+static constexpr uint32_t MS_PER_S = 1000;
+static constexpr uint32_t NS_PER_MS = 1000000;
+
+Vibrator::Vibrator() {
+    sigevent se{};
+    se.sigev_notify = SIGEV_THREAD;
+    se.sigev_value.sival_ptr = this;
+    se.sigev_notify_function = timerCallback;
+    se.sigev_notify_attributes = nullptr;
+
+    if (timer_create(CLOCK_REALTIME, &se, &mTimer) < 0) {
+        ALOGE("Can not create timer!%s", strerror(errno));
+    }
+}
+
+// Methods from ::android::hardware::vibrator::V1_0::IVibrator follow.
+
+Return<Status> Vibrator::on(uint32_t timeoutMs) {
+    return activate(timeoutMs);
+}
+
+Return<Status> Vibrator::off() {
+    return activate(0);
+}
+
+Return<bool> Vibrator::supportsAmplitudeControl() {
+    return true;
+}
+
+Return<Status> Vibrator::setAmplitude(uint8_t amplitude) {
+    ALOGI("Amplitude: %u -> %u\n", mAmplitude, amplitude);
+    mAmplitude = amplitude;
+    return Status::OK;
+}
+
+Return<void> Vibrator::perform(V1_0::Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
+    return perform_1_1(static_cast<V1_1::Effect_1_1>(effect), strength, _hidl_cb);
+}
+
+// Methods from ::android::hardware::vibrator::V1_1::IVibrator follow.
+
+Return<void> Vibrator::perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength,
+                                   perform_cb _hidl_cb) {
+    return perform_1_2(static_cast<V1_2::Effect>(effect), strength, _hidl_cb);
+}
+
+// Methods from ::android::hardware::vibrator::V1_2::IVibrator follow.
+
+Return<void> Vibrator::perform_1_2(Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
+    uint8_t amplitude;
+    uint32_t ms;
+    Status status;
+
+    ALOGI("Perform: Effect %s\n", effectToName(effect));
+
+    amplitude = strengthToAmplitude(strength);
+    setAmplitude(amplitude);
+
+    ms = effectToMs(effect);
+    status = activate(ms);
+
+    _hidl_cb(status, ms);
+
+    return Void();
+}
+
+// Methods from ::android::hardware::vibrator::V1_3::IVibrator follow.
+
+Return<bool> Vibrator::supportsExternalControl() {
+    return true;
+}
+
+Return<Status> Vibrator::setExternalControl(bool enabled) {
+    if (mEnabled) {
+        ALOGW("Setting external control while the vibrator is enabled is unsupported!");
+        return Status::UNSUPPORTED_OPERATION;
+    } else {
+        ALOGI("ExternalControl: %s -> %s\n", mExternalControl ? "true" : "false",
+              enabled ? "true" : "false");
+        mExternalControl = enabled;
+        return Status::OK;
+    }
+}
+
+// Private methods follow.
+
+Status Vibrator::enable(bool enabled) {
+    if (mExternalControl) {
+        ALOGW("Enabling/disabling while the vibrator is externally controlled is unsupported!");
+        return Status::UNSUPPORTED_OPERATION;
+    } else {
+        ALOGI("Enabled: %s -> %s\n", mEnabled ? "true" : "false", enabled ? "true" : "false");
+        mEnabled = enabled;
+        return Status::OK;
+    }
+}
+
+Status Vibrator::activate(uint32_t ms) {
+    std::lock_guard<std::mutex> lock{mMutex};
+    Status status = Status::OK;
+
+    if (ms > 0) {
+        status = enable(true);
+        if (status != Status::OK) {
+            return status;
+        }
+    }
+
+    itimerspec ts{};
+    ts.it_value.tv_sec = ms / MS_PER_S;
+    ts.it_value.tv_nsec = ms % MS_PER_S * NS_PER_MS;
+
+    if (timer_settime(mTimer, 0, &ts, nullptr) < 0) {
+        ALOGE("Can not set timer!");
+        status = Status::UNKNOWN_ERROR;
+    }
+
+    if ((status != Status::OK) || !ms) {
+        Status _status;
+
+        _status = enable(false);
+
+        if (status == Status::OK) {
+            status = _status;
+        }
+    }
+
+    return status;
+}
+
+void Vibrator::timeout() {
+    std::lock_guard<std::mutex> lock{mMutex};
+    itimerspec ts{};
+
+    if (timer_gettime(mTimer, &ts) < 0) {
+        ALOGE("Can not read timer!");
+    }
+
+    if (ts.it_value.tv_sec == 0 && ts.it_value.tv_nsec == 0) {
+        enable(false);
+    }
+}
+
+void Vibrator::timerCallback(union sigval sigval) {
+    static_cast<Vibrator*>(sigval.sival_ptr)->timeout();
+}
+
+const char* Vibrator::effectToName(Effect effect) {
+    return toString(effect).c_str();
+}
+
+uint32_t Vibrator::effectToMs(Effect effect) {
+    switch (effect) {
+        case Effect::CLICK:
+            return 10;
+        case Effect::DOUBLE_CLICK:
+            return 15;
+        case Effect::TICK:
+            return 5;
+        case Effect::THUD:
+            return 5;
+        case Effect::POP:
+            return 5;
+        case Effect::HEAVY_CLICK:
+            return 10;
+        case Effect::RINGTONE_1:
+            return 30000;
+        case Effect::RINGTONE_2:
+            return 30000;
+        case Effect::RINGTONE_3:
+            return 30000;
+        case Effect::RINGTONE_4:
+            return 30000;
+        case Effect::RINGTONE_5:
+            return 30000;
+        case Effect::RINGTONE_6:
+            return 30000;
+        case Effect::RINGTONE_7:
+            return 30000;
+        case Effect::RINGTONE_8:
+            return 30000;
+        case Effect::RINGTONE_9:
+            return 30000;
+        case Effect::RINGTONE_10:
+            return 30000;
+        case Effect::RINGTONE_11:
+            return 30000;
+        case Effect::RINGTONE_12:
+            return 30000;
+        case Effect::RINGTONE_13:
+            return 30000;
+        case Effect::RINGTONE_14:
+            return 30000;
+        case Effect::RINGTONE_15:
+            return 30000;
+    }
+}
+
+uint8_t Vibrator::strengthToAmplitude(EffectStrength strength) {
+    switch (strength) {
+        case EffectStrength::LIGHT:
+            return 128;
+        case EffectStrength::MEDIUM:
+            return 192;
+        case EffectStrength::STRONG:
+            return 255;
+    }
+}
+
+}  // namespace implementation
+}  // namespace V1_3
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
diff --git a/vibrator/1.3/example/Vibrator.h b/vibrator/1.3/example/Vibrator.h
new file mode 100644
index 0000000..a931b63
--- /dev/null
+++ b/vibrator/1.3/example/Vibrator.h
@@ -0,0 +1,78 @@
+/*
+ * 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_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H
+#define ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace vibrator {
+namespace V1_3 {
+namespace implementation {
+
+using android::hardware::vibrator::V1_0::EffectStrength;
+using android::hardware::vibrator::V1_0::Status;
+using android::hardware::vibrator::V1_2::Effect;
+
+class Vibrator : public IVibrator {
+  public:
+    Vibrator();
+
+    // Methods from ::android::hardware::vibrator::V1_0::IVibrator follow.
+    Return<Status> on(uint32_t timeoutMs) override;
+    Return<Status> off() override;
+    Return<bool> supportsAmplitudeControl() override;
+    Return<Status> setAmplitude(uint8_t amplitude) override;
+    Return<void> perform(V1_0::Effect effect, EffectStrength strength,
+                         perform_cb _hidl_cb) override;
+
+    // Methods from ::android::hardware::vibrator::V1_1::IVibrator follow.
+    Return<void> perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength,
+                             perform_cb _hidl_cb) override;
+
+    // Methods from ::android::hardware::vibrator::V1_2::IVibrator follow.
+    Return<void> perform_1_2(Effect effect, EffectStrength strength, perform_cb _hidl_cb) override;
+
+    // Methods from ::android::hardware::vibrator::V1_3::IVibrator follow.
+    Return<bool> supportsExternalControl() override;
+    Return<Status> setExternalControl(bool enabled) override;
+
+  private:
+    Status enable(bool enabled);
+    Status activate(uint32_t ms);
+    void timeout();
+
+    static void timerCallback(union sigval sigval);
+    static const char* effectToName(Effect effect);
+    static uint32_t effectToMs(Effect effect);
+    static uint8_t strengthToAmplitude(EffectStrength strength);
+
+  private:
+    bool mEnabled{false};
+    uint8_t mAmplitude{UINT8_MAX};
+    bool mExternalControl{false};
+    std::mutex mMutex;
+    timer_t mTimer{nullptr};
+};
+}  // namespace implementation
+}  // namespace V1_3
+}  // namespace vibrator
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H
diff --git a/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.rc b/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.rc
new file mode 100644
index 0000000..ed7a562
--- /dev/null
+++ b/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.rc
@@ -0,0 +1,4 @@
+service vendor.vibrator-1-3 /vendor/bin/hw/android.hardware.vibrator@1.3-service.example
+    class hal
+    user system
+    group system
diff --git a/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.xml b/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.xml
new file mode 100644
index 0000000..172aa21
--- /dev/null
+++ b/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.vibrator</name>
+        <transport>hwbinder</transport>
+        <version>1.3</version>
+        <interface>
+            <name>IVibrator</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/vibrator/1.3/example/service.cpp b/vibrator/1.3/example/service.cpp
new file mode 100644
index 0000000..449996e
--- /dev/null
+++ b/vibrator/1.3/example/service.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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_TAG "android.hardware.vibrator@1.3-service.example"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "Vibrator.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::vibrator::V1_3::IVibrator;
+using android::hardware::vibrator::V1_3::implementation::Vibrator;
+using namespace android;
+
+status_t registerVibratorService() {
+    sp<IVibrator> vibrator = new Vibrator();
+
+    return vibrator->registerAsService();
+}
+
+int main() {
+    configureRpcThreadpool(1, true);
+    status_t status = registerVibratorService();
+
+    if (status != OK) {
+        return status;
+    }
+
+    joinRpcThreadpool();
+
+    return 1;
+}
diff --git a/wifi/1.3/Android.bp b/wifi/1.3/Android.bp
index 45e2e88..8af1dc4 100644
--- a/wifi/1.3/Android.bp
+++ b/wifi/1.3/Android.bp
@@ -19,9 +19,9 @@
         "android.hidl.base@1.0",
     ],
     types: [
-      "StaLinkLayerRadioStats",
-      "StaLinkLayerStats",
-      "WifiChannelStats",
+        "StaLinkLayerRadioStats",
+        "StaLinkLayerStats",
+        "WifiChannelStats",
     ],
     gen_java: true,
 }
diff --git a/wifi/1.3/default/Android.mk b/wifi/1.3/default/Android.mk
index 8312c31..01fa934 100644
--- a/wifi/1.3/default/Android.mk
+++ b/wifi/1.3/default/Android.mk
@@ -98,6 +98,37 @@
 include $(BUILD_EXECUTABLE)
 
 ###
+### android.hardware.wifi daemon
+###
+include $(CLEAR_VARS)
+LOCAL_MODULE := android.hardware.wifi@1.0-service-lazy
+LOCAL_OVERRIDES_MODULES := android.hardware.wifi@1.0-service
+LOCAL_CFLAGS := -DLAZY_SERVICE
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_CPPFLAGS := -Wall -Werror -Wextra
+LOCAL_SRC_FILES := \
+    service.cpp
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libcutils \
+    libhidlbase \
+    libhidltransport \
+    liblog \
+    libnl \
+    libutils \
+    libwifi-hal \
+    libwifi-system-iface \
+    android.hardware.wifi@1.0 \
+    android.hardware.wifi@1.1 \
+    android.hardware.wifi@1.2 \
+    android.hardware.wifi@1.3
+LOCAL_STATIC_LIBRARIES := \
+    android.hardware.wifi@1.0-service-lib
+LOCAL_INIT_RC := android.hardware.wifi@1.0-service-lazy.rc
+include $(BUILD_EXECUTABLE)
+
+###
 ### android.hardware.wifi unit tests.
 ###
 include $(CLEAR_VARS)
diff --git a/wifi/1.3/default/android.hardware.wifi@1.0-service-lazy.rc b/wifi/1.3/default/android.hardware.wifi@1.0-service-lazy.rc
new file mode 100644
index 0000000..cf917b5
--- /dev/null
+++ b/wifi/1.3/default/android.hardware.wifi@1.0-service-lazy.rc
@@ -0,0 +1,8 @@
+service vendor.wifi_hal_legacy /vendor/bin/hw/android.hardware.wifi@1.0-service-lazy
+    interface android.hardware.wifi@1.0::IWifi default
+    oneshot
+    disabled
+    class hal
+    capabilities NET_ADMIN NET_RAW SYS_MODULE
+    user wifi
+    group wifi gps
diff --git a/wifi/1.3/default/hidl_struct_util.cpp b/wifi/1.3/default/hidl_struct_util.cpp
index 49c1477..2e4db70 100644
--- a/wifi/1.3/default/hidl_struct_util.cpp
+++ b/wifi/1.3/default/hidl_struct_util.cpp
@@ -1259,10 +1259,6 @@
         hidl_request.debugConfigs
             .useSdfInBandVal[(size_t)NanBandIndex::NAN_BAND_5GHZ];
 
-    // disable NANv3 NDPe
-    legacy_request->config_ndpe_attr = 1;
-    legacy_request->use_ndpe_attr = 0;
-
     return true;
 }
 
@@ -1774,10 +1770,6 @@
         hidl_request.bandSpecificConfig[(size_t)NanBandIndex::NAN_BAND_5GHZ]
             .discoveryWindowIntervalVal;
 
-    // disable NANv3 NDPe
-    legacy_request->config_ndpe_attr = 1;
-    legacy_request->use_ndpe_attr = 0;
-
     return true;
 }
 
diff --git a/wifi/1.3/default/service.cpp b/wifi/1.3/default/service.cpp
index 5fd83c1..5daf659 100644
--- a/wifi/1.3/default/service.cpp
+++ b/wifi/1.3/default/service.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android-base/logging.h>
+#include <hidl/HidlLazyUtils.h>
 #include <hidl/HidlTransportSupport.h>
 #include <utils/Looper.h>
 #include <utils/StrongPointer.h>
@@ -26,12 +27,19 @@
 
 using android::hardware::configureRpcThreadpool;
 using android::hardware::joinRpcThreadpool;
+using android::hardware::LazyServiceRegistrar;
 using android::hardware::wifi::V1_3::implementation::feature_flags::
     WifiFeatureFlags;
 using android::hardware::wifi::V1_3::implementation::legacy_hal::WifiLegacyHal;
 using android::hardware::wifi::V1_3::implementation::mode_controller::
     WifiModeController;
 
+#ifdef LAZY_SERVICE
+const bool kLazyService = true;
+#else
+const bool kLazyService = false;
+#endif
+
 int main(int /*argc*/, char** argv) {
     android::base::InitLogging(
         argv, android::base::LogdLogger(android::base::SYSTEM));
@@ -45,8 +53,14 @@
             std::make_shared<WifiLegacyHal>(),
             std::make_shared<WifiModeController>(),
             std::make_shared<WifiFeatureFlags>());
-    CHECK_EQ(service->registerAsService(), android::NO_ERROR)
-        << "Failed to register wifi HAL";
+    if (kLazyService) {
+        LazyServiceRegistrar registrar;
+        CHECK_EQ(registrar.registerService(service), android::NO_ERROR)
+            << "Failed to register wifi HAL";
+    } else {
+        CHECK_EQ(service->registerAsService(), android::NO_ERROR)
+            << "Failed to register wifi HAL";
+    }
 
     joinRpcThreadpool();
 
diff --git a/wifi/supplicant/1.2/Android.bp b/wifi/supplicant/1.2/Android.bp
index 18e1cca..a0fc2b1 100644
--- a/wifi/supplicant/1.2/Android.bp
+++ b/wifi/supplicant/1.2/Android.bp
@@ -7,12 +7,12 @@
         enabled: true,
     },
     srcs: [
+        "types.hal",
         "ISupplicant.hal",
         "ISupplicantP2pIface.hal",
         "ISupplicantStaIface.hal",
         "ISupplicantStaIfaceCallback.hal",
         "ISupplicantStaNetwork.hal",
-        "types.hal",
     ],
     interfaces: [
         "android.hardware.wifi.supplicant@1.0",
@@ -21,10 +21,9 @@
     ],
     types: [
         "DppAkm",
-        "DppNetRole",
-        "DppSuccessCode",
-        "DppProgressCode",
         "DppFailureCode",
+        "DppNetRole",
+        "DppProgressCode",
     ],
     gen_java: true,
 }
diff --git a/wifi/supplicant/1.2/ISupplicantStaIfaceCallback.hal b/wifi/supplicant/1.2/ISupplicantStaIfaceCallback.hal
index 5d5cccf..3eac398 100644
--- a/wifi/supplicant/1.2/ISupplicantStaIfaceCallback.hal
+++ b/wifi/supplicant/1.2/ISupplicantStaIfaceCallback.hal
@@ -37,7 +37,7 @@
     /**
      * Indicates DPP configuration sent success event (Configurator mode).
      */
-    oneway onDppSuccess(DppSuccessCode code);
+    oneway onDppSuccessConfigSent();
 
     /**
      * Indicates a DPP progress event.
diff --git a/wifi/supplicant/1.2/types.hal b/wifi/supplicant/1.2/types.hal
index eaf2546..303af19 100644
--- a/wifi/supplicant/1.2/types.hal
+++ b/wifi/supplicant/1.2/types.hal
@@ -35,13 +35,6 @@
 };
 
 /**
- * DppSuccessCode: Success codes for DPP (Easy Connect)
- */
-enum DppSuccessCode : uint32_t {
-    CONFIGURATION_SENT,
-};
-
-/**
  * DppProgressCode: Progress codes for DPP (Easy Connect)
  */
 enum DppProgressCode : uint32_t {
diff --git a/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
index 4425281..2ff7751 100644
--- a/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -40,7 +40,6 @@
 using ::android::hardware::wifi::supplicant::V1_2::DppFailureCode;
 using ::android::hardware::wifi::supplicant::V1_2::DppNetRole;
 using ::android::hardware::wifi::supplicant::V1_2::DppProgressCode;
-using ::android::hardware::wifi::supplicant::V1_2::DppSuccessCode;
 using ::android::hardware::wifi::supplicant::V1_2::ISupplicantStaIface;
 using ::android::hardware::wifi::supplicant::V1_2::ISupplicantStaIfaceCallback;
 using ::android::hardware::wifi::supplicant::V1_2::ISupplicantStaNetwork;
@@ -64,7 +63,7 @@
         ANY_CALLBACK = -2,
         INVALID = -1,
 
-        EVENT_SUCCESS = 0,
+        EVENT_SUCCESS_CONFIG_SENT = 0,
         EVENT_SUCCESS_CONFIG_RECEIVED,
         EVENT_PROGRESS,
         EVENT_FAILURE,
@@ -202,9 +201,7 @@
         DppAkm /* securityAkm */) override {
         return Void();
     }
-    Return<void> onDppSuccess(DppSuccessCode /* code */) override {
-        return Void();
-    }
+    Return<void> onDppSuccessConfigSent() override { return Void(); }
     Return<void> onDppProgress(DppProgressCode /* code */) override {
         return Void();
     }
@@ -226,10 +223,10 @@
         parent_.notify();
         return Void();
     }
-    Return<void> onDppSuccess(DppSuccessCode code) override {
-        parent_.code = (uint32_t)code;
-        parent_.dppCallbackType =
-            SupplicantStaIfaceHidlTest::DppCallbackType::EVENT_SUCCESS;
+    Return<void> onDppSuccessConfigSent() override {
+        parent_.code = 0;
+        parent_.dppCallbackType = SupplicantStaIfaceHidlTest::DppCallbackType::
+            EVENT_SUCCESS_CONFIG_SENT;
         parent_.notify();
         return Void();
     }