Merge "Add OTHER to GnssMeasurementCodeType"
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/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/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/IBiometricsFace.hal b/biometrics/face/1.0/IBiometricsFace.hal
index e3c256a..813f040 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.
*/
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/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..e52577c 100644
--- a/camera/device/3.4/default/CameraDeviceSession.cpp
+++ b/camera/device/3.4/default/CameraDeviceSession.cpp
@@ -87,6 +87,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 +105,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 +117,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 +125,7 @@
" trigger overrides!", __FUNCTION__,
mInflightAETriggerOverrides.size());
_hidl_cb(Status::INTERNAL_ERROR, outStreams);
- return Void();
+ return;
}
if (!mInflightRawBoostPresent.empty()) {
@@ -125,12 +133,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 +147,12 @@
}
camera3_stream_configuration_t stream_list{};
+ stream_list.stream_configuration_counter = 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 +177,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..00500b1 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,
diff --git a/camera/device/3.5/default/CameraDeviceSession.cpp b/camera/device/3.5/default/CameraDeviceSession.cpp
index 963893a..0770f04 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,14 +65,297 @@
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) {
+ 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];
+ }
+ }
+ if (mDevice->ops->signal_stream_flush != nullptr) {
+ mDevice->ops->signal_stream_flush(mDevice,
+ streamConfigCounter, 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();
+
+ if (status == BufferRequestStatus::OK || status == BufferRequestStatus::FAILED_PARTIAL) {
+ 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;
+ }
+
+ 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;
+ }
+
+ std::vector<int> importedFences;
+ std::vector<std::pair<buffer_handle_t, int>> importedBuffers;
+ for (size_t i = 0; i < num_buffer_reqs; i++) {
+ int streamId = bufRets[i].streamId;
+ switch (bufRets[i].val.getDiscriminator()) {
+ case 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());
+ cleanupInflightBufferFences(importedFences, importedBuffers);
+ return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
+ }
+ break;
+ case StreamBuffersVal::hidl_discriminator::buffers: {
+ 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;
+ } break;
+ default:
+ ALOGE("%s: unknown StreamBuffersVal discrimator!", __FUNCTION__);
+ cleanupInflightBufferFences(importedFences, importedBuffers);
+ return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
+ }
+ }
+
+ *num_returned_buf_reqs = num_buffer_reqs;
+
+ return (status == BufferRequestStatus::OK) ?
+ CAMERA3_BUF_REQ_OK : CAMERA3_BUF_REQ_FAILED_PARTIAL;
+ }
+
+ switch (status) {
+ case BufferRequestStatus::FAILED_CONFIGURING:
+ return CAMERA3_BUF_REQ_FAILED_CONFIGURING;
+ case BufferRequestStatus::FAILED_ILLEGAL_ARGUMENTS:
+ return CAMERA3_BUF_REQ_FAILED_ILLEGAL_ARGUMENTS;
+ case BufferRequestStatus::FAILED_UNKNOWN:
+ default:
+ return CAMERA3_BUF_REQ_FAILED_UNKNOWN;
+ }
+}
+
+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);
+}
+
} // 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..4f7284c 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,85 @@
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;
+ /**
+ * 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:
diff --git a/camera/provider/2.4/default/CameraProvider.cpp b/camera/provider/2.4/default/CameraProvider.cpp
index f143dd7..e02cc7e 100644
--- a/camera/provider/2.4/default/CameraProvider.cpp
+++ b/camera/provider/2.4/default/CameraProvider.cpp
@@ -41,11 +41,8 @@
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;
@@ -226,22 +223,6 @@
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 cameraId, int deviceVersion) {
// Maybe consider create a version check method and SortedVec to speed up?
@@ -249,9 +230,16 @@
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 +249,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",
@@ -360,7 +350,8 @@
// 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 +361,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:
@@ -575,10 +580,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);
diff --git a/camera/provider/2.4/default/CameraProvider.h b/camera/provider/2.4/default/CameraProvider.h
index 0f0959f..10e9b0d 100644
--- a/camera/provider/2.4/default/CameraProvider.h
+++ b/camera/provider/2.4/default/CameraProvider.h
@@ -98,7 +98,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);
diff --git a/current.txt b/current.txt
index 3b87306..478cbc5 100644
--- a/current.txt
+++ b/current.txt
@@ -399,8 +399,9 @@
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
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/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/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/GeneratedTestHarness.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
index c2ecd9a..2e13854 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "GeneratedTestHarness.h"
#include "Callbacks.h"
#include "ExecutionBurstController.h"
#include "TestHarness.h"
@@ -364,6 +365,51 @@
kDefaultRtol, executor, measure, outputType);
}
+void EvaluatePreparedModel(sp<V1_2::IPreparedModel>& preparedModel,
+ std::function<bool(int)> is_ignored,
+ const std::vector<MixedTypedExample>& examples,
+ bool hasRelaxedFloat32Model, bool testDynamicOutputShape) {
+ if (testDynamicOutputShape) {
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::ASYNC, MeasureTiming::NO, OutputType::UNSPECIFIED);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::SYNC, MeasureTiming::NO, OutputType::UNSPECIFIED);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::BURST, MeasureTiming::NO, OutputType::UNSPECIFIED);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::ASYNC, MeasureTiming::YES, OutputType::UNSPECIFIED);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::SYNC, MeasureTiming::YES, OutputType::UNSPECIFIED);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::BURST, MeasureTiming::YES, OutputType::UNSPECIFIED);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::ASYNC, MeasureTiming::NO, OutputType::INSUFFICIENT);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::SYNC, MeasureTiming::NO, OutputType::INSUFFICIENT);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::BURST, MeasureTiming::NO, OutputType::INSUFFICIENT);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::ASYNC, MeasureTiming::YES, OutputType::INSUFFICIENT);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::SYNC, MeasureTiming::YES, OutputType::INSUFFICIENT);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::BURST, MeasureTiming::YES, OutputType::INSUFFICIENT);
+ } else {
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::ASYNC, MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::SYNC, MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::BURST, MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::ASYNC, MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::SYNC, MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+ Executor::BURST, MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
+ }
+}
+
static void getPreparedModel(sp<PreparedModelCallback> callback,
sp<V1_0::IPreparedModel>* preparedModel) {
*preparedModel = callback->getPreparedModel();
@@ -468,12 +514,8 @@
MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
}
-// TODO: Reduce code duplication.
-void Execute(const sp<V1_2::IDevice>& device, std::function<V1_2::Model(void)> create_model,
- std::function<bool(int)> is_ignored, const std::vector<MixedTypedExample>& examples,
- bool testDynamicOutputShape) {
- V1_2::Model model = create_model();
-
+void PrepareModel(const sp<V1_2::IDevice>& device, const V1_2::Model& model,
+ sp<V1_2::IPreparedModel>* preparedModel) {
// see if service can handle model
bool fullySupportsModel = false;
Return<void> supportedCall = device->getSupportedOperations_1_2(
@@ -496,12 +538,11 @@
// retrieve prepared model
preparedModelCallback->wait();
ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
- sp<V1_2::IPreparedModel> preparedModel;
- getPreparedModel(preparedModelCallback, &preparedModel);
+ getPreparedModel(preparedModelCallback, preparedModel);
// early termination if vendor service cannot fully prepare model
if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
- ASSERT_EQ(nullptr, preparedModel.get());
+ ASSERT_EQ(nullptr, preparedModel->get());
LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
"prepare model that it does not support.";
std::cout << "[ ] Early termination of test because vendor service cannot "
@@ -510,65 +551,18 @@
GTEST_SKIP();
}
EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
- ASSERT_NE(nullptr, preparedModel.get());
+ ASSERT_NE(nullptr, preparedModel->get());
+}
- if (testDynamicOutputShape) {
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::ASYNC,
- MeasureTiming::NO, OutputType::UNSPECIFIED);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::SYNC,
- MeasureTiming::NO, OutputType::UNSPECIFIED);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::BURST,
- MeasureTiming::NO, OutputType::UNSPECIFIED);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::ASYNC,
- MeasureTiming::YES, OutputType::UNSPECIFIED);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::SYNC,
- MeasureTiming::YES, OutputType::UNSPECIFIED);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::BURST,
- MeasureTiming::YES, OutputType::UNSPECIFIED);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::ASYNC,
- MeasureTiming::NO, OutputType::INSUFFICIENT);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::SYNC,
- MeasureTiming::NO, OutputType::INSUFFICIENT);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::BURST,
- MeasureTiming::NO, OutputType::INSUFFICIENT);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::ASYNC,
- MeasureTiming::YES, OutputType::INSUFFICIENT);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::SYNC,
- MeasureTiming::YES, OutputType::INSUFFICIENT);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::BURST,
- MeasureTiming::YES, OutputType::INSUFFICIENT);
- } else {
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::ASYNC,
- MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::SYNC,
- MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::BURST,
- MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::ASYNC,
- MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::SYNC,
- MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
- EvaluatePreparedModel(preparedModel, is_ignored, examples,
- model.relaxComputationFloat32toFloat16, Executor::BURST,
- MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
- }
+// TODO: Reduce code duplication.
+void Execute(const sp<V1_2::IDevice>& device, std::function<V1_2::Model(void)> create_model,
+ std::function<bool(int)> is_ignored, const std::vector<MixedTypedExample>& examples,
+ bool testDynamicOutputShape) {
+ V1_2::Model model = create_model();
+ sp<V1_2::IPreparedModel> preparedModel = nullptr;
+ PrepareModel(device, model, &preparedModel);
+ EvaluatePreparedModel(preparedModel, is_ignored, examples,
+ model.relaxComputationFloat32toFloat16, testDynamicOutputShape);
}
} // namespace generated_tests
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
new file mode 100644
index 0000000..c7d2399
--- /dev/null
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
@@ -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.
+ */
+
+#ifndef VTS_HAL_NEURALNETWORKS_GENERATED_TEST_HARNESS_H
+#define VTS_HAL_NEURALNETWORKS_GENERATED_TEST_HARNESS_H
+
+#include "TestHarness.h"
+
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+
+namespace generated_tests {
+using ::test_helper::MixedTypedExample;
+
+void PrepareModel(const sp<V1_2::IDevice>& device, const V1_2::Model& model,
+ sp<V1_2::IPreparedModel>* preparedModel);
+
+void EvaluatePreparedModel(sp<V1_2::IPreparedModel>& preparedModel,
+ std::function<bool(int)> is_ignored,
+ const std::vector<MixedTypedExample>& examples,
+ bool hasRelaxedFloat32Model, bool testDynamicOutputShape);
+
+void Execute(const sp<V1_0::IDevice>& device, std::function<V1_0::Model(void)> create_model,
+ std::function<bool(int)> is_ignored, const std::vector<MixedTypedExample>& examples);
+
+void Execute(const sp<V1_1::IDevice>& device, std::function<V1_1::Model(void)> create_model,
+ std::function<bool(int)> is_ignored, const std::vector<MixedTypedExample>& examples);
+
+void Execute(const sp<V1_2::IDevice>& device, std::function<V1_2::Model(void)> create_model,
+ std::function<bool(int)> is_ignored, const std::vector<MixedTypedExample>& examples,
+ bool testDynamicOutputShape = false);
+
+} // namespace generated_tests
+
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
+
+#endif // VTS_HAL_NEURALNETWORKS_GENERATED_TEST_HARNESS_H
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTests.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTests.cpp
index 55e5861..d1c7de3 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTests.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTests.cpp
@@ -19,6 +19,7 @@
#include "VtsHalNeuralnetworks.h"
#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
#include "TestHarness.h"
#include "Utils.h"
@@ -29,13 +30,6 @@
namespace android {
namespace hardware {
namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_0::IDevice>&, std::function<V1_0::Model(void)>,
- std::function<bool(int)>, const std::vector<MixedTypedExample>&);
-} // namespace generated_tests
-
namespace V1_0 {
namespace vts {
namespace functional {
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTests.cpp b/neuralnetworks/1.1/vts/functional/GeneratedTests.cpp
index d98ea04..4db1276 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTests.cpp
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTests.cpp
@@ -19,6 +19,7 @@
#include "VtsHalNeuralnetworks.h"
#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
#include "TestHarness.h"
#include "Utils.h"
@@ -29,13 +30,6 @@
namespace android {
namespace hardware {
namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_1::IDevice>&, std::function<V1_1::Model(void)>,
- std::function<bool(int)>, const std::vector<MixedTypedExample>&);
-} // namespace generated_tests
-
namespace V1_1 {
namespace vts {
namespace functional {
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTestsV1_0.cpp b/neuralnetworks/1.1/vts/functional/GeneratedTestsV1_0.cpp
index 1df3218..e67ef8e 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTestsV1_0.cpp
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTestsV1_0.cpp
@@ -19,6 +19,7 @@
#include "VtsHalNeuralnetworks.h"
#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
#include "TestHarness.h"
#include "Utils.h"
@@ -29,13 +30,6 @@
namespace android {
namespace hardware {
namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_1::IDevice>&, std::function<V1_1::Model(void)>,
- std::function<bool(int)>, const std::vector<MixedTypedExample>&);
-} // namespace generated_tests
-
namespace V1_1 {
namespace vts {
namespace functional {
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 9e7d8f0..2e48ba0 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -47,7 +47,7 @@
* 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.
*/
TENSOR_QUANT16_SYMM = 7,
/** A tensor of IEEE 754 16 bit floating point values. */
@@ -97,6 +97,16 @@
* real_value = (integer_value - zeroPoint) * scale.
*/
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.
+ */
+ TENSOR_QUANT8_SYMM = 13,
/* ADDING A NEW FUNDAMENTAL TYPE REQUIRES UPDATING THE VALUE OF
* OperandTypeRange::FUNDAMENTAL_MAX.
*/
@@ -111,7 +121,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,
diff --git a/neuralnetworks/1.2/vts/functional/Android.bp b/neuralnetworks/1.2/vts/functional/Android.bp
index 0cb9e16..510a0d5 100644
--- a/neuralnetworks/1.2/vts/functional/Android.bp
+++ b/neuralnetworks/1.2/vts/functional/Android.bp
@@ -46,6 +46,7 @@
defaults: ["VtsHalNeuralNetworksTargetTestDefaults"],
srcs: [
"BasicTests.cpp",
+ "CompilationCachingTests.cpp",
"GeneratedTests.cpp",
],
cflags: [
diff --git a/neuralnetworks/1.2/vts/functional/BasicTests.cpp b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
index 0eec365..2b88edd 100644
--- a/neuralnetworks/1.2/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
@@ -72,6 +72,12 @@
EXPECT_TRUE(ret.isOk());
}
+// isCachingSupported test
+TEST_F(NeuralnetworksHidlTest, IsCachingSupported) {
+ Return<void> ret = device->isCachingSupported(
+ [](ErrorStatus status, bool) { EXPECT_EQ(ErrorStatus::NONE, status); });
+ EXPECT_TRUE(ret.isOk());
+}
} // namespace functional
} // namespace vts
} // namespace V1_2
diff --git a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
new file mode 100644
index 0000000..454aa1f
--- /dev/null
+++ b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
@@ -0,0 +1,652 @@
+/*
+ * 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 "neuralnetworks_hidl_hal_test"
+
+#include "VtsHalNeuralnetworks.h"
+
+#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
+#include "TestHarness.h"
+#include "Utils.h"
+
+#include <android-base/logging.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <hidlmemory/mapping.h>
+#include <cstdio>
+#include <cstdlib>
+#include <random>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_2 {
+namespace vts {
+namespace functional {
+
+using ::android::hardware::neuralnetworks::V1_2::implementation::ExecutionCallback;
+using ::android::hardware::neuralnetworks::V1_2::implementation::PreparedModelCallback;
+using ::android::nn::allocateSharedMemory;
+using ::test_helper::MixedTypedExample;
+
+namespace {
+
+// In frameworks/ml/nn/runtime/tests/generated/, creates a hidl model of mobilenet.
+#include "examples/mobilenet_224_gender_basic_fixed.example.cpp"
+#include "vts_models/mobilenet_224_gender_basic_fixed.model.cpp"
+
+// Prevent the compiler from complaining about an otherwise unused function.
+[[maybe_unused]] auto dummy_createTestModel = createTestModel_dynamic_output_shape;
+[[maybe_unused]] auto dummy_get_examples = get_examples_dynamic_output_shape;
+
+enum class AccessMode { READ_ONLY, WRITE_ONLY };
+
+void createCacheHandle(const std::vector<std::string>& files, AccessMode mode,
+ hidl_handle* handle) {
+ std::vector<int> fds;
+ for (const auto& file : files) {
+ int fd;
+ if (mode == AccessMode::READ_ONLY) {
+ fd = open(file.c_str(), O_RDONLY);
+ } else if (mode == AccessMode::WRITE_ONLY) {
+ fd = open(file.c_str(), O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+ } else {
+ FAIL();
+ }
+ ASSERT_GE(fd, 0);
+ fds.push_back(fd);
+ }
+ native_handle_t* cacheNativeHandle = native_handle_create(fds.size(), 0);
+ ASSERT_NE(cacheNativeHandle, nullptr);
+ for (uint32_t i = 0; i < fds.size(); i++) {
+ cacheNativeHandle->data[i] = fds[i];
+ }
+ handle->setTo(cacheNativeHandle, /*shouldOwn=*/true);
+}
+
+} // namespace
+
+// Tag for the compilation caching tests.
+class CompilationCachingTest : public NeuralnetworksHidlTest {
+ protected:
+ void SetUp() override {
+ NeuralnetworksHidlTest::SetUp();
+
+ // Create cache directory.
+ char cacheDirTemp[] = "/data/local/tmp/TestCompilationCachingXXXXXX";
+ char* cacheDir = mkdtemp(cacheDirTemp);
+ ASSERT_NE(cacheDir, nullptr);
+ mCache1 = cacheDir + mCache1;
+ mCache2 = cacheDir + mCache2;
+ mCache3 = cacheDir + mCache3;
+
+ // Check if caching is supported.
+ bool isCachingSupported;
+ Return<void> ret = device->isCachingSupported(
+ [&isCachingSupported](ErrorStatus status, bool supported) {
+ EXPECT_EQ(ErrorStatus::NONE, status);
+ isCachingSupported = supported;
+ });
+ EXPECT_TRUE(ret.isOk());
+ if (isCachingSupported) {
+ mIsCachingSupported = true;
+ } else {
+ LOG(INFO) << "NN VTS: Early termination of test because vendor service does not "
+ "support compilation caching.";
+ std::cout << "[ ] Early termination of test because vendor service does not "
+ "support compilation caching."
+ << std::endl;
+ mIsCachingSupported = false;
+ }
+
+ // Create empty cache files.
+ hidl_handle handle;
+ createCacheHandle({mCache1, mCache2, mCache3}, AccessMode::WRITE_ONLY, &handle);
+ }
+
+ void saveModelToCache(sp<IPreparedModel> preparedModel, const hidl_handle& cache1,
+ const hidl_handle& cache2, ErrorStatus* status) {
+ // Save IPreparedModel to cache.
+ hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
+ Return<ErrorStatus> saveToCacheStatus =
+ preparedModel->saveToCache(cache1, cache2, cacheToken);
+ ASSERT_TRUE(saveToCacheStatus.isOk());
+ *status = static_cast<ErrorStatus>(saveToCacheStatus);
+ }
+
+ bool checkEarlyTermination(ErrorStatus status) {
+ if (status == ErrorStatus::GENERAL_FAILURE) {
+ LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
+ "save the prepared model that it does not support.";
+ std::cout << "[ ] Early termination of test because vendor service cannot "
+ "save the prepared model that it does not support."
+ << std::endl;
+ return true;
+ }
+ return false;
+ }
+
+ void prepareModelFromCache(const hidl_handle& cache1, const hidl_handle& cache2,
+ sp<IPreparedModel>* preparedModel, ErrorStatus* status) {
+ // Launch prepare model from cache.
+ sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
+ ASSERT_NE(nullptr, preparedModelCallback.get());
+ hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
+ Return<ErrorStatus> prepareLaunchStatus =
+ device->prepareModelFromCache(cache1, cache2, cacheToken, preparedModelCallback);
+ ASSERT_TRUE(prepareLaunchStatus.isOk());
+ if (static_cast<ErrorStatus>(prepareLaunchStatus) != ErrorStatus::NONE) {
+ *preparedModel = nullptr;
+ *status = static_cast<ErrorStatus>(prepareLaunchStatus);
+ return;
+ }
+
+ // Retrieve prepared model.
+ preparedModelCallback->wait();
+ *status = preparedModelCallback->getStatus();
+ *preparedModel = V1_2::IPreparedModel::castFrom(preparedModelCallback->getPreparedModel())
+ .withDefault(nullptr);
+ }
+
+ std::string mCache1 = "/cache1";
+ std::string mCache2 = "/cache2";
+ std::string mCache3 = "/cache3";
+ uint8_t mToken[static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)] = {};
+ bool mIsCachingSupported;
+};
+
+TEST_F(CompilationCachingTest, CacheSavingAndRetrieval) {
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+ sp<IPreparedModel> preparedModel = nullptr;
+ generated_tests::PrepareModel(device, testModel, &preparedModel);
+ // Terminate early if the driver cannot prepare the model.
+ if (preparedModel == nullptr) return;
+
+ // Save the compilation to cache.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ if (!mIsCachingSupported) {
+ EXPECT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ } else {
+ if (checkEarlyTermination(status)) return;
+ ASSERT_EQ(status, ErrorStatus::NONE);
+ }
+ }
+
+ // Retrieve preparedModel from cache.
+ {
+ preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+ prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ if (!mIsCachingSupported) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ ASSERT_EQ(preparedModel, nullptr);
+ return;
+ } else {
+ ASSERT_EQ(status, ErrorStatus::NONE);
+ ASSERT_NE(preparedModel, nullptr);
+ }
+ }
+
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; }, get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+}
+
+TEST_F(CompilationCachingTest, CacheSavingAndRetrievalNonZeroOffset) {
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+ sp<IPreparedModel> preparedModel = nullptr;
+ generated_tests::PrepareModel(device, testModel, &preparedModel);
+ // Terminate early if the driver cannot prepare the model.
+ if (preparedModel == nullptr) return;
+
+ // Save the compilation to cache.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ if (!mIsCachingSupported) {
+ EXPECT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ } else {
+ if (checkEarlyTermination(status)) return;
+ ASSERT_EQ(status, ErrorStatus::NONE);
+ }
+ }
+
+ // Retrieve preparedModel from cache.
+ {
+ preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+ uint8_t dummyByte = 0;
+ // Advance offset by one byte.
+ ASSERT_GE(read(cache1.getNativeHandle()->data[0], &dummyByte, 1), 0);
+ ASSERT_GE(read(cache2.getNativeHandle()->data[0], &dummyByte, 1), 0);
+ prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ if (!mIsCachingSupported) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ ASSERT_EQ(preparedModel, nullptr);
+ return;
+ } else {
+ ASSERT_EQ(status, ErrorStatus::NONE);
+ ASSERT_NE(preparedModel, nullptr);
+ }
+ }
+
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; }, get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+}
+
+TEST_F(CompilationCachingTest, SaveToCacheInvalidNumFd) {
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+ sp<IPreparedModel> preparedModel = nullptr;
+ generated_tests::PrepareModel(device, testModel, &preparedModel);
+ // Terminate early if the driver cannot prepare the model.
+ if (preparedModel == nullptr) return;
+
+ // cache1 with invalid NumFd.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1, mCache3}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ }
+ }
+
+ // cache2 with invalid NumFd.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2, mCache3}, AccessMode::WRITE_ONLY, &cache2);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ }
+ }
+}
+
+TEST_F(CompilationCachingTest, PrepareModelFromCacheInvalidNumFd) {
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+ sp<IPreparedModel> preparedModel = nullptr;
+ generated_tests::PrepareModel(device, testModel, &preparedModel);
+ // Terminate early if the driver cannot prepare the model.
+ if (preparedModel == nullptr) return;
+
+ // Save the compilation to cache.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::NONE);
+ }
+ }
+
+ // cache1 with invalid NumFd.
+ {
+ preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1, mCache3}, AccessMode::READ_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+ prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+ }
+
+ // cache2 with invalid NumFd.
+ {
+ preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+ createCacheHandle({mCache2, mCache3}, AccessMode::READ_ONLY, &cache2);
+ prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+ }
+}
+
+TEST_F(CompilationCachingTest, SaveToCacheInvalidAccessMode) {
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+ sp<IPreparedModel> preparedModel = nullptr;
+ generated_tests::PrepareModel(device, testModel, &preparedModel);
+ // Terminate early if the driver cannot prepare the model.
+ if (preparedModel == nullptr) return;
+
+ // cache1 with invalid access mode.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+
+ // cache2 with invalid access mode.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+}
+
+TEST_F(CompilationCachingTest, PrepareModelFromCacheInvalidAccessMode) {
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+ sp<IPreparedModel> preparedModel = nullptr;
+ generated_tests::PrepareModel(device, testModel, &preparedModel);
+ // Terminate early if the driver cannot prepare the model.
+ if (preparedModel == nullptr) return;
+
+ // Save the compilation to cache.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::NONE);
+ }
+ }
+
+ // cache1 with invalid access mode.
+ {
+ preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+ prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+
+ // cache2 with invalid access mode.
+ {
+ preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+}
+
+TEST_F(CompilationCachingTest, SaveToCacheInvalidOffset) {
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+ sp<IPreparedModel> preparedModel = nullptr;
+ generated_tests::PrepareModel(device, testModel, &preparedModel);
+ // Terminate early if the driver cannot prepare the model.
+ if (preparedModel == nullptr) return;
+
+ // cache1 with invalid file descriptor offset.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ uint8_t dummyByte = 0;
+ // Advance offset by one byte.
+ ASSERT_EQ(write(cache1.getNativeHandle()->data[0], &dummyByte, 1), 1);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+
+ // cache2 with invalid file descriptor offset.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ uint8_t dummyByte = 0;
+ // Advance offset by one byte.
+ ASSERT_EQ(write(cache2.getNativeHandle()->data[0], &dummyByte, 1), 1);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+}
+
+TEST_F(CompilationCachingTest, SaveToCacheInvalidFileSize) {
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+ sp<IPreparedModel> preparedModel = nullptr;
+ generated_tests::PrepareModel(device, testModel, &preparedModel);
+ // Terminate early if the driver cannot prepare the model.
+ if (preparedModel == nullptr) return;
+
+ // cache1 with invalid file size.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ uint8_t dummyByte = 0;
+ // Write one byte and seek back to the beginning.
+ ASSERT_EQ(write(cache1.getNativeHandle()->data[0], &dummyByte, 1), 1);
+ ASSERT_EQ(lseek(cache1.getNativeHandle()->data[0], 0, SEEK_SET), 0);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+
+ // cache2 with invalid file size.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ uint8_t dummyByte = 0;
+ // Write one byte and seek back to the beginning.
+ ASSERT_EQ(write(cache2.getNativeHandle()->data[0], &dummyByte, 1), 1);
+ ASSERT_EQ(lseek(cache2.getNativeHandle()->data[0], 0, SEEK_SET), 0);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+}
+
+class CompilationCachingSecurityTest : public CompilationCachingTest,
+ public ::testing::WithParamInterface<uint32_t> {
+ protected:
+ void SetUp() {
+ CompilationCachingTest::SetUp();
+ generator.seed(kSeed);
+ }
+
+ // Get a random integer within a closed range [lower, upper].
+ template <typename T>
+ T getRandomInt(T lower, T upper) {
+ std::uniform_int_distribution<T> dis(lower, upper);
+ return dis(generator);
+ }
+
+ const uint32_t kSeed = GetParam();
+ std::mt19937 generator;
+};
+
+TEST_P(CompilationCachingSecurityTest, CorruptedSecuritySensitiveCache) {
+ if (!mIsCachingSupported) return;
+
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+ sp<IPreparedModel> preparedModel = nullptr;
+ generated_tests::PrepareModel(device, testModel, &preparedModel);
+ // Terminate early if the driver cannot prepare the model.
+ if (preparedModel == nullptr) return;
+
+ // Save the compilation to cache.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ if (checkEarlyTermination(status)) return;
+ ASSERT_EQ(status, ErrorStatus::NONE);
+ }
+
+ // Randomly flip one single bit of the cache entry.
+ FILE* pFile = fopen(mCache1.c_str(), "r+");
+ ASSERT_EQ(fseek(pFile, 0, SEEK_END), 0);
+ long int fileSize = ftell(pFile);
+ ASSERT_GT(fileSize, 0);
+ ASSERT_EQ(fseek(pFile, getRandomInt(0l, fileSize - 1), SEEK_SET), 0);
+ int readByte = fgetc(pFile);
+ ASSERT_NE(readByte, EOF);
+ ASSERT_EQ(fseek(pFile, -1, SEEK_CUR), 0);
+ ASSERT_NE(fputc(static_cast<uint8_t>(readByte) ^ (1U << getRandomInt(0, 7)), pFile), EOF);
+ fclose(pFile);
+
+ // Retrieve preparedModel from cache, expect failure.
+ {
+ preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+ prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+}
+
+TEST_P(CompilationCachingSecurityTest, WrongLengthSecuritySensitiveCache) {
+ if (!mIsCachingSupported) return;
+
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+ sp<IPreparedModel> preparedModel = nullptr;
+ generated_tests::PrepareModel(device, testModel, &preparedModel);
+ // Terminate early if the driver cannot prepare the model.
+ if (preparedModel == nullptr) return;
+
+ // Save the compilation to cache.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ if (checkEarlyTermination(status)) return;
+ ASSERT_EQ(status, ErrorStatus::NONE);
+ }
+
+ // Randomly append bytes to the cache entry.
+ FILE* pFile = fopen(mCache1.c_str(), "a");
+ uint32_t appendLength = getRandomInt(1, 256);
+ for (uint32_t i = 0; i < appendLength; i++) {
+ ASSERT_NE(fputc(getRandomInt<uint8_t>(0, 255), pFile), EOF);
+ }
+ fclose(pFile);
+
+ // Retrieve preparedModel from cache, expect failure.
+ {
+ preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+ prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+}
+
+TEST_P(CompilationCachingSecurityTest, WrongToken) {
+ if (!mIsCachingSupported) return;
+
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+ sp<IPreparedModel> preparedModel = nullptr;
+ generated_tests::PrepareModel(device, testModel, &preparedModel);
+ // Terminate early if the driver cannot prepare the model.
+ if (preparedModel == nullptr) return;
+
+ // Save the compilation to cache.
+ {
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+ saveModelToCache(preparedModel, cache1, cache2, &status);
+ if (checkEarlyTermination(status)) return;
+ ASSERT_EQ(status, ErrorStatus::NONE);
+ }
+
+ // Randomly flip one single bit in mToken.
+ uint32_t ind = getRandomInt(0u, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN) - 1);
+ mToken[ind] ^= (1U << getRandomInt(0, 7));
+
+ // Retrieve the preparedModel from cache, expect failure.
+ {
+ preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_handle cache1, cache2;
+ createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+ createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+ prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(TestCompilationCaching, CompilationCachingSecurityTest,
+ ::testing::Range(0U, 10U));
+
+} // namespace functional
+} // namespace vts
+} // namespace V1_2
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp
index 4bc891f..2c3287a 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp
@@ -19,6 +19,7 @@
#include "VtsHalNeuralnetworks.h"
#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
#include "TestHarness.h"
#include "Utils.h"
@@ -29,14 +30,6 @@
namespace android {
namespace hardware {
namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_2::IDevice>&, std::function<V1_2::Model(void)>,
- std::function<bool(int)>, const std::vector<MixedTypedExample>&,
- bool testDynamicOutputShape = false);
-} // namespace generated_tests
-
namespace V1_2 {
namespace vts {
namespace functional {
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_0.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_0.cpp
index 956926a..990cab9 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_0.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_0.cpp
@@ -19,6 +19,7 @@
#include "VtsHalNeuralnetworks.h"
#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
#include "TestHarness.h"
#include "Utils.h"
@@ -29,14 +30,6 @@
namespace android {
namespace hardware {
namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_2::IDevice>&, std::function<V1_2::Model(void)>,
- std::function<bool(int)>, const std::vector<MixedTypedExample>&,
- bool testDynamicOutputShape = false);
-} // namespace generated_tests
-
namespace V1_2 {
namespace vts {
namespace functional {
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_1.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_1.cpp
index 425690f..fa6d54d 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_1.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_1.cpp
@@ -19,6 +19,7 @@
#include "VtsHalNeuralnetworks.h"
#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
#include "TestHarness.h"
#include "Utils.h"
@@ -29,14 +30,6 @@
namespace android {
namespace hardware {
namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_2::IDevice>&, std::function<V1_2::Model(void)>,
- std::function<bool(int)>, const std::vector<MixedTypedExample>&,
- bool testDynamicOutputShape = false);
-} // namespace generated_tests
-
namespace V1_2 {
namespace vts {
namespace functional {
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/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/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/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]++;