Merge "Camera: Add HAL interface change for HEIC"
diff --git a/audio/common/all-versions/default/service/Android.mk b/audio/common/all-versions/default/service/Android.mk
index 58987c7..b57a1ae 100644
--- a/audio/common/all-versions/default/service/Android.mk
+++ b/audio/common/all-versions/default/service/Android.mk
@@ -49,6 +49,7 @@
android.hardware.audio.effect@4.0 \
android.hardware.audio.effect@5.0 \
android.hardware.bluetooth.a2dp@1.0 \
+ android.hardware.bluetooth.audio@2.0 \
android.hardware.soundtrigger@2.0 \
android.hardware.soundtrigger@2.1 \
android.hardware.soundtrigger@2.2
diff --git a/audio/common/all-versions/default/service/service.cpp b/audio/common/all-versions/default/service/service.cpp
index 15ce5e0..8a7b2ea 100644
--- a/audio/common/all-versions/default/service/service.cpp
+++ b/audio/common/all-versions/default/service/service.cpp
@@ -23,6 +23,7 @@
#include <android/hardware/audio/effect/4.0/IEffectsFactory.h>
#include <android/hardware/audio/effect/5.0/IEffectsFactory.h>
#include <android/hardware/bluetooth/a2dp/1.0/IBluetoothAudioOffload.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvidersFactory.h>
#include <android/hardware/soundtrigger/2.0/ISoundTriggerHw.h>
#include <android/hardware/soundtrigger/2.1/ISoundTriggerHw.h>
#include <android/hardware/soundtrigger/2.2/ISoundTriggerHw.h>
@@ -64,6 +65,11 @@
registerPassthroughServiceImplementation<soundtrigger::V2_0::ISoundTriggerHw>() != OK,
ALOGW_IF(fail, "Could not register soundtrigger API 2.0, 2.1 nor 2.2");
+ fail = registerPassthroughServiceImplementation<
+ bluetooth::audio::V2_0::IBluetoothAudioProvidersFactory>() != OK;
+ ALOGW_IF(fail, "Could not register Bluetooth Audio API 2.0");
+
+ // remove the old HIDL when Bluetooth Audio Hal V2 has offloading supported
fail =
registerPassthroughServiceImplementation<bluetooth::a2dp::V1_0::IBluetoothAudioOffload>() !=
OK;
diff --git a/audio/core/all-versions/vts/functional/5.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/5.0/AudioPrimaryHidlHalTest.cpp
new file mode 100644
index 0000000..bdb17cd
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/5.0/AudioPrimaryHidlHalTest.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <math.h>
+
+// pull in all the <= 4.0 tests
+#include "4.0/AudioPrimaryHidlHalTest.cpp"
+
+TEST_P(InputStreamTest, SetMicrophoneDirection) {
+ doc::test("Make sure setMicrophoneDirection correctly handles valid & invalid arguments");
+
+ // MicrophoneDirection dir = MicrophoneDirection::FRONT;
+ for (MicrophoneDirection dir : android::hardware::hidl_enum_range<MicrophoneDirection>()) {
+ ASSERT_RESULT(okOrNotSupported, stream->setMicrophoneDirection(dir));
+ }
+
+ // Bogus values
+ for (auto dir : {42, -1, 4}) {
+ ASSERT_RESULT(invalidArgsOrNotSupported,
+ stream->setMicrophoneDirection(MicrophoneDirection(dir)));
+ }
+}
+
+TEST_P(InputStreamTest, SetMicrophoneFieldDimension) {
+ doc::test("Make sure setMicrophoneFieldDimension correctly handles valid & invalid arguments");
+
+ // Valid zoom values -1.0 -> 1.0
+ float incr = 0.1f;
+ for (float val = -1.0f; val <= 1.0; val += incr) {
+ ASSERT_RESULT(okOrNotSupported, stream->setMicrophoneFieldDimension(val));
+ }
+
+ // Bogus values
+ for (float val = 1.0f + incr; val <= 10.0f; val += incr) {
+ ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(val));
+ ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(-val));
+ }
+ // Some extremes
+ ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(NAN));
+ ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(-NAN));
+ ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(INFINITY));
+ ASSERT_RESULT(invalidArgsOrNotSupported, stream->setMicrophoneFieldDimension(-INFINITY));
+}
diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp
index 6498289..88fdb5a 100644
--- a/audio/core/all-versions/vts/functional/Android.bp
+++ b/audio/core/all-versions/vts/functional/Android.bp
@@ -20,9 +20,6 @@
static_libs: [
"android.hardware.audio.common.test.utility",
"libaudiopolicycomponents",
- "libicuuc",
- "libicuuc_stubdata",
- "libandroidicu",
"libmedia_helper",
"libxml2",
],
@@ -73,8 +70,7 @@
name: "VtsHalAudioV5_0TargetTest",
defaults: ["VtsHalAudioTargetTest_defaults"],
srcs: [
- // for now the tests are the same as V4
- "4.0/AudioPrimaryHidlHalTest.cpp",
+ "5.0/AudioPrimaryHidlHalTest.cpp",
],
static_libs: [
"android.hardware.audio@5.0",
diff --git a/audio/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/biometrics/face/1.0/types.hal b/biometrics/face/1.0/types.hal
index 89e809b..b5db966 100644
--- a/biometrics/face/1.0/types.hal
+++ b/biometrics/face/1.0/types.hal
@@ -297,7 +297,7 @@
* capture plane is too high.
*
* The tilt angle is defined as the angle swept out by the user’s face looking up
- * and down. The pan angle would be zero if the user faced the camera directly.
+ * and down. The tilt angle would be zero if the user faced the camera directly.
*
* The user should be informed to look more directly at the camera.
*/
@@ -308,8 +308,8 @@
* capture plane is too high.
*
* The roll angle is defined as the angle swept out by the user’s face tilting their head
- * towards their shoulders to the left and right. The pan angle would be zero if the user
- * faced the camera directly.
+ * towards their shoulders to the left and right. The roll angle would be zero if the user's
+ * head is vertically aligned with the camera.
*
* The user should be informed to look more directly at the camera.
*/
diff --git a/bluetooth/audio/2.0/Android.bp b/bluetooth/audio/2.0/Android.bp
index 5d67f75..542c812 100644
--- a/bluetooth/audio/2.0/Android.bp
+++ b/bluetooth/audio/2.0/Android.bp
@@ -1,3 +1,5 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
hidl_interface {
name: "android.hardware.bluetooth.audio@2.0",
root: "android.hardware",
@@ -11,8 +13,8 @@
"IBluetoothAudioProvidersFactory.hal",
],
interfaces: [
- "android.hidl.base@1.0",
"android.hardware.audio.common@5.0",
+ "android.hidl.base@1.0",
],
types: [
"AacObjectType",
@@ -34,6 +36,7 @@
"SbcAllocMethod",
"SbcBlockLength",
"SbcChannelMode",
+ "SbcNumSubbands",
"SbcParameters",
"SessionType",
"Status",
diff --git a/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.cpp
new file mode 100644
index 0000000..84fba34
--- /dev/null
+++ b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderA2dpOffload"
+
+#include <android-base/logging.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "A2dpOffloadAudioProvider.h"
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Void;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+A2dpOffloadAudioProvider::A2dpOffloadAudioProvider()
+ : BluetoothAudioProvider() {
+ session_type_ = SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
+}
+
+bool A2dpOffloadAudioProvider::isValid(const SessionType& sessionType) {
+ return (sessionType == session_type_);
+}
+
+Return<void> A2dpOffloadAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ /**
+ * Initialize the audio platform if audioConfiguration is supported.
+ * Save the the IBluetoothAudioPort interface, so that it can be used
+ * later to send stream control commands to the HAL client, based on
+ * interaction with Audio framework.
+ */
+ if (audioConfig.getDiscriminator() !=
+ AudioConfiguration::hidl_discriminator::codecConfig) {
+ LOG(WARNING) << __func__
+ << " - Invalid Audio Configuration=" << toString(audioConfig);
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ } else if (!android::bluetooth::audio::IsOffloadCodecConfigurationValid(
+ session_type_, audioConfig.codecConfig())) {
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> A2dpOffloadAudioProvider::onSessionReady(
+ startSession_cb _hidl_cb) {
+ BluetoothAudioSessionReport::OnSessionStarted(session_type_, stack_iface_,
+ nullptr, audio_config_);
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.h b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.h
new file mode 100644
index 0000000..9f40278
--- /dev/null
+++ b/bluetooth/audio/2.0/default/A2dpOffloadAudioProvider.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+class A2dpOffloadAudioProvider : public BluetoothAudioProvider {
+ public:
+ A2dpOffloadAudioProvider();
+
+ bool isValid(const SessionType& sessionType) override;
+
+ Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.cpp b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.cpp
new file mode 100644
index 0000000..f71a73e
--- /dev/null
+++ b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderA2dpSoftware"
+
+#include <android-base/logging.h>
+
+#include "A2dpSoftwareAudioProvider.h"
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::hardware::Void;
+
+static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo
+static constexpr uint32_t kPcmFrameCount = 128;
+static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
+static constexpr uint32_t kRtpFrameCount = 7; // max counts by 1 tick (20ms)
+static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
+static constexpr uint32_t kBufferCount = 2; // double buffer
+static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
+
+A2dpSoftwareAudioProvider::A2dpSoftwareAudioProvider()
+ : BluetoothAudioProvider(), mDataMQ(nullptr) {
+ LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
+ << " byte(s)";
+ std::unique_ptr<DataMQ> tempDataMQ(
+ new DataMQ(kDataMqSize, /* EventFlag */ true));
+ if (tempDataMQ && tempDataMQ->isValid()) {
+ mDataMQ = std::move(tempDataMQ);
+ session_type_ = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
+ } else {
+ ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
+ ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
+ }
+}
+
+bool A2dpSoftwareAudioProvider::isValid(const SessionType& sessionType) {
+ return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
+}
+
+Return<void> A2dpSoftwareAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ /**
+ * Initialize the audio platform if audioConfiguration is supported.
+ * Save the the IBluetoothAudioPort interface, so that it can be used
+ * later to send stream control commands to the HAL client, based on
+ * interaction with Audio framework.
+ */
+ if (audioConfig.getDiscriminator() !=
+ AudioConfiguration::hidl_discriminator::pcmConfig) {
+ LOG(WARNING) << __func__
+ << " - Invalid Audio Configuration=" << toString(audioConfig);
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid(
+ audioConfig.pcmConfig())) {
+ LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
+ << toString(audioConfig.pcmConfig());
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> A2dpSoftwareAudioProvider::onSessionReady(
+ startSession_cb _hidl_cb) {
+ if (mDataMQ && mDataMQ->isValid()) {
+ BluetoothAudioSessionReport::OnSessionStarted(
+ session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
+ } else {
+ _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+ }
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.h b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.h
new file mode 100644
index 0000000..228a928
--- /dev/null
+++ b/bluetooth/audio/2.0/default/A2dpSoftwareAudioProvider.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class A2dpSoftwareAudioProvider : public BluetoothAudioProvider {
+ public:
+ A2dpSoftwareAudioProvider();
+
+ bool isValid(const SessionType& sessionType) override;
+
+ Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ // audio data queue for software encoding
+ std::unique_ptr<DataMQ> mDataMQ;
+
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/Android.bp b/bluetooth/audio/2.0/default/Android.bp
new file mode 100644
index 0000000..1dfc05d
--- /dev/null
+++ b/bluetooth/audio/2.0/default/Android.bp
@@ -0,0 +1,48 @@
+cc_library_shared {
+ name: "android.hardware.bluetooth.audio@2.0-impl",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "BluetoothAudioProvidersFactory.cpp",
+ "BluetoothAudioProvider.cpp",
+ "A2dpOffloadAudioProvider.cpp",
+ "A2dpSoftwareAudioProvider.cpp",
+ "HearingAidAudioProvider.cpp",
+ ],
+ header_libs: ["libhardware_headers"],
+ shared_libs: [
+ "android.hardware.audio.common@5.0",
+ "android.hardware.bluetooth.audio@2.0",
+ "libbase",
+ "libbluetooth_audio_session",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ ],
+}
+
+cc_library_shared {
+ name: "libbluetooth_audio_session",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ srcs: [
+ "session/BluetoothAudioSession.cpp",
+ "session/BluetoothAudioSupportedCodecsDB.cpp",
+ ],
+ export_include_dirs: ["session/"],
+ header_libs: ["libhardware_headers"],
+ shared_libs: [
+ "android.hardware.bluetooth.audio@2.0",
+ "libbase",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.0/default/BluetoothAudioProvider.cpp
new file mode 100644
index 0000000..ab8973e
--- /dev/null
+++ b/bluetooth/audio/2.0/default/BluetoothAudioProvider.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderStub"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioProvider.h"
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Void;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+void BluetoothAudioDeathRecipient::serviceDied(
+ uint64_t cookie __unused,
+ const wp<::android::hidl::base::V1_0::IBase>& who __unused) {
+ LOG(ERROR) << "BluetoothAudioDeathRecipient::" << __func__
+ << " - BluetoothAudio Service died";
+ provider_->endSession();
+}
+
+BluetoothAudioProvider::BluetoothAudioProvider()
+ : death_recipient_(new BluetoothAudioDeathRecipient(this)),
+ session_type_(SessionType::UNKNOWN),
+ audio_config_({}) {}
+
+Return<void> BluetoothAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ if (hostIf == nullptr) {
+ _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+ return Void();
+ }
+
+ /**
+ * Initialize the audio platform if audioConfiguration is supported.
+ * Save the the IBluetoothAudioPort interface, so that it can be used
+ * later to send stream control commands to the HAL client, based on
+ * interaction with Audio framework.
+ */
+ audio_config_ = audioConfig;
+ stack_iface_ = hostIf;
+ stack_iface_->linkToDeath(death_recipient_, 0);
+
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", AudioConfiguration=[" << toString(audio_config_) << "]";
+
+ onSessionReady(_hidl_cb);
+ return Void();
+}
+
+Return<void> BluetoothAudioProvider::streamStarted(
+ BluetoothAudioStatus status) {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", status=" << toString(status);
+
+ /**
+ * Streaming on control path has started,
+ * HAL server should start the streaming on data path.
+ */
+ if (stack_iface_) {
+ BluetoothAudioSessionReport::ReportControlStatus(session_type_, true,
+ status);
+ } else {
+ LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", status=" << toString(status) << " has NO session";
+ }
+
+ return Void();
+}
+
+Return<void> BluetoothAudioProvider::streamSuspended(
+ BluetoothAudioStatus status) {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", status=" << toString(status);
+
+ /**
+ * Streaming on control path has suspend,
+ * HAL server should suspend the streaming on data path.
+ */
+ if (stack_iface_) {
+ BluetoothAudioSessionReport::ReportControlStatus(session_type_, false,
+ status);
+ } else {
+ LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", status=" << toString(status) << " has NO session";
+ }
+
+ return Void();
+}
+
+Return<void> BluetoothAudioProvider::endSession() {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
+
+ if (stack_iface_) {
+ BluetoothAudioSessionReport::OnSessionEnded(session_type_);
+ stack_iface_->unlinkToDeath(death_recipient_);
+ } else {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO session";
+ }
+
+ /**
+ * Clean up the audio platform as remote audio device is no
+ * longer active
+ */
+ stack_iface_ = nullptr;
+ audio_config_ = {};
+
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvider.h b/bluetooth/audio/2.0/default/BluetoothAudioProvider.h
new file mode 100644
index 0000000..02bf2b9
--- /dev/null
+++ b/bluetooth/audio/2.0/default/BluetoothAudioProvider.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvider.h>
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+using BluetoothAudioStatus =
+ ::android::hardware::bluetooth::audio::V2_0::Status;
+
+class BluetoothAudioDeathRecipient;
+
+class BluetoothAudioProvider : public IBluetoothAudioProvider {
+ public:
+ BluetoothAudioProvider();
+ ~BluetoothAudioProvider() = default;
+
+ virtual bool isValid(const SessionType& sessionType) = 0;
+
+ Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+ Return<void> streamStarted(BluetoothAudioStatus status) override;
+ Return<void> streamSuspended(BluetoothAudioStatus status) override;
+ Return<void> endSession() override;
+
+ protected:
+ sp<BluetoothAudioDeathRecipient> death_recipient_;
+
+ SessionType session_type_;
+ AudioConfiguration audio_config_;
+ sp<IBluetoothAudioPort> stack_iface_;
+
+ virtual Return<void> onSessionReady(startSession_cb _hidl_cb) = 0;
+};
+
+class BluetoothAudioDeathRecipient : public hidl_death_recipient {
+ public:
+ BluetoothAudioDeathRecipient(const sp<BluetoothAudioProvider> provider)
+ : provider_(provider) {}
+
+ virtual void serviceDied(
+ uint64_t cookie,
+ const wp<::android::hidl::base::V1_0::IBase>& who) override;
+
+ private:
+ sp<BluetoothAudioProvider> provider_;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp
new file mode 100644
index 0000000..df89cc8
--- /dev/null
+++ b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProvidersFactory"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioProvidersFactory.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Void;
+
+A2dpSoftwareAudioProvider
+ BluetoothAudioProvidersFactory::a2dp_software_provider_instance_;
+A2dpOffloadAudioProvider
+ BluetoothAudioProvidersFactory::a2dp_offload_provider_instance_;
+HearingAidAudioProvider
+ BluetoothAudioProvidersFactory::hearing_aid_provider_instance_;
+
+Return<void> BluetoothAudioProvidersFactory::openProvider(
+ const SessionType sessionType, openProvider_cb _hidl_cb) {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType);
+ BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS;
+ BluetoothAudioProvider* provider = nullptr;
+ switch (sessionType) {
+ case SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH:
+ provider = &a2dp_software_provider_instance_;
+ break;
+ case SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH:
+ provider = &a2dp_offload_provider_instance_;
+ break;
+ case SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH:
+ provider = &hearing_aid_provider_instance_;
+ break;
+ default:
+ status = BluetoothAudioStatus::FAILURE;
+ }
+ if (provider == nullptr || !provider->isValid(sessionType)) {
+ provider = nullptr;
+ status = BluetoothAudioStatus::FAILURE;
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(sessionType)
+ << ", status=" << toString(status);
+ }
+ _hidl_cb(status, provider);
+ return Void();
+}
+
+Return<void> BluetoothAudioProvidersFactory::getProviderCapabilities(
+ const SessionType sessionType, getProviderCapabilities_cb _hidl_cb) {
+ hidl_vec<AudioCapabilities> audio_capabilities =
+ hidl_vec<AudioCapabilities>(0);
+ if (sessionType == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ std::vector<CodecCapabilities> db_codec_capabilities =
+ android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType);
+ if (db_codec_capabilities.size()) {
+ audio_capabilities.resize(db_codec_capabilities.size());
+ for (int i = 0; i < db_codec_capabilities.size(); ++i) {
+ audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]);
+ }
+ }
+ } else if (sessionType != SessionType::UNKNOWN) {
+ std::vector<PcmParameters> db_pcm_capabilities =
+ android::bluetooth::audio::GetSoftwarePcmCapabilities();
+ if (db_pcm_capabilities.size() == 1) {
+ audio_capabilities.resize(1);
+ audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]);
+ }
+ }
+ LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType)
+ << " supports " << audio_capabilities.size() << " codecs";
+ _hidl_cb(audio_capabilities);
+ return Void();
+}
+
+IBluetoothAudioProvidersFactory* HIDL_FETCH_IBluetoothAudioProvidersFactory(
+ const char* /* name */) {
+ return new BluetoothAudioProvidersFactory();
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.h b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.h
new file mode 100644
index 0000000..0b51536
--- /dev/null
+++ b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvidersFactory.h>
+
+#include "A2dpOffloadAudioProvider.h"
+#include "A2dpSoftwareAudioProvider.h"
+#include "BluetoothAudioProvider.h"
+#include "HearingAidAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+class BluetoothAudioProvidersFactory : public IBluetoothAudioProvidersFactory {
+ public:
+ BluetoothAudioProvidersFactory() {}
+
+ Return<void> openProvider(const SessionType sessionType,
+ openProvider_cb _hidl_cb) override;
+
+ Return<void> getProviderCapabilities(
+ const SessionType sessionType,
+ getProviderCapabilities_cb _hidl_cb) override;
+
+ private:
+ static A2dpSoftwareAudioProvider a2dp_software_provider_instance_;
+ static A2dpOffloadAudioProvider a2dp_offload_provider_instance_;
+ static HearingAidAudioProvider hearing_aid_provider_instance_;
+};
+
+extern "C" IBluetoothAudioProvidersFactory*
+HIDL_FETCH_IBluetoothAudioProvidersFactory(const char* name);
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/HearingAidAudioProvider.cpp b/bluetooth/audio/2.0/default/HearingAidAudioProvider.cpp
new file mode 100644
index 0000000..e91cf8a
--- /dev/null
+++ b/bluetooth/audio/2.0/default/HearingAidAudioProvider.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderHearingAid"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport.h"
+#include "BluetoothAudioSupportedCodecsDB.h"
+#include "HearingAidAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport;
+using ::android::hardware::Void;
+
+static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo
+static constexpr uint32_t kPcmFrameCount = 128;
+static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
+static constexpr uint32_t kRtpFrameCount = 7; // max counts by 1 tick (20ms)
+static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
+static constexpr uint32_t kBufferCount = 1; // single buffer
+static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
+
+HearingAidAudioProvider::HearingAidAudioProvider()
+ : BluetoothAudioProvider(), mDataMQ(nullptr) {
+ LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
+ << " byte(s)";
+ std::unique_ptr<DataMQ> tempDataMQ(
+ new DataMQ(kDataMqSize, /* EventFlag */ true));
+ if (tempDataMQ && tempDataMQ->isValid()) {
+ mDataMQ = std::move(tempDataMQ);
+ session_type_ = SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH;
+ } else {
+ ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
+ ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
+ }
+}
+
+bool HearingAidAudioProvider::isValid(const SessionType& sessionType) {
+ return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
+}
+
+Return<void> HearingAidAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ /**
+ * Initialize the audio platform if audioConfiguration is supported.
+ * Save the the IBluetoothAudioPort interface, so that it can be used
+ * later to send stream control commands to the HAL client, based on
+ * interaction with Audio framework.
+ */
+ if (audioConfig.getDiscriminator() !=
+ AudioConfiguration::hidl_discriminator::pcmConfig) {
+ LOG(WARNING) << __func__
+ << " - Invalid Audio Configuration=" << toString(audioConfig);
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid(
+ audioConfig.pcmConfig())) {
+ LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
+ << toString(audioConfig.pcmConfig());
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> HearingAidAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
+ if (mDataMQ && mDataMQ->isValid()) {
+ BluetoothAudioSessionReport::OnSessionStarted(
+ session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
+ } else {
+ _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+ }
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/HearingAidAudioProvider.h b/bluetooth/audio/2.0/default/HearingAidAudioProvider.h
new file mode 100644
index 0000000..117eb32
--- /dev/null
+++ b/bluetooth/audio/2.0/default/HearingAidAudioProvider.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class HearingAidAudioProvider : public BluetoothAudioProvider {
+ public:
+ HearingAidAudioProvider();
+
+ bool isValid(const SessionType& sessionType) override;
+
+ Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ // audio data queue for software encoding
+ std::unique_ptr<DataMQ> mDataMQ;
+
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp
new file mode 100644
index 0000000..d60e732
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderSession"
+
+#include "BluetoothAudioSession.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::hardware::audio::common::V5_0::AudioContentType;
+using ::android::hardware::audio::common::V5_0::AudioUsage;
+using ::android::hardware::audio::common::V5_0::PlaybackTrackMetadata;
+using ::android::hardware::audio::common::V5_0::SourceMetadata;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::TimeSpec;
+
+const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {
+ .codecType = CodecType::UNKNOWN,
+ .encodedAudioBitrate = 0x00000000,
+ .peerMtu = 0xffff,
+ .isScmstEnabled = false,
+ .config = {}};
+AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration =
+ {};
+AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {};
+
+static constexpr int kFmqSendTimeoutMs = 1000; // 1000 ms timeout for sending
+static constexpr int kWritePollMs = 1; // polled non-blocking interval
+
+static inline timespec timespec_convert_from_hal(const TimeSpec& TS) {
+ return {.tv_sec = static_cast<long>(TS.tvSec),
+ .tv_nsec = static_cast<long>(TS.tvNSec)};
+}
+
+BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type)
+ : session_type_(session_type), stack_iface_(nullptr), mDataMQ(nullptr) {
+ invalidSoftwareAudioConfiguration.pcmConfig(kInvalidPcmParameters);
+ invalidOffloadAudioConfiguration.codecConfig(kInvalidCodecConfiguration);
+}
+
+// The report function is used to report that the Bluetooth stack has started
+// this session without any failure, and will invoke session_changed_cb_ to
+// notify those registered bluetooth_audio outputs
+void BluetoothAudioSession::OnSessionStarted(
+ const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
+ const AudioConfiguration& audio_config) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (stack_iface == nullptr) {
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", IBluetoothAudioPort Invalid";
+ } else if (!UpdateAudioConfig(audio_config)) {
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", AudioConfiguration=" << toString(audio_config)
+ << " Invalid";
+ } else if (!UpdateDataPath(dataMQ)) {
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+ << " DataMQ Invalid";
+ audio_config_ =
+ (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
+ ? kInvalidOffloadAudioConfiguration
+ : kInvalidSoftwareAudioConfiguration);
+ } else {
+ stack_iface_ = stack_iface;
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", AudioConfiguration=" << toString(audio_config);
+ ReportSessionStatus();
+ }
+}
+
+// The report function is used to report that the Bluetooth stack has ended the
+// session, and will invoke session_changed_cb_ to notify registered
+// bluetooth_audio outputs
+void BluetoothAudioSession::OnSessionEnded() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (IsSessionReady()) {
+ ReportSessionStatus();
+ }
+ audio_config_ = (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
+ ? kInvalidOffloadAudioConfiguration
+ : kInvalidSoftwareAudioConfiguration);
+ stack_iface_ = nullptr;
+ UpdateDataPath(nullptr);
+}
+
+// invoking the registered session_changed_cb_
+void BluetoothAudioSession::ReportSessionStatus() {
+ // This is locked already by OnSessionStarted / OnSessionEnded
+ if (observers_.empty()) {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO port state observer";
+ return;
+ }
+ for (auto& observer : observers_) {
+ uint16_t cookie = observer.first;
+ std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << " notify to bluetooth_audio=0x"
+ << android::base::StringPrintf("%04x", cookie);
+ cb->session_changed_cb_(cookie);
+ }
+}
+
+// The report function is used to report that the Bluetooth stack has notified
+// the result of startStream or suspendStream, and will invoke
+// control_result_cb_ to notify registered bluetooth_audio outputs
+void BluetoothAudioSession::ReportControlStatus(
+ bool start_resp, const BluetoothAudioStatus& status) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (observers_.empty()) {
+ LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO port state observer";
+ return;
+ }
+ for (auto& observer : observers_) {
+ uint16_t cookie = observer.first;
+ std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
+ LOG(INFO) << __func__ << " - status=" << toString(status)
+ << " for SessionType=" << toString(session_type_)
+ << ", bluetooth_audio=0x"
+ << android::base::StringPrintf("%04x", cookie)
+ << (start_resp ? " started" : " suspended");
+ cb->control_result_cb_(cookie, start_resp, status);
+ }
+}
+
+// The function helps to check if this session is ready or not
+// @return: true if the Bluetooth stack has started the specified session
+bool BluetoothAudioSession::IsSessionReady() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ bool dataMQ_valid =
+ (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
+ (mDataMQ != nullptr && mDataMQ->isValid()));
+ return stack_iface_ != nullptr && dataMQ_valid;
+}
+
+bool BluetoothAudioSession::UpdateDataPath(const DataMQ::Descriptor* dataMQ) {
+ if (dataMQ == nullptr) {
+ // usecase of reset by nullptr
+ mDataMQ = nullptr;
+ return true;
+ }
+ std::unique_ptr<DataMQ> tempDataMQ;
+ tempDataMQ.reset(new DataMQ(*dataMQ));
+ if (!tempDataMQ || !tempDataMQ->isValid()) {
+ mDataMQ = nullptr;
+ return false;
+ }
+ mDataMQ = std::move(tempDataMQ);
+ return true;
+}
+
+bool BluetoothAudioSession::UpdateAudioConfig(
+ const AudioConfiguration& audio_config) {
+ bool is_software_session =
+ (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+ session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
+ bool is_offload_session =
+ (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+ auto audio_config_discriminator = audio_config.getDiscriminator();
+ bool is_software_audio_config =
+ (is_software_session &&
+ audio_config_discriminator ==
+ AudioConfiguration::hidl_discriminator::pcmConfig);
+ bool is_offload_audio_config =
+ (is_offload_session &&
+ audio_config_discriminator ==
+ AudioConfiguration::hidl_discriminator::codecConfig);
+ if (!is_software_audio_config && !is_offload_audio_config) {
+ return false;
+ }
+ audio_config_ = audio_config;
+ return true;
+}
+
+// The control function helps the bluetooth_audio module to register
+// PortStatusCallbacks
+// @return: cookie - the assigned number to this bluetooth_audio output
+uint16_t BluetoothAudioSession::RegisterStatusCback(
+ const PortStatusCallbacks& cbacks) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ uint16_t cookie = ObserversCookieGetInitValue(session_type_);
+ uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_);
+
+ while (cookie < cookie_upper_bound) {
+ if (observers_.find(cookie) == observers_.end()) {
+ break;
+ }
+ ++cookie;
+ }
+ if (cookie >= cookie_upper_bound) {
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has " << observers_.size()
+ << " observers already (No Resource)";
+ return kObserversCookieUndefined;
+ }
+ std::shared_ptr<struct PortStatusCallbacks> cb =
+ std::make_shared<struct PortStatusCallbacks>();
+ *cb = cbacks;
+ observers_[cookie] = cb;
+ return cookie;
+}
+
+// The control function helps the bluetooth_audio module to unregister
+// PortStatusCallbacks
+// @param: cookie - indicates which bluetooth_audio output is
+void BluetoothAudioSession::UnregisterStatusCback(uint16_t cookie) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (observers_.erase(cookie) != 1) {
+ LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+ << " no such provider=0x"
+ << android::base::StringPrintf("%04x", cookie);
+ }
+}
+
+// The control function is for the bluetooth_audio module to get the current
+// AudioConfiguration
+const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (IsSessionReady()) {
+ return audio_config_;
+ } else if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ return kInvalidOffloadAudioConfiguration;
+ } else {
+ return kInvalidSoftwareAudioConfiguration;
+ }
+}
+
+// Those control functions are for the bluetooth_audio module to start, suspend,
+// stop stream, to check position, and to update metadata.
+bool BluetoothAudioSession::StartStream() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (!IsSessionReady()) {
+ LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO session";
+ return false;
+ }
+ auto hal_retval = stack_iface_->startStream();
+ if (!hal_retval.isOk()) {
+ LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+ << toString(session_type_) << " failed";
+ return false;
+ }
+ return true;
+}
+
+bool BluetoothAudioSession::SuspendStream() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (!IsSessionReady()) {
+ LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO session";
+ return false;
+ }
+ auto hal_retval = stack_iface_->suspendStream();
+ if (!hal_retval.isOk()) {
+ LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+ << toString(session_type_) << " failed";
+ return false;
+ }
+ return true;
+}
+
+void BluetoothAudioSession::StopStream() {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (!IsSessionReady()) {
+ return;
+ }
+ auto hal_retval = stack_iface_->stopStream();
+ if (!hal_retval.isOk()) {
+ LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+ << toString(session_type_) << " failed";
+ }
+}
+
+bool BluetoothAudioSession::GetPresentationPosition(
+ uint64_t* remote_delay_report_ns, uint64_t* total_bytes_readed,
+ timespec* data_position) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (!IsSessionReady()) {
+ LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO session";
+ return false;
+ }
+ bool retval = false;
+ auto hal_retval = stack_iface_->getPresentationPosition(
+ [&retval, &remote_delay_report_ns, &total_bytes_readed, &data_position](
+ BluetoothAudioStatus status,
+ const uint64_t& remoteDeviceAudioDelayNanos,
+ uint64_t transmittedOctets,
+ const TimeSpec& transmittedOctetsTimeStamp) {
+ if (status == BluetoothAudioStatus::SUCCESS) {
+ if (remote_delay_report_ns)
+ *remote_delay_report_ns = remoteDeviceAudioDelayNanos;
+ if (total_bytes_readed) *total_bytes_readed = transmittedOctets;
+ if (data_position)
+ *data_position =
+ timespec_convert_from_hal(transmittedOctetsTimeStamp);
+ retval = true;
+ }
+ });
+ if (!hal_retval.isOk()) {
+ LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+ << toString(session_type_) << " failed";
+ return false;
+ }
+ return retval;
+}
+
+void BluetoothAudioSession::UpdateTracksMetadata(
+ const struct source_metadata* source_metadata) {
+ std::lock_guard<std::recursive_mutex> guard(mutex_);
+ if (!IsSessionReady()) {
+ LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO session";
+ return;
+ }
+
+ ssize_t track_count = source_metadata->track_count;
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ", "
+ << track_count << " track(s)";
+ if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+ session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ return;
+ }
+
+ struct playback_track_metadata* track = source_metadata->tracks;
+ SourceMetadata sourceMetadata;
+ PlaybackTrackMetadata* halMetadata;
+
+ sourceMetadata.tracks.resize(track_count);
+ halMetadata = sourceMetadata.tracks.data();
+ while (track_count && track) {
+ halMetadata->usage = static_cast<AudioUsage>(track->usage);
+ halMetadata->contentType =
+ static_cast<AudioContentType>(track->content_type);
+ halMetadata->gain = track->gain;
+ LOG(VERBOSE) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", usage=" << toString(halMetadata->usage)
+ << ", content=" << toString(halMetadata->contentType)
+ << ", gain=" << halMetadata->gain;
+ --track_count;
+ ++track;
+ ++halMetadata;
+ }
+ auto hal_retval = stack_iface_->updateMetadata(sourceMetadata);
+ if (!hal_retval.isOk()) {
+ LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+ << toString(session_type_) << " failed";
+ }
+}
+
+// The control function writes stream to FMQ
+size_t BluetoothAudioSession::OutWritePcmData(const void* buffer,
+ size_t bytes) {
+ if (buffer == nullptr || !bytes) return 0;
+ size_t totalWritten = 0;
+ int ms_timeout = kFmqSendTimeoutMs;
+ do {
+ std::unique_lock<std::recursive_mutex> lock(mutex_);
+ if (!IsSessionReady()) break;
+ size_t availableToWrite = mDataMQ->availableToWrite();
+ if (availableToWrite) {
+ if (availableToWrite > (bytes - totalWritten)) {
+ availableToWrite = bytes - totalWritten;
+ }
+
+ if (!mDataMQ->write(static_cast<const uint8_t*>(buffer) + totalWritten,
+ availableToWrite)) {
+ ALOGE("FMQ datapath writting %zu/%zu failed", totalWritten, bytes);
+ return totalWritten;
+ }
+ totalWritten += availableToWrite;
+ } else if (ms_timeout >= kWritePollMs) {
+ lock.unlock();
+ usleep(kWritePollMs * 1000);
+ ms_timeout -= kWritePollMs;
+ } else {
+ ALOGD("data %zu/%zu overflow %d ms", totalWritten, bytes,
+ (kFmqSendTimeoutMs - ms_timeout));
+ return totalWritten;
+ }
+ } while (totalWritten < bytes);
+ return totalWritten;
+}
+
+std::unique_ptr<BluetoothAudioSessionInstance>
+ BluetoothAudioSessionInstance::instance_ptr =
+ std::unique_ptr<BluetoothAudioSessionInstance>(
+ new BluetoothAudioSessionInstance());
+
+// API to fetch the session of A2DP / Hearing Aid
+std::shared_ptr<BluetoothAudioSession>
+BluetoothAudioSessionInstance::GetSessionInstance(
+ const SessionType& session_type) {
+ std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
+ if (!instance_ptr->sessions_map_.empty()) {
+ auto entry = instance_ptr->sessions_map_.find(session_type);
+ if (entry != instance_ptr->sessions_map_.end()) {
+ return entry->second;
+ }
+ }
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ std::make_shared<BluetoothAudioSession>(session_type);
+ instance_ptr->sessions_map_[session_type] = session_ptr;
+ return session_ptr;
+}
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h
new file mode 100644
index 0000000..85e8742
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <unordered_map>
+
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
+#include <fmq/MessageQueue.h>
+#include <hardware/audio.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::sp;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
+using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+using BluetoothAudioStatus =
+ ::android::hardware::bluetooth::audio::V2_0::Status;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+static constexpr uint16_t kObserversCookieSize = 0x0010; // 0x0000 ~ 0x000f
+constexpr uint16_t kObserversCookieUndefined =
+ (static_cast<uint16_t>(SessionType::UNKNOWN) << 8 & 0xff00);
+inline SessionType ObserversCookieGetSessionType(uint16_t cookie) {
+ return static_cast<SessionType>(cookie >> 8 & 0x00ff);
+}
+inline uint16_t ObserversCookieGetInitValue(SessionType session_type) {
+ return (static_cast<uint16_t>(session_type) << 8 & 0xff00);
+}
+inline uint16_t ObserversCookieGetUpperBound(SessionType session_type) {
+ return (static_cast<uint16_t>(session_type) << 8 & 0xff00) +
+ kObserversCookieSize;
+}
+
+// This presents the callbacks of started / suspended and session changed,
+// and the bluetooth_audio module uses to receive the status notification
+struct PortStatusCallbacks {
+ // control_result_cb_ - when the Bluetooth stack reports results of
+ // streamStarted or streamSuspended, the BluetoothAudioProvider will invoke
+ // this callback to report to the bluetooth_audio module.
+ // @param: cookie - indicates which bluetooth_audio output should handle
+ // @param: start_resp - this report is for startStream or not
+ // @param: status - the result of startStream
+ std::function<void(uint16_t cookie, bool start_resp,
+ const BluetoothAudioStatus& status)>
+ control_result_cb_;
+ // session_changed_cb_ - when the Bluetooth stack start / end session, the
+ // BluetoothAudioProvider will invoke this callback to notify to the
+ // bluetooth_audio module.
+ // @param: cookie - indicates which bluetooth_audio output should handle
+ std::function<void(uint16_t cookie)> session_changed_cb_;
+};
+
+class BluetoothAudioSession {
+ private:
+ // using recursive_mutex to allow hwbinder to re-enter agian.
+ std::recursive_mutex mutex_;
+ SessionType session_type_;
+
+ // audio control path to use for both software and offloading
+ sp<IBluetoothAudioPort> stack_iface_;
+ // audio data path (FMQ) for software encoding
+ std::unique_ptr<DataMQ> mDataMQ;
+ // audio data configuration for both software and offloading
+ AudioConfiguration audio_config_;
+
+ static AudioConfiguration invalidSoftwareAudioConfiguration;
+ static AudioConfiguration invalidOffloadAudioConfiguration;
+
+ // saving those registered bluetooth_audio's callbacks
+ std::unordered_map<uint16_t, std::shared_ptr<struct PortStatusCallbacks>>
+ observers_;
+
+ bool UpdateDataPath(const DataMQ::Descriptor* dataMQ);
+ bool UpdateAudioConfig(const AudioConfiguration& audio_config);
+ // invoking the registered session_changed_cb_
+ void ReportSessionStatus();
+
+ public:
+ BluetoothAudioSession(const SessionType& session_type);
+
+ // The function helps to check if this session is ready or not
+ // @return: true if the Bluetooth stack has started the specified session
+ bool IsSessionReady();
+
+ // The report function is used to report that the Bluetooth stack has started
+ // this session without any failure, and will invoke session_changed_cb_ to
+ // notify those registered bluetooth_audio outputs
+ void OnSessionStarted(const sp<IBluetoothAudioPort> stack_iface,
+ const DataMQ::Descriptor* dataMQ,
+ const AudioConfiguration& audio_config);
+
+ // The report function is used to report that the Bluetooth stack has ended
+ // the session, and will invoke session_changed_cb_ to notify registered
+ // bluetooth_audio outputs
+ void OnSessionEnded();
+
+ // The report function is used to report that the Bluetooth stack has notified
+ // the result of startStream or suspendStream, and will invoke
+ // control_result_cb_ to notify registered bluetooth_audio outputs
+ void ReportControlStatus(bool start_resp, const BluetoothAudioStatus& status);
+
+ // The control function helps the bluetooth_audio module to register
+ // PortStatusCallbacks
+ // @return: cookie - the assigned number to this bluetooth_audio output
+ uint16_t RegisterStatusCback(const PortStatusCallbacks& cbacks);
+
+ // The control function helps the bluetooth_audio module to unregister
+ // PortStatusCallbacks
+ // @param: cookie - indicates which bluetooth_audio output is
+ void UnregisterStatusCback(uint16_t cookie);
+
+ // The control function is for the bluetooth_audio module to get the current
+ // AudioConfiguration
+ const AudioConfiguration& GetAudioConfig();
+
+ // Those control functions are for the bluetooth_audio module to start,
+ // suspend, stop stream, to check position, and to update metadata.
+ bool StartStream();
+ bool SuspendStream();
+ void StopStream();
+ bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
+ uint64_t* total_bytes_readed,
+ timespec* data_position);
+ void UpdateTracksMetadata(const struct source_metadata* source_metadata);
+
+ // The control function writes stream to FMQ
+ size_t OutWritePcmData(const void* buffer, size_t bytes);
+
+ static constexpr PcmParameters kInvalidPcmParameters = {
+ .sampleRate = SampleRate::RATE_UNKNOWN,
+ .bitsPerSample = BitsPerSample::BITS_UNKNOWN,
+ .channelMode = ChannelMode::UNKNOWN};
+ // can't be constexpr because of non-literal type
+ static const CodecConfiguration kInvalidCodecConfiguration;
+
+ static constexpr AudioConfiguration& kInvalidSoftwareAudioConfiguration =
+ invalidSoftwareAudioConfiguration;
+ static constexpr AudioConfiguration& kInvalidOffloadAudioConfiguration =
+ invalidOffloadAudioConfiguration;
+};
+
+class BluetoothAudioSessionInstance {
+ public:
+ // The API is to fetch the specified session of A2DP / Hearing Aid
+ static std::shared_ptr<BluetoothAudioSession> GetSessionInstance(
+ const SessionType& session_type);
+
+ private:
+ static std::unique_ptr<BluetoothAudioSessionInstance> instance_ptr;
+ std::mutex mutex_;
+ std::unordered_map<SessionType, std::shared_ptr<BluetoothAudioSession>>
+ sessions_map_;
+};
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSessionControl.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionControl.h
new file mode 100644
index 0000000..6707765
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionControl.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BluetoothAudioSession.h"
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothAudioSessionControl {
+ public:
+ // The control API helps to check if session is ready or not
+ // @return: true if the Bluetooth stack has started th specified session
+ static bool IsSessionReady(const SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->IsSessionReady();
+ }
+ return false;
+ }
+
+ // The control API helps the bluetooth_audio module to register
+ // PortStatusCallbacks
+ // @return: cookie - the assigned number to this bluetooth_audio output
+ static uint16_t RegisterControlResultCback(
+ const SessionType& session_type, const PortStatusCallbacks& cbacks) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->RegisterStatusCback(cbacks);
+ }
+ return kObserversCookieUndefined;
+ }
+
+ // The control API helps the bluetooth_audio module to unregister
+ // PortStatusCallbacks
+ // @param: cookie - indicates which bluetooth_audio output is
+ static void UnregisterControlResultCback(const SessionType& session_type,
+ uint16_t cookie) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->UnregisterStatusCback(cookie);
+ }
+ }
+
+ // The control API for the bluetooth_audio module to get current
+ // AudioConfiguration
+ static const AudioConfiguration& GetAudioConfig(
+ const SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->GetAudioConfig();
+ } else if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ return BluetoothAudioSession::kInvalidOffloadAudioConfiguration;
+ } else {
+ return BluetoothAudioSession::kInvalidSoftwareAudioConfiguration;
+ }
+ }
+
+ // Those control APIs for the bluetooth_audio module to start / suspend / stop
+ // stream, to check position, and to update metadata.
+ static bool StartStream(const SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->StartStream();
+ }
+ return false;
+ }
+
+ static bool SuspendStream(const SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->SuspendStream();
+ }
+ return false;
+ }
+
+ static void StopStream(const SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->StopStream();
+ }
+ }
+
+ static bool GetPresentationPosition(const SessionType& session_type,
+ uint64_t* remote_delay_report_ns,
+ uint64_t* total_bytes_readed,
+ timespec* data_position) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->GetPresentationPosition(
+ remote_delay_report_ns, total_bytes_readed, data_position);
+ }
+ return false;
+ }
+
+ static void UpdateTracksMetadata(
+ const SessionType& session_type,
+ const struct source_metadata* source_metadata) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->UpdateTracksMetadata(source_metadata);
+ }
+ }
+
+ // The control API writes stream to FMQ
+ static size_t OutWritePcmData(const SessionType& session_type,
+ const void* buffer, size_t bytes) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ return session_ptr->OutWritePcmData(buffer, bytes);
+ }
+ return 0;
+ }
+};
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSessionReport.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionReport.h
new file mode 100644
index 0000000..5a83ae2
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSessionReport.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "BluetoothAudioSession.h"
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothAudioSessionReport {
+ public:
+ // The API reports the Bluetooth stack has started the session, and will
+ // inform registered bluetooth_audio outputs
+ static void OnSessionStarted(const SessionType& session_type,
+ const sp<IBluetoothAudioPort> host_iface,
+ const DataMQ::Descriptor* dataMQ,
+ const AudioConfiguration& audio_config) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->OnSessionStarted(host_iface, dataMQ, audio_config);
+ }
+ }
+ // The API reports the Bluetooth stack has ended the session, and will
+ // inform registered bluetooth_audio outputs
+ static void OnSessionEnded(const SessionType& session_type) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->OnSessionEnded();
+ }
+ }
+ // The API reports the Bluetooth stack has replied the result of startStream
+ // or suspendStream, and will inform registered bluetooth_audio outputs
+ static void ReportControlStatus(const SessionType& session_type,
+ const bool& start_resp,
+ const BluetoothAudioStatus& status) {
+ std::shared_ptr<BluetoothAudioSession> session_ptr =
+ BluetoothAudioSessionInstance::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->ReportControlStatus(start_resp, status);
+ }
+ }
+};
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.cpp b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.cpp
new file mode 100644
index 0000000..292e28b
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderSessionCodecsDB"
+
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
+using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
+using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
+using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
+using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
+using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
+using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
+
+// Default Supported PCM Parameters
+static const PcmParameters kDefaultSoftwarePcmCapabilities = {
+ .sampleRate = static_cast<SampleRate>(
+ SampleRate::RATE_44100 | SampleRate::RATE_48000 |
+ SampleRate::RATE_88200 | SampleRate::RATE_96000 |
+ SampleRate::RATE_16000 | SampleRate::RATE_24000),
+ .channelMode =
+ static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
+ .bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
+ BitsPerSample::BITS_24 |
+ BitsPerSample::BITS_32)};
+
+// Default Supported Codecs
+// SBC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(MONO|STEREO)
+// all blocks | subbands 8 | Loudness
+static const SbcParameters kDefaultOffloadSbcCapability = {
+ .sampleRate = SampleRate::RATE_44100,
+ .channelMode = static_cast<SbcChannelMode>(SbcChannelMode::MONO |
+ SbcChannelMode::JOINT_STEREO),
+ .blockLength = static_cast<SbcBlockLength>(
+ SbcBlockLength::BLOCKS_4 | SbcBlockLength::BLOCKS_8 |
+ SbcBlockLength::BLOCKS_12 | SbcBlockLength::BLOCKS_16),
+ .numSubbands = SbcNumSubbands::SUBBAND_8,
+ .allocMethod = SbcAllocMethod::ALLOC_MD_L,
+ .bitsPerSample = BitsPerSample::BITS_16,
+ .minBitpool = 2,
+ .maxBitpool = 53};
+
+// AAC: mSampleRate:(44100), mBitsPerSample:(16), mChannelMode:(STEREO)
+static const AacParameters kDefaultOffloadAacCapability = {
+ .objectType = AacObjectType::MPEG2_LC,
+ .sampleRate = SampleRate::RATE_44100,
+ .channelMode = ChannelMode::STEREO,
+ .variableBitRateEnabled = AacVariableBitRate::DISABLED,
+ .bitsPerSample = BitsPerSample::BITS_16};
+
+// LDAC: mSampleRate:(44100|48000|88200|96000), mBitsPerSample:(16|24|32),
+// mChannelMode:(DUAL|STEREO)
+static const LdacParameters kDefaultOffloadLdacCapability = {
+ .sampleRate = static_cast<SampleRate>(
+ SampleRate::RATE_44100 | SampleRate::RATE_48000 |
+ SampleRate::RATE_88200 | SampleRate::RATE_96000),
+ .channelMode = static_cast<LdacChannelMode>(LdacChannelMode::DUAL |
+ LdacChannelMode::STEREO),
+ .qualityIndex = LdacQualityIndex::QUALITY_HIGH,
+ .bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
+ BitsPerSample::BITS_24 |
+ BitsPerSample::BITS_32)};
+
+// aptX: mSampleRate:(44100|48000), mBitsPerSample:(16), mChannelMode:(STEREO)
+static const AptxParameters kDefaultOffloadAptxCapability = {
+ .sampleRate = static_cast<SampleRate>(SampleRate::RATE_44100 |
+ SampleRate::RATE_48000),
+ .bitsPerSample = BitsPerSample::BITS_16,
+ .channelMode = ChannelMode::STEREO};
+
+// aptX HD: mSampleRate:(44100|48000), mBitsPerSample:(24),
+// mChannelMode:(STEREO)
+static const AptxParameters kDefaultOffloadAptxHdCapability = {
+ .sampleRate = static_cast<SampleRate>(SampleRate::RATE_44100 |
+ SampleRate::RATE_48000),
+ .bitsPerSample = BitsPerSample::BITS_24,
+ .channelMode = ChannelMode::STEREO};
+
+const std::vector<CodecCapabilities> kDefaultOffloadA2dpCodecCapabilities = {
+ {.codecType = CodecType::SBC, .capabilities = {}},
+ {.codecType = CodecType::AAC, .capabilities = {}},
+ {.codecType = CodecType::LDAC, .capabilities = {}},
+ {.codecType = CodecType::APTX, .capabilities = {}},
+ {.codecType = CodecType::APTX_HD, .capabilities = {}}};
+
+static bool IsSingleBit(uint32_t bitmasks, uint32_t bitfield) {
+ bool single = false;
+ uint32_t test_bit = 0x00000001;
+ while (test_bit <= bitmasks && test_bit <= bitfield) {
+ if (bitfield & test_bit && bitmasks & test_bit) {
+ if (single) return false;
+ single = true;
+ }
+ if (test_bit == 0x80000000) break;
+ test_bit <<= 1;
+ }
+ return single;
+}
+
+static bool IsOffloadSbcConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadAacConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadLdacConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadAptxConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific);
+static bool IsOffloadAptxHdConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific);
+
+static bool IsOffloadSbcConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific) {
+ if (codec_specific.getDiscriminator() !=
+ CodecConfiguration::CodecSpecific::hidl_discriminator::sbcConfig) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ }
+ const SbcParameters sbc_data = codec_specific.sbcConfig();
+ if (!IsSingleBit(static_cast<uint32_t>(sbc_data.sampleRate), 0xff) ||
+ !IsSingleBit(static_cast<uint32_t>(sbc_data.channelMode), 0x0f) ||
+ !IsSingleBit(static_cast<uint32_t>(sbc_data.blockLength), 0xf0) ||
+ !IsSingleBit(static_cast<uint32_t>(sbc_data.numSubbands), 0x0c) ||
+ !IsSingleBit(static_cast<uint32_t>(sbc_data.allocMethod), 0x03) ||
+ !IsSingleBit(static_cast<uint32_t>(sbc_data.bitsPerSample), 0x07) ||
+ sbc_data.minBitpool > sbc_data.maxBitpool) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ } else if ((sbc_data.sampleRate & kDefaultOffloadSbcCapability.sampleRate) &&
+ (sbc_data.channelMode &
+ kDefaultOffloadSbcCapability.channelMode) &&
+ (sbc_data.blockLength &
+ kDefaultOffloadSbcCapability.blockLength) &&
+ (sbc_data.numSubbands &
+ kDefaultOffloadSbcCapability.numSubbands) &&
+ (sbc_data.allocMethod &
+ kDefaultOffloadSbcCapability.allocMethod) &&
+ (sbc_data.bitsPerSample &
+ kDefaultOffloadSbcCapability.bitsPerSample) &&
+ (kDefaultOffloadSbcCapability.minBitpool <= sbc_data.minBitpool &&
+ sbc_data.maxBitpool <= kDefaultOffloadSbcCapability.maxBitpool)) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported CodecSpecific=" << toString(codec_specific);
+ return false;
+}
+
+static bool IsOffloadAacConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific) {
+ if (codec_specific.getDiscriminator() !=
+ CodecConfiguration::CodecSpecific::hidl_discriminator::aacConfig) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ }
+ const AacParameters aac_data = codec_specific.aacConfig();
+ if (!IsSingleBit(static_cast<uint32_t>(aac_data.objectType), 0xf0) ||
+ !IsSingleBit(static_cast<uint32_t>(aac_data.sampleRate), 0xff) ||
+ !IsSingleBit(static_cast<uint32_t>(aac_data.channelMode), 0x03) ||
+ !IsSingleBit(static_cast<uint32_t>(aac_data.bitsPerSample), 0x07)) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ } else if ((aac_data.objectType & kDefaultOffloadAacCapability.objectType) &&
+ (aac_data.sampleRate & kDefaultOffloadAacCapability.sampleRate) &&
+ (aac_data.channelMode &
+ kDefaultOffloadAacCapability.channelMode) &&
+ (aac_data.variableBitRateEnabled == AacVariableBitRate::DISABLED ||
+ kDefaultOffloadAacCapability.variableBitRateEnabled ==
+ AacVariableBitRate::ENABLED) &&
+ (aac_data.bitsPerSample &
+ kDefaultOffloadAacCapability.bitsPerSample)) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported CodecSpecific=" << toString(codec_specific);
+ return false;
+}
+
+static bool IsOffloadLdacConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific) {
+ if (codec_specific.getDiscriminator() !=
+ CodecConfiguration::CodecSpecific::hidl_discriminator::ldacConfig) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ }
+ const LdacParameters ldac_data = codec_specific.ldacConfig();
+ if (!IsSingleBit(static_cast<uint32_t>(ldac_data.sampleRate), 0xff) ||
+ !IsSingleBit(static_cast<uint32_t>(ldac_data.channelMode), 0x07) ||
+ (ldac_data.qualityIndex > LdacQualityIndex::QUALITY_LOW &&
+ ldac_data.qualityIndex != LdacQualityIndex::QUALITY_ABR) ||
+ !IsSingleBit(static_cast<uint32_t>(ldac_data.bitsPerSample), 0x07)) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ } else if ((ldac_data.sampleRate &
+ kDefaultOffloadLdacCapability.sampleRate) &&
+ (ldac_data.channelMode &
+ kDefaultOffloadLdacCapability.channelMode) &&
+ (ldac_data.bitsPerSample &
+ kDefaultOffloadLdacCapability.bitsPerSample)) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported CodecSpecific=" << toString(codec_specific);
+ return false;
+}
+
+static bool IsOffloadAptxConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific) {
+ if (codec_specific.getDiscriminator() !=
+ CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ }
+ const AptxParameters aptx_data = codec_specific.aptxConfig();
+ if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
+ !IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
+ !IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ } else if ((aptx_data.sampleRate &
+ kDefaultOffloadAptxCapability.sampleRate) &&
+ (aptx_data.channelMode &
+ kDefaultOffloadAptxCapability.channelMode) &&
+ (aptx_data.bitsPerSample &
+ kDefaultOffloadAptxCapability.bitsPerSample)) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported CodecSpecific=" << toString(codec_specific);
+ return false;
+}
+
+static bool IsOffloadAptxHdConfigurationValid(
+ const CodecConfiguration::CodecSpecific& codec_specific) {
+ if (codec_specific.getDiscriminator() !=
+ CodecConfiguration::CodecSpecific::hidl_discriminator::aptxConfig) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ }
+ const AptxParameters aptx_data = codec_specific.aptxConfig();
+ if (!IsSingleBit(static_cast<uint32_t>(aptx_data.sampleRate), 0xff) ||
+ !IsSingleBit(static_cast<uint32_t>(aptx_data.channelMode), 0x03) ||
+ !IsSingleBit(static_cast<uint32_t>(aptx_data.bitsPerSample), 0x07)) {
+ LOG(WARNING) << __func__
+ << ": Invalid CodecSpecific=" << toString(codec_specific);
+ return false;
+ } else if ((aptx_data.sampleRate &
+ kDefaultOffloadAptxHdCapability.sampleRate) &&
+ (aptx_data.channelMode &
+ kDefaultOffloadAptxHdCapability.channelMode) &&
+ (aptx_data.bitsPerSample &
+ kDefaultOffloadAptxHdCapability.bitsPerSample)) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported CodecSpecific=" << toString(codec_specific);
+ return false;
+}
+
+std::vector<PcmParameters> GetSoftwarePcmCapabilities() {
+ return std::vector<PcmParameters>(1, kDefaultSoftwarePcmCapabilities);
+}
+
+std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
+ const SessionType& session_type) {
+ if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ return std::vector<CodecCapabilities>(0);
+ }
+ std::vector<CodecCapabilities> offload_a2dp_codec_capabilities =
+ kDefaultOffloadA2dpCodecCapabilities;
+ for (auto& codec_capability : offload_a2dp_codec_capabilities) {
+ switch (codec_capability.codecType) {
+ case CodecType::SBC:
+ codec_capability.capabilities.sbcCapabilities(
+ kDefaultOffloadSbcCapability);
+ break;
+ case CodecType::AAC:
+ codec_capability.capabilities.aacCapabilities(
+ kDefaultOffloadAacCapability);
+ break;
+ case CodecType::LDAC:
+ codec_capability.capabilities.ldacCapabilities(
+ kDefaultOffloadLdacCapability);
+ break;
+ case CodecType::APTX:
+ codec_capability.capabilities.aptxCapabilities(
+ kDefaultOffloadAptxCapability);
+ break;
+ case CodecType::APTX_HD:
+ codec_capability.capabilities.aptxCapabilities(
+ kDefaultOffloadAptxHdCapability);
+ break;
+ case CodecType::UNKNOWN:
+ codec_capability = {};
+ break;
+ }
+ }
+ return offload_a2dp_codec_capabilities;
+}
+
+bool IsSoftwarePcmConfigurationValid(const PcmParameters& pcm_config) {
+ if ((pcm_config.sampleRate != SampleRate::RATE_44100 &&
+ pcm_config.sampleRate != SampleRate::RATE_48000 &&
+ pcm_config.sampleRate != SampleRate::RATE_88200 &&
+ pcm_config.sampleRate != SampleRate::RATE_96000 &&
+ pcm_config.sampleRate != SampleRate::RATE_16000 &&
+ pcm_config.sampleRate != SampleRate::RATE_24000) ||
+ (pcm_config.bitsPerSample != BitsPerSample::BITS_16 &&
+ pcm_config.bitsPerSample != BitsPerSample::BITS_24 &&
+ pcm_config.bitsPerSample != BitsPerSample::BITS_32) ||
+ (pcm_config.channelMode != ChannelMode::MONO &&
+ pcm_config.channelMode != ChannelMode::STEREO)) {
+ LOG(WARNING) << __func__
+ << ": Invalid PCM Configuration=" << toString(pcm_config);
+ return false;
+ } else if (pcm_config.sampleRate &
+ kDefaultSoftwarePcmCapabilities.sampleRate &&
+ pcm_config.bitsPerSample &
+ kDefaultSoftwarePcmCapabilities.bitsPerSample &&
+ pcm_config.channelMode &
+ kDefaultSoftwarePcmCapabilities.channelMode) {
+ return true;
+ }
+ LOG(WARNING) << __func__
+ << ": Unsupported PCM Configuration=" << toString(pcm_config);
+ return false;
+}
+
+bool IsOffloadCodecConfigurationValid(const SessionType& session_type,
+ const CodecConfiguration& codec_config) {
+ if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ LOG(ERROR) << __func__
+ << ": Invalid SessionType=" << toString(session_type);
+ return false;
+ } else if (codec_config.encodedAudioBitrate < 0x00000001 ||
+ 0x00ffffff < codec_config.encodedAudioBitrate) {
+ LOG(ERROR) << __func__ << ": Unsupported Codec Configuration="
+ << toString(codec_config);
+ return false;
+ }
+ const CodecConfiguration::CodecSpecific& codec_specific = codec_config.config;
+ switch (codec_config.codecType) {
+ case CodecType::SBC:
+ if (IsOffloadSbcConfigurationValid(codec_specific)) {
+ return true;
+ }
+ return false;
+ case CodecType::AAC:
+ if (IsOffloadAacConfigurationValid(codec_specific)) {
+ return true;
+ }
+ return false;
+ case CodecType::LDAC:
+ if (IsOffloadLdacConfigurationValid(codec_specific)) {
+ return true;
+ }
+ return false;
+ case CodecType::APTX:
+ if (IsOffloadAptxConfigurationValid(codec_specific)) {
+ return true;
+ }
+ return false;
+ case CodecType::APTX_HD:
+ if (IsOffloadAptxHdConfigurationValid(codec_specific)) {
+ return true;
+ }
+ return false;
+ case CodecType::UNKNOWN:
+ return false;
+ }
+ return false;
+}
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.h
new file mode 100644
index 0000000..e71dc8a
--- /dev/null
+++ b/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.0/types.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+std::vector<PcmParameters> GetSoftwarePcmCapabilities();
+std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
+ const SessionType& session_type);
+
+bool IsSoftwarePcmConfigurationValid(const PcmParameters& pcm_config);
+bool IsOffloadCodecConfigurationValid(const SessionType& session_type,
+ const CodecConfiguration& codec_config);
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/2.0/vts/OWNERS b/bluetooth/audio/2.0/vts/OWNERS
new file mode 100644
index 0000000..b6c0813
--- /dev/null
+++ b/bluetooth/audio/2.0/vts/OWNERS
@@ -0,0 +1,3 @@
+include platform/system/bt:/OWNERS
+
+cheneyni@google.com
diff --git a/bluetooth/audio/2.0/vts/functional/Android.bp b/bluetooth/audio/2.0/vts/functional/Android.bp
new file mode 100644
index 0000000..b672fe4
--- /dev/null
+++ b/bluetooth/audio/2.0/vts/functional/Android.bp
@@ -0,0 +1,12 @@
+cc_test {
+ name: "VtsHalBluetoothAudioV2_0TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalBluetoothAudioV2_0TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.audio.common@5.0",
+ "android.hardware.bluetooth.audio@2.0",
+ ],
+ shared_libs: [
+ "libfmq",
+ ],
+}
diff --git a/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp b/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp
new file mode 100644
index 0000000..9572d3f
--- /dev/null
+++ b/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp
@@ -0,0 +1,915 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "bluetooth_audio_hidl_hal_test"
+
+#include <android-base/logging.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvider.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioProvidersFactory.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <utils/Log.h>
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::audio::common::V5_0::SourceMetadata;
+using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
+using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
+using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AudioCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioProvider;
+using ::android::hardware::bluetooth::audio::V2_0::
+ IBluetoothAudioProvidersFactory;
+using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
+using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
+using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
+using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
+using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using BluetoothAudioStatus =
+ ::android::hardware::bluetooth::audio::V2_0::Status;
+using CodecSpecificConfig = ::android::hardware::bluetooth::audio::V2_0::
+ CodecConfiguration::CodecSpecific;
+
+namespace {
+constexpr SampleRate a2dp_sample_rates[5] = {
+ SampleRate::RATE_UNKNOWN, SampleRate::RATE_44100, SampleRate::RATE_48000,
+ SampleRate::RATE_88200, SampleRate::RATE_96000};
+constexpr BitsPerSample a2dp_bits_per_samples[4] = {
+ BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16, BitsPerSample::BITS_24,
+ BitsPerSample::BITS_32};
+constexpr ChannelMode a2dp_channel_modes[3] = {
+ ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+constexpr CodecType a2dp_codec_types[6] = {CodecType::UNKNOWN, CodecType::SBC,
+ CodecType::AAC, CodecType::APTX,
+ CodecType::APTX_HD, CodecType::LDAC};
+
+template <typename T>
+std::vector<T> ExtractValuesFromBitmask(T bitmasks, uint32_t bitfield,
+ bool supported) {
+ std::vector<T> retval;
+ if (!supported) {
+ retval.push_back(static_cast<T>(bitfield));
+ }
+ uint32_t test_bit = 0x00000001;
+ while (test_bit <= static_cast<uint32_t>(bitmasks) && test_bit <= bitfield) {
+ if ((bitfield & test_bit)) {
+ if ((!(bitmasks & test_bit) && !supported) ||
+ ((bitmasks & test_bit) && supported)) {
+ retval.push_back(static_cast<T>(test_bit));
+ }
+ }
+ if (test_bit == 0x80000000) {
+ break;
+ }
+ test_bit <<= 1;
+ }
+ return retval;
+}
+} // namespace
+
+// Test environment for Bluetooth Audio HAL.
+class BluetoothAudioHidlEnvironment
+ : public ::testing::VtsHalHidlTargetTestEnvBase {
+ public:
+ // get the test environment singleton
+ static BluetoothAudioHidlEnvironment* Instance() {
+ static BluetoothAudioHidlEnvironment* instance =
+ new BluetoothAudioHidlEnvironment;
+ return instance;
+ }
+
+ virtual void registerTestServices() override {
+ registerTestService<IBluetoothAudioProvidersFactory>();
+ }
+
+ private:
+ BluetoothAudioHidlEnvironment() {}
+};
+
+// The base test class for Bluetooth Audio HAL.
+class BluetoothAudioProvidersFactoryHidlTest
+ : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+ virtual void SetUp() override {
+ providers_factory_ = ::testing::VtsHalHidlTargetTestBase::getService<
+ IBluetoothAudioProvidersFactory>(
+ BluetoothAudioHidlEnvironment::Instance()
+ ->getServiceName<IBluetoothAudioProvidersFactory>());
+ ASSERT_NE(providers_factory_, nullptr);
+ }
+
+ virtual void TearDown() override { providers_factory_ = nullptr; }
+
+ // A simple test implementation of IBluetoothAudioPort.
+ class BluetoothAudioPort : public ::testing::VtsHalHidlTargetCallbackBase<
+ BluetoothAudioProvidersFactoryHidlTest>,
+ public IBluetoothAudioPort {
+ BluetoothAudioProvidersFactoryHidlTest& parent_;
+
+ public:
+ BluetoothAudioPort(BluetoothAudioProvidersFactoryHidlTest& parent)
+ : parent_(parent) {}
+ virtual ~BluetoothAudioPort() = default;
+
+ Return<void> startStream() override {
+ parent_.audio_provider_->streamStarted(BluetoothAudioStatus::SUCCESS);
+ return Void();
+ }
+
+ Return<void> suspendStream() override {
+ parent_.audio_provider_->streamSuspended(BluetoothAudioStatus::SUCCESS);
+ return Void();
+ }
+
+ Return<void> stopStream() override { return Void(); }
+
+ Return<void> getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, 0, 0, {.tvSec = 0, .tvNSec = 0});
+ return Void();
+ }
+
+ Return<void> updateMetadata(const SourceMetadata& sourceMetadata __unused) {
+ return Void();
+ }
+ };
+
+ void GetProviderCapabilitiesHelper(const SessionType& session_type) {
+ temp_provider_capabilities_.clear();
+ auto hidl_cb = [& temp_capabilities = this->temp_provider_capabilities_](
+ const hidl_vec<AudioCapabilities>& audioCapabilities) {
+ for (auto audioCapability : audioCapabilities)
+ temp_capabilities.push_back(audioCapability);
+ };
+ auto hidl_retval =
+ providers_factory_->getProviderCapabilities(session_type, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ if (session_type == SessionType::UNKNOWN) {
+ ASSERT_TRUE(temp_provider_capabilities_.empty());
+ } else if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ // All software paths are mandatory and must have exact 1 "PcmParameters"
+ ASSERT_EQ(temp_provider_capabilities_.size(), 1);
+ ASSERT_EQ(temp_provider_capabilities_[0].getDiscriminator(),
+ AudioCapabilities::hidl_discriminator::pcmCapabilities);
+ } else {
+ uint32_t codec_type_bitmask = 0x00000000;
+ // empty capability means offload is unsupported
+ for (auto audio_capability : temp_provider_capabilities_) {
+ ASSERT_EQ(audio_capability.getDiscriminator(),
+ AudioCapabilities::hidl_discriminator::codecCapabilities);
+ const CodecCapabilities& codec_capabilities =
+ audio_capability.codecCapabilities();
+ // Every codec can present once at most
+ ASSERT_EQ(codec_type_bitmask &
+ static_cast<uint32_t>(codec_capabilities.codecType),
+ 0);
+ switch (codec_capabilities.codecType) {
+ case CodecType::SBC:
+ ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+ CodecCapabilities::Capabilities::hidl_discriminator::
+ sbcCapabilities);
+ break;
+ case CodecType::AAC:
+ ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+ CodecCapabilities::Capabilities::hidl_discriminator::
+ aacCapabilities);
+ break;
+ case CodecType::APTX:
+ FALLTHROUGH_INTENDED;
+ case CodecType::APTX_HD:
+ ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+ CodecCapabilities::Capabilities::hidl_discriminator::
+ aptxCapabilities);
+ break;
+ case CodecType::LDAC:
+ ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+ CodecCapabilities::Capabilities::hidl_discriminator::
+ ldacCapabilities);
+ break;
+ case CodecType::UNKNOWN:
+ break;
+ }
+ codec_type_bitmask |= codec_capabilities.codecType;
+ }
+ }
+ }
+
+ // This helps to open the specified provider and check the openProvider()
+ // has corruct return values. BUT, to keep it simple, it does not consider
+ // the capability, and please do so at the SetUp of each session's test.
+ void OpenProviderHelper(const SessionType& session_type) {
+ BluetoothAudioStatus cb_status;
+ auto hidl_cb = [&cb_status, &local_provider = this->audio_provider_](
+ BluetoothAudioStatus status,
+ const sp<IBluetoothAudioProvider>& provider) {
+ cb_status = status;
+ local_provider = provider;
+ };
+ auto hidl_retval = providers_factory_->openProvider(session_type, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ if (cb_status == BluetoothAudioStatus::SUCCESS) {
+ ASSERT_NE(session_type, SessionType::UNKNOWN);
+ ASSERT_NE(audio_provider_, nullptr);
+ audio_port_ = new BluetoothAudioPort(*this);
+ } else {
+ // A2DP_HARDWARE_OFFLOAD_DATAPATH is optional
+ ASSERT_TRUE(session_type == SessionType::UNKNOWN ||
+ session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+ ASSERT_EQ(cb_status, BluetoothAudioStatus::FAILURE);
+ ASSERT_EQ(audio_provider_, nullptr);
+ }
+ }
+
+ bool IsPcmParametersSupported(const PcmParameters& pcm_parameters) {
+ if (temp_provider_capabilities_.size() != 1 ||
+ temp_provider_capabilities_[0].getDiscriminator() !=
+ AudioCapabilities::hidl_discriminator::pcmCapabilities) {
+ return false;
+ }
+ auto pcm_capability = temp_provider_capabilities_[0].pcmCapabilities();
+ bool is_parameter_valid =
+ (pcm_parameters.sampleRate != SampleRate::RATE_UNKNOWN &&
+ pcm_parameters.channelMode != ChannelMode::UNKNOWN &&
+ pcm_parameters.bitsPerSample != BitsPerSample::BITS_UNKNOWN);
+ bool is_parameter_in_capability =
+ (pcm_capability.sampleRate & pcm_parameters.sampleRate &&
+ pcm_capability.channelMode & pcm_parameters.channelMode &&
+ pcm_capability.bitsPerSample & pcm_parameters.bitsPerSample);
+ return is_parameter_valid && is_parameter_in_capability;
+ }
+
+ sp<IBluetoothAudioProvidersFactory> providers_factory_;
+
+ // temp storage saves the specified provider capability by
+ // GetProviderCapabilitiesHelper()
+ std::vector<AudioCapabilities> temp_provider_capabilities_;
+
+ // audio_provider_ is for the Bluetooth stack to report session started/ended
+ // and handled audio stream started / suspended
+ sp<IBluetoothAudioProvider> audio_provider_;
+
+ // audio_port_ is for the Audio HAL to send stream start/suspend/stop commands
+ // to Bluetooth stack
+ sp<IBluetoothAudioPort> audio_port_;
+
+ static constexpr SessionType session_types_[4] = {
+ SessionType::UNKNOWN, SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH,
+ SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH,
+ SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH};
+};
+
+/**
+ * Test whether we can get the FactoryService from HIDL
+ */
+TEST_F(BluetoothAudioProvidersFactoryHidlTest, GetProvidersFactoryService) {}
+
+/**
+ * Test whether we can open a provider for each provider returned by
+ * getProviderCapabilities() with non-empty capabalities
+ */
+TEST_F(BluetoothAudioProvidersFactoryHidlTest,
+ OpenProviderAndCheckCapabilitiesBySession) {
+ for (auto session_type : session_types_) {
+ GetProviderCapabilitiesHelper(session_type);
+ OpenProviderHelper(session_type);
+ // We must be able to open a provider if its getProviderCapabilities()
+ // returns non-empty list.
+ EXPECT_TRUE(temp_provider_capabilities_.empty() ||
+ audio_provider_ != nullptr);
+ }
+}
+
+/**
+ * openProvider A2DP_SOFTWARE_ENCODING_DATAPATH
+ */
+class BluetoothAudioProviderA2dpSoftwareHidlTest
+ : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+ virtual void SetUp() override {
+ BluetoothAudioProvidersFactoryHidlTest::SetUp();
+ GetProviderCapabilitiesHelper(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
+ OpenProviderHelper(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
+ ASSERT_NE(audio_provider_, nullptr);
+ }
+
+ virtual void TearDown() override {
+ audio_port_ = nullptr;
+ audio_provider_ = nullptr;
+ BluetoothAudioProvidersFactoryHidlTest::TearDown();
+ }
+};
+
+/**
+ * Test whether we can open a provider of type
+ */
+TEST_F(BluetoothAudioProviderA2dpSoftwareHidlTest, OpenA2dpSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH can be started and stopped with
+ * different PCM config
+ */
+TEST_F(BluetoothAudioProviderA2dpSoftwareHidlTest,
+ StartAndEndA2dpSoftwareSessionWithPossiblePcmConfig) {
+ bool is_codec_config_valid;
+ std::unique_ptr<DataMQ> tempDataMQ;
+ auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+ BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ if (is_codec_config_valid) {
+ ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ ASSERT_TRUE(dataMQ.isHandleValid());
+ tempDataMQ.reset(new DataMQ(dataMQ));
+ } else {
+ EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ }
+ };
+ AudioConfiguration audio_config = {};
+ PcmParameters pcm_parameters = {};
+ for (auto sample_rate : a2dp_sample_rates) {
+ pcm_parameters.sampleRate = sample_rate;
+ for (auto bits_per_sample : a2dp_bits_per_samples) {
+ pcm_parameters.bitsPerSample = bits_per_sample;
+ for (auto channel_mode : a2dp_channel_modes) {
+ pcm_parameters.channelMode = channel_mode;
+ is_codec_config_valid = IsPcmParametersSupported(pcm_parameters);
+ audio_config.pcmConfig(pcm_parameters);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ if (is_codec_config_valid) {
+ EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+ }
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ } // ChannelMode
+ } // BitsPerSampple
+ } // SampleRate
+}
+
+/**
+ * openProvider A2DP_HARDWARE_OFFLOAD_DATAPATH
+ */
+class BluetoothAudioProviderA2dpHardwareHidlTest
+ : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+ virtual void SetUp() override {
+ BluetoothAudioProvidersFactoryHidlTest::SetUp();
+ GetProviderCapabilitiesHelper(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+ OpenProviderHelper(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+ ASSERT_TRUE(temp_provider_capabilities_.empty() ||
+ audio_provider_ != nullptr);
+ }
+
+ virtual void TearDown() override {
+ audio_port_ = nullptr;
+ audio_provider_ = nullptr;
+ BluetoothAudioProvidersFactoryHidlTest::TearDown();
+ }
+
+ bool IsOffloadSupported() { return (temp_provider_capabilities_.size() > 0); }
+
+ void GetOffloadCodecCapabilityHelper(const CodecType& codec_type) {
+ temp_codec_capabilities_ = {};
+ for (auto codec_capability : temp_provider_capabilities_) {
+ if (codec_capability.codecCapabilities().codecType != codec_type) {
+ continue;
+ }
+ temp_codec_capabilities_ = codec_capability.codecCapabilities();
+ }
+ }
+
+ std::vector<CodecSpecificConfig> GetSbcCodecSpecificSupportedList(
+ bool supported) {
+ std::vector<CodecSpecificConfig> sbc_codec_specifics;
+ GetOffloadCodecCapabilityHelper(CodecType::SBC);
+ if (temp_codec_capabilities_.codecType != CodecType::SBC) {
+ return sbc_codec_specifics;
+ }
+ // parse the capability
+ SbcParameters sbc_capability =
+ temp_codec_capabilities_.capabilities.sbcCapabilities();
+ if (sbc_capability.minBitpool > sbc_capability.maxBitpool) {
+ return sbc_codec_specifics;
+ }
+ std::vector<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+ sbc_capability.sampleRate, 0xff, supported);
+ std::vector<SbcChannelMode> channel_modes =
+ ExtractValuesFromBitmask<SbcChannelMode>(sbc_capability.channelMode,
+ 0x0f, supported);
+ std::vector<SbcBlockLength> block_lengths =
+ ExtractValuesFromBitmask<SbcBlockLength>(sbc_capability.blockLength,
+ 0xf0, supported);
+ std::vector<SbcNumSubbands> num_subbandss =
+ ExtractValuesFromBitmask<SbcNumSubbands>(sbc_capability.numSubbands,
+ 0x0c, supported);
+ std::vector<SbcAllocMethod> alloc_methods =
+ ExtractValuesFromBitmask<SbcAllocMethod>(sbc_capability.allocMethod,
+ 0x03, supported);
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(sbc_capability.bitsPerSample,
+ 0x07, supported);
+ // combine those parameters into one list of
+ // CodecConfiguration::CodecSpecific
+ CodecSpecificConfig codec_specific = {};
+ SbcParameters sbc_data;
+ for (auto sample_rate : sample_rates) {
+ for (auto channel_mode : channel_modes) {
+ for (auto block_length : block_lengths) {
+ for (auto num_subbands : num_subbandss) {
+ for (auto alloc_method : alloc_methods) {
+ for (auto bits_per_sample : bits_per_samples) {
+ sbc_data = {.sampleRate = sample_rate,
+ .channelMode = channel_mode,
+ .blockLength = block_length,
+ .numSubbands = num_subbands,
+ .allocMethod = alloc_method,
+ .bitsPerSample = bits_per_sample,
+ .minBitpool = sbc_capability.minBitpool,
+ .maxBitpool = sbc_capability.maxBitpool};
+ codec_specific.sbcConfig(sbc_data);
+ sbc_codec_specifics.push_back(codec_specific);
+ }
+ }
+ }
+ }
+ }
+ }
+ return sbc_codec_specifics;
+ }
+
+ std::vector<CodecSpecificConfig> GetAacCodecSpecificSupportedList(
+ bool supported) {
+ std::vector<CodecSpecificConfig> aac_codec_specifics;
+ GetOffloadCodecCapabilityHelper(CodecType::AAC);
+ if (temp_codec_capabilities_.codecType != CodecType::AAC) {
+ return aac_codec_specifics;
+ }
+ // parse the capability
+ AacParameters aac_capability =
+ temp_codec_capabilities_.capabilities.aacCapabilities();
+ std::vector<AacObjectType> object_types =
+ ExtractValuesFromBitmask<AacObjectType>(aac_capability.objectType, 0xf0,
+ supported);
+ std::vector<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+ aac_capability.sampleRate, 0xff, supported);
+ std::vector<ChannelMode> channel_modes =
+ ExtractValuesFromBitmask<ChannelMode>(aac_capability.channelMode, 0x03,
+ supported);
+ std::vector<AacVariableBitRate> variable_bit_rate_enableds = {
+ AacVariableBitRate::DISABLED};
+ if (aac_capability.variableBitRateEnabled == AacVariableBitRate::ENABLED) {
+ variable_bit_rate_enableds.push_back(AacVariableBitRate::ENABLED);
+ }
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(aac_capability.bitsPerSample,
+ 0x07, supported);
+ // combine those parameters into one list of
+ // CodecConfiguration::CodecSpecific
+ CodecSpecificConfig codec_specific = {};
+ AacParameters aac_data;
+ for (auto object_type : object_types) {
+ for (auto sample_rate : sample_rates) {
+ for (auto channel_mode : channel_modes) {
+ for (auto variable_bit_rate_enabled : variable_bit_rate_enableds) {
+ for (auto bits_per_sample : bits_per_samples) {
+ aac_data = {.objectType = object_type,
+ .sampleRate = sample_rate,
+ .channelMode = channel_mode,
+ .variableBitRateEnabled = variable_bit_rate_enabled,
+ .bitsPerSample = bits_per_sample};
+ codec_specific.aacConfig(aac_data);
+ aac_codec_specifics.push_back(codec_specific);
+ }
+ }
+ }
+ }
+ }
+ return aac_codec_specifics;
+ }
+
+ std::vector<CodecSpecificConfig> GetLdacCodecSpecificSupportedList(
+ bool supported) {
+ std::vector<CodecSpecificConfig> ldac_codec_specifics;
+ GetOffloadCodecCapabilityHelper(CodecType::LDAC);
+ if (temp_codec_capabilities_.codecType != CodecType::LDAC) {
+ return ldac_codec_specifics;
+ }
+ // parse the capability
+ LdacParameters ldac_capability =
+ temp_codec_capabilities_.capabilities.ldacCapabilities();
+ std::vector<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+ ldac_capability.sampleRate, 0xff, supported);
+ std::vector<LdacChannelMode> channel_modes =
+ ExtractValuesFromBitmask<LdacChannelMode>(ldac_capability.channelMode,
+ 0x07, supported);
+ std::vector<LdacQualityIndex> quality_indexes = {
+ LdacQualityIndex::QUALITY_HIGH, LdacQualityIndex::QUALITY_MID,
+ LdacQualityIndex::QUALITY_LOW, LdacQualityIndex::QUALITY_ABR};
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(ldac_capability.bitsPerSample,
+ 0x07, supported);
+ // combine those parameters into one list of
+ // CodecConfiguration::CodecSpecific
+ CodecSpecificConfig codec_specific = {};
+ LdacParameters ldac_data;
+ for (auto sample_rate : sample_rates) {
+ for (auto channel_mode : channel_modes) {
+ for (auto quality_index : quality_indexes) {
+ for (auto bits_per_sample : bits_per_samples) {
+ ldac_data = {.sampleRate = sample_rate,
+ .channelMode = channel_mode,
+ .qualityIndex = quality_index,
+ .bitsPerSample = bits_per_sample};
+ codec_specific.ldacConfig(ldac_data);
+ ldac_codec_specifics.push_back(codec_specific);
+ }
+ }
+ }
+ }
+ return ldac_codec_specifics;
+ }
+
+ std::vector<CodecSpecificConfig> GetAptxCodecSpecificSupportedList(
+ bool is_hd, bool supported) {
+ std::vector<CodecSpecificConfig> aptx_codec_specifics;
+ GetOffloadCodecCapabilityHelper(
+ (is_hd ? CodecType::APTX_HD : CodecType::APTX));
+ if ((is_hd && temp_codec_capabilities_.codecType != CodecType::APTX_HD) ||
+ (!is_hd && temp_codec_capabilities_.codecType != CodecType::APTX)) {
+ return aptx_codec_specifics;
+ }
+ // parse the capability
+ AptxParameters aptx_capability =
+ temp_codec_capabilities_.capabilities.aptxCapabilities();
+ std::vector<SampleRate> sample_rates = ExtractValuesFromBitmask<SampleRate>(
+ aptx_capability.sampleRate, 0xff, supported);
+ std::vector<ChannelMode> channel_modes =
+ ExtractValuesFromBitmask<ChannelMode>(aptx_capability.channelMode, 0x03,
+ supported);
+ std::vector<BitsPerSample> bits_per_samples =
+ ExtractValuesFromBitmask<BitsPerSample>(aptx_capability.bitsPerSample,
+ 0x07, supported);
+ // combine those parameters into one list of
+ // CodecConfiguration::CodecSpecific
+ CodecSpecificConfig codec_specific = {};
+ AptxParameters aptx_data;
+ for (auto sample_rate : sample_rates) {
+ for (auto channel_mode : channel_modes) {
+ for (auto bits_per_sample : bits_per_samples) {
+ aptx_data = {.sampleRate = sample_rate,
+ .channelMode = channel_mode,
+ .bitsPerSample = bits_per_sample};
+ codec_specific.aptxConfig(aptx_data);
+ aptx_codec_specifics.push_back(codec_specific);
+ }
+ }
+ }
+ return aptx_codec_specifics;
+ }
+
+ // temp storage saves the specified codec capability by
+ // GetOffloadCodecCapabilityHelper()
+ CodecCapabilities temp_codec_capabilities_;
+};
+
+/**
+ * Test whether we can open a provider of type
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest, OpenA2dpHardwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * SBC hardware encoding config
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest,
+ StartAndEndA2dpSbcHardwareSession) {
+ if (!IsOffloadSupported()) {
+ return;
+ }
+
+ CodecConfiguration codec_config = {};
+ codec_config.codecType = CodecType::SBC;
+ codec_config.encodedAudioBitrate = 328000;
+ codec_config.peerMtu = 1005;
+ codec_config.isScmstEnabled = false;
+ AudioConfiguration audio_config = {};
+ std::vector<CodecSpecificConfig> sbc_codec_specifics =
+ GetSbcCodecSpecificSupportedList(true);
+ auto hidl_cb = [](BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ };
+ for (auto codec_specific : sbc_codec_specifics) {
+ codec_config.config = codec_specific;
+ audio_config.codecConfig(codec_config);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * AAC hardware encoding config
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest,
+ StartAndEndA2dpAacHardwareSession) {
+ if (!IsOffloadSupported()) {
+ return;
+ }
+
+ CodecConfiguration codec_config = {};
+ codec_config.codecType = CodecType::AAC;
+ codec_config.encodedAudioBitrate = 320000;
+ codec_config.peerMtu = 1005;
+ codec_config.isScmstEnabled = false;
+ AudioConfiguration audio_config = {};
+ std::vector<CodecSpecificConfig> aac_codec_specifics =
+ GetAacCodecSpecificSupportedList(true);
+ auto hidl_cb = [](BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ };
+ for (auto codec_specific : aac_codec_specifics) {
+ codec_config.config = codec_specific;
+ audio_config.codecConfig(codec_config);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * LDAC hardware encoding config
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest,
+ StartAndEndA2dpLdacHardwareSession) {
+ if (!IsOffloadSupported()) {
+ return;
+ }
+
+ CodecConfiguration codec_config = {};
+ codec_config.codecType = CodecType::LDAC;
+ codec_config.encodedAudioBitrate = 990000;
+ codec_config.peerMtu = 1005;
+ codec_config.isScmstEnabled = false;
+ AudioConfiguration audio_config = {};
+ std::vector<CodecSpecificConfig> ldac_codec_specifics =
+ GetLdacCodecSpecificSupportedList(true);
+ auto hidl_cb = [](BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ };
+ for (auto codec_specific : ldac_codec_specifics) {
+ codec_config.config = codec_specific;
+ audio_config.codecConfig(codec_config);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * AptX hardware encoding config
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest,
+ StartAndEndA2dpAptxHardwareSession) {
+ if (!IsOffloadSupported()) {
+ return;
+ }
+
+ for (auto codec_type : {CodecType::APTX, CodecType::APTX_HD}) {
+ CodecConfiguration codec_config = {};
+ codec_config.codecType = codec_type;
+ codec_config.encodedAudioBitrate =
+ (codec_type == CodecType::APTX ? 352000 : 576000);
+ codec_config.peerMtu = 1005;
+ codec_config.isScmstEnabled = false;
+ AudioConfiguration audio_config = {};
+ std::vector<CodecSpecificConfig> aptx_codec_specifics =
+ GetAptxCodecSpecificSupportedList(
+ (codec_type == CodecType::APTX_HD ? true : false), true);
+ auto hidl_cb = [](BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ };
+ for (auto codec_specific : aptx_codec_specifics) {
+ codec_config.config = codec_specific;
+ audio_config.codecConfig(codec_config);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ }
+ }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * an invalid codec config
+ */
+TEST_F(BluetoothAudioProviderA2dpHardwareHidlTest,
+ StartAndEndA2dpHardwareSessionInvalidCodecConfig) {
+ if (!IsOffloadSupported()) {
+ return;
+ }
+ ASSERT_NE(audio_provider_, nullptr);
+
+ std::vector<CodecSpecificConfig> codec_specifics;
+ for (auto codec_type : a2dp_codec_types) {
+ switch (codec_type) {
+ case CodecType::SBC:
+ codec_specifics = GetSbcCodecSpecificSupportedList(false);
+ break;
+ case CodecType::AAC:
+ codec_specifics = GetAacCodecSpecificSupportedList(false);
+ break;
+ case CodecType::LDAC:
+ codec_specifics = GetLdacCodecSpecificSupportedList(false);
+ break;
+ case CodecType::APTX:
+ codec_specifics = GetAptxCodecSpecificSupportedList(false, false);
+ break;
+ case CodecType::APTX_HD:
+ codec_specifics = GetAptxCodecSpecificSupportedList(true, false);
+ break;
+ case CodecType::UNKNOWN:
+ codec_specifics.clear();
+ break;
+ }
+ if (codec_specifics.empty()) {
+ continue;
+ }
+
+ CodecConfiguration codec_config = {};
+ codec_config.codecType = codec_type;
+ codec_config.encodedAudioBitrate = 328000;
+ codec_config.peerMtu = 1005;
+ codec_config.isScmstEnabled = false;
+ AudioConfiguration audio_config = {};
+ auto hidl_cb = [](BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ };
+ for (auto codec_specific : codec_specifics) {
+ codec_config.config = codec_specific;
+ audio_config.codecConfig(codec_config);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ }
+ }
+}
+
+/**
+ * openProvider HEARING_AID_SOFTWARE_ENCODING_DATAPATH
+ */
+class BluetoothAudioProviderHearingAidSoftwareHidlTest
+ : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+ virtual void SetUp() override {
+ BluetoothAudioProvidersFactoryHidlTest::SetUp();
+ GetProviderCapabilitiesHelper(
+ SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
+ OpenProviderHelper(SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
+ ASSERT_NE(audio_provider_, nullptr);
+ }
+
+ virtual void TearDown() override {
+ audio_port_ = nullptr;
+ audio_provider_ = nullptr;
+ BluetoothAudioProvidersFactoryHidlTest::TearDown();
+ }
+
+ static constexpr SampleRate hearing_aid_sample_rates_[3] = {
+ SampleRate::RATE_UNKNOWN, SampleRate::RATE_16000, SampleRate::RATE_24000};
+ static constexpr BitsPerSample hearing_aid_bits_per_samples_[3] = {
+ BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16,
+ BitsPerSample::BITS_24};
+ static constexpr ChannelMode hearing_aid_channel_modes_[3] = {
+ ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+};
+
+/**
+ * Test whether each provider of type
+ * SessionType::HEARING_AID_HARDWARE_ENCODING_DATAPATH can be started and
+ * stopped with SBC hardware encoding config
+ */
+TEST_F(BluetoothAudioProviderHearingAidSoftwareHidlTest,
+ OpenHearingAidSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH can be started and
+ * stopped with different PCM config
+ */
+TEST_F(BluetoothAudioProviderHearingAidSoftwareHidlTest,
+ StartAndEndHearingAidSessionWithPossiblePcmConfig) {
+ bool is_codec_config_valid;
+ std::unique_ptr<DataMQ> tempDataMQ;
+ auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+ BluetoothAudioStatus status,
+ const DataMQ::Descriptor& dataMQ) {
+ if (is_codec_config_valid) {
+ ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+ ASSERT_TRUE(dataMQ.isHandleValid());
+ tempDataMQ.reset(new DataMQ(dataMQ));
+ } else {
+ EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+ EXPECT_FALSE(dataMQ.isHandleValid());
+ }
+ };
+ AudioConfiguration audio_config = {};
+ PcmParameters pcm_parameters = {};
+ for (auto sample_rate : hearing_aid_sample_rates_) {
+ pcm_parameters.sampleRate = sample_rate;
+ for (auto bits_per_sample : hearing_aid_bits_per_samples_) {
+ pcm_parameters.bitsPerSample = bits_per_sample;
+ for (auto channel_mode : hearing_aid_channel_modes_) {
+ pcm_parameters.channelMode = channel_mode;
+ is_codec_config_valid = IsPcmParametersSupported(pcm_parameters);
+ audio_config.pcmConfig(pcm_parameters);
+ auto hidl_retval =
+ audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+ // HIDL calls should not be failed and callback has to be executed
+ ASSERT_TRUE(hidl_retval.isOk());
+ if (is_codec_config_valid) {
+ EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+ }
+ EXPECT_TRUE(audio_provider_->endSession().isOk());
+ } // ChannelMode
+ } // BitsPerSampple
+ } // SampleRate
+}
+
+int main(int argc, char** argv) {
+ ::testing::AddGlobalTestEnvironment(
+ BluetoothAudioHidlEnvironment::Instance());
+ ::testing::InitGoogleTest(&argc, argv);
+ BluetoothAudioHidlEnvironment::Instance()->init(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/camera/common/1.0/default/Exif.cpp b/camera/common/1.0/default/Exif.cpp
index 6054999..4de05c5 100644
--- a/camera/common/1.0/default/Exif.cpp
+++ b/camera/common/1.0/default/Exif.cpp
@@ -18,7 +18,7 @@
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0
-#include <cutils/log.h>
+#include <android/log.h>
#include <inttypes.h>
#include <math.h>
diff --git a/camera/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/metadata/3.4/types.hal b/camera/metadata/3.4/types.hal
index 4caf3e3..bb630f1 100644
--- a/camera/metadata/3.4/types.hal
+++ b/camera/metadata/3.4/types.hal
@@ -179,6 +179,14 @@
* Enumeration definitions for the various entries that need them
*/
+/** android.request.availableCapabilities enumeration values added since v3.3
+ * @see ANDROID_REQUEST_AVAILABLE_CAPABILITIES
+ */
+enum CameraMetadataEnumAndroidRequestAvailableCapabilities :
+ @3.3::CameraMetadataEnumAndroidRequestAvailableCapabilities {
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA,
+};
+
/** android.scaler.availableFormats enumeration values added since v3.2
* @see ANDROID_SCALER_AVAILABLE_FORMATS
*/
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/cas/1.1/Android.bp b/cas/1.1/Android.bp
new file mode 100644
index 0000000..bb0edb9
--- /dev/null
+++ b/cas/1.1/Android.bp
@@ -0,0 +1,20 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.cas@1.1",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "ICas.hal",
+ "ICasListener.hal",
+ "IMediaCasService.hal",
+ ],
+ interfaces: [
+ "android.hardware.cas@1.0",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
+
diff --git a/cas/1.1/ICas.hal b/cas/1.1/ICas.hal
new file mode 100644
index 0000000..027968e
--- /dev/null
+++ b/cas/1.1/ICas.hal
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.cas@1.1;
+
+import @1.0::HidlCasSessionId;
+import @1.0::ICas;
+import @1.0::Status;
+
+/**
+ * ICas is the API to control the cas system and is accessible from both
+ * Java and native level. It is used to manage sessions, provision/refresh
+ * the cas system, and process the EMM/ECM messages. It also allows bi-directional,
+ * scheme-specific communications between the client and the cas system.
+ */
+
+interface ICas extends @1.0::ICas {
+ /**
+ * Send an scheme-specific session event to the CasPlugin.
+ *
+ * @param sessionId the id of an opened session.
+ * @param event an integer denoting a scheme-specific event to be sent.
+ * @param arg a scheme-specific integer argument for the event.
+ * @param data a byte array containing scheme-specific data for the event.
+ * @return status the status of the call.
+ */
+ sendSessionEvent(HidlCasSessionId sessionId, int32_t event, int32_t arg,
+ vec<uint8_t> eventData)
+ generates (Status status);
+};
diff --git a/cas/1.1/ICasListener.hal b/cas/1.1/ICasListener.hal
new file mode 100644
index 0000000..5ec1154
--- /dev/null
+++ b/cas/1.1/ICasListener.hal
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.cas@1.1;
+
+import @1.0::ICasListener;
+import @1.0::HidlCasSessionId;
+
+interface ICasListener extends @1.0::ICasListener{
+ /**
+ * Notify the listener of a scheme-specific session event from CA system.
+ *
+ * @param sessionId the id of an opened session.
+ * @param event an integer whose meaning is scheme-specific.
+ * @param arg an integer whose meaning is scheme-specific.
+ * @param data a byte array of data whose format and meaning are
+ * scheme-specific.
+ */
+ onSessionEvent(HidlCasSessionId sessionId, int32_t event, int32_t arg,
+ vec<uint8_t> data);
+};
diff --git a/cas/1.1/IMediaCasService.hal b/cas/1.1/IMediaCasService.hal
new file mode 100644
index 0000000..e82b54c
--- /dev/null
+++ b/cas/1.1/IMediaCasService.hal
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.cas@1.1;
+
+import @1.1::ICas;
+import @1.1::ICasListener;
+import @1.0::IMediaCasService;
+
+/**
+ * IMediaCasService is the main entry point for interacting with a vendor's
+ * cas HAL to create cas and descrambler plugin instances. A cas plugin instance
+ * opens cas sessions which are used to obtain keys for a descrambler session,
+ * which can in turn be used to descramble protected video content.
+ */
+
+interface IMediaCasService extends @1.0::IMediaCasService {
+ /**
+ * Construct a new instance of a @1.1 ICAS CasPlugin given a CA_system_id.
+ *
+ * @param caSystemId the id of the CA system.
+ * @param listener the event listener to receive events coming from the CasPlugin.
+ * @return cas the newly created CasPlugin interface.
+ */
+ createPluginExt(int32_t caSystemId, ICasListener listener) generates (ICas cas);
+};
diff --git a/cas/1.1/default/Android.bp b/cas/1.1/default/Android.bp
new file mode 100644
index 0000000..68a49cf
--- /dev/null
+++ b/cas/1.1/default/Android.bp
@@ -0,0 +1,49 @@
+cc_defaults {
+ name: "cas_service_defaults@1.1",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "CasImpl.cpp",
+ "DescramblerImpl.cpp",
+ "MediaCasService.cpp",
+ "service.cpp",
+ "SharedLibrary.cpp",
+ "TypeConvert.cpp",
+ ],
+
+ compile_multilib: "32",
+
+ shared_libs: [
+ "android.hardware.cas@1.0",
+ "android.hardware.cas@1.1",
+ "android.hardware.cas.native@1.0",
+ "android.hidl.memory@1.0",
+ "libbinder",
+ "libhidlbase",
+ "libhidlmemory",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ ],
+ header_libs: [
+ "libstagefright_foundation_headers",
+ "media_plugin_headers",
+ ],
+}
+
+cc_binary {
+ name: "android.hardware.cas@1.1-service",
+ vintf_fragments: ["android.hardware.cas@1.1-service.xml"],
+ defaults: ["cas_service_defaults@1.1"],
+ init_rc: ["android.hardware.cas@1.1-service.rc"],
+}
+
+cc_binary {
+ name: "android.hardware.cas@1.1-service-lazy",
+ vintf_fragments: ["android.hardware.cas@1.1-service-lazy.xml"],
+ overrides: ["android.hardware.cas@1.1-service"],
+ defaults: ["cas_service_defaults@1.1"],
+ init_rc: ["android.hardware.cas@1.1-service-lazy.rc"],
+ cflags: ["-DLAZY_SERVICE"],
+}
diff --git a/cas/1.1/default/CasImpl.cpp b/cas/1.1/default/CasImpl.cpp
new file mode 100644
index 0000000..4cc6017
--- /dev/null
+++ b/cas/1.1/default/CasImpl.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.1-CasImpl"
+
+#include <android/hardware/cas/1.1/ICasListener.h>
+#include <media/cas/CasAPI.h>
+#include <utils/Log.h>
+
+#include "CasImpl.h"
+#include "SharedLibrary.h"
+#include "TypeConvert.h"
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+CasImpl::CasImpl(const sp<ICasListener>& listener) : mListener(listener) {
+ ALOGV("CTOR");
+}
+
+CasImpl::~CasImpl() {
+ ALOGV("DTOR");
+ release();
+}
+
+// static
+void CasImpl::OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size) {
+ if (appData == NULL) {
+ ALOGE("Invalid appData!");
+ return;
+ }
+ CasImpl* casImpl = static_cast<CasImpl*>(appData);
+ casImpl->onEvent(event, arg, data, size);
+}
+
+// static
+void CasImpl::CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size,
+ const CasSessionId* sessionId) {
+ if (appData == NULL) {
+ ALOGE("Invalid appData!");
+ return;
+ }
+ CasImpl* casImpl = static_cast<CasImpl*>(appData);
+ casImpl->onEvent(sessionId, event, arg, data, size);
+}
+
+void CasImpl::init(const sp<SharedLibrary>& library, CasPlugin* plugin) {
+ mLibrary = library;
+ std::shared_ptr<CasPlugin> holder(plugin);
+ std::atomic_store(&mPluginHolder, holder);
+}
+
+void CasImpl::onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size) {
+ if (mListener == NULL) {
+ return;
+ }
+
+ HidlCasData eventData;
+ if (data != NULL) {
+ eventData.setToExternal(data, size);
+ }
+
+ mListener->onEvent(event, arg, eventData);
+}
+
+void CasImpl::onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data,
+ size_t size) {
+ if (mListener == NULL) {
+ return;
+ }
+
+ HidlCasData eventData;
+ if (data != NULL) {
+ eventData.setToExternal(data, size);
+ }
+
+ if (sessionId != NULL) {
+ mListener->onSessionEvent(*sessionId, event, arg, eventData);
+ } else {
+ mListener->onEvent(event, arg, eventData);
+ }
+}
+
+Return<Status> CasImpl::setPrivateData(const HidlCasData& pvtData) {
+ ALOGV("%s", __FUNCTION__);
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+ return toStatus(holder->setPrivateData(pvtData));
+}
+
+Return<void> CasImpl::openSession(openSession_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+ CasSessionId sessionId;
+
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ status_t err = INVALID_OPERATION;
+ if (holder.get() != nullptr) {
+ err = holder->openSession(&sessionId);
+ holder.reset();
+ }
+
+ _hidl_cb(toStatus(err), sessionId);
+
+ return Void();
+}
+
+Return<Status> CasImpl::setSessionPrivateData(const HidlCasSessionId& sessionId,
+ const HidlCasData& pvtData) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+ return toStatus(holder->setSessionPrivateData(sessionId, pvtData));
+}
+
+Return<Status> CasImpl::closeSession(const HidlCasSessionId& sessionId) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+ return toStatus(holder->closeSession(sessionId));
+}
+
+Return<Status> CasImpl::processEcm(const HidlCasSessionId& sessionId, const HidlCasData& ecm) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ return toStatus(holder->processEcm(sessionId, ecm));
+}
+
+Return<Status> CasImpl::processEmm(const HidlCasData& emm) {
+ ALOGV("%s", __FUNCTION__);
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ return toStatus(holder->processEmm(emm));
+}
+
+Return<Status> CasImpl::sendEvent(int32_t event, int32_t arg, const HidlCasData& eventData) {
+ ALOGV("%s", __FUNCTION__);
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ status_t err = holder->sendEvent(event, arg, eventData);
+ return toStatus(err);
+}
+
+Return<Status> CasImpl::sendSessionEvent(const HidlCasSessionId& sessionId, int32_t event,
+ int32_t arg, const HidlCasData& eventData) {
+ ALOGV("%s", __FUNCTION__);
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ status_t err = holder->sendSessionEvent(sessionId, event, arg, eventData);
+ return toStatus(err);
+}
+
+Return<Status> CasImpl::provision(const hidl_string& provisionString) {
+ ALOGV("%s: provisionString=%s", __FUNCTION__, provisionString.c_str());
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ return toStatus(holder->provision(String8(provisionString.c_str())));
+}
+
+Return<Status> CasImpl::refreshEntitlements(int32_t refreshType, const HidlCasData& refreshData) {
+ ALOGV("%s", __FUNCTION__);
+ std::shared_ptr<CasPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ status_t err = holder->refreshEntitlements(refreshType, refreshData);
+ return toStatus(err);
+}
+
+Return<Status> CasImpl::release() {
+ ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get());
+
+ std::shared_ptr<CasPlugin> holder(nullptr);
+ std::atomic_store(&mPluginHolder, holder);
+
+ return Status::OK;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.1/default/CasImpl.h b/cas/1.1/default/CasImpl.h
new file mode 100644
index 0000000..18aee9e
--- /dev/null
+++ b/cas/1.1/default/CasImpl.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_CAS_IMPL_H_
+#define ANDROID_HARDWARE_CAS_V1_1_CAS_IMPL_H_
+
+#include <android/hardware/cas/1.1/ICas.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+struct CasPlugin;
+
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+struct ICasListener;
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasData;
+using ::android::hardware::cas::V1_0::HidlCasSessionId;
+using ::android::hardware::cas::V1_0::Status;
+
+class SharedLibrary;
+
+class CasImpl : public ICas {
+ public:
+ CasImpl(const sp<ICasListener>& listener);
+ virtual ~CasImpl();
+
+ static void OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size);
+
+ static void CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size,
+ const CasSessionId* sessionId);
+
+ void init(const sp<SharedLibrary>& library, CasPlugin* plugin);
+ void onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size);
+
+ void onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data,
+ size_t size);
+
+ // ICas inherits
+
+ virtual Return<Status> setPrivateData(const HidlCasData& pvtData) override;
+
+ virtual Return<void> openSession(openSession_cb _hidl_cb) override;
+
+ virtual Return<Status> closeSession(const HidlCasSessionId& sessionId) override;
+
+ virtual Return<Status> setSessionPrivateData(const HidlCasSessionId& sessionId,
+ const HidlCasData& pvtData) override;
+
+ virtual Return<Status> processEcm(const HidlCasSessionId& sessionId,
+ const HidlCasData& ecm) override;
+
+ virtual Return<Status> processEmm(const HidlCasData& emm) override;
+
+ virtual Return<Status> sendEvent(int32_t event, int32_t arg,
+ const HidlCasData& eventData) override;
+
+ virtual Return<Status> sendSessionEvent(const HidlCasSessionId& sessionId, int32_t event,
+ int32_t arg, const HidlCasData& eventData) override;
+
+ virtual Return<Status> provision(const hidl_string& provisionString) override;
+
+ virtual Return<Status> refreshEntitlements(int32_t refreshType,
+ const HidlCasData& refreshData) override;
+
+ virtual Return<Status> release() override;
+
+ private:
+ struct PluginHolder;
+ sp<SharedLibrary> mLibrary;
+ std::shared_ptr<CasPlugin> mPluginHolder;
+ sp<ICasListener> mListener;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CasImpl);
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_CAS_IMPL_H_
diff --git a/cas/1.1/default/DescramblerImpl.cpp b/cas/1.1/default/DescramblerImpl.cpp
new file mode 100644
index 0000000..36dc1a5
--- /dev/null
+++ b/cas/1.1/default/DescramblerImpl.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.1-DescramblerImpl"
+
+#include <hidlmemory/mapping.h>
+#include <media/cas/DescramblerAPI.h>
+#include <media/hardware/CryptoAPI.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <utils/Log.h>
+
+#include "DescramblerImpl.h"
+#include "SharedLibrary.h"
+#include "TypeConvert.h"
+
+namespace android {
+using hidl::memory::V1_0::IMemory;
+
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+#define CHECK_SUBSAMPLE_DEF(type) \
+ static_assert(sizeof(SubSample) == sizeof(type::SubSample), "SubSample: size doesn't match"); \
+ static_assert(offsetof(SubSample, numBytesOfClearData) == \
+ offsetof(type::SubSample, mNumBytesOfClearData), \
+ "SubSample: numBytesOfClearData offset doesn't match"); \
+ static_assert(offsetof(SubSample, numBytesOfEncryptedData) == \
+ offsetof(type::SubSample, mNumBytesOfEncryptedData), \
+ "SubSample: numBytesOfEncryptedData offset doesn't match")
+
+CHECK_SUBSAMPLE_DEF(DescramblerPlugin);
+CHECK_SUBSAMPLE_DEF(CryptoPlugin);
+
+DescramblerImpl::DescramblerImpl(const sp<SharedLibrary>& library, DescramblerPlugin* plugin)
+ : mLibrary(library), mPluginHolder(plugin) {
+ ALOGV("CTOR: plugin=%p", mPluginHolder.get());
+}
+
+DescramblerImpl::~DescramblerImpl() {
+ ALOGV("DTOR: plugin=%p", mPluginHolder.get());
+ release();
+}
+
+Return<Status> DescramblerImpl::setMediaCasSession(const HidlCasSessionId& sessionId) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string());
+
+ std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ return toStatus(holder->setMediaCasSession(sessionId));
+}
+
+Return<bool> DescramblerImpl::requiresSecureDecoderComponent(const hidl_string& mime) {
+ std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ return false;
+ }
+
+ return holder->requiresSecureDecoderComponent(String8(mime.c_str()));
+}
+
+static inline bool validateRangeForSize(uint64_t offset, uint64_t length, uint64_t size) {
+ return isInRange<uint64_t, uint64_t>(0, size, offset, length);
+}
+
+Return<void> DescramblerImpl::descramble(ScramblingControl scramblingControl,
+ const hidl_vec<SubSample>& subSamples,
+ const SharedBuffer& srcBuffer, uint64_t srcOffset,
+ const DestinationBuffer& dstBuffer, uint64_t dstOffset,
+ descramble_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ // hidl_memory's size is stored in uint64_t, but mapMemory's mmap will map
+ // size in size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed
+ // but the mapped memory's actual size will be smaller than the reported size.
+ if (srcBuffer.heapBase.size() > SIZE_MAX) {
+ ALOGE("Invalid hidl_memory size: %llu", srcBuffer.heapBase.size());
+ android_errorWriteLog(0x534e4554, "79376389");
+ _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+ return Void();
+ }
+
+ sp<IMemory> srcMem = mapMemory(srcBuffer.heapBase);
+
+ // Validate if the offset and size in the SharedBuffer is consistent with the
+ // mapped ashmem, since the offset and size is controlled by client.
+ if (srcMem == NULL) {
+ ALOGE("Failed to map src buffer.");
+ _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+ return Void();
+ }
+ if (!validateRangeForSize(srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize())) {
+ ALOGE("Invalid src buffer range: offset %llu, size %llu, srcMem size %llu",
+ srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
+ android_errorWriteLog(0x534e4554, "67962232");
+ _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+ return Void();
+ }
+
+ // use 64-bit here to catch bad subsample size that might be overflowing.
+ uint64_t totalBytesInSubSamples = 0;
+ for (size_t i = 0; i < subSamples.size(); i++) {
+ totalBytesInSubSamples +=
+ (uint64_t)subSamples[i].numBytesOfClearData + subSamples[i].numBytesOfEncryptedData;
+ }
+ // Further validate if the specified srcOffset and requested total subsample size
+ // is consistent with the source shared buffer size.
+ if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) {
+ ALOGE("Invalid srcOffset and subsample size: "
+ "srcOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
+ srcOffset, totalBytesInSubSamples, srcBuffer.size);
+ android_errorWriteLog(0x534e4554, "67962232");
+ _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+ return Void();
+ }
+
+ void* srcPtr = (uint8_t*)(void*)srcMem->getPointer() + srcBuffer.offset;
+ void* dstPtr = NULL;
+ if (dstBuffer.type == BufferType::SHARED_MEMORY) {
+ // When using shared memory, src buffer is also used as dst,
+ // we don't map it again here.
+ dstPtr = srcPtr;
+
+ // In this case the dst and src would be the same buffer, need to validate
+ // dstOffset against the buffer size too.
+ if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) {
+ ALOGE("Invalid dstOffset and subsample size: "
+ "dstOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
+ dstOffset, totalBytesInSubSamples, srcBuffer.size);
+ android_errorWriteLog(0x534e4554, "67962232");
+ _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
+ return Void();
+ }
+ } else {
+ native_handle_t* handle =
+ const_cast<native_handle_t*>(dstBuffer.secureMemory.getNativeHandle());
+ dstPtr = static_cast<void*>(handle);
+ }
+
+ // Get a local copy of the shared_ptr for the plugin. Note that before
+ // calling the HIDL callback, this shared_ptr must be manually reset,
+ // since the client side could proceed as soon as the callback is called
+ // without waiting for this method to go out of scope.
+ std::shared_ptr<DescramblerPlugin> holder = std::atomic_load(&mPluginHolder);
+ if (holder.get() == nullptr) {
+ _hidl_cb(toStatus(INVALID_OPERATION), 0, NULL);
+ return Void();
+ }
+
+ // Casting hidl SubSample to DescramblerPlugin::SubSample, but need
+ // to ensure structs are actually idential
+
+ int32_t result =
+ holder->descramble(dstBuffer.type != BufferType::SHARED_MEMORY,
+ (DescramblerPlugin::ScramblingControl)scramblingControl,
+ subSamples.size(), (DescramblerPlugin::SubSample*)subSamples.data(),
+ srcPtr, srcOffset, dstPtr, dstOffset, NULL);
+
+ holder.reset();
+ _hidl_cb(toStatus(result >= 0 ? OK : result), result, NULL);
+ return Void();
+}
+
+Return<Status> DescramblerImpl::release() {
+ ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get());
+
+ std::shared_ptr<DescramblerPlugin> holder(nullptr);
+ std::atomic_store(&mPluginHolder, holder);
+
+ return Status::OK;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.1/default/DescramblerImpl.h b/cas/1.1/default/DescramblerImpl.h
new file mode 100644
index 0000000..a1f66ae
--- /dev/null
+++ b/cas/1.1/default/DescramblerImpl.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_DESCRAMBLER_IMPL_H_
+#define ANDROID_HARDWARE_CAS_V1_1_DESCRAMBLER_IMPL_H_
+
+#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+struct DescramblerPlugin;
+using namespace hardware::cas::native::V1_0;
+
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasSessionId;
+using ::android::hardware::cas::V1_0::Status;
+
+class SharedLibrary;
+
+class DescramblerImpl : public IDescrambler {
+ public:
+ DescramblerImpl(const sp<SharedLibrary>& library, DescramblerPlugin* plugin);
+ virtual ~DescramblerImpl();
+
+ virtual Return<Status> setMediaCasSession(const HidlCasSessionId& sessionId) override;
+
+ virtual Return<bool> requiresSecureDecoderComponent(const hidl_string& mime) override;
+
+ virtual Return<void> descramble(ScramblingControl scramblingControl,
+ const hidl_vec<SubSample>& subSamples,
+ const SharedBuffer& srcBuffer, uint64_t srcOffset,
+ const DestinationBuffer& dstBuffer, uint64_t dstOffset,
+ descramble_cb _hidl_cb) override;
+
+ virtual Return<Status> release() override;
+
+ private:
+ sp<SharedLibrary> mLibrary;
+ std::shared_ptr<DescramblerPlugin> mPluginHolder;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DescramblerImpl);
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_DESCRAMBLER_IMPL_H_
diff --git a/cas/1.1/default/FactoryLoader.h b/cas/1.1/default/FactoryLoader.h
new file mode 100644
index 0000000..c4a48e2
--- /dev/null
+++ b/cas/1.1/default/FactoryLoader.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_FACTORY_LOADER_H_
+#define ANDROID_HARDWARE_CAS_V1_1_FACTORY_LOADER_H_
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <media/cas/CasAPI.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include "SharedLibrary.h"
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasPluginDescriptor;
+
+template <class T>
+class FactoryLoader {
+ public:
+ FactoryLoader(const char* name) : mFactory(NULL), mCreateFactoryFuncName(name) {}
+
+ virtual ~FactoryLoader() { closeFactory(); }
+
+ bool findFactoryForScheme(int32_t CA_system_id, sp<SharedLibrary>* library = NULL,
+ T** factory = NULL);
+
+ bool enumeratePlugins(vector<HidlCasPluginDescriptor>* results);
+
+ private:
+ typedef T* (*CreateFactoryFunc)();
+
+ Mutex mMapLock;
+ T* mFactory;
+ const char* mCreateFactoryFuncName;
+ sp<SharedLibrary> mLibrary;
+ KeyedVector<int32_t, String8> mCASystemIdToLibraryPathMap;
+ KeyedVector<String8, wp<SharedLibrary> > mLibraryPathToOpenLibraryMap;
+
+ bool loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
+ sp<SharedLibrary>* library, T** factory);
+
+ bool queryPluginsFromPath(const String8& path, vector<HidlCasPluginDescriptor>* results);
+
+ bool openFactory(const String8& path);
+ void closeFactory();
+};
+
+template <class T>
+bool FactoryLoader<T>::findFactoryForScheme(int32_t CA_system_id, sp<SharedLibrary>* library,
+ T** factory) {
+ if (library != NULL) {
+ library->clear();
+ }
+ if (factory != NULL) {
+ *factory = NULL;
+ }
+
+ Mutex::Autolock autoLock(mMapLock);
+
+ // first check cache
+ ssize_t index = mCASystemIdToLibraryPathMap.indexOfKey(CA_system_id);
+ if (index >= 0) {
+ return loadFactoryForSchemeFromPath(mCASystemIdToLibraryPathMap[index], CA_system_id,
+ library, factory);
+ }
+
+ // no luck, have to search
+ String8 dirPath("/vendor/lib/mediacas");
+ DIR* pDir = opendir(dirPath.string());
+
+ if (pDir == NULL) {
+ ALOGE("Failed to open plugin directory %s", dirPath.string());
+ return false;
+ }
+
+ struct dirent* pEntry;
+ while ((pEntry = readdir(pDir))) {
+ String8 pluginPath = dirPath + "/" + pEntry->d_name;
+ if (pluginPath.getPathExtension() == ".so") {
+ if (loadFactoryForSchemeFromPath(pluginPath, CA_system_id, library, factory)) {
+ mCASystemIdToLibraryPathMap.add(CA_system_id, pluginPath);
+ closedir(pDir);
+
+ return true;
+ }
+ }
+ }
+
+ closedir(pDir);
+
+ ALOGE("Failed to find plugin");
+ return false;
+}
+
+template <class T>
+bool FactoryLoader<T>::enumeratePlugins(vector<HidlCasPluginDescriptor>* results) {
+ ALOGI("enumeratePlugins");
+
+ results->clear();
+
+ String8 dirPath("/vendor/lib/mediacas");
+ DIR* pDir = opendir(dirPath.string());
+
+ if (pDir == NULL) {
+ ALOGE("Failed to open plugin directory %s", dirPath.string());
+ return false;
+ }
+
+ Mutex::Autolock autoLock(mMapLock);
+
+ struct dirent* pEntry;
+ while ((pEntry = readdir(pDir))) {
+ String8 pluginPath = dirPath + "/" + pEntry->d_name;
+ if (pluginPath.getPathExtension() == ".so") {
+ queryPluginsFromPath(pluginPath, results);
+ }
+ }
+ return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
+ sp<SharedLibrary>* library, T** factory) {
+ closeFactory();
+
+ if (!openFactory(path) || !mFactory->isSystemIdSupported(CA_system_id)) {
+ closeFactory();
+ return false;
+ }
+
+ if (library != NULL) {
+ *library = mLibrary;
+ }
+ if (factory != NULL) {
+ *factory = mFactory;
+ }
+ return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::queryPluginsFromPath(const String8& path,
+ vector<HidlCasPluginDescriptor>* results) {
+ closeFactory();
+
+ vector<CasPluginDescriptor> descriptors;
+ if (!openFactory(path) || mFactory->queryPlugins(&descriptors) != OK) {
+ closeFactory();
+ return false;
+ }
+
+ for (auto it = descriptors.begin(); it != descriptors.end(); it++) {
+ results->push_back(
+ HidlCasPluginDescriptor{.caSystemId = it->CA_system_id, .name = it->name.c_str()});
+ }
+ return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::openFactory(const String8& path) {
+ // get strong pointer to open shared library
+ ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path);
+ if (index >= 0) {
+ mLibrary = mLibraryPathToOpenLibraryMap[index].promote();
+ } else {
+ index = mLibraryPathToOpenLibraryMap.add(path, NULL);
+ }
+
+ if (!mLibrary.get()) {
+ mLibrary = new SharedLibrary(path);
+ if (!*mLibrary) {
+ return false;
+ }
+
+ mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary);
+ }
+
+ CreateFactoryFunc createFactory = (CreateFactoryFunc)mLibrary->lookup(mCreateFactoryFuncName);
+ if (createFactory == NULL || (mFactory = createFactory()) == NULL) {
+ return false;
+ }
+ return true;
+}
+
+template <class T>
+void FactoryLoader<T>::closeFactory() {
+ delete mFactory;
+ mFactory = NULL;
+ mLibrary.clear();
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_FACTORY_LOADER_H_
diff --git a/cas/1.1/default/MediaCasService.cpp b/cas/1.1/default/MediaCasService.cpp
new file mode 100644
index 0000000..e2d3357
--- /dev/null
+++ b/cas/1.1/default/MediaCasService.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.1-MediaCasService"
+
+#include <android/hardware/cas/1.1/ICasListener.h>
+#include <media/cas/CasAPI.h>
+#include <media/cas/DescramblerAPI.h>
+#include <utils/Log.h>
+
+#include "CasImpl.h"
+#include "DescramblerImpl.h"
+#include "MediaCasService.h"
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+MediaCasService::MediaCasService()
+ : mCasLoader("createCasFactory"), mDescramblerLoader("createDescramblerFactory") {}
+
+MediaCasService::~MediaCasService() {}
+
+Return<void> MediaCasService::enumeratePlugins(enumeratePlugins_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ vector<HidlCasPluginDescriptor> results;
+ mCasLoader.enumeratePlugins(&results);
+
+ _hidl_cb(results);
+ return Void();
+}
+
+Return<bool> MediaCasService::isSystemIdSupported(int32_t CA_system_id) {
+ ALOGV("isSystemIdSupported: CA_system_id=%d", CA_system_id);
+
+ return mCasLoader.findFactoryForScheme(CA_system_id);
+}
+
+Return<sp<V1_0::ICas>> MediaCasService::createPlugin(int32_t /* CA_system_id */,
+ const sp<V1_0::ICasListener>& /* listener */) {
+ ALOGE("%s:Use createPluginExt to create plugin with cas@1.1", __FUNCTION__);
+
+ sp<V1_0::ICas> result;
+ /*
+ CasFactory *factory;
+ sp<SharedLibrary> library;
+ if (mCasLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
+ CasPlugin *plugin = NULL;
+ sp<CasImpl> casImpl = new CasImpl(listener);
+ if (factory->createPlugin(CA_system_id, casImpl.get(),
+ CasImpl::OnEvent, &plugin) == OK && plugin != NULL) {
+ casImpl->init(library, plugin);
+ result = casImpl;
+ }
+ }
+ */
+ return result;
+}
+
+Return<sp<ICas>> MediaCasService::createPluginExt(int32_t CA_system_id,
+ const sp<ICasListener>& listener) {
+ ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+ sp<ICas> result;
+
+ CasFactory* factory;
+ sp<SharedLibrary> library;
+ if (mCasLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
+ CasPlugin* plugin = NULL;
+ sp<CasImpl> casImpl = new CasImpl(listener);
+ if (factory->createPlugin(CA_system_id, casImpl.get(), &CasImpl::CallBackExt, &plugin) ==
+ OK &&
+ plugin != NULL) {
+ casImpl->init(library, plugin);
+ result = casImpl;
+ }
+ }
+
+ return result;
+}
+
+Return<bool> MediaCasService::isDescramblerSupported(int32_t CA_system_id) {
+ ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+ return mDescramblerLoader.findFactoryForScheme(CA_system_id);
+}
+
+Return<sp<IDescramblerBase>> MediaCasService::createDescrambler(int32_t CA_system_id) {
+ ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+ sp<IDescrambler> result;
+
+ DescramblerFactory* factory;
+ sp<SharedLibrary> library;
+ if (mDescramblerLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
+ DescramblerPlugin* plugin = NULL;
+ if (factory->createPlugin(CA_system_id, &plugin) == OK && plugin != NULL) {
+ result = new DescramblerImpl(library, plugin);
+ }
+ }
+
+ return result;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.1/default/MediaCasService.h b/cas/1.1/default/MediaCasService.h
new file mode 100644
index 0000000..ec5a86d
--- /dev/null
+++ b/cas/1.1/default/MediaCasService.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_MEDIA_CAS_SERVICE_H_
+#define ANDROID_HARDWARE_CAS_V1_1_MEDIA_CAS_SERVICE_H_
+
+#include <android/hardware/cas/1.1/IMediaCasService.h>
+
+#include "FactoryLoader.h"
+
+namespace android {
+struct CasFactory;
+struct DescramblerFactory;
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::HidlCasPluginDescriptor;
+using ::android::hardware::cas::V1_0::IDescramblerBase;
+
+class MediaCasService : public IMediaCasService {
+ public:
+ MediaCasService();
+
+ virtual Return<void> enumeratePlugins(enumeratePlugins_cb _hidl_cb) override;
+
+ virtual Return<bool> isSystemIdSupported(int32_t CA_system_id) override;
+
+ virtual Return<sp<V1_0::ICas>> createPlugin(int32_t CA_system_id,
+ const sp<V1_0::ICasListener>& listener) override;
+
+ virtual Return<sp<ICas>> createPluginExt(int32_t CA_system_id,
+ const sp<ICasListener>& listener) override;
+
+ virtual Return<bool> isDescramblerSupported(int32_t CA_system_id) override;
+
+ virtual Return<sp<IDescramblerBase>> createDescrambler(int32_t CA_system_id) override;
+
+ private:
+ FactoryLoader<CasFactory> mCasLoader;
+ FactoryLoader<DescramblerFactory> mDescramblerLoader;
+
+ virtual ~MediaCasService();
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_MEDIA_CAS_SERVICE_H_
diff --git a/cas/1.1/default/SharedLibrary.cpp b/cas/1.1/default/SharedLibrary.cpp
new file mode 100644
index 0000000..ffe4bb9
--- /dev/null
+++ b/cas/1.1/default/SharedLibrary.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.1-SharedLibrary"
+
+#include "SharedLibrary.h"
+#include <dlfcn.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+SharedLibrary::SharedLibrary(const String8& path) {
+ mLibHandle = dlopen(path.string(), RTLD_NOW);
+}
+
+SharedLibrary::~SharedLibrary() {
+ if (mLibHandle != NULL) {
+ dlclose(mLibHandle);
+ mLibHandle = NULL;
+ }
+}
+
+bool SharedLibrary::operator!() const {
+ return mLibHandle == NULL;
+}
+
+void* SharedLibrary::lookup(const char* symbol) const {
+ if (!mLibHandle) {
+ return NULL;
+ }
+ // Clear last error before we load the symbol again,
+ // in case the caller didn't retrieve it.
+ (void)dlerror();
+ return dlsym(mLibHandle, symbol);
+}
+
+const char* SharedLibrary::lastError() const {
+ const char* error = dlerror();
+ return error ? error : "No errors or unknown error";
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.1/default/SharedLibrary.h b/cas/1.1/default/SharedLibrary.h
new file mode 100644
index 0000000..f4d2ff6
--- /dev/null
+++ b/cas/1.1/default/SharedLibrary.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_SHARED_LIBRARY_H_
+#define ANDROID_HARDWARE_CAS_V1_1_SHARED_LIBRARY_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+class SharedLibrary : public RefBase {
+ public:
+ explicit SharedLibrary(const String8& path);
+ ~SharedLibrary();
+
+ bool operator!() const;
+ void* lookup(const char* symbol) const;
+ const char* lastError() const;
+
+ private:
+ void* mLibHandle;
+ DISALLOW_EVIL_CONSTRUCTORS(SharedLibrary);
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_SHARED_LIBRARY_H_
diff --git a/cas/1.1/default/TypeConvert.cpp b/cas/1.1/default/TypeConvert.cpp
new file mode 100644
index 0000000..09ef41a
--- /dev/null
+++ b/cas/1.1/default/TypeConvert.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.1-TypeConvert"
+
+#include "TypeConvert.h"
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+Status toStatus(status_t legacyStatus) {
+ Status status;
+ switch (legacyStatus) {
+ case android::OK:
+ status = Status::OK;
+ break;
+ case android::ERROR_CAS_NO_LICENSE:
+ status = Status::ERROR_CAS_NO_LICENSE;
+ break;
+ case android::ERROR_CAS_LICENSE_EXPIRED:
+ status = Status::ERROR_CAS_LICENSE_EXPIRED;
+ break;
+ case android::ERROR_CAS_SESSION_NOT_OPENED:
+ status = Status::ERROR_CAS_SESSION_NOT_OPENED;
+ break;
+ case android::ERROR_CAS_CANNOT_HANDLE:
+ status = Status::ERROR_CAS_CANNOT_HANDLE;
+ break;
+ case android::ERROR_CAS_TAMPER_DETECTED:
+ status = Status::ERROR_CAS_INVALID_STATE;
+ break;
+ case android::BAD_VALUE:
+ status = Status::BAD_VALUE;
+ break;
+ case android::ERROR_CAS_NOT_PROVISIONED:
+ status = Status::ERROR_CAS_NOT_PROVISIONED;
+ break;
+ case android::ERROR_CAS_RESOURCE_BUSY:
+ status = Status::ERROR_CAS_RESOURCE_BUSY;
+ break;
+ case android::ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION:
+ status = Status::ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION;
+ break;
+ case android::ERROR_CAS_DEVICE_REVOKED:
+ status = Status::ERROR_CAS_DEVICE_REVOKED;
+ break;
+ case android::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED:
+ status = Status::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED;
+ break;
+ case android::ERROR_CAS_DECRYPT:
+ status = Status::ERROR_CAS_DECRYPT;
+ break;
+ default:
+ ALOGW("Unable to convert legacy status: %d, defaulting to UNKNOWN", legacyStatus);
+ status = Status::ERROR_CAS_UNKNOWN;
+ break;
+ }
+ return status;
+}
+
+String8 sessionIdToString(const CasSessionId& sessionId) {
+ String8 result;
+ for (size_t i = 0; i < sessionId.size(); i++) {
+ result.appendFormat("%02x ", sessionId[i]);
+ }
+ if (result.isEmpty()) {
+ result.append("(null)");
+ }
+ return result;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.1/default/TypeConvert.h b/cas/1.1/default/TypeConvert.h
new file mode 100644
index 0000000..c4a0119
--- /dev/null
+++ b/cas/1.1/default/TypeConvert.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_1_TYPE_CONVERT_H
+#define ANDROID_HARDWARE_CAS_V1_1_TYPE_CONVERT_H
+
+#include <android/hardware/cas/1.0/types.h>
+#include <media/cas/CasAPI.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::cas::V1_0::Status;
+
+Status toStatus(status_t legacyStatus);
+
+String8 sessionIdToString(const CasSessionId& sessionId);
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_1_TYPE_CONVERT_H
diff --git a/cas/1.1/default/android.hardware.cas@1.1-service-lazy.rc b/cas/1.1/default/android.hardware.cas@1.1-service-lazy.rc
new file mode 100644
index 0000000..9227b6f
--- /dev/null
+++ b/cas/1.1/default/android.hardware.cas@1.1-service-lazy.rc
@@ -0,0 +1,9 @@
+service vendor.cas-hal-1-1 /vendor/bin/hw/android.hardware.cas@1.1-service-lazy
+ interface android.hardware.cas@1.1::IMediaCasService default
+ oneshot
+ disabled
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
diff --git a/cas/1.1/default/android.hardware.cas@1.1-service-lazy.xml b/cas/1.1/default/android.hardware.cas@1.1-service-lazy.xml
new file mode 100644
index 0000000..c9f13ba
--- /dev/null
+++ b/cas/1.1/default/android.hardware.cas@1.1-service-lazy.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.cas</name>
+ <transport>hwbinder</transport>
+ <version>1.1</version>
+ <interface>
+ <name>IMediaCasService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/cas/1.1/default/android.hardware.cas@1.1-service.rc b/cas/1.1/default/android.hardware.cas@1.1-service.rc
new file mode 100644
index 0000000..4081fe1
--- /dev/null
+++ b/cas/1.1/default/android.hardware.cas@1.1-service.rc
@@ -0,0 +1,6 @@
+service vendor.cas-hal-1-1 /vendor/bin/hw/android.hardware.cas@1.1-service
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
diff --git a/cas/1.1/default/android.hardware.cas@1.1-service.xml b/cas/1.1/default/android.hardware.cas@1.1-service.xml
new file mode 100644
index 0000000..c9f13ba
--- /dev/null
+++ b/cas/1.1/default/android.hardware.cas@1.1-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.cas</name>
+ <transport>hwbinder</transport>
+ <version>1.1</version>
+ <interface>
+ <name>IMediaCasService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/cas/1.1/default/service.cpp b/cas/1.1/default/service.cpp
new file mode 100644
index 0000000..9625303
--- /dev/null
+++ b/cas/1.1/default/service.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#ifdef LAZY_SERVICE
+#define LOG_TAG "android.hardware.cas@1.1-service-lazy"
+#else
+#define LOG_TAG "android.hardware.cas@1.1-service"
+#endif
+
+#include <binder/ProcessState.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+
+#include "MediaCasService.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::LazyServiceRegistrar;
+using android::hardware::cas::V1_1::IMediaCasService;
+using android::hardware::cas::V1_1::implementation::MediaCasService;
+
+#ifdef LAZY_SERVICE
+const bool kLazyService = true;
+#else
+const bool kLazyService = false;
+#endif
+
+int main() {
+ configureRpcThreadpool(8, true /* callerWillJoin */);
+
+ // Setup hwbinder service
+ android::sp<IMediaCasService> service = new MediaCasService();
+ android::status_t status;
+ if (kLazyService) {
+ auto serviceRegistrar = std::make_shared<LazyServiceRegistrar>();
+ status = serviceRegistrar->registerService(service);
+ } else {
+ status = service->registerAsService();
+ }
+ LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering cas service: %d", status);
+
+ joinRpcThreadpool();
+ return 0;
+}
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 94ffe80..0540ef1 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -1,5 +1,5 @@
<compatibility-matrix version="1.0" type="framework" level="4">
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
<name>android.hardware.atrace</name>
<version>1.0</version>
<interface>
@@ -121,7 +121,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.cas</name>
- <version>1.0</version>
+ <version>1.0-1</version>
<interface>
<name>IMediaCasService</name>
<instance>default</instance>
diff --git a/configstore/utils/ConfigStoreUtils.cpp b/configstore/utils/ConfigStoreUtils.cpp
index 5a1fb42..8cb61a9 100644
--- a/configstore/utils/ConfigStoreUtils.cpp
+++ b/configstore/utils/ConfigStoreUtils.cpp
@@ -23,12 +23,12 @@
namespace hardware {
namespace details {
-bool wouldLogInfo() {
- return WOULD_LOG(INFO);
+bool wouldLogVerbose() {
+ return WOULD_LOG(VERBOSE);
}
-void logAlwaysInfo(const std::string& message) {
- LOG(INFO) << message;
+void logAlwaysVerbose(const std::string& message) {
+ LOG(VERBOSE) << message;
}
void logAlwaysError(const std::string& message) {
diff --git a/configstore/utils/include/configstore/Utils.h b/configstore/utils/include/configstore/Utils.h
index e04f57d..d65f41f 100644
--- a/configstore/utils/include/configstore/Utils.h
+++ b/configstore/utils/include/configstore/Utils.h
@@ -29,8 +29,8 @@
namespace details {
// Templated classes can use the below method
// to avoid creating dependencies on liblog.
-bool wouldLogInfo();
-void logAlwaysInfo(const std::string& message);
+bool wouldLogVerbose();
+void logAlwaysVerbose(const std::string& message);
void logAlwaysError(const std::string& message);
} // namespace details
@@ -92,7 +92,7 @@
};
static V cachedValue = getHelper();
- if (wouldLogInfo()) {
+ if (wouldLogVerbose()) {
std::string iname = __PRETTY_FUNCTION__;
// func name starts with "func = " in __PRETTY_FUNCTION__
auto pos = iname.find("func = ");
@@ -107,7 +107,7 @@
oss << iname << " retrieved: "
<< (cachedValue.specified ? cachedValue.value : defValue)
<< (cachedValue.specified ? "" : " (default)");
- logAlwaysInfo(oss.str());
+ logAlwaysVerbose(oss.str());
}
return cachedValue.specified ? cachedValue.value : defValue;
diff --git a/current.txt b/current.txt
index 3b87306..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/drm/1.2/IDrmPlugin.hal b/drm/1.2/IDrmPlugin.hal
index 3c21815..7d266f4 100644
--- a/drm/1.2/IDrmPlugin.hal
+++ b/drm/1.2/IDrmPlugin.hal
@@ -74,7 +74,7 @@
* @return status the status of the call. Must be one of OK on
* success, BAD_VALUE if the license is not found or
* ERROR_DRM_INVALID_STATE if the HAL is in a state where the
- * KeySetIds can't be returned.
+ * KeySetIds can't be removed.
*/
removeOfflineLicense(KeySetId keySetId) generates (@1.0::Status status);
diff --git a/gnss/1.0/default/GnssBatching.cpp b/gnss/1.0/default/GnssBatching.cpp
index 02b38cb..f2e2e45 100644
--- a/gnss/1.0/default/GnssBatching.cpp
+++ b/gnss/1.0/default/GnssBatching.cpp
@@ -20,7 +20,7 @@
#include <Gnss.h> // for wakelock consolidation
#include <GnssUtils.h>
-#include <cutils/log.h> // for ALOGE
+#include <android/log.h> // for ALOGE
#include <vector>
namespace android {
diff --git a/gnss/1.0/default/ThreadCreationWrapper.h b/gnss/1.0/default/ThreadCreationWrapper.h
index df0a9e4..f401ce2 100644
--- a/gnss/1.0/default/ThreadCreationWrapper.h
+++ b/gnss/1.0/default/ThreadCreationWrapper.h
@@ -17,9 +17,9 @@
#ifndef ANDROID_HARDWARE_GNSS_THREADCREATIONWRAPPER_H
#define ANDROID_HARDWARE_GNSS_THREADCREATIONWRAPPER_H
+#include <log/log.h>
#include <pthread.h>
#include <vector>
-#include <cutils/log.h>
typedef void (*threadEntryFunc)(void* ret);
diff --git a/gnss/2.0/IGnssMeasurementCallback.hal b/gnss/2.0/IGnssMeasurementCallback.hal
index 226934e..fc44465 100644
--- a/gnss/2.0/IGnssMeasurementCallback.hal
+++ b/gnss/2.0/IGnssMeasurementCallback.hal
@@ -23,48 +23,48 @@
interface IGnssMeasurementCallback extends @1.1::IGnssMeasurementCallback {
/**
* Enumeration of available values for the GNSS Measurement's code type. Similar to the
- * Attribute field described in Rinex 3.03, e.g., in Tables 4-10, and Table A2 at the Rinex 3.03
+ * Attribute field described in RINEX 3.03, e.g., in Tables 4-10, and Table A2 at the RINEX 3.03
* Update 1 Document.
*/
enum GnssMeasurementCodeType : uint8_t {
/** GALILEO E1A, GALILEO E6A, IRNSS L5A, IRNSS SA. */
- CODE_TYPE_A = 0,
+ A = 0,
/** GALILEO E1B, GALILEO E6B, IRNSS L5B, IRNSS SB. */
- CODE_TYPE_B = 1,
+ B = 1,
/**
* GPS L1 C/A, GPS L2 C/A, GLONASS G1 C/A, GLONASS G2 C/A, GALILEO E1C, GALILEO E6C, SBAS
* L1 C/A, QZSS L1 C/A, IRNSS L5C.
*/
- CODE_TYPE_C = 2,
+ C = 2,
/**
* GPS L5 I, GLONASS G3 I, GALILEO E5a I, GALILEO E5b I, GALILEO E5a+b I, SBAS L5 I, QZSS L5
* I, BDS B1 I, BDS B2 I, BDS B3 I.
*/
- CODE_TYPE_I = 3,
+ I = 3,
/** GPS L1C (P), GPS L2C (L), QZSS L1C (P), QZSS L2C (L), LEX(6) L. */
- CODE_TYPE_L = 4,
+ L = 4,
/** GPS L1M, GPS L2M. */
- CODE_TYPE_M = 5,
+ M = 5,
/** GPS L1P, GPS L2P, GLONASS G1P, GLONASS G2P. */
- CODE_TYPE_P = 6,
+ P = 6,
/**
* GPS L5 Q, GLONASS G3 Q, GALILEO E5a Q, GALILEO E5b Q, GALILEO E5a+b Q, SBAS L5 Q, QZSS L5
* Q, BDS B1 Q, BDS B2 Q, BDS B3 Q.
*/
- CODE_TYPE_Q = 7,
+ Q = 7,
/** GPS L1C (D), GPS L2C (M), QZSS L1C (D), QZSS L2C (M), LEX(6) S. */
- CODE_TYPE_S = 8,
+ S = 8,
/** GPS L1 Z-tracking, GPS L2 Z-tracking. */
- CODE_TYPE_W = 9,
+ W = 9,
/**
* GPS L1C (D+P), GPS L2C (M+L), GPS L5 (I+Q), GLONASS G3 (I+Q), GALILEO E1 (B+C), GALILEO
@@ -72,16 +72,366 @@
* L1C (D+P), QZSS L2C (M+L), QZSS L5 (I+Q), LEX(6) (S+L), BDS B1 (I+Q), BDS B2 (I+Q), BDS
* B3 (I+Q), IRNSS L5 (B+C).
*/
- CODE_TYPE_X = 10,
+ X = 10,
/** GPS L1Y, GPS L2Y. */
- CODE_TYPE_Y = 11,
+ Y = 11,
/** GALILEO E1 (A+B+C), GALILEO E6 (A+B+C), QZSS L1-SAIF. */
- CODE_TYPE_Z = 12,
+ Z = 12,
/** GPS L1 codeless, GPS L2 codeless. */
- CODE_TYPE_CODELESS = 13
+ N = 13,
+
+ /**
+ * Other code type that does not belong to any of the above code types.
+ *
+ * This code type is used in the case that the above code types do not cover all the code
+ * types introduced in a new version of RINEX standard. When this code type is set, the
+ * field GnssMeasurement.otherCodeTypeName must specify the new code type.
+ */
+ OTHER = 255
+ };
+
+ /**
+ * Flags indicating the GNSS measurement state.
+ *
+ * <p>The expected behavior here is for GNSS HAL to set all the flags that apply. For example,
+ * if the state for a satellite is only C/A code locked and bit synchronized, and there is still
+ * millisecond ambiguity, the state must be set as:
+ *
+ * STATE_CODE_LOCK | STATE_BIT_SYNC | STATE_MSEC_AMBIGUOUS
+ *
+ * <p>If GNSS is still searching for a satellite, the corresponding state must be set to
+ * STATE_UNKNOWN(0).
+ *
+ * <p>In @2.0::IGnssMeasurementCallback.GnssMeasurement, v1_1.v1_0.receivedSvTimeInNs, the
+ * received satellite time, is relative to the beginning of the system week for all
+ * constellations except for Glonass where it is relative to the beginning of the Glonass system
+ * day.
+ *
+ * <p>The table below indicates the valid range of the received GNSS satellite time. These
+ * ranges depend on the constellation and code being tracked and the state of the tracking
+ * algorithms given by the getState method. If the state flag is set, then the valid measurement
+ * range is zero to the value in the table. The state flag with the widest range indicates the
+ * range of the received GNSS satellite time value.
+ *
+ * <table>
+ * <thead>
+ * <tr>
+ * <td />
+ * <td colspan="3"><strong>GPS/QZSS</strong></td>
+ * <td><strong>GLNS</strong></td>
+ * <td colspan="2"><strong>BDS</strong></td>
+ * <td colspan="3"><strong>GAL</strong></td>
+ * <td><strong>SBAS</strong></td>
+ * </tr>
+ * <tr>
+ * <td><strong>State Flag</strong></td>
+ * <td><strong>L1 C/A</strong></td>
+ * <td><strong>L5I</strong></td>
+ * <td><strong>L5Q</strong></td>
+ * <td><strong>L1OF</strong></td>
+ * <td><strong>B1I (D1)</strong></td>
+ * <td><strong>B1I (D2)</strong></td>
+ * <td><strong>E1B</strong></td>
+ * <td><strong>E1C</strong></td>
+ * <td><strong>E5AQ</strong></td>
+ * <td><strong>L1 C/A</strong></td>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>
+ * <strong>STATE_UNKNOWN</strong>
+ * </td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_CODE_LOCK</strong>
+ * </td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_SYMBOL_SYNC</strong>
+ * </td>
+ * <td>20 ms (optional)</td>
+ * <td>10 ms</td>
+ * <td>1 ms (optional)</td>
+ * <td>10 ms</td>
+ * <td>20 ms (optional)</td>
+ * <td>2 ms</td>
+ * <td>4 ms (optional)</td>
+ * <td>4 ms (optional)</td>
+ * <td>1 ms (optional)</td>
+ * <td>2 ms</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_BIT_SYNC</strong>
+ * </td>
+ * <td>20 ms</td>
+ * <td>20 ms</td>
+ * <td>1 ms (optional)</td>
+ * <td>20 ms</td>
+ * <td>20 ms</td>
+ * <td>-</td>
+ * <td>8 ms</td>
+ * <td>-</td>
+ * <td>1 ms (optional)</td>
+ * <td>4 ms</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_SUBFRAME_SYNC</strong>
+ * </td>
+ * <td>6s</td>
+ * <td>6s</td>
+ * <td>-</td>
+ * <td>2 s</td>
+ * <td>6 s</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>100 ms</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_TOW_DECODED</strong>
+ * </td>
+ * <td colspan="2">1 week</td>
+ * <td>-</td>
+ * <td>1 day</td>
+ * <td colspan="2">1 week</td>
+ * <td colspan="2">1 week</td>
+ * <td>-</td>
+ * <td>1 week</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_TOW_KNOWN</strong>
+ * </td>
+ * <td colspan="3">1 week</td>
+ * <td>1 day</td>
+ * <td colspan="2">1 week</td>
+ * <td colspan="3">1 week</td>
+ * <td>1 week</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GLO_STRING_SYNC</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>2 s</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GLO_TOD_DECODED</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>1 day</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GLO_TOD_KNOWN</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>1 day</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_BDS_D2_BIT_SYNC</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>2 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_BDS_D2_SUBFRAME_SYNC</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>600 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GAL_E1BC_CODE_LOCK</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>4 ms</td>
+ * <td>4 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GAL_E1C_2ND_CODE_LOCK</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>100 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_2ND_CODE_LOCK</strong>
+ * </td>
+ * <td>-</td>
+ * <td>10 ms (optional)</td>
+ * <td>20 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>100 ms (optional)</td>
+ * <td>100 ms</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GAL_E1B_PAGE_SYNC</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>2 s</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_SBAS_SYNC</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>1 s</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <p>Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
+ * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
+ *
+ * <p>Note well: if there is any ambiguity in integer millisecond, STATE_MSEC_AMBIGUOUS must be
+ * set accordingly, in the 'state' field. This value must be populated if 'state' !=
+ * STATE_UNKNOWN.
+ *
+ * <p>Note on optional flags:
+ * <ul>
+ * <li> For L1 C/A and B1I, STATE_SYMBOL_SYNC is optional since the symbol length is the
+ * same as the bit length.
+ * <li> For L5Q and E5aQ, STATE_BIT_SYNC and STATE_SYMBOL_SYNC are optional since they are
+ * implied by STATE_CODE_LOCK.
+ * <li> STATE_2ND_CODE_LOCK for L5I is optional since it is implied by STATE_SYMBOL_SYNC.
+ * <li> STATE_2ND_CODE_LOCK for E1C is optional since it is implied by
+ * STATE_GAL_E1C_2ND_CODE_LOCK.
+ * <li> For E1B and E1C, STATE_SYMBOL_SYNC is optional, because it is implied by
+ * STATE_GAL_E1BC_CODE_LOCK.
+ * </ul>
+ *
+ */
+ @export(name="", value_prefix="GNSS_MEASUREMENT_")
+ enum GnssMeasurementState : uint32_t {
+ STATE_UNKNOWN = 0,
+ STATE_CODE_LOCK = 1 << 0,
+ STATE_BIT_SYNC = 1 << 1,
+ STATE_SUBFRAME_SYNC = 1 << 2,
+ STATE_TOW_DECODED = 1 << 3,
+ STATE_MSEC_AMBIGUOUS = 1 << 4,
+ STATE_SYMBOL_SYNC = 1 << 5,
+ STATE_GLO_STRING_SYNC = 1 << 6,
+ STATE_GLO_TOD_DECODED = 1 << 7,
+ STATE_BDS_D2_BIT_SYNC = 1 << 8,
+ STATE_BDS_D2_SUBFRAME_SYNC = 1 << 9,
+ STATE_GAL_E1BC_CODE_LOCK = 1 << 10,
+ STATE_GAL_E1C_2ND_CODE_LOCK = 1 << 11,
+ STATE_GAL_E1B_PAGE_SYNC = 1 << 12,
+ STATE_SBAS_SYNC = 1 << 13,
+ STATE_TOW_KNOWN = 1 << 14,
+ STATE_GLO_TOD_KNOWN = 1 << 15,
+ STATE_2ND_CODE_LOCK = 1 << 16,
};
/**
@@ -96,6 +446,10 @@
* quarter cycle offset as they do when transmitted from the satellites. If the measurement
* is from a combination of the in-phase and quadrature phase components, then the alignment
* of the phase measurement will be aligned to the in-phase component.
+ *
+ * In this version of the HAL, the field 'state' in the v1_1.v1_0 struct is deprecated, and
+ * is no longer used by the framework. The satellite sync state is instead reported in
+ * @2.0::IGnssMeasurementCallback.GnssMeasurement.state.
*/
@1.1::IGnssMeasurementCallback.GnssMeasurement v1_1;
@@ -106,6 +460,27 @@
* in-order to properly apply code specific corrections to the psuedorange measurements.
*/
GnssMeasurementCodeType codeType;
+
+ /**
+ * The name of the code type when codeType is OTHER.
+ *
+ * This is used to specify the observation descriptor defined in GNSS Observation Data File
+ * Header Section Description in the RINEX standard (Version 3.XX). In RINEX Version 3.03,
+ * in Appendix Table A2 Attributes are listed as uppercase letters (for instance, "A" for
+ * "A channel"). In the future, if for instance a code "G" was added in the official RINEX
+ * standard, "G" could be specified here.
+ */
+ string otherCodeTypeName;
+
+ /**
+ * Per satellite sync state. It represents the current sync state for the associated
+ * satellite.
+ *
+ * Based on the sync state, the receivedSvTimeInNs field must be interpreted accordingly.
+ *
+ * This value is mandatory.
+ */
+ bitfield<GnssMeasurementState> state;
};
/**
diff --git a/gnss/2.0/default/GnssMeasurement.cpp b/gnss/2.0/default/GnssMeasurement.cpp
index dc23db3..0f707b4 100644
--- a/gnss/2.0/default/GnssMeasurement.cpp
+++ b/gnss/2.0/default/GnssMeasurement.cpp
@@ -26,7 +26,7 @@
using GnssConstellationType = V1_0::GnssConstellationType;
using GnssMeasurementFlags = V1_0::IGnssMeasurementCallback::GnssMeasurementFlags;
-using GnssMeasurementState = V1_0::IGnssMeasurementCallback::GnssMeasurementState;
+using GnssMeasurementState = V2_0::IGnssMeasurementCallback::GnssMeasurementState;
sp<V2_0::IGnssMeasurementCallback> GnssMeasurement::sCallback = nullptr;
@@ -97,11 +97,6 @@
.svid = (int16_t)6,
.constellation = GnssConstellationType::GLONASS,
.timeOffsetNs = 0.0,
- .state = GnssMeasurementState::STATE_CODE_LOCK | GnssMeasurementState::STATE_BIT_SYNC |
- GnssMeasurementState::STATE_SUBFRAME_SYNC |
- GnssMeasurementState::STATE_TOW_DECODED |
- GnssMeasurementState::STATE_GLO_STRING_SYNC |
- GnssMeasurementState::STATE_GLO_TOD_DECODED,
.receivedSvTimeInNs = 8195997131077,
.receivedSvTimeUncertaintyInNs = 15,
.cN0DbHz = 30.0,
@@ -116,8 +111,14 @@
V1_0::IGnssMeasurementCallback::GnssMultipathIndicator::INDICATOR_UNKNOWN};
V1_1::IGnssMeasurementCallback::GnssMeasurement measurement_1_1 = {.v1_0 = measurement_1_0};
V2_0::IGnssMeasurementCallback::GnssMeasurement measurement_2_0 = {
- .v1_1 = measurement_1_1,
- .codeType = IGnssMeasurementCallback::GnssMeasurementCodeType::CODE_TYPE_C};
+ .v1_1 = measurement_1_1,
+ .codeType = IGnssMeasurementCallback::GnssMeasurementCodeType::C,
+ .otherCodeTypeName = "",
+ .state = GnssMeasurementState::STATE_CODE_LOCK | GnssMeasurementState::STATE_BIT_SYNC |
+ GnssMeasurementState::STATE_SUBFRAME_SYNC |
+ GnssMeasurementState::STATE_TOW_DECODED |
+ GnssMeasurementState::STATE_GLO_STRING_SYNC |
+ GnssMeasurementState::STATE_GLO_TOD_DECODED};
hidl_vec<IGnssMeasurementCallback::GnssMeasurement> measurements(1);
measurements[0] = measurement_2_0;
diff --git a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
index 552cf1b..48f2be8 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
@@ -185,10 +185,16 @@
ASSERT_TRUE(last_measurement_.measurements.size() > 0);
for (auto measurement : last_measurement_.measurements) {
ASSERT_TRUE(
- (int)measurement.codeType >=
- (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::CODE_TYPE_A &&
- (int)measurement.codeType <=
- (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::CODE_TYPE_CODELESS);
+ ((int)measurement.codeType >=
+ (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::A &&
+ (int)measurement.codeType <=
+ (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::N) ||
+ (int)measurement.codeType ==
+ (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::OTHER);
+ if ((int)measurement.codeType ==
+ (int)IGnssMeasurementCallback_2_0::GnssMeasurementCodeType::OTHER) {
+ ASSERT_NE(measurement.otherCodeTypeName, "");
+ }
}
iGnssMeasurement->close();
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/keymaster/3.0/default/KeymasterDevice.cpp b/keymaster/3.0/default/KeymasterDevice.cpp
index 6fabbde..7d3e6f2 100644
--- a/keymaster/3.0/default/KeymasterDevice.cpp
+++ b/keymaster/3.0/default/KeymasterDevice.cpp
@@ -19,7 +19,7 @@
#include "KeymasterDevice.h"
-#include <cutils/log.h>
+#include <log/log.h>
#include <AndroidKeymaster3Device.h>
#include <hardware/keymaster0.h>
diff --git a/media/c2/1.0/Android.bp b/media/c2/1.0/Android.bp
index c37c22b..56c78b2 100644
--- a/media/c2/1.0/Android.bp
+++ b/media/c2/1.0/Android.bp
@@ -13,6 +13,7 @@
"IComponentListener.hal",
"IComponentStore.hal",
"IConfigurable.hal",
+ "IInputSink.hal",
"IInputSurface.hal",
"IInputSurfaceConnection.hal",
],
diff --git a/media/c2/1.0/IComponent.hal b/media/c2/1.0/IComponent.hal
index deb9255..7fd551f 100644
--- a/media/c2/1.0/IComponent.hal
+++ b/media/c2/1.0/IComponent.hal
@@ -22,13 +22,19 @@
import IConfigurable;
import IComponentInterface;
import IComponentListener;
+import IInputSurface;
+import IInputSurfaceConnection;
/**
- * Interface for a Codec 2.0 component corresponding to API level 1.0 or
- * below. Components have two states: stopped and running. The running
- * state has three sub-states: executing, tripped and error.
+ * Interface for a Codec2 component corresponding to API level 1.0 or below.
+ * Components have two states: stopped and running. The running state has three
+ * sub-states: executing, tripped and error.
+ *
+ * All methods in `IComponent` must not block. If a method call cannot be
+ * completed in a timely manner, it must return `TIMED_OUT` in the return
+ * status.
*/
-interface IComponent extends IComponentInterface {
+interface IComponent {
// METHODS AVAILABLE WHEN RUNNING
// =========================================================================
@@ -38,44 +44,42 @@
*
* This method must be supported in running (including tripped) states.
*
- * This method must return within 1ms
+ * It is acceptable for this method to return `OK` and return an error value
+ * using the IComponentListener::onWorkDone() callback.
*
- * It is acceptable for this method to return OK and return an error value
- * using the onWorkDone() callback.
- *
- * @param workBundle WorkBundle object containing Works to queue to the
- * component.
+ * @param workBundle `WorkBundle` object containing a list of `Work` objects
+ * to queue to the component.
* @return status Status of the call, which may be
- * - OK - Works in \p workBundle were successfully queued.
- * - BAD_INDEX - Some component(s) in some Work do(es) not exist.
- * - CANNOT_DO - The components are not tunneled.
- * - NO_MEMORY - Not enough memory to queue \p workBundle.
- * - CORRUPTED - Some unknown error prevented queuing the Works.
- * (unexpected).
+ * - `OK` - Works in @p workBundle were successfully queued.
+ * - `BAD_INDEX` - Some component id in some `Worklet` is not valid.
+ * - `CANNOT_DO` - The components are not tunneled but some `Work` object
+ * contains tunneling information.
+ * - `NO_MEMORY` - Not enough memory to queue @p workBundle.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
*/
queue(WorkBundle workBundle) generates (Status status);
/**
- * Discards and abandons any pending work for the component.
+ * Discards and abandons any pending `Work` items for the component.
*
* This method must be supported in running (including tripped) states.
*
- * This method must return within 5ms.
+ * `Work` that could be immediately abandoned/discarded must be returned in
+ * @p flushedWorkBundle. The order in which queued `Work` items are
+ * discarded can be arbitrary.
*
- * Work that could be immediately abandoned/discarded must be returned in
- * \p flushedWorks; this can be done in an arbitrary order.
- *
- * Work that could not be abandoned or discarded immediately must be marked
- * to be discarded at the earliest opportunity, and must be returned via
- * the onWorkDone() callback. This must be completed within 500ms.
+ * `Work` that could not be abandoned or discarded immediately must be
+ * marked to be discarded at the earliest opportunity, and must be returned
+ * via IComponentListener::onWorkDone(). This must be completed within
+ * 500ms.
*
* @return status Status of the call, which may be
- * - OK - The component has been successfully flushed.
- * - TIMED_OUT - The flush could not be completed within the time limit.
- * (unexpected)
- * - CORRUPTED - Some unknown error prevented flushing from
- * completion. (unexpected)
- * @return flushedWorkBundle WorkBundle object containing flushed Works.
+ * - `OK` - The component has been successfully flushed.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return flushedWorkBundle `WorkBundle` object containing flushed `Work`
+ * items.
*/
flush(
) generates (
@@ -87,42 +91,39 @@
* Drains the component, and optionally downstream components. This is a
* signalling method; as such it does not wait for any work completion.
*
- * Marks last work item as "drain-till-here", so component is notified not
- * to wait for further work before it processes work already queued. This
- * method can also be used to set the end-of-stream flag after work has been
- * queued. Client can continue to queue further work immediately after this
- * method returns.
+ * The last `Work` item is marked as "drain-till-here", so the component is
+ * notified not to wait for further `Work` before it processes what is
+ * already queued. This method can also be used to set the end-of-stream
+ * flag after `Work` has been queued. Client can continue to queue further
+ * `Work` immediately after this method returns.
*
* This method must be supported in running (including tripped) states.
*
- * This method must return within 1ms.
- *
- * Work that is completed must be returned via the onWorkDone() callback.
+ * `Work` that is completed must be returned via
+ * IComponentListener::onWorkDone().
*
* @param withEos Whether to drain the component with marking end-of-stream.
* @return status Status of the call, which may be
- * - OK - The drain request has been successfully recorded.
- * - TIMED_OUT - The flush could not be completed within the time limit.
- * (unexpected)
- * - CORRUPTED - Some unknown error prevented flushing from completion.
- * (unexpected)
+ * - `OK` - The drain request has been successfully recorded.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
*/
drain(bool withEos) generates (Status status);
/**
* Starts using a surface for output.
*
- * @param blockPoolId The id of the BlockPool to be associated with the
- * output surface.
- * @param surface A surface to use for codec output.
+ * This method must not block.
+ *
+ * @param blockPoolId Id of the `C2BlockPool` to be associated with the
+ * output surface.
+ * @param surface Output surface.
* @return status Status of the call, which may be
- * - OK - The operation completed successfully.
- * - CANNOT_DO - The component does not support an output surface.
- * - REFUSED - The output surface cannot be accessed.
- * - TIMED_OUT - The component could not be connected within the time
- * limit. (unexpected)
- * - CORRUPTED - Some unknown error prevented connecting the component.
- * (unexpected)
+ * - `OK` - The operation completed successfully.
+ * - `CANNOT_DO` - The component does not support an output surface.
+ * - `REFUSED` - The output surface cannot be accessed.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
*/
setOutputSurface(
uint64_t blockPoolId,
@@ -132,65 +133,101 @@
);
/**
- * Starts using a persistent OMX input surface for a component.
+ * Starts using an input surface.
*
* The component must be in running state.
*
- * @param producer Producer component of an OMX persistent input surface.
- * @param source Source component of an OMX persistent input surface.
+ * @param inputSurface Input surface to connect to.
* @return status Status of the call, which may be
- * - OK - The operation completed successfully.
- * - CANNOT_DO - The component does not support an input surface.
- * - BAD_STATE - Component is not in running state.
- * - DUPLICATE - The component is already connected to an input surface.
- * - REFUSED - The input surface is already in use.
- * - NO_MEMORY - Not enough memory to start the component.
- * - TIMED_OUT - The component could not be connected within the time
- * limit. (unexpected)
- * - CORRUPTED - Some unknown error prevented connecting the component.
- * (unexpected)
+ * - `OK` - The operation completed successfully.
+ * - `CANNOT_DO` - The component does not support an input surface.
+ * - `BAD_STATE` - The component is not in running state.
+ * - `DUPLICATE` - The component is already connected to an input surface.
+ * - `REFUSED` - The input surface is already in use.
+ * - `NO_MEMORY` - Not enough memory to start the component.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return connection `IInputSurfaceConnection` object, which can be used to
+ * query and configure properties of the connection. This cannot be
+ * null.
+ */
+ connectToInputSurface(
+ IInputSurface inputSurface
+ ) generates (
+ Status status,
+ IInputSurfaceConnection connection
+ );
+
+ /**
+ * Starts using an OMX input surface.
+ *
+ * The component must be in running state.
+ *
+ * This method is similar to connectToInputSurface(), but it takes an OMX
+ * input surface (as a pair of `IGraphicBufferProducer` and
+ * `IGraphicBufferSource`) instead of Codec2's own `IInputSurface`.
+ *
+ * @param producer Producer component of an OMX input surface.
+ * @param source Source component of an OMX input surface.
+ * @return status Status of the call, which may be
+ * - `OK` - The operation completed successfully.
+ * - `CANNOT_DO` - The component does not support an OMX input surface.
+ * - `BAD_STATE` - The component is not in running state.
+ * - `DUPLICATE` - The component is already connected to an input surface.
+ * - `REFUSED` - The input surface is already in use.
+ * - `NO_MEMORY` - Not enough memory to start the component.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return connection `IInputSurfaceConnection` object, which can be used to
+ * query and configure properties of the connection. This cannot be
+ * null.
*/
connectToOmxInputSurface(
IGraphicBufferProducer producer,
IGraphicBufferSource source
- ) generates (Status status);
+ ) generates (
+ Status status,
+ IInputSurfaceConnection connection
+ );
/**
* Stops using an input surface.
*
- * This call is used for both Codec 2.0 and OMX input surfaces.
- *
* The component must be in running state.
*
* @return status Status of the call, which may be
- * - OK - The operation completed successfully.
- * - CANNOT_DO - The component does not support an input surface.
- * - BAD_STATE - Component is not in running state.
- * - NOT_FOUND - The component is not connected to an input surface.
- * - TIMED_OUT - The component could not be connected within the time
- * limit. (unexpected)
- * - CORRUPTED - Some unknown error prevented connecting the component.
- * (unexpected)
+ * - `OK` - The operation completed successfully.
+ * - `CANNOT_DO` - The component does not support an input surface.
+ * - `BAD_STATE` - The component is not in running state.
+ * - `NOT_FOUND` - The component is not connected to an input surface.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
*/
disconnectFromInputSurface() generates (Status Status);
/**
- * Creates a local block pool backed by the given allocator and returns its
- * identifier.
+ * Creates a local `C2BlockPool` backed by the given allocator and returns
+ * its id.
*
- * This call must return within 100 msec.
+ * The returned @p blockPoolId is the only way the client can refer to a
+ * `C2BlockPool` object in the component. The id can be passed to
+ * setOutputSurface() or used in some C2Param objects later.
*
- * @param allocatorId The Codec 2.0 allocator ID
+ * The created `C2BlockPool` object can be destroyed by calling
+ * destroyBlockPool(), reset() or release(). reset() and release() must
+ * destroy all `C2BlockPool` objects that have been created.
+ *
+ * @param allocatorId Id of a `C2Allocator`.
* @return status Status of the call, which may be
- * - OK - The operation completed successfully.
- * - NO_MEMORY - Not enough memory to create the pool.
- * - BAD_VALUE - Invalid allocator.
- * - TIMED_OUT - The pool could not be created within the time
- * limit. (unexpected)
- * - CORRUPTED - Some unknown error prevented creating the pool.
- * (unexpected)
- * @return blockPoolId The Codec 2.0 blockpool ID for the created pool.
- * @return configurable Configuration interface for the created pool.
+ * - `OK` - The operation completed successfully.
+ * - `NO_MEMORY` - Not enough memory to create the pool.
+ * - `BAD_VALUE` - @p allocatorId is not recognized.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return blockPoolId Id of the created C2BlockPool object. This may be
+ * used in setOutputSurface() if the allocator
+ * @return configurable Configuration interface for the created pool. This
+ * must not be null.
*/
createBlockPool(uint32_t allocatorId) generates (
Status status,
@@ -201,17 +238,13 @@
/**
* Destroys a local block pool previously created by createBlockPool().
*
- * This call must return within 100 msec.
- *
- * @param blockPoolId The block pool id previously returned by
+ * @param blockPoolId Id of a `C2BlockPool` that was previously returned by
* createBlockPool().
* @return status Status of the call, which may be
- * - OK - The operation completed successfully.
- * - NOT_FOUND - The supplied blockPoolId is not valid.
- * - TIMED_OUT - The pool could not be destroyedwithin the time limit.
- * (unexpected)
- * - CORRUPTED - Some unknown error prevented destruction of the pool.
- * (unexpected)
+ * - `OK` - The operation completed successfully.
+ * - `NOT_FOUND` - The supplied blockPoolId is not valid.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
*/
destroyBlockPool(uint64_t blockPoolId) generates (Status status);
@@ -223,28 +256,24 @@
*
* This method must be supported in stopped state as well as tripped state.
*
- * If the return value is OK, the component must be in the running state.
- * If the return value is BAD_STATE or DUPLICATE, no state change is
- * expected as a response to this call.
- * Otherwise, the component must be in the stopped state.
+ * If the return value is `OK`, the component must be in the running state.
+ * If the return value is `BAD_STATE` or `DUPLICATE`, no state change is
+ * expected as a response to this call. Otherwise, the component must be in
+ * the stopped state.
*
* If a component is in the tripped state and start() is called while the
- * component configuration still results in a trip, start must succeed and
- * a new onTripped callback must be used to communicate the configuration
+ * component configuration still results in a trip, start() must succeed and
+ * a new onTripped() callback must be used to communicate the configuration
* conflict that results in the new trip.
*
- * This method must return within 500ms.
- *
* @return status Status of the call, which may be
- * - OK - The component has started successfully.
- * - BAD_STATE - Component is not in stopped or tripped state.
- * - DUPLICATE - When called during another start call from another
- * thread.
- * - NO_MEMORY - Not enough memory to start the component.
- * - TIMED_OUT - The component could not be started within the time limit.
- * (unexpected)
- * - CORRUPTED - Some unknown error prevented starting the component.
- * (unexpected)
+ * - `OK` - The component has started successfully.
+ * - `BAD_STATE` - Component is not in stopped or tripped state.
+ * - `DUPLICATE` - When called during another start call from another
+ * thread.
+ * - `NO_MEMORY` - Not enough memory to start the component.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
*/
start() generates (Status status);
@@ -255,22 +284,22 @@
*
* This method must return withing 500ms.
*
- * Upon this call, all pending work must be abandoned.
- * If the return value is BAD_STATE or DUPLICATE, no state change is
- * expected as a response to this call.
- * For all other return values, the component must be in the stopped state.
+ * Upon this call, all pending `Work` must be abandoned.
+ *
+ * If the return value is `BAD_STATE` or `DUPLICATE`, no state change is
+ * expected as a response to this call. For all other return values, the
+ * component must be in the stopped state.
*
* This does not alter any settings and tunings that may have resulted in a
* tripped state.
*
* @return status Status of the call, which may be
- * - OK - The component has stopped successfully.
- * - BAD_STATE - Component is not in running state.
- * - DUPLICATE - When called during another stop call from another thread.
- * - TIMED_OUT - The component could not be stopped within the time limit.
- * (unexpected)
- * - CORRUPTED - Some unknown error prevented starting the component.
- * (unexpected)
+ * - `OK` - The component has stopped successfully.
+ * - `BAD_STATE` - Component is not in running state.
+ * - `DUPLICATE` - When called during another stop call from another
+ * thread.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
*/
stop() generates (Status status);
@@ -284,25 +313,24 @@
*
* This method must return withing 500ms.
*
- * After this call returns all work must have been abandoned, all references
- * must have been released.
+ * When this call returns, if @p status is `OK`, all `Work` items must
+ * have been abandoned, and all resources (including `C2BlockPool` objects
+ * previously created by createBlockPool()) must have been released.
*
- * If the return value is BAD_STATE or DUPLICATE, no state change is
- * expected as a response to this call.
- * For all other return values, the component shall be in the stopped state.
+ * If the return value is `BAD_STATE` or `DUPLICATE`, no state change is
+ * expected as a response to this call. For all other return values, the
+ * component must be in the stopped state.
*
- * This brings settings back to their default - "guaranteeing" no tripped
+ * This brings settings back to their default, "guaranteeing" no tripped
* state.
*
* @return status Status of the call, which may be
- * - OK - The component has been reset.
- * - BAD_STATE - Component is in released state.
- * - DUPLICATE - When called during another reset call from another
- * thread.
- * - TIMED_OUT - The component could not be reset within the time limit.
- * (unexpected)
- * - CORRUPTED - Some unknown error prevented resetting the component.
- * (unexpected)
+ * - `OK` - The component has been reset.
+ * - `BAD_STATE` - Component is in released state.
+ * - `DUPLICATE` - When called during another reset call from another
+ * thread.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
*/
reset() generates (Status status);
@@ -311,19 +339,27 @@
*
* This method must be supported in stopped state.
*
- * This method must return withing 500ms. Upon return all references must
- * be abandoned.
+ * This method destroys the component. Upon return, if @p status is `OK` or
+ * `DUPLICATE`, all resources must have been released.
*
* @return status Status of the call, which may be
- * - OK - The component has been released.
- * - BAD_STATE - The component is running.
- * - DUPLICATE - The component is already released.
- * - TIMED_OUT - The component could not be released within the time
- * limit. (unexpected)
- * - CORRUPTED - Some unknown error prevented releasing the component.
- * (unexpected)
+ * - `OK` - The component has been released.
+ * - `BAD_STATE` - The component is running.
+ * - `DUPLICATE` - The component is already released.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
*/
release() generates (Status status);
+ /**
+ * Returns the @ref IComponentInterface instance associated to this
+ * component.
+ *
+ * An @p IConfigurable instance for the component can be obtained by calling
+ * IComponentInterface::getConfigurable() on the returned @p intf.
+ *
+ * @return intf `IComponentInterface` instance. This must not be null.
+ */
+ getInterface() generates (IComponentInterface intf);
};
diff --git a/media/c2/1.0/IComponentInterface.hal b/media/c2/1.0/IComponentInterface.hal
index d4b30b1..a007d02 100644
--- a/media/c2/1.0/IComponentInterface.hal
+++ b/media/c2/1.0/IComponentInterface.hal
@@ -19,21 +19,20 @@
import IConfigurable;
/**
- * Component interface object. This object contains all of the configuration of
+ * Component interface object. This object contains all of the configurations of
* a potential or actual component. It can be created and used independently of
- * an actual Codec 2.0 component instance to query support and parameters for
- * various component settings and configurations for a potential component.
- * Actual components also expose this interface.
+ * an actual Codec2 component to query supported parameters for various
+ * component settings, and configurations for a potential component.
+ *
+ * An actual component exposes this interface via IComponent::getInterface().
*/
-interface IComponentInterface extends IConfigurable {
- /*
- * There are no additional methods to IConfigurable interface.
+interface IComponentInterface {
+ /**
+ * Returns the @ref IConfigurable instance associated to this component
+ * interface.
*
- * Component interfaces have no states.
- *
- * The name of the component or component interface object is a unique name
- * for that component or component interface 'class'; however, multiple
- * instances of that component must have the same name.
+ * @return configurable `IConfigurable` instance. This must not be null.
*/
+ getConfigurable() generates (IConfigurable configurable);
};
diff --git a/media/c2/1.0/IComponentListener.hal b/media/c2/1.0/IComponentListener.hal
index eb71ecb..70d5fb2 100644
--- a/media/c2/1.0/IComponentListener.hal
+++ b/media/c2/1.0/IComponentListener.hal
@@ -17,54 +17,112 @@
package android.hardware.media.c2@1.0;
/**
- * This callback interface is used for handling notifications from IComponent.
+ * Callback interface for handling notifications from @ref IComponent.
*/
interface IComponentListener {
/**
- * Notify the listener that some works have been completed.
+ * Notify the listener that some `Work` items have been completed.
+ *
+ * All the input buffers in the returned `Work` objects must not be used by
+ * the component after onWorkDone() is called.
+ *
+ * @param workBundle List of completed `Work` objects.
*/
oneway onWorkDone(WorkBundle workBundle);
/**
* Notify the listener that the component is tripped.
+ *
+ * @param settingResults List of failures.
*/
oneway onTripped(vec<SettingResult> settingResults);
/**
* Notify the listener of an error.
*
- * @param status Error type. \p status may be `OK`, which means that an
- * error has occurred, but the error type is unknown.
- * @param errorCode Additional error code. The framework may not recognize
- * this.
+ * @param status Error type. @p status may be `OK`, which means that an
+ * error has occurred, but the error type does not fit into the type
+ * `Status`. In this case, additional information is provided by
+ * @p errorCode.
+ * @param errorCode Additional error information. The framework may not
+ * recognize the meaning of this value.
*/
oneway onError(Status status, uint32_t errorCode);
/**
- * Information about rendering of a frame.
+ * Information about rendering of a frame to a `Surface`.
*/
struct RenderedFrame {
/**
- * Id of the buffer queue containing the rendered buffer.
+ * Id of the `BufferQueue` containing the rendered buffer.
+ *
+ * This value must have been obtained by an earlier call to
+ * IGraphicBufferProducer::getUniqueId().
*/
uint64_t bufferQueueId;
/**
* Id of the slot of the rendered buffer.
+ *
+ * This value must have been obtained by an earlier call to
+ * IGraphicBufferProducer::dequeueBuffer() or
+ * IGraphicBufferProducer::attachBuffer().
*/
int32_t slotId;
/**
- * Timestamp of the rendering (consistent with timestamps in
- * the associated BufferQueue).
+ * Timestamp the rendering happened.
+ *
+ * The reference point for the timestamp is determined by the
+ * `BufferQueue` that performed the rendering.
*/
int64_t timestampNs;
};
/**
- * Notify the listener that frames are rendered.
+ * Notify the listener that frames have been rendered.
*
- * @param renderedFrames List of information about renderings of frames.
+ * @param renderedFrames List of @ref RenderedFrame objects.
*/
oneway onFramesRendered(vec<RenderedFrame> renderedFrames);
+
+ /**
+ * Identifying information for an input buffer previously queued to the
+ * component via IComponent::queue().
+ */
+ struct InputBuffer {
+ /**
+ * This value comes from `Work::input.ordinal.frameIndex` in a `Work`
+ * object that was previously queued.
+ */
+ uint64_t frameIndex;
+ /**
+ * This value is an index into `Work::input.buffers` (which is an array)
+ * in a `Work` object that was previously queued.
+ */
+ uint32_t arrayIndex;
+ };
+
+ /**
+ * Notify the listener that some input buffers are no longer needed by the
+ * component, and hence can be released or reused by the client.
+ *
+ * Input buffers that are contained in a `Work` object returned by an
+ * earlier onWorkDone() call are assumed released, so they must not appear
+ * in any onInputBuffersReleased() calls. That means
+ * onInputBuffersReleased() must only report input buffers that are released
+ * before the output in the same `Work` item is produced. However, it is
+ * possible for an input buffer to be returned by onWorkDone() after it has
+ * been reported by onInputBuffersReleased().
+ *
+ * @note onWorkDone() and onInputBuffersReleased() both notify the client
+ * that input buffers are no longer needed. However, in order to minimize
+ * IPC calls, onInputBuffersReleased() should be called only when
+ * onWorkDone() cannot be called, e.g., the component needs more input
+ * before an output can be produced.
+ *
+ * @param inputBuffers List of `InputBuffer` objects, identifying input
+ * buffers that are no longer needed by the component.
+ */
+ oneway onInputBuffersReleased(vec<InputBuffer> inputBuffers);
};
diff --git a/media/c2/1.0/IComponentStore.hal b/media/c2/1.0/IComponentStore.hal
index 4bfa170..2aa6a70 100644
--- a/media/c2/1.0/IComponentStore.hal
+++ b/media/c2/1.0/IComponentStore.hal
@@ -23,27 +23,33 @@
import IConfigurable;
import IInputSurface;
-interface IComponentStore extends IConfigurable {
+/**
+ * Entry point for Codec2 HAL.
+ *
+ * All methods in `IComponentStore` must not block. If a method call cannot be
+ * completed in a timely manner, it must return `TIMED_OUT` in the return
+ * status. The only exceptions are getPoolClientManager() and getConfigurable(),
+ * which must always return immediately.
+ */
+interface IComponentStore {
/**
* Creates a component by name.
*
- * This method must return within 100ms.
- *
- * @param name Name of the component to create. This should match one of the
+ * @param name Name of the component to create. This must match one of the
* names returned by listComponents().
- * @param listener The component listener to use for the component.
- * @param pool The buffer pool client manager of the component listener.
- * This must be null if the listener process does not own a buffer pool.
+ * @param listener Callback receiver.
+ * @param pool `IClientManager` object of the BufferPool in the client
+ * process. This may be null if the client does not own a BufferPool.
* @return status Status of the call, which may be
- * - OK - The component was created successfully.
- * - NOT_FOUND - There is no component with the given name.
- * - NO_MEMORY - Not enough memory to create the component.
- * - TIMED_OUT - The component could not be created within the time limit.
- * (unexpected)
- * - CORRUPTED - Some unknown error prevented the creation of the
- * component. (unexpected)
- * @return comp The created component if `Status = OK`.
+ * - `OK` - The component was created successfully.
+ * - `NOT_FOUND` - There is no component with the given name.
+ * - `NO_MEMORY` - Not enough memory to create the component.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return comp The created component if @p status is `OK`.
+ *
+ * @sa IComponentListener.
*/
createComponent(
string name,
@@ -57,19 +63,15 @@
/**
* Creates a component interface by name.
*
- * This method must return within 100ms.
- *
* @param name Name of the component interface to create. This should match
* one of the names returned by listComponents().
* @return status Status of the call, which may be
- * - OK - The component interface was created successfully.
- * - NOT_FOUND - There is no component interface with the given name.
- * - NO_MEMORY - Not enough memory to create the component interface.
- * - TIMED_OUT - The component interface could not be created within the
- * time limit. (unexpected)
- * - CORRUPTED - Some unknown error prevented the creation of the
- * component interface. (unexpected)
- * @return compIntf The created component interface if `Status = OK`.
+ * - `OK` - The component interface was created successfully.
+ * - `NOT_FOUND` - There is no component interface with the given name.
+ * - `NO_MEMORY` - Not enough memory to create the component interface.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return compIntf The created component interface if @p status is `OK`.
*/
createInterface(
string name
@@ -83,57 +85,49 @@
*/
struct ComponentTraits {
/**
- * Name of the component.
+ * Name of the component. This must be unique for each component.
+ *
+ * This name is use to identify the component to create in
+ * createComponent() and createComponentInterface().
*/
string name;
enum Domain : uint32_t {
- AUDIO,
+ OTHER = 0,
VIDEO,
- OTHER = 0xffffffff,
+ AUDIO,
+ IMAGE,
};
/**
- * Component domain. The framework may not recognize `OTHER`.
+ * Component domain.
*/
Domain domain;
- /**
- * If #domain is `OTHER`, #domainOther can be used to provide additional
- * information. Otherwise, #domainOther is ignored. The framework may
- * not inspect this value.
- */
- uint32_t domainOther;
enum Kind : uint32_t {
+ OTHER = 0,
DECODER,
ENCODER,
- OTHER = 0xffffffff,
};
/**
- * Component kind. The framework may not recognize `OTHER`.
+ * Component kind.
*/
Kind kind;
- /**
- * If #kind is `OTHER`, #kindOther can be used to provide additional
- * information. Otherwise, #kindOther is ignored. The framework may not
- * inspect this value.
- */
- uint32_t kindOther;
/**
- * Rank used by MediaCodecList to determine component ordering. Lower
+ * Rank used by `MediaCodecList` to determine component ordering. Lower
* value means higher priority.
*/
uint32_t rank;
/**
- * Media type.
+ * MIME type.
*/
string mediaType;
/**
* Aliases for component name for backward compatibility.
*
- * \note Multiple components can have the same alias (but not the same
+ * Multiple components can have the same alias (but not the same
* component name) as long as their media types differ.
*/
vec<string> aliases;
@@ -142,36 +136,51 @@
/**
* Returns the list of components supported by this component store.
*
- * This method must return within 500ms.
- *
- * @return traits List of component traits for all components supported by this store in no
- * particular order.
+ * @return status Status of the call, which may be
+ * - `OK` - The operation was successful.
+ * - `NO_MEMORY` - Not enough memory to complete this method.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return traits List of component traits for all components supported by
+ * this store (in no particular order).
*/
- listComponents() generates (vec<ComponentTraits> traits);
+ listComponents() generates (
+ Status status,
+ vec<ComponentTraits> traits
+ );
/**
* Creates a persistent input surface that can be used as an input surface
* for any IComponent instance
*
- * This method must return within 100ms.
- *
- * @return surface A persistent input surface
+ * @return status Status of the call, which may be
+ * - `OK` - The operation was successful.
+ * - `NO_MEMORY` - Not enough memory to complete this method.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return surface A persistent input surface. This may be null to indicate
+ * an error.
*/
- createInputSurface() generates (IInputSurface surface);
+ createInputSurface() generates (
+ Status status,
+ IInputSurface surface
+ );
/**
- * Returns a list of StructDescriptor object for a set of requested
- * structures that this store is aware of.
+ * Returns a list of `StructDescriptor` objects for a set of requested
+ * C2Param structure indices that this store is aware of.
*
* This operation must be performed at best effort, e.g. the component
* store must simply ignore all struct indices that it is not aware of.
*
- * @param indices struct indices to return des
+ * @param indices Indices of C2Param structures to describe.
* @return status Status of the call, which may be
- * - OK - The operation completed successfully.
- * - NOT_FOUND - Some indices were not known.
- * - NO_MEMORY - Not enough memory to complete this method.
- * @return structs List of StructDescriptor objects.
+ * - `OK` - The operation completed successfully.
+ * - `NOT_FOUND` - Some indices were not known.
+ * - `NO_MEMORY` - Not enough memory to complete this method.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return structs List of `StructDescriptor` objects.
*/
getStructDescriptors(
vec<ParamIndex> indices
@@ -181,33 +190,35 @@
);
/**
- * Returns information required for using BufferPool API in buffer passing.
- * If the returned pool is not null, the client can call registerSender() to
- * register its IAccessor instance, hence allowing the client to send
- * buffers to components hosted by this process.
- *
- * @return pool If the component store supports receiving buffers via
- * BufferPool API, \p pool must be a valid `IClientManager` instance.
- * Otherwise, \p pool must be null.
- */
- getPoolClientManager(
- ) generates (
- IClientManager pool
- );
-
- /**
- * The store must copy the contents of \p src into \p dst without changing
- * the format of \p dst.
+ * Copies the contents of @p src into @p dst without changing the format of
+ * @p dst.
*
* @param src Source buffer.
* @param dst Destination buffer.
* @return status Status of the call, which may be
- * - OK - The copy is successful.
- * - CANNOT_DO - \p src and \p dst are not compatible.
- * - REFUSED - No permission to copy.
- * - CORRUPTED - The copy cannot be done. (unexpected)
+ * - `OK` - The copy is successful.
+ * - `CANNOT_DO` - @p src and @p dst are not compatible.
+ * - `REFUSED` - No permission to copy.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
*/
copyBuffer(Buffer src, Buffer dst) generates (Status status);
+ /**
+ * Returns the `IClientManager` object for the component's BufferPool.
+ *
+ * @return pool If the component store supports receiving buffers via
+ * BufferPool API, @p pool must be a valid `IClientManager` instance.
+ * Otherwise, @p pool must be null.
+ */
+ getPoolClientManager() generates (IClientManager pool);
+
+ /**
+ * Returns the @ref IConfigurable instance associated to this component
+ * store.
+ *
+ * @return configurable `IConfigurable` instance. This must not be null.
+ */
+ getConfigurable() generates (IConfigurable configurable);
};
diff --git a/media/c2/1.0/IConfigurable.hal b/media/c2/1.0/IConfigurable.hal
index cd4dd10..31dc4d3 100644
--- a/media/c2/1.0/IConfigurable.hal
+++ b/media/c2/1.0/IConfigurable.hal
@@ -17,43 +17,78 @@
package android.hardware.media.c2@1.0;
/**
- * Generic configuration interface used by all configurable Codec 2.0
- * components.
+ * Generic configuration interface presented by all configurable Codec2 objects.
*
- * This interface must be supported in all states of the inheriting
- * object, and must not change the state of the inheriting object.
+ * This interface must be supported in all states of the owning object, and must
+ * not change the state of the owning object.
*/
interface IConfigurable {
/**
- * Returns the name of this object. This must match the name that was
- * supplied during the creation of the object.
+ * Returns the id of the object. This must be unique among all objects of
+ * the same type hosted by the same store.
*
- * @return name Name of this object.
+ * @return id Id of the object.
+ */
+ getId() generates (uint32_t id);
+
+ /**
+ * Returns the name of the object.
+ *
+ * This must match the name that was supplied during the creation of the
+ * object.
+ *
+ * @return name Name of the object.
*/
getName() generates (string name);
/**
- * Queries a set of parameters from the object. Querying is performed at
- * best effort: the object must query all supported parameters and skip
- * unsupported ones, or parameters that could not be allocated. Any errors
- * are communicated in the return value.
+ * Queries a set of parameters from the object.
*
- * \note Parameter values do not depend on the order of query.
+ * Querying is performed at best effort: the object must query all supported
+ * parameters and skip unsupported ones (which may include parameters that
+ * could not be allocated). Any errors are communicated in the return value.
*
- * This method must return within 1ms if \p mayBlock is DONT_BLOCK, and
- * within 5ms otherwise.
+ * If @p mayBlock is false, this method must not block. All parameter
+ * queries that require blocking must be skipped.
*
- * @param indices List of param indices for params to be queried.
+ * If @p mayBlock is true, a query may block, but the whole method call
+ * has to complete in a timely manner, or `status = TIMED_OUT` is returned.
+ *
+ * If @p mayBlock is false, this method must not block. Otherwise, this
+ * method is allowed to block for a certain period of time before completing
+ * the operation. If the operation is not completed in a timely manner,
+ * `status = TIMED_OUT` is returned.
+ *
+ * @note The order of C2Param objects in @p param does not depend on the
+ * order of C2Param structure indices in @p indices.
+ *
+ * \par For IComponent
+ *
+ * When the object type is @ref IComponent, this method must be supported in
+ * any state except released. This call must not change the state nor the
+ * internal configuration of the component.
+ *
+ * The blocking behavior of this method differs among states:
+ * - In the stopped state, this must be non-blocking. @p mayBlock is
+ * ignored. (The method operates as if @p mayBlock was false.)
+ * - In any of the running states, this method may block momentarily if
+ * @p mayBlock is true. However, if the call cannot be completed in a
+ * timely manner, `status = TIMED_OUT` is returned.
+ *
+ * @param indices List of C2Param structure indices to query.
* @param mayBlock Whether this call may block or not.
* @return status Status of the call, which may be
- * - OK - All parameters could be queried.
- * - BAD_INDEX - All supported parameters could be queried, but some
- * parameters were not supported.
- * - NO_MEMORY - Could not allocate memory for a supported parameter.
- * - BLOCKING - Querying some parameters requires blocking.
- * - CORRUPTED - Some unknown error prevented the querying of the
- * parameters. (unexpected)
- * @return params List of params queried corresponding to \p indices.
+ * - `OK` - All parameters could be queried.
+ * - `BAD_INDEX` - All supported parameters could be queried, but some
+ * parameters were not supported.
+ * - `NO_MEMORY` - Could not allocate memory for a supported parameter.
+ * - `BLOCKING` - Querying some parameters requires blocking, but
+ * @p mayBlock is false.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return params Flattened representation of C2Param objects.
+ *
+ * @sa Params.
*/
query(
vec<ParamIndex> indices,
@@ -64,31 +99,60 @@
);
/**
- * Sets a set of parameters for the object. Tuning is performed at best
- * effort: the object must update all supported configuration at best
- * effort and skip unsupported parameters. Any errors are communicated in
- * the return value and in \p failures.
+ * Sets a set of parameters for the object.
*
- * \note Parameter tuning DOES depend on the order of the tuning parameters.
- * E.g. some parameter update may allow some subsequent parameter update.
+ * Tuning is performed at best effort: the object must update all supported
+ * configurations at best effort and skip unsupported parameters. Any errors
+ * are communicated in the return value and in @p failures.
*
- * This method must return within 1ms if \p mayBlock is false, and within
- * 5ms otherwise.
+ * A non-strict parameter update with an unsupported value shall cause an
+ * update to the closest supported value. A strict parameter update with an
+ * unsupported value shall be skipped and a failure shall be returned.
+ *
+ * If @p mayBlock is false, this method must not block. An update that
+ * requires blocking shall be skipped and a failure shall be returned.
+ *
+ * If @p mayBlock is true, an update may block, but the whole method call
+ * has to complete in a timely manner, or `status = TIMED_OUT` is returned.
+ *
+ * The final values for all parameters set are propagated back to the caller
+ * in @p params.
+ *
+ * \par For IComponent
+ *
+ * When the object type is @ref IComponent, this method must be supported in
+ * any state except released.
+ *
+ * The blocking behavior of this method differs among states:
+ * - In the stopped state, this must be non-blocking. @p mayBlock is
+ * ignored. (The method operates as if @p mayBlock was false.)
+ * - In any of the running states, this method may block momentarily if
+ * @p mayBlock is true. However, if the call cannot be completed in a
+ * timely manner, `status = TIMED_OUT` is returned.
+ *
+ * @note Parameter tuning @e does depend on the order of the tuning
+ * parameters, e.g., some parameter update may enable some subsequent
+ * parameter update.
*
* @param inParams Requested parameter updates.
* @param mayBlock Whether this call may block or not.
* @return status Status of the call, which may be
- * - OK - All parameters could be updated successfully.
- * - BAD_INDEX - All supported parameters could be updated successfully,
- * but some parameters were not supported.
- * - NO_MEMORY - Some supported parameters could not be updated
- * successfully because they contained unsupported values.
- * These are returned in \p failures.
- * - BLOCKING - Setting some parameters requires blocking.
- * - CORRUPTED - Some unknown error prevented the update of the
- * parameters. (unexpected)
- * @return failures List of parameter failures.
- * @return outParams Resulting values for the configured parameters.
+ * - `OK` - All parameters could be updated successfully.
+ * - `BAD_INDEX` - All supported parameters could be updated successfully,
+ * but some parameters were not supported.
+ * - `NO_MEMORY` - Some supported parameters could not be updated
+ * successfully because they contained unsupported values.
+ * These are returned in @p failures.
+ * - `BLOCKING` - Setting some parameters requires blocking, but
+ * @p mayBlock is false.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return failures List of update failures.
+ * @return outParams Flattened representation of configured parameters. The
+ * order of parameters in @p outParams is based on the order of
+ * requested updates in @p inParams.
+ *
+ * @sa SettingResult.
*/
config(
Params inParams,
@@ -103,22 +167,19 @@
// =========================================================================
/**
- * Returns a selected range of the set of supported parameters.
+ * Returns a list of supported parameters within a selected range of C2Param
+ * structure indices.
*
- * The set of supported parameters are represented in a vector with a
- * start index of 0, and the selected range are indices into this vector.
- * Fewer than \p count parameters are returned if the selected range is
- * not fully/not at all part of the available vector indices.
- *
- * This method must return within 1ms.
- *
- * @param start start index of selected range
- * @param count size of the selected
+ * @param start The first index of the selected range.
+ * @param count The length of the selected range.
* @return status Status of the call, which may be
- * - OK - The operation completed successfully.
- * - NO_MEMORY - Not enough memory to complete this method.
- * @return params Vector containing the selected range of supported
- * parameters.
+ * - `OK` - The operation completed successfully.
+ * - `NO_MEMORY` - Not enough memory to complete this method.
+ * @return params List of supported parameters in the selected range. This
+ * list may have fewer than @p count elements if some indices in the
+ * range are not supported.
+ *
+ * @sa ParamDescriptor.
*/
querySupportedParams(
uint32_t start,
@@ -131,23 +192,42 @@
/**
* Retrieves the supported values for the queried fields.
*
- * Upon return the object must fill in the supported
- * values for the fields listed as well as a status for each field.
- * Object shall process all fields queried even if some queries fail.
+ * The object must process all fields queried even if some queries fail.
*
- * This method must return within 1ms if \p mayBlock is false, and within
- * 5ms otherwise.
+ * If @p mayBlock is false, this method must not block. Otherwise, this
+ * method is allowed to block for a certain period of time before completing
+ * the operation. If the operation cannot be completed in a timely manner,
+ * `status = TIMED_OUT` is returned.
*
- * @param inFields Vector of field queries.
+ * \par For IComponent
+ *
+ * When the object type is @ref IComponent, this method must be supported in
+ * any state except released.
+ *
+ * The blocking behavior of this method differs among states:
+ * - In the stopped state, this must be non-blocking. @p mayBlock is
+ * ignored. (The method operates as if @p mayBlock was false.)
+ * - In any of the running states, this method may block momentarily if
+ * @p mayBlock is true. However, if the call cannot be completed in a
+ * timely manner, `status = TIMED_OUT` is returned.
+ *
+ * @param inFields List of field queries.
* @param mayBlock Whether this call may block or not.
* @return status Status of the call, which may be
- * - OK - The operation completed successfully.
- * - BLOCKING - Querying some parameters requires blocking.
- * - NO_MEMORY - Not enough memory to complete this method.
- * - BAD_INDEX - At least one field was not recognized as a component
- * field.
- * @return outFields Vector containing supported values and query result
- * for the selected fields.
+ * - `OK` - The operation completed successfully.
+ * - `BLOCKING` - Querying some parameters requires blocking, but
+ * @p mayBlock is false.
+ * - `NO_MEMORY` - Not enough memory to complete this method.
+ * - `BAD_INDEX` - At least one field was not recognized as a component
+ * field.
+ * - `BLOCKING` - Querying some fields requires blocking, but @p mayblock
+ * is false.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return outFields List of supported values and results for the
+ * supplied queries.
+ *
+ * @sa FieldSupportedValuesQuery, FieldSupportedValuesQueryResult.
*/
querySupportedValues(
vec<FieldSupportedValuesQuery> inFields,
diff --git a/media/c2/1.0/IInputSink.hal b/media/c2/1.0/IInputSink.hal
new file mode 100644
index 0000000..809c27a
--- /dev/null
+++ b/media/c2/1.0/IInputSink.hal
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.media.c2@1.0;
+
+import android.hardware.graphics.bufferqueue@1.0::IGraphicBufferProducer;
+
+import IConfigurable;
+
+/**
+ * An `IInputSink` is a receiver of work items.
+ *
+ * An @ref IComponent instance can present itself as an `IInputSink` via a thin
+ * wrapper.
+ *
+ * @sa IInputSurface, IComponent.
+ */
+interface IInputSink {
+ /**
+ * Feeds work to the sink.
+ *
+ * @param workBundle `WorkBundle` object containing a list of `Work` objects
+ * to queue to the component.
+ * @return status Status of the call, which may be
+ * - `OK` - Works in @p workBundle were successfully queued.
+ * - `BAD_INDEX` - Some component id in some `Worklet` is not valid.
+ * - `CANNOT_DO` - Tunneling has not been set up for this sink, but some
+ * `Work` object contains tunneling information.
+ * - `NO_MEMORY` - Not enough memory to queue @p workBundle.
+ * - `TIMED_OUT` - The operation cannot be finished in a timely manner.
+ * - `CORRUPTED` - Some unknown error occurred.
+ */
+ queue(WorkBundle workBundle) generates (Status status);
+
+ /**
+ * Returns the @ref IConfigurable instance associated to this sink.
+ *
+ * @return configurable `IConfigurable` instance. This must not be null.
+ */
+ getConfigurable() generates (IConfigurable configurable);
+};
+
diff --git a/media/c2/1.0/IInputSurface.hal b/media/c2/1.0/IInputSurface.hal
index c083a21..d11ce15 100644
--- a/media/c2/1.0/IInputSurface.hal
+++ b/media/c2/1.0/IInputSurface.hal
@@ -19,43 +19,57 @@
import android.hardware.graphics.bufferqueue@1.0::IGraphicBufferProducer;
import IConfigurable;
-import IComponent;
+import IInputSink;
import IInputSurfaceConnection;
/**
- * Input surface that can be configured for the IComponent.
+ * Input surface for a Codec2 component.
+ *
+ * An <em>input surface</em> is an instance of `IInputSurface`, which may be
+ * created by calling IComponentStore::createInputSurface(). Once created, the
+ * client may
+ * 1. write data to it via the `IGraphicBufferProducer` interface; and
+ * 2. use it as input to a Codec2 encoder.
+ *
+ * @sa IInputSurfaceConnection, IComponentStore::createInputSurface(),
+ * IComponent::connectToInputSurface().
*/
-interface IInputSurface extends IGraphicBufferProducer {
+interface IInputSurface {
+ /**
+ * Returns the producer interface into the internal buffer queue.
+ *
+ * @return producer `IGraphicBufferProducer` instance. This must not be
+ * null.
+ */
+ getGraphicBufferProducer() generates (IGraphicBufferProducer producer);
/**
- * Connects this input surface to a component.
+ * Returns the @ref IConfigurable instance associated to this input surface.
*
- * This call must return within 100 ms.
- *
- * @param component The component to connect to. This must have type
- * IComponent.
- * @return status Status of the call, which may be
- * - OK - The operation succeeded.
- * - BAD_STATE - The component is in running state.
- * - DUPLICATE - The surface is already connected to a component.
- * - NO_MEMORY - Could not allocate memory to connect to the component.
- * - CORRUPTED - Some unknown error prevented the connection. (unexpected)
- * @return connection Connection object that is used to disconnect
- * from the component.
+ * @return configurable `IConfigurable` instance. This must not be null.
*/
- connectToComponent(
- IComponent component
+ getConfigurable() generates (IConfigurable configurable);
+
+ /**
+ * Connects the input surface to an input sink.
+ *
+ * This function is generally called from inside the implementation of
+ * IComponent::connectToInputSurface(), where @p sink is a thin wrapper of
+ * the component that consumes buffers from this surface.
+ *
+ * @param sink Input sink. See `IInputSink` for more information.
+ * @return status Status of the call, which may be
+ * - `OK` - Configuration successful.
+ * - `BAD_VALUE` - @p sink is invalid.
+ * - `CORRUPTED` - Some unknown error occurred.
+ * @return connection `IInputSurfaceConnection` object. This must not be
+ * null if @p status is `OK`.
+ */
+ connect(
+ IInputSink sink
) generates (
Status status,
IInputSurfaceConnection connection
);
-
- /**
- * Returns the Codec 2.0 configuration object for this surface.
- *
- * @return configurable The configuration object for this surface.
- */
- getConfigurable() generates (IConfigurable configurable);
-
};
diff --git a/media/c2/1.0/IInputSurfaceConnection.hal b/media/c2/1.0/IInputSurfaceConnection.hal
index 500091d..035b115 100644
--- a/media/c2/1.0/IInputSurfaceConnection.hal
+++ b/media/c2/1.0/IInputSurfaceConnection.hal
@@ -16,20 +16,33 @@
package android.hardware.media.c2@1.0;
-interface IInputSurfaceConnection {
+import IConfigurable;
+/**
+ * Connection between a component and an input surface.
+ *
+ * An instance of `IInputSurfaceConnection` contains an `IConfigurable`
+ * interface for querying and configuring properties of the connection.
+ */
+interface IInputSurfaceConnection {
/**
- * Disconnects this input surface from the component.
- *
- * This call must return within 100 ms.
+ * Destroys the connection between an input surface and a component.
*
* @return status Status of the call, which may be
- * - OK - The operation succeeded.
- * - BAD_STATE - The component is not in running state.
- * - NOT_FOUND - The surface is not connected to a component.
- * - CORRUPTED - Some unknown error prevented the connection. (unexpected)
+ * - `OK` - The disconnection succeeded.
+ * - `BAD_STATE` - The component is not in running state.
+ * - `NOT_FOUND` - The surface is not connected to a component.
+ * - `CORRUPTED` - Some unknown error occurred.
*/
disconnect() generates (Status status);
+ /**
+ * Returns the @ref IConfigurable instance associated to this connection.
+ *
+ * This can be used to customize the connection.
+ *
+ * @return configurable `IConfigurable` instance. This must not be null.
+ */
+ getConfigurable() generates (IConfigurable configurable);
};
diff --git a/media/c2/1.0/types.hal b/media/c2/1.0/types.hal
index 252d781..ec422b1 100644
--- a/media/c2/1.0/types.hal
+++ b/media/c2/1.0/types.hal
@@ -18,220 +18,278 @@
import android.hardware.media.bufferpool@2.0::BufferStatusMessage;
+/**
+ * Common return values for Codec2 operations.
+ */
enum Status : int32_t {
- /** operation completed successfully */
+ /** Operation completed successfully. */
OK = 0,
// bad input
- /** argument has invalid value (user error) */
+ /** Argument has invalid value (user error). */
BAD_VALUE = -22,
- /** argument uses invalid index (user error) */
+ /** Argument uses invalid index (user error). */
BAD_INDEX = -75,
- /** argument/index is valid but not possible */
+ /** Argument/Index is valid but not possible. */
CANNOT_DO = -2147483646,
// bad sequencing of events
- /** object already exists */
+ /** Object already exists. */
DUPLICATE = -17,
- /** object not found */
+ /** Object not found. */
NOT_FOUND = -2,
- /** operation is not permitted in the current state */
+ /** Operation is not permitted in the current state. */
BAD_STATE = -38,
- /** operation would block but blocking is not permitted */
+ /** Operation would block but blocking is not permitted. */
BLOCKING = -9930,
// bad environment
- /** not enough memory to complete operation */
+ /** Not enough memory to complete operation. */
NO_MEMORY = -12,
- /** missing permission to complete operation */
+ /** Missing permission to complete operation. */
REFUSED = -1,
- /** operation did not complete within timeout */
+ /** Operation did not complete within timeout. */
TIMED_OUT = -110,
// missing functionality
- /** operation is not implemented/supported (optional only) */
+ /** Operation is not implemented/supported (optional only). */
OMITTED = -74,
// unknown fatal
- /** some unexpected error prevented the operation */
+ /** Some unexpected error prevented the operation. */
CORRUPTED = -2147483648,
// uninitialized
- /** status has not been initialized */
+ /** Status has not been initialized. */
NO_INIT = -19,
};
/**
- * Codec 2.0 parameter index
+ * C2Param structure index.
+ *
+ * This is a number that is unique for each C2Param structure type.
+ *
+ * @sa Codec 2.0 standard.
*/
typedef uint32_t ParamIndex;
/**
- * Codec 2.0 parameter structure
+ * Flattened representation of C2Param objects.
*
- * The description of a Params is provided by supplying a ParamIndex to
- * IComponentStore::getStructDescriptors().
+ * The `Params` type is an array of bytes made up by concatenating a list of
+ * C2Param objects. The start index (offset into @ref Params) of each C2Param
+ * object in the list is divisible by 8. Up to 7 padding bytes may be added
+ * after each C2Param object to achieve this 64-bit alignment.
+ *
+ * Each C2Param object has the following layout:
+ * - 4 bytes: C2Param structure index (of type @ref ParamIndex) identifying the
+ * type of the C2Param object.
+ * - 4 bytes: size of the C2Param object (unsigned 4-byte integer).
+ * - (size - 8) bytes: data of the C2Param object.
+ *
+ * In order to interpret each C2Param object correctly, its structure must be
+ * described by IComponentStore::getStructDescriptors().
+ *
+ * @note Please refer to the Codec 2.0 standard for the list of standard
+ * parameter structures.
+ *
+ * @sa Codec 2.0 standard.
*/
typedef vec<uint8_t> Params;
/**
- * Struct uniquely specifying a field in an arbitrary parameter structure.
+ * Identifying information of a field relative to a known C2Param structure.
+ *
+ * Within a given C2Param structure, each field is uniquely identified by @ref
+ * FieldId.
*/
struct FieldId {
- /** Offset of the field in bytes */
+ /** Offset of the field in bytes. */
uint32_t offset;
- /** Size of the field in bytes */
+ /** Size of the field in bytes. */
uint32_t size;
};
/**
- * Struct representing a location of a field in a parameter with a given index.
+ * Reference to a field in a C2Param structure.
*/
struct ParamField {
- /** Index of the parameter */
+ /** Index of the C2Param structure. */
ParamIndex index;
- /** Field identifier */
+ /** Identifier of the field inside the C2Param structure. */
FieldId fieldId;
};
/**
- * Struct describing basic properties of a parameter with a given index.
+ * Usage description of a C2Param structure.
+ *
+ * @ref ParamDescriptor is returned by IConfigurable::querySupportedParams().
*/
struct ParamDescriptor {
- /** Parameter index */
+ /**
+ * Index of the C2Param structure being described.
+ */
ParamIndex index;
enum Attrib : uint32_t {
/**
- * Parameter is required to be specified.
+ * The parameter is required to be specified.
*/
REQUIRED = 1u << 0,
/**
- * Parameter retains its value.
+ * The parameter retains its value.
*/
PERSISTENT = 1u << 1,
/**
- * Parameter is strict.
+ * The parameter is strict.
*/
STRICT = 1u << 2,
/**
- * Parameter is publicly read-only.
+ * The parameter is publicly read-only.
*/
READ_ONLY = 1u << 3,
/**
- * Parameter must not be visible to clients.
+ * The parameter must not be visible to clients.
*/
HIDDEN = 1u << 4,
/**
- * Parameter must not be used by framework (other than testing).
+ * The parameter must not be used by framework (other than testing).
*/
INTERNAL = 1u << 5,
/**
- * Parameter is publicly constant (hence read-only).
+ * The parameter is publicly constant (hence read-only).
*/
CONST = 1u << 6,
};
- /** Parameter attributes */
bitfield<Attrib> attrib;
- /** Parameter name */
+ /**
+ * Name of the structure. This must be unique for each structure.
+ */
string name;
- /** index of other parameters that this parameter depends on */
+ /**
+ * Indices of other C2Param structures that this C2Param structure depends
+ * on.
+ */
vec<ParamIndex> dependencies;
};
-// Generic way to describe supported numeric values for Codec 2.0 interfaces.
+// Generic way to describe supported numeric values for Codec2 interfaces.
/**
- * An untyped value that can fit on 64 bits - the type of which is communicated
- * via a separate channel (FieldType).
+ * An untyped value that can fit in 64 bits, the type of which is communicated
+ * via a separate channel (@ref FieldSupportedValues.type).
*/
typedef uint64_t PrimitiveValue;
/*
- * Generic supported values for a field.
+ * Description of supported values for a field.
*
- * This can be either a range or a set of values. The range can be linear or
- * geometric with clear minimum and maximum values, and can have an optional
- * step size or geometric ratio. Values can optionally represent flags.
+ * This can be a continuous range or a discrete set of values.
*/
struct FieldSupportedValues {
+ /**
+ * Used if #type is `RANGE`.
+ *
+ * If the `step` member is 0, and `num` and `denom` are both 1, the `Range`
+ * structure represents a closed interval bounded by `min` and `max`.
+ *
+ * Otherwise, the #Range structure represents a finite sequence of numbers
+ * produced from the following recurrence relation:
+ *
+ * @code
+ * v[0] = min
+ * v[i] = v[i - 1] * num / denom + step ; i >= 1
+ * @endcode
+ *
+ * Both the ratio `num / denom` and the value `step` must be positive. The
+ * last number in the sequence described by this #Range structure is the
+ * largest number in the sequence that is smaller than or equal to `max`.
+ *
+ * @note
+ * The division in the formula may truncate the result if the data type of
+ * these values is an integral type.
+ */
struct Range {
+ /**
+ * Lower end of the range (inclusive).
+ */
PrimitiveValue min;
+ /**
+ * Upper end of the range (inclusive).
+ */
PrimitiveValue max;
+ /**
+ * The non-homogeneous term in the recurrence relation.
+ */
PrimitiveValue step;
+ /**
+ * The numerator of the scale coefficient in the recurrence relation.
+ */
PrimitiveValue num;
+ /**
+ * The denominator of the scale coefficient in the recurrence relation.
+ */
PrimitiveValue denom;
};
enum Type : int32_t {
/** No supported values */
- EMPTY,
- /** Numeric range that can be continuous or discrete */
+ EMPTY = 0,
+ /** Numeric range, described in a #Range structure */
RANGE,
/** List of values */
VALUES,
/** List of flags that can be OR-ed */
FLAGS,
- /** Other representations */
- OTHER = 0xffffffff,
};
/**
- * Type of the supported values. The framework may not recognize `OTHER`.
+ * Type of the supported values.
*/
Type type;
- /**
- * Codec2.0 type code of the supported values.
- * * If #type is `OTHER`, #typeOther can be used to give more information.
- * In this case, the interpretation of this structure is
- * implementation-defined.
- * * For all other values of #type, #typeOther is not used.
- * The framework may not inspect this value.
- */
- int32_t typeOther;
-
- /*
- * If #type = EMPTY, #range and #value are unused.
- */
/**
- * If #type = RANGE, #range will specify the range of possible values.
+ * When #type is #Type.RANGE, #range shall specify the range of possible
+ * values.
*
- * The intended type of members of #range will be clear in the context where
- * FieldSupportedValues is used.
+ * The intended type of members of #range shall be clear in the context
+ * where `FieldSupportedValues` is used.
*/
Range range;
/**
- * If #type is `VALUES` or `FLAGS`, #value will list supported values.
+ * When #type is #Type.VALUES or #Type.FLAGS, #value shall list supported
+ * values/flags.
*
- * The intended type of components of #value will be clear in the context
- * where FieldSupportedValues is used.
+ * The intended type of components of #value shall be clear in the context
+ * where `FieldSupportedValues` is used.
*/
vec<PrimitiveValue> values;
};
/**
- * Supported values for a specific field.
+ * Supported values for a field.
*
* This is a pair of the field specifier together with an optional supported
* values object. This structure is used when reporting parameter configuration
* failures and conflicts.
*/
struct ParamFieldValues {
- /** the field or parameter */
+ /**
+ * Reference to a field or a C2Param structure.
+ */
ParamField paramOrField;
/**
- * optional supported values for the field if paramOrField specifies an
+ * Optional supported values for the field if #paramOrField specifies an
* actual field that is numeric (non struct, blob or string). Supported
* values for arrays (including string and blobs) describe the supported
* values for each element (character for string, and bytes for blobs). It
@@ -241,18 +299,18 @@
};
/**
- * Field descriptor.
+ * Description of a field inside a C2Param structure.
*/
struct FieldDescriptor {
- /** Field id */
+ /** Location of the field in the C2Param structure */
FieldId fieldId;
/**
- * Possible types of a field.
+ * Possible types of the field.
*/
enum Type : uint32_t {
- NO_INIT,
+ NO_INIT = 0,
INT32,
UINT32,
CNTR32,
@@ -261,186 +319,227 @@
CNTR64,
FLOAT,
/**
- * Fixed-size string (POD)
+ * Fixed-size string (POD).
*/
STRING = 0x100,
/**
- * blobs have no sub-elements and can be thought of as byte arrays.
- * However, bytes cannot be individually addressed by clients.
+ * A blob has no sub-elements and can be thought of as an array of
+ * bytes. However, bytes cannot be individually addressed by clients.
*/
BLOB,
/**
- * Structs. Marked with this flag in addition to their coreIndex.
+ * The field is a structure that may contain other fields.
*/
- STRUCT_FLAG = 0x20000,
+ STRUCT = 0x20000,
};
/**
* Type of the field.
*/
bitfield<Type> type;
- /** Extent of the field */
- uint32_t length;
- /*
- * Note: the last member of a param struct can be of arbitrary length (e.g.
- * if it is T[] array, which extends to the last byte of the parameter.)
- * This is marked with extent 0.
+ /**
+ * If #type is #Type.STRUCT, #structIndex is the C2Param structure index;
+ * otherwise, #structIndex is not used.
*/
+ ParamIndex structIndex;
- /** Name of the field */
+ /**
+ * Extent of the field.
+ * - For a non-array field, #extent is 1.
+ * - For a fixed-length array field, #extent is the length. An array field
+ * of length 1 is indistinguishable from a non-array field.
+ * - For a variable-length array field, #extent is 0. This can only occur as
+ * the last member of a C2Param structure.
+ */
+ uint32_t extent;
+
+ /**
+ * Name of the field. This must be unique for each field in the same
+ * structure.
+ */
string name;
- /** Named value type */
+
+ /**
+ * Named value type. This is used for defining an enum value for a numeric
+ * type.
+ */
struct NamedValue {
+ /**
+ * Name of the enum value. This must be unique for each enum value in
+ * the same field.
+ */
string name;
+ /**
+ * Underlying value of the enum value. Multiple enum names may have the
+ * same underlying value.
+ */
PrimitiveValue value;
};
- /** Named values for the field */
+ /**
+ * List of enum values. This is not used when #type is not one of the
+ * numeric types.
+ */
vec<NamedValue> namedValues;
};
/**
- * Struct descriptor.
+ * Description of a C2Param structure. It consists of an index and a list of
+ * `FieldDescriptor`s.
*/
struct StructDescriptor {
- /** Struct type */
+ /**
+ * Index of the structure.
+ */
ParamIndex type;
- /** Field descriptors for each field */
+ /**
+ * List of fields in the structure.
+ *
+ * Fields are ordered by their offsets. A field that is a structure is
+ * ordered before its members.
+ */
vec<FieldDescriptor> fields;
};
/**
- * Information describing the reason a parameter settings may fail, or
- * may be overriden.
+ * Information describing the reason the parameter settings may fail, or may be
+ * overridden.
*/
struct SettingResult {
- /** Failure code (of Codec 2.0 SettingResult failure type) */
+ /** Failure code */
enum Failure : uint32_t {
- /** Parameter is read-only and cannot be set. */
- READ_ONLY,
- /** Parameter mismatches input data. */
- MISMATCH,
- /** Parameter does not accept value. */
- BAD_VALUE,
/** Parameter is not supported. */
BAD_TYPE,
/** Parameter is not supported on the specific port. */
BAD_PORT,
/** Parameter is not supported on the specific stream. */
BAD_INDEX,
- /** Parameter is in conflict with an/other setting(s). */
+ /** Parameter is read-only and cannot be set. */
+ READ_ONLY,
+ /** Parameter mismatches input data. */
+ MISMATCH,
+ /** Strict parameter does not accept value for the field at all. */
+ BAD_VALUE,
+ /**
+ * Strict parameter field value is in conflict with an/other
+ * setting(s).
+ */
CONFLICT,
/**
- * Parameter is out of range due to other settings. (This failure mode
- * can only be used for strict parameters.)
+ * Parameter field is out of range due to other settings. (This failure
+ * mode can only be used for strict calculated parameters.)
*/
UNSUPPORTED,
/**
+ * Field does not access the requested parameter value at all. It has
+ * been corrected to the closest supported value. This failure mode is
+ * provided to give guidance as to what are the currently supported
+ * values for this field (which may be a subset of the at-all-potential
+ * values).
+ */
+ INFO_BAD_VALUE,
+ /**
* Requested parameter value is in conflict with an/other setting(s)
* and has been corrected to the closest supported value. This failure
- * mode is given to provide suggestion to the client as to how to enable
- * the requested parameter value. */
- INFO_CONFLICT,
- /**
- * This failure mode is reported when all the above failure modes do not
- * apply.
+ * mode is given to provide guidance as to what are the currently
+ * supported values as well as to optionally provide suggestion to the
+ * client as to how to enable the requested parameter value.
*/
- OTHER = 0xffffffff,
+ INFO_CONFLICT,
};
- /**
- * The failure type. The framework might not recognize `OTHER`.
- */
Failure failure;
- /**
- * The failure code.
- * * If #failure is `OTHER`, #failureOther can be used to give more
- * information.
- * * For all other values of #failure, #failureOther is not used.
- * The framework may not inspect this value.
- */
- uint32_t failureOther;
/**
- * Failing (or corrected) field. Currently supported values for the field.
- * This is set if different from the globally supported values (e.g. due to
- * restrictions by another param or input data)
+ * Failing (or corrected) field or parameter and optionally, currently
+ * supported values for the field. Values must only be set for field
+ * failures other than `BAD_VALUE`, and only if they are different from the
+ * globally supported values (e.g. due to restrictions by another parameter
+ * or input data).
*/
ParamFieldValues field;
/**
- * Conflicting parameters or fields with
- * (optional) suggested values for any conflicting fields to avoid the conflict.
+ * Conflicting parameters or fields with (optional) suggested values for any
+ * conflicting fields to avoid the conflict. Values must only be set for
+ * `CONFLICT`, `UNSUPPORTED` or `INFO_CONFLICT` failure code.
*/
vec<ParamFieldValues> conflicts;
};
/**
- * Data structure for ordering Work objects. Each member is used for comparing
- * urgency in the same fashion: a smaller value indicates that the associated
- * Work object is more urgent.
+ * Ordering information of @ref FrameData objects. Each member is used for
+ * comparing urgency: a smaller difference from a reference value indicates that
+ * the associated Work object is more urgent. The reference value for each
+ * member is initialized the first time it is communicated between the client
+ * and the codec, and it may be updated to later values that are communicated.
+ *
+ * Each member of `WorkOrdinal` is stored as an unsigned integer, but the actual
+ * order it represents is derived by subtracting the reference value, then
+ * interpreting the result as a signed number with the same storage size (using
+ * two's complement).
+ *
+ * @note `WorkOrdinal` is the HIDL counterpart of `C2WorkOrdinalStruct` in the
+ * Codec 2.0 standard.
*/
struct WorkOrdinal {
/**
- * Timestamp in microseconds - can wrap around.
+ * Timestamp in microseconds.
*/
uint64_t timestampUs;
/**
- * Frame index - can wrap around.
+ * Frame index.
*/
uint64_t frameIndex;
/**
- * Component specific frame ordinal - can wrap around.
+ * Component specific frame ordinal.
*/
uint64_t customOrdinal;
};
/**
- * A structure that holds information of a Block. There are two types of Blocks:
- * NATIVE and POOLED. Each type has its own way of identifying blocks.
+ * Storage type for `BaseBlock`.
+ *
+ * A `BaseBlock` is a representation of a codec memory block. Coded data,
+ * decoded data, codec-specific data, and other codec-related data are all sent
+ * in the form of BaseBlocks.
*/
-struct BaseBlock {
- enum Type : int32_t {
- NATIVE,
- POOLED,
- };
+safe_union BaseBlock {
/**
- * There are two types of blocks: NATIVE and POOLED.
- */
- Type type;
-
- /**
- * A "NATIVE" block is represented by a native handle.
+ * #nativeBlock is the opaque representation of a buffer.
*/
handle nativeBlock;
-
- /*
- * A "POOLED" block is represented by `BufferStatusMessage`.
+ /**
+ * #pooledBlock is a reference to a buffer handled by a BufferPool.
*/
BufferStatusMessage pooledBlock;
};
/**
- * A Block in transfer consists of an index into an array of BaseBlock plus some
- * extra information. One BaseBlock may occur in multiple blocks in one
- * `WorkBundle`.
+ * Reference to a @ref BaseBlock within a @ref WorkBundle.
+ *
+ * `Block` contains additional attributes that `BaseBlock` does not. These
+ * attributes may differ among `Block` objects that refer to the same
+ * `BaseBlock` in the same `WorkBundle`.
*/
struct Block {
/**
- * Identity of the BaseBlock within a WorkBundle. This is an index into the
- * `baseBlocks` array of a `WorkBundle` object.
+ * Identity of a `BaseBlock` within a `WorkBundle`. This is an index into
+ * #WorkBundle.baseBlocks.
*/
uint32_t index;
/**
- * Metadata associated with the block.
+ * Metadata associated with this `Block`.
*/
Params meta;
/**
- * Fence for synchronizing block access.
+ * Fence for synchronizing `Block` access.
*/
handle fence;
};
/**
- * Type of buffers processed by a component.
+ * A codec buffer, which is a collection of @ref Block objects and metadata.
+ *
+ * This is a part of @ref FrameData.
*/
struct Buffer {
/**
@@ -454,23 +553,37 @@
};
/**
- * An extension of Buffer that also contains an index.
+ * An extension of @ref Buffer that also contains a C2Param structure index.
+ *
+ * This is a part of @ref FrameData.
*/
struct InfoBuffer {
+ /**
+ * A C2Param structure index.
+ */
ParamIndex index;
+ /**
+ * Associated @ref Buffer object.
+ */
Buffer buffer;
};
/**
- * This structure represents a frame with its metadata. A frame consists of an
- * ordered set of buffers, configuration changes, and info buffers along with
- * some non-configuration metadata.
+ * Data for an input frame or an output frame.
+ *
+ * This structure represents a @e frame with its metadata. A @e frame consists
+ * of an ordered set of buffers, configuration changes, and info buffers along
+ * with some non-configuration metadata.
+ *
+ * @note `FrameData` is the HIDL counterpart of `C2FrameData` in the Codec 2.0
+ * standard.
*/
struct FrameData {
enum Flags : uint32_t {
/**
- * For input frames: no output frame will be generated when processing
+ * For input frames: no output frame shall be generated when processing
* this frame, but metadata must still be processed.
+ *
* For output frames: this frame must be discarded but metadata is still
* valid.
*/
@@ -482,92 +595,178 @@
END_OF_STREAM = (1 << 1),
/**
* This frame must be discarded with its metadata.
- * This flag is only set by components - e.g. as a response to the flush
+ *
+ * This flag is only set by components, e.g. as a response to the flush
* command.
*/
DISCARD_FRAME = (1 << 2),
/**
+ * This frame is not the last frame produced for the input.
+ *
+ * This flag is normally set by the component - e.g. when an input frame
+ * results in multiple output frames, this flag is set on all but the
+ * last output frame.
+ *
+ * Also, when components are chained, this flag should be propagated
+ * down the work chain. That is, if set on an earlier frame of a
+ * work-chain, it should be propagated to all later frames in that
+ * chain. Additionally, components down the chain could set this flag
+ * even if not set earlier, e.g. if multiple output frames are generated
+ * at that component for the input frame.
+ */
+ FLAG_INCOMPLETE = (1 << 3),
+ /**
* This frame contains only codec-specific configuration data, and no
* actual access unit.
*
- * \deprecated Pass codec configuration with the codec-specific
+ * @deprecated Pass codec configuration with the codec-specific
* configuration info together with the access unit.
*/
CODEC_CONFIG = (1u << 31),
};
/**
- * Frame flags.
+ * Frame flags, as described in #Flags.
*/
bitfield<Flags> flags;
/**
- * Ordinal of the frame.
+ * @ref WorkOrdinal of the frame.
*/
WorkOrdinal ordinal;
/**
- * Frame buffers.
+ * List of frame buffers.
*/
vec<Buffer> buffers;
/**
- * Params determining a configuration update.
+ * List of configuration updates.
*/
Params configUpdate;
/**
- * Info buffers.
+ * List of info buffers.
*/
vec<InfoBuffer> infoBuffers;
};
/**
- * Struct for
+ * In/out structure containing some instructions for and results from output
+ * processing.
+ *
+ * This is a part of @ref Work. One `Worklet` corresponds to one output
+ * @ref FrameData. The client must construct an original `Worklet` object inside
+ * a @ref Work object for each expected output before calling
+ * IComponent::queue().
*/
struct Worklet {
/**
- * List of Params describing tunings.
+ * Component id. (Input)
+ *
+ * This is used only when tunneling is enabled.
+ *
+ * When used, this must match the return value from IConfigurable::getId().
*/
- vec<Params> tunings;
+ uint32_t componentId;
/**
- * List of failures.
+ * List of C2Param objects describing tunings to be applied before
+ * processing this `Worklet`. (Input)
+ */
+ Params tunings;
+
+ /**
+ * List of failures. (Output)
*/
vec<SettingResult> failures;
/**
- * Output frame data.
+ * Output frame data. (Output)
*/
FrameData output;
-
- /* Note: Component id is not necessary as tunneling is not supported. */
};
/**
- * This structure holds information about a single work item. It must be passed
- * by the client to the component.
+ * A collection of input data to and output data from the component.
+ *
+ * A `Work` object holds information about a single work item. It is created by
+ * the client and passed to the component via IComponent::queue(). The component
+ * has two ways of returning a `Work` object to the client:
+ * 1. If the queued `Work` object has been successfully processed,
+ * IComponentListener::onWorkDone() shall be called to notify the listener,
+ * and the output shall be included in the returned `Work` object.
+ * 2. If the client calls IComponent::flush(), a `Work` object that has not
+ * been processed shall be returned.
+ *
+ * `Work` is a part of @ref WorkBundle.
*/
struct Work {
/**
- * FrameData for the input. Indices of Blocks inside #input refer to
- * BaseBlocks in the member `blocks` of the containing `WorkBundle`.
+ * Additional work chain info not part of this work.
+ */
+ Params chainInfo;
+
+ /**
+ * @ref FrameData for the input.
*/
FrameData input;
+
/**
- * Worklet. Indices of Blocks inside `worklet.output` refer to
- * BaseBlocks in the member `blocks` of the containing `WorkBundle`.
+ * The chain of `Worklet`s.
+ *
+ * The length of #worklets is 1 when tunneling is not enabled.
+ *
+ * If #worklets has more than a single element, the tunnels between
+ * successive components of the work chain must have been successfully
+ * pre-registered at the time that the `Work` is submitted. Allocating the
+ * output buffers in the `Worklet`s is the responsibility of each component
+ * in the chain.
+ *
+ * Upon `Work` submission, #worklets must be an appropriately sized vector
+ * containing `Worklet`s with @ref Worklet.hasOutput set to `false`. After a
+ * successful processing, all but the final `Worklet` in the returned
+ * #worklets must have @ref Worklet.hasOutput set to `false`.
*/
- Worklet worklet;
+ vec<Worklet> worklets;
+
/**
- * Whether the worklet was processed or not.
+ * The number of `Worklet`s successfully processed in this chain.
+ *
+ * This must be initialized to 0 by the client when the `Work` is submitted,
+ * and it must contain the number of `Worklet`s that were successfully
+ * processed when the `Work` is returned to the client.
+ *
+ * #workletsProcessed cannot exceed the length of #worklets. If
+ * #workletsProcessed is smaller than the length of #worklets, #result
+ * cannot be `OK`.
*/
- bool workletProcessed;
+ uint32_t workletsProcessed;
+
+ /**
+ * The final outcome of the `Work` (corresponding to #workletsProcessed).
+ *
+ * The value of @ref Status.OK implies that all `Worklet`s have been
+ * successfully processed.
+ */
Status result;
};
/**
- * This structure holds a list of Work objects and a list of BaseBlocks.
+ * List of `Work` objects.
+ *
+ * `WorkBundle` is used in IComponent::queue(), IComponent::flush() and
+ * IComponentListener::onWorkDone(). A `WorkBundle` object consists of a list of
+ * `Work` objects and a list of `BaseBlock` objects. Bundling multiple `Work`
+ * objects together provides two benefits:
+ * 1. Batching of `Work` objects can reduce the number of IPC calls.
+ * 2. If multiple `Work` objects contain `Block`s that refer to the same
+ * `BaseBlock`, the number of `BaseBlock`s that is sent between processes
+ * is also reduced.
+ *
+ * @note `WorkBundle` is the HIDL counterpart of the vector of `C2Work` in the
+ * Codec 2.0 standard. The presence of #baseBlocks helps with minimizing the
+ * data transferred over an IPC.
*/
struct WorkBundle {
/**
@@ -581,27 +780,48 @@
};
/**
- * This structure describes a query for supported values of a field. This is
- * used as input to IConfigurable::queryFieldSupportedValues().
+ * Query information for supported values of a field. This is used as input to
+ * IConfigurable::querySupportedValues().
*/
struct FieldSupportedValuesQuery {
+ /**
+ * Identity of the field to query.
+ */
+ ParamField field;
+
enum Type : uint32_t {
- /** Query all possible values regardless of other settings */
+ /** Query all possible values regardless of other settings. */
POSSIBLE,
- /** Query currently possible values given dependent settings */
+ /** Query currently possible values given dependent settings. */
CURRENT,
};
-
- ParamField field;
+ /**
+ * Type of the query. See #Type for more information.
+ */
Type type;
};
/**
* This structure is used to hold the result from
- * IConfigurable::queryFieldSupportedValues().
+ * IConfigurable::querySupportedValues().
*/
struct FieldSupportedValuesQueryResult {
+ /**
+ * Result of the query. Possible values are
+ * - `OK`: The query was successful.
+ * - `BAD_STATE`: The query was requested when the `IConfigurable` instance
+ * was in a bad state.
+ * - `BAD_INDEX`: The requested field was not recognized.
+ * - `TIMED_OUT`: The query could not be completed in a timely manner.
+ * - `BLOCKING`: The query must block, but the parameter `mayBlock` in the
+ * call to `querySupportedValues()` was `false`.
+ * - `CORRUPTED`: Some unknown error occurred.
+ */
Status status;
+
+ /**
+ * Supported values. This is meaningful only when #status is `OK`.
+ */
FieldSupportedValues values;
};
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
index 2e13854..0724c09 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
@@ -548,7 +548,7 @@
std::cout << "[ ] Early termination of test because vendor service cannot "
"prepare model that it does not support."
<< std::endl;
- GTEST_SKIP();
+ return;
}
EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
ASSERT_NE(nullptr, preparedModel->get());
@@ -561,6 +561,9 @@
V1_2::Model model = create_model();
sp<V1_2::IPreparedModel> preparedModel = nullptr;
PrepareModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) {
+ GTEST_SKIP();
+ }
EvaluatePreparedModel(preparedModel, is_ignored, examples,
model.relaxComputationFloat32toFloat16, testDynamicOutputShape);
}
diff --git a/neuralnetworks/1.2/Android.bp b/neuralnetworks/1.2/Android.bp
index 9057b94..0642dce 100644
--- a/neuralnetworks/1.2/Android.bp
+++ b/neuralnetworks/1.2/Android.bp
@@ -22,9 +22,11 @@
"android.hidl.safe_union@1.0",
],
types: [
+ "Constant",
"DeviceType",
"FmqRequestDatum",
"FmqResultDatum",
+ "MeasureTiming",
"Model",
"Operand",
"OperandType",
@@ -34,6 +36,7 @@
"OperationTypeRange",
"OutputShape",
"SymmPerChannelQuantParams",
+ "Timing",
],
gen_java: false,
}
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 2e48ba0..3bddbe8 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -38,6 +38,8 @@
*
* Values of this operand type are either true or false. A zero value
* represents false; any other value represents true.
+ *
+ * Available since API level 29.
*/
BOOL = 6,
/**
@@ -48,41 +50,49 @@
* realValue = integerValue * scale.
*
* scale is a 32 bit floating point with value greater than zero.
+ *
+ * Available since API level 29.
*/
TENSOR_QUANT16_SYMM = 7,
- /** A tensor of IEEE 754 16 bit floating point values. */
+ /**
+ * A tensor of IEEE 754 16 bit floating point values.
+ *
+ * Available since API level 29.
+ */
TENSOR_FLOAT16 = 8,
/**
* A tensor of 8 bit boolean values.
*
* Values of this operand type are either true or false. A zero value
* represents false; any other value represents true.
+ *
+ * Available since API level 29.
*/
TENSOR_BOOL8 = 9,
- /** An IEEE 754 16 bit floating point scalar value. */
+ /**
+ * An IEEE 754 16 bit floating point scalar value.
+ *
+ * Available since API level 29.
+ */
FLOAT16 = 10,
/**
* A tensor of 8 bit signed integers that represent real numbers.
*
- * This tensor is associated with additional fields that are
- * used to convert the 8 bit signed integer to the real value and vice versa.
+ * This tensor is associated with additional fields that can
+ * be used to convert the 8 bit signed integer to the real value and vice versa.
* These fields are:
* - channelDim: a 32 bit unsigned integer indicating channel dimension.
* - scales: an array of positive 32 bit floating point values.
* The size of the scales array must be equal to dimensions[channelDim].
- * These fields are located inside Operand's extraParams union, inside the
- * SymmPerChannelQuantParams struct.
*
- * An Operand of this type must use the 'channelQuant' variant of its
- * extraParams field.
+ * The channel dimension of this tensor must not be unknown (dimensions[channelDim] != 0).
*
- * The channel dimension of this tensor must be known, i.e.
- * dimensions[channelDim] must be non-zero.
- *
- * The formula for real values:
+ * The formula is:
* realValue[..., C, ...] =
* integerValue[..., C, ...] * scales[C]
* where C is an index in the Channel dimension.
+ *
+ * Available since API level 29.
*/
TENSOR_QUANT8_SYMM_PER_CHANNEL = 11,
/**
@@ -95,6 +105,8 @@
*
* The formula is:
* real_value = (integer_value - zeroPoint) * scale.
+ *
+ * Available since API level 29.
*/
TENSOR_QUANT16_ASYMM = 12,
/**
@@ -105,8 +117,24 @@
* realValue = integerValue * scale.
*
* scale is a 32 bit floating point with value greater than zero.
+ *
+ * Available since API level 29.
*/
TENSOR_QUANT8_SYMM = 13,
+ /*
+ * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
+ * OEM operation and data types.
+ *
+ * OEM specific scalar value.
+ * OEM = 10000,
+ */
+ /*
+ * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
+ * OEM operation and data types.
+ *
+ * A tensor of OEM specific values.
+ * TENSOR_OEM_BYTE = 10001,
+ */
/* ADDING A NEW FUNDAMENTAL TYPE REQUIRES UPDATING THE VALUE OF
* OperandTypeRange::FUNDAMENTAL_MAX.
*/
@@ -132,64 +160,4295 @@
*
* The type of an operation in a model.
*/
-enum OperationType : @1.1::OperationType {
- // TODO(b/116445845): Sync docs when all ops are implemented.
+enum OperationType : int32_t {
+ /**
+ * Adds two tensors, element-wise.
+ *
+ * Takes two input tensors of identical {@link OperandType} and compatible
+ * dimensions. The output is the sum of both input tensors, optionally
+ * modified by an activation function.
+ *
+ * Two dimensions are compatible when:
+ * 1. they are equal, or
+ * 2. one of them is 1
+ *
+ * The size of the output is the maximum size along each dimension of the
+ * input operands. It starts with the trailing dimensions, and works its
+ * way forward.
+ *
+ * Example:
+ *
+ * input1.dimension = {4, 1, 2}
+ * input2.dimension = {5, 4, 3, 1}
+ * output.dimension = {5, 4, 3, 2}
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: A tensor.
+ * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+ * as input0.
+ * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ *
+ * Outputs:
+ * * 0: The sum, a tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 27.
+ */
+ ADD = @1.1::OperationType:ADD,
+
+ /**
+ * Performs a 2-D average pooling operation.
+ *
+ * The output dimensions are functions of the filter dimensions, stride, and
+ * padding.
+ *
+ * The values in the output tensor are computed as:
+ *
+ * output[b, i, j, channel] =
+ * sum_{di, dj}(
+ * input[b, strides[1] * i + di, strides[2] * j + dj, channel]
+ * ) / sum(1)
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Both explicit padding and implicit padding are supported.
+ *
+ * Inputs (explicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+ * the input.
+ * * 1: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the left, in the ‘width’ dimension.
+ * * 2: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the right, in the ‘width’ dimension.
+ * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the top, in the ‘height’ dimension.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the bottom, in the ‘height’ dimension.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 7: An {@link OperandType::INT32} scalar, specifying the filter
+ * width.
+ * * 8: An {@link OperandType::INT32} scalar, specifying the filter
+ * height.
+ * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ *
+ * Inputs (implicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+ * the input.
+ * * 1: An {@link OperandType::INT32} scalar, specifying the implicit
+ * padding scheme, has to be one of the
+ * following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+ * * 2: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 3: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the filter
+ * width.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the filter
+ * height.
+ * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: The output 4-D tensor, of shape
+ * [batches, out_height, out_width, depth].
+ *
+ * Available since API level 27.
+ */
+ AVERAGE_POOL_2D = @1.1::OperationType:AVERAGE_POOL_2D,
+
+ /**
+ * Concatenates the input tensors along the given dimension.
+ *
+ * The input tensors must have identical {@link OperandType} and the same
+ * dimensions except the dimension along the concatenation axis.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM} (full support since API
+ * level 29, see the input section)
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0 ~ n-1: The list of n input tensors, of shape
+ * [D0, D1, ..., Daxis(i), ..., Dm].
+ * Before API level 29, all input tensors of
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * must have the same scale and zeroPoint as the output tensor.
+ * * n: An {@link OperandType::INT32} scalar, specifying the
+ * concatenation axis.
+ *
+ * Outputs:
+ * * 0: The output, a tensor of the same {@link OperandType} as the input
+ * tensors. The output shape is [D0, D1, ..., sum(Daxis(i)), ..., Dm].
+ *
+ * Available since API level 27.
+ */
+ CONCATENATION = @1.1::OperationType:CONCATENATION,
+
+ /**
+ * Performs an 2-D convolution operation.
+ *
+ * The CONV_2D op sweeps a 2-D filter that can mix channels together over a
+ * batch of images, applying the filter to each window of each image of the
+ * appropriate size.
+ *
+ * The output dimensions are functions of the filter dimensions, stride, and
+ * padding.
+ *
+ * The values in the output tensor are computed as:
+ *
+ * output[b, i, j, channel] =
+ * sum_{di, dj, k} (
+ * input[b, strides[1] * i + di, strides[2] * j + dj, k] *
+ * filter[channel, di, dj, k]
+ * ) + bias[channel]
+ *
+ * Supported tensor {@link OperandType} configurations:
+ * * 32 bit Floating point :
+ * * * {@link OperandType::TENSOR_FLOAT32} for input, filter, output, and bias.
+ *
+ * * Quantized:
+ * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, filter, and output.
+ * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+ * * * input.scale * filter.scale).
+ *
+ * Available since API level 29:
+ * * Quantized with symetric per channel quantization for the filter:
+ * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, and output.
+ * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+ * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+ * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+ *
+ * * 16 bit Floating point:
+ * * {@link OperandType::TENSOR_FLOAT16} for input, filter, output, and bias.
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Both explicit padding and implicit padding are supported.
+ *
+ * Inputs (explicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+ * specifying the input.
+ * * 1: A 4-D tensor, of shape
+ * [depth_out, filter_height, filter_width, depth_in], specifying the
+ * filter. For tensor of type
+ * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} the channel
+ * dimension (extraParams.channelQuant.channelDim) must be set to 0.
+ * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+ * tensor of type {@link OperandType::TENSOR_FLOAT32} or
+ * {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+ * type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+ * of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+ * of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+ * should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+ * 0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+ * bias_scale[i] = input_scale * filter_scale[i].
+ * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the left, in the ‘width’ dimension.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the right, in the ‘width’ dimension.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the top, in the ‘height’ dimension.
+ * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the bottom, in the ‘height’ dimension.
+ * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ * * 11: An optional {@link OperandType::INT32} scalar, specifying the dilation
+ * factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+ * cells between each filter element on width dimension. If this input is set,
+ * input 12 (dilation factor for height) must be specified as well.
+ * Available since API level 29.
+ * * 12: An optional {@link OperandType::INT32} scalar, specifying the dilation
+ * factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+ * cells between each filter element on height dimension. If this input is set,
+ * input 11 (dilation factor for width) must be specified as well.
+ * Available since API level 29.
+ *
+ * Inputs (implicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+ * specifying the input.
+ * * 1: A 4-D tensor, of shape
+ * [depth_out, filter_height, filter_width, depth_in], specifying the
+ * filter. For tensor of type
+ * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} the channel
+ * dimension (extraParams.channelQuant.channelDim) must be set to 0.
+ * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+ * tensor of type {@link OperandType::TENSOR_FLOAT32} or
+ * {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+ * type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+ * of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+ * of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+ * should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+ * 0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+ * bias_scale[i] = input_scale * filter_scale[i].
+ * * 3: An {@link OperandType::INT32} scalar, specifying the implicit
+ * padding scheme, has to be one of the
+ * following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ * * 8: An optional {@link OperandType::INT32} scalar, specifying the dilation
+ * factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+ * cells between each filter element on width dimension. If this input is set,
+ * input 9 (dilation factor for height) must be specified as well.
+ * Available since API level 29.
+ * * 9: An optional {@link OperandType::INT32} scalar, specifying the dilation
+ * factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+ * cells between each filter element on height dimension. If this input is set,
+ * input 8 (dilation factor for width) must be specified as well.
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: The output 4-D tensor, of shape
+ * [batches, out_height, out_width, depth_out]. For output tensor of
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
+ * must be satisfied: output_scale > input_scale * filter_scale (for
+ * filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+ * this condition must be true for all filter scales).
+ *
+ * Available since API level 27.
+ */
+ CONV_2D = @1.1::OperationType:CONV_2D,
+
+ /**
+ * Performs a depthwise 2-D convolution operation.
+ *
+ * Given an input tensor of shape [batches, height, width, depth_in] and a
+ * filter tensor of shape [1, filter_height, filter_width, depth_out]
+ * containing depth_out convolutional filters of depth 1, DEPTHWISE_CONV
+ * applies a different filter to each input channel (expanding from 1
+ * channel to channel_multiplier channels for each), then concatenates the
+ * results together.
+ *
+ * The output has depth_out = depth_in * depth_multiplier channels.
+ * The output dimensions are functions of the filter dimensions, stride, and
+ * padding.
+ *
+ * The values in the output tensor are computed as:
+ *
+ * output[b, i, j, k * channel_multiplier + q] =
+ * sum_{di, dj} (
+ * input[b, strides[1] * i + di, strides[2] * j + dj, k] *
+ * filter[1, di, dj, k * channel_multiplier + q]
+ * ) + bias[k * channel_multiplier + q]
+ *
+ * Supported tensor {@link OperandType} configurations:
+ * * 32 bit Floating point :
+ * * * {@link OperandType::TENSOR_FLOAT32} for input, filter, output, and bias.
+ *
+ * * Quantized:
+ * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, filter, and output.
+ * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+ * * * input.scale * filter.scale).
+ *
+ * Available since API level 29:
+ * * Quantized with symetric per channel quantization for the filter:
+ * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, and output.
+ * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+ * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+ * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Both explicit padding and implicit padding are supported.
+ *
+ * Inputs (explicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+ * specifying the input.
+ * * 1: A 4-D tensor, of shape [1, filter_height, filter_width, depth_out],
+ * specifying the filter. For tensor of type
+ * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} the channel
+ * dimension (extraParams.channelQuant.channelDim) must be set to 3.
+ * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+ * tensor of type {@link OperandType::TENSOR_FLOAT32} or
+ * {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+ * type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+ * of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+ * of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+ * should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+ * 0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+ * bias_scale[i] = input_scale * filter_scale[i].
+ * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the left, in the ‘width’ dimension.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the right, in the ‘width’ dimension.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the top, in the ‘height’ dimension.
+ * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the bottom, in the ‘height’ dimension.
+ * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 9: An {@link OperandType::INT32} scalar, specifying the depthwise
+ * multiplier.
+ * * 10: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 11: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ * * 12: An optional {@link OperandType::INT32} scalar, specifying the dilation
+ * factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+ * cells between each filter element on width dimension. If this input is set,
+ * input 13 (dilation factor for height) must be specified as well.
+ * Available since API level 29.
+ * * 13: An optional {@link OperandType::INT32} scalar, specifying the dilation
+ * factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+ * cells between each filter element on height dimension. If this input is set,
+ * input 12 (dilation factor for width) must be specified as well.
+ * Available since API level 29.
+ *
+ * Inputs (implicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+ * specifying the input.
+ * * 1: A 4-D tensor, of shape [1, filter_height, filter_width, depth_out],
+ * specifying the filter.
+ * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+ * tensor of type {@link OperandType::TENSOR_FLOAT32} or
+ * {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+ * type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+ * of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+ * of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+ * should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+ * 0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+ * bias_scale[i] = input_scale * filter_scale[i].
+ * * 3: An {@link OperandType::INT32} scalar, specifying the implicit
+ * padding scheme, has to be one of the
+ * following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 6: An {@link OperandType::INT32} scalar, specifying the depthwise
+ * multiplier.
+ * * 7: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 8: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ * * 9: An optional {@link OperandType::INT32} scalar, specifying the dilation
+ * factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+ * cells between each filter element on width dimension. If this input is set,
+ * input 10 (dilation factor for height) must be specified as well.
+ * Available since API level 29.
+ * * 10: An optional {@link OperandType::INT32} scalar, specifying the dilation
+ * factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+ * cells between each filter element on height dimension. If this input is set,
+ * input 9 (dilation factor for width) must be specified as well.
+ * Available since API level 29.
+
+ *
+ * Outputs:
+ * * 0: The output 4-D tensor, of shape
+ * [batches, out_height, out_width, depth_out]. For output tensor of
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
+ * must be satisfied: output_scale > input_scale * filter_scale (for
+ * filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+ * this condition must be true for all filter scales).
+ *
+ * Available since API level 27.
+ */
+ DEPTHWISE_CONV_2D = @1.1::OperationType:DEPTHWISE_CONV_2D,
+
+ /**
+ * Rearranges data from depth into blocks of spatial data.
+ *
+ * More specifically, this op outputs a copy of the input tensor where
+ * values from the depth dimension are moved in spatial blocks to the height
+ * and width dimensions. The value block_size indicates the input block size
+ * and how the data is moved.
+ *
+ * Chunks of data of size block_size * block_size from depth are rearranged
+ * into non-overlapping blocks of size block_size x block_size.
+ *
+ * The width of the output tensor is input_depth * block_size, whereas the
+ * height is input_height * block_size. The depth of the input tensor must
+ * be divisible by block_size * block_size
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Inputs:
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+ * specifying the input.
+ * * 1: An {@link OperandType::INT32} scalar, specifying the block_size.
+ * block_size must be >=1 and block_size * block_size must be a divisor
+ * of the input depth.
+ * * 2: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: The output 4-D tensor, of shape [batch, height*block_size,
+ * width*block_size, depth/(block_size*block_size)].
+ *
+ * Available since API level 27.
+ */
+ DEPTH_TO_SPACE = @1.1::OperationType:DEPTH_TO_SPACE,
+
+ /**
+ * Dequantizes the input tensor.
+ *
+ * The formula is:
+ *
+ * output = (input - zeroPoint) * scale.
+ *
+ * Supported input tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * * {@link OperandType::TENSOR_QUANT8_SYMM} (since API level 29)
+ * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} (since API level 29)
+ *
+ * Supported output tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}.
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: A tensor with the same shape as input0.
+ *
+ * Available since API level 27.
+ */
+ DEQUANTIZE = @1.1::OperationType:DEQUANTIZE,
+
+ /**
+ * Looks up sub-tensors in the input tensor.
+ *
+ * This operator takes for input a tensor of values (Values) and
+ * a one-dimensional tensor of selection indices (Lookups).
+ * The output tensor is the concatenation of sub-tensors of Values as
+ * selected by Lookups.
+ *
+ * Think of Values as being sliced along its first dimension:
+ * The entries in Lookups select which slices are concatenated together
+ * to create the output tensor.
+ *
+ * For example, if Values has shape of [40, 200, 300] and
+ * Lookups has shape of [3], all three values found in Lookups are
+ * expected to be between 0 and 39. The resulting tensor must
+ * have shape of [3, 200, 300].
+ *
+ * If a value in Lookups is out of bounds, the operation must fail
+ * and an error must be reported.
+ *
+ * Supported value tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported value tensor rank: from 2
+ *
+ * Inputs:
+ * * 0: Lookups. A 1-D tensor of {@link OperandType::TENSOR_INT32}.
+ * The values are indices into the first dimension of Values.
+ * * 1: Values. An n-D tensor, where n >= 2, from which sub-tensors are
+ * extracted.
+ *
+ * Output:
+ * * 0: A n-D tensor with the same rank and shape as the Values
+ * tensor, except for the first dimension which has the same size
+ * as Lookups' only dimension.
+ *
+ * Available since API level 27.
+ */
+ EMBEDDING_LOOKUP = @1.1::OperationType:EMBEDDING_LOOKUP,
+
+ /**
+ * Computes element-wise floor() on the input tensor.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: The output tensor, of the same {@link OperandType} and dimensions as
+ * the input tensor.
+ *
+ * Available since API level 27.
+ */
+ FLOOR = @1.1::OperationType:FLOOR,
+
+ /**
+ * Denotes a fully (densely) connected layer, which connects all elements
+ * in the input tensor with each element in the output tensor.
+ *
+ * This layer implements the operation:
+ *
+ * outputs = activation(inputs * weights’ + bias)
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4.
+ *
+ * Inputs:
+ * * 0: A tensor of at least rank 2, specifying the input. If rank is
+ * greater than 2, then it gets flattened to a 2-D Tensor. The
+ * (flattened) 2-D Tensor is reshaped (if necessary) to
+ * [batch_size, input_size], where "input_size" corresponds to the
+ * number of inputs to the layer, matching the second dimension of
+ * weights, and "batch_size" is calculated by dividing the number of
+ * elements by "input_size".
+ * * 1: A 2-D tensor, specifying the weights, of shape
+ * [num_units, input_size], where "num_units" corresponds to the number
+ * of output nodes.
+ * * 2: A 1-D tensor, of shape [num_units], specifying the bias. For input
+ * tensor of {@link OperandType::TENSOR_FLOAT32}, the bias should
+ * also be of {@link OperandType::TENSOR_FLOAT32}. For input tensor
+ * of {@link OperandType::TENSOR_QUANT8_ASYMM}, the bias should be
+ * of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0 and
+ * bias_scale == input_scale * filter_scale.
+ * * 3: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ *
+ * Outputs:
+ * * 0: The output tensor, of shape [batch_size, num_units]. For output
+ * tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}, the following
+ * condition must be satisfied:
+ * output_scale > input_scale * filter_scale.
+ *
+ * Available since API level 27.
+ */
+ FULLY_CONNECTED = @1.1::OperationType:FULLY_CONNECTED,
+
+ /**
+ * Looks up sub-tensors in the input tensor using a key-value map.
+ *
+ * This operator takes for input a tensor of values (Values),
+ * a one-dimensional tensor of selection values (Lookups) and
+ * a one-dimensional tensor that maps these values to Values
+ * indexes. The output tensor is the concatenation of sub-tensors of
+ * Values as selected by Lookups via Keys.
+ *
+ * Think of Values as being sliced along its outer-most dimension.
+ * The output is a concatenation of selected slices, with one slice
+ * for each entry of Lookups. The slice selected is the one at the
+ * same index as the Maps entry that matches the value in Lookups.
+ *
+ * For a hit, the corresponding sub-tensor of Values is included
+ * in the Output tensor. For a miss, the corresponding sub-tensor in
+ * Output must have zero values.
+ *
+ * For example, if Values has shape of [40, 200, 300],
+ * Keys should have a shape of [40]. If Lookups tensor has shape
+ * of [3], three slices are being concatenated, so the resulting tensor
+ * must have the shape of [3, 200, 300]. If the first entry in Lookups
+ * has the value 123456, that value must be located in Keys tensor.
+ * If the sixth entry of Keys contains 123456, the sixth slice of Values
+ * must be selected. If no entry in Keys has 123456, a slice of zeroes
+ * must be concatenated.
+ *
+ * Supported value tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported value tensor rank: from 2
+ *
+ * Inputs:
+ * * 0: Lookups. A 1-D {@link OperandType::TENSOR_INT32} tensor with
+ * shape [ k ].
+ * * 1: Keys. A 1-D {@link OperandType::TENSOR_INT32} tensor with shape
+ * [ n ]; Keys and Values pair represent a map, i.e., the ith element
+ * in Keys (Keys[i]) is the key to select the ith sub-tensor in Values
+ * (Values[i]), where 0 <= i <= n-1. Keys tensor *MUST* be sorted in
+ * ascending order.
+ * * 2: Values. A tensor with shape of [ n, … ]; i.e., the first dimension
+ * must be n.
+ *
+ * Outputs:
+ * * 0: Output. A tensor with shape [ k …].
+ * * 1: Hits. A boolean tensor with shape [ k ] indicates whether the lookup
+ * hits (True) or not (False).
+ * Stored as {@link OperandType::TENSOR_QUANT8_ASYMM} with offset 0
+ * and scale 1.0f.
+ * A non-zero byte represents True, a hit. A zero indicates otherwise.
+ *
+ * Available since API level 27.
+ */
+ HASHTABLE_LOOKUP = @1.1::OperationType:HASHTABLE_LOOKUP,
+
+ /**
+ * Applies L2 normalization along the depth dimension.
+ *
+ * The values in the output tensor are computed as:
+ *
+ * output[batch, row, col, channel] =
+ * input[batch, row, col, channel] /
+ * sqrt(sum_{c} pow(input[batch, row, col, c], 2))
+ *
+ * For input tensor with rank less than 4, independently normalizes each
+ * 1-D slice along dimension dim.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: up to 4
+ * Tensors with rank less than 4 are only supported since API level 29.
+ *
+ * Inputs:
+ * * 0: An n-D tensor, specifying the tensor to be normalized.
+ * * 1: An optional {@link OperandType::INT32} scalar, default to -1,
+ * specifying the dimension normalization would be performed on.
+ * Negative index is used to specify axis from the end (e.g. -1 for
+ * the last axis). Must be in the range [-n, n).
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} and same shape as input0.
+ *
+ * Available since API level 27.
+ */
+ L2_NORMALIZATION = @1.1::OperationType:L2_NORMALIZATION,
+
+ /**
+ * Performs an 2-D L2 pooling operation.
+ *
+ * The output dimensions are functions of the filter dimensions, stride, and
+ * padding.
+ *
+ * The values in the output tensor are computed as:
+ *
+ * output[b, i, j, c] =
+ * sqrt(sum_{di, dj} pow(input[b, strides[1] * i + di, strides[2] * j + dj, c], 2) /
+ * sum(1))
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Both explicit padding and implicit padding are supported.
+ *
+ * Inputs (explicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+ * the input.
+ * * 1: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the left, in the ‘width’ dimension.
+ * * 2: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the right, in the ‘width’ dimension.
+ * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the top, in the ‘height’ dimension.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the bottom, in the ‘height’ dimension.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 7: An {@link OperandType::INT32} scalar, specifying the filter
+ * width.
+ * * 8: An {@link OperandType::INT32} scalar, specifying the filter
+ * height.
+ * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ *
+ * Inputs (implicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+ * the input.
+ * * 1: An {@link OperandType::INT32} scalar, specifying the implicit
+ * padding scheme, has to be one of the
+ * following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+ * * 2: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 3: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the filter
+ * width.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the filter
+ * height.
+ * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: The output 4-D tensor, of shape
+ * [batches, out_height, out_width, depth].
+ *
+ * Available since API level 27.
+ */
+ L2_POOL_2D = @1.1::OperationType:L2_POOL_2D,
+
+ /**
+ * Applies Local Response Normalization along the depth dimension.
+ *
+ * The 4-D input tensor is treated as a 3-D array of 1-D vectors (along the
+ * last dimension), and each vector is normalized independently. Within a
+ * given vector, each component is divided by the weighted, squared sum of
+ * inputs within depth_radius.
+ *
+ * The output is calculated using this formula:
+ *
+ * sqr_sum[a, b, c, d] = sum(
+ * pow(input[a, b, c, d - depth_radius : d + depth_radius + 1], 2))
+ * output = input / pow((bias + alpha * sqr_sum), beta)
+ *
+ * For input tensor with rank less than 4, independently normalizes each
+ * 1-D slice along specified dimension.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: up to 4
+ * Tensors with rank less than 4 are only supported since API level 29.
+ *
+ * Inputs:
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+ * the input.
+ * * 1: An {@link OperandType::INT32} scalar, specifying the radius of
+ * the normalization window.
+ * * 2: An {@link OperandType::FLOAT32} scalar, specifying the bias, must
+ * not be zero.
+ * * 3: An {@link OperandType::FLOAT32} scalar, specifying the scale
+ * factor, alpha.
+ * * 4: An {@link OperandType::FLOAT32} scalar, specifying the exponent,
+ * beta.
+ * * 5: An optional {@link OperandType::INT32} scalar, default to -1,
+ * specifying the dimension normalization would be performed on.
+ * Negative index is used to specify axis from the end (e.g. -1 for
+ * the last axis). Must be in the range [-n, n).
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 27.
+ */
+ LOCAL_RESPONSE_NORMALIZATION = @1.1::OperationType:LOCAL_RESPONSE_NORMALIZATION,
+
+ /**
+ * Computes sigmoid activation on the input tensor element-wise.
+ *
+ * The output is calculated using this formula:
+ *
+ * output = 1 / (1 + exp(-input))
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4.
+ *
+ * Inputs:
+ * * 0: A tensor, specifying the input.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ * For {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the scale must be 1.f / 256 and the zeroPoint must be 0.
+ *
+ * Available since API level 27.
+ */
+ LOGISTIC = @1.1::OperationType:LOGISTIC,
+
+ /**
+ * Projects an input to a bit vector via locality senstive hashing.
+ *
+ * Supported input tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported input tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: Hash functions. Dim.size == 2, DataType: Float.
+ * Tensor[0].Dim[0]: Number of hash functions.
+ * Tensor[0].Dim[1]: Number of projected output bits generated by each
+ * hash function.
+ * If the projection type is Sparse:
+ * Tensor[0].Dim[1] + ceil(log2(Tensor[0].Dim[0])) <= 32
+ *
+ * * 1: Input. Dim.size >= 1, no restriction on DataType.
+ * * 2: Weight. Optional. Dim.size == 1, DataType: Float.
+ * If not set, each input element is considered to have the same weight
+ * of 1.0.
+ * Tensor[1].Dim[0] == Tensor[2].Dim[0]
+ * * 3: Type:
+ * Sparse:
+ * Value LSHProjectionType_SPARSE(=3) (since API level 29).
+ * Computed bit vector is considered to be sparse.
+ * Each output element is an int32 made up of multiple bits
+ * computed from hash functions.
+ *
+ * NOTE: To avoid collisions across hash functions, an offset value
+ * of k * (1 << Tensor[0].Dim[1]) will be added to each signature,
+ * where k is the index of the hash function.
+ *
+ * Value LSHProjectionType_SPARSE_DEPRECATED(=1).
+ * Legacy behavior that does not include the offset value.
+ *
+ * Dense:
+ * Value LSHProjectionType_DENSE(=2).
+ * Computed bit vector is considered to be dense. Each output
+ * element represents a bit and can take the value of either
+ * 0 or 1.
+ *
+ * Outputs:
+ * * 0: If the projection type is Sparse:
+ * Output.Dim == { Tensor[0].Dim[0] }
+ * A tensor of int32 that represents hash signatures,
+ *
+ * If the projection type is Dense:
+ * Output.Dim == { Tensor[0].Dim[0] * Tensor[0].Dim[1] }
+ * A flattened tensor that represents projected bit vectors.
+ *
+ * Available since API level 27.
+ * The offset value for sparse projections was added in API level 29.
+ */
+ LSH_PROJECTION = @1.1::OperationType:LSH_PROJECTION,
+
+ /**
+ * Performs a single time step in a Long Short-Term Memory (LSTM) layer
+ *
+ * The LSTM operation is described by the following equations.
+ *
+ * \f{eqnarray*}{
+ * i_t =& \sigma(W_{xi}x_t+W_{hi}h_{t-1}+W_{ci}C_{t-1}+b_i) & \\
+ * f_t =& \sigma(W_{xf}x_t+W_{hf}h_{t-1}+W_{cf}C_{t-1}+b_f) & \\
+ * C_t =& clip(f_t \odot C_{t-1} + i_t \odot
+ * g(W_{xc}x_t+W_{hc}h_{t-1}+b_c),\ t_{cell}) & \\
+ * o_t =& \sigma(W_{xo}x_t+W_{ho}h_{t-1}+W_{co}C_t+b_o) & \\
+ * & & \\
+ * & clip(W_{proj}(o_t \odot g(C_t))+b_{proj},\ t_{proj})
+ * & if\ there\ is\ a\ projection; \\
+ * h_t =& & \\
+ * & o_t \odot g(C_t) & otherwise. \\
+ * \f}
+ * Where:
+ * * \f$x_t\f$ is the input,
+ * * \f$i_t\f$ is the input gate,
+ * * \f$f_t\f$ is the forget gate,
+ * * \f$C_t\f$ is the cell state,
+ * * \f$o_t\f$ is the output,
+ * * \f$h_t\f$ is the output state,
+ * * \f$\sigma\f$ is the logistic sigmoid function,
+ * * \f$g\f$ is the cell input and cell output activation function, usually
+ * \f$tahn\f$,
+ * * \f$W_{xi}\f$ is the input-to-input weight matrix,
+ * * \f$W_{hi}\f$ is the recurrent to input weight matrix,
+ * * \f$W_{ci}\f$ is the cell-to-input weight matrix,
+ * * \f$b_i\f$ is the input gate bias,
+ * * \f$W_{xf}\f$ is the input-to-forget weight matrix,
+ * * \f$W_{hf}\f$ is the recurrent-to-forget weight matrix,
+ * * \f$W_{cf}\f$ is the cell-to-forget weight matrix,
+ * * \f$b_f\f$ is the forget gate bias,
+ * * \f$W_{xc}\f$ is the input-to-cell weight matrix,
+ * * \f$W_{hc}\f$ is the recurrent-to-cell weight matrix,
+ * * \f$b_c\f$ is the cell bias,
+ * * \f$W_{xo}\f$ is the input-to-output weight matrix,
+ * * \f$W_{ho}\f$ is the recurrent-to-output weight matrix,
+ * * \f$W_{co}\f$ is the cell-to-output weight matrix,
+ * * \f$b_o\f$ is the output gate bias,
+ * * \f$W_{proj}\f$ is the projection weight matrix,
+ * * \f$b_{proj}\f$ is the projection bias,
+ * * \f$t_{cell}\f$ is the threshold for clipping the cell state, and
+ * * \f$t_{proj}\f$ is the threshold for clipping the projected output.
+ * * \f$\odot\f$ is the
+ * <a href="https://en.wikipedia.org/wiki/Hadamard_product_(matrices)">
+ * Hadamard product</a> that takes two matrices and produces another
+ * matrix, each element of which is the product of the corresponding
+ * elements of the input matrices.
+ *
+ * Since API level 29 LSTM supports layer normalization.
+ * In case layer normalization is used, the inputs to internal activation
+ * functions (sigmoid and \f$g\f$) are normalized, rescaled and recentered
+ * following an approach from section 3.1 from
+ * https://arxiv.org/pdf/1607.06450.pdf
+ *
+ * The operation has the following independently optional inputs:
+ * * The input-to-input weights (\f$W_{xi}\f$), recurrent-to-input weights
+ * (\f$W_{hi}\f$), cell-to-input (\f$W_{ci}\f$) weights, and input gate
+ * bias (\f$b_i\f$) either all have values, or none of them have values
+ * (i.e., all set to null). If they have no values, coupling of input and
+ * forget gates (CIFG) is used, in which case the input gate (\f$i_t\f$)
+ * is calculated using the following equation instead.
+ * \f{eqnarray*}{
+ * i_t = 1 - f_t
+ * \f}
+ * * The cell-to-forget weights (\f$W_{cf}\f$) and cell-to-output weights
+ * (\f$W_{co}\f$) either both have values or neither of them have values.
+ * If they have values, the peephole optimization is used. Additionally,
+ * if CIFG is not used, cell-to-input weights (\f$W_{ci}\f$) is also
+ * required to have values for peephole optimization.
+ * * The projection weights (\f$W_{proj}\f$) is required only for the
+ * recurrent projection layer, and should otherwise have no value.
+ * * The projection bias (\f$b_{proj}\f$) may (but not required to) have a
+ * value if the recurrent projection layer exists, and should otherwise
+ * have no value.
+ * * (API level >= 29) The four layer normalization weights either all have
+ * values or none of them have values. Layer normalization is used when
+ * values are present.
+ *
+ * References:
+ *
+ * The default non-peephole non-CIFG implementation is based on:
+ * http://www.bioinf.jku.at/publications/older/2604.pdf
+ * S. Hochreiter and J. Schmidhuber. "Long Short-Term Memory". Neural
+ * Computation, 9(8):1735-1780, 1997.
+ *
+ * The peephole implementation and projection layer is based on:
+ * https://research.google.com/pubs/archive/43905.pdf
+ * Hasim Sak, Andrew Senior, and Francoise Beaufays. "Long short-term memory
+ * recurrent neural network architectures for large scale acoustic
+ * modeling." INTERSPEECH, 2014.
+ * (However, the concept of peephole optimization was introduced in work
+ * prior to this paper.)
+ *
+ * The coupling of input and forget gate (CIFG) is based on:
+ * http://arxiv.org/pdf/1503.04069.pdf
+ * Greff et al. "LSTM: A Search Space Odyssey"
+ *
+ * The layer normalization is based on:
+ * https://arxiv.org/pdf/1607.06450.pdf
+ * Jimmy Ba et al. "Layer Normalization"
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * All input and output tensors must be of the same type.
+ *
+ * Inputs:
+ * * 0: The input (\f$x_t\f$).
+ * A 2-D tensor of shape [batch_size, input_size], where “batch_size”
+ * corresponds to the batching dimension, and “input_size” is the size
+ * of the input.
+ * * 1: The input-to-input weights (\f$W_{xi}\f$). Optional.
+ * A 2-D tensor of shape [num_units, input_size], where “num_units”
+ * corresponds to the number of cell units.
+ * * 2: The input-to-forget weights (\f$W_{xf}\f$).
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 3: The input-to-cell weights (\f$W_{xc}\f$).
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 4: The input-to-output weights (\f$W_{xo}\f$).
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 5: The recurrent-to-input weights (\f$W_{hi}\f$). Optional.
+ * A 2-D tensor of shape [num_units, output_size], where “output_size”
+ * corresponds to either the number of cell units (i.e., “num_units”),
+ * or the second dimension of the “projection_weights”, if defined.
+ * * 6: The recurrent-to-forget weights (\f$W_{hf}\f$).
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 7: The recurrent-to-cell weights (\f$W_{hc}\f$).
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 8: The recurrent-to-output weights (\f$W_{ho}\f$).
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 9: The cell-to-input weights (\f$W_{ci}\f$). Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 10:The cell-to-forget weights (\f$W_{cf}\f$). Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 11:The cell-to-output weights (\f$W_{co}\f$). Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 12:The input gate bias (\f$b_i\f$). Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 13:The forget gate bias (\f$b_f\f$).
+ * A 1-D tensor of shape [num_units].
+ * * 14:The cell bias (\f$b_c\f$).
+ * A 1-D tensor of shape [num_units].
+ * * 15:The output gate bias (\f$b_o\f$).
+ * A 1-D tensor of shape [num_units].
+ * * 16:The projection weights (\f$W_{proj}\f$). Optional.
+ * A 2-D tensor of shape [output_size, num_units].
+ * * 17:The projection bias (\f$b_{proj}\f$). Optional.
+ * A 1-D tensor of shape [output_size].
+ * * 18:The output state (in) (\f$h_{t-1}\f$).
+ * A 2-D tensor of shape [batch_size, output_size].
+ * * 19:The cell state (in) (\f$C_{t-1}\f$).
+ * A 2-D tensor of shape [batch_size, num_units].
+ * * 20:The activation function (\f$g\f$).
+ * A value indicating the activation function:
+ * <ul>
+ * <li>0: None;
+ * <li>1: Relu;
+ * <li>3: Relu6;
+ * <li>4: Tanh;
+ * <li>6: Sigmoid.
+ * </ul>
+ * * 21:The clipping threshold (\f$t_{cell}\f$) for the cell state, such
+ * that values are bound within [-cell_clip, cell_clip]. If set to 0.0
+ * then clipping is disabled.
+ * Until API level 29 this scalar must be of type {@link
+ * FLOAT32}. Since API level 29, if all the input
+ * tensors have type {@link OperandType::TENSOR_FLOAT32}, this
+ * scalar must be of the type {@link OperandType::FLOAT32},
+ * otherwise if all the input tensors have the type {@link
+ * TENSOR_FLOAT16}, this scalar must be of type {@link
+ * FLOAT16}.
+ * * 22:The clipping threshold (\f$t_{proj}\f$) for the output from the
+ * projection layer, such that values are bound within
+ * [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+ * Until API level 29 this scalar must be of type {@link
+ * FLOAT32}. Since API level 29, if all the input
+ * tensors have type {@link OperandType::TENSOR_FLOAT32}, this
+ * scalar must be of the type {@link OperandType::FLOAT32},
+ * otherwise if all the input tensors have the type {@link
+ * TENSOR_FLOAT16}, this scalar must be of type {@link
+ * FLOAT16}.
+ * Since API level 29 there are additional inputs to this op:
+ * * 23:The input layer normalization weights.
+ * A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+ * to activation at input gate.
+ * * 24:The forget layer normalization weights.
+ * A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+ * to activation at forget gate.
+ * * 25:The cell layer normalization weights.
+ * A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+ * to activation at cell gate.
+ * * 26:The output layer normalization weights.
+ * A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+ * to activation at output gate.
+ *
+ * Outputs:
+ * * 0: The scratch buffer.
+ * A 2-D tensor of shape [batch_size, num_units * 3] with CIFG, or
+ * [batch_size, num_units * 4] without CIFG.
+ * * 1: The output state (out) (\f$h_t\f$).
+ * A 2-D tensor of shape [batch_size, output_size].
+ * * 2: The cell state (out) (\f$C_t\f$).
+ * A 2-D tensor of shape [batch_size, num_units].
+ * * 3: The output (\f$o_t\f$).
+ * A 2-D tensor of shape [batch_size, output_size]. This is effectively
+ * the same as the current “output state (out)” value.
+ *
+ * Available since API level 27.
+ */
+ LSTM = @1.1::OperationType:LSTM,
+
+ /**
+ * Performs an 2-D max pooling operation.
+ *
+ * The output dimensions are functions of the filter dimensions, stride, and
+ * padding.
+ *
+ * The values in the output tensor are computed as:
+ *
+ * output[b, i, j, channel] =
+ * max_{di, dj} (
+ * input[b, strides[1] * i + di, strides[2] * j + dj, channel]
+ * )
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Both explicit padding and implicit padding are supported.
+ *
+ * Inputs (explicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+ * the input.
+ * * 1: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the left, in the ‘width’ dimension.
+ * * 2: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the right, in the ‘width’ dimension.
+ * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the top, in the ‘height’ dimension.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the bottom, in the ‘height’ dimension.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 7: An {@link OperandType::INT32} scalar, specifying the filter
+ * width.
+ * * 8: An {@link OperandType::INT32} scalar, specifying the filter
+ * height.
+ * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ *
+ * Inputs (implicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+ * the input.
+ * * 1: An {@link OperandType::INT32} scalar, specifying the implicit
+ * padding scheme, has to be one of the
+ * following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+ * * 2: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 3: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the filter
+ * width.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the filter
+ * height.
+ * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: The output 4-D tensor, of shape
+ * [batches, out_height, out_width, depth].
+ *
+ * Available since API level 27.
+ */
+ MAX_POOL_2D = @1.1::OperationType:MAX_POOL_2D,
+
+ /**
+ * Multiplies two tensors, element-wise.
+ *
+ * Takes two input tensors of identical {@link OperandType} and compatible
+ * dimensions. The output is the product of both input tensors, optionally
+ * modified by an activation function.
+ *
+ * Two dimensions are compatible when:
+ * 1. they are equal, or
+ * 2. one of them is 1
+ *
+ * The size of the resulting output is the maximum size along each dimension
+ * of the input operands. It starts with the trailing dimensions, and works
+ * its way forward.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: A tensor.
+ * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+ * as input0.
+ * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ *
+ * Outputs:
+ * * 0: The product, a tensor of the same {@link OperandType} as input0.
+ * For output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the following condition must be satisfied:
+ * output_scale > input1_scale * input2_scale.
+ *
+ * Available since API level 27.
+ */
+ MUL = @1.1::OperationType:MUL,
+
+ /**
+ * Computes rectified linear activation on the input tensor element-wise.
+ *
+ * The output is calculated using this formula:
+ *
+ * output = max(0, input)
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4.
+ *
+ * Inputs:
+ * * 0: A tensor, specifying the input.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 27.
+ */
+ RELU = @1.1::OperationType:RELU,
+
+ /**
+ * Computes rectified linear 1 activation on the input tensor element-wise.
+ *
+ * The output is calculated using this formula:
+ *
+ * output = min(1.f, max(-1.f, input))
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4.
+ *
+ * Inputs:
+ * * 0: A tensor, specifying the input.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 27.
+ */
+ RELU1 = @1.1::OperationType:RELU1,
+
+ /**
+ * Computes rectified linear 6 activation on the input tensor element-wise.
+ *
+ * The output is calculated using this formula:
+ *
+ * output = min(6, max(0, input))
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4.
+ *
+ * Inputs:
+ * * 0: A tensor, specifying the input.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 27.
+ */
+ RELU6 = @1.1::OperationType:RELU6,
+
+ /**
+ * Reshapes a tensor.
+ *
+ * Given tensor, this operation returns a tensor that has the same values as
+ * tensor, but with a newly specified shape.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4.
+ *
+ * Inputs:
+ * * 0: A tensor, specifying the tensor to be reshaped.
+ * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}, defining the
+ * shape of the output tensor. The number of elements implied by shape
+ * must be the same as the number of elements in the input tensor.
+ *
+ * Outputs:
+ * * 0: The output tensor, of shape specified by the input shape.
+ *
+ * Available since API level 27.
+ */
+ RESHAPE = @1.1::OperationType:RESHAPE,
+
+ /**
+ * Resizes images to given size using the bilinear interpretation.
+ *
+ * Resized images must be distorted if their output aspect ratio is not the
+ * same as input aspect ratio. The corner pixels of output may not be the
+ * same as corner pixels of input.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Inputs:
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+ * the input.
+ * * 1: An {@link OperandType::INT32} scalar, specifying the output
+ * height of the output tensor.
+ * * 2: An {@link OperandType::INT32} scalar, specifying the output
+ * width of the output tensor.
+ * * 3: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: The output 4-D tensor, of shape
+ * [batches, new_height, new_width, depth].
+ *
+ * Available since API level 27.
+ */
+ RESIZE_BILINEAR = @1.1::OperationType:RESIZE_BILINEAR,
+
+ /**
+ * A basic recurrent neural network layer.
+ *
+ * This layer implements the operation:
+ * outputs = state = activation(inputs * input_weights +
+ * state * recurrent_weights + bias)
+ *
+ * Where:
+ * * “input_weights” is a weight matrix that multiplies the inputs;
+ * * “recurrent_weights” is a weight matrix that multiplies the current
+ * “state” which itself is the output from the previous time step
+ * computation;
+ * * “bias” is a bias vector (added to each output vector in the batch);
+ * * “activation” is the function passed as the “fused_activation_function”
+ * argument (if not “NONE”).
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * The input tensors must all be the same type.
+ *
+ * Inputs:
+ * * 0: input.
+ * A 2-D tensor of shape [batch_size, input_size], where “batch_size”
+ * corresponds to the batching dimension, and “input_size” is the size
+ * of the input.
+ * * 1: weights.
+ * A 2-D tensor of shape [num_units, input_size], where “num_units”
+ * corresponds to the number of units.
+ * * 2: recurrent_weights.
+ * A 2-D tensor of shape [num_units, num_units], with columns
+ * corresponding to the weights from each unit.
+ * * 3: bias.
+ * A 1-D tensor of shape [num_units].
+ * * 4: hidden state (in).
+ * A 2-D tensor of shape [batch_size, num_units].
+ * * 5: fused_activation_function.
+ * An optional {@link FusedActivationFunc} value indicating the
+ * activation function. If “NONE” is specified then it results in a
+ * linear activation.
+ *
+ * Outputs:
+ * * 0: hidden state (out).
+ * A 2-D tensor of shape [batch_size, num_units].
+ *
+ * * 1: output.
+ * A 2-D tensor of shape [batch_size, num_units]. This is effectively
+ * the same as the current state value.
+ *
+ * Available since API level 27.
+ */
+ RNN = @1.1::OperationType:RNN,
+
+ /**
+ * Computes the softmax activation on the input tensor element-wise, per
+ * batch, by normalizing the input vector so the maximum coefficient is
+ * zero.
+ *
+ * The output is calculated using this formula:
+ *
+ * output[batch, i] =
+ * exp((input[batch, i] - max(input[batch, :])) * beta) /
+ * sum_{k}{exp((input[batch, k] - max(input[batch, :])) * beta)}
+ *
+ * For input tensor with rank other than 2, the activation will be applied
+ * independently on each 1-D slice along specified dimension.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4.
+ * Tensors with rank other than 2 or 4 are only supported since API level 29.
+ *
+ * Inputs:
+ * * 0: A 2-D or 4-D tensor, specifying the tensor to be reshaped.
+ * * 1: An {@link OperandType::FLOAT32} scalar, specifying the positive
+ * scaling factor for the exponent, beta.
+ * * 2: An optional {@link OperandType::INT32} scalar, default to -1,
+ * specifying the dimension the activation would be performed on.
+ * Negative index is used to specify axis from the end (e.g. -1 for
+ * the last axis). Must be in the range [-n, n).
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ * For {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the scale must be 1.f / 256 and the zeroPoint must be 0.
+ *
+ * Available since API level 27.
+ */
+ SOFTMAX = @1.1::OperationType:SOFTMAX,
+
+ /**
+ * Rearranges blocks of spatial data, into depth.
+ *
+ * More specifically, this op outputs a copy of the input tensor where
+ * values from the height and width dimensions are moved to the depth
+ * dimension. The value block_size indicates the input block size and how
+ * the data is moved.
+ *
+ * Chunks of data of size block_size * block_size from depth are rearranged
+ * into non-overlapping blocks of size block_size x block_size.
+ *
+ * The depth of the output tensor is input_depth * block_size * block_size.
+ * The input tensor's height and width must be divisible by block_size.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Inputs:
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+ * specifying the input.
+ * * 1: An {@link OperandType::INT32} scalar, specifying the block_size.
+ * block_size must be >=1 and block_size must be a divisor of both the
+ * input height and width.
+ * * 2: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: The output 4-D tensor, of shape [batches, height/block_size,
+ * width/block_size, depth_in*block_size*block_size].
+ *
+ * Available since API level 27.
+ */
+ SPACE_TO_DEPTH = @1.1::OperationType:SPACE_TO_DEPTH,
+
+ /**
+ * SVDF op is a kind of stateful layer derived from the notion that a
+ * densely connected layer that's processing a sequence of input frames can
+ * be approximated by using a singular value decomposition of each of its
+ * nodes. The implementation is based on:
+ *
+ * https://research.google.com/pubs/archive/43813.pdf
+ *
+ * P. Nakkiran, R. Alvarez, R. Prabhavalkar, C. Parada.
+ * “Compressing Deep Neural Networks using a Rank-Constrained Topology”.
+ * INTERSPEECH, 2015.
+ *
+ * It processes the incoming input using a 2-stage filtering mechanism:
+ * * stage 1 performs filtering on the "features" dimension, whose outputs
+ * get pushed into a memory of fixed-size memory_size.
+ * * stage 2 performs filtering on the "time" dimension of the memory_size
+ * memoized outputs of stage 1.
+ *
+ * Specifically, for rank 1, this layer implements the operation:
+ *
+ * memory = push(conv1d(inputs, weights_feature, feature_dim,
+ * "PADDING_VALID"));
+ * outputs = activation(memory * weights_time + bias);
+ *
+ * Where:
+ * * “weights_feature” is a weights matrix that processes the inputs (by
+ * convolving the input with every “feature filter”), and whose outputs
+ * get pushed, stacked in order, into the fixed-size “memory” (the oldest
+ * entry gets dropped);
+ * * “weights_time” is a weights matrix that processes the “memory” (by a
+ * batched matrix multiplication on the num_units);
+ * * “bias” is an optional bias vector (added to each output vector in the
+ * batch); and
+ * * “activation” is the function passed as the “fused_activation_function”
+ * argument (if not “NONE”).
+ *
+ * Each rank adds a dimension to the weights matrices by means of stacking
+ * the filters.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * All input tensors must be the same type.
+ *
+ * Inputs:
+ * * 0: input.
+ * A 2-D tensor of shape [batch_size, input_size], where “batch_size”
+ * corresponds to the batching dimension, and “input_size” is the size
+ * of the input.
+ * * 1: weights_feature.
+ * A 2-D tensor of shape [num_units, input_size], where “num_units”
+ * corresponds to the number of units.
+ * * 2: weights_time.
+ * A 2-D tensor of shape [num_units, memory_size], where “memory_size”
+ * corresponds to the fixed-size of the memory.
+ * * 3: bias.
+ * An optional 1-D tensor of shape [num_units].
+ * * 4: state (in).
+ * A 2-D tensor of shape [batch_size, (memory_size - 1) * num_units * rank].
+ * * 5: rank.
+ * The rank of the SVD approximation.
+ * * 6: fused_activation_function.
+ * An optional {@link FusedActivationFunc} value indicating the
+ * activation function. If “NONE” is specified then it results in a
+ * linear activation.
+ *
+ * Outputs:
+ * * 0: state (out).
+ * A 2-D tensor of the same {@link OperandType} as the inputs, with shape
+ * [batch_size, (memory_size - 1) * num_units * rank].
+ * * 1: output.
+ * A 2-D tensor of the same {@link OperandType} as the inputs, with shape
+ * [batch_size, num_units].
+ *
+ * Available since API level 27.
+ */
+ SVDF = @1.1::OperationType:SVDF,
+
+ /**
+ * Computes hyperbolic tangent of input tensor element-wise.
+ *
+ * The output is calculated using this formula:
+ *
+ * output = tanh(input)
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since API level 29)
+ *
+ * Supported tensor rank: up to 4.
+ *
+ * Inputs:
+ * * 0: A tensor, specifying the input.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ * For {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the scale must be 1.f / 128 and the zeroPoint must be 128.
+ *
+ * Available since API level 27.
+ */
+ TANH = @1.1::OperationType:TANH,
+
+ /**
+ * BatchToSpace for N-dimensional tensors.
+ *
+ * This operation reshapes the batch dimension (dimension 0) into M + 1
+ * dimensions of shape block_shape + [batch], interleaves these blocks back
+ * into the grid defined by the spatial dimensions [1, ..., M], to obtain a
+ * result with the same rank as the input.
+ *
+ * This is the reverse of SpaceToBatch.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Inputs:
+ * * 0: An n-D tensor, specifying the tensor to be reshaped
+ * * 1: A 1-D Tensor of {@link OperandType::TENSOR_INT32}, the block
+ * sizes for each spatial dimension of the input tensor. All values
+ * must be >= 1.
+ * * 2: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 28.
+ */
+ BATCH_TO_SPACE_ND = @1.1::OperationType:BATCH_TO_SPACE_ND,
+
+ /**
+ * Element-wise division of two tensors.
+ *
+ * Takes two input tensors of identical {@link OperandType} and compatible
+ * dimensions. The output is the result of dividing the first input tensor
+ * by the second, optionally modified by an activation function.
+ *
+ * Two dimensions are compatible when:
+ * 1. they are equal, or
+ * 2. one of them is 1
+ *
+ * The size of the output is the maximum size along each dimension of the
+ * input operands. It starts with the trailing dimensions, and works its way
+ * forward.
+ *
+ * Example:
+ * input1.dimension = {4, 1, 2}
+ * input2.dimension = {5, 4, 3, 1}
+ * output.dimension = {5, 4, 3, 2}
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor, specifying the first input.
+ * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+ * as input0.
+ * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 28.
+ */
+ DIV = @1.1::OperationType:DIV,
+
+ /**
+ * Computes the mean of elements across dimensions of a tensor.
+ *
+ * Reduces the input tensor along the given dimensions to reduce. Unless
+ * keep_dims is true, the rank of the tensor is reduced by 1 for each entry
+ * in axis. If keep_dims is true, the reduced dimensions are retained with
+ * length 1.
+ *
+ * If dimensions to reduce have no entries, all dimensions are reduced, and
+ * a tensor with a single element is returned.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: A tensor, specifying the input.
+ * * 1: A 1-D Tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+ * to reduce. If None (the default), reduces all dimensions. Must be in
+ * the range [-rank(input_tensor), rank(input_tensor)).
+ * * 2: An {@link OperandType::INT32} scalar, keep_dims. If positive,
+ * retains reduced dimensions with length 1.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 28.
+ */
+ MEAN = @1.1::OperationType:MEAN,
+
+ /**
+ * Pads a tensor with zeros.
+ *
+ * This operation pads a tensor according to the specified paddings.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor, specifying the tensor to be padded.
+ * * 1: A 2-D Tensor of {@link OperandType::TENSOR_INT32}, the paddings
+ * for each spatial dimension of the input tensor. The shape of the
+ * tensor must be {rank(input0), 2}.
+ * padding[i, 0] specifies the number of elements to be padded in the
+ * front of dimension i.
+ * padding[i, 1] specifies the number of elements to be padded after the
+ * end of dimension i.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0. The
+ * output tensor has the same rank as input0, and each
+ * dimension of the output tensor has the same size as the
+ * corresponding dimension of the input tensor plus the size
+ * of the padding:
+ * output0.dimension[i] =
+ * padding[i, 0] + input0.dimension[i] + padding[i, 1]
+ *
+ * Available since API level 28.
+ */
+ PAD = @1.1::OperationType:PAD,
+
+ /**
+ * SpaceToBatch for N-Dimensional tensors.
+ *
+ * This operation divides "spatial" dimensions [1, ..., M] of the input into
+ * a grid of blocks of shape block_shape, and interleaves these blocks with
+ * the "batch" dimension (0) such that in the output, the spatial dimensions
+ * [1, ..., M] correspond to the position within the grid, and the batch
+ * dimension combines both the position within a spatial block and the
+ * original batch position. Prior to division into blocks, the spatial
+ * dimensions of the input are optionally zero padded according to paddings.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Inputs:
+ * * 0: An n-D tensor, specifying the input.
+ * * 1: A 1-D Tensor of {@link OperandType::TENSOR_INT32}, the block
+ * sizes for each spatial dimension of the input tensor. All values
+ * must be >= 1.
+ * * 2: A 2-D Tensor of {@link OperandType::TENSOR_INT32}, the paddings
+ * for each spatial dimension of the input tensor. All values must be
+ * >= 0. The shape of the tensor must be {rank(input0), 2}.
+ * padding[i, 0] specifies the number of element to be padded in the
+ * front of dimension i.
+ * padding[i, 1] specifies the number of element to be padded after the
+ * end of dimension i.
+ * * 3: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ * Available since API level 29.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 28.
+ */
+ SPACE_TO_BATCH_ND = @1.1::OperationType:SPACE_TO_BATCH_ND,
+
+ /**
+ * Removes dimensions of size 1 from the shape of a tensor.
+ *
+ * Given a tensor input, this operation returns a tensor of the same
+ * {@link OperandType} with all dimensions of size 1 removed. If you don't
+ * want to remove all size 1 dimensions, you can remove specific size 1
+ * dimensions by specifying the axes (input1).
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor, the tensor to be squeezed.
+ * * 1: An optional 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+ * dimensions to squeeze. If specified only squeezes the dimensions
+ * listed. Otherwise, squeezes all dimensions. The dimension index
+ * starts at 0. An error must be reported if squeezing a dimension that
+ * is not 1.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0. Contains the
+ * same data as input, but has one or more dimensions of size 1
+ * removed.
+ *
+ * Available since API level 28.
+ */
+ SQUEEZE = @1.1::OperationType:SQUEEZE,
+
+ /**
+ * Extracts a strided slice of a tensor.
+ *
+ * Roughly speaking, this op extracts a slice of size (end - begin) / stride
+ * from the given input tensor. Starting at the location specified by begin
+ * the slice continues by adding stride to the index until all dimensions
+ * are not less than end. Note that a stride can be negative, which causes a
+ * reverse slice.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor, specifying the tensor to be sliced.
+ * * 1: begin, a 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+ * starts of the dimensions of the input tensor to be sliced. The
+ * length must be of rank(input0).
+ * * 2: end, a 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+ * ends of the dimensions of the input tensor to be sliced. The length
+ * must be of rank(input0).
+ * * 3: strides, a 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+ * strides of the dimensions of the input tensor to be sliced. The
+ * length must be of rank(input0). The entries must be non-zero.
+ * * 4: begin_mask, an {@link OperandType::INT32} scalar. If the ith bit
+ * of begin_mask is set, begin[i] is ignored and the fullest possible
+ * range in that dimension is used instead.
+ * * 5: end_mask, an {@link OperandType::INT32} scalar. If the ith bit of
+ * end_mask is set, end[i] is ignored and the fullest possible range in
+ * that dimension is used instead.
+ * * 6: shrink_axis_mask, an {@link OperandType::INT32} scalar. If the
+ * ith bit of shrink_axis_mask is set, the ith dimension specification
+ * shrinks the dimensionality by 1, taking on the value at index
+ * begin[i]. In this case, the ith specification must define a
+ * slice of size 1, e.g. begin[i] = x, end[i] = x + 1.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0 and rank (n - k),
+ * where k is the number of bits set in shrink_axis_mask.
+ *
+ * Available since API level 28.
+ */
+ STRIDED_SLICE = @1.1::OperationType:STRIDED_SLICE,
+
+ /**
+ * Element-wise subtraction of two tensors.
+ *
+ * Takes two input tensors of identical {@link OperandType} and compatible
+ * dimensions. The output is the result of subtracting the second input
+ * tensor from the first one, optionally modified by an activation function.
+ *
+ * Two dimensions are compatible when:
+ * 1. they are equal, or
+ * 2. one of them is 1
+ *
+ * The size of the output is the maximum size along each dimension of the
+ * input operands. It starts with the trailing dimensions, and works its way
+ * forward.
+ *
+ * Example:
+ * input1.dimension = {4, 1, 2}
+ * input2.dimension = {5, 4, 3, 1}
+ * output.dimension = {5, 4, 3, 2}
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since API level 29)
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor, specifying the first input.
+ * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+ * as input0.
+ * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 28.
+ */
+ SUB = @1.1::OperationType:SUB,
+
+ /**
+ * Transposes the input tensor, permuting the dimensions according to the
+ * perm tensor.
+ *
+ * The returned tensor's dimension i corresponds to the input dimension
+ * perm[i]. If perm is not given, it is set to (n-1...0), where n is the
+ * rank of the input tensor. Hence by default, this operation performs a
+ * regular matrix transpose on 2-D input Tensors.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor, specifying the tensor to be transposed.
+ * * 1: An optional 1-D Tensor of {@link OperandType::TENSOR_INT32},
+ * the permutation of the dimensions of the input tensor.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 28.
+ */
+ TRANSPOSE = @1.1::OperationType:TRANSPOSE,
+
+ /**
+ * Computes the absolute value of a tensor, element-wise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: from 1.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 29.
+ */
ABS = 38,
+
+ /**
+ * Returns the index of the largest element along an axis.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: An n-D tensor specifying the input. Must be non-empty.
+ * * 1: An {@link OperandType::INT32} scalar specifying the axis to
+ * reduce across. Negative index is used to specify axis from the
+ * end (e.g. -1 for the last axis). Must be in the range [-n, n).
+ *
+ * Outputs:
+ * * 0: An (n - 1)-D {@link OperandType::TENSOR_INT32} tensor.
+ *
+ * Available since API level 29.
+ */
+ // There is no underscore in ARG_MAX to avoid name conflict with
+ // the macro defined in libc/kernel/uapi/linux/limits.h.
ARGMAX = 39,
- ARGMIN = 40,
+
+ /**
+ * Returns the index of the smallest element along an axis.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: An n-D tensor specifying the input. Must be non-empty.
+ * * 1: An {@link OperandType::INT32} scalar specifying the axis to
+ * reduce across. Negative index is used to specify axis from the
+ * end (e.g. -1 for the last axis). Must be in the range [-n, n).
+ *
+ * Outputs:
+ * * 0: An (n - 1)-D {@link OperandType::TENSOR_INT32} tensor.
+ *
+ * Available since API level 29.
+ */
+ ARGMIN = 40, // See ARGMAX for naming discussion.
+
+ /**
+ * Transform axis-aligned bounding box proposals using bounding box deltas.
+ *
+ * Given the positions of bounding box proposals and the corresponding
+ * bounding box deltas for each class, return the refined bounding box
+ * regions. The resulting bounding boxes are cliped against the edges of
+ * the image.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Inputs:
+ * * 0: A 2-D Tensor of shape [num_rois, 4], specifying the locations of the
+ * bounding box proposals, each line with format [x1, y1, x2, y2].
+ * For tensor of type {@link OperandType::TENSOR_QUANT16_ASYMM},
+ * the zeroPoint must be 0 and the scale must be 0.125.
+ * * 1: A 2-D Tensor of shape [num_rois, num_classes * 4], specifying the
+ * bounding box delta for each region of interest and each class. The
+ * bounding box deltas are organized in the following order
+ * [dx, dy, dw, dh], where dx and dy is the relative correction factor
+ * for the center position of the bounding box with respect to the width
+ * and height, dw and dh is the log-scale relative correction factor
+ * for the width and height. For input0 of type
+ * {@link OperandType::TENSOR_QUANT16_ASYMM}, this tensor should be
+ * of {@link OperandType::TENSOR_QUANT8_ASYMM}.
+ * * 2: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+ * [batches], specifying the number of output boxes for each batch.
+ * * 3: A 2-D Tensor of shape [batches, 2], specifying the information of
+ * each image in the batch, each line with format
+ * [image_height, image_width].
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0, with shape
+ * [num_rois, num_classes * 4], specifying the coordinates of each
+ * output bounding box for each class, with format [x1, y1, x2, y2].
+ *
+ * Available since API level 29.
+ */
AXIS_ALIGNED_BBOX_TRANSFORM = 41,
+
+ /**
+ * Performs a forward LSTM on the input followed by a backward LSTM.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: 3, either time-major or batch-major.
+ *
+ * All input and output tensors must be of the same type.
+ *
+ *
+ * Inputs:
+ * * 0: The input.
+ * A 3-D tensor of shape:
+ * If time-major: [max_time, batch_size, output_size]
+ * If batch-major: [batch_size, max_time, output_size]
+ * where "max_time" is the number of timesteps (sequence length),
+ * "batch_size" corresponds to the batching dimension, and
+ * "input_size" is the size of the input.
+ * * 1: The forward input-to-input weights. Optional.
+ * A 2-D tensor of shape [num_units, input_size], where “num_units”
+ * corresponds to the number of cell units.
+ * * 2: The forward input-to-forget weights.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 3: The forward input-to-cell weights.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 4: The forward input-to-output weights.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 5: The forward recurrent-to-input weights. Optional.
+ * A 2-D tensor of shape [num_units, output_size], where “output_size”
+ * corresponds to either the number of cell units (i.e., “num_units”),
+ * or the second dimension of the “projection_weights”, if defined.
+ * * 6: The forward recurrent-to-forget weights.
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 7: The forward recurrent-to-cell weights.
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 8: The forward recurrent-to-output weights.
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 9: The forward cell-to-input weights. Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 10: The forward cell-to-forget weights. Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 11: The forward cell-to-output weights. Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 12: The forward input gate bias. Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 13: The forward forget gate bias.
+ * A 1-D tensor of shape [num_units].
+ * * 14: The forward cell gate bias.
+ * A 1-D tensor of shape [num_units].
+ * * 15: The forward output gate bias.
+ * A 1-D tensor of shape [num_units].
+ * * 16: The forward projection weights. Optional.
+ * A 2-D tensor of shape [output_size, num_units].
+ * * 17: The forward projection bias. Optional.
+ * A 1-D tensor of shape [output_size].
+ * * 18: The backward input-to-input weights. Optional.
+ * A 2-D tensor of shape [num_units, input_size], where “num_units”
+ * corresponds to the number of cell units.
+ * * 19: The backward input-to-forget weights.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 20: The backward input-to-cell weights.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 21: The backward input-to-output weights.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 22: The backward recurrent-to-input weights. Optional.
+ * A 2-D tensor of shape [num_units, output_size], where “output_size”
+ * corresponds to either the number of cell units (i.e., “num_units”),
+ * or the second dimension of the “projection_weights”, if defined.
+ * * 23: The backward recurrent-to-forget weights.
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 24: The backward recurrent-to-cell weights.
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 25: The backward recurrent-to-output weights.
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 26: The backward cell-to-input weights. Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 27: The backward cell-to-forget weights. Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 28: The backward cell-to-output weights. Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 29: The backward input gate bias. Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 30: The backward forget gate bias.
+ * A 1-D tensor of shape [num_units].
+ * * 31: The backward cell gate bias.
+ * A 1-D tensor of shape [num_units].
+ * * 32: The backward output gate bias.
+ * A 1-D tensor of shape [num_units].
+ * * 33: The backward projection weights. Optional.
+ * A 2-D tensor of shape [output_size, num_units].
+ * * 34: The backward projection bias. Optional.
+ * A 1-D tensor of shape [output_size].
+ * * 35: The forward input activation state.
+ * A 2-D tensor of shape [batch_size, output_size].
+ * * 36: The forward input cell state.
+ * A 2-D tensor of shape [batch_size, num_units].
+ * * 37: The backward input activation state.
+ * A 2-D tensor of shape [batch_size, output_size].
+ * * 38: The backward input cell state.
+ * A 2-D tensor of shape [batch_size, num_units].
+ * * 39: The auxiliary input. Optional.
+ * A 3-D tensor of shape [max_time, batch_size, input_size], where “batch_size”
+ * corresponds to the batching dimension, and “input_size” is the size
+ * of the input.
+ * * 40: The forward auxiliary input-to-input weights. Optional.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 41: The forward auxiliary input-to-forget weights. Optional.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 42: The forward auxiliary input-to-cell weights. Optional.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 43: The forward auxiliary input-to-output weights. Optional.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 44: The backward auxiliary input-to-input weights. Optional.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 45: The backward auxiliary input-to-forget weights. Optional.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 46: The backward auxiliary input-to-cell weights. Optional.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 47: The backward auxiliary input-to-output weights. Optional.
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 48: The activation function.
+ * A value indicating the activation function:
+ * <ul>
+ * <li>0: None;
+ * <li>1: Relu;
+ * <li>3: Relu6;
+ * <li>4: Tanh;
+ * <li>6: Sigmoid.
+ * </ul>
+ * * 49: The clipping threshold for the cell state, such
+ * that values are bound within [-cell_clip, cell_clip]. If set to 0.0
+ * then clipping is disabled.
+ * If all the input tensors have type {@link OperandType::TENSOR_FLOAT32},
+ * this scalar must be of the type {@link OperandType::FLOAT32},
+ * otherwise if all the input tensors have the type {@link
+ * TENSOR_FLOAT16}, this scalar must be of type {@link
+ * FLOAT16}.
+ * * 50: The clipping threshold for the output from the
+ * projection layer, such that values are bound within
+ * [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+ * If all the input tensors have type {@link OperandType::TENSOR_FLOAT32},
+ * this scalar must be of the type {@link OperandType::FLOAT32},
+ * otherwise if all the input tensors have the type {@link
+ * TENSOR_FLOAT16}, this scalar must be of type {@link
+ * FLOAT16}.
+ * * 51: merge_outputs
+ * An {@link OperandType::BOOL} scalar specifying if the outputs
+ * from forward and backward cells should be merged.
+ * * 52: time_major
+ * An {@link OperandType::BOOL} scalar specifying the shape format
+ * of input and output tensors.
+ *
+ * Outputs:
+ * * 0: The forward output.
+ * A 3-D tensor of shape:
+ * If time-major: [max_time, batch_size, output_size]
+ * If batch-major: [batch_size, max_time, output_size]
+ * * 1: The backward output. Unused if merge_outputs is true.
+ * A 3-D tensor of shape:
+ * If time-major: [max_time, batch_size, output_size]
+ * If batch-major: [batch_size, max_time, output_size]
+ *
+ * Available since API level 29.
+ */
BIDIRECTIONAL_SEQUENCE_LSTM = 42,
+
+ /**
+ * A recurrent neural network layer that applies a basic RNN cell to a
+ * sequence of inputs in forward and backward directions.
+ *
+ * This Op unrolls the input along the sequence dimension, and implements
+ * the following operation for each element in the sequence s =
+ * 1...sequence_length:
+ * fw_outputs[s] = fw_state = activation(inputs[s] * fw_input_weights’ +
+ * fw_state * fw_recurrent_weights’ + fw_bias)
+ *
+ * And for each element in sequence t = sequence_length : 1
+ * bw_outputs[t] = bw_state = activation(inputs[t] * bw_input_weights’ +
+ * bw_state * bw_recurrent_weights’ + bw_bias)
+ *
+ * Where:
+ * * “{fw,bw}_input_weights” is a weight matrix that multiplies the inputs;
+ * * “{fw,bw}_recurrent_weights” is a weight matrix that multiplies the
+ * current “state” which itself is the output from the previous time step
+ * computation;
+ * * “{fw,bw}_bias” is a bias vector (added to each output vector in the
+ * batch);
+ * * “activation” is the function passed as the “fused_activation_function”
+ * argument (if not “NONE”).
+ *
+ * The op also supports an auxiliary input. Regular cell feeds one input
+ * into the two RNN cells in the following way:
+ *
+ * INPUT (INPUT_REVERSED)
+ * | |
+ * ---------------------
+ * | FW_RNN BW_RNN |
+ * ---------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * An op with an auxiliary input takes two inputs and feeds them into the
+ * RNN cells in the following way:
+ *
+ * AUX_INPUT (AUX_INPUT_REVERSED)
+ * | |
+ * INPUT | (INPUT_R'D.)|
+ * | | | |
+ * -----------------------
+ * | \ / \ / |
+ * | FW_RNN BW_RNN |
+ * -----------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * While stacking this op on top of itself, this allows to connect both
+ * forward and backward outputs from previous cell to the next cell's
+ * inputs.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * The input tensors must all be the same type.
+ *
+ * Inputs:
+ * * 0: input.
+ * A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+ * it is set to true, then the input has a shape [maxTime, batchSize,
+ * inputSize], otherwise the input has a shape [batchSize, maxTime,
+ * inputSize].
+ * * 1: fwWeights.
+ * A 2-D tensor of shape [fwNumUnits, inputSize].
+ * * 2: fwRecurrentWeights.
+ * A 2-D tensor of shape [fwNumUnits, fwNumUnits].
+ * * 3: fwBias.
+ * A 1-D tensor of shape [fwNumUnits].
+ * * 4: fwHiddenState.
+ * A 2-D tensor of shape [batchSize, fwNumUnits]. Specifies a hidden
+ * state input for the first time step of the computation.
+ * * 5: bwWeights.
+ * A 2-D tensor of shape [bwNumUnits, inputSize].
+ * * 6: bwRecurrentWeights.
+ * A 2-D tensor of shape [bwNumUnits, bwNumUnits].
+ * * 7: bwBias.
+ * A 1-D tensor of shape [bwNumUnits].
+ * * 8: bwHiddenState
+ * A 2-D tensor of shape [batchSize, bwNumUnits]. Specifies a hidden
+ * state input for the first time step of the computation.
+ * * 9: auxInput.
+ * A 3-D tensor. The shape is the same as of the input 0.
+ * * 10:fwAuxWeights.
+ * A 2-D tensor of shape [fwNumUnits, inputSize].
+ * * 11:bwAuxWeights.
+ * A 2-D tensor of shape [bwNumUnits, inputSize].
+ * * 12:fusedActivationFunction.
+ * A {@link FusedActivationFunc} value indicating the activation function. If
+ * “NONE” is specified then it results in a linear activation.
+ * * 13:timeMajor
+ * An {@link OperandType::BOOL} scalar specifying the shape format
+ * of input and output tensors.
+ * * 14:mergeOutputs
+ * An {@link OperandType::BOOL} scalar specifying if the outputs
+ * from forward and backward cells are separate (if set to false) or
+ * concatenated (if set to true).
+ * Outputs:
+ * * 0: fwOutput.
+ * A 3-D tensor. The first two dimensions of the shape are defined by
+ * the input 6 (timeMajor) and the third dimension is defined by the
+ * input 14 (mergeOutputs). If timeMajor is set to true, then the first
+ * two dimensions are [maxTime, batchSize], otherwise they are set to
+ * [batchSize, maxTime]. If mergeOutputs is set to true, then the third
+ * dimension is equal to (fwNumUnits + bwNumUnits), otherwise it is set
+ * to fwNumUnits.
+ * * 1: bwOutput.
+ * A 3-D tensor. If the input 14 (mergeOutputs) is set to true, then
+ * this tensor is not produced. The shape is defined by the input 6
+ * (timeMajor). If it is set to true, then the shape is set to
+ * [maxTime, batchSize, bwNumUnits], otherwise the shape is set to
+ * [batchSize, maxTime, bwNumUnits].
+ *
+ * Available since API level 29.
+ */
BIDIRECTIONAL_SEQUENCE_RNN = 43,
+
+ /**
+ * Greedily selects a subset of bounding boxes in descending order of score.
+ *
+ * This op applies hard NMS algorithm to each class. In each loop of
+ * execution, the box with maximum score gets selected, and any boxes with
+ * the intersection-over-union (IOU) greater than a threshold are removed
+ * from the pending set.
+ *
+ * Axis-aligned bounding boxes are represented by its upper-left corner
+ * coordinate (x1,y1) and lower-right corner coordinate (x2,y2). A valid
+ * bounding box should satisfy x1 <= x2 and y1 <= y2.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Inputs:
+ * * 0: A 2-D Tensor of shape [num_rois, num_classes], specifying the score
+ * of each bounding box proposal. The boxes are grouped by batches in the
+ * first dimension.
+ * * 1: A 2-D Tensor specifying the bounding boxes of shape
+ * [num_rois, num_classes * 4], organized in the order [x1, y1, x2, y2].
+ * The boxes are grouped by batches in the first dimension. The sequential
+ * order of the boxes corresponds with input0. For input0 of type
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, this tensor should be of
+ * {@link OperandType::TENSOR_QUANT16_ASYMM}, with zeroPoint of 0 and
+ * scale of 0.125.
+ * * 2: A 1-D Tensor of shape [batches], specifying the number of boxes
+ * for each image in the batch.
+ * * 3: An {@link OperandType::FLOAT32} scalar, score_threshold. Boxes
+ * with scores lower than the threshold are filtered before sending
+ * to the NMS algorithm.
+ * * 4: An {@link OperandType::FLOAT32} scalar, specifying the IoU
+ * threshold.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the maximum
+ * number of selected bounding boxes for each image. Set to a negative
+ * value for unlimited number of output bounding boxes.
+ *
+ * Outputs:
+ * * 0: A 1-D Tensor of the same {@link OperandType} as input0, with shape
+ * [num_output_rois], specifying the score of each output box. The boxes
+ * are grouped by batches, but the sequential order in each batch is not
+ * guaranteed. For type of {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the scale and zero point must be the same as input0.
+ * * 1: A 2-D Tensor of the same {@link OperandType} as input1, with shape
+ * [num_output_rois, 4], specifying the coordinates of each
+ * output bounding box with the same format as input1. The sequential
+ * order of the boxes corresponds with output0. For type of
+ * {@link OperandType::TENSOR_QUANT16_ASYMM}, the scale must be
+ * 0.125 and the zero point must be 0.
+ * * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+ * [num_output_rois], specifying the class of each output box. The
+ * sequential order of the boxes corresponds with output0.
+ * * 3: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+ * [batches], specifying the number of output boxes for each image.
+ *
+ * Available since API level 29.
+ */
BOX_WITH_NMS_LIMIT = 44,
+
+ /**
+ * Casts a tensor to a new type.
+ *
+ * This operation ignores the scale and zeroPoint of quanized tensors,
+ * e.g. it treats a {@link OperandType::TENSOR_QUANT8_ASYMM} input
+ * as a tensor of uint8 values.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: A tensor with the same shape as input0.
+ *
+ * Available since API level 29.
+ */
CAST = 45,
+
+ /**
+ * Shuffle the channels of the input tensor.
+ *
+ * Given an input tensor and a integer value of num_groups, CHANNEL_SHUFFLE
+ * divide the channel dimension into num_groups groups, and reorganize the
+ * channels by grouping channels with the same index in each group.
+ *
+ * Along the channel dimension, the output is calculated using this formula:
+ *
+ * output_channel[k * num_groups + g] = input_channel[g * group_size + k]
+ *
+ * where group_size = num_channels / num_groups
+ *
+ * The number of channels must be divisible by num_groups.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor, specifying the tensor to be shuffled.
+ * * 1: An {@link OperandType::INT32} scalar, specifying the number of
+ * groups.
+ * * 2: An {@link OperandType::INT32} scalar, specifying the dimension
+ * channel shuffle would be performed on. Negative index is used to
+ * specify axis from the end (e.g. -1 for the last axis). Must be in
+ * the range [-n, n).
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} and same shape as input0.
+ *
+ * Available since API level 29.
+ */
CHANNEL_SHUFFLE = 46,
+
+ /**
+ * Apply postprocessing steps to bounding box detections.
+ *
+ * Bounding box detections are generated by applying transformation on a set
+ * of predefined anchors with the bounding box deltas from bounding box
+ * regression. A final step of hard NMS is applied to limit the number of
+ * returned boxes.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Inputs:
+ * * 0: A 3-D Tensor of shape [batches, num_anchors, num_classes], specifying
+ * the score of each anchor with each class. Class 0 for each
+ * [batches, num_anchors, 0] is background and will be ignored.
+ * * 1: A 3-D Tensor of shape [batches, num_anchors, length_box_encoding], with
+ * the first four values in length_box_encoding specifying the bounding
+ * box deltas. The box deltas are encoded in the order of [dy, dx, dh, dw],
+ * where dy and dx is the linear-scale relative correction factor for the
+ * center position of the bounding box with respect to the width and height,
+ * dh and dw is the log-scale relative correction factor for the width and
+ * height. All the entries in length_box_encoding beyond the first four
+ * values are ignored in this operation.
+ * * 2: A 2-D Tensor of shape [num_anchors, 4], specifying the shape of each
+ * predefined anchor, with format [ctr_y, ctr_x, h, w], where ctr_y and
+ * ctr_x are the center position of the box, and h and w are the height
+ * and the width.
+ * * 3: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+ * factor for dy in bounding box deltas.
+ * * 4: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+ * factor for dx in bounding box deltas.
+ * * 5: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+ * factor for dh in bounding box deltas.
+ * * 6: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+ * factor for dw in bounding box deltas.
+ * * 7: An {@link OperandType::BOOL} scalar, set to true to use regular
+ * multi-class NMS algorithm that do NMS separately for each class,
+ * set to false for a faster algorithm that only do one single NMS
+ * using the highest class score..
+ * * 8: An {@link OperandType::INT32} scalar, max_num_detections, specifying
+ * the maximum number of boxes for the output. Boxes with the lowest
+ * scores are discarded to meet the limit.
+ * * 9: An {@link OperandType::INT32} scalar, only used when input7 is
+ * set to false, specifying the maximum number of classes per detection.
+ * * 10: An {@link OperandType::INT32} scalar, only used when input7 is
+ * set to true, specifying the maximum number of detections when
+ * applying NMS algorithm for each single class.
+ * * 11: An {@link OperandType::FLOAT32} scalar, score_threshold. Boxes
+ * with scores lower than the threshold are filtered before sending
+ * to the NMS algorithm.
+ * * 12: An {@link OperandType::FLOAT32} scalar, specifying the IoU
+ * threshold for hard NMS.
+ * * 13: An {@link OperandType::BOOL} scalar, set to true to include
+ * background class in the list of label map for the output, set
+ * to false to not include the background. When the background
+ * class is included, it has label 0 and the output classes start
+ * at 1 in the label map, otherwise, the output classes start at 0.
+ *
+ * Outputs:
+ * * 0: A 2-D tensor of the same {@link OperandType} as input0, with shape
+ * [batches, max_num_detections], specifying the score of each output
+ * detections.
+ * * 1: A 3-D tensor of shape [batches, max_num_detections, 4], specifying the
+ * coordinates of each output bounding box, with format
+ * [y1, x1, y2, x2].
+ * * 2: A 2-D {@link OperandType::TENSOR_INT32} tensor, of shape
+ * [batches, max_num_detections], specifying the class label for each
+ * output detection.
+ * * 3: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape [batches],
+ * specifying the number of valid output detections for each batch.
+ *
+ * Available since API level 29.
+ */
DETECTION_POSTPROCESSING = 47,
+
+ /**
+ * For input tensors x and y, computes x == y elementwise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_BOOL8}
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * This operation supports broadcasting.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+ * with input0.
+ *
+ * Outputs:
+ * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+ *
+ * Available since API level 29.
+ */
EQUAL = 48,
+
+ /**
+ * Computes exponential of x element-wise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: from 1.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 29.
+ */
EXP = 49,
+
+ /**
+ * Inserts a dimension of 1 into a tensor's shape.
+ *
+ * Given a tensor input, this operation inserts a dimension of 1 at the
+ * given dimension index of input's shape. The dimension index starts at
+ * zero; if you specify a negative dimension index, it is counted backward
+ * from the end.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: An n-D tensor.
+ * * 1: An {@link OperandType::INT32} scalar specifying the dimension
+ * index to expand. Must be in the range [-(n + 1), (n + 1)).
+ *
+ * Outputs:
+ * * 0: An (n + 1)-D tensor with the same {@link OperandType} and data as
+ * input0.
+ *
+ * Available since API level 29.
+ */
EXPAND_DIMS = 50,
+
+ /**
+ * Gathers values along an axis.
+ *
+ * Produces an output tensor with shape
+ * input0.dimension[:axis] + indices.dimension + input0.dimension[axis + 1:]
+ * where:
+ * # Vector indices (output is rank(input0)).
+ * output[a_0, ..., a_n, i, b_0, ..., b_n] =
+ * input0[a_0, ..., a_n, indices[i], b_0, ..., b_n]
+ *
+ * # Higher rank indices (output is rank(input0) + rank(indices) - 1).
+ * output[a_0, ..., a_n, i, ..., j, b_0, ... b_n] =
+ * input0[a_0, ..., a_n, indices[i, ..., j], b_0, ..., b_n]
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: An n-D tensor from which to gather values.
+ * * 1: An {@link OperandType::INT32} scalar specifying the axis.
+ * Negative index is used to specify axis from the end
+ * (e.g. -1 for the last axis). Must be in the range [-n, n).
+ * * 2: A k-D tensor {@link OperandType::TENSOR_INT32} of indices.
+ * The values must be in the bounds of the corresponding dimensions
+ * of input0.
+ *
+ * Outputs:
+ * * 0: An (n + k - 1)-D tensor with the same {@link OperandType} as input0.
+ *
+ * Available since API level 29.
+ */
GATHER = 51,
+
+ /**
+ * Generate aixs-aligned bounding box proposals.
+ *
+ * Bounding box proposals are generated by applying transformation on a set
+ * of predefined anchors with the bounding box deltas from bounding box
+ * regression. A final step of hard NMS is applied to limit the number of
+ * returned boxes.
+ *
+ * Axis-aligned bounding boxes are represented by its upper-left corner
+ * coordinate (x1,y1) and lower-right corner coordinate (x2,y2). A valid
+ * bounding box should satisfy x1 <= x2 and y1 <= y2.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Inputs:
+ * * 0: A 4-D Tensor specifying the score of each anchor at each
+ * location. With "NHWC" data layout, the tensor shape is
+ * [batches, height, width, num_anchors]. With "NCHW" data layout,
+ * the tensor shape is [batches, num_anchors, height, width].
+ * * 1: A 4-D Tensor specifying the bounding box deltas. With "NHWC" data
+ * layout, the tensor shape is [batches, height, width, num_anchors * 4].
+ * With "NCHW" data layout, the tensor shape is
+ * [batches, num_anchors * 4, height, width]. The box deltas are encoded
+ * in the order of [dx, dy, dw, dh], where dx and dy is the linear-scale
+ * relative correction factor for the center position of the bounding box
+ * with respect to the width and height, dw and dh is the log-scale
+ * relative correction factor for the width and height. The last
+ * dimensions is the channel dimension.
+ * * 2: A 2-D Tensor of shape [num_anchors, 4], specifying the shape of each
+ * predefined anchor, with format [x1, y1, x2, y2]. For input0 of type
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, this tensor should be of
+ * {@link OperandType::TENSOR_QUANT16_SYMM}, with scale of 0.125.
+ * * 3: A 2-D Tensor of shape [batches, 2], specifying the size of
+ * each image in the batch, with format [image_height, image_width].
+ * * 4: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+ * from the height of original image to the height of feature map.
+ * * 5: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+ * from the width of original image to the width of feature map.
+ * * 6: An {@link OperandType::INT32} scalar, specifying the maximum
+ * number of boxes before going into the hard NMS algorithm. Boxes
+ * with the lowest scores are discarded to meet the limit. Set to
+ * a non-positive value for unlimited number.
+ * * 7: An {@link OperandType::INT32} scalar, specifying the maximum
+ * number of boxes returning from the hard NMS algorithm. Boxes
+ * with the lowest scores are discarded to meet the limit. Set to
+ * a non-positive value for unlimited number.
+ * * 8: An {@link OperandType::FLOAT32} scalar, specifying the IoU
+ * threshold for hard NMS.
+ * * 9: An {@link OperandType::FLOAT32} scalar, min_size. Boxes with
+ * height or width lower than the absolute threshold are filtered out.
+ * * 10: An {@link OperandType::BOOL} scalar, set to true to specify
+ * NCHW data layout for input0 and input1. Set to false for NHWC.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0, of shape
+ * [num_output_rois], specifying the score of each output box.
+ * The boxes are grouped by batches, but the sequential order in
+ * each batch is not guaranteed. For type of
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, the scale and zero
+ * point must be the same as input0.
+ * * 1: A tensor of the same {@link OperandType} as input1, of shape
+ * [num_output_rois, 4], specifying the coordinates of each output
+ * bounding box for each class, with format [x1, y1, x2, y2].
+ * The sequential order of the boxes corresponds with output0.
+ * For type of {@link OperandType::TENSOR_QUANT16_ASYMM}, the
+ * scale must be 0.125 and the zero point must be 0.
+ * * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+ * [batches], specifying the number of output boxes for each image.
+ *
+ * Available since API level 29.
+ */
GENERATE_PROPOSALS = 52,
+
+ /**
+ * For input tensors x and y, computes x > y elementwise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_BOOL8}
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * This operation supports broadcasting.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+ * with input0.
+ *
+ * Outputs:
+ * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+ *
+ * Available since API level 29.
+ */
GREATER = 53,
+ /**
+ * For input tensors x and y, computes x >= y elementwise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_BOOL8}
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * This operation supports broadcasting.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+ * with input0.
+ *
+ * Outputs:
+ * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+ *
+ * Available since API level 29.
+ */
GREATER_EQUAL = 54,
+
+ /**
+ * Performs a grouped 2-D convolution operation.
+ *
+ * Given an input tensor of shape [batches, height, width, depth_in] and a
+ * filter tensor of shape [depth_out, filter_height, filter_width, depth_group]
+ * containing depth_out convolutional filters of depth depth_group, GROUPED_CONV
+ * applies a group of different filters to each input channel group, then
+ * concatenates the results together.
+ *
+ * Specifically, the input channels are divided into num_groups groups, each with
+ * depth depth_group, i.e. depth_in = num_groups * depth_group. The convolutional
+ * filters are also divided into num_groups groups, i.e. depth_out is divisible
+ * by num_groups. GROUPED_CONV applies each group of filters to the corresponding
+ * input channel group, and the result are concatenated together.
+ *
+ * The output dimensions are functions of the filter dimensions, stride, and
+ * padding.
+ *
+ * The values in the output tensor are computed as:
+ *
+ * output[b, i, j, g * channel_multiplier + q] =
+ * sum_{di, dj, dk} (
+ * input[b, strides[1] * i + di, strides[2] * j + dj,
+ * g * depth_group + dk] *
+ * filter[g * channel_multiplier + q, di, dj, dk]
+ * ) + bias[channel]
+ *
+ * where channel_multiplier = depth_out / num_groups
+ *
+ * Supported tensor {@link OperandType} configurations:
+ * * 32 bit Floating point :
+ * * * {@link OperandType::TENSOR_FLOAT32} for input, filter, output, and bias.
+ *
+ * * 16 bit Floating point:
+ * * {@link OperandType::TENSOR_FLOAT16} for input, filter, output, and bias.
+ *
+ * * Quantized:
+ * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, filter, and output.
+ * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+ * * * input.scale * filter.scale).
+ *
+ * * Quantized with symetric per channel quantization for the filter:
+ * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, and output.
+ * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+ * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+ * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Both explicit padding and implicit padding are supported.
+ *
+ * Inputs (explicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+ * specifying the input, where depth_in = num_groups * depth_group.
+ * * 1: A 4-D tensor, of shape
+ * [depth_out, filter_height, filter_width, depth_group], specifying
+ * the filter, where depth_out must be divisible by num_groups. For
+ * tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+ * the channel dimension must be set to 0.
+ * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+ * tensor of type {@link OperandType::TENSOR_FLOAT32} or
+ * {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+ * type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+ * of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+ * of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+ * should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+ * 0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+ * bias_scale[i] = input_scale * filter_scale[i].
+ * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the left, in the ‘width’ dimension.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the right, in the ‘width’ dimension.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the top, in the ‘height’ dimension.
+ * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the bottom, in the ‘height’ dimension.
+ * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 9: An {@link OperandType::INT32} scalar, specifying the number of
+ groups.
+ * * 10: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 11: An {@link OperandType::BOOL} scalar, set to true to specify
+ * NCHW data layout for input0 and output0. Set to false for NHWC.
+ *
+ * Inputs (implicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+ * specifying the input, where depth_in = num_groups * depth_group.
+ * * 1: A 4-D tensor, of shape
+ * [depth_out, filter_height, filter_width, depth_group], specifying
+ * the filter, where depth_out must be divisible by num_groups. For
+ * tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+ * the channel dimension must be set to 0.
+ * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+ * tensor of type {@link OperandType::TENSOR_FLOAT32} or
+ * {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+ * type. For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+ * of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+ * of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+ * should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+ * 0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+ * bias_scale[i] = input_scale * filter_scale[i].
+ * * 3: An {@link OperandType::INT32} scalar, specifying the implicit
+ * padding scheme, has to be one of the
+ * following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 6: An {@link OperandType::INT32} scalar, specifying the number of
+ * groups.
+ * * 7: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 8: An {@link OperandType::BOOL} scalar, set to true to specify
+ * NCHW data layout for input0 and output0. Set to false for NHWC.
+ *
+ * Outputs:
+ * * 0: The output 4-D tensor, of shape
+ * [batches, out_height, out_width, depth_out]. For output tensor of
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
+ * must be satisfied: output_scale > input_scale * filter_scale (for
+ * filter tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+ * this condition must be true for all filter scales).
+ *
+ * Available since API level 29.
+ */
GROUPED_CONV_2D = 55,
+
+ /**
+ * Localize the maximum keypoints from heatmaps.
+ *
+ * This operation approximates the accurate maximum keypoint scores and
+ * indices after bicubic upscaling by using Taylor expansion up to the
+ * quadratic term.
+ *
+ * The bounding box is represented by its upper-left corner coordinate
+ * (x1,y1) and lower-right corner coordinate (x2,y2) in the original image.
+ * A valid bounding box should satisfy x1 <= x2 and y1 <= y2.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Inputs:
+ * * 0: A 4-D Tensor of shape
+ * [num_boxes, heatmap_size, heatmap_size, num_keypoints],
+ * specifying the heatmaps, the height and width of heatmaps should
+ * be the same, and must be greater than or equal to 2.
+ * * 1: A 2-D Tensor of shape [num_boxes, 4], specifying the bounding boxes,
+ * each with format [x1, y1, x2, y2]. For input0 of type
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, this tensor should
+ * be of {@link OperandType::TENSOR_QUANT16_ASYMM}, with zeroPoint
+ * of 0 and scale of 0.125.
+ * * 2: An {@link OperandType::BOOL} scalar, set to true to specify
+ * NCHW data layout for input0. Set to false for NHWC.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0, with shape
+ * [num_boxes, num_keypoints], specifying score of the keypoints.
+ * * 1: A tensor of the same {@link OperandType} as input1, with shape
+ * [num_boxes, num_keypoints, 2], specifying the location of
+ * the keypoints, the second dimension is organized as
+ * [keypoint_x, keypoint_y].
+ *
+ * Available since API level 29.
+ */
HEATMAP_MAX_KEYPOINT = 56,
+
+ /**
+ * Applies instance normalization to the input tensor.
+ *
+ * The values in the output tensor are computed as:
+ *
+ * output[b, h, w, c] =
+ * (input[b, h, w, c] - mean[b, c]) * gamma /
+ * sqrt(var[b, c] + epsilon) + beta
+ *
+ * Where the mean and variance are computed across the spatial dimensions:
+ *
+ * mean[b, c] =
+ * sum_{h, w}(input[b, h, w, c]) / sum(1)
+ *
+ * var[b, c] =
+ * sum_{h, w}(pow(input[b, h, w, c] - mean[b, c], 2)) / sum(1)
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Inputs:
+ * * 0: An n-D tensor, specifying the tensor to be normalized.
+ * * 1: An {@link OperandType::FLOAT32} scalar, specifying gamma, the
+ * scale applied to the normalized tensor.
+ * * 2: An {@link OperandType::FLOAT32} scalar, specifying beta, the
+ * offset applied to the normalized tensor.
+ * * 3: An {@link OperandType::FLOAT32} scalar, specifying epsilon, the
+ * small value added to variance to avoid dividing by zero.
+ * * 4: An {@link OperandType::BOOL} scalar, set to true to specify
+ * NCHW data layout for input0 and output0. Set to false for NHWC.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} and same shape as input0.
+ *
+ * Available since API level 29.
+ */
INSTANCE_NORMALIZATION = 57,
+
+ /**
+ * For input tensors x and y, computes x < y elementwise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_BOOL8}
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * This operation supports broadcasting.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+ * with input0.
+ *
+ * Outputs:
+ * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+ *
+ * Available since API level 29.
+ */
LESS = 58,
+
+ /**
+ * For input tensors x and y, computes x <= y elementwise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_BOOL8}
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * This operation supports broadcasting.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+ * with input0.
+ *
+ * Outputs:
+ * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+ *
+ * Available since API level 29.
+ */
LESS_EQUAL = 59,
+
+ /**
+ * Computes natural logarithm of x element-wise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: from 1.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 29.
+ */
LOG = 60,
+
+ /**
+ * Returns the truth value of x AND y element-wise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_BOOL8}
+ *
+ * Supported tensor rank: from 1
+ *
+ * This operation supports broadcasting.
+ *
+ * Inputs:
+ * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+ * * 1: A tensor of {@link OperandType::TENSOR_BOOL8} and dimensions
+ * compatible with input0.
+ *
+ * Outputs:
+ * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+ *
+ * Available since API level 29.
+ */
LOGICAL_AND = 61,
+
+ /**
+ * Computes the truth value of NOT x element-wise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_BOOL8}
+ *
+ * Supported tensor rank: from 1.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 29.
+ */
LOGICAL_NOT = 62,
+
+ /**
+ * Returns the truth value of x OR y element-wise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_BOOL8}
+ *
+ * Supported tensor rank: from 1
+ *
+ * This operation supports broadcasting.
+ *
+ * Inputs:
+ * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+ * * 1: A tensor of {@link OperandType::TENSOR_BOOL8} and dimensions
+ * compatible with input0.
+ *
+ * Outputs:
+ * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+ *
+ * Available since API level 29.
+ */
LOGICAL_OR = 63,
+
+ /**
+ * Computes the log softmax activations given logits.
+ *
+ * The output is calculated using this formula:
+ *
+ * output = logits * beta - log(reduce_sum(exp(logits * beta), axis))
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: from 1.
+ *
+ * Inputs:
+ * * 0: A tensor specifying the input logits.
+ * * 1: An {@link OperandType::FLOAT32} scalar, specifying the positive
+ * scaling factor for the exponent, beta.
+ * * 2: An {@link OperandType::INT32} scalar specifying the axis to
+ * reduce across. Negative index is used to specify axis from the
+ * end (e.g. -1 for the last axis). Must be in the range [-n, n).
+ *
+ * Outputs:
+ * * 0: The output tensor of the same {@link OperandType} and shape as
+ * input0.
+ *
+ * Available since API level 29.
+ */
LOG_SOFTMAX = 64,
+
+ /**
+ * Returns the element-wise maximum of two tensors.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Inputs:
+ * * 0: A tensor.
+ * * 1: A tensor of the same {@link OperandType} and compatible dimensions
+ * with input0.
+ *
+ * Outputs:
+ * * 0: The sum, a tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 29.
+ */
MAXIMUM = 65,
+
+ /**
+ * Returns the element-wise minimum of two tensors.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Inputs:
+ * * 0: A tensor.
+ * * 1: A tensor of the same {@link OperandType} and compatible dimensions
+ * with input0.
+ *
+ * Outputs:
+ * * 0: The sum, a tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 29.
+ */
MINIMUM = 66,
+
+ /**
+ * Computes numerical negative value element-wise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ *
+ * Supported tensor rank: from 1.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 29.
+ */
NEG = 67,
+
+ /**
+ * For input tensors x and y, computes x != y elementwise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_BOOL8}
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * This operation supports broadcasting.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+ * with input0.
+ *
+ * Outputs:
+ * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+ *
+ * Available since API level 29.
+ */
NOT_EQUAL = 68,
+
+ /**
+ * Pads a tensor with the given constant value according to the specified
+ * paddings.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor, specifying the tensor to be padded.
+ * * 1: A 2-D Tensor of {@link OperandType::TENSOR_INT32}, the paddings
+ * for each spatial dimension of the input tensor. The shape of the
+ * tensor must be {rank(input0), 2}.
+ * padding[i, 0] specifies the number of elements to be padded in the
+ * front of dimension i.
+ * padding[i, 1] specifies the number of elements to be padded after
+ * the end of dimension i.
+ * * 2: An scalar specifying the value to use for padding input0.
+ * For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
+ * pad value should be of {@link OperandType::FLOAT32}.
+ * For input tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the pad value should be of {@link OperandType::INT32}. The
+ * scale and zeroPoint are assumed to be the same as in input0.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0. The
+ * output tensor has the same rank as input0, and each
+ * dimension of the output tensor has the same size as the
+ * corresponding dimension of the input tensor plus the size
+ * of the padding:
+ * output0.dimension[i] =
+ * padding[i, 0] + input0.dimension[i] + padding[i, 1]
+ *
+ * Available since API level 29.
+ */
PAD_V2 = 69,
+
+ /**
+ * Computes the power of one value to another.
+ *
+ * Given a tensor base and a tensor exponent, this operation computes
+ * base^exponent elementwise.
+ *
+ * This operations supports broadcasting. The size of the output is the
+ * maximum size along each dimension of the input operands. It starts with
+ * the trailing dimensions, and works its way forward.
+ *
+ * For example:
+ * base.dimension = {4, 1, 2}
+ * exponent.dimension = {5, 4, 3, 1}
+ * output.dimension = {5, 4, 3, 2}
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: A tensor specifying the base.
+ * * 1: A tensor specifying the exponent.
+ *
+ * Outputs:
+ * * 0: An output tensor.
+ *
+ * Available since API level 29.
+ */
POW = 70,
+
+ /**
+ * Parametric Rectified Linear Unit.
+ *
+ * It follows: f(x) = alpha * x for x < 0, f(x) = x for x >= 0, where alpha
+ * is a learned array with the same {@link OperandType} and compatible
+ * dimensions as input x.
+ *
+ * Two dimensions are compatible when:
+ * 1. they are equal, or
+ * 2. one of them is 1
+ *
+ * The size of the output is the maximum size along each dimension of the
+ * input operands. It starts with the trailing dimensions, and works its way
+ * forward.
+ *
+ * Example:
+ * input.dimension = {4, 1, 2}
+ * alpha.dimension = {5, 4, 3, 1}
+ * output.dimension = {5, 4, 3, 2}
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: A tensor, specifying the input.
+ * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+ * as input0, specifying the alpha.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 29.
+ */
PRELU = 71,
+
+ /**
+ * Quantizes the input tensor.
+ *
+ * The formula is:
+ *
+ * output = max(0, min(255, round(input / scale) + zeroPoint)
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0, but with
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}.
+ *
+ * Available since API level 29.
+ */
QUANTIZE = 72,
+
+ /**
+ * A version of quantized LSTM, using 16 bit quantization for internal
+ * state.
+ *
+ * There is no projection layer, so cell state size is equal to the output
+ * size.
+ *
+ * Inputs:
+ * * 0: A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and shape [numBatches, inputSize] specifying the input to the LSTM
+ * cell. Tensor is quantized with a fixed quantization range of
+ * [-1, 127/128] (scale = 1/128, zeroPoint = 128).
+ * * 1: The input-to-input weights.
+ * A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and shape [outputSize, inputSize] specifying input-to-input part of
+ * weights for fully-connected layer inside the LSTM cell.
+ * Quantization zero point and scale must be the same across all the
+ * weights.
+ * * 2: The input-to-forget weights.
+ * A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and shape [outputSize, inputSize] specifying input-to-forget part of
+ * weights for fully-connected layer inside the LSTM cell.
+ * Quantization zero point and scale must be the same across all the
+ * weights.
+ * * 3: The input-to-cell weights.
+ * A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and shape [outputSize, inputSize] specifying input-to-cell part of
+ * weights for fully-connected layer inside the LSTM cell.
+ * Quantization zero point and scale must be the same across all the
+ * weights.
+ * * 4: The input-to-output weights.
+ * A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and shape [outputSize, inputSize] specifying input-to-output part of
+ * weights for fully-connected layer inside the LSTM cell.
+ * Quantization zero point and scale must be the same across all the
+ * weights.
+ * * 5: The recurrent-to-input weights.
+ * A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and shape [outputSize, inputSize] specifying recurrent-to-input part
+ * of weights for fully-connected layer inside the LSTM cell.
+ * Quantization zero point and scale must be the same across all the
+ * weights.
+ * * 6: The recurrent-to-forget weights.
+ * A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and shape [outputSize, inputSize] specifying recurrent-to-forget
+ * part of weights for fully-connected layer inside the LSTM cell.
+ * Quantization zero point and scale must be the same across all the
+ * weights.
+ * * 7: The recurrent-to-cell weights.
+ * A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and shape [outputSize, inputSize] specifying recurrent-to-cell part
+ * of weights for fully-connected layer inside the LSTM cell.
+ * Quantization zero point and scale must be the same across all the
+ * weights.
+ * * 8: The recurrent-to-output weights.
+ * A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and shape [outputSize, inputSize] specifying recurrent-to-output
+ * part of weights for fully-connected layer inside the LSTM cell.
+ * Quantization zero point and scale must be the same across all the
+ * weights.
+ * * 9: The input gate bias.
+ * A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+ * [outputSize] specifying the bias for the fully-connected layer
+ * inside the LSTM cell. Bias is quantized with scale being a product
+ * of input and weights scales and zeroPoint equal to 0.
+ * * 10:The forget gate bias.
+ * A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+ * [outputSize] specifying the bias for the fully-connected layer
+ * inside the LSTM cell. Bias is quantized with scale being a product
+ * of input and weights scales and zeroPoint equal to 0.
+ * * 11:The cell bias.
+ * A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+ * [outputSize] specifying the bias for the fully-connected layer
+ * inside the LSTM cell. Bias is quantized with scale being a product
+ * of input and weights scales and zeroPoint equal to 0.
+ * * 12:The output gate bias.
+ * A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+ * [outputSize] specifying the bias for the fully-connected layer
+ * inside the LSTM cell. Bias is quantized with scale being a product
+ * of input and weights scales and zeroPoint equal to 0.
+ * * 13: A 2-D tensor of type {@link OperandType::TENSOR_QUANT16_SYMM}
+ * and shape [numBatches, outputSize] specifying the cell state from the
+ * previous time step of the LSTM cell. It is quantized using a
+ * quantization range of [-2^4, 2^4 * 32767/32768] (scale = 2^4 /
+ * 32768, zeroPoint = 0).
+ * * 14: A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and shape [numBathes, outputSize] specifying the output of the LSTM
+ * cell from previous time-step. Tensor is quantized with a fixed
+ * quantization range of [-1, 127/128] (scale = 1/128, zeroPoint =
+ * 128).
+ *
+ *
+ * Outputs:
+ * * 0: A 2-D tensor of type {@link OperandType::TENSOR_QUANT16_SYMM}
+ * and shape [numBatches, outputSize] which contains a cell state from
+ * the current time step. Tensor is quantized using a quantization
+ * range of [-2^4, 2^4 * 32767/32768] (scale = 2^4 / 32768, zeroPoint =
+ * 0).
+ * * 1: A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * and shape [numBathes, outputSize] which contains the output value.
+ * Tensor is quantized with a fixed quantization range of [-1, 127/128]
+ * (scale = 1/128, zeroPoint = 128).
+ */
QUANTIZED_16BIT_LSTM = 73,
+
+ /**
+ * Draws samples from a multinomial distribution.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Inputs:
+ * * 0: A 2-D tensor with shape [batches, classes], specifying the
+ * unnormalized log-probabilities for all classes.
+ * * 1: A scalar {@link OperandType::INT32}, specifying the number of
+ * independent samples to draw for each row slice.
+ * * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor with shape [2],
+ * specifying seeds used to initialize the random distribution.
+ * Outputs:
+ * * 0: A 2-D {@link OperandType::TENSOR_INT32} tensor with shape
+ * [batches, samples], containing the drawn samples.
+ *
+ * Available since API level 29.
+ */
RANDOM_MULTINOMIAL = 74,
+
+ /**
+ * Reduces a tensor by computing the "logical and" of elements along given
+ * dimensions.
+ *
+ * If keep_dims is true, the reduced dimensions are
+ * retained with length 1. Otherwise, the rank of the tensor is reduced by
+ * 1 for each entry in dimensions.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_BOOL8}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor.
+ * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+ * to reduce. Dimension values must be in the range [-n, n).
+ * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+ * retains reduced dimensions with length 1.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 29.
+ */
REDUCE_ALL = 75,
+
+ /**
+ * Reduces a tensor by computing the "logical or" of elements along given
+ * dimensions.
+ *
+ * If keep_dims is true, the reduced dimensions are
+ * retained with length 1. Otherwise, the rank of the tensor is reduced by
+ * 1 for each entry in dimensions.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_BOOL8}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor.
+ * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+ * to reduce. Dimension values must be in the range [-n, n).
+ * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+ * retains reduced dimensions with length 1.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 29.
+ */
REDUCE_ANY = 76,
+
+ /**
+ * Reduces a tensor by computing the maximum of elements along given
+ * dimensions.
+ *
+ * If keep_dims is true, the reduced dimensions are
+ * retained with length 1. Otherwise, the rank of the tensor is reduced by
+ * 1 for each entry in dimensions.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor.
+ * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+ * to reduce. Dimension values must be in the range [-n, n).
+ * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+ * retains reduced dimensions with length 1.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 29.
+ */
REDUCE_MAX = 77,
+
+ /**
+ * Reduces a tensor by computing the minimum of elements along given
+ * dimensions.
+ *
+ * If keep_dims is true, the reduced dimensions are
+ * retained with length 1. Otherwise, the rank of the tensor is reduced by
+ * 1 for each entry in dimensions.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor.
+ * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+ * to reduce. Dimension values must be in the range [-n, n).
+ * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+ * retains reduced dimensions with length 1.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 29.
+ */
REDUCE_MIN = 78,
+
+ /**
+ * Reduces a tensor by multiplying elements along given dimensions.
+ *
+ * If keep_dims is true, the reduced dimensions are
+ * retained with length 1. Otherwise, the rank of the tensor is reduced by
+ * 1 for each entry in dimensions.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor.
+ * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+ * to reduce. Dimension values must be in the range [-n, n).
+ * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+ * retains reduced dimensions with length 1.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 29.
+ */
REDUCE_PROD = 79,
+
+ /**
+ * Reduces a tensor by summing elements along given dimensions.
+ *
+ * If keep_dims is true, the reduced dimensions are
+ * retained with length 1. Otherwise, the rank of the tensor is reduced by
+ * 1 for each entry in dimensions.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: up to 4
+ *
+ * Inputs:
+ * * 0: An n-D tensor.
+ * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+ * to reduce. Dimension values must be in the range [-n, n).
+ * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+ * retains reduced dimensions with length 1.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0.
+ *
+ * Available since API level 29.
+ */
REDUCE_SUM = 80,
+
+ /**
+ * Select and scale the feature map of each region of interest to a unified
+ * output size by average pooling sampling points from bilinear interpolation.
+ *
+ * The region of interest is represented by its upper-left corner coordinate
+ * (x1,y1) and lower-right corner coordinate (x2,y2) in the original image.
+ * A spatial scaling factor is applied to map into feature map coordinate.
+ * A valid region of interest should satisfy x1 <= x2 and y1 <= y2.
+ *
+ * No rounding is applied in this operation. The sampling points are unified
+ * distributed in the pooling bin and their values are calculated by bilinear
+ * interpolation.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Inputs:
+ * * 0: A 4-D tensor, specifying the feature map.
+ * * 1: A 2-D Tensor of shape [num_rois, 4], specifying the locations of
+ * the regions of interest, each line with format [x1, y1, x2, y2].
+ * For input0 of type {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * this tensor should be of {@link OperandType::TENSOR_QUANT16_ASYMM},
+ * with zeroPoint of 0 and scale of 0.125.
+ * * 2: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+ * [batches], specifying the number of output boxes for each batch.
+ * * 3: An {@link OperandType::INT32} scalar, specifying the output
+ * height of the output tensor.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the output
+ * width of the output tensor.
+ * * 5: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+ * from the height of original image to the height of feature map.
+ * * 6: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+ * from the width of original image to the width of feature map.
+ * * 7: An {@link OperandType::INT32} scalar, specifying the number of
+ * sampling points in height dimension used to compute the output.
+ * Set to 0 for adaptive value of ceil(roi_height/out_height).
+ * * 8: An {@link OperandType::INT32} scalar, specifying the number of
+ * sampling points in width dimension used to compute the output.
+ * Set to 0 for adaptive value of ceil(roi_width/out_width).
+ * * 9: An {@link OperandType::BOOL} scalar, set to true to specify
+ * NCHW data layout for input0 and output0. Set to false for NHWC.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0. The output
+ * shape is [num_rois, out_height, out_width, depth].
+ *
+ * Available since API level 29.
+ */
ROI_ALIGN = 81,
+
+ /**
+ * Select and scale the feature map of each region of interest to a unified
+ * output size by max-pooling.
+ *
+ * The region of interest is represented by its upper-left corner coordinate
+ * (x1,y1) and lower-right corner coordinate (x2,y2) in the original image.
+ * A spatial scaling factor is applied to map into feature map coordinate.
+ * A valid region of interest should satisfy x1 <= x2 and y1 <= y2.
+ *
+ * Rounding is applied in this operation to ensure integer boundary for
+ * regions of interest and pooling bins.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Inputs:
+ * * 0: A 4-D tensor, specifying the feature map.
+ * * 1: A 2-D Tensor of shape [num_rois, 4], specifying the locations of
+ * the regions of interest, each line with format [x1, y1, x2, y2].
+ * For input0 of type {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * this tensor should be of {@link OperandType::TENSOR_QUANT16_ASYMM},
+ * with zeroPoint of 0 and scale of 0.125.
+ * * 2: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+ * [batches], specifying the number of output boxes for each batch.
+ * * 3: An {@link OperandType::INT32} scalar, specifying the output
+ * height of the output tensor.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the output
+ * width of the output tensor.
+ * * 5: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+ * from the height of original image to the height of feature map.
+ * * 6: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+ * from the width of original image to the width of feature map.
+ * * 7: An {@link OperandType::BOOL} scalar, set to true to specify
+ * NCHW data layout for input0 and output0. Set to false for NHWC.
+ *
+ * Outputs:
+ * * 0: A tensor of the same {@link OperandType} as input0. The output
+ * shape is [num_rois, out_height, out_width, depth].
+ *
+ * Available since API level 29.
+ */
ROI_POOLING = 82,
+
+ /**
+ * Computes reciprocal of square root of x element-wise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: from 1.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 29.
+ */
RSQRT = 83,
+
+ /**
+ * Using a tensor of booleans c and input tensors x and y select values
+ * elementwise from both input tensors:
+ *
+ * O[i] = C[i] ? x[i] : y[i].
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: A tensor of type {@link OperandType::TENSOR_BOOL8} acting as a
+ * mask that chooses, based on the value at each element, whether the
+ * corresponding element in the output should be taken from input1 (if
+ * true) or input2 (if false).
+ * * 1: An input tensor of the same shape as input0.
+ * * 2: An input tensor of the same shape and type as input1.
+ *
+ * Outputs:
+ * * 0: A tensor of the same type and shape as input1 and input2.
+ *
+ */
SELECT = 84,
+
+ /**
+ * Computes sin of x element-wise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: from 1.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 29.
+ */
SIN = 85,
+
+ /**
+ * Extracts a slice of specified size from the input tensor starting at a
+ * specified location.
+ *
+ * The starting location is specified as a 1-D tensor containing offsets
+ * for each dimension. The size is specified as a 1-D tensor containing
+ * either size of a slice along corresponding dimension or -1. In the latter
+ * case, all the remaining elements in dimension are included in the slice.
+ * Slice size in each dimension cannot be zero.
+ *
+ * A sum of begin offset and a size of a slice must not exceed size of a
+ * corresponding dimension.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: An n-D tensor to take slice from.
+ * * 1: A 1-D tensor of type {@link OperandType::TENSOR_INT32} specifying
+ * the beginning indices of the slice in each dimension.
+ * * 2: A 1-D tensor of type {@link OperandType::TENSOR_INT32} specifying
+ * the size of the slice in each dimension.
+ *
+ * Outputs:
+ * * 0: An n-D tensor of the same type as the input containing the slice.
+ *
+ * Available since API level 29.
+ */
SLICE = 86,
+
+ /**
+ * Splits a tensor along a given axis into num_splits subtensors.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: An n-D tensor to split.
+ * * 1: An {@link OperandType::INT32} scalar specifying the axis along
+ * which to split.
+ * * 2: An {@link OperandType::INT32} scalar indicating the number of
+ * splits along given axis. Must evenly divide axis size.
+ *
+ * Outputs:
+ * * 0 ~ (num_splits - 1): Resulting subtensors.
+ *
+ * Available since API level 29.
+ */
SPLIT = 87,
+
+ /**
+ * Computes square root of x element-wise.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: from 1.
+ *
+ * Inputs:
+ * * 0: A tensor.
+ *
+ * Outputs:
+ * * 0: The output tensor of same shape as input0.
+ *
+ * Available since API level 29.
+ */
SQRT = 88,
+
+ /**
+ * Constructs a tensor by tiling a given tensor.
+ *
+ * This operation creates a new tensor by replicating `input` `multiples`
+ * times. The output tensor's i-th dimension has `input.dims(i) * multiples[i]`
+ * elements, and the values of `input` are replicated `multiples[i]` times
+ * along the i-th dimension.
+ * For example, tiling `[a b c d]` by `[2]` produces `[a b c d a b c d]`.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: input, an n-D tensor specifying the input.
+ * * 1: multiples, a 1-D tensor of {@link OperandType::TENSOR_INT32}.
+ * The length of multiples must be n.
+ *
+ * Outputs:
+ * * 0: A tiled tensor of the same {@link OperandType} and rank as `input`.
+ *
+ * Available since API level 29.
+ */
TILE = 89,
+
+ /**
+ * Finds values and indices of the k largest entries for the last dimension.
+ *
+ * Resulting values in each dimensions are sorted in descending order. If
+ * two values are equal, the one with larger index appears first.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_INT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: input, an n-D tensor specifying the input.
+ * * 1: k, an {@link OperandType::INT32} scalar, specifying the number of
+ * top elements to look for along the last dimension.
+ *
+ * Outputs:
+ * * 0: An n-D tensor of the same type as the input, containing the k
+ * largest elements along each last dimensional slice.
+ * * 1: An n-D tensor of type {@link OperandType::TENSOR_INT32}
+ * containing the indices of values within the last dimension of input.
+ *
+ * Available since API level 29.
+ */
TOPK_V2 = 90,
+
+ /**
+ * Performs the tranpose of 2-D convolution operation.
+ *
+ * This operation is sometimes called "deconvolution" after Deconvolutional
+ * Networks, but is actually the transpose (gradient) of
+ * {@link OperandType::CONV_2D} rather than an actual deconvolution.
+ *
+ * The output dimensions are functions of the filter dimensions, stride, and
+ * padding.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ *
+ * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+ * With the default data layout NHWC, the data is stored in the order of:
+ * [batch, height, width, channels]. Alternatively, the data layout could
+ * be NCHW, the data storage order of: [batch, channels, height, width].
+ *
+ * Both explicit padding and implicit padding are supported.
+ *
+ * Inputs (explicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+ * specifying the input.
+ * * 1: A 4-D tensor, of shape
+ * [depth_out, filter_height, filter_width, depth_in], specifying the
+ * filter.
+ * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+ * tensor of type {@link OperandType::TENSOR_FLOAT32} or
+ * {@link OperandType::TENSOR_FLOAT16}, the bias should be of the
+ * same type. For input tensor of type
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, the bias should be
+ * of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0 and
+ * bias_scale == input_scale * filter_scale.
+ * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the left, in the ‘width’ dimension.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the right, in the ‘width’ dimension.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the top, in the ‘height’ dimension.
+ * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+ * the bottom, in the ‘height’ dimension.
+ * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 10: An {@link OperandType::BOOL} scalar, set to true to specify
+ * NCHW data layout for input0 and output0. Set to false for NHWC.
+ *
+ * Inputs (implicit padding):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+ * specifying the input.
+ * * 1: A 4-D tensor, of shape
+ * [depth_out, filter_height, filter_width, depth_in], specifying the
+ * filter.
+ * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+ * tensor of type {@link OperandType::TENSOR_FLOAT32} or
+ * {@link OperandType::TENSOR_FLOAT16}, the bias should be of the
+ * same type. For input tensor of type
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, the bias should be
+ * of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0 and
+ * bias_scale == input_scale * filter_scale.
+ * * 3: An {@link OperandType::TENSOR_INT32} tensor, specifying the output
+ * tensor shape.
+ * * 4: An {@link OperandType::INT32} scalar, specifying the implicit
+ * padding scheme, has to be one of the
+ * following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘width’ dimension.
+ * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+ * walking through input in the ‘height’ dimension.
+ * * 7: An {@link OperandType::INT32} scalar, and has to be one of the
+ * {@link FusedActivationFunc} values. Specifies the activation to
+ * invoke on the result.
+ * * 8: An {@link OperandType::BOOL} scalar, set to true to specify
+ * NCHW data layout for input0 and output0. Set to false for NHWC.
+ *
+ * Outputs:
+ * * 0: The output 4-D tensor, of shape
+ * [batches, out_height, out_width, depth_out]. For output tensor of
+ * {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
+ * must be satisfied: output_scale > input_scale * filter_scale.
+ *
+ * Available since API level 29.
+ */
TRANSPOSE_CONV_2D = 91,
+
+ /**
+ * A recurrent neural network specified by an LSTM cell.
+ *
+ * Performs (fully) dynamic unrolling of input.
+ *
+ * This Op unrolls the input along the time dimension, and implements the
+ * following operation for each element in the sequence
+ * s = 1...sequence_length:
+ * outputs[s] = projection(state = activation(LSTMOp(inputs[s])))
+ *
+ * Where LSTMOp is the LSTM op as in {@link OperandType::LSTM},
+ * the "projection" is an optional projection layer from state and output
+ * and the “activation” is the function passed as the
+ * “fused_activation_function” argument (if not “NONE”).
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * Supported tensor rank: 3, either time-major or batch-major.
+ *
+ * All input and output tensors must be of the same type.
+ *
+ * Inputs:
+ * * 0: The input (\f$x_t\f$).
+ * A 3-D tensor of shape:
+ * If time-major: [max_time, batch_size, output_size]
+ * If batch-major: [batch_size, max_time, output_size]
+ * where “max_size” is the number of timesteps (sequence length),
+ * “batch_size” corresponds to the batching dimension, and
+ * “input_size” is the size of the input.
+ * * 1: The input-to-input weights (\f$W_{xi}\f$). Optional.
+ * A 2-D tensor of shape [num_units, input_size], where “num_units”
+ * corresponds to the number of cell units.
+ * * 2: The input-to-forget weights (\f$W_{xf}\f$).
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 3: The input-to-cell weights (\f$W_{xc}\f$).
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 4: The input-to-output weights (\f$W_{xo}\f$).
+ * A 2-D tensor of shape [num_units, input_size].
+ * * 5: The recurrent-to-input weights (\f$W_{hi}\f$). Optional.
+ * A 2-D tensor of shape [num_units, output_size], where “output_size”
+ * corresponds to either the number of cell units (i.e., “num_units”),
+ * or the second dimension of the “projection_weights”, if defined.
+ * * 6: The recurrent-to-forget weights (\f$W_{hf}\f$).
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 7: The recurrent-to-cell weights (\f$W_{hc}\f$).
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 8: The recurrent-to-output weights (\f$W_{ho}\f$).
+ * A 2-D tensor of shape [num_units, output_size].
+ * * 9: The cell-to-input weights (\f$W_{ci}\f$). Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 10:The cell-to-forget weights (\f$W_{cf}\f$). Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 11:The cell-to-output weights (\f$W_{co}\f$). Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 12:The input gate bias (\f$b_i\f$). Optional.
+ * A 1-D tensor of shape [num_units].
+ * * 13:The forget gate bias (\f$b_f\f$).
+ * A 1-D tensor of shape [num_units].
+ * * 14:The cell bias (\f$b_c\f$).
+ * A 1-D tensor of shape [num_units].
+ * * 15:The output gate bias (\f$b_o\f$).
+ * A 1-D tensor of shape [num_units].
+ * * 16:The projection weights (\f$W_{proj}\f$). Optional.
+ * A 2-D tensor of shape [output_size, num_units].
+ * * 17:The projection bias (\f$b_{proj}\f$). Optional.
+ * A 1-D tensor of shape [output_size].
+ * * 18:The output state (in) (\f$h_{t-1}\f$).
+ * A 2-D tensor of shape [batch_size, output_size].
+ * * 19:The cell state (in) (\f$C_{t-1}\f$).
+ * A 2-D tensor of shape [batch_size, num_units].
+ * * 20:The activation function (\f$g\f$).
+ * A value indicating the activation function:
+ * <ul>
+ * <li>0: None;
+ * <li>1: Relu;
+ * <li>3: Relu6;
+ * <li>4: Tanh;
+ * <li>6: Sigmoid.
+ * </ul>
+ * * 21:The clipping threshold (\f$t_{cell}\f$) for the cell state, such
+ * that values are bound within [-cell_clip, cell_clip]. If set to 0.0
+ * then clipping is disabled.
+ * * 22:The clipping threshold (\f$t_{proj}\f$) for the output from the
+ * projection layer, such that values are bound within
+ * [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+ * * 23:Time-major if true, batch-major if false.
+ * * 24:The input layer normalization weights.
+ * A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+ * to activation at input gate.
+ * * 25:The forget layer normalization weights.
+ * A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+ * to activation at forget gate.
+ * * 26:The cell layer normalization weights.
+ * A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+ * to activation at cell gate.
+ * * 27:The output layer normalization weights.
+ * A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+ * to activation at output gate.
+ *
+ * Outputs:
+ * * 0: The output (\f$o_t\f$).
+ * A 3-D tensor of shape:
+ * If time-major: [max_time, batch_size, output_size]
+ * If batch-major: [batch_size, max_time, output_size]
+ *
+ * Available since API level 29.
+ */
UNIDIRECTIONAL_SEQUENCE_LSTM = 92,
+
+ /**
+ * A recurrent neural network layer that applies a basic RNN cell to a
+ * sequence of inputs.
+ *
+ * This layer unrolls the input along the sequence dimension, and implements
+ * the following operation
+ * for each element in the sequence s = 1...sequence_length:
+ * outputs[s] = state = activation(inputs[s] * input_weights’ + state *
+ * recurrent_weights’ + bias)
+ *
+ * Where:
+ * * “input_weights” is a weight matrix that multiplies the inputs;
+ * * “recurrent_weights” is a weight matrix that multiplies the current
+ * “state” which itself is the output from the previous time step
+ * computation;
+ * * “bias” is a bias vector (added to each output vector in the batch);
+ * * “activation” is the function passed as the “fused_activation_function”
+ * argument (if not “NONE”).
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ *
+ * The input tensors must all be the same type.
+ *
+ * Inputs:
+ * * 0: input.
+ * A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+ * it is set to 1, then the input has a shape [maxTime, batchSize,
+ * inputSize], otherwise the input has a shape [batchSize, maxTime,
+ * inputSize].
+ * * 1: weights.
+ * A 2-D tensor of shape [numUnits, inputSize].
+ * * 2: recurrent_weights.
+ * A 2-D tensor of shape [numUnits, numUnits].
+ * * 3: bias.
+ * A 1-D tensor of shape [numUnits].
+ * * 4: hidden state
+ * A 2-D tensor of shape [batchSize, numUnits]. Specifies a hidden
+ * state input for the first time step of the computation.
+ * * 5: fusedActivationFunction.
+ * A {@link FusedActivationFunc} value indicating the activation function. If
+ * “NONE” is specified then it results in a linear activation.
+ * * 6: timeMajor
+ * An {@link OperandType::INT32} scalar specifying the shape format
+ * of input and output tensors. Must be set to either 0 or 1.
+ * Outputs:
+ * * 0: output.
+ * A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+ * it is set to 1, then the output has a shape [maxTime, batchSize,
+ * numUnits], otherwise the output has a shape [batchSize, maxTime,
+ * numUnits].
+ *
+ * Available since API level 29.
+ */
UNIDIRECTIONAL_SEQUENCE_RNN = 93,
+ /**
+ * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
+ * OEM operation and data types.
+ *
+ * This operation is OEM specific. It should only be used for OEM
+ * applications.
+ */
+ OEM_OPERATION = @1.1::OperationType:OEM_OPERATION,
/* ADDING A NEW FUNDAMENTAL OPERATION REQUIRES UPDATING THE VALUE OF
* OperationTypeRange::FUNDAMENTAL_MAX.
*/
@@ -459,7 +4718,7 @@
* {@link OperationTypeRange::BASE_MAX} respectively should be interpreted
* as an extension operand. The low
* {@link Model::ExtensionTypeEncoding::LOW_BITS_TYPE} bits of the value
- * correspond to the value within the extension and the high
+ * correspond to the type ID within the extension and the high
* {@link Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX} bits encode
* the "prefix", which maps uniquely to the extension name.
*
diff --git a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
index a0f11eb..3b8e3dd 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
@@ -157,6 +157,7 @@
case OperandType::UINT32:
case OperandType::BOOL:
return 1;
+ case OperandType::TENSOR_BOOL8:
case OperandType::TENSOR_FLOAT16:
case OperandType::TENSOR_FLOAT32:
case OperandType::TENSOR_INT32:
@@ -194,6 +195,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_QUANT8_SYMM_PER_CHANNEL:
@@ -230,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:
@@ -283,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 =
@@ -339,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
@@ -357,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;
@@ -397,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,
@@ -484,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/radio/1.3/Android.bp b/radio/1.3/Android.bp
index 30b8edf..63056cc 100644
--- a/radio/1.3/Android.bp
+++ b/radio/1.3/Android.bp
@@ -7,6 +7,7 @@
enabled: true,
},
srcs: [
+ "types.hal",
"IRadio.hal",
"IRadioIndication.hal",
"IRadioResponse.hal",
@@ -17,6 +18,9 @@
"android.hardware.radio@1.2",
"android.hidl.base@1.0",
],
+ types: [
+ "RadioResponseInfoModem",
+ ],
gen_java: true,
}
diff --git a/radio/1.4/Android.bp b/radio/1.4/Android.bp
index 9fd0374..e8b8777 100644
--- a/radio/1.4/Android.bp
+++ b/radio/1.4/Android.bp
@@ -25,8 +25,10 @@
"CardStatus",
"CarrierRestrictionsWithPriority",
"CellConfigLte",
+ "CellIdentityNr",
"CellInfo",
"CellInfoLte",
+ "CellInfoNr",
"DataCallFailCause",
"DataConnActiveStatus",
"DataProfileInfo",
@@ -39,6 +41,7 @@
"LteVopsInfo",
"NetworkScanResult",
"NrIndicators",
+ "NrSignalStrength",
"PdpProtocolType",
"PhysicalChannelConfig",
"RadioAccessFamily",
@@ -46,6 +49,7 @@
"RadioFrequencyInfo",
"RadioTechnology",
"SetupDataCallResult",
+ "SignalStrength",
"SimLockMultiSimPolicy",
],
gen_java: true,
diff --git a/radio/1.4/IRadio.hal b/radio/1.4/IRadio.hal
index 21c5097..8ef1f96 100644
--- a/radio/1.4/IRadio.hal
+++ b/radio/1.4/IRadio.hal
@@ -215,4 +215,13 @@
* Response callback is IRadioResponse.getAllowedCarriersResponse_1_3()
*/
oneway getAllowedCarriers_1_4(int32_t serial);
+
+ /**
+ * Requests current signal strength and associated information. Must succeed if radio is on.
+ *
+ * @param serial Serial number of request.
+ *
+ * Response function is IRadioResponse.getSignalStrengthResponse_1_4()
+ */
+ oneway getSignalStrength_1_4(int32_t serial);
};
diff --git a/radio/1.4/IRadioIndication.hal b/radio/1.4/IRadioIndication.hal
index 58b7b70..f4d3ba9 100644
--- a/radio/1.4/IRadioIndication.hal
+++ b/radio/1.4/IRadioIndication.hal
@@ -53,7 +53,7 @@
vec<EmergencyNumber> emergencyNumberList);
/**
- * Request all of the current cell information known to the radio.
+ * Report all of the current cell information known to the radio.
*
* @param type Type of radio indication
* @param records Current cell information
@@ -89,4 +89,12 @@
* 3. Unsolicited disconnect from either modem or network side.
*/
oneway dataCallListChanged_1_4(RadioIndicationType type, vec<SetupDataCallResult> dcList);
+
+ /**
+ * Indicates current signal strength of the radio.
+ *
+ * @param type Type of radio indication
+ * @param signalStrength SignalStrength information
+ */
+ oneway currentSignalStrength_1_4(RadioIndicationType type, SignalStrength signalStrength);
};
diff --git a/radio/1.4/IRadioResponse.hal b/radio/1.4/IRadioResponse.hal
index 6b8977d..f43295a 100644
--- a/radio/1.4/IRadioResponse.hal
+++ b/radio/1.4/IRadioResponse.hal
@@ -24,6 +24,7 @@
import @1.4::DataRegStateResult;
import @1.4::RadioAccessFamily;
import @1.4::SetupDataCallResult;
+import @1.4::SignalStrength;
import @1.4::SimLockMultiSimPolicy;
/**
@@ -200,4 +201,14 @@
*/
oneway getAllowedCarriersResponse_1_4(RadioResponseInfo info,
CarrierRestrictionsWithPriority carriers, SimLockMultiSimPolicy multiSimPolicy);
+
+ /**
+ * @param signalStrength Current signal strength
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:INTERNAL_ERR
+ */
+ oneway getSignalStrengthResponse_1_4(RadioResponseInfo info, SignalStrength signalStrength);
};
diff --git a/radio/1.4/types.hal b/radio/1.4/types.hal
index 6fa7ebd..2747732 100644
--- a/radio/1.4/types.hal
+++ b/radio/1.4/types.hal
@@ -20,12 +20,16 @@
import @1.0::ApnTypes;
import @1.0::Carrier;
import @1.0::CellInfoType;
+import @1.0::CdmaSignalStrength;
import @1.0::DataCallFailCause;
import @1.0::DataProfileId;
import @1.0::DataProfileInfoType;
+import @1.0::EvdoSignalStrength;
+import @1.0::GsmSignalStrength;
+import @1.0::LteSignalStrength;
import @1.0::RadioAccessFamily;
import @1.0::RadioCapabilityPhase;
-import @1.0::RadioCapabilityStatus ;
+import @1.0::RadioCapabilityStatus;
import @1.0::RadioError;
import @1.0::RadioTechnology;
import @1.0::RegState;
@@ -39,10 +43,17 @@
import @1.2::CellInfoTdscdma;
import @1.2::CellInfoWcdma;
import @1.2::CardStatus;
-import @1.2::CellIdentity;
import @1.2::CellConnectionStatus;
+import @1.2::CellIdentityCdma;
+import @1.2::CellIdentityGsm;
+import @1.2::CellIdentityLte;
+import @1.2::CellIdentityTdscdma;
+import @1.2::CellIdentityWcdma;
+import @1.2::CellIdentityOperatorNames;
import @1.2::DataRegStateResult;
import @1.2::PhysicalChannelConfig;
+import @1.2::TdscdmaSignalStrength;
+import @1.2::WcdmaSignalStrength;
import android.hidl.safe_union@1.0::Monostate;
@@ -188,12 +199,12 @@
};
enum RadioTechnology : @1.0::RadioTechnology {
- /** 5G NR. */
+ /** 5G NR. This is only use in 5G Standalone mode. */
NR = 20,
};
enum RadioAccessFamily : @1.0::RadioAccessFamily {
- /** 5G NR. */
+ /** 5G NR. This is only use in 5G Standalone mode. */
NR = 1 << RadioTechnology:NR,
};
@@ -1551,9 +1562,15 @@
CellInfoWcdma wcdma;
CellInfoTdscdma tdscdma;
CellInfoLte lte;
+ CellInfoNr nr;
} info;
};
+struct CellInfoNr {
+ NrSignalStrength signalStrength;
+ CellIdentityNr cellidentity;
+};
+
/** Overwritten from @1.2::NetworkScanResult in order to update the CellInfo to 1.4 version. */
struct NetworkScanResult {
/**
@@ -1770,3 +1787,131 @@
*/
bool allowedCarriersPrioritized;
};
+
+struct CellIdentityNr {
+ /** 3-digit Mobile Country Code, in range[0, 999], INT_MAX means invalid/unreported. */
+ string mcc;
+
+ /**
+ * 2 or 3-digit Mobile Network Code, in range [0, 999], INT_MAX means invalid/unreported.
+ */
+ string mnc;
+
+ /**
+ * Physical cell id in range [0, 1007] described in 3GPP TS 38.331. This value must be valid.
+ */
+ uint32_t pci;
+
+ /** 16-bit tracking area code, INT_MAX means invalid/unreported. */
+ int32_t tac;
+
+ /**
+ * NR Absolute Radio Frequency Channel Number, in range [0, 3279165].
+ * Reference: 3GPP TS 38.101-1 and 3GPP TS 38.101-2 section 5.4.2.1.
+ * This value must be valid.
+ */
+ int32_t nrarfcn;
+
+ CellIdentityOperatorNames operatorNames;
+};
+
+struct NrSignalStrength {
+ /**
+ * SS reference signal received power, multipled by -1.
+ *
+ * Reference: 3GPP TS 38.215.
+ *
+ * Range [44, 140], INT_MAX means invalid/unreported.
+ */
+ int32_t ssRsrp;
+
+ /**
+ * SS reference signal received quality, multipled by -1.
+ *
+ * Reference: 3GPP TS 38.215.
+ *
+ * Range [3, 20], INT_MAX means invalid/unreported.
+ */
+ int32_t ssRsrq;
+
+ /**
+ * SS signal-to-noise and interference ratio.
+ *
+ * Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
+ *
+ * Range [-23, 40], INT_MAX means invalid/unreported.
+ */
+ int32_t ssSinr;
+
+ /**
+ * CSI reference signal received power, multipled by -1.
+ *
+ * Reference: 3GPP TS 38.215.
+ *
+ * Range [44, 140], INT_MAX means invalid/unreported.
+ */
+ int32_t csiRsrp;
+
+ /**
+ * CSI reference signal received quality, multipled by -1.
+ *
+ * Reference: 3GPP TS 38.215.
+ *
+ * Range [3, 20], INT_MAX means invalid/unreported.
+ */
+ int32_t csiRsrq;
+
+ /**
+ * CSI signal-to-noise and interference ratio.
+ *
+ * Reference: 3GPP TS 138.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
+ *
+ * Range [-23, 40], INT_MAX means invalid/unreported.
+ */
+ int32_t csiSinr;
+};
+
+/** Overwritten from @1.2::SignalStrength in order to add signal strength for NR. */
+struct SignalStrength {
+ /**
+ * If GSM measurements are provided, this structure must contain valid measurements; otherwise
+ * all fields should be set to INT_MAX to mark them as invalid.
+ */
+ GsmSignalStrength gsm;
+
+ /**
+ * If CDMA measurements are provided, this structure must contain valid measurements; otherwise
+ * all fields should be set to INT_MAX to mark them as invalid.
+ */
+ CdmaSignalStrength cdma;
+
+ /**
+ * If EvDO measurements are provided, this structure must contain valid measurements; otherwise
+ * all fields should be set to INT_MAX to mark them as invalid.
+ */
+ EvdoSignalStrength evdo;
+
+ /**
+ * If LTE measurements are provided, this structure must contain valid measurements; otherwise
+ * all fields should be set to INT_MAX to mark them as invalid.
+ */
+ LteSignalStrength lte;
+
+ /**
+ * If TD-SCDMA measurements are provided, this structure must contain valid measurements;
+ * otherwise all fields should be set to INT_MAX to mark them as invalid.
+ */
+ TdscdmaSignalStrength tdscdma;
+
+ /**
+ * If WCDMA measurements are provided, this structure must contain valid measurements; otherwise
+ * all fields should be set to INT_MAX to mark them as invalid.
+ */
+ WcdmaSignalStrength wcdma;
+
+ /**
+ * If NR 5G measurements are provided, this structure must contain valid measurements; otherwise
+ * all fields should be set to INT_MAX to mark them as invalid.
+ */
+ NrSignalStrength nr;
+};
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h b/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h
index f290d5d..b77814f 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h
+++ b/radio/1.4/vts/functional/radio_hidl_hal_utils_v1_4.h
@@ -441,6 +441,9 @@
const RadioResponseInfo& info,
const ::android::hardware::radio::V1_2::SignalStrength& sig_strength);
+ Return<void> getSignalStrengthResponse_1_4(const RadioResponseInfo& info,
+ const ::android::hardware::radio::V1_4::SignalStrength& sig_strength);
+
Return<void> getCellInfoListResponse_1_2(
const RadioResponseInfo& info,
const ::android::hardware::hidl_vec<::android::hardware::radio::V1_2::CellInfo>&
@@ -559,6 +562,9 @@
RadioIndicationType type,
const ::android::hardware::radio::V1_2::SignalStrength& signalStrength);
+ Return<void> currentSignalStrength_1_4(RadioIndicationType type,
+ const ::android::hardware::radio::V1_4::SignalStrength& signalStrength);
+
/* 1.1 Api */
Return<void> carrierInfoForImsiEncryption(RadioIndicationType info);
@@ -730,4 +736,4 @@
/* radio indication handle */
sp<RadioIndication_v1_4> radioInd_v1_4;
-};
\ No newline at end of file
+};
diff --git a/radio/1.4/vts/functional/radio_indication.cpp b/radio/1.4/vts/functional/radio_indication.cpp
index f08b361..c3722b0 100644
--- a/radio/1.4/vts/functional/radio_indication.cpp
+++ b/radio/1.4/vts/functional/radio_indication.cpp
@@ -85,6 +85,11 @@
return Void();
}
+Return<void> RadioIndication_v1_4::currentSignalStrength_1_4(RadioIndicationType /*type*/,
+ const ::android::hardware::radio::V1_4::SignalStrength& /*signalStrength*/) {
+ return Void();
+}
+
/* 1.1 Apis */
Return<void> RadioIndication_v1_4::carrierInfoForImsiEncryption(RadioIndicationType /*info*/) {
return Void();
@@ -321,4 +326,4 @@
Return<void> RadioIndication_v1_4::modemReset(RadioIndicationType /*type*/,
const ::android::hardware::hidl_string& /*reason*/) {
return Void();
-}
\ No newline at end of file
+}
diff --git a/radio/1.4/vts/functional/radio_response.cpp b/radio/1.4/vts/functional/radio_response.cpp
index 2ae5f7c..10ecead 100644
--- a/radio/1.4/vts/functional/radio_response.cpp
+++ b/radio/1.4/vts/functional/radio_response.cpp
@@ -739,6 +739,13 @@
return Void();
}
+Return<void> RadioResponse_v1_4::getSignalStrengthResponse_1_4(const RadioResponseInfo& info,
+ const ::android::hardware::radio::V1_4::SignalStrength& /*sig_strength*/) {
+ rspInfo = info;
+ parent_v1_4.notify(info.serial);
+ return Void();
+}
+
Return<void> RadioResponse_v1_4::getCellInfoListResponse_1_2(
const RadioResponseInfo& /*info*/,
const ::android::hardware::hidl_vec<
@@ -864,4 +871,4 @@
const RadioResponseInfo& /*info*/, const CarrierRestrictionsWithPriority& /*carriers*/,
SimLockMultiSimPolicy /*multiSimPolicy*/) {
return Void();
-}
\ No newline at end of file
+}
diff --git a/sensors/2.0/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]++;
diff --git a/tests/libhwbinder/1.0/default/Android.bp b/tests/libhwbinder/1.0/default/Android.bp
index 3d18720..aad1e31 100644
--- a/tests/libhwbinder/1.0/default/Android.bp
+++ b/tests/libhwbinder/1.0/default/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
name: "android.hardware.tests.libhwbinder@1.0-impl",
defaults: ["hidl_defaults"],
relative_install_path: "hw",
diff --git a/vibrator/1.3/example/Android.bp b/vibrator/1.3/example/Android.bp
new file mode 100644
index 0000000..36f2ff8
--- /dev/null
+++ b/vibrator/1.3/example/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+ name: "android.hardware.vibrator@1.3-service.example",
+ vendor: true,
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.vibrator@1.3-service.example.rc"],
+ vintf_fragments: ["android.hardware.vibrator@1.3-service.example.xml"],
+ srcs: ["service.cpp", "Vibrator.cpp"],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
+ ],
+}
diff --git a/vibrator/1.3/example/Vibrator.cpp b/vibrator/1.3/example/Vibrator.cpp
new file mode 100644
index 0000000..bb9a057
--- /dev/null
+++ b/vibrator/1.3/example/Vibrator.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorService"
+
+#include <log/log.h>
+
+#include "Vibrator.h"
+
+namespace android {
+namespace hardware {
+namespace vibrator {
+namespace V1_3 {
+namespace implementation {
+
+static constexpr uint32_t MS_PER_S = 1000;
+static constexpr uint32_t NS_PER_MS = 1000000;
+
+Vibrator::Vibrator() {
+ sigevent se{};
+ se.sigev_notify = SIGEV_THREAD;
+ se.sigev_value.sival_ptr = this;
+ se.sigev_notify_function = timerCallback;
+ se.sigev_notify_attributes = nullptr;
+
+ if (timer_create(CLOCK_REALTIME, &se, &mTimer) < 0) {
+ ALOGE("Can not create timer!%s", strerror(errno));
+ }
+}
+
+// Methods from ::android::hardware::vibrator::V1_0::IVibrator follow.
+
+Return<Status> Vibrator::on(uint32_t timeoutMs) {
+ return activate(timeoutMs);
+}
+
+Return<Status> Vibrator::off() {
+ return activate(0);
+}
+
+Return<bool> Vibrator::supportsAmplitudeControl() {
+ return true;
+}
+
+Return<Status> Vibrator::setAmplitude(uint8_t amplitude) {
+ ALOGI("Amplitude: %u -> %u\n", mAmplitude, amplitude);
+ mAmplitude = amplitude;
+ return Status::OK;
+}
+
+Return<void> Vibrator::perform(V1_0::Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
+ return perform_1_1(static_cast<V1_1::Effect_1_1>(effect), strength, _hidl_cb);
+}
+
+// Methods from ::android::hardware::vibrator::V1_1::IVibrator follow.
+
+Return<void> Vibrator::perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength,
+ perform_cb _hidl_cb) {
+ return perform_1_2(static_cast<V1_2::Effect>(effect), strength, _hidl_cb);
+}
+
+// Methods from ::android::hardware::vibrator::V1_2::IVibrator follow.
+
+Return<void> Vibrator::perform_1_2(Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
+ uint8_t amplitude;
+ uint32_t ms;
+ Status status;
+
+ ALOGI("Perform: Effect %s\n", effectToName(effect));
+
+ amplitude = strengthToAmplitude(strength);
+ setAmplitude(amplitude);
+
+ ms = effectToMs(effect);
+ status = activate(ms);
+
+ _hidl_cb(status, ms);
+
+ return Void();
+}
+
+// Methods from ::android::hardware::vibrator::V1_3::IVibrator follow.
+
+Return<bool> Vibrator::supportsExternalControl() {
+ return true;
+}
+
+Return<Status> Vibrator::setExternalControl(bool enabled) {
+ if (mEnabled) {
+ ALOGW("Setting external control while the vibrator is enabled is unsupported!");
+ return Status::UNSUPPORTED_OPERATION;
+ } else {
+ ALOGI("ExternalControl: %s -> %s\n", mExternalControl ? "true" : "false",
+ enabled ? "true" : "false");
+ mExternalControl = enabled;
+ return Status::OK;
+ }
+}
+
+// Private methods follow.
+
+Status Vibrator::enable(bool enabled) {
+ if (mExternalControl) {
+ ALOGW("Enabling/disabling while the vibrator is externally controlled is unsupported!");
+ return Status::UNSUPPORTED_OPERATION;
+ } else {
+ ALOGI("Enabled: %s -> %s\n", mEnabled ? "true" : "false", enabled ? "true" : "false");
+ mEnabled = enabled;
+ return Status::OK;
+ }
+}
+
+Status Vibrator::activate(uint32_t ms) {
+ std::lock_guard<std::mutex> lock{mMutex};
+ Status status = Status::OK;
+
+ if (ms > 0) {
+ status = enable(true);
+ if (status != Status::OK) {
+ return status;
+ }
+ }
+
+ itimerspec ts{};
+ ts.it_value.tv_sec = ms / MS_PER_S;
+ ts.it_value.tv_nsec = ms % MS_PER_S * NS_PER_MS;
+
+ if (timer_settime(mTimer, 0, &ts, nullptr) < 0) {
+ ALOGE("Can not set timer!");
+ status = Status::UNKNOWN_ERROR;
+ }
+
+ if ((status != Status::OK) || !ms) {
+ Status _status;
+
+ _status = enable(false);
+
+ if (status == Status::OK) {
+ status = _status;
+ }
+ }
+
+ return status;
+}
+
+void Vibrator::timeout() {
+ std::lock_guard<std::mutex> lock{mMutex};
+ itimerspec ts{};
+
+ if (timer_gettime(mTimer, &ts) < 0) {
+ ALOGE("Can not read timer!");
+ }
+
+ if (ts.it_value.tv_sec == 0 && ts.it_value.tv_nsec == 0) {
+ enable(false);
+ }
+}
+
+void Vibrator::timerCallback(union sigval sigval) {
+ static_cast<Vibrator*>(sigval.sival_ptr)->timeout();
+}
+
+const char* Vibrator::effectToName(Effect effect) {
+ return toString(effect).c_str();
+}
+
+uint32_t Vibrator::effectToMs(Effect effect) {
+ switch (effect) {
+ case Effect::CLICK:
+ return 10;
+ case Effect::DOUBLE_CLICK:
+ return 15;
+ case Effect::TICK:
+ return 5;
+ case Effect::THUD:
+ return 5;
+ case Effect::POP:
+ return 5;
+ case Effect::HEAVY_CLICK:
+ return 10;
+ case Effect::RINGTONE_1:
+ return 30000;
+ case Effect::RINGTONE_2:
+ return 30000;
+ case Effect::RINGTONE_3:
+ return 30000;
+ case Effect::RINGTONE_4:
+ return 30000;
+ case Effect::RINGTONE_5:
+ return 30000;
+ case Effect::RINGTONE_6:
+ return 30000;
+ case Effect::RINGTONE_7:
+ return 30000;
+ case Effect::RINGTONE_8:
+ return 30000;
+ case Effect::RINGTONE_9:
+ return 30000;
+ case Effect::RINGTONE_10:
+ return 30000;
+ case Effect::RINGTONE_11:
+ return 30000;
+ case Effect::RINGTONE_12:
+ return 30000;
+ case Effect::RINGTONE_13:
+ return 30000;
+ case Effect::RINGTONE_14:
+ return 30000;
+ case Effect::RINGTONE_15:
+ return 30000;
+ }
+}
+
+uint8_t Vibrator::strengthToAmplitude(EffectStrength strength) {
+ switch (strength) {
+ case EffectStrength::LIGHT:
+ return 128;
+ case EffectStrength::MEDIUM:
+ return 192;
+ case EffectStrength::STRONG:
+ return 255;
+ }
+}
+
+} // namespace implementation
+} // namespace V1_3
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
diff --git a/vibrator/1.3/example/Vibrator.h b/vibrator/1.3/example/Vibrator.h
new file mode 100644
index 0000000..a931b63
--- /dev/null
+++ b/vibrator/1.3/example/Vibrator.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H
+#define ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace vibrator {
+namespace V1_3 {
+namespace implementation {
+
+using android::hardware::vibrator::V1_0::EffectStrength;
+using android::hardware::vibrator::V1_0::Status;
+using android::hardware::vibrator::V1_2::Effect;
+
+class Vibrator : public IVibrator {
+ public:
+ Vibrator();
+
+ // Methods from ::android::hardware::vibrator::V1_0::IVibrator follow.
+ Return<Status> on(uint32_t timeoutMs) override;
+ Return<Status> off() override;
+ Return<bool> supportsAmplitudeControl() override;
+ Return<Status> setAmplitude(uint8_t amplitude) override;
+ Return<void> perform(V1_0::Effect effect, EffectStrength strength,
+ perform_cb _hidl_cb) override;
+
+ // Methods from ::android::hardware::vibrator::V1_1::IVibrator follow.
+ Return<void> perform_1_1(V1_1::Effect_1_1 effect, EffectStrength strength,
+ perform_cb _hidl_cb) override;
+
+ // Methods from ::android::hardware::vibrator::V1_2::IVibrator follow.
+ Return<void> perform_1_2(Effect effect, EffectStrength strength, perform_cb _hidl_cb) override;
+
+ // Methods from ::android::hardware::vibrator::V1_3::IVibrator follow.
+ Return<bool> supportsExternalControl() override;
+ Return<Status> setExternalControl(bool enabled) override;
+
+ private:
+ Status enable(bool enabled);
+ Status activate(uint32_t ms);
+ void timeout();
+
+ static void timerCallback(union sigval sigval);
+ static const char* effectToName(Effect effect);
+ static uint32_t effectToMs(Effect effect);
+ static uint8_t strengthToAmplitude(EffectStrength strength);
+
+ private:
+ bool mEnabled{false};
+ uint8_t mAmplitude{UINT8_MAX};
+ bool mExternalControl{false};
+ std::mutex mMutex;
+ timer_t mTimer{nullptr};
+};
+} // namespace implementation
+} // namespace V1_3
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_VIBRATOR_V1_3_VIBRATOR_H
diff --git a/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.rc b/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.rc
new file mode 100644
index 0000000..ed7a562
--- /dev/null
+++ b/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.rc
@@ -0,0 +1,4 @@
+service vendor.vibrator-1-3 /vendor/bin/hw/android.hardware.vibrator@1.3-service.example
+ class hal
+ user system
+ group system
diff --git a/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.xml b/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.xml
new file mode 100644
index 0000000..172aa21
--- /dev/null
+++ b/vibrator/1.3/example/android.hardware.vibrator@1.3-service.example.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.vibrator</name>
+ <transport>hwbinder</transport>
+ <version>1.3</version>
+ <interface>
+ <name>IVibrator</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/vibrator/1.3/example/service.cpp b/vibrator/1.3/example/service.cpp
new file mode 100644
index 0000000..449996e
--- /dev/null
+++ b/vibrator/1.3/example/service.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "android.hardware.vibrator@1.3-service.example"
+
+#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "Vibrator.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::vibrator::V1_3::IVibrator;
+using android::hardware::vibrator::V1_3::implementation::Vibrator;
+using namespace android;
+
+status_t registerVibratorService() {
+ sp<IVibrator> vibrator = new Vibrator();
+
+ return vibrator->registerAsService();
+}
+
+int main() {
+ configureRpcThreadpool(1, true);
+ status_t status = registerVibratorService();
+
+ if (status != OK) {
+ return status;
+ }
+
+ joinRpcThreadpool();
+
+ return 1;
+}
diff --git a/wifi/1.3/default/Android.mk b/wifi/1.3/default/Android.mk
index 8312c31..01fa934 100644
--- a/wifi/1.3/default/Android.mk
+++ b/wifi/1.3/default/Android.mk
@@ -98,6 +98,37 @@
include $(BUILD_EXECUTABLE)
###
+### android.hardware.wifi daemon
+###
+include $(CLEAR_VARS)
+LOCAL_MODULE := android.hardware.wifi@1.0-service-lazy
+LOCAL_OVERRIDES_MODULES := android.hardware.wifi@1.0-service
+LOCAL_CFLAGS := -DLAZY_SERVICE
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_CPPFLAGS := -Wall -Werror -Wextra
+LOCAL_SRC_FILES := \
+ service.cpp
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libcutils \
+ libhidlbase \
+ libhidltransport \
+ liblog \
+ libnl \
+ libutils \
+ libwifi-hal \
+ libwifi-system-iface \
+ android.hardware.wifi@1.0 \
+ android.hardware.wifi@1.1 \
+ android.hardware.wifi@1.2 \
+ android.hardware.wifi@1.3
+LOCAL_STATIC_LIBRARIES := \
+ android.hardware.wifi@1.0-service-lib
+LOCAL_INIT_RC := android.hardware.wifi@1.0-service-lazy.rc
+include $(BUILD_EXECUTABLE)
+
+###
### android.hardware.wifi unit tests.
###
include $(CLEAR_VARS)
diff --git a/wifi/1.3/default/android.hardware.wifi@1.0-service-lazy.rc b/wifi/1.3/default/android.hardware.wifi@1.0-service-lazy.rc
new file mode 100644
index 0000000..cf917b5
--- /dev/null
+++ b/wifi/1.3/default/android.hardware.wifi@1.0-service-lazy.rc
@@ -0,0 +1,8 @@
+service vendor.wifi_hal_legacy /vendor/bin/hw/android.hardware.wifi@1.0-service-lazy
+ interface android.hardware.wifi@1.0::IWifi default
+ oneshot
+ disabled
+ class hal
+ capabilities NET_ADMIN NET_RAW SYS_MODULE
+ user wifi
+ group wifi gps
diff --git a/wifi/1.3/default/service.cpp b/wifi/1.3/default/service.cpp
index 5fd83c1..5daf659 100644
--- a/wifi/1.3/default/service.cpp
+++ b/wifi/1.3/default/service.cpp
@@ -15,6 +15,7 @@
*/
#include <android-base/logging.h>
+#include <hidl/HidlLazyUtils.h>
#include <hidl/HidlTransportSupport.h>
#include <utils/Looper.h>
#include <utils/StrongPointer.h>
@@ -26,12 +27,19 @@
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
+using android::hardware::LazyServiceRegistrar;
using android::hardware::wifi::V1_3::implementation::feature_flags::
WifiFeatureFlags;
using android::hardware::wifi::V1_3::implementation::legacy_hal::WifiLegacyHal;
using android::hardware::wifi::V1_3::implementation::mode_controller::
WifiModeController;
+#ifdef LAZY_SERVICE
+const bool kLazyService = true;
+#else
+const bool kLazyService = false;
+#endif
+
int main(int /*argc*/, char** argv) {
android::base::InitLogging(
argv, android::base::LogdLogger(android::base::SYSTEM));
@@ -45,8 +53,14 @@
std::make_shared<WifiLegacyHal>(),
std::make_shared<WifiModeController>(),
std::make_shared<WifiFeatureFlags>());
- CHECK_EQ(service->registerAsService(), android::NO_ERROR)
- << "Failed to register wifi HAL";
+ if (kLazyService) {
+ LazyServiceRegistrar registrar;
+ CHECK_EQ(registrar.registerService(service), android::NO_ERROR)
+ << "Failed to register wifi HAL";
+ } else {
+ CHECK_EQ(service->registerAsService(), android::NO_ERROR)
+ << "Failed to register wifi HAL";
+ }
joinRpcThreadpool();