Merge "MH2 | Implement dynamic sensors callbacks on HalProxy"
diff --git a/boot/1.1/Android.bp b/boot/1.1/Android.bp
new file mode 100644
index 0000000..6a8d57a
--- /dev/null
+++ b/boot/1.1/Android.bp
@@ -0,0 +1,18 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.boot@1.1",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "IBootControl.hal",
+ ],
+ interfaces: [
+ "android.hardware.boot@1.0",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/boot/1.1/IBootControl.hal b/boot/1.1/IBootControl.hal
new file mode 100644
index 0000000..939dfb3
--- /dev/null
+++ b/boot/1.1/IBootControl.hal
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.boot@1.1;
+
+import @1.0::IBootControl;
+
+interface IBootControl extends @1.0::IBootControl {
+ /**
+ * Sets whether a snapshot-merge of any dynamic partition is in progress.
+ *
+ * After the merge status is set to a given value, subsequent calls to
+ * getSnapshotMergeStatus must return the set value.
+ *
+ * The merge status must be persistent across reboots. That is, getSnapshotMergeStatus
+ * must return the same value after a reboot if the merge status is not altered in any way
+ * (e.g. set by setSnapshotMergeStatus or set to CANCELLED by bootloader).
+ *
+ * Read/write access to the merge status must be atomic. When the HAL is processing a
+ * setSnapshotMergeStatus call, all subsequent calls to getSnapshotMergeStatus must block until
+ * setSnapshotMergeStatus has returned.
+ *
+ * A MERGING state indicates that dynamic partitions are partially comprised by blocks in the
+ * userdata partition.
+ *
+ * When the merge status is set to MERGING, the following operations must be prohibited from the
+ * bootloader:
+ * - Flashing or erasing "userdata" or "metadata".
+ *
+ * The following operations may be prohibited when the status is set to MERGING. If not
+ * prohibited, it is recommended that the user receive a warning.
+ * - Changing the active slot (e.g. via "fastboot set_active")
+ *
+ * @param status Merge status.
+ *
+ * @return success True on success, false otherwise.
+ */
+ setSnapshotMergeStatus(MergeStatus status) generates (bool success);
+
+ /**
+ * Returns whether a snapshot-merge of any dynamic partition is in progress.
+ *
+ * This function must return the merge status set by the last setSnapshotMergeStatus call and
+ * recorded by the bootloader with one exception. If the partitions are being flashed from the
+ * bootloader such that the pending merge must be canceled (for example, if the super partition
+ * is being flashed), this function must return CANCELLED.
+ *
+ * @return success True if the merge status is read successfully, false otherwise.
+ * @return status Merge status.
+ */
+ getSnapshotMergeStatus() generates (MergeStatus status);
+};
+
diff --git a/boot/1.1/default/Android.bp b/boot/1.1/default/Android.bp
new file mode 100644
index 0000000..dca5c26
--- /dev/null
+++ b/boot/1.1/default/Android.bp
@@ -0,0 +1,43 @@
+cc_library_shared {
+ name: "android.hardware.boot@1.1-impl",
+ defaults: [
+ "hidl_defaults",
+ "libboot_control_defaults",
+ ],
+ relative_install_path: "hw",
+ vendor: true,
+ recovery_available: true,
+ srcs: ["BootControl.cpp"],
+
+ shared_libs: [
+ "liblog",
+ "libhidlbase",
+ "libhardware",
+ "libutils",
+ "android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
+ ],
+ static_libs: [
+ "libboot_control",
+ "libfstab",
+ ],
+}
+
+cc_binary {
+ name: "android.hardware.boot@1.1-service",
+ defaults: ["hidl_defaults"],
+ relative_install_path: "hw",
+ vendor: true,
+ init_rc: ["android.hardware.boot@1.1-service.rc"],
+ srcs: ["service.cpp"],
+
+ shared_libs: [
+ "liblog",
+ "libhardware",
+ "libhidlbase",
+ "libutils",
+ "android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
+ ],
+
+}
diff --git a/boot/1.1/default/BootControl.cpp b/boot/1.1/default/BootControl.cpp
new file mode 100644
index 0000000..c9c62a4
--- /dev/null
+++ b/boot/1.1/default/BootControl.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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.boot@1.1-impl"
+
+#include <memory>
+
+#include <log/log.h>
+
+#include "BootControl.h"
+
+namespace android {
+namespace hardware {
+namespace boot {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::boot::V1_0::CommandResult;
+
+bool BootControl::Init() {
+ return impl_.Init();
+}
+
+// Methods from ::android::hardware::boot::V1_0::IBootControl follow.
+Return<uint32_t> BootControl::getNumberSlots() {
+ return impl_.GetNumberSlots();
+}
+
+Return<uint32_t> BootControl::getCurrentSlot() {
+ return impl_.GetCurrentSlot();
+}
+
+Return<void> BootControl::markBootSuccessful(markBootSuccessful_cb _hidl_cb) {
+ struct CommandResult cr;
+ if (impl_.MarkBootSuccessful()) {
+ cr.success = true;
+ cr.errMsg = "Success";
+ } else {
+ cr.success = false;
+ cr.errMsg = "Operation failed";
+ }
+ _hidl_cb(cr);
+ return Void();
+}
+
+Return<void> BootControl::setActiveBootSlot(uint32_t slot, setActiveBootSlot_cb _hidl_cb) {
+ struct CommandResult cr;
+ if (impl_.SetActiveBootSlot(slot)) {
+ cr.success = true;
+ cr.errMsg = "Success";
+ } else {
+ cr.success = false;
+ cr.errMsg = "Operation failed";
+ }
+ _hidl_cb(cr);
+ return Void();
+}
+
+Return<void> BootControl::setSlotAsUnbootable(uint32_t slot, setSlotAsUnbootable_cb _hidl_cb) {
+ struct CommandResult cr;
+ if (impl_.SetSlotAsUnbootable(slot)) {
+ cr.success = true;
+ cr.errMsg = "Success";
+ } else {
+ cr.success = false;
+ cr.errMsg = "Operation failed";
+ }
+ _hidl_cb(cr);
+ return Void();
+}
+
+Return<BoolResult> BootControl::isSlotBootable(uint32_t slot) {
+ if (!impl_.IsValidSlot(slot)) {
+ return BoolResult::INVALID_SLOT;
+ }
+ return impl_.IsSlotBootable(slot) ? BoolResult::TRUE : BoolResult::FALSE;
+}
+
+Return<BoolResult> BootControl::isSlotMarkedSuccessful(uint32_t slot) {
+ if (!impl_.IsValidSlot(slot)) {
+ return BoolResult::INVALID_SLOT;
+ }
+ return impl_.IsSlotMarkedSuccessful(slot) ? BoolResult::TRUE : BoolResult::FALSE;
+}
+
+Return<void> BootControl::getSuffix(uint32_t slot, getSuffix_cb _hidl_cb) {
+ hidl_string ans;
+ const char* suffix = impl_.GetSuffix(slot);
+ if (suffix) {
+ ans = suffix;
+ }
+ _hidl_cb(ans);
+ return Void();
+}
+
+Return<bool> BootControl::setSnapshotMergeStatus(MergeStatus status) {
+ return impl_.SetSnapshotMergeStatus(status);
+}
+
+Return<MergeStatus> BootControl::getSnapshotMergeStatus() {
+ return impl_.GetSnapshotMergeStatus();
+}
+
+IBootControl* HIDL_FETCH_IBootControl(const char* /* hal */) {
+ auto module = std::make_unique<BootControl>();
+ if (!module->Init()) {
+ ALOGE("Could not initialize BootControl module");
+ return nullptr;
+ }
+ return module.release();
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace boot
+} // namespace hardware
+} // namespace android
diff --git a/boot/1.1/default/BootControl.h b/boot/1.1/default/BootControl.h
new file mode 100644
index 0000000..75511b6
--- /dev/null
+++ b/boot/1.1/default/BootControl.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/boot/1.1/IBootControl.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <libboot_control/libboot_control.h>
+
+namespace android {
+namespace hardware {
+namespace boot {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::boot::V1_0::BoolResult;
+using ::android::hardware::boot::V1_1::IBootControl;
+using ::android::hardware::boot::V1_1::MergeStatus;
+
+class BootControl : public IBootControl {
+ public:
+ bool Init();
+
+ // Methods from ::android::hardware::boot::V1_0::IBootControl follow.
+ Return<uint32_t> getNumberSlots() override;
+ Return<uint32_t> getCurrentSlot() override;
+ Return<void> markBootSuccessful(markBootSuccessful_cb _hidl_cb) override;
+ Return<void> setActiveBootSlot(uint32_t slot, setActiveBootSlot_cb _hidl_cb) override;
+ Return<void> setSlotAsUnbootable(uint32_t slot, setSlotAsUnbootable_cb _hidl_cb) override;
+ Return<BoolResult> isSlotBootable(uint32_t slot) override;
+ Return<BoolResult> isSlotMarkedSuccessful(uint32_t slot) override;
+ Return<void> getSuffix(uint32_t slot, getSuffix_cb _hidl_cb) override;
+
+ // Methods from ::android::hardware::boot::V1_1::IBootControl follow.
+ Return<bool> setSnapshotMergeStatus(MergeStatus status) override;
+ Return<MergeStatus> getSnapshotMergeStatus() override;
+
+ private:
+ android::bootable::BootControl impl_;
+};
+
+extern "C" IBootControl* HIDL_FETCH_IBootControl(const char* name);
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace boot
+} // namespace hardware
+} // namespace android
diff --git a/boot/1.1/default/android.hardware.boot@1.1-service.rc b/boot/1.1/default/android.hardware.boot@1.1-service.rc
new file mode 100644
index 0000000..83fa9d0
--- /dev/null
+++ b/boot/1.1/default/android.hardware.boot@1.1-service.rc
@@ -0,0 +1,6 @@
+service vendor.boot-hal-1-1 /vendor/bin/hw/android.hardware.boot@1.1-service
+ interface android.hardware.boot@1.0::IBootControl default
+ interface android.hardware.boot@1.1::IBootControl default
+ class early_hal
+ user root
+ group root
diff --git a/boot/1.1/default/service.cpp b/boot/1.1/default/service.cpp
new file mode 100644
index 0000000..b24b464
--- /dev/null
+++ b/boot/1.1/default/service.cpp
@@ -0,0 +1,26 @@
+/*
+ * 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.boot@1.1-service"
+
+#include <android/hardware/boot/1.1/IBootControl.h>
+#include <hidl/LegacySupport.h>
+
+using android::hardware::defaultPassthroughServiceImplementation;
+using ::android::hardware::boot::V1_1::IBootControl;
+
+int main(int /* argc */, char* /* argv */[]) {
+ return defaultPassthroughServiceImplementation<IBootControl>();
+}
diff --git a/boot/1.1/types.hal b/boot/1.1/types.hal
new file mode 100644
index 0000000..6346078
--- /dev/null
+++ b/boot/1.1/types.hal
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.boot@1.1;
+
+enum MergeStatus : int32_t {
+ /**
+ * No snapshot or merge is in progress.
+ */
+ NONE = 0,
+
+ /**
+ * The merge status could not be determined.
+ */
+ UNKNOWN,
+
+ /**
+ * Partitions are being snapshotted, but no merge has been started.
+ */
+ SNAPSHOTTED,
+
+ /**
+ * At least one partition has merge is in progress.
+ */
+ MERGING,
+
+ /**
+ * A merge was in progress, but it was canceled by the bootloader.
+ */
+ CANCELLED,
+};
diff --git a/boot/1.1/vts/functional/Android.bp b/boot/1.1/vts/functional/Android.bp
new file mode 100644
index 0000000..49ea09a
--- /dev/null
+++ b/boot/1.1/vts/functional/Android.bp
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "VtsHalBootV1_1TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalBootV1_1TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
+ "libgmock",
+ ],
+ test_suites: ["device-tests"],
+}
+
diff --git a/boot/1.1/vts/functional/VtsHalBootV1_1TargetTest.cpp b/boot/1.1/vts/functional/VtsHalBootV1_1TargetTest.cpp
new file mode 100644
index 0000000..fba9a5e
--- /dev/null
+++ b/boot/1.1/vts/functional/VtsHalBootV1_1TargetTest.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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 "boot_hidl_hal_test"
+
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android/hardware/boot/1.1/IBootControl.h>
+#include <android/hardware/boot/1.1/types.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <unistd.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_enum_range;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::boot::V1_1::IBootControl;
+using ::android::hardware::boot::V1_1::MergeStatus;
+using ::testing::Contains;
+
+class BootHidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ boot = IBootControl::getService(GetParam());
+ ASSERT_NE(boot, nullptr);
+
+ LOG(INFO) << "Test is remote " << boot->isRemote();
+ }
+
+ sp<IBootControl> boot;
+};
+
+static std::vector<MergeStatus> ValidMergeStatusValues() {
+ std::vector<MergeStatus> values;
+ for (const auto value : hidl_enum_range<MergeStatus>()) {
+ if (value == MergeStatus::UNKNOWN) {
+ continue;
+ }
+ values.push_back(value);
+ }
+ return values;
+}
+
+/**
+ * Ensure merge status can be retrieved.
+ */
+TEST_P(BootHidlTest, GetSnapshotMergeStatus) {
+ auto values = ValidMergeStatusValues();
+ auto status = (MergeStatus)boot->getSnapshotMergeStatus();
+ EXPECT_THAT(values, Contains(status));
+}
+
+/**
+ * Ensure merge status can be set to arbitrary value.
+ */
+TEST_P(BootHidlTest, SetSnapshotMergeStatus) {
+ for (const auto value : ValidMergeStatusValues()) {
+ EXPECT_TRUE(boot->setSnapshotMergeStatus(value).withDefault(false));
+ auto status = boot->getSnapshotMergeStatus();
+ EXPECT_EQ(status, value);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ , BootHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBootControl::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 409c5a2..a409650 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -89,7 +89,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.boot</name>
- <version>1.0</version>
+ <version>1.1</version>
<interface>
<name>IBootControl</name>
<instance>default</instance>
diff --git a/contexthub/1.0/default/OWNERS b/contexthub/1.0/default/OWNERS
index 5373073..90c2330 100644
--- a/contexthub/1.0/default/OWNERS
+++ b/contexthub/1.0/default/OWNERS
@@ -1,4 +1,3 @@
-aarossig@google.com
arthuri@google.com
bduddie@google.com
-bstack@google.com
+stange@google.com
diff --git a/contexthub/1.0/vts/functional/OWNERS b/contexthub/1.0/vts/functional/OWNERS
index ee01441..045cc4e 100644
--- a/contexthub/1.0/vts/functional/OWNERS
+++ b/contexthub/1.0/vts/functional/OWNERS
@@ -1,8 +1,7 @@
#Context Hub team
-aarossig@google.com
arthuri@google.com
bduddie@google.com
-bstack@google.com
+stange@google.com
#VTS team
yim@google.com
diff --git a/current.txt b/current.txt
index e164a3f..4e0d0e0 100644
--- a/current.txt
+++ b/current.txt
@@ -585,11 +585,16 @@
fd65298e1e09e0e3c781ab18305920d757dbe55a3b459ce17814ec5cf6dfee99 android.hardware.wifi@1.0::IWifiP2pIface
# HALs released in Android R
+07d0a252b2d8fa35887908a996ba395cf392968395fc30afab791f46e0c22a52 android.hardware.boot@1.1::IBootControl
+74049a402be913963edfdd80828a53736570e9d8124a1bf18166b6ed46a6b0ab android.hardware.boot@1.1::types
34515afa2bb792d3c6d8495a5f5d907d179c8507ca5e55c10050d02ae1d516ef android.hardware.neuralnetworks@1.3::IDevice
-e2d20d4eb24f40b44a3766d05f77052581cb3f4df35fb48c0cc5d9cdcf5c872e android.hardware.neuralnetworks@1.3::types
+b74fe72cfe438f50e772e6a307657ff449d5bde83c15dd1f140ff2edbe73499c android.hardware.neuralnetworks@1.3::types
04395b26be33db17747c3d3b0e8066d323f891ff4f9f3b3ddb490b2f3f844a18 android.hardware.wifi@1.4::IWifi
270f0eb670dfd9bc5cd718e09711f2534fa8425f54d06c1a46523ca156b509e2 android.hardware.wifi.supplicant@1.3::ISupplicant
dd4b7cfbb6e1c6ff011c33920762ad89dd02240c63a4d3a3d5037f154eae3e3b android.hardware.wifi.supplicant@1.3::ISupplicantStaIface
619fc9839ec6e369cfa9b28e3e9412e6885720ff8f9b5750c1b6ffb905120391 android.hardware.wifi.supplicant@1.3::ISupplicantStaIfaceCallback
6fe09b18e913608579638594788198ec45bb2369e567d7df661db46c4f0e5f08 android.hardware.wifi.supplicant@1.3::ISupplicantStaNetwork
91931b05bd70ea6bdffbe075086183f803379571788564e28854207620eb75cf android.hardware.wifi.supplicant@1.3::types
+033eae03c09ebc75e82db37bc39995dfaa9086745577b44d9e14e9ccb48bd8cc android.hardware.vibrator@1.4::types
+544049dcda3f943ad67d83d5277f06681a3782982a9af5a78b5d4e8d295d061a android.hardware.vibrator@1.4::IVibrator
+5e1c12efbbba89c9143d10b1b90eceff8bc79aa079f5106215b528e104fef101 android.hardware.vibrator@1.4::IVibratorCallback
diff --git a/drm/TEST_MAPPING b/drm/TEST_MAPPING
new file mode 100644
index 0000000..cff6819
--- /dev/null
+++ b/drm/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "imports": [
+ // gts and cts filters
+ {
+ "path": "frameworks/av/drm/libmediadrm"
+ }
+ ]
+}
diff --git a/graphics/mapper/2.0/utils/hal/include/mapper-hal/2.0/Mapper.h b/graphics/mapper/2.0/utils/hal/include/mapper-hal/2.0/Mapper.h
index 5ad2a65..8134174 100644
--- a/graphics/mapper/2.0/utils/hal/include/mapper-hal/2.0/Mapper.h
+++ b/graphics/mapper/2.0/utils/hal/include/mapper-hal/2.0/Mapper.h
@@ -80,17 +80,21 @@
}
Return<Error> freeBuffer(void* buffer) override {
- native_handle_t* bufferHandle = removeImportedBuffer(buffer);
+ native_handle_t* bufferHandle = getImportedBuffer(buffer);
if (!bufferHandle) {
return Error::BAD_BUFFER;
}
- return mHal->freeBuffer(bufferHandle);
+ Error error = mHal->freeBuffer(bufferHandle);
+ if (error == Error::NONE) {
+ removeImportedBuffer(buffer);
+ }
+ return error;
}
Return<void> lock(void* buffer, uint64_t cpuUsage, const V2_0::IMapper::Rect& accessRegion,
const hidl_handle& acquireFence, IMapper::lock_cb hidl_cb) override {
- const native_handle_t* bufferHandle = getImportedBuffer(buffer);
+ const native_handle_t* bufferHandle = getConstImportedBuffer(buffer);
if (!bufferHandle) {
hidl_cb(Error::BAD_BUFFER, nullptr);
return Void();
@@ -112,7 +116,7 @@
Return<void> lockYCbCr(void* buffer, uint64_t cpuUsage, const V2_0::IMapper::Rect& accessRegion,
const hidl_handle& acquireFence,
IMapper::lockYCbCr_cb hidl_cb) override {
- const native_handle_t* bufferHandle = getImportedBuffer(buffer);
+ const native_handle_t* bufferHandle = getConstImportedBuffer(buffer);
if (!bufferHandle) {
hidl_cb(Error::BAD_BUFFER, YCbCrLayout{});
return Void();
@@ -132,7 +136,7 @@
}
Return<void> unlock(void* buffer, IMapper::unlock_cb hidl_cb) override {
- const native_handle_t* bufferHandle = getImportedBuffer(buffer);
+ const native_handle_t* bufferHandle = getConstImportedBuffer(buffer);
if (!bufferHandle) {
hidl_cb(Error::BAD_BUFFER, nullptr);
return Void();
@@ -160,7 +164,11 @@
return static_cast<native_handle_t*>(buffer);
}
- virtual const native_handle_t* getImportedBuffer(void* buffer) const {
+ virtual native_handle_t* getImportedBuffer(void* buffer) const {
+ return static_cast<native_handle_t*>(buffer);
+ }
+
+ virtual const native_handle_t* getConstImportedBuffer(void* buffer) const {
return static_cast<const native_handle_t*>(buffer);
}
diff --git a/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/GrallocLoader.h b/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/GrallocLoader.h
index e8b1b4b..85a91c3 100644
--- a/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/GrallocLoader.h
+++ b/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/GrallocLoader.h
@@ -68,7 +68,14 @@
return mBufferHandles.erase(bufferHandle) == 1 ? bufferHandle : nullptr;
}
- const native_handle_t* get(void* buffer) {
+ native_handle_t* get(void* buffer) {
+ auto bufferHandle = static_cast<native_handle_t*>(buffer);
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mBufferHandles.count(bufferHandle) == 1 ? bufferHandle : nullptr;
+ }
+
+ const native_handle_t* getConst(void* buffer) {
auto bufferHandle = static_cast<const native_handle_t*>(buffer);
std::lock_guard<std::mutex> lock(mMutex);
@@ -92,9 +99,13 @@
return GrallocImportedBufferPool::getInstance().remove(buffer);
}
- const native_handle_t* getImportedBuffer(void* buffer) const override {
+ native_handle_t* getImportedBuffer(void* buffer) const override {
return GrallocImportedBufferPool::getInstance().get(buffer);
}
+
+ const native_handle_t* getConstImportedBuffer(void* buffer) const override {
+ return GrallocImportedBufferPool::getInstance().getConst(buffer);
+ }
};
class GrallocLoader {
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
index 97dab68..cb29c64 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
@@ -280,39 +280,50 @@
*/
template <typename ValueT>
class NullOr {
- template <typename T>
- struct reference_initializer {
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wnull-dereference"
- static T&& init() { return *static_cast<std::remove_reference_t<T>*>(nullptr); }
-#pragma GCC diagnostic pop
- };
- template <typename T>
- struct pointer_initializer {
- static T init() { return nullptr; }
- };
- template <typename T>
- struct value_initializer {
- static T init() { return T(); }
- };
- template <typename T>
- using initializer_t =
- std::conditional_t<std::is_lvalue_reference<T>::value, reference_initializer<T>,
- std::conditional_t<std::is_pointer<T>::value, pointer_initializer<T>,
- value_initializer<T>>>;
+ using internal_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value,
+ std::remove_reference_t<ValueT>*, ValueT>;
- public:
- NullOr() : value_(initializer_t<ValueT>::init()), null_(true) {}
- NullOr(ValueT&& value) : value_(std::forward<ValueT>(value)), null_(false) {}
+ struct pointer_initializer {
+ static std::nullptr_t init() { return nullptr; }
+ };
+ struct value_initializer {
+ static ValueT init() { return ValueT(); }
+ };
+ struct value_pointer_deref_t {
+ static ValueT& deref(ValueT& v) { return v; }
+ };
+ struct reference_deref_t {
+ static auto& deref(internal_t v) { return *v; }
+ };
+ using initializer_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value ||
+ std::is_pointer<ValueT>::value,
+ pointer_initializer, value_initializer>;
+ using deref_t = std::conditional_t<std::is_lvalue_reference<ValueT>::value, reference_deref_t,
+ value_pointer_deref_t>;
+
+ public:
+ NullOr() : value_(initializer_t::init()), null_(true) {}
+ template <typename T>
+ NullOr(T&& value, typename std::enable_if<
+ !std::is_lvalue_reference<ValueT>::value &&
+ std::is_same<std::decay_t<ValueT>, std::decay_t<T>>::value,
+ int>::type = 0)
+ : value_(std::forward<ValueT>(value)), null_(false) {}
+ template <typename T>
+ NullOr(T& value, typename std::enable_if<
+ std::is_lvalue_reference<ValueT>::value &&
+ std::is_same<std::decay_t<ValueT>, std::decay_t<T>>::value,
+ int>::type = 0)
+ : value_(&value), null_(false) {}
bool isOk() const { return !null_; }
- const ValueT& value() const & { return value_; }
- ValueT& value() & { return value_; }
- ValueT&& value() && { return std::move(value_); }
+ const ValueT& value() const& { return deref_t::deref(value_); }
+ ValueT& value() & { return deref_t::deref(value_); }
+ ValueT&& value() && { return std::move(deref_t::deref(value_)); }
- private:
- ValueT value_;
+ private:
+ internal_t value_;
bool null_;
};
diff --git a/light/utils/main.cpp b/light/utils/main.cpp
index d07e799..b834132 100644
--- a/light/utils/main.cpp
+++ b/light/utils/main.cpp
@@ -25,7 +25,7 @@
std::cerr << msg << std::endl;
}
-int main() {
+int main(int argc, char* argv[]) {
using ::android::hardware::hidl_vec;
using ::android::hardware::light::V2_0::Brightness;
using ::android::hardware::light::V2_0::Flash;
@@ -41,10 +41,29 @@
return -1;
}
- const static LightState off = {
- .color = 0u, .flashMode = Flash::NONE, .brightnessMode = Brightness::USER,
+ static LightState off = {
+ .color = 0u,
+ .flashMode = Flash::NONE,
+ .brightnessMode = Brightness::USER,
};
+ if (argc > 2) {
+ error("Usage: blank_screen [color]");
+ return -1;
+ }
+
+ if (argc > 1) {
+ char* col_ptr;
+ unsigned int col_new;
+
+ col_new = strtoul(argv[1], &col_ptr, 0);
+ if (*col_ptr != '\0') {
+ error("Failed to convert " + std::string(argv[1]) + " to number");
+ return -1;
+ }
+ off.color = col_new;
+ }
+
service->getSupportedTypes([&](const hidl_vec<Type>& types) {
for (Type type : types) {
Status ret = service->setLight(type, off);
diff --git a/neuralnetworks/1.2/types.t b/neuralnetworks/1.2/types.t
index cab330d..d197f6b 100644
--- a/neuralnetworks/1.2/types.t
+++ b/neuralnetworks/1.2/types.t
@@ -41,27 +41,7 @@
enum OperandType : @1.0::OperandType {
%insert Operand_1.2
-
- /*
- * DEPRECATED. Since HAL version 1.2, extensions are the preferred
- * alternative to OEM operation and data types.
- *
- * OEM specific scalar value.
- * OEM = 10000,
- */
- /*
- * DEPRECATED. Since HAL version 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.
- */
- /* ADDING A NEW OEM TYPE REQUIRES UPDATING THE VALUE OF
- * OperandTypeRange::OEM_MAX.
- */
+%insert OEMDeprecationAndOperandTypeRangeMaxComment
};
/**
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
index 2beec98..aacb385 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.cpp
@@ -33,6 +33,7 @@
#include <gtest/gtest.h>
#include <algorithm>
+#include <chrono>
#include <iostream>
#include <numeric>
@@ -190,7 +191,8 @@
}
static std::shared_ptr<::android::nn::ExecutionBurstController> CreateBurst(
const sp<IPreparedModel>& preparedModel) {
- return android::nn::ExecutionBurstController::create(preparedModel, /*blocking=*/true);
+ return android::nn::ExecutionBurstController::create(preparedModel,
+ std::chrono::microseconds{0});
}
enum class Executor { ASYNC, SYNC, BURST };
@@ -254,8 +256,10 @@
}
// execute burst
- std::tie(executionStatus, outputShapes, timing) =
+ int n;
+ std::tie(n, outputShapes, timing, std::ignore) =
controller->compute(request, measure, keys);
+ executionStatus = nn::convertResultCodeToErrorStatus(n);
break;
}
diff --git a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
index 1d4493d..416744f 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp
@@ -26,6 +26,7 @@
#include "Utils.h"
#include <android-base/logging.h>
+#include <chrono>
#include <cstring>
namespace android::hardware::neuralnetworks::V1_2::vts::functional {
@@ -64,9 +65,9 @@
// create FMQ objects
auto [fmqRequestChannel, fmqRequestDescriptor] =
- RequestChannelSender::create(kExecutionBurstChannelLength, /*blocking=*/true);
+ RequestChannelSender::create(kExecutionBurstChannelLength);
auto [fmqResultChannel, fmqResultDescriptor] =
- ResultChannelReceiver::create(resultChannelLength, /*blocking=*/true);
+ ResultChannelReceiver::create(resultChannelLength, std::chrono::microseconds{0});
ASSERT_NE(nullptr, fmqRequestChannel.get());
ASSERT_NE(nullptr, fmqResultChannel.get());
ASSERT_NE(nullptr, fmqRequestDescriptor);
@@ -293,8 +294,10 @@
}
// collect serialized result by running regular burst
- const auto [statusRegular, outputShapesRegular, timingRegular] =
+ const auto [nRegular, outputShapesRegular, timingRegular, fallbackRegular] =
controllerRegular->compute(request, MeasureTiming::NO, keys);
+ const ErrorStatus statusRegular = nn::convertResultCodeToErrorStatus(nRegular);
+ EXPECT_FALSE(fallbackRegular);
// skip test if regular burst output isn't useful for testing a failure
// caused by having too small of a length for the result FMQ
@@ -307,11 +310,13 @@
// by this point, execution should fail because the result channel isn't
// large enough to return the serialized result
- const auto [statusSmall, outputShapesSmall, timingSmall] =
+ const auto [nSmall, outputShapesSmall, timingSmall, fallbackSmall] =
controllerSmall->compute(request, MeasureTiming::NO, keys);
+ const ErrorStatus statusSmall = nn::convertResultCodeToErrorStatus(nSmall);
EXPECT_NE(ErrorStatus::NONE, statusSmall);
EXPECT_EQ(0u, outputShapesSmall.size());
EXPECT_TRUE(badTiming(timingSmall));
+ EXPECT_FALSE(fallbackSmall);
}
static bool isSanitized(const FmqResultDatum& datum) {
diff --git a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
index f25ee62..2d83b81 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "neuralnetworks_hidl_hal_test"
+#include <chrono>
#include "1.0/Utils.h"
#include "1.2/Callbacks.h"
#include "ExecutionBurstController.h"
@@ -94,7 +95,8 @@
// create burst
std::shared_ptr<::android::nn::ExecutionBurstController> burst =
- android::nn::ExecutionBurstController::create(preparedModel, /*blocking=*/true);
+ android::nn::ExecutionBurstController::create(preparedModel,
+ std::chrono::microseconds{0});
ASSERT_NE(nullptr, burst.get());
// create memory keys
@@ -104,13 +106,12 @@
}
// execute and verify
- ErrorStatus error;
- std::vector<OutputShape> outputShapes;
- Timing timing;
- std::tie(error, outputShapes, timing) = burst->compute(request, measure, keys);
- EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
+ const auto [n, outputShapes, timing, fallback] = burst->compute(request, measure, keys);
+ const ErrorStatus status = nn::convertResultCodeToErrorStatus(n);
+ EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
EXPECT_EQ(outputShapes.size(), 0);
EXPECT_TRUE(badTiming(timing));
+ EXPECT_FALSE(fallback);
// additional burst testing
if (request.pools.size() > 0) {
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index db5dd51..86ab287 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -25,13 +25,6 @@
import android.hidl.safe_union@1.0::Monostate;
-/**
- * NOTE: Since NNAPI 1.2, OEM operation and data type are deprecated. Extensions
- * are the preferred alternative.
- *
- * NOTE: Adding a new fundamental type requires updating the value of
- * OperandTypeRange::FUNDAMENTAL_MAX.
- */
enum OperandType : @1.2::OperandType {
/**
* A tensor of 8 bit signed integers that represent real numbers.
@@ -43,10 +36,29 @@
*
* The formula is:
* real_value = (integer_value - zeroPoint) * scale.
- *
- * Available since API level 30.
*/
TENSOR_QUANT8_ASYMM_SIGNED = 14,
+
+ /*
+ * DEPRECATED. Since HAL version 1.2, extensions are the preferred
+ * alternative to OEM operation and data types.
+ *
+ * OEM specific scalar value.
+ * OEM = 10000,
+ */
+ /*
+ * DEPRECATED. Since HAL version 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.
+ */
+ /* ADDING A NEW OEM TYPE REQUIRES UPDATING THE VALUE OF
+ * OperandTypeRange::OEM_MAX.
+ */
};
/**
diff --git a/neuralnetworks/1.3/types.t b/neuralnetworks/1.3/types.t
new file mode 100644
index 0000000..d41cfd2
--- /dev/null
+++ b/neuralnetworks/1.3/types.t
@@ -0,0 +1,344 @@
+%% template file for generating types.hal.
+%% see frameworks/ml/nn/tools/api/README.md.
+/*
+ * 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.neuralnetworks@1.3;
+
+import @1.0::DataLocation;
+import @1.0::OperandLifeTime;
+import @1.0::PerformanceInfo;
+import @1.2::OperandType;
+import @1.2::OperationType;
+import @1.2::SymmPerChannelQuantParams;
+
+import android.hidl.safe_union@1.0::Monostate;
+
+enum OperandType : @1.2::OperandType {
+%insert Operand_1.3
+%insert OEMDeprecationAndOperandTypeRangeMaxComment
+};
+
+/**
+ * The range of operand values in the OperandType enum.
+ */
+enum OperandTypeRange : uint32_t {
+ BASE_MIN = 0,
+ FUNDAMENTAL_MIN = 0,
+%insert Operand_1.3_MAX
+ OEM_MIN = 10000,
+ OEM_MAX = 10001,
+ BASE_MAX = 0xFFFF,
+};
+
+
+/**
+ * The capabilities of a driver.
+ *
+ * Performance of an operation comes from the type of its first operand.
+ * This represents performance for non extension operand types.
+ */
+struct Capabilities {
+ /**
+ * Driver performance when operating on float32 data but performing
+ * calculations with range and/or precision as low as that of the IEEE
+ * 754 16-bit floating-point format.
+ */
+ PerformanceInfo relaxedFloat32toFloat16PerformanceScalar;
+ PerformanceInfo relaxedFloat32toFloat16PerformanceTensor;
+
+ /**
+ * Driver performance when operating on a particular data type.
+ * In the case of float32 data, this is used when the calculations
+ * are not relaxed.
+ */
+ struct OperandPerformance {
+ OperandType type;
+ PerformanceInfo info;
+ };
+
+ /**
+ * Performance by operand type. Must be sorted by OperandType.
+ * If a particular OperandType is not present in operandPerformance,
+ * its performance is treated as
+ * { .execTime = FLT_MAX, .powerUsage = FLT_MAX }.
+ */
+ vec<OperandPerformance> operandPerformance;
+};
+
+/**
+ * Describes one operand of the model's graph.
+ */
+struct Operand {
+ /**
+ * The data type.
+ *
+ * Besides the values listed in {@link OperandType}, any value above
+ * {@link OperandTypeRange::BASE_MAX} is possible and should be interpreted
+ * as an extension type according to {@link Model::extensionNameToPrefix}.
+ */
+ OperandType type;
+
+ /**
+ * Dimensions of the operand.
+ *
+ * For a scalar operand, dimensions.size() must be 0.
+ *
+ * A tensor operand with all dimensions specified has "fully
+ * specified" dimensions. Whenever possible (i.e., whenever the
+ * dimensions are known at model construction time), a tensor
+ * operand should have (but is not required to have) fully
+ * specified dimensions, in order to enable the best possible
+ * performance.
+ *
+ * If a tensor operand's dimensions are not fully specified, the
+ * dimensions of the operand are deduced from the operand
+ * dimensions and values of the operation for which that operand
+ * is an output.
+ *
+ * In the following situations, a tensor operand's dimensions must
+ * be fully specified:
+ *
+ * . The operand has lifetime CONSTANT_COPY or
+ * CONSTANT_REFERENCE.
+ *
+ * . The operand has lifetime MODEL_INPUT. Fully
+ * specified dimensions must either be present in the
+ * Operand or they must be provided in the corresponding
+ * RequestArgument.
+ * EXCEPTION: If the input is optional and omitted
+ * (by setting the hasNoValue field of the corresponding
+ * RequestArgument to true) then it need not have fully
+ * specified dimensions.
+ *
+ * A tensor operand with some number of unspecified dimensions is
+ * represented by setting each unspecified dimension to 0.
+ *
+ * A tensor operand with unspecified rank is represented by providing
+ * an empty dimensions vector.
+ */
+ vec<uint32_t> dimensions;
+
+ /**
+ * The number of times this operand appears as an operation input.
+ *
+ * (For example, if this operand appears once in one operation's
+ * input list, and three times in another operation's input list,
+ * then numberOfConsumers = 4.)
+ */
+ uint32_t numberOfConsumers;
+
+ /**
+ * Quantized scale of the operand.
+ *
+ * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM or
+ * TENSOR_INT32.
+ */
+ float scale;
+
+ /**
+ * Quantized zero-point offset of the operand.
+ *
+ * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM.
+ */
+ int32_t zeroPoint;
+
+ /**
+ * How the operand is used.
+ */
+ OperandLifeTime lifetime;
+
+ /**
+ * Where to find the data for this operand.
+ * If the lifetime is TEMPORARY_VARIABLE, MODEL_INPUT, MODEL_OUTPUT, or
+ * NO_VALUE:
+ * - All the fields must be 0.
+ * If the lifetime is CONSTANT_COPY:
+ * - location.poolIndex is 0.
+ * - location.offset is the offset in bytes into Model.operandValues.
+ * - location.length is set.
+ * If the lifetime is CONSTANT_REFERENCE:
+ * - location.poolIndex is set.
+ * - location.offset is the offset in bytes into the specified pool.
+ * - location.length is set.
+ */
+ DataLocation location;
+
+ /**
+ * Additional parameters specific to a particular operand type.
+ */
+ safe_union ExtraParams {
+ /**
+ * No additional parameters.
+ */
+ Monostate none;
+
+ /**
+ * Symmetric per-channel quantization parameters.
+ *
+ * Only applicable to operands of type TENSOR_QUANT8_SYMM_PER_CHANNEL.
+ */
+ SymmPerChannelQuantParams channelQuant;
+
+ /**
+ * Extension operand parameters.
+ *
+ * The framework treats this as an opaque data blob.
+ * The format is up to individual extensions.
+ */
+ vec<uint8_t> extension;
+ } extraParams;
+};
+
+/**
+ * Describes one operation of the model's graph.
+ */
+struct Operation {
+ /**
+ * The operation type.
+ */
+ OperationType type;
+
+ /**
+ * Describes the table that contains the indexes of the inputs of the
+ * operation. The offset is the index in the operandIndexes table.
+ */
+ vec<uint32_t> inputs;
+
+ /**
+ * Describes the table that contains the indexes of the outputs of the
+ * operation. The offset is the index in the operandIndexes table.
+ */
+ vec<uint32_t> outputs;
+};
+
+/**
+ * A Neural Network Model.
+ *
+ * This includes not only the execution graph, but also constant data such as
+ * weights or scalars added at construction time. The only information that
+ * may not be known is the shape of the input tensors.
+ */
+struct Model {
+ /**
+ * All operands included in the model.
+ */
+ vec<Operand> operands;
+
+ /**
+ * All operations included in the model.
+ *
+ * The operations are sorted into execution order. Every operand
+ * with lifetime MODEL_OUTPUT or TEMPORARY_VARIABLE must be
+ * written before it is read.
+ */
+ vec<Operation> operations;
+
+ /**
+ * Input indexes of the model. There must be at least one.
+ *
+ * Each value corresponds to the index of the operand in "operands".
+ */
+ vec<uint32_t> inputIndexes;
+
+ /**
+ * Output indexes of the model. There must be at least one.
+ *
+ * Each value corresponds to the index of the operand in "operands".
+ */
+ vec<uint32_t> outputIndexes;
+
+ /**
+ * A byte buffer containing operand data that were copied into the model.
+ *
+ * An operand's value must be located here if and only if Operand::lifetime
+ * equals OperandLifeTime::CONSTANT_COPY.
+ */
+ vec<uint8_t> operandValues;
+
+ /**
+ * A collection of shared memory pools containing operand values.
+ *
+ * An operand's value must be located here if and only if Operand::lifetime
+ * equals OperandLifeTime::CONSTANT_REFERENCE.
+ */
+ vec<memory> pools;
+
+ /**
+ * 'true' indicates TENSOR_FLOAT32 may be calculated with range and/or
+ * precision as low as that of the IEEE 754 16-bit floating-point format.
+ * 'false' indicates TENSOR_FLOAT32 must be calculated using at least the
+ * range and precision of the IEEE 754 32-bit floating-point format.
+ */
+ bool relaxComputationFloat32toFloat16;
+
+ /**
+ * The mapping between extension names and prefixes of operand and
+ * operation type values.
+ *
+ * An operand or operation whose numeric type value is above
+ * {@link OperandTypeRange::BASE_MAX} or
+ * {@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 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.
+ *
+ * For example, if a model contains an operation whose value is
+ * 0xAAAABBBB and extensionNameToPrefix contains an entry with
+ * prefix=0xAAAA and name="vendor.test.test_extension", then
+ * the operation should be interpreted as the operation 0xBBBB
+ * of the extension named vendor.test.test_extension.
+ *
+ * This is a one-to-one correspondence. That is, there must be at most one
+ * prefix corresponding to each extension name and at most one extension
+ * name corresponding to each prefix.
+ */
+ vec<ExtensionNameAndPrefix> extensionNameToPrefix;
+
+ /**
+ * A correspondence between an extension name and a prefix of operand and
+ * operation type values.
+ */
+ struct ExtensionNameAndPrefix {
+ /**
+ * The extension name.
+ *
+ * See {@link Extension::name} for the format specification.
+ */
+ string name;
+
+ /**
+ * The unique extension identifier within the model.
+ *
+ * See {@link Model::extensionNameToPrefix}.
+ */
+ uint16_t prefix;
+ };
+
+ /**
+ * Numeric values of extension operand and operation types have the
+ * following structure:
+ * - 16 high bits represent the "prefix", which corresponds uniquely to the
+ * extension name.
+ * - 16 low bits represent the type ID within the extension.
+ */
+ enum ExtensionTypeEncoding : uint8_t {
+ HIGH_BITS_PREFIX = 16,
+ LOW_BITS_TYPE = 16,
+ };
+};
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index 16a7d70..8a7ed24 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
@@ -36,6 +36,7 @@
#include <gtest/gtest.h>
#include <algorithm>
+#include <chrono>
#include <iostream>
#include <numeric>
@@ -200,7 +201,8 @@
}
static std::shared_ptr<::android::nn::ExecutionBurstController> CreateBurst(
const sp<IPreparedModel>& preparedModel) {
- return android::nn::ExecutionBurstController::create(preparedModel, /*blocking=*/true);
+ return android::nn::ExecutionBurstController::create(preparedModel,
+ std::chrono::microseconds{0});
}
enum class Executor { ASYNC, SYNC, BURST };
@@ -264,8 +266,10 @@
}
// execute burst
- std::tie(executionStatus, outputShapes, timing) =
+ int n;
+ std::tie(n, outputShapes, timing, std::ignore) =
controller->compute(request, measure, keys);
+ executionStatus = nn::convertResultCodeToErrorStatus(n);
break;
}
diff --git a/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp b/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
index 95f9f42..2c97294 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateBurst.cpp
@@ -26,6 +26,7 @@
#include "Utils.h"
#include <android-base/logging.h>
+#include <chrono>
#include <cstring>
namespace android::hardware::neuralnetworks::V1_3::vts::functional {
@@ -71,9 +72,9 @@
// create FMQ objects
auto [fmqRequestChannel, fmqRequestDescriptor] =
- RequestChannelSender::create(kExecutionBurstChannelLength, /*blocking=*/true);
+ RequestChannelSender::create(kExecutionBurstChannelLength);
auto [fmqResultChannel, fmqResultDescriptor] =
- ResultChannelReceiver::create(resultChannelLength, /*blocking=*/true);
+ ResultChannelReceiver::create(resultChannelLength, std::chrono::microseconds{0});
ASSERT_NE(nullptr, fmqRequestChannel.get());
ASSERT_NE(nullptr, fmqResultChannel.get());
ASSERT_NE(nullptr, fmqRequestDescriptor);
@@ -300,8 +301,10 @@
}
// collect serialized result by running regular burst
- const auto [statusRegular, outputShapesRegular, timingRegular] =
+ const auto [nRegular, outputShapesRegular, timingRegular, fallbackRegular] =
controllerRegular->compute(request, MeasureTiming::NO, keys);
+ const ErrorStatus statusRegular = nn::convertResultCodeToErrorStatus(nRegular);
+ EXPECT_FALSE(fallbackRegular);
// skip test if regular burst output isn't useful for testing a failure
// caused by having too small of a length for the result FMQ
@@ -314,11 +317,13 @@
// by this point, execution should fail because the result channel isn't
// large enough to return the serialized result
- const auto [statusSmall, outputShapesSmall, timingSmall] =
+ const auto [nSmall, outputShapesSmall, timingSmall, fallbackSmall] =
controllerSmall->compute(request, MeasureTiming::NO, keys);
+ const ErrorStatus statusSmall = nn::convertResultCodeToErrorStatus(nSmall);
EXPECT_NE(ErrorStatus::NONE, statusSmall);
EXPECT_EQ(0u, outputShapesSmall.size());
EXPECT_TRUE(badTiming(timingSmall));
+ EXPECT_FALSE(fallbackSmall);
}
static bool isSanitized(const FmqResultDatum& datum) {
diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
index 6122123..c00512c 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "neuralnetworks_hidl_hal_test"
+#include <chrono>
#include "1.0/Utils.h"
#include "1.2/Callbacks.h"
#include "ExecutionBurstController.h"
@@ -98,7 +99,8 @@
// create burst
std::shared_ptr<::android::nn::ExecutionBurstController> burst =
- android::nn::ExecutionBurstController::create(preparedModel, /*blocking=*/true);
+ android::nn::ExecutionBurstController::create(preparedModel,
+ std::chrono::microseconds{0});
ASSERT_NE(nullptr, burst.get());
// create memory keys
@@ -108,13 +110,12 @@
}
// execute and verify
- ErrorStatus error;
- std::vector<OutputShape> outputShapes;
- Timing timing;
- std::tie(error, outputShapes, timing) = burst->compute(request, measure, keys);
- EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
+ const auto [n, outputShapes, timing, fallback] = burst->compute(request, measure, keys);
+ const ErrorStatus status = nn::convertResultCodeToErrorStatus(n);
+ EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
EXPECT_EQ(outputShapes.size(), 0);
EXPECT_TRUE(badTiming(timing));
+ EXPECT_FALSE(fallback);
// additional burst testing
if (request.pools.size() > 0) {
diff --git a/radio/1.2/vts/functional/radio_hidl_hal_api.cpp b/radio/1.2/vts/functional/radio_hidl_hal_api.cpp
index 5184ef9..a98f22a 100644
--- a/radio/1.2/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.2/vts/functional/radio_hidl_hal_api.cpp
@@ -46,7 +46,10 @@
::android::hardware::radio::V1_2::NetworkScanRequest request = {
.type = ScanType::ONE_SHOT,
.interval = 60,
- .specifiers = {::GERAN_SPECIFIER_P900, ::GERAN_SPECIFIER_850}};
+ .specifiers = {::GERAN_SPECIFIER_P900, ::GERAN_SPECIFIER_850},
+ .maxSearchTime = 60,
+ .incrementalResults = false,
+ .incrementalResultsPeriodicity = 1};
Return<void> res = radio_v1_2->startNetworkScan_1_2(serial, request);
ASSERT_OK(res);
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
index bf225bc..a4953d7 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
@@ -183,7 +183,12 @@
.channels = {1, 2}};
::android::hardware::radio::V1_2::NetworkScanRequest request = {
- .type = ScanType::ONE_SHOT, .interval = 60, .specifiers = {specifier}};
+ .type = ScanType::ONE_SHOT,
+ .interval = 60,
+ .specifiers = {specifier},
+ .maxSearchTime = 60,
+ .incrementalResults = false,
+ .incrementalResultsPeriodicity = 1};
Return<void> res = radio_v1_4->startNetworkScan_1_4(serial, request);
ASSERT_OK(res);
diff --git a/sensors/1.0/default/android.hardware.sensors@1.0-service.rc b/sensors/1.0/default/android.hardware.sensors@1.0-service.rc
index 4faa562..b41730b 100644
--- a/sensors/1.0/default/android.hardware.sensors@1.0-service.rc
+++ b/sensors/1.0/default/android.hardware.sensors@1.0-service.rc
@@ -2,6 +2,6 @@
interface android.hardware.sensors@1.0::ISensors default
class hal
user system
- group system wakelock
+ group system wakelock uhid
capabilities BLOCK_SUSPEND
rlimit rtprio 10 10
diff --git a/tv/tuner/1.0/IDemux.hal b/tv/tuner/1.0/IDemux.hal
index e03095b..7fd7e26 100644
--- a/tv/tuner/1.0/IDemux.hal
+++ b/tv/tuner/1.0/IDemux.hal
@@ -1,3 +1,19 @@
+/*
+ * 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.tv.tuner@1.0;
import IDemuxCallback;
@@ -46,9 +62,9 @@
*
* It is used by the client to get the descriptor of the filter's Fast
* Message Queue. The data in FMQ is filtered out from MPEG transport
- * stream. The data is origanized to data blocks which may have
+ * stream. The data is organized to data blocks which may have
* different length. The length's information of one or multiple data blocks
- * is sent to client throught DemuxFilterEvent.
+ * is sent to client through DemuxFilterEvent.
*
* @param filterId the ID of the filter.
* @return result Result status of the operation.
@@ -81,7 +97,7 @@
/**
* Start the filter.
*
- * It is used by the client to ask the filter to start filterring data.
+ * It is used by the client to ask the filter to start filtering data.
*
* @param filterId the ID of the filter.
* @return result Result status of the operation.
@@ -202,7 +218,7 @@
*
* It is used by the client to get the descriptor of the output's Fast
* Message Queue. The data in FMQ is muxed packets output from selected
- * filters. The packet's format is specifed by DemuxDataFormat in
+ * filters. The packet's format is specified by DemuxDataFormat in
* DemuxOutputSettings.
*
* @return result Result status of the operation.
@@ -236,7 +252,7 @@
* INVALID_STATE if failed for wrong state.
* UNKNOWN_ERROR if failed for other reasons.
*/
- attachOutputTsFilter(DemuxFilterId filterId) generates (Result result);
+ attachOutputFilter(DemuxFilterId filterId) generates (Result result);
/**
* Detach one filter from the demux's output.
@@ -250,7 +266,7 @@
* INVALID_STATE if failed for wrong state.
* UNKNOWN_ERROR if failed for other reasons.
*/
- detachOutputTsFilter(DemuxFilterId filterId) generates (Result result);
+ detachOutputFilter(DemuxFilterId filterId) generates (Result result);
/**
* Start to take data to the demux's output.
diff --git a/tv/tuner/1.0/IDemuxCallback.hal b/tv/tuner/1.0/IDemuxCallback.hal
index 55e8420..7bce9ef 100644
--- a/tv/tuner/1.0/IDemuxCallback.hal
+++ b/tv/tuner/1.0/IDemuxCallback.hal
@@ -1,3 +1,19 @@
+/*
+ * 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.tv.tuner@1.0;
interface IDemuxCallback {
diff --git a/tv/tuner/1.0/IDescrambler.hal b/tv/tuner/1.0/IDescrambler.hal
index d078657..61ff1df 100644
--- a/tv/tuner/1.0/IDescrambler.hal
+++ b/tv/tuner/1.0/IDescrambler.hal
@@ -1,3 +1,19 @@
+/*
+ * 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.tv.tuner@1.0;
/**
* Descrambler is used to descramble input data.
diff --git a/tv/tuner/1.0/IFrontend.hal b/tv/tuner/1.0/IFrontend.hal
index 8788643..83e390d 100644
--- a/tv/tuner/1.0/IFrontend.hal
+++ b/tv/tuner/1.0/IFrontend.hal
@@ -145,10 +145,10 @@
* cable frontend.
* UNKNOWN_ERROR if failed for other reasons.
*/
- setLnb(ILnb lnb) generates (Result result);
+ setLnb(LnbId lnbId) generates (Result result);
/**
- * Enble or Disable Low Noise Amplifier (LNA).
+ * Enable or Disable Low Noise Amplifier (LNA).
*
* @param bEnable true if activate LNA module; false if deactivate LNA
*
@@ -158,22 +158,4 @@
* UNKNOWN_ERROR if failed for other reasons.
*/
setLna(bool bEnable) generates (Result result);
-
- /**
- * Sends DiSEqC (Digital Satellite Equipment Control) message.
- *
- * Client sends DiSeqc message to DiSEqc compatible device through the
- * frontend. The response message from the device comes back to the client
- * through frontend's callback onDiseqcMessage.
- *
- * @param diseqcMessage a byte array of data for DiSEqC message which is
- * specified by EUTELSAT Bus Functional Specification Version 4.2.
- *
- * @return result Result status of the operation.
- * SUCCESS if successful,
- * INVALID_STATE if the frontend can't send DiSEqc Message, such as
- * cable frontend.
- * UNKNOWN_ERROR if failed for other reasons.
- */
- sendDiseqcMessage(vec<uint8_t> diseqcMessage) generates (Result result);
};
diff --git a/tv/tuner/1.0/ILnb.hal b/tv/tuner/1.0/ILnb.hal
index 49fc3b4..6b7119e 100644
--- a/tv/tuner/1.0/ILnb.hal
+++ b/tv/tuner/1.0/ILnb.hal
@@ -55,6 +55,24 @@
setSatellitePosition(FrontendLnbPosition position) generates (Result result);
/**
+ * Sends DiSEqC (Digital Satellite Equipment Control) message.
+ *
+ * Client sends DiSeqc message to DiSEqc to LNB. The response message from
+ * the device comes back to the client through frontend's callback
+ * onDiseqcMessage.
+ *
+ * @param diseqcMessage a byte array of data for DiSEqC message which is
+ * specified by EUTELSAT Bus Functional Specification Version 4.2.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * INVALID_STATE if the frontend can't send DiSEqc Message, such as
+ * cable frontend.
+ * UNKNOWN_ERROR if failed for other reasons.
+ */
+ sendDiseqcMessage(vec<uint8_t> diseqcMessage) generates (Result result);
+
+ /**
* Releases the LNB instance
*
* Associated resources are released. close may be called more than once.
diff --git a/tv/tuner/1.0/ITuner.hal b/tv/tuner/1.0/ITuner.hal
index f1a8617..1cf0e38 100644
--- a/tv/tuner/1.0/ITuner.hal
+++ b/tv/tuner/1.0/ITuner.hal
@@ -23,7 +23,7 @@
/**
* Top level interface to manage Frontend, Demux and Decrambler hardware
- * resouces which are needed for Android TV.
+ * resources which are needed for Android TV.
*/
interface ITuner {
/**
@@ -68,6 +68,16 @@
generates (Result result, DemuxId demuxId, IDemux demux);
/**
+ * Retrieve the Demux's Capabilities.
+ *
+ * @return result Result status of the operation.
+ * SUCCESS if successful,
+ * UNKNOWN_ERROR if the inquiry failed for other reasons.
+ * @return caps the Demux's Capabilities.
+ */
+ getDemuxCaps() generates (Result result, DemuxCapabilities caps);
+
+ /**
* Create a new instance of Descrambler.
*
* It is used by the client to create a Descrambler instance.
@@ -81,14 +91,13 @@
generates (Result result, IDescrambler descrambler);
/**
- * Create a new instance of Descrambler.
+ * Retrieve the frontend's information.
*
- * It is used by the client to create a Descrambler instance.
- *
+ * @param frontendId the id of the frontend to be inquiried.
* @return result Result status of the operation.
* SUCCESS if successful,
- * UNKNOWN_ERROR if creation failed for other reasons.
- * @return descrambler the newly created descrambler interface.
+ * UNKNOWN_ERROR if the inquiry failed for other reasons.
+ * @return info the frontend's information.
*/
getFrontendInfo(FrontendId frontendId)
generates (Result result, FrontendInfo info);
@@ -119,6 +128,5 @@
*/
openLnbById(LnbId lnbId)
generates (Result result, ILnb lnb);
-
};
diff --git a/tv/tuner/1.0/default/Demux.cpp b/tv/tuner/1.0/default/Demux.cpp
index 889e42e..d65df59 100644
--- a/tv/tuner/1.0/default/Demux.cpp
+++ b/tv/tuner/1.0/default/Demux.cpp
@@ -67,8 +67,9 @@
0x73, 0x63, 0x65, 0x6e, 0x65,
};
-Demux::Demux(uint32_t demuxId) {
+Demux::Demux(uint32_t demuxId, sp<Tuner> tuner) {
mDemuxId = demuxId;
+ mTunerService = tuner;
}
Demux::~Demux() {}
@@ -76,9 +77,20 @@
Return<Result> Demux::setFrontendDataSource(uint32_t frontendId) {
ALOGV("%s", __FUNCTION__);
- mSourceFrontendId = frontendId;
+ if (mTunerService == nullptr) {
+ return Result::NOT_INITIALIZED;
+ }
- return Result::SUCCESS;
+ mFrontend = mTunerService->getFrontendById(frontendId);
+
+ if (mFrontend == nullptr) {
+ return Result::INVALID_STATE;
+ }
+
+ mFrontendSourceFile = mFrontend->getSourceFile();
+
+ mTunerService->setFrontendAsDemuxSource(frontendId, mDemuxId);
+ return startBroadcastInputLoop();
}
Return<void> Demux::addFilter(DemuxFilterType type, uint32_t bufferSize,
@@ -100,6 +112,8 @@
mFilterEventFlags.resize(filterId + 1);
mFilterThreadRunning.resize(filterId + 1);
mFilterThreads.resize(filterId + 1);
+ mFilterPids.resize(filterId + 1);
+ mFilterOutputs.resize(filterId + 1);
}
mUsedFilterIds.insert(filterId);
@@ -142,10 +156,34 @@
return Void();
}
-Return<Result> Demux::configureFilter(uint32_t /* filterId */,
- const DemuxFilterSettings& /* settings */) {
+Return<Result> Demux::configureFilter(uint32_t filterId, const DemuxFilterSettings& settings) {
ALOGV("%s", __FUNCTION__);
+ switch (mFilterEvents[filterId].filterType) {
+ case DemuxFilterType::SECTION:
+ mFilterPids[filterId] = settings.section().tpid;
+ break;
+ case DemuxFilterType::PES:
+ mFilterPids[filterId] = settings.pesData().tpid;
+ break;
+ case DemuxFilterType::TS:
+ mFilterPids[filterId] = settings.ts().tpid;
+ break;
+ case DemuxFilterType::AUDIO:
+ mFilterPids[filterId] = settings.audio().tpid;
+ break;
+ case DemuxFilterType::VIDEO:
+ mFilterPids[filterId] = settings.video().tpid;
+ break;
+ case DemuxFilterType::RECORD:
+ mFilterPids[filterId] = settings.record().tpid;
+ break;
+ case DemuxFilterType::PCR:
+ mFilterPids[filterId] = settings.pcr().tpid;
+ break;
+ default:
+ return Result::UNKNOWN_ERROR;
+ }
return Result::SUCCESS;
}
@@ -158,36 +196,18 @@
return Result::INVALID_ARGUMENT;
}
- switch (mFilterEvents[filterId].filterType) {
- case DemuxFilterType::SECTION:
- result = startFilterLoop(filterId);
- break;
- case DemuxFilterType::PES:
- result = startPesFilterHandler(filterId);
- break;
- case DemuxFilterType::TS:
- result = startTsFilterHandler();
- return Result::SUCCESS;
- case DemuxFilterType::AUDIO:
- case DemuxFilterType::VIDEO:
- result = startMediaFilterHandler(filterId);
- break;
- case DemuxFilterType::RECORD:
- result = startRecordFilterHandler(filterId);
- break;
- case DemuxFilterType::PCR:
- result = startPcrFilterHandler();
- return Result::SUCCESS;
- default:
- return Result::UNKNOWN_ERROR;
- }
+ result = startFilterLoop(filterId);
return result;
}
-Return<Result> Demux::stopFilter(uint32_t /* filterId */) {
+Return<Result> Demux::stopFilter(uint32_t filterId) {
ALOGV("%s", __FUNCTION__);
+ mFilterThreadRunning[filterId] = false;
+
+ std::lock_guard<std::mutex> lock(mFilterThreadLock);
+
return Result::SUCCESS;
}
@@ -238,6 +258,8 @@
mFilterMQs.clear();
mFilterEvents.clear();
mFilterEventFlags.clear();
+ mFilterOutputs.clear();
+ mFilterPids.clear();
mLastUsedFilterId = -1;
return Result::SUCCESS;
@@ -277,19 +299,21 @@
return Void();
}
-Return<Result> Demux::configureOutput(const DemuxOutputSettings& /* settings */) {
+Return<Result> Demux::configureOutput(const DemuxOutputSettings& settings) {
+ ALOGV("%s", __FUNCTION__);
+
+ mOutputConfigured = true;
+ mOutputSettings = settings;
+ return Result::SUCCESS;
+}
+
+Return<Result> Demux::attachOutputFilter(uint32_t /*filterId*/) {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
}
-Return<Result> Demux::attachOutputTsFilter(uint32_t /*filterId*/) {
- ALOGV("%s", __FUNCTION__);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Demux::detachOutputTsFilter(uint32_t /* filterId */) {
+Return<Result> Demux::detachOutputFilter(uint32_t /* filterId */) {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
@@ -353,15 +377,26 @@
return Void();
}
-Return<Result> Demux::configureInput(const DemuxInputSettings& /* settings */) {
+Return<Result> Demux::configureInput(const DemuxInputSettings& settings) {
ALOGV("%s", __FUNCTION__);
+ mInputConfigured = true;
+ mInputSettings = settings;
+
return Result::SUCCESS;
}
Return<Result> Demux::startInput() {
ALOGV("%s", __FUNCTION__);
+ if (!mInputCallback) {
+ return Result::NOT_INITIALIZED;
+ }
+
+ if (!mInputConfigured) {
+ return Result::INVALID_STATE;
+ }
+
pthread_create(&mInputThread, NULL, __threadLoopInput, this);
pthread_setname_np(mInputThread, "demux_input_waiting_loop");
@@ -373,6 +408,10 @@
Return<Result> Demux::stopInput() {
ALOGV("%s", __FUNCTION__);
+ mInputThreadRunning = false;
+
+ std::lock_guard<std::mutex> lock(mInputThreadLock);
+
return Result::SUCCESS;
}
@@ -403,36 +442,52 @@
return Result::SUCCESS;
}
-Result Demux::startSectionFilterHandler(uint32_t filterId, vector<uint8_t> data) {
- if (!writeSectionsAndCreateEvent(filterId, data)) {
+Result Demux::startSectionFilterHandler(uint32_t filterId) {
+ if (mFilterOutputs[filterId].empty()) {
+ return Result::SUCCESS;
+ }
+ if (!writeSectionsAndCreateEvent(filterId, mFilterOutputs[filterId])) {
ALOGD("[Demux] filter %d fails to write into FMQ. Ending thread", filterId);
return Result::UNKNOWN_ERROR;
}
+ mFilterOutputs[filterId].clear();
+
return Result::SUCCESS;
}
Result Demux::startPesFilterHandler(uint32_t filterId) {
- // TODO generate multiple events in one event callback
+ std::lock_guard<std::mutex> lock(mFilterEventLock);
DemuxFilterPesEvent pesEvent;
- pesEvent = {
- // temp dump meta data
- .streamId = 0,
- .dataLength = 530,
- };
- mFilterEvents[filterId].events.resize(1);
- mFilterEvents[filterId].events[0].pes(pesEvent);
- /*pthread_create(&mThreadId, NULL, __threadLoop, this);
- pthread_setname_np(mThreadId, "demux_section_filter_waiting_loop");*/
- if (!writeDataToFilterMQ(fakeDataInputBuffer, filterId)) {
- return Result::INVALID_STATE;
+ if (mFilterOutputs[filterId].empty()) {
+ return Result::SUCCESS;
}
- if (mDemuxCallbacks[filterId] == nullptr) {
- return Result::NOT_INITIALIZED;
+ for (int i = 0; i < mFilterOutputs[filterId].size(); i += 188) {
+ uint8_t pusi = mFilterOutputs[filterId][i + 1] & 0x40;
+ uint8_t adaptFieldControl = (mFilterOutputs[filterId][i + 3] & 0x30) >> 4;
+ ALOGD("[Demux] pusi %d, adaptFieldControl %d", pusi, adaptFieldControl);
+ if (pusi && (adaptFieldControl == 0x01)) {
+ vector<uint8_t>::const_iterator first = mFilterOutputs[filterId].begin() + i + 4;
+ vector<uint8_t>::const_iterator last = mFilterOutputs[filterId].begin() + i + 187;
+ vector<uint8_t> filterOutData(first, last);
+ if (!writeDataToFilterMQ(filterOutData, filterId)) {
+ mFilterOutputs[filterId].clear();
+ return Result::INVALID_STATE;
+ }
+ pesEvent = {
+ // temp dump meta data
+ .streamId = filterOutData[3],
+ .dataLength = static_cast<uint16_t>(filterOutData.size()),
+ };
+ int size = mFilterEvents[filterId].events.size();
+ mFilterEvents[filterId].events.resize(size + 1);
+ mFilterEvents[filterId].events[size].pes(pesEvent);
+ }
}
- mDemuxCallbacks[filterId]->onFilterEvent(mFilterEvents[filterId]);
+ mFilterOutputs[filterId].clear();
+
return Result::SUCCESS;
}
@@ -451,6 +506,8 @@
};
mFilterEvents[filterId].events.resize(1);
mFilterEvents[filterId].events[0].media() = mediaEvent;
+
+ mFilterOutputs[filterId].clear();
// TODO handle write FQM for media stream
return Result::SUCCESS;
}
@@ -465,6 +522,8 @@
recordEvent.indexMask.tsIndexMask() = 0x01;
mFilterEvents[filterId].events.resize(1);
mFilterEvents[filterId].events[0].ts() = recordEvent;
+
+ mFilterOutputs[filterId].clear();
return Result::SUCCESS;
}
@@ -499,18 +558,18 @@
bool Demux::writeSectionsAndCreateEvent(uint32_t filterId, vector<uint8_t> data) {
// TODO check how many sections has been read
std::lock_guard<std::mutex> lock(mFilterEventLock);
- int size = mFilterEvents[filterId].events.size();
- mFilterEvents[filterId].events.resize(size + 1);
if (!writeDataToFilterMQ(data, filterId)) {
return false;
}
+ int size = mFilterEvents[filterId].events.size();
+ mFilterEvents[filterId].events.resize(size + 1);
DemuxFilterSectionEvent secEvent;
secEvent = {
// temp dump meta data
.tableId = 0,
.version = 1,
.sectionNum = 1,
- .dataLength = 530,
+ .dataLength = static_cast<uint16_t>(data.size()),
};
mFilterEvents[filterId].events[size].section(secEvent);
return true;
@@ -524,21 +583,44 @@
return false;
}
-bool Demux::filterAndOutputData() {
- ALOGD("[Demux] start to dispatch data to filters");
+bool Demux::readInputFMQ() {
// Read input data from the input FMQ
int size = mInputMQ->availableToRead();
+ int inputPacketSize = mInputSettings.packetSize;
vector<uint8_t> dataOutputBuffer;
- dataOutputBuffer.resize(size);
- mInputMQ->read(dataOutputBuffer.data(), size);
+ dataOutputBuffer.resize(inputPacketSize);
- Result result;
- // Filter the data and feed the output to each filter
+ // Dispatch the packet to the PID matching filter output buffer
+ for (int i = 0; i < size / inputPacketSize; i++) {
+ if (!mInputMQ->read(dataOutputBuffer.data(), inputPacketSize)) {
+ return false;
+ }
+ startTsFilter(dataOutputBuffer);
+ }
+
+ return true;
+}
+
+void Demux::startTsFilter(vector<uint8_t> data) {
set<uint32_t>::iterator it;
for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
+ uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
+ ALOGW("start ts filter pid: %d", pid);
+ if (pid == mFilterPids[*it]) {
+ mFilterOutputs[*it].insert(mFilterOutputs[*it].end(), data.begin(), data.end());
+ }
+ }
+}
+
+bool Demux::startFilterDispatcher() {
+ Result result;
+ set<uint32_t>::iterator it;
+
+ // Handle the output data per filter type
+ for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
switch (mFilterEvents[*it].filterType) {
case DemuxFilterType::SECTION:
- result = startSectionFilterHandler(*it, dataOutputBuffer);
+ result = startSectionFilterHandler(*it);
break;
case DemuxFilterType::PES:
result = startPesFilterHandler(*it);
@@ -578,6 +660,7 @@
void Demux::filterThreadLoop(uint32_t filterId) {
ALOGD("[Demux] filter %d threadLoop start.", filterId);
+ std::lock_guard<std::mutex> lock(mFilterThreadLock);
mFilterThreadRunning[filterId] = true;
// For the first time of filter output, implementation needs to send the filter
@@ -640,6 +723,7 @@
void Demux::inputThreadLoop() {
ALOGD("[Demux] input threadLoop start.");
+ std::lock_guard<std::mutex> lock(mInputThreadLock);
mInputThreadRunning = true;
while (mInputThreadRunning) {
@@ -651,18 +735,112 @@
ALOGD("[Demux] wait for data ready on the input FMQ");
continue;
}
- // Our current implementation filter the data and write it into the filter FMQ immedaitely
+ // Our current implementation filter the data and write it into the filter FMQ immediately
// after the DATA_READY from the VTS/framework
- if (!filterAndOutputData()) {
+ if (!readInputFMQ() || !startFilterDispatcher()) {
ALOGD("[Demux] input data failed to be filtered. Ending thread");
break;
}
+
+ maySendInputStatusCallback();
}
mInputThreadRunning = false;
ALOGD("[Demux] input thread ended.");
}
+void Demux::maySendInputStatusCallback() {
+ std::lock_guard<std::mutex> lock(mInputStatusLock);
+ int availableToRead = mInputMQ->availableToRead();
+ int availableToWrite = mInputMQ->availableToWrite();
+
+ DemuxInputStatus newStatus =
+ checkStatusChange(availableToWrite, availableToRead, mInputSettings.highThreshold,
+ mInputSettings.lowThreshold);
+ if (mIntputStatus != newStatus) {
+ mInputCallback->onInputStatus(newStatus);
+ mIntputStatus = newStatus;
+ }
+}
+
+DemuxInputStatus Demux::checkStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+ uint32_t highThreshold, uint32_t lowThreshold) {
+ if (availableToWrite == 0) {
+ return DemuxInputStatus::SPACE_FULL;
+ } else if (availableToRead > highThreshold) {
+ return DemuxInputStatus::SPACE_ALMOST_FULL;
+ } else if (availableToRead < lowThreshold) {
+ return DemuxInputStatus::SPACE_ALMOST_EMPTY;
+ } else if (availableToRead == 0) {
+ return DemuxInputStatus::SPACE_EMPTY;
+ }
+ return mIntputStatus;
+}
+
+Result Demux::startBroadcastInputLoop() {
+ pthread_create(&mBroadcastInputThread, NULL, __threadLoopBroadcast, this);
+ pthread_setname_np(mBroadcastInputThread, "broadcast_input_thread");
+
+ return Result::SUCCESS;
+}
+
+void* Demux::__threadLoopBroadcast(void* user) {
+ Demux* const self = static_cast<Demux*>(user);
+ self->broadcastInputThreadLoop();
+ return 0;
+}
+
+void Demux::broadcastInputThreadLoop() {
+ std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
+ mBroadcastInputThreadRunning = true;
+ mKeepFetchingDataFromFrontend = true;
+
+ // open the stream and get its length
+ std::ifstream inputData(mFrontendSourceFile, std::ifstream::binary);
+ // TODO take the packet size from the frontend setting
+ int packetSize = 188;
+ int writePacketAmount = 6;
+ char* buffer = new char[packetSize];
+ ALOGW("[Demux] broadcast input thread loop start %s", mFrontendSourceFile.c_str());
+ if (!inputData.is_open()) {
+ mBroadcastInputThreadRunning = false;
+ ALOGW("[Demux] Error %s", strerror(errno));
+ }
+
+ while (mBroadcastInputThreadRunning) {
+ // move the stream pointer for packet size * 6 every read until the end
+ while (mKeepFetchingDataFromFrontend) {
+ for (int i = 0; i < writePacketAmount; i++) {
+ inputData.read(buffer, packetSize);
+ if (!inputData) {
+ mBroadcastInputThreadRunning = false;
+ break;
+ }
+ // filter and dispatch filter output
+ vector<uint8_t> byteBuffer;
+ byteBuffer.resize(sizeof(buffer));
+ for (int index = 0; index < byteBuffer.size(); index++) {
+ byteBuffer[index] = static_cast<uint8_t>(buffer[index]);
+ }
+ startTsFilter(byteBuffer);
+ inputData.seekg(packetSize, inputData.cur);
+ }
+ startFilterDispatcher();
+ sleep(1);
+ }
+ }
+
+ ALOGW("[Demux] Broadcast Input thread end.");
+ delete[] buffer;
+ inputData.close();
+}
+
+void Demux::stopBroadcastInput() {
+ mKeepFetchingDataFromFrontend = false;
+ mBroadcastInputThreadRunning = false;
+ std::lock_guard<std::mutex> lock(mBroadcastInputThreadLock);
+}
+
} // namespace implementation
} // namespace V1_0
} // namespace tuner
diff --git a/tv/tuner/1.0/default/Demux.h b/tv/tuner/1.0/default/Demux.h
index 2fdde8d..e4a4e2b 100644
--- a/tv/tuner/1.0/default/Demux.h
+++ b/tv/tuner/1.0/default/Demux.h
@@ -20,6 +20,8 @@
#include <android/hardware/tv/tuner/1.0/IDemux.h>
#include <fmq/MessageQueue.h>
#include <set>
+#include "Frontend.h"
+#include "Tuner.h"
using namespace std;
@@ -40,9 +42,12 @@
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+class Tuner;
+class Frontend;
+
class Demux : public IDemux {
public:
- Demux(uint32_t demuxId);
+ Demux(uint32_t demuxId, sp<Tuner> tuner);
~Demux();
@@ -91,9 +96,9 @@
virtual Return<Result> configureOutput(const DemuxOutputSettings& settings) override;
- virtual Return<Result> attachOutputTsFilter(uint32_t filterId) override;
+ virtual Return<Result> attachOutputFilter(uint32_t filterId) override;
- virtual Return<Result> detachOutputTsFilter(uint32_t filterId) override;
+ virtual Return<Result> detachOutputFilter(uint32_t filterId) override;
virtual Return<Result> startOutput() override;
@@ -103,7 +108,17 @@
virtual Return<Result> removeOutput() override;
+ // Functions interacts with Tuner Service
+ void stopBroadcastInput();
+
private:
+ // Tuner service
+ sp<Tuner> mTunerService;
+
+ // Frontend source
+ sp<Frontend> mFrontend;
+ string mFrontendSourceFile;
+
// A struct that passes the arguments to a newly created filter thread
struct ThreadArgs {
Demux* user;
@@ -115,13 +130,14 @@
* They are also responsible to write the filtered output into the filter FMQ
* and update the filterEvent bound with the same filterId.
*/
- Result startSectionFilterHandler(uint32_t filterId, vector<uint8_t> data);
+ Result startSectionFilterHandler(uint32_t filterId);
Result startPesFilterHandler(uint32_t filterId);
Result startTsFilterHandler();
Result startMediaFilterHandler(uint32_t filterId);
Result startRecordFilterHandler(uint32_t filterId);
Result startPcrFilterHandler();
Result startFilterLoop(uint32_t filterId);
+ Result startBroadcastInputLoop();
/**
* To create a FilterMQ with the the next available Filter ID.
@@ -136,18 +152,24 @@
bool writeDataToFilterMQ(const std::vector<uint8_t>& data, uint32_t filterId);
bool readDataFromMQ();
bool writeSectionsAndCreateEvent(uint32_t filterId, vector<uint8_t> data);
+ void maySendInputStatusCallback();
+ DemuxInputStatus checkStatusChange(uint32_t availableToWrite, uint32_t availableToRead,
+ uint32_t highThreshold, uint32_t lowThreshold);
/**
* A dispatcher to read and dispatch input data to all the started filters.
* Each filter handler handles the data filtering/output writing/filterEvent updating.
*/
- bool filterAndOutputData();
+ bool readInputFMQ();
+ void startTsFilter(vector<uint8_t> data);
+ bool startFilterDispatcher();
static void* __threadLoopFilter(void* data);
static void* __threadLoopInput(void* user);
+ static void* __threadLoopBroadcast(void* user);
void filterThreadLoop(uint32_t filterId);
void inputThreadLoop();
+ void broadcastInputThreadLoop();
uint32_t mDemuxId;
- uint32_t mSourceFrontendId;
/**
* Record the last used filter id. Initial value is -1.
* Filter Id starts with 0.
@@ -169,6 +191,8 @@
* A list of created FilterMQ ptrs.
* The array number is the filter ID.
*/
+ vector<uint16_t> mFilterPids;
+ vector<vector<uint8_t>> mFilterOutputs;
vector<unique_ptr<FilterMQ>> mFilterMQs;
vector<EventFlag*> mFilterEventFlags;
vector<DemuxFilterEvent> mFilterEvents;
@@ -182,15 +206,26 @@
vector<sp<IDemuxCallback>> mDemuxCallbacks;
sp<IDemuxCallback> mInputCallback;
sp<IDemuxCallback> mOutputCallback;
+ bool mInputConfigured = false;
+ bool mOutputConfigured = false;
+ DemuxInputSettings mInputSettings;
+ DemuxOutputSettings mOutputSettings;
+
// Thread handlers
pthread_t mInputThread;
pthread_t mOutputThread;
+ pthread_t mBroadcastInputThread;
vector<pthread_t> mFilterThreads;
+
+ // FMQ status local records
+ DemuxInputStatus mIntputStatus;
/**
* If a specific filter's writing loop is still running
*/
vector<bool> mFilterThreadRunning;
bool mInputThreadRunning;
+ bool mBroadcastInputThreadRunning;
+ bool mKeepFetchingDataFromFrontend;
/**
* Lock to protect writes to the FMQs
*/
@@ -198,8 +233,16 @@
/**
* Lock to protect writes to the filter event
*/
+ // TODO make each filter separate event lock
std::mutex mFilterEventLock;
/**
+ * Lock to protect writes to the input status
+ */
+ std::mutex mInputStatusLock;
+ std::mutex mBroadcastInputThreadLock;
+ std::mutex mFilterThreadLock;
+ std::mutex mInputThreadLock;
+ /**
* How many times a filter should write
* TODO make this dynamic/random/can take as a parameter
*/
diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp
index 0609d05..1e07edd 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -27,14 +27,10 @@
namespace V1_0 {
namespace implementation {
-Frontend::Frontend() {
- // Init callback to nullptr
- mCallback = nullptr;
-}
-
-Frontend::Frontend(FrontendType type, FrontendId id) {
+Frontend::Frontend(FrontendType type, FrontendId id, sp<Tuner> tuner) {
mType = type;
mId = id;
+ mTunerService = tuner;
// Init callback to nullptr
mCallback = nullptr;
}
@@ -67,13 +63,18 @@
return Result::INVALID_STATE;
}
- mCallback->onEvent(FrontendEventType::NO_SIGNAL);
+ // TODO dynamically allocate file to the source file
+ mSourceStreamFile = FRONTEND_STREAM_FILE;
+
+ mCallback->onEvent(FrontendEventType::LOCKED);
return Result::SUCCESS;
}
Return<Result> Frontend::stopTune() {
ALOGV("%s", __FUNCTION__);
+ mTunerService->frontendStopTune(mId);
+
return Result::SUCCESS;
}
@@ -105,13 +106,7 @@
return Result::SUCCESS;
}
-Return<Result> Frontend::setLnb(const sp<ILnb>& /* lnb */) {
- ALOGV("%s", __FUNCTION__);
-
- return Result::SUCCESS;
-}
-
-Return<Result> Frontend::sendDiseqcMessage(const hidl_vec<uint8_t>& /* diseqcMessage */) {
+Return<Result> Frontend::setLnb(uint32_t /* lnb */) {
ALOGV("%s", __FUNCTION__);
return Result::SUCCESS;
@@ -125,6 +120,10 @@
return mId;
}
+string Frontend::getSourceFile() {
+ return mSourceStreamFile;
+}
+
} // namespace implementation
} // namespace V1_0
} // namespace tuner
diff --git a/tv/tuner/1.0/default/Frontend.h b/tv/tuner/1.0/default/Frontend.h
index fc586b5..07fa7b9 100644
--- a/tv/tuner/1.0/default/Frontend.h
+++ b/tv/tuner/1.0/default/Frontend.h
@@ -18,7 +18,9 @@
#define ANDROID_HARDWARE_TV_TUNER_V1_0_FRONTEND_H_
#include <android/hardware/tv/tuner/1.0/IFrontend.h>
-#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <fstream>
+#include <iostream>
+#include "Tuner.h"
using namespace std;
@@ -35,11 +37,11 @@
using ::android::hardware::tv::tuner::V1_0::IFrontendCallback;
using ::android::hardware::tv::tuner::V1_0::Result;
+class Tuner;
+
class Frontend : public IFrontend {
public:
- Frontend();
-
- Frontend(FrontendType type, FrontendId id);
+ Frontend(FrontendType type, FrontendId id, sp<Tuner> tuner);
virtual Return<Result> close() override;
@@ -56,21 +58,26 @@
virtual Return<void> getStatus(const hidl_vec<FrontendStatusType>& statusTypes,
getStatus_cb _hidl_cb) override;
- virtual Return<Result> sendDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) override;
-
virtual Return<Result> setLna(bool bEnable) override;
- virtual Return<Result> setLnb(const sp<ILnb>& lnb) override;
+ virtual Return<Result> setLnb(uint32_t lnb) override;
FrontendType getFrontendType();
FrontendId getFrontendId();
+ string getSourceFile();
+
private:
virtual ~Frontend();
sp<IFrontendCallback> mCallback;
+ sp<Tuner> mTunerService;
FrontendType mType = FrontendType::UNDEFINED;
FrontendId mId = 0;
+
+ const string FRONTEND_STREAM_FILE = "/vendor/etc/test1.ts";
+ string mSourceStreamFile;
+ std::ifstream mFrontendData;
};
} // namespace implementation
diff --git a/tv/tuner/1.0/default/Lnb.cpp b/tv/tuner/1.0/default/Lnb.cpp
index b81bb15..1446f7f 100644
--- a/tv/tuner/1.0/default/Lnb.cpp
+++ b/tv/tuner/1.0/default/Lnb.cpp
@@ -48,6 +48,12 @@
return Result::SUCCESS;
}
+Return<Result> Lnb::sendDiseqcMessage(const hidl_vec<uint8_t>& /* diseqcMessage */) {
+ ALOGV("%s", __FUNCTION__);
+
+ return Result::SUCCESS;
+}
+
Return<Result> Lnb::close() {
ALOGV("%s", __FUNCTION__);
diff --git a/tv/tuner/1.0/default/Lnb.h b/tv/tuner/1.0/default/Lnb.h
index df7e0fe..4c251f7 100644
--- a/tv/tuner/1.0/default/Lnb.h
+++ b/tv/tuner/1.0/default/Lnb.h
@@ -38,12 +38,14 @@
public:
Lnb();
- virtual Return<Result> setVoltage(FrontendLnbVoltage voltage);
+ virtual Return<Result> setVoltage(FrontendLnbVoltage voltage) override;
virtual Return<Result> setTone(FrontendLnbTone tone) override;
virtual Return<Result> setSatellitePosition(FrontendLnbPosition position) override;
+ virtual Return<Result> sendDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) override;
+
virtual Return<Result> close() override;
private:
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index 00831ae..f86b28d 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -38,14 +38,14 @@
// Array index matches their FrontendId in the default impl
mFrontendSize = 8;
mFrontends.resize(mFrontendSize);
- mFrontends[0] = new Frontend();
- mFrontends[1] = new Frontend(FrontendType::ATSC, 1);
- mFrontends[2] = new Frontend(FrontendType::DVBC, 2);
- mFrontends[3] = new Frontend(FrontendType::DVBS, 3);
- mFrontends[4] = new Frontend(FrontendType::DVBT, 4);
- mFrontends[5] = new Frontend(FrontendType::ISDBT, 5);
- mFrontends[6] = new Frontend(FrontendType::ANALOG, 6);
- mFrontends[7] = new Frontend(FrontendType::ATSC, 7);
+ mFrontends[0] = new Frontend(FrontendType::DVBT, 0, this);
+ mFrontends[1] = new Frontend(FrontendType::ATSC, 1, this);
+ mFrontends[2] = new Frontend(FrontendType::DVBC, 2, this);
+ mFrontends[3] = new Frontend(FrontendType::DVBS, 3, this);
+ mFrontends[4] = new Frontend(FrontendType::DVBT, 4, this);
+ mFrontends[5] = new Frontend(FrontendType::ISDBT, 5, this);
+ mFrontends[6] = new Frontend(FrontendType::ANALOG, 6, this);
+ mFrontends[7] = new Frontend(FrontendType::ATSC, 7, this);
}
Tuner::~Tuner() {}
@@ -81,12 +81,22 @@
DemuxId demuxId = mLastUsedId + 1;
mLastUsedId += 1;
- sp<IDemux> demux = new Demux(demuxId);
+ sp<Demux> demux = new Demux(demuxId, this);
+ mDemuxes[demuxId] = demux;
_hidl_cb(Result::SUCCESS, demuxId, demux);
return Void();
}
+Return<void> Tuner::getDemuxCaps(getDemuxCaps_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ DemuxCapabilities caps;
+
+ _hidl_cb(Result::SUCCESS, caps);
+ return Void();
+}
+
Return<void> Tuner::openDescrambler(openDescrambler_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
@@ -123,6 +133,25 @@
return Void();
}
+sp<Frontend> Tuner::getFrontendById(uint32_t frontendId) {
+ ALOGV("%s", __FUNCTION__);
+
+ return mFrontends[frontendId];
+}
+
+void Tuner::setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId) {
+ mFrontendToDemux[frontendId] = demuxId;
+}
+
+void Tuner::frontendStopTune(uint32_t frontendId) {
+ map<uint32_t, uint32_t>::iterator it = mFrontendToDemux.find(frontendId);
+ uint32_t demuxId;
+ if (it != mFrontendToDemux.end()) {
+ demuxId = it->second;
+ mDemuxes[demuxId]->stopBroadcastInput();
+ }
+}
+
} // namespace implementation
} // namespace V1_0
} // namespace tuner
diff --git a/tv/tuner/1.0/default/Tuner.h b/tv/tuner/1.0/default/Tuner.h
index 62227ee..96da257 100644
--- a/tv/tuner/1.0/default/Tuner.h
+++ b/tv/tuner/1.0/default/Tuner.h
@@ -18,6 +18,8 @@
#define ANDROID_HARDWARE_TV_TUNER_V1_0_TUNER_H_
#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <map>
+#include "Demux.h"
#include "Frontend.h"
using namespace std;
@@ -29,6 +31,9 @@
namespace V1_0 {
namespace implementation {
+class Frontend;
+class Demux;
+
class Tuner : public ITuner {
public:
Tuner();
@@ -39,6 +44,8 @@
virtual Return<void> openDemux(openDemux_cb _hidl_cb) override;
+ virtual Return<void> getDemuxCaps(getDemuxCaps_cb _hidl_cb) override;
+
virtual Return<void> openDescrambler(openDescrambler_cb _hidl_cb) override;
virtual Return<void> getFrontendInfo(FrontendId frontendId,
@@ -48,10 +55,18 @@
virtual Return<void> openLnbById(LnbId lnbId, openLnbById_cb _hidl_cb) override;
+ sp<Frontend> getFrontendById(uint32_t frontendId);
+
+ void setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId);
+
+ void frontendStopTune(uint32_t frontendId);
+
private:
virtual ~Tuner();
// Static mFrontends array to maintain local frontends information
vector<sp<Frontend>> mFrontends;
+ std::map<uint32_t, uint32_t> mFrontendToDemux;
+ std::map<uint32_t, sp<Demux>> mDemuxes;
// To maintain how many Frontends we have
int mFrontendSize;
// The last used demux id. Initial value is -1.
diff --git a/tv/tuner/1.0/types.hal b/tv/tuner/1.0/types.hal
index d37f63a..890c1ed 100644
--- a/tv/tuner/1.0/types.hal
+++ b/tv/tuner/1.0/types.hal
@@ -43,7 +43,7 @@
ANALOG,
/* Advanced Television Systems Committee (ATSC) Standard A/72. */
ATSC,
- /* Advanced Television Systems Committee (ATSC 3.0) Standard A/330. */
+ /* Advanced Television Systems Committee (ATSC 3.0) Standard A/300. */
ATSC3,
/**
* Digital Video Broadcasting - Cable
@@ -58,16 +58,16 @@
DVBS,
/**
* Digital Video Broadcasting - Terrestrial
- * DVB Terresttrial Frontend Standard ETSI EN 300 468 V1.15.1 and
+ * DVB Terrestrial Frontend Standard ETSI EN 300 468 V1.15.1 and
* ETSI EN 302 755 V1.4.1.
*/
DVBT,
/* Integrated Services Digital Broadcasting-Satellite (ISDB-S)
- * ARIB SDT-B20 is technical document of ISDB-S.
+ * ARIB STD-B20 is technical document of ISDB-S.
*/
ISDBS,
/* Integrated Services Digital Broadcasting-Satellite (ISDB-S)
- * ARIB TR-B15 is technical document of ISDB-S3.
+ * ARIB STD-B44 is technical document of ISDB-S3.
*/
ISDBS3,
/* Integrated Services Digital Broadcasting-Terrestrial (ISDB-T or SBTVD)
@@ -164,8 +164,10 @@
@export
enum FrontendAtscModulation : uint32_t {
UNDEFINED = 0,
- MOD_8VSB = 1 << 0,
- MOD_16VSB = 1 << 1,
+ /** hardware is able to detect and set modulation automatically */
+ AUTO = 1 << 0,
+ MOD_8VSB = 1 << 2,
+ MOD_16VSB = 1 << 3,
};
/**
@@ -191,12 +193,14 @@
@export
enum FrontendAtsc3Modulation : uint32_t {
UNDEFINED = 0,
- MOD_QPSK = 1 << 0,
- MOD_16QAM = 1 << 1,
- MOD_64QAM = 1 << 2,
- MOD_256QAM = 1 << 3,
- MOD_1024QAM = 1 << 4,
- MOD_4096QAM = 1 << 5,
+ /** hardware is able to detect and set modulation automatically */
+ AUTO = 1 << 0,
+ MOD_QPSK = 1 << 1,
+ MOD_16QAM = 1 << 2,
+ MOD_64QAM = 1 << 3,
+ MOD_256QAM = 1 << 4,
+ MOD_1024QAM = 1 << 5,
+ MOD_4096QAM = 1 << 6,
};
/**
@@ -205,9 +209,11 @@
@export
enum FrontendAtsc3Bandwidth : uint32_t {
UNDEFINED = 0,
- BANDWIDTH_8MHZ = 1 << 0,
- BANDWIDTH_7MHZ = 1 << 1,
- BANDWIDTH_6MHZ = 1 << 2,
+ /** hardware is able to detect and set bandwidth automatically */
+ AUTO = 1 << 0,
+ BANDWIDTH_6MHZ = 1 << 1,
+ BANDWIDTH_7MHZ = 1 << 2,
+ BANDWIDTH_8MHZ = 1 << 3,
};
/**
@@ -215,9 +221,11 @@
*/
@export
enum FrontendAtsc3TimeInterleaveMode : uint32_t {
- UNDEFINED,
- CTI,
- HTI,
+ UNDEFINED = 0,
+ /** hardware is able to detect and set TimeInterleaveMode automatically */
+ AUTO = 1 << 0,
+ CTI = 1 << 1,
+ HTI = 1 << 2,
};
/**
@@ -247,13 +255,39 @@
*/
@export
enum FrontendAtsc3Fec : uint32_t {
- UNDEFINED,
- BCH_LDPC_16K,
- BCH_LDPC_64K,
- CRC_LDPC_16K,
- CRC_LDPC_64K,
- LDPC_16K,
- LDPC_64K,
+ UNDEFINED = 0,
+ /** hardware is able to detect and set FEC automatically */
+ AUTO = 1 << 0,
+ BCH_LDPC_16K = 1 << 1,
+ BCH_LDPC_64K = 1 << 2,
+ CRC_LDPC_16K = 1 << 3,
+ CRC_LDPC_64K = 1 << 4,
+ LDPC_16K = 1 << 5,
+ LDPC_64K = 1 << 6,
+};
+
+/**
+ * Demodulator Output Format for an ATSC3 Frontend.
+ */
+@export
+enum FrontendAtsc3DemodOutputFormat : uint8_t {
+ /** Dummy. Scan uses this. */
+ UNDEFINED = 0,
+ /** ALP format. Typically used in US region. */
+ ATSC3_LINKLAYER_PACKET = 1 << 0,
+ /** BaseBand packet format. Typically used in Korea region. */
+ BASEBAND_PACKET = 1 << 1,
+};
+
+/**
+ * PLP basis Signal Settings for an ATSC3 Frontend.
+ */
+struct FrontendAtsc3PlpSettings {
+ uint8_t plpId;
+ FrontendAtsc3Modulation modulation;
+ FrontendAtsc3TimeInterleaveMode interleaveMode;
+ FrontendAtsc3CodeRate codeRate;
+ FrontendAtsc3Fec fec;
};
/**
@@ -262,21 +296,28 @@
struct FrontendAtsc3Settings {
/** Signal frequency in Hertz */
uint32_t frequency;
+ /** Bandwidth of tuning band. */
FrontendAtsc3Bandwidth bandwidth;
- FrontendAtsc3TimeInterleaveMode interleaveMode;
- FrontendAtsc3CodeRate codeRate;
- FrontendAtsc3Fec fec;
- vec<uint8_t> plpIdList;
+ FrontendAtsc3DemodOutputFormat demodOutputFormat;
+ vec<FrontendAtsc3PlpSettings> plpSettings;
};
/**
* Capabilities for ATSC3 Frontend.
*/
struct FrontendAtsc3Capabilities {
- /** Modulation capability */
- bitfield<FrontendAtsc3Modulation> modulationCap;
/** Bandwidth capability */
bitfield<FrontendAtsc3Bandwidth> bandwidthCap;
+ /** Modulation capability */
+ bitfield<FrontendAtsc3Modulation> modulationCap;
+ /** TimeInterleaveMode capability */
+ bitfield<FrontendAtsc3TimeInterleaveMode> timeInterleaveModeCap;
+ /** CodeRate capability */
+ bitfield<FrontendAtsc3CodeRate> codeRateCap;
+ /** FEC capability */
+ bitfield<FrontendAtsc3Fec> fecCap;
+ /** Demodulator Output Format capability */
+ bitfield<FrontendAtsc3DemodOutputFormat> demodOutputFormatCap;
};
/**
@@ -614,7 +655,7 @@
};
/**
- * Modulaltion Type for ISDBS.
+ * Modulation Type for ISDBS.
*/
@export
enum FrontendIsdbsModulation : uint32_t {
@@ -647,7 +688,7 @@
@export
enum FrontendIsdbsStreamIdType : uint32_t {
STREAM_ID,
- RELATIVE_STREAM_ID,
+ RELATIVE_STREAM_NUMBER,
};
/**
@@ -845,6 +886,7 @@
M_EIA_J = 1 << 13,
I_NICAM = 1 << 14,
L_NICAM = 1 << 15,
+ L_PRIME = 1 << 16,
};
/**
@@ -907,17 +949,27 @@
PLP_IDS,
/** Locked group Ids for DVBT2 frontend. */
GROUP_IDS,
- /** Locked the number of the Plps. */
- INPUT_STREAM_IDS,
- /** Locked signal stardard. */
+ /** Stream Ids. */
+ INPUT_STREAM_IDS,
+ /** Locked signal standard. */
STANDARD,
+ /** PLP status in a tuned frequency band for ATSC3 frontend. */
+ ATSC3_PLP_INFO,
+};
+
+/**
+ * ATSC3.0 PLP information for scan
+ */
+struct FrontendScanAtsc3PlpInfo {
+ uint8_t plpId;
+ bool bLlsFlag;
};
/**
* Scan Message for Frontend.
*/
safe_union FrontendScanMessage {
- bool islocked;
+ bool isLocked;
bool isEnd;
/** scan progress percent (0..100) */
uint8_t progressPercent;
@@ -927,11 +979,13 @@
uint32_t symbolRate;
vec<uint8_t> plpIds;
vec<uint8_t> groupIds;
- vec<uint8_t> inputStreamIds;
+ vec<uint16_t> inputStreamIds;
safe_union standard {
FrontendDvbsStandard sStd;
FrontendDvbtStandard tStd;
} std;
+ /** A list of PLP status in a tuned frequency band for ATSC3 frontend. */
+ vec<FrontendScanAtsc3PlpInfo> atsc3PlpInfos;
};
/**
@@ -940,17 +994,17 @@
@export
enum FrontendEventType : uint32_t {
/**
- * If frontend locked the signal which is specified by tune method, HAL sent
+ * If frontend locked the signal which is specified by tune method, HAL sends
* Locked event.
*/
LOCKED,
/**
* If frontend can't locked the signal which is specified by tune method,
- * HAL sent NO_SIGNAL event.
+ * HAL sends NO_SIGNAL event.
*/
NO_SIGNAL,
/**
- * If frontend detect that the locked signal get lost, HAL sent LOST_LOCK
+ * If frontend detect that the locked signal get lost, HAL sends LOST_LOCK
* event.
*/
LOST_LOCK,
@@ -977,15 +1031,15 @@
*/
@export
enum FrontendStatusType : uint32_t {
- /** Lock status for RF or Demod. */
- LOCK,
+ /** Lock status for Demod. */
+ DEMOD_LOCK,
/** Signal to Noise Ratio. */
SNR,
/** Bit Error Ratio. */
BER,
/** Packages Error Ratio. */
PER,
- /** Bit Error Ratio befor FEC. */
+ /** Bit Error Ratio before FEC. */
PRE_BER,
/*
* Signal Quality (0..100). Good data over total data in percent can be
@@ -993,7 +1047,7 @@
*/
SIGNAL_QUALITY,
/** Signal Strength. */
- SIGGAL_STRENGTH,
+ SIGNAL_STRENGTH,
/** Symbol Rate. */
SYMBOL_RATE,
/** Forward Error Correction Type. */
@@ -1008,21 +1062,62 @@
PLP_ID,
/** Status for Emergency Warning Broadcasting System. */
EWBS,
+ /** Automatic Gain Control. */
+ AGC,
+ /** Low Noise Amplifier. */
+ LNA,
+ /** Lock status for stream. */
+ STREAM_LOCK,
+ /** Error status by layer. */
+ LAYER_ERROR,
+ /** CN value by VBER. */
+ VBER_CN,
+ /** CN value by LBER. */
+ LBER_CN,
+ /** CN value by XER. */
+ XER_CN,
+ /** Moduration Error Ratio. */
+ MER,
+ /** Difference between tuning frequency and actual locked frequency. */
+ FREQ_OFFSET,
+ /* Hierarchy for DVBT. */
+ HIERARCHY,
+ /** Lock status for RF. */
+ RF_LOCK,
+ /** PLP information in a frequency band for ATSC3.0 frontend. */
+ ATSC3_PLP_INFO,
};
/**
+ * Status for each tuning PLPs
+ */
+struct FrontendStatusAtsc3PlpInfo {
+ /** PLP Id value. */
+ uint8_t plpId;
+ /** Demod Lock/Unlock status of this particular PLP. */
+ bool isLocked;
+ /** Uncorrectable Error Counts (UEC) of this particular PLP since last tune operation. */
+ uint32_t uec;
+};
+
+
+/**
* Modulation Type for Frontend's status.
*/
safe_union FrontendModulationStatus {
+ FrontendDvbcModulation dvbc;
FrontendDvbsModulation dvbs;
- FrontendAtsc3Modulation atsc3;
+ FrontendIsdbsModulation isdbs;
+ FrontendIsdbs3Modulation isdbs3;
+ FrontendIsdbtModulation isdbt;
};
/**
* The status for Frontend.
*/
safe_union FrontendStatus {
- bool isLocked;
+ /** Lock status for Demod in True/False. */
+ bool isDemodLocked;
/** SNR value measured by 0.001 dB. */
int32_t snr;
/** The number of error bit per 1 billion bits. */
@@ -1043,6 +1138,25 @@
FrontendLnbVoltage lnbVoltage;
uint8_t plpId;
bool isEWBS;
+ /** AGC value is normalized from 0 to 255. */
+ uint8_t agc;
+ bool isLnaOn;
+ bool isStreamLock;
+ vec<bool> isLayerError;
+ /** CN value by VBER measured by 0.001 dB */
+ int32_t vberCn;
+ /** CN value by LBER measured by 0.001 dB */
+ int32_t lberCn;
+ /** CN value by XER measured by 0.001 dB */
+ int32_t xerCn;
+ /** MER value measured by 0.001 dB */
+ int32_t mer;
+ /** Frequency difference in Hertz. */
+ int32_t freqOffset;
+ FrontendDvbtHierarchy hierarchy;
+ bool isRfLocked;
+ /** A list of PLP status for tuned PLPs for ATSC3 frontend. */
+ vec<FrontendStatusAtsc3PlpInfo> plpInfo;
};
/**
@@ -1121,7 +1235,6 @@
POSITION_B,
};
-
/* Demux ID is used to associate with a hardware demux resource. */
typedef uint32_t DemuxId;
@@ -1150,7 +1263,7 @@
*/
AUDIO,
/**
- * A filter to filter Vidoe Metadata out from input stream.
+ * A filter to filter Video Metadata out from input stream.
*/
VIDEO,
/**
@@ -1475,6 +1588,8 @@
PES,
/* Data is Elementary Stream. */
ES,
+ /* Data is TLV (type-length-value) Stream for JP SHV */
+ SHV_TLV,
};
/**
@@ -1534,6 +1649,10 @@
SPACE_FULL = 1 << 3,
};
+/**
+ * The Settings for the demux's input.
+ */
+@export
struct DemuxInputSettings {
/**
* Register for interested status events so that the HAL can send these
@@ -1559,3 +1678,30 @@
*/
uint8_t packetSize;
};
+
+/**
+ * Capabilities for Demux.
+ */
+@export
+struct DemuxCapabilities {
+ /* The number of Demux to be supported. */
+ uint32_t numDemux;
+ /* The number of Input to be supported. */
+ uint32_t numInput;
+ /* The number of Output to be supported. */
+ uint32_t numOutput;
+ /* The number of TS Filter to be supported. */
+ uint32_t numTsFilter;
+ /* The number of Section Filter to be supported. */
+ uint32_t numSectionFilter;
+ /* The number of Audio Filter to be supported. */
+ uint32_t numAudioFilter;
+ /* The number of Video Filter to be supported. */
+ uint32_t numVideoFilter;
+ /* The number of PES Filter to be supported. */
+ uint32_t numPesFilter;
+ /* The number of PCR Filter to be supported. */
+ uint32_t numPcrFilter;
+ /* The maximum number of bytes is supported in the mask of Section Filter. */
+ uint32_t numBytesInSectionFilter;
+};
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index 7256cc4..7936185 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -39,6 +39,7 @@
#include <map>
#define WAIT_TIMEOUT 3000000000
+#define WAIT_TIMEOUT_data_ready 3000000000 * 4
using android::Condition;
using android::IMemory;
@@ -58,8 +59,10 @@
using android::hardware::Void;
using android::hardware::tv::tuner::V1_0::DemuxDataFormat;
using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
using android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent;
using android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
using android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
using android::hardware::tv::tuner::V1_0::DemuxFilterType;
@@ -130,9 +133,6 @@
const uint16_t FMQ_SIZE_4K = 0x1000;
const uint32_t FMQ_SIZE_1M = 0x100000;
-// Equal to SECTION_WRITE_COUNT on the HAL impl side
-// The HAL impl will repeatedly write to the FMQ the count times
-const uint16_t SECTION_READ_COUNT = 10;
struct FilterConf {
DemuxFilterType type;
@@ -214,11 +214,15 @@
class DemuxCallback : public IDemuxCallback {
public:
virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent) override {
- ALOGW("[VTS] FILTER EVENT %d", filterEvent.filterId);
android::Mutex::Autolock autoLock(mMsgLock);
- mFilterEventReceived = true;
+ // Temprarily we treat the first coming back filter data on the matching pid a success
+ // once all of the MQ are cleared, means we got all the expected output
mFilterIdToEvent[filterEvent.filterId] = filterEvent;
- startFilterEventThread(filterEvent);
+ readFilterEventData(filterEvent.filterId);
+ mPidFilterOutputCount++;
+ // mFilterIdToMQ.erase(filterEvent.filterId);
+
+ // startFilterEventThread(filterEvent);
mMsgCondition.signal();
return Void();
}
@@ -232,13 +236,16 @@
virtual Return<void> onInputStatus(DemuxInputStatus status) override {
// android::Mutex::Autolock autoLock(mMsgLock);
+ ALOGW("[vts] input status %d", status);
switch (status) {
case DemuxInputStatus::SPACE_EMPTY:
case DemuxInputStatus::SPACE_ALMOST_EMPTY:
+ ALOGW("[vts] keep inputing %d", status);
mKeepWritingInputFMQ = true;
break;
case DemuxInputStatus::SPACE_ALMOST_FULL:
case DemuxInputStatus::SPACE_FULL:
+ ALOGW("[vts] stop inputing %d", status);
mKeepWritingInputFMQ = false;
break;
}
@@ -246,78 +253,64 @@
}
void testOnFilterEvent(uint32_t filterId);
- void testOnSectionFilterEvent(sp<IDemux>& demux, uint32_t filterId, MQDesc& filterMQDescriptor,
- MQDesc& inputMQDescriptor);
void testFilterDataOutput();
- // Legacy
- bool readAndCompareSectionEventData(uint32_t filterId);
+ void stopInputThread();
void startPlaybackInputThread(InputConf inputConf, MQDesc& inputMQDescriptor);
void startFilterEventThread(DemuxFilterEvent event);
static void* __threadLoopInput(void* threadArgs);
static void* __threadLoopFilter(void* threadArgs);
- void inputThreadLoop(InputConf inputConf, bool* keepWritingInputFMQ, MQDesc& inputMQDescriptor);
+ void inputThreadLoop(InputConf* inputConf, bool* keepWritingInputFMQ);
void filterThreadLoop(DemuxFilterEvent& event);
void updateFilterMQ(uint32_t filterId, MQDesc& filterMQDescriptor);
void updateGoldenOutputMap(uint32_t filterId, string goldenOutputFile);
+ bool readFilterEventData(uint32_t filterId);
private:
struct InputThreadArgs {
DemuxCallback* user;
- InputConf inputConf;
+ InputConf* inputConf;
bool* keepWritingInputFMQ;
- MQDesc& inputMQDesc;
};
struct FilterThreadArgs {
DemuxCallback* user;
- DemuxFilterEvent& event;
+ DemuxFilterEvent event;
};
uint16_t mDataLength = 0;
std::vector<uint8_t> mDataOutputBuffer;
bool mFilterEventReceived;
std::map<uint32_t, string> mFilterIdToGoldenOutput;
- std::map<uint32_t, DemuxFilterEvent> mFilterIdToEvent;
std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterIdToMQ;
std::unique_ptr<FilterMQ> mInputMQ;
std::map<uint32_t, EventFlag*> mFilterIdToMQEventFlag;
+ std::map<uint32_t, DemuxFilterEvent> mFilterIdToEvent;
EventFlag* mInputMQEventFlag;
android::Mutex mMsgLock;
android::Mutex mFilterOutputLock;
+ android::Mutex mInputThreadLock;
android::Condition mMsgCondition;
android::Condition mFilterOutputCondition;
- bool mKeepWritingInputFMQ;
+ bool mKeepWritingInputFMQ = true;
bool mInputThreadRunning;
pthread_t mInputThread;
pthread_t mFilterThread;
+
+ int mPidFilterOutputCount = 0;
};
-// Legacy
-void DemuxCallback::testOnFilterEvent(uint32_t filterId) {
- android::Mutex::Autolock autoLock(mMsgLock);
- while (!mFilterEventReceived) {
- if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
- EXPECT_TRUE(false) << "filter event not received within timeout";
- return;
- }
- }
- // Reset the filter event recieved flag
- mFilterEventReceived = false;
- // Check if filter id match
- EXPECT_TRUE(filterId == mFilterIdToEvent[filterId].filterId) << "filter id match";
-}
-
void DemuxCallback::startPlaybackInputThread(InputConf inputConf, MQDesc& inputMQDescriptor) {
+ mInputMQ = std::make_unique<FilterMQ>(inputMQDescriptor, true /* resetPointers */);
+ EXPECT_TRUE(mInputMQ);
struct InputThreadArgs* threadArgs =
(struct InputThreadArgs*)malloc(sizeof(struct InputThreadArgs));
threadArgs->user = this;
- threadArgs->inputConf = inputConf;
+ threadArgs->inputConf = &inputConf;
threadArgs->keepWritingInputFMQ = &mKeepWritingInputFMQ;
- threadArgs->inputMQDesc = inputMQDescriptor;
pthread_create(&mInputThread, NULL, __threadLoopInput, (void*)threadArgs);
pthread_setname_np(mInputThread, "test_playback_input_loop");
@@ -334,72 +327,22 @@
}
void DemuxCallback::testFilterDataOutput() {
- android::Mutex::Autolock autoLock(mFilterOutputLock);
- while (!mFilterIdToMQ.empty()) {
- if (-ETIMEDOUT == mFilterOutputCondition.waitRelative(mFilterOutputLock, WAIT_TIMEOUT)) {
- EXPECT_TRUE(false) << "filter output does not match golden output within timeout";
+ android::Mutex::Autolock autoLock(mMsgLock);
+ while (mPidFilterOutputCount < 1) {
+ if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+ EXPECT_TRUE(false) << "filter output matching pid does not output within timeout";
return;
}
}
+ mPidFilterOutputCount = 0;
+ ALOGW("[vts] pass and stop");
}
-// Legacy
-void DemuxCallback::testOnSectionFilterEvent(sp<IDemux>& demux, uint32_t filterId,
- MQDesc& filterMQDescriptor,
- MQDesc& inputMQDescriptor) {
- Result status;
- // Create MQ to read the output into the local buffer
- mFilterIdToMQ[filterId] =
- std::make_unique<FilterMQ>(filterMQDescriptor, true /* resetPointers */);
- EXPECT_TRUE(mFilterIdToMQ[filterId]);
- // Get the MQ to write the input to the HAL
- mInputMQ = std::make_unique<FilterMQ>(inputMQDescriptor, true /* resetPointers */);
- EXPECT_TRUE(mInputMQ);
- // Create the EventFlag that is used to signal the HAL impl that data have been
- // read the Filter FMQ
- EXPECT_TRUE(EventFlag::createEventFlag(mFilterIdToMQ[filterId]->getEventFlagWord(),
- &mFilterIdToMQEventFlag[filterId]) == android::OK);
- // Create the EventFlag that is used to signal the HAL impl that data have been
- // written into the Input FMQ
- EXPECT_TRUE(EventFlag::createEventFlag(mInputMQ->getEventFlagWord(), &mInputMQEventFlag) ==
- android::OK);
- // Start filter
- status = demux->startFilter(filterId);
- status = demux->startInput();
+void DemuxCallback::stopInputThread() {
+ mInputThreadRunning = false;
+ mKeepWritingInputFMQ = false;
- EXPECT_EQ(status, Result::SUCCESS);
- // Test start filter and receive callback event
- for (int i = 0; i < SECTION_READ_COUNT; i++) {
- // Write input FMQ and notify the Tuner Implementation
- EXPECT_TRUE(mInputMQ->write(goldenDataOutputBuffer.data(), goldenDataOutputBuffer.size()));
- mInputMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
- testOnFilterEvent(filterId);
- // checksum of mDataOutputBuffer and Input golden input
- if (readAndCompareSectionEventData(filterId) && i < SECTION_READ_COUNT - 1) {
- mFilterIdToMQEventFlag[filterId]->wake(
- static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
- }
- }
-}
-
-// Legacy
-bool DemuxCallback::readAndCompareSectionEventData(uint32_t filterId) {
- bool result = false;
- DemuxFilterEvent filterEvent = mFilterIdToEvent[filterId];
- for (int i = 0; i < filterEvent.events.size(); i++) {
- DemuxFilterSectionEvent event = filterEvent.events[i].section();
- mDataLength = event.dataLength;
- EXPECT_TRUE(mDataLength == goldenDataOutputBuffer.size()) << "buffer size does not match";
-
- mDataOutputBuffer.resize(mDataLength);
- result = mFilterIdToMQ[filterId]->read(mDataOutputBuffer.data(), mDataLength);
- EXPECT_TRUE(result) << "can't read from Filter MQ";
-
- for (int i = 0; i < mDataLength; i++) {
- EXPECT_TRUE(goldenDataOutputBuffer[i] == mDataOutputBuffer[i]) << "data does not match";
- }
- }
- return result;
+ android::Mutex::Autolock autoLock(mInputThreadLock);
}
void DemuxCallback::updateFilterMQ(uint32_t filterId, MQDesc& filterMQDescriptor) {
@@ -418,58 +361,60 @@
DemuxCallback* const self =
static_cast<DemuxCallback*>(((struct InputThreadArgs*)threadArgs)->user);
self->inputThreadLoop(((struct InputThreadArgs*)threadArgs)->inputConf,
- ((struct InputThreadArgs*)threadArgs)->keepWritingInputFMQ,
- ((struct InputThreadArgs*)threadArgs)->inputMQDesc);
+ ((struct InputThreadArgs*)threadArgs)->keepWritingInputFMQ);
return 0;
}
-void DemuxCallback::inputThreadLoop(InputConf inputConf, bool* keepWritingInputFMQ,
- MQDesc& inputMQDescriptor) {
+void DemuxCallback::inputThreadLoop(InputConf* inputConf, bool* keepWritingInputFMQ) {
+ android::Mutex::Autolock autoLock(mInputThreadLock);
mInputThreadRunning = true;
- std::unique_ptr inputMQ =
- std::make_unique<FilterMQ>(inputMQDescriptor, true /* resetPointers */);
- EXPECT_TRUE(inputMQ);
-
// Create the EventFlag that is used to signal the HAL impl that data have been
// written into the Input FMQ
EventFlag* inputMQEventFlag;
- EXPECT_TRUE(EventFlag::createEventFlag(inputMQ->getEventFlagWord(), &inputMQEventFlag) ==
+ EXPECT_TRUE(EventFlag::createEventFlag(mInputMQ->getEventFlagWord(), &inputMQEventFlag) ==
android::OK);
// open the stream and get its length
- std::ifstream inputData(inputConf.inputDataFile /*"ts/test1.ts"*/, std::ifstream::binary);
- int writeSize = inputConf.setting.packetSize * 6;
+ std::ifstream inputData(inputConf->inputDataFile, std::ifstream::binary);
+ int writeSize = inputConf->setting.packetSize * 6;
char* buffer = new char[writeSize];
- if (!inputData) {
- // log
+ ALOGW("[vts] input thread loop start %s", inputConf->inputDataFile.c_str());
+ if (!inputData.is_open()) {
mInputThreadRunning = false;
+ ALOGW("[vts] Error %s", strerror(errno));
}
while (mInputThreadRunning) {
- // move the stream pointer for packet size * 2k? every read until end
+ // move the stream pointer for packet size * 6 every read until the end
while (*keepWritingInputFMQ) {
inputData.read(buffer, writeSize);
if (!inputData) {
int leftSize = inputData.gcount();
+ if (leftSize == 0) {
+ mInputThreadRunning = false;
+ break;
+ }
inputData.clear();
inputData.read(buffer, leftSize);
// Write the left over of the input data and quit the thread
if (leftSize > 0) {
- EXPECT_TRUE(inputMQ->write((unsigned char*)&buffer[0],
- leftSize / inputConf.setting.packetSize));
+ EXPECT_TRUE(mInputMQ->write((unsigned char*)&buffer[0], leftSize));
inputMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
}
mInputThreadRunning = false;
break;
}
// Write input FMQ and notify the Tuner Implementation
- EXPECT_TRUE(inputMQ->write((unsigned char*)&buffer[0], 6));
+ EXPECT_TRUE(mInputMQ->write((unsigned char*)&buffer[0], writeSize));
inputMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
inputData.seekg(writeSize, inputData.cur);
+ sleep(1);
}
}
+ ALOGW("[vts] Input thread end.");
+
delete[] buffer;
inputData.close();
}
@@ -481,9 +426,9 @@
return 0;
}
-void DemuxCallback::filterThreadLoop(DemuxFilterEvent& /*event*/) {
+void DemuxCallback::filterThreadLoop(DemuxFilterEvent& /* event */) {
android::Mutex::Autolock autoLock(mFilterOutputLock);
- // Read from MQ[event.filterId] per event and filter type
+ // Read from mFilterIdToMQ[event.filterId] per event and filter type
// Assemble to filterOutput[filterId]
@@ -494,6 +439,30 @@
// end thread
}
+bool DemuxCallback::readFilterEventData(uint32_t filterId) {
+ bool result = false;
+ DemuxFilterEvent filterEvent = mFilterIdToEvent[filterId];
+ ALOGW("[vts] reading from filter FMQ %d", filterId);
+ // todo separate filter handlers
+ for (int i = 0; i < filterEvent.events.size(); i++) {
+ DemuxFilterPesEvent event = filterEvent.events[i].pes();
+ mDataLength = event.dataLength;
+ // EXPECT_TRUE(mDataLength == goldenDataOutputBuffer.size()) << "buffer size does not
+ // match";
+
+ mDataOutputBuffer.resize(mDataLength);
+ result = mFilterIdToMQ[filterId]->read(mDataOutputBuffer.data(), mDataLength);
+ EXPECT_TRUE(result) << "can't read from Filter MQ";
+
+ /*for (int i = 0; i < mDataLength; i++) {
+ EXPECT_TRUE(goldenDataOutputBuffer[i] == mDataOutputBuffer[i]) << "data does not match";
+ }*/
+ }
+ mFilterIdToMQEventFlag[filterId]->wake(
+ static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
+ return result;
+}
+
// Test environment for Tuner HIDL HAL.
class TunerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
public:
@@ -528,6 +497,7 @@
sp<DemuxCallback> mDemuxCallback;
MQDesc mFilterMQDescriptor;
MQDesc mInputMQDescriptor;
+ vector<uint32_t> mUsedFilterIds;
uint32_t mDemuxId;
uint32_t mFilterId;
@@ -540,7 +510,8 @@
::testing::AssertionResult stopTuneFrontend(int32_t frontendId);
::testing::AssertionResult closeFrontend(int32_t frontendId);
::testing::AssertionResult createDemux();
- ::testing::AssertionResult createDemuxWithFrontend(int32_t frontendId);
+ ::testing::AssertionResult createDemuxWithFrontend(int32_t frontendId,
+ FrontendSettings settings);
::testing::AssertionResult getInputMQDescriptor();
::testing::AssertionResult addInputToDemux(DemuxInputSettings setting);
::testing::AssertionResult addFilterToDemux(DemuxFilterType type, DemuxFilterSettings setting);
@@ -552,10 +523,8 @@
::testing::AssertionResult playbackDataFlowTest(vector<FilterConf> filterConf,
InputConf inputConf,
vector<string> goldenOutputFiles);
-
- // Legacy
- ::testing::AssertionResult addSectionFilterToDemux();
- ::testing::AssertionResult readSectionFilterDataOutput();
+ ::testing::AssertionResult broadcastDataFlowTest(vector<FilterConf> filterConf,
+ vector<string> goldenOutputFiles);
};
::testing::AssertionResult TunerHidlTest::createFrontend(int32_t frontendId) {
@@ -586,7 +555,7 @@
.frequency = 0,
.modulation = FrontendAtscModulation::UNDEFINED,
};
- frontendSettings.atsc() = frontendAtscSettings;
+ frontendSettings.atsc(frontendAtscSettings);
mFrontendCallback->testOnEvent(mFrontend, frontendSettings);
FrontendDvbtSettings frontendDvbtSettings{
@@ -630,7 +599,8 @@
return ::testing::AssertionResult(status == Result::SUCCESS);
}
-::testing::AssertionResult TunerHidlTest::createDemuxWithFrontend(int32_t frontendId) {
+::testing::AssertionResult TunerHidlTest::createDemuxWithFrontend(int32_t frontendId,
+ FrontendSettings settings) {
Result status;
if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
@@ -641,6 +611,8 @@
return ::testing::AssertionFailure();
}
+ mFrontendCallback->testOnEvent(mFrontend, settings);
+
status = mDemux->setFrontendDataSource(frontendId);
return ::testing::AssertionResult(status == Result::SUCCESS);
@@ -705,7 +677,7 @@
mDemuxCallback = new DemuxCallback();
}
- // Add section filter to the local demux
+ // Add playback input to the local demux
status = mDemux->addInput(FMQ_SIZE_1M, mDemuxCallback);
if (status != Result::SUCCESS) {
@@ -732,29 +704,6 @@
return ::testing::AssertionResult(status == Result::SUCCESS);
}
-// Legacy
-::testing::AssertionResult TunerHidlTest::addSectionFilterToDemux() {
- Result status;
-
- if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
- return ::testing::AssertionFailure();
- }
-
- // Create demux callback
- if (!mDemuxCallback) {
- mDemuxCallback = new DemuxCallback();
- }
-
- // Add section filter to the local demux
- mDemux->addFilter(DemuxFilterType::SECTION, FMQ_SIZE_4K, mDemuxCallback,
- [&](Result result, uint32_t filterId) {
- mFilterId = filterId;
- status = result;
- });
-
- return ::testing::AssertionResult(status == Result::SUCCESS);
-}
-
::testing::AssertionResult TunerHidlTest::addFilterToDemux(DemuxFilterType type,
DemuxFilterSettings setting) {
Result status;
@@ -800,36 +749,10 @@
return ::testing::AssertionResult(status == Result::SUCCESS);
}
-// Legacy
-::testing::AssertionResult TunerHidlTest::readSectionFilterDataOutput() {
- // Filter Configuration Module
- DemuxInputSettings setting{
- .statusMask = 0xf,
- .lowThreshold = 0x1000,
- .highThreshold = 0x100000,
- .dataFormat = DemuxDataFormat::TS,
- .packetSize = 188,
- };
- if (addSectionFilterToDemux() == ::testing::AssertionFailure() ||
- getFilterMQDescriptor(mFilterId) == ::testing::AssertionFailure() ||
- addInputToDemux(setting) == ::testing::AssertionFailure() ||
- getInputMQDescriptor() == ::testing::AssertionFailure()) {
- return ::testing::AssertionFailure();
- }
-
- // Data Verify Module
- // Test start filter and read the output data
- mDemuxCallback->testOnSectionFilterEvent(mDemux, mFilterId, mFilterMQDescriptor,
- mInputMQDescriptor);
-
- // Clean Up Module
- return closeDemux(); //::testing::AssertionSuccess();
-}
-
-::testing::AssertionResult TunerHidlTest::playbackDataFlowTest(vector<FilterConf> filterConf,
- InputConf inputConf,
- vector<string> goldenOutputFiles) {
+::testing::AssertionResult TunerHidlTest::playbackDataFlowTest(
+ vector<FilterConf> filterConf, InputConf inputConf, vector<string> /*goldenOutputFiles*/) {
Result status;
+ int filterIdsSize;
// Filter Configuration Module
for (int i = 0; i < filterConf.size(); i++) {
if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
@@ -838,8 +761,11 @@
getFilterMQDescriptor(mFilterId) == ::testing::AssertionFailure()) {
return ::testing::AssertionFailure();
}
+ filterIdsSize = mUsedFilterIds.size();
+ mUsedFilterIds.resize(filterIdsSize + 1);
+ mUsedFilterIds[filterIdsSize] = mFilterId;
mDemuxCallback->updateFilterMQ(mFilterId, mFilterMQDescriptor);
- mDemuxCallback->updateGoldenOutputMap(mFilterId, goldenOutputFiles[i]);
+ // mDemuxCallback->updateGoldenOutputMap(mFilterId, goldenOutputFiles[i]);
status = mDemux->startFilter(mFilterId);
if (status != Result::SUCCESS) {
return ::testing::AssertionFailure();
@@ -860,8 +786,76 @@
// Data Verify Module
mDemuxCallback->testFilterDataOutput();
+ mDemuxCallback->stopInputThread();
// Clean Up Module
+ for (int i = 0; i <= filterIdsSize; i++) {
+ if (mDemux->stopFilter(mUsedFilterIds[i]) != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ }
+ if (mDemux->stopInput() != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ return closeDemux();
+}
+
+::testing::AssertionResult TunerHidlTest::broadcastDataFlowTest(
+ vector<FilterConf> filterConf, vector<string> /*goldenOutputFiles*/) {
+ Result status;
+ hidl_vec<FrontendId> feIds;
+
+ mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
+ status = result;
+ feIds = frontendIds;
+ });
+
+ if (feIds.size() == 0) {
+ ALOGW("[ WARN ] Frontend isn't available");
+ return ::testing::AssertionFailure();
+ }
+
+ FrontendDvbtSettings dvbt{
+ .frequency = 1000,
+ };
+ FrontendSettings settings;
+ settings.dvbt(dvbt);
+
+ if (createDemuxWithFrontend(feIds[0], settings) != ::testing::AssertionSuccess()) {
+ return ::testing::AssertionFailure();
+ }
+
+ int filterIdsSize;
+ // Filter Configuration Module
+ for (int i = 0; i < filterConf.size(); i++) {
+ if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
+ ::testing::AssertionFailure() ||
+ // TODO use a map to save the FMQs/EvenFlags and pass to callback
+ getFilterMQDescriptor(mFilterId) == ::testing::AssertionFailure()) {
+ return ::testing::AssertionFailure();
+ }
+ filterIdsSize = mUsedFilterIds.size();
+ mUsedFilterIds.resize(filterIdsSize + 1);
+ mUsedFilterIds[filterIdsSize] = mFilterId;
+ mDemuxCallback->updateFilterMQ(mFilterId, mFilterMQDescriptor);
+ status = mDemux->startFilter(mFilterId);
+ if (status != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ }
+
+ // Data Verify Module
+ mDemuxCallback->testFilterDataOutput();
+
+ // Clean Up Module
+ for (int i = 0; i <= filterIdsSize; i++) {
+ if (mDemux->stopFilter(mUsedFilterIds[i]) != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
+ }
+ if (mFrontend->stopTune() != Result::SUCCESS) {
+ return ::testing::AssertionFailure();
+ }
return closeDemux();
}
@@ -948,7 +942,7 @@
}
}
-TEST_F(TunerHidlTest, CreateDemuxWithFrontend) {
+/*TEST_F(TunerHidlTest, CreateDemuxWithFrontend) {
Result status;
hidl_vec<FrontendId> feIds;
@@ -963,10 +957,17 @@
return;
}
+ FrontendDvbtSettings dvbt{
+ .frequency = 1000,
+ };
+ FrontendSettings settings;
+ settings.dvbt(dvbt);
+
for (size_t i = 0; i < feIds.size(); i++) {
- ASSERT_TRUE(createDemuxWithFrontend(feIds[i]));
+ ASSERT_TRUE(createDemuxWithFrontend(feIds[i], settings));
+ mFrontend->stopTune();
}
-}
+}*/
TEST_F(TunerHidlTest, CreateDemux) {
description("Create Demux");
@@ -991,9 +992,63 @@
/*
* DATA FLOW TESTS
*/
-TEST_F(TunerHidlTest, ReadSectionFilterOutput) {
- description("Read data output from FMQ of a Section Filter");
- ASSERT_TRUE(readSectionFilterDataOutput());
+TEST_F(TunerHidlTest, PlaybackDataFlowWithPesFilterTest) {
+ description("Feed ts data from playback and configure pes filter to get output");
+
+ // todo modulize the filter conf parser
+ vector<FilterConf> filterConf;
+ filterConf.resize(1);
+
+ DemuxFilterSettings filterSetting;
+ DemuxFilterPesDataSettings pesFilterSetting{
+ .tpid = 18,
+ };
+ filterSetting.pesData(pesFilterSetting);
+ FilterConf pesFilterConf{
+ .type = DemuxFilterType::PES,
+ .setting = filterSetting,
+ };
+ filterConf[0] = pesFilterConf;
+
+ DemuxInputSettings inputSetting{
+ .statusMask = 0xf,
+ .lowThreshold = 0x1000,
+ .highThreshold = 0x07fff,
+ .dataFormat = DemuxDataFormat::TS,
+ .packetSize = 188,
+ };
+
+ InputConf inputConf{
+ .inputDataFile = "/vendor/etc/test1.ts",
+ .setting = inputSetting,
+ };
+
+ vector<string> goldenOutputFiles;
+
+ ASSERT_TRUE(playbackDataFlowTest(filterConf, inputConf, goldenOutputFiles));
+}
+
+TEST_F(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) {
+ description("Feed ts data from frontend and test with PES filter");
+
+ // todo modulize the filter conf parser
+ vector<FilterConf> filterConf;
+ filterConf.resize(1);
+
+ DemuxFilterSettings filterSetting;
+ DemuxFilterPesDataSettings pesFilterSetting{
+ .tpid = 18,
+ };
+ filterSetting.pesData(pesFilterSetting);
+ FilterConf pesFilterConf{
+ .type = DemuxFilterType::PES,
+ .setting = filterSetting,
+ };
+ filterConf[0] = pesFilterConf;
+
+ vector<string> goldenOutputFiles;
+
+ ASSERT_TRUE(broadcastDataFlowTest(filterConf, goldenOutputFiles));
}
} // namespace
diff --git a/tv/tuner/README.md b/tv/tuner/README.md
new file mode 100644
index 0000000..a833c87
--- /dev/null
+++ b/tv/tuner/README.md
@@ -0,0 +1,12 @@
+# Tuner HALs
+
+## Overview
+
+TV specific tuners.
+
+Sett 1.0/ITuner.hal for an overview.
+
+*** note
+**Warning:** The HALs are not (yet) frozen, as the HAL definition is
+expected to evolve between Android releases.
+***
diff --git a/vibrator/1.4/vts/functional/VtsHalVibratorV1_4TargetTest.cpp b/vibrator/1.4/vts/functional/VtsHalVibratorV1_4TargetTest.cpp
index b51cc96..1b6abe9 100644
--- a/vibrator/1.4/vts/functional/VtsHalVibratorV1_4TargetTest.cpp
+++ b/vibrator/1.4/vts/functional/VtsHalVibratorV1_4TargetTest.cpp
@@ -22,6 +22,8 @@
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
+
+#include <getopt.h>
#include <unistd.h>
#include <future>
@@ -38,6 +40,8 @@
using ::android::hardware::vibrator::V1_4::IVibrator;
using ::android::hardware::vibrator::V1_4::IVibratorCallback;
+static uint32_t sCompletionLimitMs = UINT32_MAX;
+
#define EXPECT_OK(ret) ASSERT_TRUE((ret).isOk())
class CompletionCallback : public IVibratorCallback {
@@ -115,7 +119,7 @@
sp<CompletionCallback> callback =
new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
EXPECT_OK(vibrator->perform_1_4(effect, strength, callback, validateWrapper));
- if (performStatus == Status::OK &&
+ if (performStatus == Status::OK && performLength < sCompletionLimitMs &&
(capabilities & Capabilities::PERFORM_COMPLETION_CALLBACK)) {
std::chrono::milliseconds timeout{performLength * 2};
EXPECT_EQ(completionFuture.wait_for(timeout), std::future_status::ready);
@@ -168,3 +172,32 @@
PerInstance, VibratorHidlTest_1_4,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(IVibrator::descriptor)),
android::hardware::PrintInstanceNameToString);
+
+enum {
+ OPTION_COMPLETION_LIMIT_MS,
+};
+
+int main(int argc, char** argv) {
+ struct option options[] = {
+ {"completion-limit-ms", required_argument, 0, OPTION_COMPLETION_LIMIT_MS}, {}};
+
+ printf("Running main() from %s\n", __FILE__);
+ testing::InitGoogleTest(&argc, argv);
+
+ while (true) {
+ int opt = getopt_long(argc, argv, "", options, nullptr);
+ if (opt == -1) {
+ break;
+ }
+ switch (opt) {
+ case OPTION_COMPLETION_LIMIT_MS:
+ std::istringstream(optarg) >> sCompletionLimitMs;
+ break;
+ default:
+ printf("Unrecognized option\n");
+ return -EINVAL;
+ }
+ }
+
+ return RUN_ALL_TESTS();
+}
diff --git a/wifi/1.0/vts/functional/Android.bp b/wifi/1.0/vts/functional/Android.bp
index 397ad17..6fa6e7e 100644
--- a/wifi/1.0/vts/functional/Android.bp
+++ b/wifi/1.0/vts/functional/Android.bp
@@ -28,7 +28,9 @@
shared_libs: [
"libnativehelper",
],
- static_libs: ["android.hardware.wifi@1.0"],
+ static_libs: [
+ "android.hardware.wifi@1.0",
+ ],
}
cc_test {
@@ -36,7 +38,6 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"VtsHalWifiV1_0TargetTest.cpp",
- "wifi_ap_iface_hidl_test.cpp",
"wifi_chip_hidl_test.cpp",
"wifi_p2p_iface_hidl_test.cpp",
"wifi_rtt_controller_hidl_test.cpp",
@@ -52,11 +53,14 @@
test_suites: ["general-tests"],
}
+// These tests are split out so that they can be conditioned on presence of the
+// "android.hardware.wifi.aware" feature.
cc_test {
name: "VtsHalWifiNanV1_0TargetTest",
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"VtsHalWifiV1_0TargetTest.cpp",
+ "wifi_chip_hidl_nan_test.cpp",
"wifi_nan_iface_hidl_test.cpp",
],
static_libs: [
@@ -65,3 +69,20 @@
],
test_suites: ["general-tests"],
}
+
+// These tests are split out so that they can be conditioned on presence of
+// the hostapd HAL, which indicates SoftAP support.
+cc_test {
+ name: "VtsHalWifiApV1_0TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "VtsHalWifiV1_0TargetTest.cpp",
+ "wifi_ap_iface_hidl_test.cpp",
+ "wifi_chip_hidl_ap_test.cpp",
+ ],
+ static_libs: [
+ "VtsHalWifiV1_0TargetTestUtil",
+ "android.hardware.wifi@1.0",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/wifi/1.0/vts/functional/VtsHalWifiV1_0TargetTest.cpp b/wifi/1.0/vts/functional/VtsHalWifiV1_0TargetTest.cpp
index e7b8593..9d25014 100644
--- a/wifi/1.0/vts/functional/VtsHalWifiV1_0TargetTest.cpp
+++ b/wifi/1.0/vts/functional/VtsHalWifiV1_0TargetTest.cpp
@@ -41,10 +41,7 @@
::testing::AddGlobalTestEnvironment(gEnv);
::testing::InitGoogleTest(&argc, argv);
gEnv->init(&argc, argv);
- int status = gEnv->initFromOptions(argc, argv);
- if (status == 0) {
- status = RUN_ALL_TESTS();
- LOG(INFO) << "Test result = " << status;
- }
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
return status;
}
diff --git a/wifi/1.0/vts/functional/wifi_ap_iface_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_ap_iface_hidl_test.cpp
index e5762f2..c55221d 100644
--- a/wifi/1.0/vts/functional/wifi_ap_iface_hidl_test.cpp
+++ b/wifi/1.0/vts/functional/wifi_ap_iface_hidl_test.cpp
@@ -29,21 +29,17 @@
using ::android::hardware::wifi::V1_0::WifiStatusCode;
using ::android::sp;
-extern WifiHidlEnvironment* gEnv;
-
/**
* Fixture to use for all AP Iface HIDL interface tests.
*/
class WifiApIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase {
public:
virtual void SetUp() override {
- if (!gEnv->isSoftApOn) return;
wifi_ap_iface_ = getWifiApIface();
ASSERT_NE(nullptr, wifi_ap_iface_.get());
}
virtual void TearDown() override {
- if (!gEnv->isSoftApOn) return;
stopWifi();
}
@@ -57,7 +53,6 @@
* successfully created.
*/
TEST(WifiApIfaceHidlTestNoFixture, Create) {
- if (!gEnv->isSoftApOn) return;
EXPECT_NE(nullptr, getWifiApIface().get());
stopWifi();
}
@@ -67,7 +62,6 @@
* Ensures that the correct interface type is returned for AP interface.
*/
TEST_F(WifiApIfaceHidlTest, GetType) {
- if (!gEnv->isSoftApOn) return;
const auto& status_and_type = HIDL_INVOKE(wifi_ap_iface_, getType);
EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_type.first.code);
EXPECT_EQ(IfaceType::AP, status_and_type.second);
@@ -79,7 +73,6 @@
* status code.
*/
TEST_F(WifiApIfaceHidlTest, SetCountryCode) {
- if (!gEnv->isSoftApOn) return;
const android::hardware::hidl_array<int8_t, 2> kCountryCode{
std::array<int8_t, 2>{{0x55, 0x53}}};
EXPECT_EQ(WifiStatusCode::SUCCESS,
@@ -91,7 +84,6 @@
* Ensures that we can retrieve valid frequencies for 2.4 GHz band.
*/
TEST_F(WifiApIfaceHidlTest, GetValidFrequenciesForBand) {
- if (!gEnv->isSoftApOn) return;
const auto& status_and_freqs = HIDL_INVOKE(
wifi_ap_iface_, getValidFrequenciesForBand, WifiBand::BAND_24GHZ);
EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_freqs.first.code);
diff --git a/wifi/1.0/vts/functional/wifi_chip_hidl_ap_test.cpp b/wifi/1.0/vts/functional/wifi_chip_hidl_ap_test.cpp
new file mode 100644
index 0000000..232ffdd
--- /dev/null
+++ b/wifi/1.0/vts/functional/wifi_chip_hidl_ap_test.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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 <android-base/logging.h>
+
+#include <android/hardware/wifi/1.0/IWifiChip.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+
+#include "wifi_hidl_call_util.h"
+#include "wifi_hidl_test_utils.h"
+
+using ::android::sp;
+using ::android::hardware::wifi::V1_0::ChipModeId;
+using ::android::hardware::wifi::V1_0::IfaceType;
+using ::android::hardware::wifi::V1_0::IWifiApIface;
+using ::android::hardware::wifi::V1_0::IWifiChip;
+using ::android::hardware::wifi::V1_0::IWifiIface;
+using ::android::hardware::wifi::V1_0::WifiStatus;
+using ::android::hardware::wifi::V1_0::WifiStatusCode;
+
+/**
+ * Fixture for IWifiChip tests that are conditioned on SoftAP support.
+ */
+class WifiChipHidlApTest : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+ virtual void SetUp() override {
+ wifi_chip_ = getWifiChip();
+ ASSERT_NE(nullptr, wifi_chip_.get());
+ }
+
+ virtual void TearDown() override { stopWifi(); }
+
+ protected:
+ // Helper function to configure the Chip in one of the supported modes.
+ // Most of the non-mode-configuration-related methods require chip
+ // to be first configured.
+ ChipModeId configureChipForIfaceType(IfaceType type, bool expectSuccess) {
+ ChipModeId mode_id;
+ EXPECT_EQ(expectSuccess,
+ configureChipToSupportIfaceType(wifi_chip_, type, &mode_id));
+ return mode_id;
+ }
+
+ std::string getIfaceName(const sp<IWifiIface>& iface) {
+ const auto& status_and_name = HIDL_INVOKE(iface, getName);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_name.first.code);
+ return status_and_name.second;
+ }
+
+ WifiStatusCode createApIface(sp<IWifiApIface>* ap_iface) {
+ const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createApIface);
+ *ap_iface = status_and_iface.second;
+ return status_and_iface.first.code;
+ }
+
+ WifiStatusCode removeApIface(const std::string& name) {
+ return HIDL_INVOKE(wifi_chip_, removeApIface, name).code;
+ }
+
+ sp<IWifiChip> wifi_chip_;
+};
+
+/*
+ * CreateApIface
+ * Configures the chip in AP mode and ensures that at least 1 iface creation
+ * succeeds.
+ */
+TEST_F(WifiChipHidlApTest, CreateApIface) {
+ configureChipForIfaceType(IfaceType::AP, true);
+
+ sp<IWifiApIface> iface;
+ EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&iface));
+ EXPECT_NE(nullptr, iface.get());
+}
+
+/*
+ * GetApIfaceNames
+ * Configures the chip in AP mode and ensures that the iface list is empty
+ * before creating the iface. Then, create the iface and ensure that
+ * iface name is returned via the list.
+ */
+TEST_F(WifiChipHidlApTest, GetApIfaceNames) {
+ configureChipForIfaceType(IfaceType::AP, true);
+
+ const auto& status_and_iface_names1 =
+ HIDL_INVOKE(wifi_chip_, getApIfaceNames);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names1.first.code);
+ EXPECT_EQ(0u, status_and_iface_names1.second.size());
+
+ sp<IWifiApIface> iface;
+ EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&iface));
+ EXPECT_NE(nullptr, iface.get());
+
+ std::string iface_name = getIfaceName(iface);
+ const auto& status_and_iface_names2 =
+ HIDL_INVOKE(wifi_chip_, getApIfaceNames);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names2.first.code);
+ EXPECT_EQ(1u, status_and_iface_names2.second.size());
+ EXPECT_EQ(iface_name, status_and_iface_names2.second[0]);
+
+ EXPECT_EQ(WifiStatusCode::SUCCESS, removeApIface(iface_name));
+ const auto& status_and_iface_names3 =
+ HIDL_INVOKE(wifi_chip_, getApIfaceNames);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names3.first.code);
+ EXPECT_EQ(0u, status_and_iface_names3.second.size());
+}
+
+/*
+ * GetApIface
+ * Configures the chip in AP mode and create an iface. Then, retrieve
+ * the iface object using the correct name and ensure any other name
+ * doesn't retrieve an iface object.
+ */
+TEST_F(WifiChipHidlApTest, GetApIface) {
+ configureChipForIfaceType(IfaceType::AP, true);
+
+ sp<IWifiApIface> ap_iface;
+ EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&ap_iface));
+ EXPECT_NE(nullptr, ap_iface.get());
+
+ std::string iface_name = getIfaceName(ap_iface);
+ const auto& status_and_iface1 =
+ HIDL_INVOKE(wifi_chip_, getApIface, iface_name);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface1.first.code);
+ EXPECT_NE(nullptr, status_and_iface1.second.get());
+
+ std::string invalid_name = iface_name + "0";
+ const auto& status_and_iface2 =
+ HIDL_INVOKE(wifi_chip_, getApIface, invalid_name);
+ EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, status_and_iface2.first.code);
+ EXPECT_EQ(nullptr, status_and_iface2.second.get());
+}
+
+/*
+ * RemoveApIface
+ * Configures the chip in AP mode and create an iface. Then, remove
+ * the iface object using the correct name and ensure any other name
+ * doesn't remove the iface.
+ */
+TEST_F(WifiChipHidlApTest, RemoveApIface) {
+ configureChipForIfaceType(IfaceType::AP, true);
+
+ sp<IWifiApIface> ap_iface;
+ EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&ap_iface));
+ EXPECT_NE(nullptr, ap_iface.get());
+
+ std::string iface_name = getIfaceName(ap_iface);
+ std::string invalid_name = iface_name + "0";
+ EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeApIface(invalid_name));
+ EXPECT_EQ(WifiStatusCode::SUCCESS, removeApIface(iface_name));
+
+ // No such iface exists now. So, this should return failure.
+ EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeApIface(iface_name));
+}
diff --git a/wifi/1.0/vts/functional/wifi_chip_hidl_nan_test.cpp b/wifi/1.0/vts/functional/wifi_chip_hidl_nan_test.cpp
new file mode 100644
index 0000000..595f23a
--- /dev/null
+++ b/wifi/1.0/vts/functional/wifi_chip_hidl_nan_test.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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 <android-base/logging.h>
+
+#include <android/hardware/wifi/1.0/IWifiChip.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+
+#include "wifi_hidl_call_util.h"
+#include "wifi_hidl_test_utils.h"
+
+using ::android::sp;
+using ::android::hardware::wifi::V1_0::ChipModeId;
+using ::android::hardware::wifi::V1_0::IfaceType;
+using ::android::hardware::wifi::V1_0::IWifiChip;
+using ::android::hardware::wifi::V1_0::IWifiIface;
+using ::android::hardware::wifi::V1_0::IWifiNanIface;
+using ::android::hardware::wifi::V1_0::WifiStatus;
+using ::android::hardware::wifi::V1_0::WifiStatusCode;
+
+/**
+ * Fixture for IWifiChip tests that are conditioned on NAN support.
+ */
+class WifiChipHidlNanTest : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+ virtual void SetUp() override {
+ wifi_chip_ = getWifiChip();
+ ASSERT_NE(nullptr, wifi_chip_.get());
+ }
+
+ virtual void TearDown() override { stopWifi(); }
+
+ protected:
+ // Helper function to configure the Chip in one of the supported modes.
+ // Most of the non-mode-configuration-related methods require chip
+ // to be first configured.
+ ChipModeId configureChipForIfaceType(IfaceType type, bool expectSuccess) {
+ ChipModeId mode_id;
+ EXPECT_EQ(expectSuccess,
+ configureChipToSupportIfaceType(wifi_chip_, type, &mode_id));
+ return mode_id;
+ }
+
+ std::string getIfaceName(const sp<IWifiIface>& iface) {
+ const auto& status_and_name = HIDL_INVOKE(iface, getName);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_name.first.code);
+ return status_and_name.second;
+ }
+
+ WifiStatusCode createNanIface(sp<IWifiNanIface>* nan_iface) {
+ const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createNanIface);
+ *nan_iface = status_and_iface.second;
+ return status_and_iface.first.code;
+ }
+
+ WifiStatusCode removeNanIface(const std::string& name) {
+ return HIDL_INVOKE(wifi_chip_, removeNanIface, name).code;
+ }
+
+ sp<IWifiChip> wifi_chip_;
+};
+
+/*
+ * CreateNanIface
+ * Configures the chip in NAN mode and ensures that at least 1 iface creation
+ * succeeds.
+ */
+TEST_F(WifiChipHidlNanTest, CreateNanIface) {
+ configureChipForIfaceType(IfaceType::NAN, true);
+
+ sp<IWifiNanIface> iface;
+ ASSERT_EQ(WifiStatusCode::SUCCESS, createNanIface(&iface));
+ EXPECT_NE(nullptr, iface.get());
+}
+
+/*
+ * GetNanIfaceNames
+ * Configures the chip in NAN mode and ensures that the iface list is empty
+ * before creating the iface. Then, create the iface and ensure that
+ * iface name is returned via the list.
+ */
+TEST_F(WifiChipHidlNanTest, GetNanIfaceNames) {
+ configureChipForIfaceType(IfaceType::NAN, true);
+
+ const auto& status_and_iface_names1 =
+ HIDL_INVOKE(wifi_chip_, getNanIfaceNames);
+ ASSERT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names1.first.code);
+ EXPECT_EQ(0u, status_and_iface_names1.second.size());
+
+ sp<IWifiNanIface> iface;
+ EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&iface));
+ EXPECT_NE(nullptr, iface.get());
+
+ std::string iface_name = getIfaceName(iface);
+ const auto& status_and_iface_names2 =
+ HIDL_INVOKE(wifi_chip_, getNanIfaceNames);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names2.first.code);
+ EXPECT_EQ(1u, status_and_iface_names2.second.size());
+ EXPECT_EQ(iface_name, status_and_iface_names2.second[0]);
+
+ EXPECT_EQ(WifiStatusCode::SUCCESS, removeNanIface(iface_name));
+ const auto& status_and_iface_names3 =
+ HIDL_INVOKE(wifi_chip_, getNanIfaceNames);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names3.first.code);
+ EXPECT_EQ(0u, status_and_iface_names3.second.size());
+}
+
+/*
+ * GetNanIface
+ * Configures the chip in NAN mode and create an iface. Then, retrieve
+ * the iface object using the correct name and ensure any other name
+ * doesn't retrieve an iface object.
+ */
+TEST_F(WifiChipHidlNanTest, GetNanIface) {
+ configureChipForIfaceType(IfaceType::NAN, true);
+
+ sp<IWifiNanIface> nan_iface;
+ EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&nan_iface));
+ EXPECT_NE(nullptr, nan_iface.get());
+
+ std::string iface_name = getIfaceName(nan_iface);
+ const auto& status_and_iface1 =
+ HIDL_INVOKE(wifi_chip_, getNanIface, iface_name);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface1.first.code);
+ EXPECT_NE(nullptr, status_and_iface1.second.get());
+
+ std::string invalid_name = iface_name + "0";
+ const auto& status_and_iface2 =
+ HIDL_INVOKE(wifi_chip_, getNanIface, invalid_name);
+ EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, status_and_iface2.first.code);
+ EXPECT_EQ(nullptr, status_and_iface2.second.get());
+}
+
+/*
+ * RemoveNanIface
+ * Configures the chip in NAN mode and create an iface. Then, remove
+ * the iface object using the correct name and ensure any other name
+ * doesn't remove the iface.
+ */
+TEST_F(WifiChipHidlNanTest, RemoveNanIface) {
+ configureChipForIfaceType(IfaceType::NAN, true);
+
+ sp<IWifiNanIface> nan_iface;
+ EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&nan_iface));
+ EXPECT_NE(nullptr, nan_iface.get());
+
+ std::string iface_name = getIfaceName(nan_iface);
+ std::string invalid_name = iface_name + "0";
+ EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeNanIface(invalid_name));
+
+ EXPECT_EQ(WifiStatusCode::SUCCESS, removeNanIface(iface_name));
+
+ // No such iface exists now. So, this should return failure.
+ EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeNanIface(iface_name));
+}
diff --git a/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp
index 1b7e821..2601b78 100644
--- a/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp
+++ b/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp
@@ -36,9 +36,7 @@
using ::android::hardware::wifi::V1_0::WifiStatus;
using ::android::hardware::wifi::V1_0::WifiStatusCode;
using ::android::hardware::wifi::V1_0::IWifiChip;
-using ::android::hardware::wifi::V1_0::IWifiApIface;
using ::android::hardware::wifi::V1_0::IWifiIface;
-using ::android::hardware::wifi::V1_0::IWifiNanIface;
using ::android::hardware::wifi::V1_0::IWifiP2pIface;
using ::android::hardware::wifi::V1_0::IWifiRttController;
using ::android::hardware::wifi::V1_0::IWifiStaIface;
@@ -64,7 +62,10 @@
} // namespace
/**
- * Fixture to use for all Wifi chip HIDL interface tests.
+ * Fixture for IWifiChip tests.
+ *
+ * Tests that require SoftAP or NAN support should go into WifiChipHidlApTest or
+ * WifiChipHidlNanTest respectively.
*/
class WifiChipHidlTest : public ::testing::VtsHalHidlTargetTestBase {
public:
@@ -114,26 +115,6 @@
return status_and_name.second;
}
- WifiStatusCode createApIface(sp<IWifiApIface>* ap_iface) {
- const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createApIface);
- *ap_iface = status_and_iface.second;
- return status_and_iface.first.code;
- }
-
- WifiStatusCode removeApIface(const std::string& name) {
- return HIDL_INVOKE(wifi_chip_, removeApIface, name).code;
- }
-
- WifiStatusCode createNanIface(sp<IWifiNanIface>* nan_iface) {
- const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createNanIface);
- *nan_iface = status_and_iface.second;
- return status_and_iface.first.code;
- }
-
- WifiStatusCode removeNanIface(const std::string& name) {
- return HIDL_INVOKE(wifi_chip_, removeNanIface, name).code;
- }
-
WifiStatusCode createP2pIface(sp<IWifiP2pIface>* p2p_iface) {
const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createP2pIface);
*p2p_iface = status_and_iface.second;
@@ -360,201 +341,6 @@
}
/*
- * CreateApIface
- * Configures the chip in AP mode and ensures that at least 1 iface creation
- * succeeds.
- */
-TEST_F(WifiChipHidlTest, CreateApIface) {
- if (!gEnv->isSoftApOn) return;
- configureChipForIfaceType(IfaceType::AP, true);
-
- sp<IWifiApIface> iface;
- EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&iface));
- EXPECT_NE(nullptr, iface.get());
-}
-
-/*
- * GetApIfaceNames
- * Configures the chip in AP mode and ensures that the iface list is empty
- * before creating the iface. Then, create the iface and ensure that
- * iface name is returned via the list.
- */
-TEST_F(WifiChipHidlTest, GetApIfaceNames) {
- if (!gEnv->isSoftApOn) return;
- configureChipForIfaceType(IfaceType::AP, true);
-
- const auto& status_and_iface_names1 =
- HIDL_INVOKE(wifi_chip_, getApIfaceNames);
- EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names1.first.code);
- EXPECT_EQ(0u, status_and_iface_names1.second.size());
-
- sp<IWifiApIface> iface;
- EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&iface));
- EXPECT_NE(nullptr, iface.get());
-
- std::string iface_name = getIfaceName(iface);
- const auto& status_and_iface_names2 =
- HIDL_INVOKE(wifi_chip_, getApIfaceNames);
- EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names2.first.code);
- EXPECT_EQ(1u, status_and_iface_names2.second.size());
- EXPECT_EQ(iface_name, status_and_iface_names2.second[0]);
-
- EXPECT_EQ(WifiStatusCode::SUCCESS, removeApIface(iface_name));
- const auto& status_and_iface_names3 =
- HIDL_INVOKE(wifi_chip_, getApIfaceNames);
- EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names3.first.code);
- EXPECT_EQ(0u, status_and_iface_names3.second.size());
-}
-
-/*
- * GetApIface
- * Configures the chip in AP mode and create an iface. Then, retrieve
- * the iface object using the correct name and ensure any other name
- * doesn't retrieve an iface object.
- */
-TEST_F(WifiChipHidlTest, GetApIface) {
- if (!gEnv->isSoftApOn) return;
- configureChipForIfaceType(IfaceType::AP, true);
-
- sp<IWifiApIface> ap_iface;
- EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&ap_iface));
- EXPECT_NE(nullptr, ap_iface.get());
-
- std::string iface_name = getIfaceName(ap_iface);
- const auto& status_and_iface1 =
- HIDL_INVOKE(wifi_chip_, getApIface, iface_name);
- EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface1.first.code);
- EXPECT_NE(nullptr, status_and_iface1.second.get());
-
- std::string invalid_name = iface_name + "0";
- const auto& status_and_iface2 =
- HIDL_INVOKE(wifi_chip_, getApIface, invalid_name);
- EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, status_and_iface2.first.code);
- EXPECT_EQ(nullptr, status_and_iface2.second.get());
-}
-
-/*
- * RemoveApIface
- * Configures the chip in AP mode and create an iface. Then, remove
- * the iface object using the correct name and ensure any other name
- * doesn't remove the iface.
- */
-TEST_F(WifiChipHidlTest, RemoveApIface) {
- if (!gEnv->isSoftApOn) return;
- configureChipForIfaceType(IfaceType::AP, true);
-
- sp<IWifiApIface> ap_iface;
- EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&ap_iface));
- EXPECT_NE(nullptr, ap_iface.get());
-
- std::string iface_name = getIfaceName(ap_iface);
- std::string invalid_name = iface_name + "0";
- EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeApIface(invalid_name));
- EXPECT_EQ(WifiStatusCode::SUCCESS, removeApIface(iface_name));
-
- // No such iface exists now. So, this should return failure.
- EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeApIface(iface_name));
-}
-
-/*
- * CreateNanIface
- * Configures the chip in NAN mode and ensures that at least 1 iface creation
- * succeeds.
- */
-TEST_F(WifiChipHidlTest, CreateNanIface) {
- if (!gEnv->isNanOn) return;
- configureChipForIfaceType(IfaceType::NAN, gEnv->isNanOn);
-
- sp<IWifiNanIface> iface;
- ASSERT_EQ(WifiStatusCode::SUCCESS, createNanIface(&iface));
- EXPECT_NE(nullptr, iface.get());
-}
-
-/*
- * GetNanIfaceNames
- * Configures the chip in NAN mode and ensures that the iface list is empty
- * before creating the iface. Then, create the iface and ensure that
- * iface name is returned via the list.
- */
-TEST_F(WifiChipHidlTest, GetNanIfaceNames) {
- if (!gEnv->isNanOn) return;
- configureChipForIfaceType(IfaceType::NAN, gEnv->isNanOn);
-
- const auto& status_and_iface_names1 =
- HIDL_INVOKE(wifi_chip_, getNanIfaceNames);
- ASSERT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names1.first.code);
- EXPECT_EQ(0u, status_and_iface_names1.second.size());
-
- sp<IWifiNanIface> iface;
- EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&iface));
- EXPECT_NE(nullptr, iface.get());
-
- std::string iface_name = getIfaceName(iface);
- const auto& status_and_iface_names2 =
- HIDL_INVOKE(wifi_chip_, getNanIfaceNames);
- EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names2.first.code);
- EXPECT_EQ(1u, status_and_iface_names2.second.size());
- EXPECT_EQ(iface_name, status_and_iface_names2.second[0]);
-
- EXPECT_EQ(WifiStatusCode::SUCCESS, removeNanIface(iface_name));
- const auto& status_and_iface_names3 =
- HIDL_INVOKE(wifi_chip_, getNanIfaceNames);
- EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface_names3.first.code);
- EXPECT_EQ(0u, status_and_iface_names3.second.size());
-}
-
-/*
- * GetNanIface
- * Configures the chip in NAN mode and create an iface. Then, retrieve
- * the iface object using the correct name and ensure any other name
- * doesn't retrieve an iface object.
- */
-TEST_F(WifiChipHidlTest, GetNanIface) {
- if (!gEnv->isNanOn) return;
- configureChipForIfaceType(IfaceType::NAN, gEnv->isNanOn);
-
- sp<IWifiNanIface> nan_iface;
- EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&nan_iface));
- EXPECT_NE(nullptr, nan_iface.get());
-
- std::string iface_name = getIfaceName(nan_iface);
- const auto& status_and_iface1 =
- HIDL_INVOKE(wifi_chip_, getNanIface, iface_name);
- EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface1.first.code);
- EXPECT_NE(nullptr, status_and_iface1.second.get());
-
- std::string invalid_name = iface_name + "0";
- const auto& status_and_iface2 =
- HIDL_INVOKE(wifi_chip_, getNanIface, invalid_name);
- EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, status_and_iface2.first.code);
- EXPECT_EQ(nullptr, status_and_iface2.second.get());
-}
-
-/*
- * RemoveNanIface
- * Configures the chip in NAN mode and create an iface. Then, remove
- * the iface object using the correct name and ensure any other name
- * doesn't remove the iface.
- */
-TEST_F(WifiChipHidlTest, RemoveNanIface) {
- if (!gEnv->isNanOn) return;
- configureChipForIfaceType(IfaceType::NAN, gEnv->isNanOn);
-
- sp<IWifiNanIface> nan_iface;
- EXPECT_EQ(WifiStatusCode::SUCCESS, createNanIface(&nan_iface));
- EXPECT_NE(nullptr, nan_iface.get());
-
- std::string iface_name = getIfaceName(nan_iface);
- std::string invalid_name = iface_name + "0";
- EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeNanIface(invalid_name));
-
- EXPECT_EQ(WifiStatusCode::SUCCESS, removeNanIface(iface_name));
-
- // No such iface exists now. So, this should return failure.
- EXPECT_EQ(WifiStatusCode::ERROR_INVALID_ARGS, removeNanIface(iface_name));
-}
-
-/*
* CreateP2pIface
* Configures the chip in P2P mode and ensures that at least 1 iface creation
* succeeds.
diff --git a/wifi/1.0/vts/functional/wifi_hidl_test_utils.h b/wifi/1.0/vts/functional/wifi_hidl_test_utils.h
index d430ce0..7dacaf1 100644
--- a/wifi/1.0/vts/functional/wifi_hidl_test_utils.h
+++ b/wifi/1.0/vts/functional/wifi_hidl_test_utils.h
@@ -54,48 +54,4 @@
stopWifi();
sleep(5);
}
-
- public:
- // Whether NaN feature is supported on the device.
- bool isNanOn = false;
- // Whether SoftAp feature is supported on the device.
- bool isSoftApOn = false;
-
- void usage(char* me, char* arg) {
- fprintf(stderr,
- "unrecognized option: %s\n\n"
- "usage: %s <gtest options> <test options>\n\n"
- "test options are:\n\n"
- "-N, --nan_on: Whether NAN feature is supported\n"
- "-S, --softap_on: Whether SOFTAP feature is supported\n",
- arg, me);
- }
-
- int initFromOptions(int argc, char** argv) {
- static struct option options[] = {{"nan_on", no_argument, 0, 'N'},
- {"softap_on", no_argument, 0, 'S'},
- {0, 0, 0, 0}};
-
- int c;
- while ((c = getopt_long(argc, argv, "NS", options, NULL)) >= 0) {
- switch (c) {
- case 'N':
- isNanOn = true;
- break;
- case 'S':
- isSoftApOn = true;
- break;
- default:
- usage(argv[0], argv[optind]);
- return 2;
- }
- }
-
- if (optind < argc) {
- usage(argv[0], argv[optind]);
- return 2;
- }
-
- return 0;
- }
};
diff --git a/wifi/1.1/vts/functional/VtsHalWifiV1_1TargetTest.cpp b/wifi/1.1/vts/functional/VtsHalWifiV1_1TargetTest.cpp
index a0f97f8..673fed3 100644
--- a/wifi/1.1/vts/functional/VtsHalWifiV1_1TargetTest.cpp
+++ b/wifi/1.1/vts/functional/VtsHalWifiV1_1TargetTest.cpp
@@ -41,10 +41,7 @@
::testing::AddGlobalTestEnvironment(gEnv);
::testing::InitGoogleTest(&argc, argv);
gEnv->init(&argc, argv);
- int status = gEnv->initFromOptions(argc, argv);
- if (status == 0) {
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "Test result = " << status;
- }
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
return status;
}
diff --git a/wifi/1.4/Android.bp b/wifi/1.4/Android.bp
index a6ac020..aba8b44 100644
--- a/wifi/1.4/Android.bp
+++ b/wifi/1.4/Android.bp
@@ -8,6 +8,7 @@
},
srcs: [
"IWifi.hal",
+ "IWifiApIface.hal",
],
interfaces: [
"android.hardware.wifi@1.0",
diff --git a/wifi/1.4/IWifiApIface.hal b/wifi/1.4/IWifiApIface.hal
new file mode 100644
index 0000000..af88afb
--- /dev/null
+++ b/wifi/1.4/IWifiApIface.hal
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi@1.4;
+
+import @1.0::IWifiApIface;
+import @1.0::MacAddress;
+import @1.0::WifiStatus;
+
+/**
+ * Represents a network interface in AP mode.
+ *
+ * This can be obtained through @1.0::IWifiChip.getApIface() and casting
+ * IWifiApIface up to 1.4.
+ */
+interface IWifiApIface extends @1.0::IWifiApIface {
+ /**
+ * Changes the MAC address of the interface to the given MAC address.
+ *
+ * @param mac MAC address to change to.
+ * @return status WifiStatus of the operation.
+ * Possible status codes:
+ * |WifiStatusCode.SUCCESS|,
+ * |WifiStatusCode.ERROR_WIFI_IFACE_INVALID|,
+ * |WifiStatusCode.ERROR_UNKNOWN|
+ */
+ setMacAddress(MacAddress mac) generates (WifiStatus status);
+
+ /**
+ * Gets the factory MAC address of the interface.
+ *
+ * @return status WifiStatus of the operation
+ * Possible status codes:
+ * |WifiStatusCode.SUCCESS|,
+ * |WifiStatusCode.ERROR_WIFI_IFACE_INVALID|,
+ * |WifiStatusCode.ERROR_UNKNOWN|
+ * @return mac factory MAC address of the interface
+ */
+ getFactoryMacAddress() generates (WifiStatus status, MacAddress mac);
+};
diff --git a/wifi/1.4/default/tests/wifi_chip_unit_tests.cpp b/wifi/1.4/default/tests/wifi_chip_unit_tests.cpp
index 2ad093a..b0357ba 100644
--- a/wifi/1.4/default/tests/wifi_chip_unit_tests.cpp
+++ b/wifi/1.4/default/tests/wifi_chip_unit_tests.cpp
@@ -173,8 +173,9 @@
std::string createIface(const IfaceType& type) {
std::string iface_name;
if (type == IfaceType::AP) {
- chip_->createApIface([&iface_name](const WifiStatus& status,
- const sp<IWifiApIface>& iface) {
+ chip_->createApIface([&iface_name](
+ const WifiStatus& status,
+ const sp<V1_0::IWifiApIface>& iface) {
if (WifiStatusCode::SUCCESS == status.code) {
ASSERT_NE(iface.get(), nullptr);
iface->getName([&iface_name](const WifiStatus& status,
diff --git a/wifi/1.4/default/wifi_ap_iface.cpp b/wifi/1.4/default/wifi_ap_iface.cpp
index 13ce2dd..b860910 100644
--- a/wifi/1.4/default/wifi_ap_iface.cpp
+++ b/wifi/1.4/default/wifi_ap_iface.cpp
@@ -85,6 +85,20 @@
hidl_status_cb, band);
}
+Return<void> WifiApIface::setMacAddress(const hidl_array<uint8_t, 6>& mac,
+ setMacAddress_cb hidl_status_cb) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiApIface::setMacAddressInternal, hidl_status_cb,
+ mac);
+}
+
+Return<void> WifiApIface::getFactoryMacAddress(
+ getFactoryMacAddress_cb hidl_status_cb) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
+ &WifiApIface::getFactoryMacAddressInternal,
+ hidl_status_cb);
+}
+
std::pair<WifiStatus, std::string> WifiApIface::getNameInternal() {
return {createWifiStatus(WifiStatusCode::SUCCESS), ifname_};
}
@@ -111,6 +125,26 @@
ifname_, hidl_struct_util::convertHidlWifiBandToLegacy(band));
return {createWifiStatusFromLegacyError(legacy_status), valid_frequencies};
}
+
+WifiStatus WifiApIface::setMacAddressInternal(
+ const std::array<uint8_t, 6>& mac) {
+ bool status = iface_util_.lock()->setMacAddress(ifname_, mac);
+ if (!status) {
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ return createWifiStatus(WifiStatusCode::SUCCESS);
+}
+
+std::pair<WifiStatus, std::array<uint8_t, 6>>
+WifiApIface::getFactoryMacAddressInternal() {
+ std::array<uint8_t, 6> mac =
+ iface_util_.lock()->getFactoryMacAddress(ifname_);
+ if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 &&
+ mac[4] == 0 && mac[5] == 0) {
+ return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), mac};
+ }
+ return {createWifiStatus(WifiStatusCode::SUCCESS), mac};
+}
} // namespace implementation
} // namespace V1_4
} // namespace wifi
diff --git a/wifi/1.4/default/wifi_ap_iface.h b/wifi/1.4/default/wifi_ap_iface.h
index 179acac..cb3ed3d 100644
--- a/wifi/1.4/default/wifi_ap_iface.h
+++ b/wifi/1.4/default/wifi_ap_iface.h
@@ -18,7 +18,7 @@
#define WIFI_AP_IFACE_H_
#include <android-base/macros.h>
-#include <android/hardware/wifi/1.0/IWifiApIface.h>
+#include <android/hardware/wifi/1.4/IWifiApIface.h>
#include "wifi_feature_flags.h"
#include "wifi_iface_util.h"
@@ -34,7 +34,7 @@
/**
* HIDL interface object used to control a AP Iface instance.
*/
-class WifiApIface : public V1_0::IWifiApIface {
+class WifiApIface : public V1_4::IWifiApIface {
public:
WifiApIface(
const std::string& ifname,
@@ -53,6 +53,10 @@
setCountryCode_cb hidl_status_cb) override;
Return<void> getValidFrequenciesForBand(
WifiBand band, getValidFrequenciesForBand_cb hidl_status_cb) override;
+ Return<void> setMacAddress(const hidl_array<uint8_t, 6>& mac,
+ setMacAddress_cb hidl_status_cb) override;
+ Return<void> getFactoryMacAddress(
+ getFactoryMacAddress_cb hidl_status_cb) override;
private:
// Corresponding worker functions for the HIDL methods.
@@ -61,6 +65,9 @@
WifiStatus setCountryCodeInternal(const std::array<int8_t, 2>& code);
std::pair<WifiStatus, std::vector<WifiChannelInMhz>>
getValidFrequenciesForBandInternal(WifiBand band);
+ WifiStatus setMacAddressInternal(const std::array<uint8_t, 6>& mac);
+ std::pair<WifiStatus, std::array<uint8_t, 6>>
+ getFactoryMacAddressInternal();
std::string ifname_;
std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
diff --git a/wifi/1.4/vts/OWNERS b/wifi/1.4/vts/OWNERS
new file mode 100644
index 0000000..8bfb148
--- /dev/null
+++ b/wifi/1.4/vts/OWNERS
@@ -0,0 +1,2 @@
+rpius@google.com
+etancohen@google.com
diff --git a/wifi/1.4/vts/functional/Android.bp b/wifi/1.4/vts/functional/Android.bp
new file mode 100644
index 0000000..42c60f2
--- /dev/null
+++ b/wifi/1.4/vts/functional/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// SoftAP-specific tests, similar to VtsHalWifiApV1_0TargetTest.
+cc_test {
+ name: "VtsHalWifiApV1_4TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "VtsHalWifiV1_4TargetTest.cpp",
+ "wifi_ap_iface_hidl_test.cpp",
+ ],
+ static_libs: [
+ "VtsHalWifiV1_0TargetTestUtil",
+ "android.hardware.wifi@1.0",
+ "android.hardware.wifi@1.1",
+ "android.hardware.wifi@1.2",
+ "android.hardware.wifi@1.3",
+ "android.hardware.wifi@1.4",
+ ],
+}
diff --git a/wifi/1.4/vts/functional/VtsHalWifiV1_4TargetTest.cpp b/wifi/1.4/vts/functional/VtsHalWifiV1_4TargetTest.cpp
new file mode 100644
index 0000000..deac0fa
--- /dev/null
+++ b/wifi/1.4/vts/functional/VtsHalWifiV1_4TargetTest.cpp
@@ -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.
+ */
+
+#include <android-base/logging.h>
+#include <android/hardware/wifi/1.4/IWifi.h>
+
+#include "wifi_hidl_test_utils.h"
+
+using ::android::hardware::wifi::V1_4::IWifi;
+
+// Test environment for Wifi HIDL HAL.
+class WifiHidlEnvironment_1_4 : public WifiHidlEnvironment {
+ public:
+ // get the test environment singleton
+ static WifiHidlEnvironment_1_4* Instance() {
+ static WifiHidlEnvironment_1_4* instance = new WifiHidlEnvironment_1_4;
+ return instance;
+ }
+
+ virtual void registerTestServices() override {
+ registerTestService<android::hardware::wifi::V1_4::IWifi>();
+ }
+
+ private:
+ WifiHidlEnvironment_1_4() {}
+};
+
+WifiHidlEnvironment_1_4* gEnv = WifiHidlEnvironment_1_4::Instance();
+
+int main(int argc, char** argv) {
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ gEnv->init(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/wifi/1.4/vts/functional/wifi_ap_iface_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_ap_iface_hidl_test.cpp
new file mode 100644
index 0000000..68e9bbb
--- /dev/null
+++ b/wifi/1.4/vts/functional/wifi_ap_iface_hidl_test.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Staache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/hardware/wifi/1.4/IWifiApIface.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+
+#include "wifi_hidl_call_util.h"
+#include "wifi_hidl_test_utils.h"
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::wifi::V1_0::WifiStatus;
+using ::android::hardware::wifi::V1_0::WifiStatusCode;
+using ::android::hardware::wifi::V1_4::IWifiApIface;
+
+extern WifiHidlEnvironment* gEnv;
+
+/**
+ * Fixture to use for all STA Iface HIDL interface tests.
+ */
+class WifiApIfaceHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+ virtual void SetUp() override {
+ wifi_ap_iface_ = IWifiApIface::castFrom(getWifiApIface());
+ ASSERT_NE(nullptr, wifi_ap_iface_.get());
+ }
+
+ virtual void TearDown() override {
+ stopWifi();
+ }
+
+ protected:
+ sp<IWifiApIface> wifi_ap_iface_;
+};
+
+/*
+ * SetMacAddress:
+ * Ensures that calls to set MAC address will return a success status
+ * code.
+ */
+TEST_F(WifiApIfaceHidlTest, SetMacAddress) {
+ const hidl_array<uint8_t, 6> kMac{{0x12, 0x22, 0x33, 0x52, 0x10, 0x41}};
+ EXPECT_EQ(WifiStatusCode::SUCCESS,
+ HIDL_INVOKE(wifi_ap_iface_, setMacAddress, kMac).code);
+}
+
+/*
+ * GetFactoryMacAddress:
+ * Ensures that calls to get factory MAC address will retrieve a non-zero MAC
+ * and return a success status code.
+ */
+TEST_F(WifiApIfaceHidlTest, GetFactoryMacAddress) {
+ std::pair<WifiStatus, hidl_array<uint8_t, 6> > status_and_mac =
+ HIDL_INVOKE(wifi_ap_iface_, getFactoryMacAddress);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_mac.first.code);
+ hidl_array<uint8_t, 6> all_zero{};
+ EXPECT_NE(all_zero, status_and_mac.second);
+}