Merge changes from topic "ethtool"
* changes:
Implement ethtool get/set value operations
Implement Netlink message printer
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.cpp
index a2acb9b..c1db124 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.cpp
@@ -158,6 +158,20 @@
return sendUserHalResponse(std::move(mSwitchUserResponseFromCmd), requestId);
}
+ if (value.value.int32Values.size() > 1) {
+ auto messageType = static_cast<SwitchUserMessageType>(value.value.int32Values[1]);
+ switch (messageType) {
+ case SwitchUserMessageType::LEGACY_ANDROID_SWITCH:
+ ALOGI("request is LEGACY_ANDROID_SWITCH; ignoring it");
+ return {};
+ case SwitchUserMessageType::ANDROID_POST_SWITCH:
+ ALOGI("request is ANDROID_POST_SWITCH; ignoring it");
+ return {};
+ default:
+ break;
+ }
+ }
+
// Returns default response
auto updatedValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue);
updatedValue->prop = SWITCH_USER;
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 341aae7..acdea8a 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -2546,9 +2546,8 @@
* NOTE: if the HAL doesn't support user management, then it should not define this property,
* which in turn would disable the other user-related properties (for example, the Android
* system would never issue them and user-related requests from the HAL layer would be ignored
- * by the Android System). But if it supports user management, then it must support all
- * user-related properties (INITIAL_USER_INFO, SWITCH_USER, CREATE_USER, REMOVE_USER,
- * and USER_IDENTIFICATION_ASSOCIATION).
+ * by the Android System). But if it supports user management, then it must support all core
+ * user-related properties (INITIAL_USER_INFO, SWITCH_USER, CREATE_USER, and REMOVE_USER).
*
* @change_mode VehiclePropertyChangeMode:ON_CHANGE
* @access VehiclePropertyAccess:READ_WRITE
@@ -2818,6 +2817,10 @@
* Property used to associate (or query the association) the current user with vehicle-specific
* identification mechanisms (such as key FOB).
*
+ * This is an optional user management property - the OEM could still support user management
+ * without defining it. In fact, this property could be used without supporting the core
+ * user-related functions described on INITIAL_USER_INFO.
+ *
* To query the association, the Android system gets the property, passing a VehiclePropValue
* containing the types of associations are being queried, as defined by
* UserIdentificationGetRequest. The HAL must return right away, returning a VehiclePropValue
diff --git a/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
index 7ac44a4..78f93af 100644
--- a/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
+++ b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
@@ -28,6 +28,7 @@
#include <chrono>
#include <cstdint>
#include <random>
+#include <thread>
using android::sp;
using android::hardware::hidl_vec;
@@ -144,7 +145,10 @@
ASSERT_EQ(Status::OK, static_cast<Status>(ret2));
}
- void TearDown() override {}
+ void TearDown() override {
+ // Hack to allow the asynchronous operations to finish on time.
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ }
sp<IBiometricsFace> mService;
sp<FaceCallback> mCallback;
diff --git a/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp b/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp
index bdbf72d..6093caa 100644
--- a/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp
+++ b/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp
@@ -49,7 +49,7 @@
static const std::chrono::seconds kTimeoutInSeconds = std::chrono::seconds(kTimeout);
static const uint32_t kGroupId = 99;
static std::string kTmpDir = "";
-static const uint32_t kIterations = 1000;
+static const uint32_t kIterations = 10;
// Wait for a callback to occur (signaled by the given future) up to the
// provided timeout. If the future is invalid or the callback does not come
diff --git a/camera/device/3.2/ICameraDeviceCallback.hal b/camera/device/3.2/ICameraDeviceCallback.hal
index dec3bd8..607502e 100644
--- a/camera/device/3.2/ICameraDeviceCallback.hal
+++ b/camera/device/3.2/ICameraDeviceCallback.hal
@@ -95,7 +95,8 @@
* statuses must be STATUS_ERROR, and the result metadata must be an
* empty buffer. In addition, notify() must be called with a ERROR_REQUEST
* message. In this case, individual ERROR_RESULT/ERROR_BUFFER messages
- * must not be sent.
+ * must not be sent. Note that valid partial results are still allowed
+ * as long as the final result metadata fails to be generated.
*
* Performance requirements:
*
diff --git a/camera/provider/2.4/vts/functional/AndroidTest.xml b/camera/provider/2.4/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..05e1639
--- /dev/null
+++ b/camera/provider/2.4/vts/functional/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs VtsHalCameraProviderV2_4TargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalCameraProviderV2_4TargetTest->/data/local/tmp/VtsHalCameraProviderV2_4TargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalCameraProviderV2_4TargetTest" />
+ <option name="native-test-timeout" value="180000"/>
+ </test>
+</configuration>
diff --git a/camera/provider/2.6/ICameraProvider.hal b/camera/provider/2.6/ICameraProvider.hal
index ed1d31d..b8873a6 100644
--- a/camera/provider/2.6/ICameraProvider.hal
+++ b/camera/provider/2.6/ICameraProvider.hal
@@ -76,12 +76,16 @@
* configuration settings exposed through camera metadata), should the sum
* of resource costs for the combination be <= 100.
*
- * The lists of camera id combinations returned by this method may contain
- * hidden physical camera ids. If a combination does contain hidden physical
- * camera ids, the camera framework must be able to open any logical cameras
- * that contain these hidden physical camera ids in their
- * ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS list, in addition to the other
- * camera ids advertised in the combination, for concurrent operation.
+ * For guaranteed concurrent camera operation, the camera framework must call
+ * ICameraDevice.open() on all devices (intended for concurrent operation), before configuring
+ * any streams on them. This gives the camera HAL process an opportunity to potentially
+ * distribute hardware resources better before stream configuration.
+ *
+ * Due to potential hardware constraints around internal switching of physical camera devices,
+ * a device's complete ZOOM_RATIO_RANGE(if supported), may not apply during concurrent
+ * operation. If ZOOM_RATIO is supported, camera HALs must ensure ZOOM_RATIO_RANGE of
+ * [1.0, ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM] is supported by that device, during
+ * concurrent operation.
*
* @return status Status code for the operation
* @return cameraIds a list of camera id combinations that support
diff --git a/cas/1.0/default/DescramblerImpl.cpp b/cas/1.0/default/DescramblerImpl.cpp
index 9b09751..f79b32d 100644
--- a/cas/1.0/default/DescramblerImpl.cpp
+++ b/cas/1.0/default/DescramblerImpl.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "android.hardware.cas@1.0-DescramblerImpl"
#include <hidlmemory/mapping.h>
+#include <inttypes.h>
#include <media/cas/DescramblerAPI.h>
#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/AString.h>
@@ -101,7 +102,7 @@
// size in size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed
// but the mapped memory's actual size will be smaller than the reported size.
if (srcBuffer.heapBase.size() > SIZE_MAX) {
- ALOGE("Invalid hidl_memory size: %llu", srcBuffer.heapBase.size());
+ ALOGE("Invalid hidl_memory size: %" PRIu64 "", srcBuffer.heapBase.size());
android_errorWriteLog(0x534e4554, "79376389");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
@@ -118,8 +119,8 @@
}
if (!validateRangeForSize(
srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize())) {
- ALOGE("Invalid src buffer range: offset %llu, size %llu, srcMem size %llu",
- srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
+ ALOGE("Invalid src buffer range: offset %" PRIu64 ", size %" PRIu64 ", srcMem"
+ "size %" PRIu64 "", srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
android_errorWriteLog(0x534e4554, "67962232");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
@@ -135,8 +136,8 @@
// is consistent with the source shared buffer size.
if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) {
ALOGE("Invalid srcOffset and subsample size: "
- "srcOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
- srcOffset, totalBytesInSubSamples, srcBuffer.size);
+ "srcOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+ "size %" PRIu64 "", srcOffset, totalBytesInSubSamples, srcBuffer.size);
android_errorWriteLog(0x534e4554, "67962232");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
@@ -153,8 +154,8 @@
// dstOffset against the buffer size too.
if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) {
ALOGE("Invalid dstOffset and subsample size: "
- "dstOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
- dstOffset, totalBytesInSubSamples, srcBuffer.size);
+ "dstOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+ "size %" PRIu64 "", dstOffset, totalBytesInSubSamples, srcBuffer.size);
android_errorWriteLog(0x534e4554, "67962232");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
diff --git a/cas/1.1/default/DescramblerImpl.cpp b/cas/1.1/default/DescramblerImpl.cpp
index 36dc1a5..309cd3c 100644
--- a/cas/1.1/default/DescramblerImpl.cpp
+++ b/cas/1.1/default/DescramblerImpl.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "android.hardware.cas@1.1-DescramblerImpl"
#include <hidlmemory/mapping.h>
+#include <inttypes.h>
#include <media/cas/DescramblerAPI.h>
#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/AUtils.h>
@@ -92,7 +93,7 @@
// size in size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed
// but the mapped memory's actual size will be smaller than the reported size.
if (srcBuffer.heapBase.size() > SIZE_MAX) {
- ALOGE("Invalid hidl_memory size: %llu", srcBuffer.heapBase.size());
+ ALOGE("Invalid hidl_memory size: %" PRIu64 "", srcBuffer.heapBase.size());
android_errorWriteLog(0x534e4554, "79376389");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
@@ -108,8 +109,8 @@
return Void();
}
if (!validateRangeForSize(srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize())) {
- ALOGE("Invalid src buffer range: offset %llu, size %llu, srcMem size %llu",
- srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
+ ALOGE("Invalid src buffer range: offset %" PRIu64 ", size %" PRIu64 ", srcMem"
+ "size %" PRIu64 "", srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
android_errorWriteLog(0x534e4554, "67962232");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
@@ -125,8 +126,8 @@
// is consistent with the source shared buffer size.
if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) {
ALOGE("Invalid srcOffset and subsample size: "
- "srcOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
- srcOffset, totalBytesInSubSamples, srcBuffer.size);
+ "srcOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+ "size %" PRIu64 "", srcOffset, totalBytesInSubSamples, srcBuffer.size);
android_errorWriteLog(0x534e4554, "67962232");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
@@ -143,8 +144,8 @@
// dstOffset against the buffer size too.
if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) {
ALOGE("Invalid dstOffset and subsample size: "
- "dstOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
- dstOffset, totalBytesInSubSamples, srcBuffer.size);
+ "dstOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+ "size %" PRIu64 "", dstOffset, totalBytesInSubSamples, srcBuffer.size);
android_errorWriteLog(0x534e4554, "67962232");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
diff --git a/cas/1.2/default/DescramblerImpl.cpp b/cas/1.2/default/DescramblerImpl.cpp
index 36dc1a5..309cd3c 100644
--- a/cas/1.2/default/DescramblerImpl.cpp
+++ b/cas/1.2/default/DescramblerImpl.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "android.hardware.cas@1.1-DescramblerImpl"
#include <hidlmemory/mapping.h>
+#include <inttypes.h>
#include <media/cas/DescramblerAPI.h>
#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/AUtils.h>
@@ -92,7 +93,7 @@
// size in size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed
// but the mapped memory's actual size will be smaller than the reported size.
if (srcBuffer.heapBase.size() > SIZE_MAX) {
- ALOGE("Invalid hidl_memory size: %llu", srcBuffer.heapBase.size());
+ ALOGE("Invalid hidl_memory size: %" PRIu64 "", srcBuffer.heapBase.size());
android_errorWriteLog(0x534e4554, "79376389");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
@@ -108,8 +109,8 @@
return Void();
}
if (!validateRangeForSize(srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize())) {
- ALOGE("Invalid src buffer range: offset %llu, size %llu, srcMem size %llu",
- srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
+ ALOGE("Invalid src buffer range: offset %" PRIu64 ", size %" PRIu64 ", srcMem"
+ "size %" PRIu64 "", srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
android_errorWriteLog(0x534e4554, "67962232");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
@@ -125,8 +126,8 @@
// is consistent with the source shared buffer size.
if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) {
ALOGE("Invalid srcOffset and subsample size: "
- "srcOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
- srcOffset, totalBytesInSubSamples, srcBuffer.size);
+ "srcOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+ "size %" PRIu64 "", srcOffset, totalBytesInSubSamples, srcBuffer.size);
android_errorWriteLog(0x534e4554, "67962232");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
@@ -143,8 +144,8 @@
// dstOffset against the buffer size too.
if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) {
ALOGE("Invalid dstOffset and subsample size: "
- "dstOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
- dstOffset, totalBytesInSubSamples, srcBuffer.size);
+ "dstOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+ "size %" PRIu64 "", dstOffset, totalBytesInSubSamples, srcBuffer.size);
android_errorWriteLog(0x534e4554, "67962232");
_hidl_cb(toStatus(BAD_VALUE), 0, NULL);
return Void();
diff --git a/current.txt b/current.txt
index 9d14c40..ad7879e 100644
--- a/current.txt
+++ b/current.txt
@@ -588,6 +588,7 @@
578f640c653726d58f99c84a7e1bb63862e21ef7cbb4f7d95c3cc62de00dca35 android.hardware.automotive.evs@1.0::IEvsDisplay
f5bc6aa840db933cb9fd36668b06d3e2021cf5384bb70e459f22e2f2f921fba5 android.hardware.automotive.evs@1.0::IEvsEnumerator
d3a344b7bd4c0d2658ae7209f55a979b8f53f361fd00f4fca29d5baa56d11fd2 android.hardware.automotive.evs@1.0::types
+d123013165a19b6353cdc46a57b2ff4a17179619d36dbd595dfcf15dcd099af6 android.hardware.camera.device@3.2::ICameraDeviceCallback # b/155353799
2410dd02d67786a732d36e80b0f8ccf55086604ef37f9838e2013ff2c571e404 android.hardware.camera.device@3.5::types
cd06a7911b9acd4a653bbf7133888878fbcb3f84be177c7a3f1becaae3d8618f android.hardware.camera.metadata@3.2::types
5cf81b1001296fbb3c5b3d275a859244f61cec5fa858d7be9cca46c5b7dfa733 android.hardware.camera.metadata@3.2::types # b/150331548
@@ -676,7 +677,7 @@
a718c8a3acaa938de5a57923e8c4625ed7ca051e05a1d930ba6998557d7b57c8 android.hardware.camera.device@3.6::ICameraOfflineSession
a35d5151b48505f06a775b38c0e2e265f80a845d92802324c643565807f81c53 android.hardware.camera.device@3.6::types
02bdf82dba7dce273a554b4474468a8fb1fb4f61ab65da95eb16e080df63fff6 android.hardware.camera.metadata@3.5::types
-21086e1c7a2acc0ebe0ff8561b11f3c2009be687a92d79b608a5f00b16c5f598 android.hardware.camera.provider@2.6::ICameraProvider
+7d6b362681f4a4fd0be95535d8913d8de9a26f0765c1bdda4bd837dea8c25db6 android.hardware.camera.provider@2.6::ICameraProvider
8f8d9463508ff9cae88eb35c429fd0e2dbca0ca8f5de7fdf836cc0c4370becb6 android.hardware.camera.provider@2.6::ICameraProviderCallback
1edf7aef68ef3bd577a1175b1462fb82e3e39f01c6915dda61fba121028df283 android.hardware.camera.provider@2.6::types
c1aa508d00b66ed5feefea398fd5edf28fa651ac89773adad7dfda4e0a73a952 android.hardware.cas@1.2::ICas
diff --git a/gnss/1.1/vts/functional/Android.bp b/gnss/1.1/vts/functional/Android.bp
index 0d540b7..94bfb89 100644
--- a/gnss/1.1/vts/functional/Android.bp
+++ b/gnss/1.1/vts/functional/Android.bp
@@ -25,6 +25,7 @@
static_libs: [
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
+ "android.hardware.gnss@2.0",
"android.hardware.gnss@common-vts-lib",
],
shared_libs: [
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.cpp b/gnss/2.0/vts/functional/gnss_hal_test.cpp
index 59e18f3..1cb44c5 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test.cpp
@@ -247,3 +247,46 @@
capabilities_cbq_.store(capabilities);
return Void();
}
+
+GnssConstellationType_1_0 GnssHalTest::startLocationAndGetNonGpsConstellation() {
+ const int kLocationsToAwait = 3;
+
+ gnss_cb_->location_cbq_.reset();
+ StartAndCheckLocations(kLocationsToAwait);
+ const int location_called_count = gnss_cb_->location_cbq_.calledCount();
+
+ // Tolerate 1 less sv status to handle edge cases in reporting.
+ int sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
+ EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
+ ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)",
+ sv_info_list_cbq_size, kLocationsToAwait, location_called_count);
+
+ // Find first non-GPS constellation to blacklist. Exclude IRNSS in GnssConstellationType_2_0
+ // as blacklisting of this constellation is not supported in gnss@2.0.
+ const int kGnssSvStatusTimeout = 2;
+ GnssConstellationType_1_0 constellation_to_blacklist = GnssConstellationType_1_0::UNKNOWN;
+ for (int i = 0; i < sv_info_list_cbq_size; ++i) {
+ hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
+ gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
+ for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
+ if ((sv_info.v1_0.svFlag & IGnssCallback_2_0::GnssSvFlags::USED_IN_FIX) &&
+ (sv_info.constellation != GnssConstellationType_2_0::UNKNOWN) &&
+ (sv_info.constellation != GnssConstellationType_2_0::IRNSS) &&
+ (sv_info.constellation != GnssConstellationType_2_0::GPS)) {
+ // found a non-GPS V1_0 constellation
+ constellation_to_blacklist = Utils::mapConstellationType(sv_info.constellation);
+ break;
+ }
+ }
+ if (constellation_to_blacklist != GnssConstellationType_1_0::UNKNOWN) {
+ break;
+ }
+ }
+
+ if (constellation_to_blacklist == GnssConstellationType_1_0::UNKNOWN) {
+ ALOGI("No non-GPS constellations found, constellation blacklist test less effective.");
+ // Proceed functionally to blacklist something.
+ constellation_to_blacklist = GnssConstellationType_1_0::GLONASS;
+ }
+ return constellation_to_blacklist;
+}
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.h b/gnss/2.0/vts/functional/gnss_hal_test.h
index a02a9ff..7fbd735 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.h
+++ b/gnss/2.0/vts/functional/gnss_hal_test.h
@@ -31,6 +31,9 @@
using android::hardware::gnss::V1_0::GnssLocationFlags;
using android::hardware::gnss::V2_0::IGnss;
+using GnssConstellationType_1_0 = android::hardware::gnss::V1_0::GnssConstellationType;
+using GnssConstellationType_2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
+
using GnssLocation_1_0 = android::hardware::gnss::V1_0::GnssLocation;
using GnssLocation_2_0 = android::hardware::gnss::V2_0::GnssLocation;
@@ -194,6 +197,16 @@
*/
void SetPositionMode(const int min_interval_msec, const bool low_power_mode);
+ /*
+ * startLocationAndGetNonGpsConstellation:
+ * 1. Start location
+ * 2. Find and return first non-GPS constellation
+ *
+ * Note that location is not stopped in this method. The client should call
+ * StopAndClearLocations() after the call.
+ */
+ GnssConstellationType_1_0 startLocationAndGetNonGpsConstellation();
+
sp<IGnss> gnss_hal_; // GNSS HAL to call into
sp<GnssCallback> gnss_cb_; // Primary callback interface
};
diff --git a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
index 094c7c1..51dcf0d 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
@@ -24,8 +24,6 @@
using android::hardware::hidl_string;
using android::hardware::hidl_vec;
-using GnssConstellationType_2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
-using GnssConstellationType_1_0 = android::hardware::gnss::V1_0::GnssConstellationType;
using IGnssConfiguration_2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
using IGnssConfiguration_1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
using IAGnssRil_2_0 = android::hardware::gnss::V2_0::IAGnssRil;
@@ -492,31 +490,6 @@
}
/*
- * MapConstellationType:
- * Given a GnssConstellationType_2_0 type constellation, maps to its equivalent
- * GnssConstellationType_1_0 type constellation. For constellations that do not have
- * an equivalent value, maps to GnssConstellationType_1_0::UNKNOWN
- */
-GnssConstellationType_1_0 MapConstellationType(GnssConstellationType_2_0 constellation) {
- switch (constellation) {
- case GnssConstellationType_2_0::GPS:
- return GnssConstellationType_1_0::GPS;
- case GnssConstellationType_2_0::SBAS:
- return GnssConstellationType_1_0::SBAS;
- case GnssConstellationType_2_0::GLONASS:
- return GnssConstellationType_1_0::GLONASS;
- case GnssConstellationType_2_0::QZSS:
- return GnssConstellationType_1_0::QZSS;
- case GnssConstellationType_2_0::BEIDOU:
- return GnssConstellationType_1_0::BEIDOU;
- case GnssConstellationType_2_0::GALILEO:
- return GnssConstellationType_1_0::GALILEO;
- default:
- return GnssConstellationType_1_0::UNKNOWN;
- }
-}
-
-/*
* FindStrongFrequentNonGpsSource:
*
* Search through a GnssSvStatus list for the strongest non-GPS satellite observed enough times
@@ -555,7 +528,7 @@
(sv_info.constellation != GnssConstellationType_2_0::GPS)) {
ComparableBlacklistedSource source;
source.id.svid = sv_info.v1_0.svid;
- source.id.constellation = MapConstellationType(sv_info.constellation);
+ source.id.constellation = Utils::mapConstellationType(sv_info.constellation);
const auto& itSignal = mapSignals.find(source);
if (itSignal == mapSignals.end()) {
@@ -694,7 +667,7 @@
hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
- auto constellation = MapConstellationType(sv_info.constellation);
+ auto constellation = Utils::mapConstellationType(sv_info.constellation);
EXPECT_FALSE((sv_info.v1_0.svid == source_to_blacklist.svid) &&
(constellation == source_to_blacklist.constellation) &&
(sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX));
@@ -736,7 +709,7 @@
hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
- auto constellation = MapConstellationType(sv_info.constellation);
+ auto constellation = Utils::mapConstellationType(sv_info.constellation);
if ((sv_info.v1_0.svid == source_to_blacklist.svid) &&
(constellation == source_to_blacklist.constellation) &&
(sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX)) {
@@ -752,7 +725,7 @@
}
/*
- * BlacklistConstellation:
+ * BlacklistConstellationWithLocationOff:
*
* 1) Turns on location, waits for 3 locations, ensuring they are valid, and checks corresponding
* GnssStatus for any non-GPS constellations.
@@ -761,12 +734,11 @@
* GnssStatus does not use any constellation but GPS.
* 4a & b) Clean up by turning off location, and send in empty blacklist.
*/
-TEST_P(GnssHalTest, BlacklistConstellation) {
+TEST_P(GnssHalTest, BlacklistConstellationWithLocationOff) {
if (!IsGnssHalVersion_2_0()) {
ALOGI("Test BlacklistConstellation skipped. GNSS HAL version is greater than 2.0.");
return;
}
-
if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::SATELLITE_BLACKLIST)) {
ALOGI("Test BlacklistConstellation skipped. SATELLITE_BLACKLIST capability not supported.");
return;
@@ -774,43 +746,12 @@
const int kLocationsToAwait = 3;
- gnss_cb_->location_cbq_.reset();
- StartAndCheckLocations(kLocationsToAwait);
- const int location_called_count = gnss_cb_->location_cbq_.calledCount();
+ // Find first non-GPS constellation to blacklist
+ GnssConstellationType_1_0 constellation_to_blacklist = startLocationAndGetNonGpsConstellation();
- // Tolerate 1 less sv status to handle edge cases in reporting.
- int sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
- EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
- ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations (%d received)",
- sv_info_list_cbq_size, kLocationsToAwait, location_called_count);
+ // Turns off location
+ StopAndClearLocations();
- // Find first non-GPS constellation to blacklist. Exclude IRNSS in GnssConstellationType_2_0
- // as blacklisting of this constellation is not supported in gnss@2.0.
- const int kGnssSvStatusTimeout = 2;
- GnssConstellationType_1_0 constellation_to_blacklist = GnssConstellationType_1_0::UNKNOWN;
- for (int i = 0; i < sv_info_list_cbq_size; ++i) {
- hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
- gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
- for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
- if ((sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX) &&
- (sv_info.constellation != GnssConstellationType_2_0::UNKNOWN) &&
- (sv_info.constellation != GnssConstellationType_2_0::IRNSS) &&
- (sv_info.constellation != GnssConstellationType_2_0::GPS)) {
- // found a non-GPS V1_0 constellation
- constellation_to_blacklist = MapConstellationType(sv_info.constellation);
- break;
- }
- }
- if (constellation_to_blacklist != GnssConstellationType_1_0::UNKNOWN) {
- break;
- }
- }
-
- if (constellation_to_blacklist == GnssConstellationType_1_0::UNKNOWN) {
- ALOGI("No non-GPS constellations found, constellation blacklist test less effective.");
- // Proceed functionally to blacklist something.
- constellation_to_blacklist = GnssConstellationType_1_0::GLONASS;
- }
IGnssConfiguration_1_1::BlacklistedSource source_to_blacklist;
source_to_blacklist.constellation = constellation_to_blacklist;
source_to_blacklist.svid = 0; // documented wildcard for all satellites in this constellation
@@ -824,6 +765,7 @@
sources.resize(1);
sources[0] = source_to_blacklist;
+ // setBlacklist when location is off.
auto result = gnss_configuration_hal->setBlacklist(sources);
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
@@ -835,15 +777,93 @@
StartAndCheckLocations(kLocationsToAwait);
// Tolerate 1 less sv status to handle edge cases in reporting.
- sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
+ int sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations", sv_info_list_cbq_size,
kLocationsToAwait);
+ const int kGnssSvStatusTimeout = 2;
for (int i = 0; i < sv_info_list_cbq_size; ++i) {
hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
- auto constellation = MapConstellationType(sv_info.constellation);
+ auto constellation = Utils::mapConstellationType(sv_info.constellation);
+ EXPECT_FALSE((constellation == source_to_blacklist.constellation) &&
+ (sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX));
+ }
+ }
+
+ // clean up
+ StopAndClearLocations();
+ sources.resize(0);
+ result = gnss_configuration_hal->setBlacklist(sources);
+ ASSERT_TRUE(result.isOk());
+ EXPECT_TRUE(result);
+}
+
+/*
+ * BlacklistConstellationWithLocationOn:
+ *
+ * 1) Turns on location, waits for 3 locations, ensuring they are valid, and checks corresponding
+ * GnssStatus for any non-GPS constellations.
+ * 2a & b) Blacklist first non-GPS constellations, and turns off location.
+ * 3) Restart location, wait for 3 locations, ensuring they are valid, and checks corresponding
+ * GnssStatus does not use any constellation but GPS.
+ * 4a & b) Clean up by turning off location, and send in empty blacklist.
+ */
+TEST_P(GnssHalTest, BlacklistConstellationWithLocationOn) {
+ if (!IsGnssHalVersion_2_0()) {
+ ALOGI("Test BlacklistConstellation skipped. GNSS HAL version is greater than 2.0.");
+ return;
+ }
+
+ if (!(gnss_cb_->last_capabilities_ & IGnssCallback::Capabilities::SATELLITE_BLACKLIST)) {
+ ALOGI("Test BlacklistConstellation skipped. SATELLITE_BLACKLIST capability not supported.");
+ return;
+ }
+
+ const int kLocationsToAwait = 3;
+
+ // Find first non-GPS constellation to blacklist
+ GnssConstellationType_1_0 constellation_to_blacklist = startLocationAndGetNonGpsConstellation();
+
+ IGnssConfiguration_1_1::BlacklistedSource source_to_blacklist;
+ source_to_blacklist.constellation = constellation_to_blacklist;
+ source_to_blacklist.svid = 0; // documented wildcard for all satellites in this constellation
+
+ auto gnss_configuration_hal_return = gnss_hal_->getExtensionGnssConfiguration_1_1();
+ ASSERT_TRUE(gnss_configuration_hal_return.isOk());
+ sp<IGnssConfiguration_1_1> gnss_configuration_hal = gnss_configuration_hal_return;
+ ASSERT_NE(gnss_configuration_hal, nullptr);
+
+ hidl_vec<IGnssConfiguration_1_1::BlacklistedSource> sources;
+ sources.resize(1);
+ sources[0] = source_to_blacklist;
+
+ // setBlacklist when location is on.
+ auto result = gnss_configuration_hal->setBlacklist(sources);
+ ASSERT_TRUE(result.isOk());
+ EXPECT_TRUE(result);
+
+ // Turns off location
+ StopAndClearLocations();
+
+ // retry and ensure constellation not used
+ gnss_cb_->sv_info_list_cbq_.reset();
+
+ gnss_cb_->location_cbq_.reset();
+ StartAndCheckLocations(kLocationsToAwait);
+
+ // Tolerate 1 less sv status to handle edge cases in reporting.
+ int sv_info_list_cbq_size = gnss_cb_->sv_info_list_cbq_.size();
+ EXPECT_GE(sv_info_list_cbq_size + 1, kLocationsToAwait);
+ ALOGD("Observed %d GnssSvStatus, while awaiting %d Locations", sv_info_list_cbq_size,
+ kLocationsToAwait);
+ const int kGnssSvStatusTimeout = 2;
+ for (int i = 0; i < sv_info_list_cbq_size; ++i) {
+ hidl_vec<IGnssCallback_2_0::GnssSvInfo> sv_info_list;
+ gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_list, kGnssSvStatusTimeout);
+ for (IGnssCallback_2_0::GnssSvInfo sv_info : sv_info_list) {
+ auto constellation = Utils::mapConstellationType(sv_info.constellation);
EXPECT_FALSE((constellation == source_to_blacklist.constellation) &&
(sv_info.v1_0.svFlag & IGnssCallback::GnssSvFlags::USED_IN_FIX));
}
diff --git a/gnss/common/utils/vts/Android.bp b/gnss/common/utils/vts/Android.bp
index fd9613b..4c6d443 100644
--- a/gnss/common/utils/vts/Android.bp
+++ b/gnss/common/utils/vts/Android.bp
@@ -29,6 +29,7 @@
export_include_dirs: ["include"],
shared_libs: [
"android.hardware.gnss@1.0",
+ "android.hardware.gnss@2.0",
"android.hardware.gnss.measurement_corrections@1.0",
"android.hardware.gnss.measurement_corrections@1.1",
],
diff --git a/gnss/common/utils/vts/Utils.cpp b/gnss/common/utils/vts/Utils.cpp
index 4b5a50f..9bf68e6 100644
--- a/gnss/common/utils/vts/Utils.cpp
+++ b/gnss/common/utils/vts/Utils.cpp
@@ -169,6 +169,31 @@
return mockCorrections_1_1;
}
+/*
+ * MapConstellationType:
+ * Given a GnssConstellationType_2_0 type constellation, maps to its equivalent
+ * GnssConstellationType_1_0 type constellation. For constellations that do not have
+ * an equivalent value, maps to GnssConstellationType_1_0::UNKNOWN
+ */
+GnssConstellationType_1_0 Utils::mapConstellationType(GnssConstellationType_2_0 constellation) {
+ switch (constellation) {
+ case GnssConstellationType_2_0::GPS:
+ return GnssConstellationType_1_0::GPS;
+ case GnssConstellationType_2_0::SBAS:
+ return GnssConstellationType_1_0::SBAS;
+ case GnssConstellationType_2_0::GLONASS:
+ return GnssConstellationType_1_0::GLONASS;
+ case GnssConstellationType_2_0::QZSS:
+ return GnssConstellationType_1_0::QZSS;
+ case GnssConstellationType_2_0::BEIDOU:
+ return GnssConstellationType_1_0::BEIDOU;
+ case GnssConstellationType_2_0::GALILEO:
+ return GnssConstellationType_1_0::GALILEO;
+ default:
+ return GnssConstellationType_1_0::UNKNOWN;
+ }
+}
+
} // namespace common
} // namespace gnss
} // namespace hardware
diff --git a/gnss/common/utils/vts/include/Utils.h b/gnss/common/utils/vts/include/Utils.h
index c3cdd18..9c838b2 100644
--- a/gnss/common/utils/vts/include/Utils.h
+++ b/gnss/common/utils/vts/include/Utils.h
@@ -18,9 +18,12 @@
#define android_hardware_gnss_common_vts_Utils_H_
#include <android/hardware/gnss/1.0/IGnss.h>
+#include <android/hardware/gnss/2.0/IGnss.h>
#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
#include <android/hardware/gnss/measurement_corrections/1.1/IMeasurementCorrections.h>
+using GnssConstellationType_1_0 = android::hardware::gnss::V1_0::GnssConstellationType;
+using GnssConstellationType_2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
using GnssLocation = ::android::hardware::gnss::V1_0::GnssLocation;
using namespace android::hardware::gnss::measurement_corrections::V1_0;
@@ -44,6 +47,8 @@
bool check_more_accuracies);
static const MeasurementCorrections_1_0 getMockMeasurementCorrections();
static const MeasurementCorrections_1_1 getMockMeasurementCorrections_1_1();
+
+ static GnssConstellationType_1_0 mapConstellationType(GnssConstellationType_2_0 constellation);
};
} // namespace common
diff --git a/graphics/common/aidl/android/hardware/graphics/common/PlaneLayout.aidl b/graphics/common/aidl/android/hardware/graphics/common/PlaneLayout.aidl
index ccb0690..b329cb2 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/PlaneLayout.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/PlaneLayout.aidl
@@ -100,10 +100,11 @@
long totalSizeInBytes;
/**
- * Horizontal and vertical subsampling. Must be a positive power of 2.
+ * Horizontal and vertical subsampling. Must be a positive power of 2. A value of 1
+ * indicates no subsampling.
*
* These fields indicate the number of horizontally or vertically adjacent pixels that use
- * the same pixel data. A value of 1 indicates no subsampling.
+ * the same pixel data.
*/
long horizontalSubsampling;
long verticalSubsampling;
diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
index 529fb18..6df7f8d 100644
--- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
+++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
@@ -605,6 +605,9 @@
ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
}
+/**
+ * Test multiple operations associated with different color formats
+ */
TEST_P(GraphicsMapperHidlTest, Lock_YCRCB_420_SP) {
auto info = mDummyDescriptorInfo;
info.format = PixelFormat::YCRCB_420_SP;
@@ -751,6 +754,90 @@
ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
}
+TEST_P(GraphicsMapperHidlTest, Lock_RAW10) {
+ auto info = mDummyDescriptorInfo;
+ info.format = PixelFormat::RAW10;
+
+ const native_handle_t* bufferHandle;
+ uint32_t stride;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(
+ info, true, Tolerance::kToleranceUnSupported, &stride));
+ if (bufferHandle == nullptr) {
+ GTEST_SUCCEED() << "RAW10 format is unsupported";
+ return;
+ }
+
+ const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
+ static_cast<int32_t>(info.height)};
+ unique_fd fence;
+
+ ASSERT_NO_FATAL_FAILURE(mGralloc->lock(bufferHandle, info.usage, region, fence.get()));
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, &vec));
+ std::vector<PlaneLayout> planeLayouts;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts));
+
+ ASSERT_EQ(1, planeLayouts.size());
+ auto planeLayout = planeLayouts[0];
+
+ EXPECT_EQ(0, planeLayout.sampleIncrementInBits);
+ EXPECT_EQ(1, planeLayout.horizontalSubsampling);
+ EXPECT_EQ(1, planeLayout.verticalSubsampling);
+
+ ASSERT_EQ(1, planeLayout.components.size());
+ auto planeLayoutComponent = planeLayout.components[0];
+
+ EXPECT_EQ(PlaneLayoutComponentType::RAW,
+ static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value));
+ EXPECT_EQ(0, planeLayoutComponent.offsetInBits % 8);
+ EXPECT_EQ(-1, planeLayoutComponent.sizeInBits);
+
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
+}
+
+TEST_P(GraphicsMapperHidlTest, Lock_RAW12) {
+ auto info = mDummyDescriptorInfo;
+ info.format = PixelFormat::RAW12;
+
+ const native_handle_t* bufferHandle;
+ uint32_t stride;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(
+ info, true, Tolerance::kToleranceUnSupported, &stride));
+ if (bufferHandle == nullptr) {
+ GTEST_SUCCEED() << "RAW12 format is unsupported";
+ return;
+ }
+
+ const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
+ static_cast<int32_t>(info.height)};
+ unique_fd fence;
+
+ ASSERT_NO_FATAL_FAILURE(mGralloc->lock(bufferHandle, info.usage, region, fence.get()));
+
+ hidl_vec<uint8_t> vec;
+ ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, &vec));
+ std::vector<PlaneLayout> planeLayouts;
+ ASSERT_EQ(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts));
+
+ ASSERT_EQ(1, planeLayouts.size());
+ auto planeLayout = planeLayouts[0];
+
+ EXPECT_EQ(0, planeLayout.sampleIncrementInBits);
+ EXPECT_EQ(1, planeLayout.horizontalSubsampling);
+ EXPECT_EQ(1, planeLayout.verticalSubsampling);
+
+ ASSERT_EQ(1, planeLayout.components.size());
+ auto planeLayoutComponent = planeLayout.components[0];
+
+ EXPECT_EQ(PlaneLayoutComponentType::RAW,
+ static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value));
+ EXPECT_EQ(0, planeLayoutComponent.offsetInBits % 8);
+ EXPECT_EQ(-1, planeLayoutComponent.sizeInBits);
+
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
+}
+
/**
* Test IMapper::unlock with bad access region
*/
@@ -1707,6 +1794,84 @@
}
/**
+ * Test get::metadata with cloned native_handle
+ */
+TEST_P(GraphicsMapperHidlTest, GetMetadataClonedHandle) {
+ const native_handle_t* bufferHandle = nullptr;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true));
+
+ const auto dataspace = Dataspace::SRGB_LINEAR;
+ {
+ hidl_vec<uint8_t> metadata;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeDataspace(dataspace, &metadata));
+
+ Error err = mGralloc->set(bufferHandle, gralloc4::MetadataType_Dataspace, metadata);
+ if (err == Error::UNSUPPORTED) {
+ GTEST_SUCCEED() << "setting this metadata is unsupported";
+ return;
+ }
+ ASSERT_EQ(Error::NONE, err);
+ }
+
+ const native_handle_t* importedHandle;
+ {
+ auto clonedHandle = native_handle_clone(bufferHandle);
+ ASSERT_NO_FATAL_FAILURE(importedHandle = mGralloc->importBuffer(clonedHandle));
+ native_handle_close(clonedHandle);
+ native_handle_delete(clonedHandle);
+ }
+
+ Dataspace realSpace = Dataspace::UNKNOWN;
+ {
+ hidl_vec<uint8_t> metadata;
+ ASSERT_EQ(Error::NONE,
+ mGralloc->get(importedHandle, gralloc4::MetadataType_Dataspace, &metadata));
+ ASSERT_NO_FATAL_FAILURE(gralloc4::decodeDataspace(metadata, &realSpace));
+ }
+
+ EXPECT_EQ(dataspace, realSpace);
+}
+
+/**
+ * Test set::metadata with cloned native_handle
+ */
+TEST_P(GraphicsMapperHidlTest, SetMetadataClonedHandle) {
+ const native_handle_t* bufferHandle = nullptr;
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(mDummyDescriptorInfo, true));
+
+ const native_handle_t* importedHandle;
+ {
+ auto clonedHandle = native_handle_clone(bufferHandle);
+ ASSERT_NO_FATAL_FAILURE(importedHandle = mGralloc->importBuffer(clonedHandle));
+ native_handle_close(clonedHandle);
+ native_handle_delete(clonedHandle);
+ }
+
+ const auto dataspace = Dataspace::SRGB_LINEAR;
+ {
+ hidl_vec<uint8_t> metadata;
+ ASSERT_EQ(NO_ERROR, gralloc4::encodeDataspace(dataspace, &metadata));
+
+ Error err = mGralloc->set(importedHandle, gralloc4::MetadataType_Dataspace, metadata);
+ if (err == Error::UNSUPPORTED) {
+ GTEST_SUCCEED() << "setting this metadata is unsupported";
+ return;
+ }
+ ASSERT_EQ(Error::NONE, err);
+ }
+
+ Dataspace realSpace = Dataspace::UNKNOWN;
+ {
+ hidl_vec<uint8_t> metadata;
+ ASSERT_EQ(Error::NONE,
+ mGralloc->get(bufferHandle, gralloc4::MetadataType_Dataspace, &metadata));
+ ASSERT_NO_FATAL_FAILURE(gralloc4::decodeDataspace(metadata, &realSpace));
+ }
+
+ EXPECT_EQ(dataspace, realSpace);
+}
+
+/**
* Test IMapper::set(metadata) for constant metadata
*/
TEST_P(GraphicsMapperHidlTest, SetConstantMetadata) {
diff --git a/health/1.0/default/Android.bp b/health/1.0/default/Android.bp
index 7581335..b815eae 100644
--- a/health/1.0/default/Android.bp
+++ b/health/1.0/default/Android.bp
@@ -51,6 +51,12 @@
"android.hardware.health@1.0-convert",
"libhealthd.default",
],
+
+ shared_libs: [
+ "libhidlbase",
+ "libutils",
+ "android.hardware.health@1.0",
+ ],
}
cc_binary {
diff --git a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index d7f47e8..3b8fbd9 100644
--- a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -160,17 +160,10 @@
* ItemsRequestBytes
* ]
*
- * SessionTranscript = [
- * DeviceEngagementBytes,
- * EReaderKeyBytes
- * ]
+ * SessionTranscript = any
*
- * DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
- * EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
* ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
*
- * EReaderKey.Pub = COSE_Key ; Ephemeral public key provided by reader
- *
* The public key corresponding to the key used to made signature, can be found in the
* 'x5chain' unprotected header element of the COSE_Sign1 structure (as as described
* in 'draft-ietf-cose-x509-04'). There will be at least one certificate in said element
@@ -184,8 +177,12 @@
*
* If the SessionTranscript CBOR is not empty, the X and Y coordinates of the public
* part of the key-pair previously generated by createEphemeralKeyPair() must appear
- * somewhere in the bytes of DeviceEngagement structure. Both X and Y should be in
- * uncompressed form. If this is not satisfied, the call fails with
+ * somewhere in the bytes of the CBOR. Each of these coordinates must appear encoded
+ * with the most significant bits first and use the exact amount of bits indicated by
+ * the key size of the ephemeral keys. For example, if the ephemeral key is using the
+ * P-256 curve then the 32 bytes for the X coordinate encoded with the most significant
+ * bits first must appear somewhere in the CBOR and ditto for the 32 bytes for the Y
+ * coordinate. If this is not satisfied, the call fails with
* STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
*
* @param accessControlProfiles
@@ -298,13 +295,8 @@
*
* DocType = tstr
*
- * SessionTranscript = [
- * DeviceEngagementBytes,
- * EReaderKeyBytes
- * ]
+ * SessionTranscript = any
*
- * DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
- * EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
* DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
*
* where
@@ -356,8 +348,9 @@
*
* - subjectPublicKeyInfo: must contain attested public key.
*
- * @param out signingKeyBlob contains an encrypted copy of the newly-generated private
- * signing key.
+ * @param out signingKeyBlob contains an AES-GCM-ENC(storageKey, R, signingKey, docType)
+ * where signingKey is an EC private key in uncompressed form. That is, the returned
+ * blob is an encrypted copy of the newly-generated private signing key.
*
* @return an X.509 certificate for the new signing key, signed by the credential key.
*/
diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index b7ad283..297fd1d 100644
--- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -29,9 +29,27 @@
* Gets the certificate chain for credentialKey which can be used to prove the hardware
* characteristics to an issuing authority. Must not be called more than once.
*
+ * The following non-optional fields for the X.509 certificate shall be set as follows:
+ *
+ * - version: INTEGER 2 (means v3 certificate).
+ *
+ * - serialNumber: INTEGER 1 (fixed value: same on all certs).
+ *
+ * - signature: must be set to ECDSA.
+ *
+ * - subject: CN shall be set to "Android Identity Credential Key".
+ *
+ * - issuer: shall be set to "credentialStoreName (credentialStoreAuthorName)" using the
+ * values returned in HardwareInformation.
+ *
+ * - validity: should be from current time and expire at the same time as the
+ * attestation batch certificate used.
+ *
+ * - subjectPublicKeyInfo: must contain attested public key.
+ *
* The certificate chain must be generated using Keymaster Attestation
* (see https://source.android.com/security/keystore/attestation) with the
- * following additional requirements:
+ * following additional requirements on the data in the attestation extension:
*
* - The attestationVersion field in the attestation extension must be at least 3.
*
@@ -109,7 +127,8 @@
* in Tag::ATTESTATION_APPLICATION_ID. This schema is described in
* https://developer.android.com/training/articles/security-key-attestation#certificate_schema_attestationid
*
- * @param attestationChallenge a challenge set by the issuer to ensure freshness.
+ * @param attestationChallenge a challenge set by the issuer to ensure freshness. If
+ * this is empty, the call fails with STATUS_INVALID_DATA.
*
* @return the X.509 certificate chain for the credentialKey
*/
@@ -250,6 +269,7 @@
* CredentialKeys = [
* bstr, ; storageKey, a 128-bit AES key
* bstr ; credentialPrivKey, the private key for credentialKey
+ * ; in uncompressed form
* ]
*
* @param out proofOfProvisioningSignature proves to the IA that the credential was imported
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp
index 381eb84..4e9e0e6 100644
--- a/identity/aidl/default/IdentityCredential.cpp
+++ b/identity/aidl/default/IdentityCredential.cpp
@@ -164,6 +164,7 @@
}
*outChallenge = challenge;
+ authChallenge_ = challenge;
return ndk::ScopedAStatus::ok();
}
@@ -223,7 +224,8 @@
}
if (authToken.challenge != int64_t(authChallenge)) {
- LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
+ LOG(ERROR) << "Challenge in authToken (" << uint64_t(authToken.challenge) << ") "
+ << "doesn't match the challenge we created (" << authChallenge << ")";
return false;
}
return true;
@@ -337,28 +339,6 @@
//
// We do this by just searching for the X and Y coordinates.
if (sessionTranscript.size() > 0) {
- const cppbor::Array* array = sessionTranscriptItem_->asArray();
- if (array == nullptr || array->size() != 2) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "SessionTranscript is not an array with two items"));
- }
- const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic();
- if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "First item in SessionTranscript array is not a "
- "semantic with value 24"));
- }
- const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr();
- if (encodedDE == nullptr) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "Child of semantic in first item in SessionTranscript "
- "array is not a bstr"));
- }
- const vector<uint8_t>& bytesDE = encodedDE->value();
-
auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
if (!getXYSuccess) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
@@ -366,8 +346,10 @@
"Error extracting X and Y from ePub"));
}
if (sessionTranscript.size() > 0 &&
- !(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr &&
- memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) {
+ !(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(),
+ ePubX.size()) != nullptr &&
+ memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(),
+ ePubY.size()) != nullptr)) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
"Did not find ephemeral public key's X and Y coordinates in "
@@ -474,9 +456,10 @@
}
// Validate all the access control profiles in the requestData.
- bool haveAuthToken = (authToken.mac.size() > 0);
+ bool haveAuthToken = (authToken.timestamp.milliSeconds != int64_t(0));
for (const auto& profile : accessControlProfiles) {
if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
+ LOG(ERROR) << "Error checking MAC for profile";
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_INVALID_DATA,
"Error checking MAC for profile"));
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
index 8bc4b49..fea289b 100644
--- a/identity/aidl/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/WritableIdentityCredential.cpp
@@ -65,6 +65,10 @@
IIdentityCredentialStore::STATUS_FAILED,
"Error attestation certificate previously generated"));
}
+ if (attestationChallenge.empty()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty"));
+ }
vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
@@ -165,6 +169,13 @@
"userAuthenticationRequired is false but timeout is non-zero"));
}
+ // If |userAuthenticationRequired| is true, then |secureUserId| must be non-zero.
+ if (userAuthenticationRequired && secureUserId == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "userAuthenticationRequired is true but secureUserId is zero"));
+ }
+
profile.id = id;
profile.readerCertificate = readerCertificate;
profile.userAuthenticationRequired = userAuthenticationRequired;
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index 5b075c6..cd6f9b0 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -10,17 +10,20 @@
"VtsIdentityTestUtils.cpp",
"VtsAttestationTests.cpp",
"VtsAttestationParserSupport.cpp",
+ "UserAuthTests.cpp",
+ "ReaderAuthTests.cpp",
],
shared_libs: [
"android.hardware.keymaster@4.0",
"libbinder",
"libcrypto",
- "libkeymaster_portable",
- "libsoft_attestation_cert",
- "libpuresoftkeymasterdevice",
+ "android.hardware.keymaster-ndk_platform",
],
static_libs: [
"libcppbor",
+ "libkeymaster_portable",
+ "libsoft_attestation_cert",
+ "libpuresoftkeymasterdevice",
"android.hardware.identity-support-lib",
"android.hardware.identity-cpp",
"android.hardware.keymaster-cpp",
diff --git a/identity/aidl/vts/ReaderAuthTests.cpp b/identity/aidl/vts/ReaderAuthTests.cpp
new file mode 100644
index 0000000..680ba5b
--- /dev/null
+++ b/identity/aidl/vts/ReaderAuthTests.cpp
@@ -0,0 +1,596 @@
+/*
+ * 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 "ReaderAuthTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "VtsIdentityTestUtils.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class ReaderAuthTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(GetParam().c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ }
+
+ void provisionData();
+ void retrieveData(const vector<uint8_t>& readerPrivateKey,
+ const vector<vector<uint8_t>>& readerCertChain, bool expectSuccess,
+ bool leaveOutAccessibleToAllFromRequestMessage);
+
+ // Set by provisionData
+ vector<uint8_t> readerPublicKey_;
+ vector<uint8_t> readerPrivateKey_;
+ vector<uint8_t> intermediateAPublicKey_;
+ vector<uint8_t> intermediateAPrivateKey_;
+ vector<uint8_t> intermediateBPublicKey_;
+ vector<uint8_t> intermediateBPrivateKey_;
+ vector<uint8_t> intermediateCPublicKey_;
+ vector<uint8_t> intermediateCPrivateKey_;
+
+ vector<uint8_t> cert_A_SelfSigned_;
+
+ vector<uint8_t> cert_B_SelfSigned_;
+
+ vector<uint8_t> cert_B_SignedBy_C_;
+
+ vector<uint8_t> cert_C_SelfSigned_;
+
+ vector<uint8_t> cert_reader_SelfSigned_;
+ vector<uint8_t> cert_reader_SignedBy_A_;
+ vector<uint8_t> cert_reader_SignedBy_B_;
+
+ SecureAccessControlProfile sacp0_;
+ SecureAccessControlProfile sacp1_;
+ SecureAccessControlProfile sacp2_;
+ SecureAccessControlProfile sacp3_;
+
+ vector<uint8_t> encContentAccessibleByA_;
+ vector<uint8_t> encContentAccessibleByAorB_;
+ vector<uint8_t> encContentAccessibleByB_;
+ vector<uint8_t> encContentAccessibleByC_;
+ vector<uint8_t> encContentAccessibleByAll_;
+ vector<uint8_t> encContentAccessibleByNone_;
+
+ vector<uint8_t> credentialData_;
+
+ // Set by retrieveData()
+ bool canGetAccessibleByA_;
+ bool canGetAccessibleByAorB_;
+ bool canGetAccessibleByB_;
+ bool canGetAccessibleByC_;
+ bool canGetAccessibleByAll_;
+ bool canGetAccessibleByNone_;
+
+ sp<IIdentityCredentialStore> credentialStore_;
+};
+
+pair<vector<uint8_t>, vector<uint8_t>> generateReaderKey() {
+ optional<vector<uint8_t>> keyPKCS8 = support::createEcKeyPair();
+ optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPKCS8.value());
+ optional<vector<uint8_t>> privateKey = support::ecKeyPairGetPrivateKey(keyPKCS8.value());
+ return make_pair(publicKey.value(), privateKey.value());
+}
+
+vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey,
+ const vector<uint8_t>& signingKey) {
+ time_t validityNotBefore = 0;
+ time_t validityNotAfter = 0xffffffff;
+ optional<vector<uint8_t>> cert =
+ support::ecPublicKeyGenerateCertificate(publicKey, signingKey, "24601", "Issuer",
+ "Subject", validityNotBefore, validityNotAfter);
+ return cert.value();
+}
+
+void ReaderAuthTests::provisionData() {
+ // Keys and certificates for intermediates.
+ tie(intermediateAPublicKey_, intermediateAPrivateKey_) = generateReaderKey();
+ tie(intermediateBPublicKey_, intermediateBPrivateKey_) = generateReaderKey();
+ tie(intermediateCPublicKey_, intermediateCPrivateKey_) = generateReaderKey();
+
+ cert_A_SelfSigned_ = generateReaderCert(intermediateAPublicKey_, intermediateAPrivateKey_);
+
+ cert_B_SelfSigned_ = generateReaderCert(intermediateBPublicKey_, intermediateBPrivateKey_);
+
+ cert_B_SignedBy_C_ = generateReaderCert(intermediateBPublicKey_, intermediateCPrivateKey_);
+
+ cert_C_SelfSigned_ = generateReaderCert(intermediateCPublicKey_, intermediateCPrivateKey_);
+
+ // Key and self-signed certificate reader
+ tie(readerPublicKey_, readerPrivateKey_) = generateReaderKey();
+ cert_reader_SelfSigned_ = generateReaderCert(readerPublicKey_, readerPrivateKey_);
+
+ // Certificate for reader signed by intermediates
+ cert_reader_SignedBy_A_ = generateReaderCert(readerPublicKey_, intermediateAPrivateKey_);
+ cert_reader_SignedBy_B_ = generateReaderCert(readerPublicKey_, intermediateBPrivateKey_);
+
+ string docType = "org.iso.18013-5.2019.mdl";
+ bool testCredential = true;
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ size_t proofOfProvisioningSize =
+ 465 + cert_A_SelfSigned_.size() + cert_B_SelfSigned_.size() + cert_C_SelfSigned_.size();
+ ASSERT_TRUE(wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize).isOk());
+
+ // Not in v1 HAL, may fail
+ wc->startPersonalization(4 /* numAccessControlProfiles */,
+ {6} /* numDataElementsPerNamespace */);
+
+ // AIDL expects certificates wrapped in the Certificate type...
+ Certificate cert_A;
+ Certificate cert_B;
+ Certificate cert_C;
+ cert_A.encodedCertificate = cert_A_SelfSigned_;
+ cert_B.encodedCertificate = cert_B_SelfSigned_;
+ cert_C.encodedCertificate = cert_C_SelfSigned_;
+
+ // Access control profile 0: accessible by A
+ ASSERT_TRUE(wc->addAccessControlProfile(0, cert_A, false, 0, 0, &sacp0_).isOk());
+
+ // Access control profile 1: accessible by B
+ ASSERT_TRUE(wc->addAccessControlProfile(1, cert_B, false, 0, 0, &sacp1_).isOk());
+
+ // Access control profile 2: accessible by C
+ ASSERT_TRUE(wc->addAccessControlProfile(2, cert_C, false, 0, 0, &sacp2_).isOk());
+
+ // Access control profile 3: open access
+ ASSERT_TRUE(wc->addAccessControlProfile(3, {}, false, 0, 0, &sacp3_).isOk());
+
+ // Data Element: "Accessible by A"
+ ASSERT_TRUE(wc->beginAddEntry({0}, "ns", "Accessible by A", 1).isOk());
+ ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByA_).isOk());
+
+ // Data Element: "Accessible by A or B"
+ ASSERT_TRUE(wc->beginAddEntry({0, 1}, "ns", "Accessible by A or B", 1).isOk());
+ ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByAorB_).isOk());
+
+ // Data Element: "Accessible by B"
+ ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Accessible by B", 1).isOk());
+ ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByB_).isOk());
+
+ // Data Element: "Accessible by C"
+ ASSERT_TRUE(wc->beginAddEntry({2}, "ns", "Accessible by C", 1).isOk());
+ ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByC_).isOk());
+
+ // Data Element: "Accessible by All"
+ ASSERT_TRUE(wc->beginAddEntry({3}, "ns", "Accessible by All", 1).isOk());
+ ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByAll_).isOk());
+
+ // Data Element: "Accessible by None"
+ ASSERT_TRUE(wc->beginAddEntry({}, "ns", "Accessible by None", 1).isOk());
+ ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByNone_).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ ASSERT_TRUE(wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature).isOk());
+}
+
+RequestDataItem buildRequestDataItem(const string& name, size_t size,
+ vector<int32_t> accessControlProfileIds) {
+ RequestDataItem item;
+ item.name = name;
+ item.size = size;
+ item.accessControlProfileIds = accessControlProfileIds;
+ return item;
+}
+
+void ReaderAuthTests::retrieveData(const vector<uint8_t>& readerPrivateKey,
+ const vector<vector<uint8_t>>& readerCertChain,
+ bool expectSuccess,
+ bool leaveOutAccessibleToAllFromRequestMessage) {
+ canGetAccessibleByA_ = false;
+ canGetAccessibleByAorB_ = false;
+ canGetAccessibleByB_ = false;
+ canGetAccessibleByC_ = false;
+ canGetAccessibleByAll_ = false;
+ canGetAccessibleByNone_ = false;
+
+ sp<IIdentityCredential> c;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData_, &c)
+ .isOk());
+
+ optional<vector<uint8_t>> readerEKeyPair = support::createEcKeyPair();
+ optional<vector<uint8_t>> readerEPublicKey =
+ support::ecKeyPairGetPublicKey(readerEKeyPair.value());
+ ASSERT_TRUE(c->setReaderEphemeralPublicKey(readerEPublicKey.value()).isOk());
+
+ vector<uint8_t> eKeyPair;
+ ASSERT_TRUE(c->createEphemeralKeyPair(&eKeyPair).isOk());
+ optional<vector<uint8_t>> ePublicKey = support::ecKeyPairGetPublicKey(eKeyPair);
+
+ // Calculate requestData field and sign it with the reader key.
+ auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ePublicKey.value());
+ ASSERT_TRUE(getXYSuccess);
+ cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
+ vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
+ vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
+ cppbor::Array sessionTranscript = cppbor::Array()
+ .add(cppbor::Semantic(24, deviceEngagementBytes))
+ .add(cppbor::Semantic(24, eReaderPubBytes));
+ vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
+
+ vector<uint8_t> itemsRequestBytes;
+ if (leaveOutAccessibleToAllFromRequestMessage) {
+ itemsRequestBytes =
+ cppbor::Map("nameSpaces",
+ cppbor::Map().add("ns", cppbor::Map()
+ .add("Accessible by A", false)
+ .add("Accessible by A or B", false)
+ .add("Accessible by B", false)
+ .add("Accessible by C", false)
+ .add("Accessible by None", false)))
+ .encode();
+ } else {
+ itemsRequestBytes =
+ cppbor::Map("nameSpaces",
+ cppbor::Map().add("ns", cppbor::Map()
+ .add("Accessible by A", false)
+ .add("Accessible by A or B", false)
+ .add("Accessible by B", false)
+ .add("Accessible by C", false)
+ .add("Accessible by All", false)
+ .add("Accessible by None", false)))
+ .encode();
+ }
+ vector<uint8_t> dataToSign = cppbor::Array()
+ .add("ReaderAuthentication")
+ .add(sessionTranscript.clone())
+ .add(cppbor::Semantic(24, itemsRequestBytes))
+ .encode();
+
+ optional<vector<uint8_t>> readerSignature =
+ support::coseSignEcDsa(readerPrivateKey, // private key for reader
+ {}, // content
+ dataToSign, // detached content
+ support::certificateChainJoin(readerCertChain));
+ ASSERT_TRUE(readerSignature);
+
+ // Generate the key that will be used to sign AuthenticatedData.
+ vector<uint8_t> signingKeyBlob;
+ Certificate signingKeyCertificate;
+ ASSERT_TRUE(c->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+
+ RequestNamespace rns;
+ rns.namespaceName = "ns";
+ rns.items.push_back(buildRequestDataItem("Accessible by A", 1, {0}));
+ rns.items.push_back(buildRequestDataItem("Accessible by A or B", 1, {0, 1}));
+ rns.items.push_back(buildRequestDataItem("Accessible by B", 1, {1}));
+ rns.items.push_back(buildRequestDataItem("Accessible by C", 1, {2}));
+ rns.items.push_back(buildRequestDataItem("Accessible by All", 1, {3}));
+ rns.items.push_back(buildRequestDataItem("Accessible by None", 1, {}));
+ // OK to fail, not available in v1 HAL
+ c->setRequestedNamespaces({rns}).isOk();
+
+ // It doesn't matter since no user auth is needed in this particular test,
+ // but for good measure, clear out the tokens we pass to the HAL.
+ HardwareAuthToken authToken;
+ VerificationToken verificationToken;
+ authToken.challenge = 0;
+ authToken.userId = 0;
+ authToken.authenticatorId = 0;
+ authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
+ authToken.timestamp.milliSeconds = 0;
+ authToken.mac.clear();
+ verificationToken.challenge = 0;
+ verificationToken.timestamp.milliSeconds = 0;
+ verificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE;
+ verificationToken.mac.clear();
+ // OK to fail, not available in v1 HAL
+ c->setVerificationToken(verificationToken);
+
+ Status status = c->startRetrieval(
+ {sacp0_, sacp1_, sacp2_, sacp3_}, authToken, itemsRequestBytes, signingKeyBlob,
+ sessionTranscriptBytes, readerSignature.value(), {6 /* numDataElementsPerNamespace */});
+ if (expectSuccess) {
+ ASSERT_TRUE(status.isOk());
+ } else {
+ ASSERT_FALSE(status.isOk());
+ return;
+ }
+
+ vector<uint8_t> decrypted;
+
+ status = c->startRetrieveEntryValue("ns", "Accessible by A", 1, {0});
+ if (status.isOk()) {
+ canGetAccessibleByA_ = true;
+ ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByA_, &decrypted).isOk());
+ }
+
+ status = c->startRetrieveEntryValue("ns", "Accessible by A or B", 1, {0, 1});
+ if (status.isOk()) {
+ canGetAccessibleByAorB_ = true;
+ ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByAorB_, &decrypted).isOk());
+ }
+
+ status = c->startRetrieveEntryValue("ns", "Accessible by B", 1, {1});
+ if (status.isOk()) {
+ canGetAccessibleByB_ = true;
+ ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByB_, &decrypted).isOk());
+ }
+
+ status = c->startRetrieveEntryValue("ns", "Accessible by C", 1, {2});
+ if (status.isOk()) {
+ canGetAccessibleByC_ = true;
+ ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByC_, &decrypted).isOk());
+ }
+
+ status = c->startRetrieveEntryValue("ns", "Accessible by All", 1, {3});
+ if (status.isOk()) {
+ canGetAccessibleByAll_ = true;
+ ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByAll_, &decrypted).isOk());
+ }
+
+ status = c->startRetrieveEntryValue("ns", "Accessible by None", 1, {});
+ if (status.isOk()) {
+ canGetAccessibleByNone_ = true;
+ ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByNone_, &decrypted).isOk());
+ }
+
+ vector<uint8_t> mac;
+ vector<uint8_t> deviceNameSpaces;
+ ASSERT_TRUE(c->finishRetrieval(&mac, &deviceNameSpaces).isOk());
+}
+
+TEST_P(ReaderAuthTests, presentingChain_Reader) {
+ provisionData();
+ retrieveData(readerPrivateKey_, {cert_reader_SelfSigned_}, true /* expectSuccess */,
+ false /* leaveOutAccessibleToAllFromRequestMessage */);
+ EXPECT_FALSE(canGetAccessibleByA_);
+ EXPECT_FALSE(canGetAccessibleByAorB_);
+ EXPECT_FALSE(canGetAccessibleByB_);
+ EXPECT_FALSE(canGetAccessibleByC_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(ReaderAuthTests, presentingChain_Reader_A) {
+ provisionData();
+ retrieveData(readerPrivateKey_, {cert_reader_SignedBy_A_, cert_A_SelfSigned_},
+ true /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
+ EXPECT_TRUE(canGetAccessibleByA_);
+ EXPECT_TRUE(canGetAccessibleByAorB_);
+ EXPECT_FALSE(canGetAccessibleByB_);
+ EXPECT_FALSE(canGetAccessibleByC_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(ReaderAuthTests, presentingChain_Reader_B) {
+ provisionData();
+ retrieveData(readerPrivateKey_, {cert_reader_SignedBy_B_, cert_B_SelfSigned_},
+ true /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
+ EXPECT_FALSE(canGetAccessibleByA_);
+ EXPECT_TRUE(canGetAccessibleByAorB_);
+ EXPECT_TRUE(canGetAccessibleByB_);
+ EXPECT_FALSE(canGetAccessibleByC_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+// This test proves that for the purpose of determining inclusion of an ACP certificate
+// in a presented reader chain, certificate equality is done by comparing public keys,
+// not bitwise comparison of the certificates.
+//
+// Specifically for this test, the ACP is configured with cert_B_SelfSigned_ and the
+// reader is presenting cert_B_SignedBy_C_. Both certificates have the same public
+// key - intermediateBPublicKey_ - but they are signed by different keys.
+//
+TEST_P(ReaderAuthTests, presentingChain_Reader_B_C) {
+ provisionData();
+ retrieveData(readerPrivateKey_,
+ {cert_reader_SignedBy_B_, cert_B_SignedBy_C_, cert_C_SelfSigned_},
+ true /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
+ EXPECT_FALSE(canGetAccessibleByA_);
+ EXPECT_TRUE(canGetAccessibleByAorB_);
+ EXPECT_TRUE(canGetAccessibleByB_);
+ EXPECT_TRUE(canGetAccessibleByC_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+// This test presents a reader chain where the chain is invalid because
+// the 2nd certificate in the chain isn't signed by the 3rd one.
+//
+TEST_P(ReaderAuthTests, presentingInvalidChain) {
+ provisionData();
+ retrieveData(readerPrivateKey_,
+ {cert_reader_SignedBy_B_, cert_B_SelfSigned_, cert_C_SelfSigned_},
+ false /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
+}
+
+// This tests presents a valid reader chain but where requestMessage isn't
+// signed by the private key corresponding to the public key in the top-level
+// certificate.
+//
+TEST_P(ReaderAuthTests, presentingMessageSignedNotByTopLevel) {
+ provisionData();
+ retrieveData(intermediateBPrivateKey_,
+ {cert_reader_SignedBy_B_, cert_B_SignedBy_C_, cert_C_SelfSigned_},
+ false /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
+}
+
+// This test leaves out "Accessible by All" data element from the signed request
+// message (the CBOR from the reader) while still including this data element at
+// the API level. The call on the API level for said element will fail with
+// STATUS_NOT_IN_REQUEST_MESSAGE but this doesn't prevent the other elements
+// from being returned (if authorized, of course).
+//
+// This test verifies that.
+//
+TEST_P(ReaderAuthTests, limitedMessage) {
+ provisionData();
+ retrieveData(readerPrivateKey_, {cert_reader_SelfSigned_}, true /* expectSuccess */,
+ true /* leaveOutAccessibleToAllFromRequestMessage */);
+ EXPECT_FALSE(canGetAccessibleByA_);
+ EXPECT_FALSE(canGetAccessibleByAorB_);
+ EXPECT_FALSE(canGetAccessibleByB_);
+ EXPECT_FALSE(canGetAccessibleByC_);
+ EXPECT_FALSE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(ReaderAuthTests, ephemeralKeyNotInSessionTranscript) {
+ provisionData();
+
+ sp<IIdentityCredential> c;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData_, &c)
+ .isOk());
+
+ optional<vector<uint8_t>> readerEKeyPair = support::createEcKeyPair();
+ optional<vector<uint8_t>> readerEPublicKey =
+ support::ecKeyPairGetPublicKey(readerEKeyPair.value());
+ ASSERT_TRUE(c->setReaderEphemeralPublicKey(readerEPublicKey.value()).isOk());
+
+ vector<uint8_t> eKeyPair;
+ ASSERT_TRUE(c->createEphemeralKeyPair(&eKeyPair).isOk());
+ optional<vector<uint8_t>> ePublicKey = support::ecKeyPairGetPublicKey(eKeyPair);
+
+ // Calculate requestData field and sign it with the reader key.
+ auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ePublicKey.value());
+ ASSERT_TRUE(getXYSuccess);
+ // Instead of include the X and Y coordinates (|ephX| and |ephY|), add NUL bytes instead.
+ vector<uint8_t> nulls(32);
+ cppbor::Map deviceEngagement = cppbor::Map().add("ephX", nulls).add("ephY", nulls);
+ vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
+ vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
+ cppbor::Array sessionTranscript = cppbor::Array()
+ .add(cppbor::Semantic(24, deviceEngagementBytes))
+ .add(cppbor::Semantic(24, eReaderPubBytes));
+ vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
+
+ vector<uint8_t> itemsRequestBytes;
+ itemsRequestBytes =
+ cppbor::Map("nameSpaces",
+ cppbor::Map().add("ns", cppbor::Map()
+ .add("Accessible by A", false)
+ .add("Accessible by A or B", false)
+ .add("Accessible by B", false)
+ .add("Accessible by C", false)
+ .add("Accessible by None", false)))
+ .encode();
+ vector<uint8_t> dataToSign = cppbor::Array()
+ .add("ReaderAuthentication")
+ .add(sessionTranscript.clone())
+ .add(cppbor::Semantic(24, itemsRequestBytes))
+ .encode();
+
+ vector<vector<uint8_t>> readerCertChain = {cert_reader_SelfSigned_};
+ optional<vector<uint8_t>> readerSignature =
+ support::coseSignEcDsa(readerPrivateKey_, // private key for reader
+ {}, // content
+ dataToSign, // detached content
+ support::certificateChainJoin(readerCertChain));
+ ASSERT_TRUE(readerSignature);
+
+ // Generate the key that will be used to sign AuthenticatedData.
+ vector<uint8_t> signingKeyBlob;
+ Certificate signingKeyCertificate;
+ ASSERT_TRUE(c->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+
+ RequestNamespace rns;
+ rns.namespaceName = "ns";
+ rns.items.push_back(buildRequestDataItem("Accessible by A", 1, {0}));
+ rns.items.push_back(buildRequestDataItem("Accessible by A or B", 1, {0, 1}));
+ rns.items.push_back(buildRequestDataItem("Accessible by B", 1, {1}));
+ rns.items.push_back(buildRequestDataItem("Accessible by C", 1, {2}));
+ rns.items.push_back(buildRequestDataItem("Accessible by All", 1, {3}));
+ rns.items.push_back(buildRequestDataItem("Accessible by None", 1, {}));
+ // OK to fail, not available in v1 HAL
+ c->setRequestedNamespaces({rns}).isOk();
+
+ // It doesn't matter since no user auth is needed in this particular test,
+ // but for good measure, clear out the tokens we pass to the HAL.
+ HardwareAuthToken authToken;
+ VerificationToken verificationToken;
+ authToken.challenge = 0;
+ authToken.userId = 0;
+ authToken.authenticatorId = 0;
+ authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
+ authToken.timestamp.milliSeconds = 0;
+ authToken.mac.clear();
+ verificationToken.challenge = 0;
+ verificationToken.timestamp.milliSeconds = 0;
+ verificationToken.securityLevel =
+ ::android::hardware::keymaster::SecurityLevel::TRUSTED_ENVIRONMENT;
+ verificationToken.mac.clear();
+ // OK to fail, not available in v1 HAL
+ c->setVerificationToken(verificationToken);
+
+ // Finally check that STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND is returned.
+ // This proves that the TA checked for X and Y coordinatets and didn't find
+ // them.
+ Status status = c->startRetrieval(
+ {sacp0_, sacp1_, sacp2_, sacp3_}, authToken, itemsRequestBytes, signingKeyBlob,
+ sessionTranscriptBytes, readerSignature.value(), {6 /* numDataElementsPerNamespace */});
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+ ASSERT_EQ(IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ status.serviceSpecificErrorCode());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Identity, ReaderAuthTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/UserAuthTests.cpp b/identity/aidl/vts/UserAuthTests.cpp
new file mode 100644
index 0000000..5b4c8f1
--- /dev/null
+++ b/identity/aidl/vts/UserAuthTests.cpp
@@ -0,0 +1,473 @@
+/*
+ * 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 "UserAuthTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "VtsIdentityTestUtils.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class UserAuthTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(GetParam().c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ }
+
+ void provisionData();
+ void setupRetrieveData();
+ pair<HardwareAuthToken, VerificationToken> mintTokens(uint64_t challengeForAuthToken,
+ int64_t ageOfAuthTokenMilliSeconds);
+ void retrieveData(HardwareAuthToken authToken, VerificationToken verificationToken,
+ bool expectSuccess, bool useSessionTranscript);
+
+ // Set by provisionData
+ SecureAccessControlProfile sacp0_;
+ SecureAccessControlProfile sacp1_;
+ SecureAccessControlProfile sacp2_;
+
+ vector<uint8_t> encContentUserAuthPerSession_;
+ vector<uint8_t> encContentUserAuthTimeout_;
+ vector<uint8_t> encContentAccessibleByAll_;
+ vector<uint8_t> encContentAccessibleByNone_;
+
+ vector<uint8_t> credentialData_;
+
+ // Set by setupRetrieveData().
+ int64_t authChallenge_;
+ cppbor::Map sessionTranscript_;
+ sp<IIdentityCredential> credential_;
+
+ // Set by retrieveData()
+ bool canGetUserAuthPerSession_;
+ bool canGetUserAuthTimeout_;
+ bool canGetAccessibleByAll_;
+ bool canGetAccessibleByNone_;
+
+ sp<IIdentityCredentialStore> credentialStore_;
+};
+
+void UserAuthTests::provisionData() {
+ string docType = "org.iso.18013-5.2019.mdl";
+ bool testCredential = true;
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ size_t proofOfProvisioningSize = 381;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(3 /* numAccessControlProfiles */,
+ {4} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: user auth every session (timeout = 0)
+ ASSERT_TRUE(wc->addAccessControlProfile(0, {}, true, 0, 65 /* secureUserId */, &sacp0_).isOk());
+
+ // Access control profile 1: user auth, 60 seconds timeout
+ ASSERT_TRUE(
+ wc->addAccessControlProfile(1, {}, true, 60000, 65 /* secureUserId */, &sacp1_).isOk());
+
+ // Access control profile 2: open access
+ ASSERT_TRUE(wc->addAccessControlProfile(2, {}, false, 0, 0, &sacp2_).isOk());
+
+ // Data Element: "UserAuth Per Session"
+ ASSERT_TRUE(wc->beginAddEntry({0}, "ns", "UserAuth Per Session", 1).isOk());
+ ASSERT_TRUE(wc->addEntryValue({9}, &encContentUserAuthPerSession_).isOk());
+
+ // Data Element: "UserAuth Timeout"
+ ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "UserAuth Timeout", 1).isOk());
+ ASSERT_TRUE(wc->addEntryValue({9}, &encContentUserAuthTimeout_).isOk());
+
+ // Data Element: "Accessible by All"
+ ASSERT_TRUE(wc->beginAddEntry({2}, "ns", "Accessible by All", 1).isOk());
+ ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByAll_).isOk());
+
+ // Data Element: "Accessible by None"
+ ASSERT_TRUE(wc->beginAddEntry({}, "ns", "Accessible by None", 1).isOk());
+ ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByNone_).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+}
+
+// From ReaderAuthTest.cpp - TODO: consolidate with VtsIdentityTestUtils.h
+pair<vector<uint8_t>, vector<uint8_t>> generateReaderKey();
+vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey,
+ const vector<uint8_t>& signingKey);
+RequestDataItem buildRequestDataItem(const string& name, size_t size,
+ vector<int32_t> accessControlProfileIds);
+
+cppbor::Map calcSessionTranscript(const vector<uint8_t>& ePublicKey) {
+ auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ePublicKey);
+ cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
+ vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
+ vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
+ // Let SessionTranscript be a map here (it's an array in EndToEndTest) just
+ // to check that the implementation can deal with either.
+ cppbor::Map sessionTranscript;
+ sessionTranscript.add(42, cppbor::Semantic(24, deviceEngagementBytes));
+ sessionTranscript.add(43, cppbor::Semantic(24, eReaderPubBytes));
+ return sessionTranscript;
+}
+
+void UserAuthTests::setupRetrieveData() {
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData_, &credential_)
+ .isOk());
+
+ optional<vector<uint8_t>> readerEKeyPair = support::createEcKeyPair();
+ optional<vector<uint8_t>> readerEPublicKey =
+ support::ecKeyPairGetPublicKey(readerEKeyPair.value());
+ ASSERT_TRUE(credential_->setReaderEphemeralPublicKey(readerEPublicKey.value()).isOk());
+
+ vector<uint8_t> eKeyPair;
+ ASSERT_TRUE(credential_->createEphemeralKeyPair(&eKeyPair).isOk());
+ optional<vector<uint8_t>> ePublicKey = support::ecKeyPairGetPublicKey(eKeyPair);
+ sessionTranscript_ = calcSessionTranscript(ePublicKey.value());
+
+ Status status = credential_->createAuthChallenge(&authChallenge_);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+}
+
+void UserAuthTests::retrieveData(HardwareAuthToken authToken, VerificationToken verificationToken,
+ bool expectSuccess, bool useSessionTranscript) {
+ canGetUserAuthPerSession_ = false;
+ canGetUserAuthTimeout_ = false;
+ canGetAccessibleByAll_ = false;
+ canGetAccessibleByNone_ = false;
+
+ vector<uint8_t> itemsRequestBytes;
+ vector<uint8_t> sessionTranscriptBytes;
+ if (useSessionTranscript) {
+ sessionTranscriptBytes = sessionTranscript_.encode();
+
+ itemsRequestBytes =
+ cppbor::Map("nameSpaces",
+ cppbor::Map().add("ns", cppbor::Map()
+ .add("UserAuth Per Session", false)
+ .add("UserAuth Timeout", false)
+ .add("Accessible by All", false)
+ .add("Accessible by None", false)))
+ .encode();
+ vector<uint8_t> dataToSign = cppbor::Array()
+ .add("ReaderAuthentication")
+ .add(sessionTranscript_.clone())
+ .add(cppbor::Semantic(24, itemsRequestBytes))
+ .encode();
+ }
+
+ // Generate the key that will be used to sign AuthenticatedData.
+ vector<uint8_t> signingKeyBlob;
+ Certificate signingKeyCertificate;
+ ASSERT_TRUE(
+ credential_->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+
+ RequestNamespace rns;
+ rns.namespaceName = "ns";
+ rns.items.push_back(buildRequestDataItem("UserAuth Per Session", 1, {0}));
+ rns.items.push_back(buildRequestDataItem("UserAuth Timeout", 1, {1}));
+ rns.items.push_back(buildRequestDataItem("Accessible by All", 1, {2}));
+ rns.items.push_back(buildRequestDataItem("Accessible by None", 1, {}));
+ // OK to fail, not available in v1 HAL
+ credential_->setRequestedNamespaces({rns}).isOk();
+
+ // OK to fail, not available in v1 HAL
+ credential_->setVerificationToken(verificationToken);
+
+ Status status = credential_->startRetrieval({sacp0_, sacp1_, sacp2_}, authToken,
+ itemsRequestBytes, signingKeyBlob,
+ sessionTranscriptBytes, {} /* readerSignature */,
+ {4 /* numDataElementsPerNamespace */});
+ if (expectSuccess) {
+ ASSERT_TRUE(status.isOk());
+ } else {
+ ASSERT_FALSE(status.isOk());
+ return;
+ }
+
+ vector<uint8_t> decrypted;
+
+ status = credential_->startRetrieveEntryValue("ns", "UserAuth Per Session", 1, {0});
+ if (status.isOk()) {
+ canGetUserAuthPerSession_ = true;
+ ASSERT_TRUE(
+ credential_->retrieveEntryValue(encContentUserAuthPerSession_, &decrypted).isOk());
+ }
+
+ status = credential_->startRetrieveEntryValue("ns", "UserAuth Timeout", 1, {1});
+ if (status.isOk()) {
+ canGetUserAuthTimeout_ = true;
+ ASSERT_TRUE(credential_->retrieveEntryValue(encContentUserAuthTimeout_, &decrypted).isOk());
+ }
+
+ status = credential_->startRetrieveEntryValue("ns", "Accessible by All", 1, {2});
+ if (status.isOk()) {
+ canGetAccessibleByAll_ = true;
+ ASSERT_TRUE(credential_->retrieveEntryValue(encContentAccessibleByAll_, &decrypted).isOk());
+ }
+
+ status = credential_->startRetrieveEntryValue("ns", "Accessible by None", 1, {});
+ if (status.isOk()) {
+ canGetAccessibleByNone_ = true;
+ ASSERT_TRUE(
+ credential_->retrieveEntryValue(encContentAccessibleByNone_, &decrypted).isOk());
+ }
+
+ vector<uint8_t> mac;
+ vector<uint8_t> deviceNameSpaces;
+ ASSERT_TRUE(credential_->finishRetrieval(&mac, &deviceNameSpaces).isOk());
+}
+
+pair<HardwareAuthToken, VerificationToken> UserAuthTests::mintTokens(
+ uint64_t challengeForAuthToken, int64_t ageOfAuthTokenMilliSeconds) {
+ HardwareAuthToken authToken;
+ VerificationToken verificationToken;
+
+ uint64_t epochMilliseconds = 1000ULL * 1000ULL * 1000ULL * 1000ULL;
+
+ authToken.challenge = challengeForAuthToken;
+ authToken.userId = 65;
+ authToken.authenticatorId = 0;
+ authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
+ authToken.timestamp.milliSeconds = epochMilliseconds - ageOfAuthTokenMilliSeconds;
+ authToken.mac.clear();
+ verificationToken.challenge = authChallenge_;
+ verificationToken.timestamp.milliSeconds = epochMilliseconds;
+ verificationToken.securityLevel =
+ ::android::hardware::keymaster::SecurityLevel::TRUSTED_ENVIRONMENT;
+ verificationToken.mac.clear();
+ return make_pair(authToken, verificationToken);
+}
+
+TEST_P(UserAuthTests, GoodChallenge) {
+ provisionData();
+ setupRetrieveData();
+ auto [authToken, verificationToken] = mintTokens(authChallenge_, // challengeForAuthToken
+ 0); // ageOfAuthTokenMilliSeconds
+ retrieveData(authToken, verificationToken, true /* expectSuccess */,
+ true /* useSessionTranscript */);
+ EXPECT_TRUE(canGetUserAuthPerSession_);
+ EXPECT_TRUE(canGetUserAuthTimeout_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, OtherChallenge) {
+ provisionData();
+ setupRetrieveData();
+ uint64_t otherChallenge = authChallenge_ ^ 0x12345678;
+ auto [authToken, verificationToken] = mintTokens(otherChallenge, // challengeForAuthToken
+ 0); // ageOfAuthTokenMilliSeconds
+ retrieveData(authToken, verificationToken, true /* expectSuccess */,
+ true /* useSessionTranscript */);
+ EXPECT_FALSE(canGetUserAuthPerSession_);
+ EXPECT_TRUE(canGetUserAuthTimeout_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, NoChallenge) {
+ provisionData();
+ setupRetrieveData();
+ auto [authToken, verificationToken] = mintTokens(0, // challengeForAuthToken
+ 0); // ageOfAuthTokenMilliSeconds
+ retrieveData(authToken, verificationToken, true /* expectSuccess */,
+ true /* useSessionTranscript */);
+ EXPECT_FALSE(canGetUserAuthPerSession_);
+ EXPECT_TRUE(canGetUserAuthTimeout_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, AuthTokenAgeZero) {
+ provisionData();
+ setupRetrieveData();
+ auto [authToken, verificationToken] = mintTokens(0, // challengeForAuthToken
+ 0); // ageOfAuthTokenMilliSeconds
+ retrieveData(authToken, verificationToken, true /* expectSuccess */,
+ true /* useSessionTranscript */);
+ EXPECT_FALSE(canGetUserAuthPerSession_);
+ EXPECT_TRUE(canGetUserAuthTimeout_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, AuthTokenFromTheFuture) {
+ provisionData();
+ setupRetrieveData();
+ auto [authToken, verificationToken] = mintTokens(0, // challengeForAuthToken
+ -1 * 1000); // ageOfAuthTokenMilliSeconds
+ retrieveData(authToken, verificationToken, true /* expectSuccess */,
+ true /* useSessionTranscript */);
+ EXPECT_FALSE(canGetUserAuthPerSession_);
+ EXPECT_FALSE(canGetUserAuthTimeout_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, AuthTokenInsideTimeout) {
+ provisionData();
+ setupRetrieveData();
+ auto [authToken, verificationToken] = mintTokens(0, // challengeForAuthToken
+ 30 * 1000); // ageOfAuthTokenMilliSeconds
+ retrieveData(authToken, verificationToken, true /* expectSuccess */,
+ true /* useSessionTranscript */);
+ EXPECT_FALSE(canGetUserAuthPerSession_);
+ EXPECT_TRUE(canGetUserAuthTimeout_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+TEST_P(UserAuthTests, AuthTokenOutsideTimeout) {
+ provisionData();
+ setupRetrieveData();
+ auto [authToken, verificationToken] = mintTokens(0, // challengeForAuthToken
+ 61 * 1000); // ageOfAuthTokenMilliSeconds
+ retrieveData(authToken, verificationToken, true /* expectSuccess */,
+ true /* useSessionTranscript */);
+ EXPECT_FALSE(canGetUserAuthPerSession_);
+ EXPECT_FALSE(canGetUserAuthTimeout_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+// The API works even when there's no SessionTranscript / itemsRequest.
+// Verify that.
+TEST_P(UserAuthTests, NoSessionTranscript) {
+ provisionData();
+ setupRetrieveData();
+ auto [authToken, verificationToken] = mintTokens(0, // challengeForAuthToken
+ 1 * 1000); // ageOfAuthTokenMilliSeconds
+ retrieveData(authToken, verificationToken, true /* expectSuccess */,
+ false /* useSessionTranscript */);
+ EXPECT_FALSE(canGetUserAuthPerSession_);
+ EXPECT_TRUE(canGetUserAuthTimeout_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+// This test verifies that it's possible to do multiple requests as long
+// as the sessionTranscript doesn't change.
+//
+TEST_P(UserAuthTests, MultipleRequestsSameSessionTranscript) {
+ provisionData();
+ setupRetrieveData();
+
+ // First we try with a stale authToken
+ //
+ auto [authToken, verificationToken] = mintTokens(0, // challengeForAuthToken
+ 61 * 1000); // ageOfAuthTokenMilliSeconds
+ retrieveData(authToken, verificationToken, true /* expectSuccess */,
+ true /* useSessionTranscript */);
+ EXPECT_FALSE(canGetUserAuthPerSession_);
+ EXPECT_FALSE(canGetUserAuthTimeout_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+
+ // Then we get a new authToken and try again.
+ tie(authToken, verificationToken) = mintTokens(0, // challengeForAuthToken
+ 5 * 1000); // ageOfAuthTokenMilliSeconds
+ retrieveData(authToken, verificationToken, true /* expectSuccess */,
+ true /* useSessionTranscript */);
+ EXPECT_FALSE(canGetUserAuthPerSession_);
+ EXPECT_TRUE(canGetUserAuthTimeout_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+}
+
+// Like MultipleRequestsSameSessionTranscript but we change the sessionTranscript
+// between the two calls. This test verifies that change is detected and the
+// second request fails.
+//
+TEST_P(UserAuthTests, MultipleRequestsSessionTranscriptChanges) {
+ provisionData();
+ setupRetrieveData();
+
+ // First we try with a stale authToken
+ //
+ auto [authToken, verificationToken] = mintTokens(0, // challengeForAuthToken
+ 61 * 1000); // ageOfAuthTokenMilliSeconds
+ retrieveData(authToken, verificationToken, true /* expectSuccess */,
+ true /* useSessionTranscript */);
+ EXPECT_FALSE(canGetUserAuthPerSession_);
+ EXPECT_FALSE(canGetUserAuthTimeout_);
+ EXPECT_TRUE(canGetAccessibleByAll_);
+ EXPECT_FALSE(canGetAccessibleByNone_);
+
+ // Then we get a new authToken and try again.
+ tie(authToken, verificationToken) = mintTokens(0, // challengeForAuthToken
+ 5 * 1000); // ageOfAuthTokenMilliSeconds
+
+ // Change sessionTranscript...
+ optional<vector<uint8_t>> eKeyPairNew = support::createEcKeyPair();
+ optional<vector<uint8_t>> ePublicKeyNew = support::ecKeyPairGetPublicKey(eKeyPairNew.value());
+ sessionTranscript_ = calcSessionTranscript(ePublicKeyNew.value());
+
+ // ... and expect failure.
+ retrieveData(authToken, verificationToken, false /* expectSuccess */,
+ true /* useSessionTranscript */);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Identity, UserAuthTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/VtsAttestationTests.cpp b/identity/aidl/vts/VtsAttestationTests.cpp
index 00b5893..c7cdfc7 100644
--- a/identity/aidl/vts/VtsAttestationTests.cpp
+++ b/identity/aidl/vts/VtsAttestationTests.cpp
@@ -61,51 +61,6 @@
sp<IIdentityCredentialStore> credentialStore_;
};
-TEST_P(VtsAttestationTests, verifyAttestationWithEmptyChallengeEmptyId) {
- Status result;
-
- HardwareInformation hwInfo;
- ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
- sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
-
- vector<uint8_t> attestationChallenge;
- vector<Certificate> attestationCertificate;
- vector<uint8_t> attestationApplicationId = {};
- result = writableCredential->getAttestationCertificate(
- attestationApplicationId, attestationChallenge, &attestationCertificate);
-
- ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
- << endl;
-
- EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
- attestationApplicationId, hwInfo));
-}
-
-TEST_P(VtsAttestationTests, verifyAttestationWithEmptyChallengeNonemptyId) {
- Status result;
-
- HardwareInformation hwInfo;
- ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
- sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_));
-
- vector<uint8_t> attestationChallenge;
- vector<Certificate> attestationCertificate;
- string applicationId = "Attestation Verification";
- vector<uint8_t> attestationApplicationId = {applicationId.begin(), applicationId.end()};
-
- result = writableCredential->getAttestationCertificate(
- attestationApplicationId, attestationChallenge, &attestationCertificate);
-
- ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
- << endl;
- EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
- attestationApplicationId, hwInfo));
-}
-
TEST_P(VtsAttestationTests, verifyAttestationWithNonemptyChallengeEmptyId) {
Status result;
diff --git a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
index 464ab0c..a0c4416 100644
--- a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
+++ b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
@@ -27,15 +27,18 @@
#include <gtest/gtest.h>
#include <future>
#include <map>
+#include <tuple>
#include "VtsIdentityTestUtils.h"
namespace android::hardware::identity {
using std::endl;
+using std::make_tuple;
using std::map;
using std::optional;
using std::string;
+using std::tuple;
using std::vector;
using ::android::sp;
@@ -66,6 +69,61 @@
ASSERT_GE(info.dataChunkSize, 256);
}
+tuple<bool, string, vector<uint8_t>, vector<uint8_t>> extractFromTestCredentialData(
+ const vector<uint8_t>& credentialData) {
+ string docType;
+ vector<uint8_t> storageKey;
+ vector<uint8_t> credentialPrivKey;
+
+ auto [item, _, message] = cppbor::parse(credentialData);
+ if (item == nullptr) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey);
+ }
+
+ const cppbor::Array* arrayItem = item->asArray();
+ if (arrayItem == nullptr || arrayItem->size() != 3) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey);
+ }
+
+ const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+ const cppbor::Bool* testCredentialItem =
+ ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
+ : nullptr);
+ const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+ if (docTypeItem == nullptr || testCredentialItem == nullptr ||
+ encryptedCredentialKeysItem == nullptr) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey);
+ }
+
+ docType = docTypeItem->value();
+
+ vector<uint8_t> hardwareBoundKey = support::getTestHardwareBoundKey();
+ const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
+ const vector<uint8_t> docTypeVec(docType.begin(), docType.end());
+ optional<vector<uint8_t>> decryptedCredentialKeys =
+ support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
+ if (!decryptedCredentialKeys) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey);
+ }
+
+ auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
+ if (dckItem == nullptr) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey);
+ }
+ const cppbor::Array* dckArrayItem = dckItem->asArray();
+ if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey);
+ }
+ const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
+ const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
+ if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey);
+ }
+ storageKey = storageKeyItem->value();
+ credentialPrivKey = credentialPrivKeyItem->value();
+ return make_tuple(true, docType, storageKey, credentialPrivKey);
+}
+
TEST_P(IdentityAidl, createAndRetrieveCredential) {
// First, generate a key-pair for the reader since its public key will be
// part of the request data.
@@ -155,6 +213,7 @@
writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
.isOk());
+ // Validate the proofOfProvisioning which was returned
optional<vector<uint8_t>> proofOfProvisioning =
support::coseSignGetPayload(proofOfProvisioningSignature);
ASSERT_TRUE(proofOfProvisioning);
@@ -215,6 +274,22 @@
credentialPubKey.value()));
writableCredential = nullptr;
+ // Extract doctype, storage key, and credentialPrivKey from credentialData... this works
+ // only because we asked for a test-credential meaning that the HBK is all zeroes.
+ auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey] =
+ extractFromTestCredentialData(credentialData);
+ ASSERT_TRUE(exSuccess);
+ ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
+ // ... check that the public key derived from the private key matches what was
+ // in the certificate.
+ optional<vector<uint8_t>> exCredentialKeyPair =
+ support::ecPrivateKeyToKeyPair(exCredentialPrivKey);
+ ASSERT_TRUE(exCredentialKeyPair);
+ optional<vector<uint8_t>> exCredentialPubKey =
+ support::ecKeyPairGetPublicKey(exCredentialKeyPair.value());
+ ASSERT_TRUE(exCredentialPubKey);
+ ASSERT_EQ(exCredentialPubKey.value(), credentialPubKey.value());
+
// Now that the credential has been provisioned, read it back and check the
// correct data is returned.
sp<IIdentityCredential> credential;
@@ -287,6 +362,24 @@
vector<uint8_t> signingKeyBlob;
Certificate signingKeyCertificate;
ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+ optional<vector<uint8_t>> signingPubKey =
+ support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
+ EXPECT_TRUE(signingPubKey);
+
+ // Since we're using a test-credential we know storageKey meaning we can get the
+ // private key. Do this, derive the public key from it, and check this matches what
+ // is in the certificate...
+ const vector<uint8_t> exDocTypeVec(exDocType.begin(), exDocType.end());
+ optional<vector<uint8_t>> exSigningPrivKey =
+ support::decryptAes128Gcm(exStorageKey, signingKeyBlob, exDocTypeVec);
+ ASSERT_TRUE(exSigningPrivKey);
+ optional<vector<uint8_t>> exSigningKeyPair =
+ support::ecPrivateKeyToKeyPair(exSigningPrivKey.value());
+ ASSERT_TRUE(exSigningKeyPair);
+ optional<vector<uint8_t>> exSigningPubKey =
+ support::ecKeyPairGetPublicKey(exSigningKeyPair.value());
+ ASSERT_TRUE(exSigningPubKey);
+ ASSERT_EQ(exSigningPubKey.value(), signingPubKey.value());
vector<RequestNamespace> requestedNamespaces = test_utils::buildRequestNamespaces(testEntries);
// OK to fail, not available in v1 HAL
@@ -316,6 +409,9 @@
content.insert(content.end(), chunk.begin(), chunk.end());
}
EXPECT_EQ(content, entry.valueCbor);
+
+ // TODO: also use |exStorageKey| to decrypt data and check it's the same as whatt
+ // the HAL returns...
}
vector<uint8_t> mac;
@@ -346,15 +442,12 @@
deviceAuthentication.add(docType);
deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
- optional<vector<uint8_t>> signingPublicKey =
- support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
- EXPECT_TRUE(signingPublicKey);
// Derive the key used for MACing.
optional<vector<uint8_t>> readerEphemeralPrivateKey =
support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
optional<vector<uint8_t>> sharedSecret =
- support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
+ support::ecdh(signingPubKey.value(), readerEphemeralPrivateKey.value());
ASSERT_TRUE(sharedSecret);
vector<uint8_t> salt = {0x00};
vector<uint8_t> info = {};
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index 8b0c050..b572b0f 100644
--- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -69,11 +69,10 @@
result = writableCredential->getAttestationCertificate(
attestationApplicationId, attestationChallenge, &attestationCertificate);
- EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
- << endl;
-
- EXPECT_TRUE(test_utils::validateAttestationCertificate(
- attestationCertificate, attestationChallenge, attestationApplicationId, hwInfo));
+ EXPECT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+ << endl;
+ EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
+ EXPECT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode());
}
TEST_P(IdentityCredentialTests, verifyAttestationSuccessWithChallenge) {
@@ -130,6 +129,7 @@
// First call should go through
const vector<int32_t> entryCounts = {2, 4};
+ writableCredential->setExpectedProofOfProvisioningSize(123456);
result = writableCredential->startPersonalization(5, entryCounts);
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
@@ -151,18 +151,8 @@
// Verify minimal number of profile count and entry count
const vector<int32_t> entryCounts = {1, 1};
- writableCredential->startPersonalization(1, entryCounts);
- EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
- << endl;
-}
-
-TEST_P(IdentityCredentialTests, verifyStartPersonalizationZero) {
- Status result;
- sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
-
- const vector<int32_t> entryCounts = {0};
- writableCredential->startPersonalization(0, entryCounts);
+ writableCredential->setExpectedProofOfProvisioningSize(123456);
+ result = writableCredential->startPersonalization(1, entryCounts);
EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
}
@@ -174,7 +164,8 @@
// Verify minimal number of profile count and entry count
const vector<int32_t> entryCounts = {1};
- writableCredential->startPersonalization(1, entryCounts);
+ writableCredential->setExpectedProofOfProvisioningSize(123456);
+ result = writableCredential->startPersonalization(1, entryCounts);
EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
}
@@ -186,7 +177,8 @@
// Verify set a large number of profile count and entry count is ok
const vector<int32_t> entryCounts = {3000};
- writableCredential->startPersonalization(3500, entryCounts);
+ writableCredential->setExpectedProofOfProvisioningSize(123456);
+ result = writableCredential->startPersonalization(25, entryCounts);
EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
}
@@ -198,7 +190,8 @@
// Enter mismatched entry and profile numbers
const vector<int32_t> entryCounts = {5, 6};
- writableCredential->startPersonalization(5, entryCounts);
+ writableCredential->setExpectedProofOfProvisioningSize(123456);
+ result = writableCredential->startPersonalization(5, entryCounts);
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
@@ -234,7 +227,8 @@
ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
const vector<int32_t> entryCounts = {3, 6};
- writableCredential->startPersonalization(3, entryCounts);
+ writableCredential->setExpectedProofOfProvisioningSize(123456);
+ result = writableCredential->startPersonalization(3, entryCounts);
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
@@ -251,9 +245,10 @@
SecureAccessControlProfile profile;
Certificate cert;
cert.encodedCertificate = testProfile.readerCertificate;
+ int64_t secureUserId = testProfile.userAuthenticationRequired ? 66 : 0;
result = writableCredential->addAccessControlProfile(
testProfile.id, cert, testProfile.userAuthenticationRequired,
- testProfile.timeoutMillis, 0, &profile);
+ testProfile.timeoutMillis, secureUserId, &profile);
if (expectOk) {
expectOk = false;
@@ -554,7 +549,7 @@
;
// OK to fail, not available in v1 HAL
writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize);
- writableCredential->startPersonalization(3, entryCounts);
+ result = writableCredential->startPersonalization(3, entryCounts);
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
@@ -608,7 +603,8 @@
// before "Image" and 2 after image, which is not correct. All of same name
// space should occur together. Let's see if this fails.
const vector<int32_t> entryCounts = {2u, 1u, 2u};
- writableCredential->startPersonalization(3, entryCounts);
+ writableCredential->setExpectedProofOfProvisioningSize(123456);
+ result = writableCredential->startPersonalization(3, entryCounts);
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
@@ -674,6 +670,7 @@
ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
const vector<int32_t> entryCounts = {1};
+ writableCredential->setExpectedProofOfProvisioningSize(123456);
Status result = writableCredential->startPersonalization(1, entryCounts);
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/VtsIdentityTestUtils.cpp
index aaebcbe..b6ed80f 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.cpp
+++ b/identity/aidl/vts/VtsIdentityTestUtils.cpp
@@ -96,9 +96,10 @@
SecureAccessControlProfile profile;
Certificate cert;
cert.encodedCertificate = testProfile.readerCertificate;
+ int64_t secureUserId = testProfile.userAuthenticationRequired ? 66 : 0;
result = writableCredential->addAccessControlProfile(
testProfile.id, cert, testProfile.userAuthenticationRequired,
- testProfile.timeoutMillis, 0, &profile);
+ testProfile.timeoutMillis, secureUserId, &profile);
// Don't use assert so all errors can be outputed. Then return
// instead of exit even on errors so caller can decide.
diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
index 507e914..0f27a72 100644
--- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
+++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
@@ -134,6 +134,11 @@
//
optional<vector<uint8_t>> ecKeyPairGetPrivateKey(const vector<uint8_t>& keyPair);
+// Creates a PKCS#8 encoded key-pair from a private key (which must be uncompressed,
+// e.g. 32 bytes). The public key is derived from the given private key..
+//
+optional<vector<uint8_t>> ecPrivateKeyToKeyPair(const vector<uint8_t>& privateKey);
+
// For an EC key |keyPair| encoded in PKCS#8 format, creates a PKCS#12 structure
// with the key-pair (not using a password to encrypt the data). The public key
// in the created structure is included as a certificate, using the given fields
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index dc49ddc..e9d5d6c 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -1047,6 +1047,42 @@
return privateKey;
}
+optional<vector<uint8_t>> ecPrivateKeyToKeyPair(const vector<uint8_t>& privateKey) {
+ auto bn = BIGNUM_Ptr(BN_bin2bn(privateKey.data(), privateKey.size(), nullptr));
+ if (bn.get() == nullptr) {
+ LOG(ERROR) << "Error creating BIGNUM";
+ return {};
+ }
+
+ auto ecKey = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+ if (EC_KEY_set_private_key(ecKey.get(), bn.get()) != 1) {
+ LOG(ERROR) << "Error setting private key from BIGNUM";
+ return {};
+ }
+
+ auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
+ if (pkey.get() == nullptr) {
+ LOG(ERROR) << "Memory allocation failed";
+ return {};
+ }
+
+ if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) {
+ LOG(ERROR) << "Error getting private key";
+ return {};
+ }
+
+ int size = i2d_PrivateKey(pkey.get(), nullptr);
+ if (size == 0) {
+ LOG(ERROR) << "Error generating public key encoding";
+ return {};
+ }
+ vector<uint8_t> keyPair;
+ keyPair.resize(size);
+ unsigned char* p = keyPair.data();
+ i2d_PrivateKey(pkey.get(), &p);
+ return keyPair;
+}
+
optional<vector<uint8_t>> ecKeyPairGetPkcs12(const vector<uint8_t>& keyPair, const string& name,
const string& serialDecimal, const string& issuer,
const string& subject, time_t validityNotBefore,
diff --git a/neuralnetworks/1.0/vts/functional/Android.bp b/neuralnetworks/1.0/vts/functional/Android.bp
index 87e8519..d802911 100644
--- a/neuralnetworks/1.0/vts/functional/Android.bp
+++ b/neuralnetworks/1.0/vts/functional/Android.bp
@@ -62,11 +62,12 @@
defaults: ["neuralnetworks_vts_functional_defaults"],
srcs: [
"BasicTests.cpp",
+ "GeneratedTestHarness.cpp",
"TestAssertions.cpp",
+ "TestMain.cpp",
"ValidateModel.cpp",
"ValidateRequest.cpp",
"VtsHalNeuralnetworks.cpp",
- "GeneratedTestHarness.cpp",
],
shared_libs: [
"libfmq",
diff --git a/neuralnetworks/1.0/vts/functional/AndroidTest.xml b/neuralnetworks/1.0/vts/functional/AndroidTest.xml
index 54e6e91..13671f9 100644
--- a/neuralnetworks/1.0/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.0/vts/functional/AndroidTest.xml
@@ -26,10 +26,6 @@
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
- <!-- b/155577050, temporarily disable the failing tests.
- Must be deleted after corresponding driver issues are fixed.
- -->
- <option name="native-test-flag" value="--gtest_filter=-*Validation*:*CycleTest*:*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*" />
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsHalNeuralnetworksV1_0TargetTest" />
</test>
diff --git a/neuralnetworks/1.0/vts/functional/TestMain.cpp b/neuralnetworks/1.0/vts/functional/TestMain.cpp
new file mode 100644
index 0000000..6bf4e5f
--- /dev/null
+++ b/neuralnetworks/1.0/vts/functional/TestMain.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <gtest/gtest.h>
+#include "1.0/LogTestCaseToLogcat.h"
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ testing::UnitTest::GetInstance()->listeners().Append(
+ new android::hardware::neuralnetworks::LogTestCaseToLogcat());
+ return RUN_ALL_TESTS();
+}
diff --git a/neuralnetworks/1.0/vts/functional/include/1.0/LogTestCaseToLogcat.h b/neuralnetworks/1.0/vts/functional/include/1.0/LogTestCaseToLogcat.h
new file mode 100644
index 0000000..f1413ef
--- /dev/null
+++ b/neuralnetworks/1.0/vts/functional/include/1.0/LogTestCaseToLogcat.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_NEURALNETWORKS_V1_0_LOG_TEST_CASE_TO_LOGCAT_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_V1_0_LOG_TEST_CASE_TO_LOGCAT_H
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+namespace android::hardware::neuralnetworks {
+
+class LogTestCaseToLogcat : public ::testing::EmptyTestEventListener {
+ public:
+ void OnTestStart(const ::testing::TestInfo& test_info) override {
+ LOG(INFO) << "[Test Case] " << test_info.test_suite_name() << "." << test_info.name()
+ << " BEGIN";
+ }
+
+ void OnTestEnd(const ::testing::TestInfo& test_info) override {
+ LOG(INFO) << "[Test Case] " << test_info.test_suite_name() << "." << test_info.name()
+ << " END";
+ }
+};
+
+} // namespace android::hardware::neuralnetworks
+
+#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_0_LOG_TEST_CASE_TO_LOGCAT_H
diff --git a/neuralnetworks/1.1/vts/functional/Android.bp b/neuralnetworks/1.1/vts/functional/Android.bp
index 9afa0af..405548f 100644
--- a/neuralnetworks/1.1/vts/functional/Android.bp
+++ b/neuralnetworks/1.1/vts/functional/Android.bp
@@ -20,6 +20,7 @@
srcs: [
"BasicTests.cpp",
"TestAssertions.cpp",
+ "TestMain.cpp",
"ValidateModel.cpp",
"ValidateRequest.cpp",
"VtsHalNeuralnetworks.cpp",
diff --git a/neuralnetworks/1.1/vts/functional/AndroidTest.xml b/neuralnetworks/1.1/vts/functional/AndroidTest.xml
index a6f812f..cfde60c 100644
--- a/neuralnetworks/1.1/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.1/vts/functional/AndroidTest.xml
@@ -26,10 +26,6 @@
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
- <!-- b/155577050, temporarily disable the failing tests.
- Must be deleted after corresponding driver issues are fixed.
- -->
- <option name="native-test-flag" value="--gtest_filter=-*Validation*:*CycleTest*:*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*" />
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsHalNeuralnetworksV1_1TargetTest" />
</test>
diff --git a/neuralnetworks/1.1/vts/functional/TestMain.cpp b/neuralnetworks/1.1/vts/functional/TestMain.cpp
new file mode 100644
index 0000000..6bf4e5f
--- /dev/null
+++ b/neuralnetworks/1.1/vts/functional/TestMain.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <gtest/gtest.h>
+#include "1.0/LogTestCaseToLogcat.h"
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ testing::UnitTest::GetInstance()->listeners().Append(
+ new android::hardware::neuralnetworks::LogTestCaseToLogcat());
+ return RUN_ALL_TESTS();
+}
diff --git a/neuralnetworks/1.2/vts/functional/Android.bp b/neuralnetworks/1.2/vts/functional/Android.bp
index 182f716..93edca6 100644
--- a/neuralnetworks/1.2/vts/functional/Android.bp
+++ b/neuralnetworks/1.2/vts/functional/Android.bp
@@ -40,6 +40,7 @@
"CompilationCachingTests.cpp",
"GeneratedTestHarness.cpp",
"TestAssertions.cpp",
+ "TestMain.cpp",
"ValidateBurst.cpp",
"ValidateModel.cpp",
"ValidateRequest.cpp",
diff --git a/neuralnetworks/1.2/vts/functional/AndroidTest.xml b/neuralnetworks/1.2/vts/functional/AndroidTest.xml
index adbdf40..3f91618 100644
--- a/neuralnetworks/1.2/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.2/vts/functional/AndroidTest.xml
@@ -26,10 +26,6 @@
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
- <!-- b/155577050, b/155674368, b/153876253, temporarily disable the test.
- Must be deleted after corresponding driver issues are fixed.
- -->
- <option name="native-test-flag" value="--gtest_filter=-*Validation*:*squeeze*_all*_inputs*:*strided_slice*_all*_inputs*:*transpose*_all*_inputs*:*l2_normalization_axis_corner_case*:*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*" />
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsHalNeuralnetworksV1_2TargetTest" />
</test>
diff --git a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
index 449b8f3..16b313a 100644
--- a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
@@ -315,7 +315,8 @@
void saveModelToCache(const Model& model, const hidl_vec<hidl_handle>& modelCache,
const hidl_vec<hidl_handle>& dataCache,
- sp<IPreparedModel>* preparedModel = nullptr) {
+ sp<IPreparedModel>* preparedModel = nullptr,
+ bool allowGeneralFailure = false) {
if (preparedModel != nullptr) *preparedModel = nullptr;
// Launch prepare model.
@@ -329,7 +330,10 @@
// Retrieve prepared model.
preparedModelCallback->wait();
- ASSERT_EQ(preparedModelCallback->getStatus(), ErrorStatus::NONE);
+ const auto prepareCallbackStatus = preparedModelCallback->getStatus();
+ if (!allowGeneralFailure || prepareCallbackStatus != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(prepareCallbackStatus, ErrorStatus::NONE);
+ }
if (preparedModel != nullptr) {
*preparedModel = IPreparedModel::castFrom(preparedModelCallback->getPreparedModel())
.withDefault(nullptr);
@@ -1022,7 +1026,8 @@
// Number of operations in the large test model.
constexpr uint32_t kLargeModelSize = 100;
-constexpr uint32_t kNumIterationsTOCTOU = 100;
+constexpr uint32_t kNumSuccessfulIterationsTOCTOU = 100;
+constexpr uint32_t kMaxNumFailedIterationsTOCTOU = 100;
TEST_P(CompilationCachingTest, SaveToCache_TOCTOU) {
if (!mIsCachingSupported) return;
@@ -1050,18 +1055,30 @@
// Use a different token for modelAdd.
mToken[0]++;
- // This test is probabilistic, so we run it multiple times.
- for (uint32_t i = 0; i < kNumIterationsTOCTOU; i++) {
+ // This test is probabilistic, so we run it multiple times. We allow the compilation to fail
+ // because it is not related to the security aspect of the TOCTOU test. However, we need to have
+ // enough successful iterations to ensure the test coverage.
+ uint32_t numSuccessfulIterations = 0, numFailedIterations = 0;
+ while (numSuccessfulIterations < kNumSuccessfulIterationsTOCTOU) {
// Save the modelAdd compilation to cache.
{
hidl_vec<hidl_handle> modelCache, dataCache;
createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ sp<IPreparedModel> preparedModel = nullptr;
// Spawn a thread to copy the cache content concurrently while saving to cache.
std::thread thread(copyCacheFiles, std::cref(modelCacheMul), std::cref(mModelCache));
- saveModelToCache(modelAdd, modelCache, dataCache);
+ saveModelToCache(modelAdd, modelCache, dataCache, &preparedModel,
+ /*allowGeneralFailure=*/true);
thread.join();
+
+ if (preparedModel == nullptr) {
+ numFailedIterations++;
+ ASSERT_LE(numFailedIterations, kMaxNumFailedIterationsTOCTOU);
+ } else {
+ numSuccessfulIterations++;
+ }
}
// Retrieve preparedModel from cache.
@@ -1112,14 +1129,26 @@
// Use a different token for modelAdd.
mToken[0]++;
- // This test is probabilistic, so we run it multiple times.
- for (uint32_t i = 0; i < kNumIterationsTOCTOU; i++) {
+ // This test is probabilistic, so we run it multiple times. We allow the compilation to fail
+ // because it is not related to the security aspect of the TOCTOU test. However, we need to have
+ // enough successful iterations to ensure the test coverage.
+ uint32_t numSuccessfulIterations = 0, numFailedIterations = 0;
+ while (numSuccessfulIterations < kNumSuccessfulIterationsTOCTOU) {
// Save the modelAdd compilation to cache.
{
hidl_vec<hidl_handle> modelCache, dataCache;
createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
- saveModelToCache(modelAdd, modelCache, dataCache);
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(modelAdd, modelCache, dataCache, &preparedModel,
+ /*allowGeneralFailure=*/true);
+
+ if (preparedModel == nullptr) {
+ numFailedIterations++;
+ ASSERT_LE(numFailedIterations, kMaxNumFailedIterationsTOCTOU);
+ } else {
+ numSuccessfulIterations++;
+ }
}
// Retrieve preparedModel from cache.
diff --git a/neuralnetworks/1.2/vts/functional/TestMain.cpp b/neuralnetworks/1.2/vts/functional/TestMain.cpp
new file mode 100644
index 0000000..6bf4e5f
--- /dev/null
+++ b/neuralnetworks/1.2/vts/functional/TestMain.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <gtest/gtest.h>
+#include "1.0/LogTestCaseToLogcat.h"
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ testing::UnitTest::GetInstance()->listeners().Append(
+ new android::hardware::neuralnetworks::LogTestCaseToLogcat());
+ return RUN_ALL_TESTS();
+}
diff --git a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
index d01336e..c4e2b15 100644
--- a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
@@ -21,6 +21,7 @@
#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
#include <android/hardware/neuralnetworks/1.2/types.h>
#include <gtest/gtest.h>
+#include <vector>
#include "1.0/Utils.h"
#include "1.2/Callbacks.h"
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index 771fc54..b17d445 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -43,6 +43,7 @@
"MemoryDomainTests.cpp",
"QualityOfServiceTests.cpp",
"TestAssertions.cpp",
+ "TestMain.cpp",
"ValidateBurst.cpp",
"ValidateModel.cpp",
"ValidateRequest.cpp",
diff --git a/neuralnetworks/1.3/vts/functional/AndroidTest.xml b/neuralnetworks/1.3/vts/functional/AndroidTest.xml
index 30cff2e..e5acd90 100644
--- a/neuralnetworks/1.3/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.3/vts/functional/AndroidTest.xml
@@ -26,10 +26,6 @@
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
- <!-- b/156691406, b/155577050, b/155674368, b/153876253, temporarily disable the test.
- Must be deleted after corresponding driver issues are fixed.
- -->
- <option name="native-test-flag" value="--gtest_filter=-*Validation*:*DynamicOutputShapeTest*:*FencedComputeTest*:*MemoryDomain*:*QuantizationCouplingTest*:*DeadlineTest*:*resize_*_v1_3*:*squeeze*_all*_inputs*:*strided_slice*_all*_inputs*:*transpose*_all*_inputs*:*l2_normalization_axis_corner_case*:*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*" />
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsHalNeuralnetworksV1_3TargetTest" />
</test>
diff --git a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
index ac18c8f..382fc76 100644
--- a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
@@ -318,7 +318,8 @@
void saveModelToCache(const Model& model, const hidl_vec<hidl_handle>& modelCache,
const hidl_vec<hidl_handle>& dataCache,
- sp<IPreparedModel>* preparedModel = nullptr) {
+ sp<IPreparedModel>* preparedModel = nullptr,
+ bool allowGeneralFailure = false) {
if (preparedModel != nullptr) *preparedModel = nullptr;
// Launch prepare model.
@@ -332,7 +333,10 @@
// Retrieve prepared model.
preparedModelCallback->wait();
- ASSERT_EQ(preparedModelCallback->getStatus(), ErrorStatus::NONE);
+ const auto prepareCallbackStatus = preparedModelCallback->getStatus();
+ if (!allowGeneralFailure || prepareCallbackStatus != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(prepareCallbackStatus, ErrorStatus::NONE);
+ }
if (preparedModel != nullptr) {
*preparedModel = IPreparedModel::castFrom(preparedModelCallback->getPreparedModel())
.withDefault(nullptr);
@@ -1013,7 +1017,8 @@
// Number of operations in the large test model.
constexpr uint32_t kLargeModelSize = 100;
-constexpr uint32_t kNumIterationsTOCTOU = 100;
+constexpr uint32_t kNumSuccessfulIterationsTOCTOU = 100;
+constexpr uint32_t kMaxNumFailedIterationsTOCTOU = 100;
TEST_P(CompilationCachingTest, SaveToCache_TOCTOU) {
if (!mIsCachingSupported) return;
@@ -1041,18 +1046,30 @@
// Use a different token for modelAdd.
mToken[0]++;
- // This test is probabilistic, so we run it multiple times.
- for (uint32_t i = 0; i < kNumIterationsTOCTOU; i++) {
+ // This test is probabilistic, so we run it multiple times. We allow the compilation to fail
+ // because it is not related to the security aspect of the TOCTOU test. However, we need to have
+ // enough successful iterations to ensure the test coverage.
+ uint32_t numSuccessfulIterations = 0, numFailedIterations = 0;
+ while (numSuccessfulIterations < kNumSuccessfulIterationsTOCTOU) {
// Save the modelAdd compilation to cache.
{
hidl_vec<hidl_handle> modelCache, dataCache;
createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ sp<IPreparedModel> preparedModel = nullptr;
// Spawn a thread to copy the cache content concurrently while saving to cache.
std::thread thread(copyCacheFiles, std::cref(modelCacheMul), std::cref(mModelCache));
- saveModelToCache(modelAdd, modelCache, dataCache);
+ saveModelToCache(modelAdd, modelCache, dataCache, &preparedModel,
+ /*allowGeneralFailure=*/true);
thread.join();
+
+ if (preparedModel == nullptr) {
+ numFailedIterations++;
+ ASSERT_LE(numFailedIterations, kMaxNumFailedIterationsTOCTOU);
+ } else {
+ numSuccessfulIterations++;
+ }
}
// Retrieve preparedModel from cache.
@@ -1103,14 +1120,26 @@
// Use a different token for modelAdd.
mToken[0]++;
- // This test is probabilistic, so we run it multiple times.
- for (uint32_t i = 0; i < kNumIterationsTOCTOU; i++) {
+ // This test is probabilistic, so we run it multiple times. We allow the compilation to fail
+ // because it is not related to the security aspect of the TOCTOU test. However, we need to have
+ // enough successful iterations to ensure the test coverage.
+ uint32_t numSuccessfulIterations = 0, numFailedIterations = 0;
+ while (numSuccessfulIterations < kNumSuccessfulIterationsTOCTOU) {
// Save the modelAdd compilation to cache.
{
hidl_vec<hidl_handle> modelCache, dataCache;
createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
- saveModelToCache(modelAdd, modelCache, dataCache);
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(modelAdd, modelCache, dataCache, &preparedModel,
+ /*allowGeneralFailure=*/true);
+
+ if (preparedModel == nullptr) {
+ numFailedIterations++;
+ ASSERT_LE(numFailedIterations, kMaxNumFailedIterationsTOCTOU);
+ } else {
+ numSuccessfulIterations++;
+ }
}
// Retrieve preparedModel from cache.
diff --git a/neuralnetworks/1.3/vts/functional/TestMain.cpp b/neuralnetworks/1.3/vts/functional/TestMain.cpp
new file mode 100644
index 0000000..6bf4e5f
--- /dev/null
+++ b/neuralnetworks/1.3/vts/functional/TestMain.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <gtest/gtest.h>
+#include "1.0/LogTestCaseToLogcat.h"
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ testing::UnitTest::GetInstance()->listeners().Append(
+ new android::hardware::neuralnetworks::LogTestCaseToLogcat());
+ return RUN_ALL_TESTS();
+}
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
index de082c3..a2e5071 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
@@ -21,6 +21,7 @@
#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
#include <android/hardware/neuralnetworks/1.3/types.h>
#include <gtest/gtest.h>
+#include <vector>
#include "1.0/Utils.h"
#include "1.3/Callbacks.h"
diff --git a/radio/1.1/vts/functional/radio_hidl_hal_api.cpp b/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
index 02dcbab..08121fd 100644
--- a/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
@@ -49,7 +49,6 @@
}
EXPECT_EQ(CardState::ABSENT, cardStatus.cardState);
}
-#endif
/* Test setSimCardPower power up */
serial = GetRandomSerialNumber();
@@ -60,6 +59,7 @@
ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_1->rspInfo.error,
{RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED,
RadioError::INVALID_ARGUMENTS, RadioError::RADIO_NOT_AVAILABLE}));
+#endif
/**
* If the sim card status for the testing environment is PRESENT,
diff --git a/radio/1.5/vts/functional/radio_response.cpp b/radio/1.5/vts/functional/radio_response.cpp
index 8cbb2d0..9b6d450 100644
--- a/radio/1.5/vts/functional/radio_response.cpp
+++ b/radio/1.5/vts/functional/radio_response.cpp
@@ -1017,8 +1017,10 @@
return Void();
}
-Return<void> RadioResponse_v1_5::sendCdmaSmsExpectMoreResponse(const RadioResponseInfo& /*info*/,
+Return<void> RadioResponse_v1_5::sendCdmaSmsExpectMoreResponse(const RadioResponseInfo& info,
const SendSmsResult& /*sms*/) {
+ rspInfo = info;
+ parent_v1_5.notify(info.serial);
return Void();
}
diff --git a/sensors/1.0/vts/functional/Android.bp b/sensors/1.0/vts/functional/Android.bp
index 31424ab..c77733b 100644
--- a/sensors/1.0/vts/functional/Android.bp
+++ b/sensors/1.0/vts/functional/Android.bp
@@ -23,11 +23,6 @@
"VtsHalSensorsV1_0TargetTest.cpp",
],
static_libs: [
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.allocator@3.0",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@2.1",
- "android.hardware.graphics.mapper@3.0",
"android.hardware.sensors@1.0",
"VtsHalSensorsTargetTestUtils",
],
diff --git a/sensors/2.0/vts/functional/Android.bp b/sensors/2.0/vts/functional/Android.bp
index 598ad15..83ebc6b 100644
--- a/sensors/2.0/vts/functional/Android.bp
+++ b/sensors/2.0/vts/functional/Android.bp
@@ -25,11 +25,6 @@
"android.hardware.sensors@2.X-shared-utils",
],
static_libs: [
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.allocator@3.0",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@2.1",
- "android.hardware.graphics.mapper@3.0",
"android.hardware.sensors@1.0",
"android.hardware.sensors@1.0-convert",
"android.hardware.sensors@2.0",
diff --git a/sensors/2.1/vts/functional/Android.bp b/sensors/2.1/vts/functional/Android.bp
index 3f01a3e..d257993 100644
--- a/sensors/2.1/vts/functional/Android.bp
+++ b/sensors/2.1/vts/functional/Android.bp
@@ -27,11 +27,6 @@
"android.hardware.sensors@2.X-shared-utils",
],
static_libs: [
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.allocator@3.0",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@2.1",
- "android.hardware.graphics.mapper@3.0",
"android.hardware.sensors@1.0",
"android.hardware.sensors@1.0-convert",
"android.hardware.sensors@2.0",
diff --git a/sensors/common/vts/2_X/Android.bp b/sensors/common/vts/2_X/Android.bp
index 8cdb5d1..e5eceb5 100644
--- a/sensors/common/vts/2_X/Android.bp
+++ b/sensors/common/vts/2_X/Android.bp
@@ -29,11 +29,6 @@
"libbinder",
],
static_libs: [
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.allocator@3.0",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@2.1",
- "android.hardware.graphics.mapper@3.0",
"android.hardware.sensors@1.0",
"android.hardware.sensors@1.0-convert",
"android.hardware.sensors@2.0",
diff --git a/sensors/common/vts/utils/Android.bp b/sensors/common/vts/utils/Android.bp
index ca4346a..baaed6c 100644
--- a/sensors/common/vts/utils/Android.bp
+++ b/sensors/common/vts/utils/Android.bp
@@ -31,13 +31,17 @@
"libutils",
],
static_libs: [
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.allocator@3.0",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@2.1",
- "android.hardware.graphics.mapper@3.0",
"android.hardware.sensors@1.0",
"android.hardware.sensors@2.0",
"android.hardware.sensors@2.1",
],
+ whole_static_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.allocator@4.0",
+ "android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@2.1",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.graphics.mapper@4.0",
+ ],
}
diff --git a/sensors/common/vts/utils/GrallocWrapper.cpp b/sensors/common/vts/utils/GrallocWrapper.cpp
index e63faa2..47d1f42 100644
--- a/sensors/common/vts/utils/GrallocWrapper.cpp
+++ b/sensors/common/vts/utils/GrallocWrapper.cpp
@@ -18,9 +18,11 @@
#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
+#include <android/hardware/graphics/allocator/4.0/IAllocator.h>
#include <android/hardware/graphics/mapper/2.0/IMapper.h>
#include <android/hardware/graphics/mapper/2.1/IMapper.h>
#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
#include <utils/Log.h>
@@ -29,19 +31,19 @@
using IAllocator2 = ::android::hardware::graphics::allocator::V2_0::IAllocator;
using IAllocator3 = ::android::hardware::graphics::allocator::V3_0::IAllocator;
+using IAllocator4 = ::android::hardware::graphics::allocator::V4_0::IAllocator;
using IMapper2 = ::android::hardware::graphics::mapper::V2_0::IMapper;
using IMapper2_1 = ::android::hardware::graphics::mapper::V2_1::IMapper;
using IMapper3 = ::android::hardware::graphics::mapper::V3_0::IMapper;
+using IMapper4 = ::android::hardware::graphics::mapper::V4_0::IMapper;
using Error2 = ::android::hardware::graphics::mapper::V2_0::Error;
using Error3 = ::android::hardware::graphics::mapper::V3_0::Error;
+using Error4 = ::android::hardware::graphics::mapper::V4_0::Error;
using ::android::hardware::graphics::common::V1_0::BufferUsage;
using ::android::hardware::graphics::common::V1_0::PixelFormat;
-// This is a typedef to the same underlying type across v2.0 and v3.0
-using ::android::hardware::graphics::mapper::V2_0::BufferDescriptor;
-
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
@@ -58,7 +60,6 @@
virtual ~IGrallocHalWrapper() = default;
// IAllocator
- virtual std::string dumpDebugInfo() = 0;
virtual native_handle_t* allocate(uint32_t size) = 0;
virtual void freeBuffer(native_handle_t* bufferHandle) = 0;
@@ -75,6 +76,24 @@
bool failed(Error3 error) {
return (error != Error3::NONE);
}
+bool failed(Error4 error) {
+ return (error != Error4::NONE);
+}
+
+template <typename>
+struct FirstArg;
+
+// Template specialization for pointer to a non-static member function, which exposes
+// the type of the first argument given to said function
+template <typename ReturnType, typename ClassT, typename Arg1, typename... OtherArgs>
+struct FirstArg<ReturnType (ClassT::*)(Arg1, OtherArgs...)> {
+ using type = Arg1;
+};
+
+// Alias to FirstArg which also removes any reference type and const associated
+template <typename T>
+using BaseTypeOfFirstArg = typename std::remove_const<
+ typename std::remove_reference<typename FirstArg<T>::type>::type>::type;
// Since all the type and function names are the same for the things we use across the major HAL
// versions, we use template magic to avoid repeating ourselves.
@@ -88,7 +107,6 @@
}
}
- virtual std::string dumpDebugInfo() override;
virtual native_handle_t* allocate(uint32_t size) override;
virtual void freeBuffer(native_handle_t* bufferHandle) override;
@@ -101,21 +119,19 @@
sp<AllocatorT> mAllocator;
sp<MapperT> mMapper;
- BufferDescriptor getDescriptor(uint32_t size);
+ // v2.0 and v3.0 use vec<uint32_t> for BufferDescriptor, but v4.0 uses vec<uint8_t>, so use
+ // some template magic to deduce the right type based off of the first argument to allocate(),
+ // which is always the version-specific BufferDescriptor type
+ typedef BaseTypeOfFirstArg<decltype(&AllocatorT::allocate)> BufferDescriptorT;
+
+ BufferDescriptorT getDescriptor(uint32_t size);
native_handle_t* importBuffer(const hidl_handle& rawHandle);
};
template <typename AllocatorT, typename MapperT>
-std::string GrallocHalWrapper<AllocatorT, MapperT>::dumpDebugInfo() {
- std::string debugInfo;
- mAllocator->dumpDebugInfo([&](const hidl_string& tmpDebugInfo) { debugInfo = tmpDebugInfo; });
- return debugInfo;
-}
-
-template <typename AllocatorT, typename MapperT>
native_handle_t* GrallocHalWrapper<AllocatorT, MapperT>::allocate(uint32_t size) {
constexpr uint32_t kBufferCount = 1;
- BufferDescriptor descriptor = getDescriptor(size);
+ BufferDescriptorT descriptor = getDescriptor(size);
native_handle_t* bufferHandle = nullptr;
auto callback = [&](auto error, uint32_t /*stride*/, const hidl_vec<hidl_handle>& buffers) {
@@ -142,7 +158,8 @@
}
template <typename AllocatorT, typename MapperT>
-BufferDescriptor GrallocHalWrapper<AllocatorT, MapperT>::getDescriptor(uint32_t size) {
+typename GrallocHalWrapper<AllocatorT, MapperT>::BufferDescriptorT
+GrallocHalWrapper<AllocatorT, MapperT>::getDescriptor(uint32_t size) {
typename MapperT::BufferDescriptorInfo descriptorInfo = {
.width = size,
.height = 1,
@@ -151,8 +168,8 @@
.usage = kBufferUsage,
};
- BufferDescriptor descriptor;
- auto callback = [&](auto error, const BufferDescriptor& tmpDescriptor) {
+ BufferDescriptorT descriptor;
+ auto callback = [&](auto error, const BufferDescriptorT& tmpDescriptor) {
if (failed(error)) {
ALOGE("Failed to create descriptor: %" PRId32, static_cast<int32_t>(error));
} else {
@@ -189,7 +206,7 @@
void* data = nullptr;
mMapper->lock(bufferHandle, kBufferUsage, accessRegion, acquireFenceHandle,
- [&](auto error, void* tmpData, ...) { // V3_0 passes extra args we don't use
+ [&](auto error, void* tmpData, ...) { // V3/4 pass extra args we don't use
if (failed(error)) {
ALOGE("Failed to lock buffer %p: %" PRId32, bufferHandle,
static_cast<int32_t>(error));
@@ -214,28 +231,40 @@
} // anonymous namespace
GrallocWrapper::GrallocWrapper() {
- sp<IAllocator3> allocator3 = IAllocator3::getService();
- sp<IMapper3> mapper3 = IMapper3::getService();
+ sp<IAllocator4> allocator4 = IAllocator4::getService();
+ sp<IMapper4> mapper4 = IMapper4::getService();
- if (allocator3 != nullptr && mapper3 != nullptr) {
+ if (allocator4 != nullptr && mapper4 != nullptr) {
+ ALOGD("Using IAllocator/IMapper v4.0");
mGrallocHal = std::unique_ptr<IGrallocHalWrapper>(
- new GrallocHalWrapper<IAllocator3, IMapper3>(allocator3, mapper3));
+ new GrallocHalWrapper<IAllocator4, IMapper4>(allocator4, mapper4));
} else {
- ALOGD("Graphics HALs 3.0 not found (allocator %d mapper %d), falling back to 2.x",
- (allocator3 != nullptr), (mapper3 != nullptr));
+ ALOGD("Graphics HALs 4.0 not found (allocator %d mapper %d), falling back to 3.0",
+ (allocator4 != nullptr), (mapper4 != nullptr));
- sp<IAllocator2> allocator2 = IAllocator2::getService();
- sp<IMapper2> mapper2 = IMapper2_1::getService();
- if (mapper2 == nullptr) {
- mapper2 = IMapper2::getService();
- }
+ sp<IAllocator3> allocator3 = IAllocator3::getService();
+ sp<IMapper3> mapper3 = IMapper3::getService();
- if (allocator2 != nullptr && mapper2 != nullptr) {
+ if (allocator3 != nullptr && mapper3 != nullptr) {
mGrallocHal = std::unique_ptr<IGrallocHalWrapper>(
- new GrallocHalWrapper<IAllocator2, IMapper2>(allocator2, mapper2));
+ new GrallocHalWrapper<IAllocator3, IMapper3>(allocator3, mapper3));
} else {
- ALOGE("Couldn't open 2.x/3.0 graphics HALs (2.x allocator %d mapper %d)",
- (allocator2 != nullptr), (mapper2 != nullptr));
+ ALOGD("Graphics HALs 3.0 not found (allocator %d mapper %d), falling back to 2.x",
+ (allocator3 != nullptr), (mapper3 != nullptr));
+
+ sp<IAllocator2> allocator2 = IAllocator2::getService();
+ sp<IMapper2> mapper2 = IMapper2_1::getService();
+ if (mapper2 == nullptr) {
+ mapper2 = IMapper2::getService();
+ }
+
+ if (allocator2 != nullptr && mapper2 != nullptr) {
+ mGrallocHal = std::unique_ptr<IGrallocHalWrapper>(
+ new GrallocHalWrapper<IAllocator2, IMapper2>(allocator2, mapper2));
+ } else {
+ ALOGE("Couldn't open graphics HALs (2.x allocator %d mapper %d)",
+ (allocator2 != nullptr), (mapper2 != nullptr));
+ }
}
}
}
@@ -248,10 +277,6 @@
mAllocatedBuffers.clear();
}
-std::string GrallocWrapper::dumpDebugInfo() {
- return mGrallocHal->dumpDebugInfo();
-}
-
std::pair<native_handle_t*, void*> GrallocWrapper::allocate(uint32_t size) {
native_handle_t* bufferHandle = mGrallocHal->allocate(size);
void* buffer = nullptr;
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/GrallocWrapper.h b/sensors/common/vts/utils/include/sensors-vts-utils/GrallocWrapper.h
index 41e6334..ebbcb2c 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/GrallocWrapper.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/GrallocWrapper.h
@@ -37,8 +37,6 @@
// returns false, other methods are not safe to call.
bool isInitialized() const { return (mGrallocHal != nullptr); };
- std::string dumpDebugInfo();
-
// Allocates a gralloc buffer suitable for direct channel sensors usage with the given size.
// The buffer should be freed using freeBuffer when it's not needed anymore; otherwise it'll
// be freed when this object is destroyed.
diff --git a/tv/tuner/1.0/default/Demux.cpp b/tv/tuner/1.0/default/Demux.cpp
index da56041..67eff1b 100644
--- a/tv/tuner/1.0/default/Demux.cpp
+++ b/tv/tuner/1.0/default/Demux.cpp
@@ -48,8 +48,6 @@
return Result::INVALID_STATE;
}
- mFrontendSourceFile = mFrontend->getSourceFile();
-
mTunerService->setFrontendAsDemuxSource(frontendId, mDemuxId);
return Result::SUCCESS;
@@ -62,8 +60,6 @@
uint32_t filterId;
filterId = ++mLastUsedFilterId;
- mUsedFilterIds.insert(filterId);
-
if (cb == nullptr) {
ALOGW("[Demux] callback can't be null");
_hidl_cb(Result::INVALID_ARGUMENT, new Filter());
@@ -82,8 +78,13 @@
mPcrFilterIds.insert(filterId);
}
bool result = true;
- if (mDvr != nullptr && mDvr->getType() == DvrType::PLAYBACK) {
- result = mDvr->addPlaybackFilter(filter);
+ if (!filter->isRecordFilter()) {
+ // Only save non-record filters for now. Record filters are saved when the
+ // IDvr.attacheFilter is called.
+ mPlaybackFilterIds.insert(filterId);
+ if (mDvrPlayback != nullptr) {
+ result = mDvrPlayback->addPlaybackFilter(filterId, filter);
+ }
}
_hidl_cb(result ? Result::SUCCESS : Result::INVALID_ARGUMENT, filter);
@@ -93,9 +94,9 @@
Return<void> Demux::openTimeFilter(openTimeFilter_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
- sp<TimeFilter> timeFilter = new TimeFilter(this);
+ mTimeFilter = new TimeFilter(this);
- _hidl_cb(Result::SUCCESS, timeFilter);
+ _hidl_cb(Result::SUCCESS, mTimeFilter);
return Void();
}
@@ -124,12 +125,12 @@
}
if (!mPcrFilterIds.empty()) {
- ALOGE("[Demux] No PCR filter opened.");
// Return the lowest pcr filter id in the default implementation as the av sync id
_hidl_cb(Result::SUCCESS, *mPcrFilterIds.begin());
return Void();
}
+ ALOGE("[Demux] No PCR filter opened.");
_hidl_cb(Result::INVALID_STATE, avSyncHwId);
return Void();
}
@@ -154,7 +155,13 @@
Return<Result> Demux::close() {
ALOGV("%s", __FUNCTION__);
- mUsedFilterIds.clear();
+ set<uint32_t>::iterator it;
+ for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) {
+ mDvrPlayback->removePlaybackFilter(*it);
+ }
+ mPlaybackFilterIds.clear();
+ mRecordFilterIds.clear();
+ mFilters.clear();
mLastUsedFilterId = -1;
return Result::SUCCESS;
@@ -170,15 +177,38 @@
return Void();
}
- mDvr = new Dvr(type, bufferSize, cb, this);
+ set<uint32_t>::iterator it;
+ switch (type) {
+ case DvrType::PLAYBACK:
+ mDvrPlayback = new Dvr(type, bufferSize, cb, this);
+ if (!mDvrPlayback->createDvrMQ()) {
+ _hidl_cb(Result::UNKNOWN_ERROR, mDvrPlayback);
+ return Void();
+ }
- if (!mDvr->createDvrMQ()) {
- _hidl_cb(Result::UNKNOWN_ERROR, mDvr);
- return Void();
+ for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) {
+ if (!mDvrPlayback->addPlaybackFilter(*it, mFilters[*it])) {
+ ALOGE("[Demux] Can't get filter info for DVR playback");
+ _hidl_cb(Result::UNKNOWN_ERROR, mDvrPlayback);
+ return Void();
+ }
+ }
+
+ _hidl_cb(Result::SUCCESS, mDvrPlayback);
+ return Void();
+ case DvrType::RECORD:
+ mDvrRecord = new Dvr(type, bufferSize, cb, this);
+ if (!mDvrRecord->createDvrMQ()) {
+ _hidl_cb(Result::UNKNOWN_ERROR, mDvrRecord);
+ return Void();
+ }
+
+ _hidl_cb(Result::SUCCESS, mDvrRecord);
+ return Void();
+ default:
+ _hidl_cb(Result::INVALID_ARGUMENT, nullptr);
+ return Void();
}
-
- _hidl_cb(Result::SUCCESS, mDvr);
- return Void();
}
Return<Result> Demux::connectCiCam(uint32_t ciCamId) {
@@ -198,8 +228,10 @@
Result Demux::removeFilter(uint32_t filterId) {
ALOGV("%s", __FUNCTION__);
- // resetFilterRecords(filterId);
- mUsedFilterIds.erase(filterId);
+ if (mDvrPlayback != nullptr) {
+ mDvrPlayback->removePlaybackFilter(filterId);
+ }
+ mPlaybackFilterIds.erase(filterId);
mRecordFilterIds.erase(filterId);
mFilters.erase(filterId);
@@ -212,7 +244,7 @@
if (DEBUG_DEMUX) {
ALOGW("[Demux] start ts filter pid: %d", pid);
}
- for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
+ for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) {
if (pid == mFilters[*it]->getTpid()) {
mFilters[*it]->updateFilterOutput(data);
}
@@ -233,7 +265,7 @@
set<uint32_t>::iterator it;
// Handle the output data per filter type
- for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
+ for (it = mPlaybackFilterIds.begin(); it != mPlaybackFilterIds.end(); it++) {
if (mFilters[*it]->startFilterHandler() != Result::SUCCESS) {
return false;
}
@@ -280,58 +312,27 @@
void Demux::frontendInputThreadLoop() {
std::lock_guard<std::mutex> lock(mFrontendInputThreadLock);
mFrontendInputThreadRunning = 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] Frontend input thread loop start %s", mFrontendSourceFile.c_str());
- if (!inputData.is_open()) {
- mFrontendInputThreadRunning = false;
- ALOGW("[Demux] Error %s", strerror(errno));
- }
while (mFrontendInputThreadRunning) {
- // 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) {
- mKeepFetchingDataFromFrontend = false;
- mFrontendInputThreadRunning = false;
- break;
- }
- // filter and dispatch filter output
- vector<uint8_t> byteBuffer;
- byteBuffer.resize(packetSize);
- for (int index = 0; index < byteBuffer.size(); index++) {
- byteBuffer[index] = static_cast<uint8_t>(buffer[index]);
- }
- if (mIsRecording) {
- // Feed the data into the Dvr recording input
- sendFrontendInputToRecord(byteBuffer);
- } else {
- // Feed the data into the broadcast demux filter
- startBroadcastTsFilter(byteBuffer);
- }
- }
- if (mIsRecording) {
- // Dispatch the data into the broadcasting filters.
- startRecordFilterDispatcher();
- } else {
- // Dispatch the data into the broadcasting filters.
- startBroadcastFilterDispatcher();
- }
- usleep(100);
+ uint32_t efState = 0;
+ status_t status = mDvrPlayback->getDvrEventFlag()->wait(
+ static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY), &efState, WAIT_TIMEOUT,
+ true /* retry on spurious wake */);
+ if (status != OK) {
+ ALOGD("[Demux] wait for data ready on the playback FMQ");
+ continue;
+ }
+ // Our current implementation filter the data and write it into the filter FMQ immediately
+ // after the DATA_READY from the VTS/framework
+ if (!mDvrPlayback->readPlaybackFMQ(true /*isVirtualFrontend*/, mIsRecording) ||
+ !mDvrPlayback->startFilterDispatcher(true /*isVirtualFrontend*/, mIsRecording)) {
+ ALOGE("[Demux] playback data failed to be filtered. Ending thread");
+ break;
}
}
+ mFrontendInputThreadRunning = false;
ALOGW("[Demux] Frontend Input thread end.");
- delete[] buffer;
- inputData.close();
}
void Demux::stopFrontendInput() {
@@ -346,18 +347,19 @@
}
bool Demux::attachRecordFilter(int filterId) {
- if (mFilters[filterId] == nullptr || mDvr == nullptr) {
+ if (mFilters[filterId] == nullptr || mDvrRecord == nullptr ||
+ !mFilters[filterId]->isRecordFilter()) {
return false;
}
mRecordFilterIds.insert(filterId);
- mFilters[filterId]->attachFilterToRecord(mDvr);
+ mFilters[filterId]->attachFilterToRecord(mDvrRecord);
return true;
}
bool Demux::detachRecordFilter(int filterId) {
- if (mFilters[filterId] == nullptr || mDvr == nullptr) {
+ if (mFilters[filterId] == nullptr || mDvrRecord == nullptr) {
return false;
}
diff --git a/tv/tuner/1.0/default/Demux.h b/tv/tuner/1.0/default/Demux.h
index 6c46b0d..7f282b2 100644
--- a/tv/tuner/1.0/default/Demux.h
+++ b/tv/tuner/1.0/default/Demux.h
@@ -91,13 +91,23 @@
void setIsRecording(bool isRecording);
void startFrontendInputLoop();
+ /**
+ * A dispatcher to read and dispatch input data to all the started filters.
+ * Each filter handler handles the data filtering/output writing/filterEvent updating.
+ * Note that recording filters are not included.
+ */
+ bool startBroadcastFilterDispatcher();
+ void startBroadcastTsFilter(vector<uint8_t> data);
+
+ void sendFrontendInputToRecord(vector<uint8_t> data);
+ bool startRecordFilterDispatcher();
+
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 {
@@ -117,16 +127,6 @@
*/
void deleteEventFlag();
bool readDataFromMQ();
- /**
- * A dispatcher to read and dispatch input data to all the started filters.
- * Each filter handler handles the data filtering/output writing/filterEvent updating.
- * Note that recording filters are not included.
- */
- bool startBroadcastFilterDispatcher();
- void startBroadcastTsFilter(vector<uint8_t> data);
-
- void sendFrontendInputToRecord(vector<uint8_t> data);
- bool startRecordFilterDispatcher();
uint32_t mDemuxId;
uint32_t mCiCamId;
@@ -137,25 +137,31 @@
*/
uint32_t mLastUsedFilterId = -1;
/**
- * Record all the used filter Ids.
+ * Record all the used playback filter Ids.
* Any removed filter id should be removed from this set.
*/
- set<uint32_t> mUsedFilterIds;
+ set<uint32_t> mPlaybackFilterIds;
/**
* Record all the attached record filter Ids.
* Any removed filter id should be removed from this set.
*/
set<uint32_t> mRecordFilterIds;
/**
- * A list of created FilterMQ ptrs.
+ * A list of created Filter sp.
* The array number is the filter ID.
*/
std::map<uint32_t, sp<Filter>> mFilters;
/**
+ * Local reference to the opened Timer Filter instance.
+ */
+ sp<TimeFilter> mTimeFilter;
+
+ /**
* Local reference to the opened DVR object.
*/
- sp<Dvr> mDvr;
+ sp<Dvr> mDvrPlayback;
+ sp<Dvr> mDvrRecord;
// Thread handlers
pthread_t mFrontendInputThread;
diff --git a/tv/tuner/1.0/default/Dvr.cpp b/tv/tuner/1.0/default/Dvr.cpp
index adb2635..68e175c 100644
--- a/tv/tuner/1.0/default/Dvr.cpp
+++ b/tv/tuner/1.0/default/Dvr.cpp
@@ -70,8 +70,7 @@
return status;
}
- // check if the attached filter is a record filter
- mFilters[filterId] = filter;
+ // TODO check if the attached filter is a record filter
if (!mDemux->attachRecordFilter(filterId)) {
return Result::INVALID_ARGUMENT;
}
@@ -94,19 +93,8 @@
return status;
}
- std::map<uint32_t, sp<IFilter>>::iterator it;
-
- it = mFilters.find(filterId);
- if (it != mFilters.end()) {
- mFilters.erase(filterId);
- if (!mDemux->detachRecordFilter(filterId)) {
- return Result::INVALID_ARGUMENT;
- }
- }
-
- // If all the filters are detached, record can't be started
- if (mFilters.empty()) {
- mIsRecordFilterAttached = false;
+ if (!mDemux->detachRecordFilter(filterId)) {
+ return Result::INVALID_ARGUMENT;
}
return Result::SUCCESS;
@@ -183,6 +171,10 @@
return true;
}
+EventFlag* Dvr::getDvrEventFlag() {
+ return mDvrEventFlag;
+}
+
void* Dvr::__threadLoopPlayback(void* user) {
Dvr* const self = static_cast<Dvr*>(user);
self->playbackThreadLoop();
@@ -205,8 +197,9 @@
}
// Our current implementation filter the data and write it into the filter FMQ immediately
// after the DATA_READY from the VTS/framework
- if (!readPlaybackFMQ() || !startFilterDispatcher()) {
- ALOGD("[Dvr] playback data failed to be filtered. Ending thread");
+ if (!readPlaybackFMQ(false /*isVirtualFrontend*/, false /*isRecording*/) ||
+ !startFilterDispatcher(false /*isVirtualFrontend*/, false /*isRecording*/)) {
+ ALOGE("[Dvr] playback data failed to be filtered. Ending thread");
break;
}
@@ -245,7 +238,7 @@
return mPlaybackStatus;
}
-bool Dvr::readPlaybackFMQ() {
+bool Dvr::readPlaybackFMQ(bool isVirtualFrontend, bool isRecording) {
// Read playback data from the input FMQ
int size = mDvrMQ->availableToRead();
int playbackPacketSize = mDvrSettings.playback().packetSize;
@@ -256,7 +249,15 @@
if (!mDvrMQ->read(dataOutputBuffer.data(), playbackPacketSize)) {
return false;
}
- startTpidFilter(dataOutputBuffer);
+ if (isVirtualFrontend) {
+ if (isRecording) {
+ mDemux->sendFrontendInputToRecord(dataOutputBuffer);
+ } else {
+ mDemux->startBroadcastTsFilter(dataOutputBuffer);
+ }
+ } else {
+ startTpidFilter(dataOutputBuffer);
+ }
}
return true;
@@ -275,7 +276,15 @@
}
}
-bool Dvr::startFilterDispatcher() {
+bool Dvr::startFilterDispatcher(bool isVirtualFrontend, bool isRecording) {
+ if (isVirtualFrontend) {
+ if (isRecording) {
+ return mDemux->startRecordFilterDispatcher();
+ } else {
+ return mDemux->startBroadcastFilterDispatcher();
+ }
+ }
+
std::map<uint32_t, sp<IFilter>>::iterator it;
// Handle the output data per filter type
for (it = mFilters.begin(); it != mFilters.end(); it++) {
@@ -329,27 +338,15 @@
return mRecordStatus;
}
-bool Dvr::addPlaybackFilter(sp<IFilter> filter) {
- uint32_t filterId;
- Result status;
-
- filter->getId([&](Result result, uint32_t id) {
- filterId = id;
- status = result;
- });
-
- if (status != Result::SUCCESS) {
- return false;
- }
-
+bool Dvr::addPlaybackFilter(uint32_t filterId, sp<IFilter> filter) {
mFilters[filterId] = filter;
return true;
}
-DvrType Dvr::getType() {
- return mType;
+bool Dvr::removePlaybackFilter(uint32_t filterId) {
+ mFilters.erase(filterId);
+ return true;
}
-
} // namespace implementation
} // namespace V1_0
} // namespace tuner
diff --git a/tv/tuner/1.0/default/Dvr.h b/tv/tuner/1.0/default/Dvr.h
index 08afd5d..a63a256 100644
--- a/tv/tuner/1.0/default/Dvr.h
+++ b/tv/tuner/1.0/default/Dvr.h
@@ -81,8 +81,11 @@
bool createDvrMQ();
void sendBroadcastInputToDvrRecord(vector<uint8_t> byteBuffer);
bool writeRecordFMQ(const std::vector<uint8_t>& data);
- DvrType getType();
- bool addPlaybackFilter(sp<IFilter> filter);
+ bool addPlaybackFilter(uint32_t filterId, sp<IFilter> filter);
+ bool removePlaybackFilter(uint32_t filterId);
+ bool readPlaybackFMQ(bool isVirtualFrontend, bool isRecording);
+ bool startFilterDispatcher(bool isVirtualFrontend, bool isRecording);
+ EventFlag* getDvrEventFlag();
private:
// Demux service
@@ -105,9 +108,7 @@
* 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 readPlaybackFMQ();
void startTpidFilter(vector<uint8_t> data);
- bool startFilterDispatcher();
static void* __threadLoopPlayback(void* user);
static void* __threadLoopRecord(void* user);
void playbackThreadLoop();
@@ -123,7 +124,6 @@
// Thread handlers
pthread_t mDvrThread;
- pthread_t mBroadcastInputThread;
// FMQ status local records
PlaybackStatus mPlaybackStatus;
@@ -132,7 +132,6 @@
* If a specific filter's writing loop is still running
*/
bool mDvrThreadRunning;
- bool mBroadcastInputThreadRunning;
bool mKeepFetchingDataFromFrontend;
/**
* Lock to protect writes to the FMQs
@@ -143,7 +142,6 @@
*/
std::mutex mPlaybackStatusLock;
std::mutex mRecordStatusLock;
- std::mutex mBroadcastInputThreadLock;
std::mutex mDvrThreadLock;
const bool DEBUG_DVR = false;
@@ -151,7 +149,6 @@
// Booleans to check if recording is running.
// Recording is ready when both of the following are set to true.
bool mIsRecordStarted = false;
- bool mIsRecordFilterAttached = false;
};
} // namespace implementation
diff --git a/tv/tuner/1.0/default/Filter.cpp b/tv/tuner/1.0/default/Filter.cpp
index 8bca70c..30b19c0 100644
--- a/tv/tuner/1.0/default/Filter.cpp
+++ b/tv/tuner/1.0/default/Filter.cpp
@@ -47,12 +47,18 @@
if (mType.subType.tsFilterType() == DemuxTsFilterType::PCR) {
mIsPcrFilter = true;
}
+ if (mType.subType.tsFilterType() == DemuxTsFilterType::RECORD) {
+ mIsRecordFilter = true;
+ }
break;
case DemuxFilterMainType::MMTP:
if (mType.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO ||
mType.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) {
mIsMediaFilter = true;
}
+ if (mType.subType.mmtpFilterType() == DemuxMmtpFilterType::RECORD) {
+ mIsRecordFilter = true;
+ }
break;
case DemuxFilterMainType::IP:
break;
@@ -535,12 +541,6 @@
}
Result Filter::startRecordFilterHandler() {
- /*DemuxFilterTsRecordEvent tsRecordEvent;
- tsRecordEvent.pid.tPid(0);
- tsRecordEvent.indexMask.tsIndexMask(0x01);
- mFilterEvent.events.resize(1);
- mFilterEvent.events[0].tsRecord(tsRecordEvent);
-*/
std::lock_guard<std::mutex> lock(mRecordFilterOutputLock);
if (mRecordFilterOutput.empty()) {
return Result::SUCCESS;
@@ -567,7 +567,7 @@
bool Filter::writeSectionsAndCreateEvent(vector<uint8_t> data) {
// TODO check how many sections has been read
- ALOGD("[Filter] section hander");
+ ALOGD("[Filter] section handler");
std::lock_guard<std::mutex> lock(mFilterEventLock);
if (!writeDataToFilterMQ(data)) {
return false;
diff --git a/tv/tuner/1.0/default/Filter.h b/tv/tuner/1.0/default/Filter.h
index 09e9604..9386dca 100644
--- a/tv/tuner/1.0/default/Filter.h
+++ b/tv/tuner/1.0/default/Filter.h
@@ -91,6 +91,7 @@
void freeAvHandle();
bool isMediaFilter() { return mIsMediaFilter; };
bool isPcrFilter() { return mIsPcrFilter; };
+ bool isRecordFilter() { return mIsRecordFilter; };
private:
// Tuner service
@@ -107,6 +108,7 @@
DemuxFilterType mType;
bool mIsMediaFilter = false;
bool mIsPcrFilter = false;
+ bool mIsRecordFilter = false;
DemuxFilterSettings mFilterSettings;
uint16_t mTpid;
diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp
index 996b6ef..8bf0ec5 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -66,7 +66,7 @@
mTunerService->frontendStartTune(mId);
mCallback->onEvent(FrontendEventType::LOCKED);
- mIsLocked = false;
+ mIsLocked = true;
return Result::SUCCESS;
}
@@ -254,7 +254,9 @@
Return<Result> Frontend::setLnb(uint32_t /* lnb */) {
ALOGV("%s", __FUNCTION__);
-
+ if (!supportsSatellite()) {
+ return Result::INVALID_STATE;
+ }
return Result::SUCCESS;
}
@@ -266,10 +268,14 @@
return mId;
}
-string Frontend::getSourceFile() {
- return FRONTEND_STREAM_FILE;
+bool Frontend::supportsSatellite() {
+ return mType == FrontendType::DVBS || mType == FrontendType::ISDBS ||
+ mType == FrontendType::ISDBS3;
}
+bool Frontend::isLocked() {
+ return mIsLocked;
+}
} // 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 65537d7..a529b74 100644
--- a/tv/tuner/1.0/default/Frontend.h
+++ b/tv/tuner/1.0/default/Frontend.h
@@ -68,15 +68,17 @@
string getSourceFile();
+ bool isLocked();
+
private:
virtual ~Frontend();
+ bool supportsSatellite();
sp<IFrontendCallback> mCallback;
sp<Tuner> mTunerService;
FrontendType mType = FrontendType::UNDEFINED;
FrontendId mId = 0;
bool mIsLocked = false;
- const string FRONTEND_STREAM_FILE = "/vendor/etc/segment000000.ts";
std::ifstream mFrontendData;
};
diff --git a/tv/tuner/1.0/default/TimeFilter.cpp b/tv/tuner/1.0/default/TimeFilter.cpp
index 0b1fd1c..cec824f 100644
--- a/tv/tuner/1.0/default/TimeFilter.cpp
+++ b/tv/tuner/1.0/default/TimeFilter.cpp
@@ -34,24 +34,32 @@
TimeFilter::~TimeFilter() {}
-Return<Result> TimeFilter::setTimeStamp(uint64_t /* timeStamp */) {
+Return<Result> TimeFilter::setTimeStamp(uint64_t timeStamp) {
ALOGV("%s", __FUNCTION__);
+ if (timeStamp == INVALID_TIME_STAMP) {
+ return Result::INVALID_ARGUMENT;
+ }
+ mTimeStamp = timeStamp;
+ mBeginTime = time(NULL);
return Result::SUCCESS;
}
Return<Result> TimeFilter::clearTimeStamp() {
ALOGV("%s", __FUNCTION__);
+ mTimeStamp = INVALID_TIME_STAMP;
return Result::SUCCESS;
}
Return<void> TimeFilter::getTimeStamp(getTimeStamp_cb _hidl_cb) {
ALOGV("%s", __FUNCTION__);
+ if (mTimeStamp == INVALID_TIME_STAMP) {
+ _hidl_cb(Result::INVALID_STATE, mTimeStamp);
+ }
- uint64_t timeStamp = 0;
-
- _hidl_cb(Result::SUCCESS, timeStamp);
+ uint64_t currentTimeStamp = mTimeStamp + difftime(time(NULL), mBeginTime) * 900000;
+ _hidl_cb(Result::SUCCESS, currentTimeStamp);
return Void();
}
@@ -66,6 +74,7 @@
Return<Result> TimeFilter::close() {
ALOGV("%s", __FUNCTION__);
+ mTimeStamp = INVALID_TIME_STAMP;
return Result::SUCCESS;
}
diff --git a/tv/tuner/1.0/default/TimeFilter.h b/tv/tuner/1.0/default/TimeFilter.h
index 7131df8..cb3f29d 100644
--- a/tv/tuner/1.0/default/TimeFilter.h
+++ b/tv/tuner/1.0/default/TimeFilter.h
@@ -19,6 +19,7 @@
#include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
#include "Demux.h"
+#include "time.h"
using namespace std;
@@ -35,6 +36,8 @@
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+#define INVALID_TIME_STAMP -1
+
class Demux;
class TimeFilter : public ITimeFilter {
@@ -57,6 +60,8 @@
private:
sp<Demux> mDemux;
+ uint64_t mTimeStamp = INVALID_TIME_STAMP;
+ time_t mBeginTime;
};
} // namespace implementation
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index 821d83f..48ce384 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -139,6 +139,8 @@
DemuxCapabilities caps;
+ // IP filter can be an MMTP filter's data source.
+ caps.linkCaps = {0x00, 0x00, 0x02, 0x00, 0x00};
_hidl_cb(Result::SUCCESS, caps);
return Void();
}
@@ -229,6 +231,9 @@
void Tuner::setFrontendAsDemuxSource(uint32_t frontendId, uint32_t demuxId) {
mFrontendToDemux[frontendId] = demuxId;
+ if (mFrontends[frontendId] != nullptr && mFrontends[frontendId]->isLocked()) {
+ mDemuxes[demuxId]->startFrontendInputLoop();
+ }
}
void Tuner::frontendStopTune(uint32_t frontendId) {
diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp
index 8c08873..1765915 100644
--- a/tv/tuner/1.0/vts/functional/Android.bp
+++ b/tv/tuner/1.0/vts/functional/Android.bp
@@ -24,6 +24,7 @@
"FilterTests.cpp",
"DvrTests.cpp",
"DescramblerTests.cpp",
+ "LnbTests.cpp",
],
static_libs: [
"android.hardware.cas@1.0",
diff --git a/tv/tuner/1.0/vts/functional/DemuxTests.cpp b/tv/tuner/1.0/vts/functional/DemuxTests.cpp
index 6c32534..37a47d7 100644
--- a/tv/tuner/1.0/vts/functional/DemuxTests.cpp
+++ b/tv/tuner/1.0/vts/functional/DemuxTests.cpp
@@ -33,6 +33,19 @@
return AssertionResult(status.isOk());
}
+AssertionResult DemuxTests::getDemuxCaps(DemuxCapabilities& demuxCaps) {
+ if (!mDemux) {
+ ALOGW("[vts] Test with openDemux first.");
+ return failure();
+ }
+ Result status;
+ mService->getDemuxCaps([&](Result result, DemuxCapabilities caps) {
+ status = result;
+ demuxCaps = caps;
+ });
+ return AssertionResult(status == Result::SUCCESS);
+}
+
AssertionResult DemuxTests::closeDemux() {
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
auto status = mDemux->close();
@@ -40,23 +53,23 @@
return AssertionResult(status.isOk());
}
-void DemuxTests::getAvSyncId(sp<IFilter> filter, uint32_t& avSyncHwId) {
- ASSERT_TRUE(mDemux) << "Demux is not opened yet.";
+AssertionResult DemuxTests::getAvSyncId(sp<IFilter> filter, uint32_t& avSyncHwId) {
+ EXPECT_TRUE(mDemux) << "Demux is not opened yet.";
Result status;
mDemux->getAvSyncHwId(filter, [&](Result result, uint32_t id) {
status = result;
avSyncHwId = id;
});
- ASSERT_TRUE(status == Result::SUCCESS) << "Fail to get avSyncHwId.";
+ return AssertionResult(status == Result::SUCCESS);
}
-void DemuxTests::getAvSyncTime(uint32_t avSyncId) {
- ASSERT_TRUE(mDemux) << "Demux is not opened yet.";
+AssertionResult DemuxTests::getAvSyncTime(uint32_t avSyncId) {
+ EXPECT_TRUE(mDemux) << "Demux is not opened yet.";
Result status;
uint64_t syncTime;
mDemux->getAvSyncTime(avSyncId, [&](Result result, uint64_t time) {
status = result;
syncTime = time;
});
- ASSERT_TRUE(status == Result::SUCCESS) << "Fail to get avSyncTime.";
+ return AssertionResult(status == Result::SUCCESS);
}
\ No newline at end of file
diff --git a/tv/tuner/1.0/vts/functional/DemuxTests.h b/tv/tuner/1.0/vts/functional/DemuxTests.h
index 0443c67..b249ea8 100644
--- a/tv/tuner/1.0/vts/functional/DemuxTests.h
+++ b/tv/tuner/1.0/vts/functional/DemuxTests.h
@@ -30,6 +30,7 @@
using android::sp;
using android::hardware::Return;
using android::hardware::Void;
+using android::hardware::tv::tuner::V1_0::DemuxCapabilities;
using android::hardware::tv::tuner::V1_0::IDemux;
using android::hardware::tv::tuner::V1_0::IFilter;
using android::hardware::tv::tuner::V1_0::ITuner;
@@ -43,8 +44,9 @@
AssertionResult openDemux(sp<IDemux>& demux, uint32_t& demuxId);
AssertionResult setDemuxFrontendDataSource(uint32_t frontendId);
- void getAvSyncId(sp<IFilter> filter, uint32_t& avSyncHwId);
- void getAvSyncTime(uint32_t avSyncId);
+ AssertionResult getAvSyncId(sp<IFilter> filter, uint32_t& avSyncHwId);
+ AssertionResult getAvSyncTime(uint32_t avSyncId);
+ AssertionResult getDemuxCaps(DemuxCapabilities& demuxCaps);
AssertionResult closeDemux();
protected:
diff --git a/tv/tuner/1.0/vts/functional/DescramblerTests.h b/tv/tuner/1.0/vts/functional/DescramblerTests.h
index 31f4663..16d480d 100644
--- a/tv/tuner/1.0/vts/functional/DescramblerTests.h
+++ b/tv/tuner/1.0/vts/functional/DescramblerTests.h
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
#include <android-base/logging.h>
#include <android/hardware/cas/1.0/types.h>
#include <android/hardware/cas/1.2/ICas.h>
@@ -28,6 +26,8 @@
#include <android/hardware/tv/tuner/1.0/ITuner.h>
#include <android/hardware/tv/tuner/1.0/types.h>
#include <fmq/MessageQueue.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
#include <hidl/Status.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
@@ -69,6 +69,8 @@
using ::testing::AssertionResult;
+using namespace std;
+
class MediaCasListener : public ICasListener {
public:
virtual Return<void> onEvent(int32_t /*event*/, int32_t /*arg*/,
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.cpp b/tv/tuner/1.0/vts/functional/DvrTests.cpp
index 7e7f8e6..0dfc032 100644
--- a/tv/tuner/1.0/vts/functional/DvrTests.cpp
+++ b/tv/tuner/1.0/vts/functional/DvrTests.cpp
@@ -49,49 +49,73 @@
EXPECT_TRUE(EventFlag::createEventFlag(mPlaybackMQ->getEventFlagWord(), &playbackMQEventFlag) ==
android::OK);
- // open the stream and get its length
- std::ifstream inputData(mInputDataFile.c_str(), std::ifstream::binary);
- int writeSize = mPlaybackSettings.packetSize * 6;
- char* buffer = new char[writeSize];
- ALOGW("[vts] playback thread loop start %s!", mInputDataFile.c_str());
- if (!inputData.is_open()) {
+ int fd = open(mInputDataFile.c_str(), O_RDONLY | O_LARGEFILE);
+ int readBytes;
+ uint32_t regionSize = 0;
+ uint8_t* buffer;
+ ALOGW("[vts] playback thread loop start %s", mInputDataFile.c_str());
+ if (fd < 0) {
mPlaybackThreadRunning = false;
ALOGW("[vts] Error %s", strerror(errno));
}
while (mPlaybackThreadRunning) {
- // move the stream pointer for packet size * 6 every read until the end
while (mKeepWritingPlaybackFMQ) {
- inputData.read(buffer, writeSize);
- if (!inputData) {
- int leftSize = inputData.gcount();
- if (leftSize == 0) {
- mPlaybackThreadRunning = 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(mPlaybackMQ->write((unsigned char*)&buffer[0], leftSize));
- playbackMQEventFlag->wake(
- static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
- }
+ int totalWrite = mPlaybackMQ->availableToWrite();
+ if (totalWrite * 4 < mPlaybackMQ->getQuantumCount()) {
+ // Wait for the HAL implementation to read more data then write.
+ continue;
+ }
+ MessageQueue<uint8_t, kSynchronizedReadWrite>::MemTransaction memTx;
+ if (!mPlaybackMQ->beginWrite(totalWrite, &memTx)) {
+ ALOGW("[vts] Fail to write into Playback fmq.");
mPlaybackThreadRunning = false;
break;
}
- // Write input FMQ and notify the Tuner Implementation
- EXPECT_TRUE(mPlaybackMQ->write((unsigned char*)&buffer[0], writeSize));
+ auto first = memTx.getFirstRegion();
+ buffer = first.getAddress();
+ regionSize = first.getLength();
+
+ if (regionSize > 0) {
+ readBytes = read(fd, buffer, regionSize);
+ if (readBytes <= 0) {
+ if (readBytes < 0) {
+ ALOGW("[vts] Read from %s failed.", mInputDataFile.c_str());
+ } else {
+ ALOGW("[vts] playback input EOF.");
+ }
+ mPlaybackThreadRunning = false;
+ break;
+ }
+ }
+ if (regionSize == 0 || (readBytes == regionSize && regionSize < totalWrite)) {
+ auto second = memTx.getSecondRegion();
+ buffer = second.getAddress();
+ regionSize = second.getLength();
+ int ret = read(fd, buffer, regionSize);
+ if (ret <= 0) {
+ if (ret < 0) {
+ ALOGW("[vts] Read from %s failed.", mInputDataFile.c_str());
+ } else {
+ ALOGW("[vts] playback input EOF.");
+ }
+ mPlaybackThreadRunning = false;
+ break;
+ }
+ readBytes += ret;
+ }
+ if (!mPlaybackMQ->commitWrite(readBytes)) {
+ ALOGW("[vts] Failed to commit write playback fmq.");
+ mPlaybackThreadRunning = false;
+ break;
+ }
playbackMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
- inputData.seekg(writeSize, inputData.cur);
- sleep(1);
}
}
+ mPlaybackThreadRunning = false;
ALOGW("[vts] Playback thread end.");
-
- delete[] buffer;
- inputData.close();
+ close(fd);
}
void DvrCallback::testRecordOutput() {
@@ -186,32 +210,65 @@
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
// Create dvr callback
- mDvrCallback = new DvrCallback();
+ if (type == DvrType::PLAYBACK) {
+ mDvrPlaybackCallback = new DvrCallback();
+ mDemux->openDvr(type, bufferSize, mDvrPlaybackCallback,
+ [&](Result result, const sp<IDvr>& dvr) {
+ mDvrPlayback = dvr;
+ status = result;
+ });
+ if (status == Result::SUCCESS) {
+ mDvrPlaybackCallback->setDvr(mDvrPlayback);
+ }
+ }
- mDemux->openDvr(type, bufferSize, mDvrCallback, [&](Result result, const sp<IDvr>& dvr) {
- mDvr = dvr;
+ if (type == DvrType::RECORD) {
+ mDvrRecordCallback = new DvrCallback();
+ mDemux->openDvr(type, bufferSize, mDvrRecordCallback,
+ [&](Result result, const sp<IDvr>& dvr) {
+ mDvrRecord = dvr;
+ status = result;
+ });
+ if (status == Result::SUCCESS) {
+ mDvrRecordCallback->setDvr(mDvrRecord);
+ }
+ }
+
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::configDvrPlayback(DvrSettings setting) {
+ Result status = mDvrPlayback->configure(setting);
+
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::configDvrRecord(DvrSettings setting) {
+ Result status = mDvrRecord->configure(setting);
+
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::getDvrPlaybackMQDescriptor() {
+ Result status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
+
+ mDvrPlayback->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
+ mDvrPlaybackMQDescriptor = dvrMQDesc;
status = result;
});
- if (status == Result::SUCCESS) {
- mDvrCallback->setDvr(mDvr);
- }
return AssertionResult(status == Result::SUCCESS);
}
-AssertionResult DvrTests::configDvr(DvrSettings setting) {
- Result status = mDvr->configure(setting);
-
- return AssertionResult(status == Result::SUCCESS);
-}
-
-AssertionResult DvrTests::getDvrMQDescriptor() {
+AssertionResult DvrTests::getDvrRecordMQDescriptor() {
Result status;
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
- EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+ EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
- mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
- mDvrMQDescriptor = dvrMQDesc;
+ mDvrRecord->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
+ mDvrRecordMQDescriptor = dvrMQDesc;
status = result;
});
@@ -221,9 +278,9 @@
AssertionResult DvrTests::attachFilterToDvr(sp<IFilter> filter) {
Result status;
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
- EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+ EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
- status = mDvr->attachFilter(filter);
+ status = mDvrRecord->attachFilter(filter);
return AssertionResult(status == Result::SUCCESS);
}
@@ -231,35 +288,61 @@
AssertionResult DvrTests::detachFilterToDvr(sp<IFilter> filter) {
Result status;
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
- EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+ EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
- status = mDvr->detachFilter(filter);
+ status = mDvrRecord->detachFilter(filter);
return AssertionResult(status == Result::SUCCESS);
}
-AssertionResult DvrTests::startDvr() {
+AssertionResult DvrTests::startDvrPlayback() {
Result status;
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
- EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+ EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
- status = mDvr->start();
+ status = mDvrPlayback->start();
return AssertionResult(status == Result::SUCCESS);
}
-AssertionResult DvrTests::stopDvr() {
+AssertionResult DvrTests::stopDvrPlayback() {
Result status;
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
- EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+ EXPECT_TRUE(mDvrPlayback) << "Test with openDvr first.";
- status = mDvr->stop();
+ status = mDvrPlayback->stop();
return AssertionResult(status == Result::SUCCESS);
}
-void DvrTests::closeDvr() {
+void DvrTests::closeDvrPlayback() {
ASSERT_TRUE(mDemux);
- ASSERT_TRUE(mDvr);
- ASSERT_TRUE(mDvr->close() == Result::SUCCESS);
+ ASSERT_TRUE(mDvrPlayback);
+ ASSERT_TRUE(mDvrPlayback->close() == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::startDvrRecord() {
+ Result status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
+
+ status = mDvrRecord->start();
+
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult DvrTests::stopDvrRecord() {
+ Result status;
+ EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+ EXPECT_TRUE(mDvrRecord) << "Test with openDvr first.";
+
+ status = mDvrRecord->stop();
+
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+void DvrTests::closeDvrRecord() {
+ ASSERT_TRUE(mDemux);
+ ASSERT_TRUE(mDvrRecord);
+ ASSERT_TRUE(mDvrRecord->close() == Result::SUCCESS);
}
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.h b/tv/tuner/1.0/vts/functional/DvrTests.h
index dd00c27..3997839 100644
--- a/tv/tuner/1.0/vts/functional/DvrTests.h
+++ b/tv/tuner/1.0/vts/functional/DvrTests.h
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
#include <android-base/logging.h>
#include <android/hardware/tv/tuner/1.0/IDvr.h>
#include <android/hardware/tv/tuner/1.0/IDvrCallback.h>
#include <android/hardware/tv/tuner/1.0/ITuner.h>
#include <android/hardware/tv/tuner/1.0/types.h>
+#include <fcntl.h>
#include <fmq/MessageQueue.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
#include <hidl/Status.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
@@ -52,6 +53,8 @@
using android::hardware::tv::tuner::V1_0::RecordStatus;
using android::hardware::tv::tuner::V1_0::Result;
+using namespace std;
+
#define WAIT_TIMEOUT 3000000000
class DvrCallback : public IDvrCallback {
@@ -149,25 +152,31 @@
void setDemux(sp<IDemux> demux) { mDemux = demux; }
void startPlaybackInputThread(string& dataInputFile, PlaybackSettings& settings) {
- mDvrCallback->startPlaybackInputThread(dataInputFile, settings, mDvrMQDescriptor);
+ mDvrPlaybackCallback->startPlaybackInputThread(dataInputFile, settings,
+ mDvrPlaybackMQDescriptor);
};
void startRecordOutputThread(RecordSettings settings) {
- mDvrCallback->startRecordOutputThread(settings, mDvrMQDescriptor);
+ mDvrRecordCallback->startRecordOutputThread(settings, mDvrRecordMQDescriptor);
};
- void stopPlaybackThread() { mDvrCallback->stopPlaybackThread(); }
- void testRecordOutput() { mDvrCallback->testRecordOutput(); }
- void stopRecordThread() { mDvrCallback->stopPlaybackThread(); }
+ void stopPlaybackThread() { mDvrPlaybackCallback->stopPlaybackThread(); }
+ void testRecordOutput() { mDvrRecordCallback->testRecordOutput(); }
+ void stopRecordThread() { mDvrRecordCallback->stopRecordThread(); }
AssertionResult openDvrInDemux(DvrType type, uint32_t bufferSize);
- AssertionResult configDvr(DvrSettings setting);
- AssertionResult getDvrMQDescriptor();
+ AssertionResult configDvrPlayback(DvrSettings setting);
+ AssertionResult configDvrRecord(DvrSettings setting);
+ AssertionResult getDvrPlaybackMQDescriptor();
+ AssertionResult getDvrRecordMQDescriptor();
AssertionResult attachFilterToDvr(sp<IFilter> filter);
AssertionResult detachFilterToDvr(sp<IFilter> filter);
- AssertionResult stopDvr();
- AssertionResult startDvr();
- void closeDvr();
+ AssertionResult stopDvrPlayback();
+ AssertionResult startDvrPlayback();
+ AssertionResult stopDvrRecord();
+ AssertionResult startDvrRecord();
+ void closeDvrPlayback();
+ void closeDvrRecord();
protected:
static AssertionResult failure() { return ::testing::AssertionFailure(); }
@@ -175,11 +184,11 @@
static AssertionResult success() { return ::testing::AssertionSuccess(); }
sp<ITuner> mService;
- sp<IDvr> mDvr;
+ sp<IDvr> mDvrPlayback;
+ sp<IDvr> mDvrRecord;
sp<IDemux> mDemux;
- sp<DvrCallback> mDvrCallback;
- MQDesc mDvrMQDescriptor;
-
- pthread_t mPlaybackshread;
- bool mPlaybackThreadRunning;
+ sp<DvrCallback> mDvrPlaybackCallback;
+ sp<DvrCallback> mDvrRecordCallback;
+ MQDesc mDvrPlaybackMQDescriptor;
+ MQDesc mDvrRecordMQDescriptor;
};
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.cpp b/tv/tuner/1.0/vts/functional/FilterTests.cpp
index 4639e59..0ecdf73 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.0/vts/functional/FilterTests.cpp
@@ -149,6 +149,44 @@
return AssertionResult(status == Result::SUCCESS);
}
+AssertionResult FilterTests::openTimeFilterInDemux() {
+ if (!mDemux) {
+ ALOGW("[vts] Test with openDemux first.");
+ return failure();
+ }
+
+ // Add time filter to the local demux
+ Result status;
+ mDemux->openTimeFilter([&](Result result, const sp<ITimeFilter>& filter) {
+ mTimeFilter = filter;
+ status = result;
+ });
+
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult FilterTests::setTimeStamp(uint64_t timeStamp) {
+ if (!mTimeFilter) {
+ ALOGW("[vts] Test with openTimeFilterInDemux first.");
+ return failure();
+ }
+
+ mBeginTimeStamp = timeStamp;
+ return AssertionResult(mTimeFilter->setTimeStamp(timeStamp) == Result::SUCCESS);
+}
+
+AssertionResult FilterTests::getTimeStamp() {
+ if (!mTimeFilter) {
+ ALOGW("[vts] Test with openTimeFilterInDemux first.");
+ return failure();
+ }
+
+ Result status;
+ mTimeFilter->getTimeStamp([&](Result result, uint64_t /*timeStamp*/) { status = result; });
+
+ return AssertionResult(status == Result::SUCCESS);
+}
+
AssertionResult FilterTests::getNewlyOpenedFilterId(uint32_t& filterId) {
Result status;
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
@@ -197,6 +235,26 @@
return AssertionResult(status == Result::SUCCESS);
}
+AssertionResult FilterTests::setFilterDataSource(uint32_t sourceFilterId, uint32_t sinkFilterId) {
+ if (!mFilters[sourceFilterId] || !mFilters[sinkFilterId]) {
+ ALOGE("[vts] setFilterDataSource filter not opened.");
+ return failure();
+ }
+
+ auto status = mFilters[sinkFilterId]->setDataSource(mFilters[sourceFilterId]);
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult FilterTests::setFilterDataSourceToDemux(uint32_t filterId) {
+ if (!mFilters[filterId]) {
+ ALOGE("[vts] setFilterDataSourceToDemux filter not opened.");
+ return failure();
+ }
+
+ auto status = mFilters[filterId]->setDataSource(NULL);
+ return AssertionResult(status == Result::SUCCESS);
+}
+
AssertionResult FilterTests::startFilter(uint32_t filterId) {
EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
Result status = mFilters[filterId]->start();
@@ -209,6 +267,15 @@
return AssertionResult(status == Result::SUCCESS);
}
+AssertionResult FilterTests::clearTimeStamp() {
+ if (!mTimeFilter) {
+ ALOGW("[vts] Test with openTimeFilterInDemux first.");
+ return failure();
+ }
+
+ return AssertionResult(mTimeFilter->clearTimeStamp() == Result::SUCCESS);
+}
+
AssertionResult FilterTests::closeFilter(uint32_t filterId) {
EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
Result status = mFilters[filterId]->close();
@@ -224,3 +291,12 @@
}
return AssertionResult(status == Result::SUCCESS);
}
+
+AssertionResult FilterTests::closeTimeFilter() {
+ if (!mTimeFilter) {
+ ALOGW("[vts] Test with openTimeFilterInDemux first.");
+ return failure();
+ }
+
+ return AssertionResult(mTimeFilter->close() == Result::SUCCESS);
+}
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.h b/tv/tuner/1.0/vts/functional/FilterTests.h
index 71efce4..a76a6b9 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.h
+++ b/tv/tuner/1.0/vts/functional/FilterTests.h
@@ -57,6 +57,7 @@
using android::hardware::tv::tuner::V1_0::IDemux;
using android::hardware::tv::tuner::V1_0::IFilter;
using android::hardware::tv::tuner::V1_0::IFilterCallback;
+using android::hardware::tv::tuner::V1_0::ITimeFilter;
using android::hardware::tv::tuner::V1_0::ITuner;
using android::hardware::tv::tuner::V1_0::Result;
@@ -151,12 +152,19 @@
std::map<uint32_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; }
AssertionResult openFilterInDemux(DemuxFilterType type, uint32_t bufferSize);
+ AssertionResult openTimeFilterInDemux();
+ AssertionResult setTimeStamp(uint64_t timeStamp);
+ AssertionResult getTimeStamp();
AssertionResult getNewlyOpenedFilterId(uint32_t& filterId);
AssertionResult configFilter(DemuxFilterSettings setting, uint32_t filterId);
AssertionResult getFilterMQDescriptor(uint32_t filterId);
+ AssertionResult setFilterDataSource(uint32_t sourceFilterId, uint32_t sinkFilterId);
+ AssertionResult setFilterDataSourceToDemux(uint32_t filterId);
AssertionResult startFilter(uint32_t filterId);
+ AssertionResult clearTimeStamp();
AssertionResult stopFilter(uint32_t filterId);
AssertionResult closeFilter(uint32_t filterId);
+ AssertionResult closeTimeFilter();
FilterEventType getFilterEventType(DemuxFilterType type) {
FilterEventType eventType = FilterEventType::UNDEFINED;
@@ -212,6 +220,7 @@
sp<ITuner> mService;
sp<IFilter> mFilter;
+ sp<ITimeFilter> mTimeFilter;
sp<IDemux> mDemux;
std::map<uint32_t, sp<IFilter>> mFilters;
std::map<uint32_t, sp<FilterCallback>> mFilterCallbacks;
@@ -221,4 +230,5 @@
vector<uint32_t> mUsedFilterIds;
uint32_t mFilterId = -1;
+ uint64_t mBeginTimeStamp;
};
diff --git a/tv/tuner/1.0/vts/functional/FrontendTests.cpp b/tv/tuner/1.0/vts/functional/FrontendTests.cpp
index d54a959..45951d2 100644
--- a/tv/tuner/1.0/vts/functional/FrontendTests.cpp
+++ b/tv/tuner/1.0/vts/functional/FrontendTests.cpp
@@ -360,21 +360,48 @@
ASSERT_TRUE(status == Result::SUCCESS);
}
-AssertionResult FrontendTests::tuneFrontend(FrontendConfig config) {
+AssertionResult FrontendTests::tuneFrontend(FrontendConfig config, bool testWithDemux) {
EXPECT_TRUE(mFrontendCallback)
<< "test with openFrontendById/setFrontendCallback/getFrontendInfo first.";
EXPECT_TRUE(mFrontendInfo.type == config.type)
<< "FrontendConfig does not match the frontend info of the given id.";
+ mIsSoftwareFe = config.isSoftwareFe;
+ bool result = true;
+ if (mIsSoftwareFe && testWithDemux) {
+ DvrConfig dvrConfig;
+ getSoftwareFrontendPlaybackConfig(dvrConfig);
+ result &= mDvrTests.openDvrInDemux(dvrConfig.type, dvrConfig.bufferSize) == success();
+ result &= mDvrTests.configDvrPlayback(dvrConfig.settings) == success();
+ result &= mDvrTests.getDvrPlaybackMQDescriptor() == success();
+ mDvrTests.startPlaybackInputThread(dvrConfig.playbackInputFile,
+ dvrConfig.settings.playback());
+ if (!result) {
+ ALOGW("[vts] Software frontend dvr configure failed.");
+ return failure();
+ }
+ }
mFrontendCallback->tuneTestOnLock(mFrontend, config.settings);
return AssertionResult(true);
}
-AssertionResult FrontendTests::stopTuneFrontend() {
+AssertionResult FrontendTests::setLnb(uint32_t lnbId) {
+ if (!mFrontendCallback) {
+ ALOGW("[vts] open and set frontend callback first.");
+ return failure();
+ }
+ return AssertionResult(mFrontend->setLnb(lnbId) == Result::SUCCESS);
+}
+
+AssertionResult FrontendTests::stopTuneFrontend(bool testWithDemux) {
EXPECT_TRUE(mFrontend) << "Test with openFrontendById first.";
Result status;
status = mFrontend->stopTune();
+ if (mIsSoftwareFe && testWithDemux) {
+ mDvrTests.stopPlaybackThread();
+ mDvrTests.closeDvrPlayback();
+ }
return AssertionResult(status == Result::SUCCESS);
}
@@ -407,9 +434,9 @@
ASSERT_TRUE(feId != INVALID_ID);
ASSERT_TRUE(openFrontendById(feId));
ASSERT_TRUE(setFrontendCallback());
- ASSERT_TRUE(tuneFrontend(frontendConf));
+ ASSERT_TRUE(tuneFrontend(frontendConf, false /*testWithDemux*/));
verifyFrontendStatus(frontendConf.tuneStatusTypes, frontendConf.expectTuneStatuses);
- ASSERT_TRUE(stopTuneFrontend());
+ ASSERT_TRUE(stopTuneFrontend(false /*testWithDemux*/));
ASSERT_TRUE(closeFrontend());
}
diff --git a/tv/tuner/1.0/vts/functional/FrontendTests.h b/tv/tuner/1.0/vts/functional/FrontendTests.h
index 2bdc8fd..c536325 100644
--- a/tv/tuner/1.0/vts/functional/FrontendTests.h
+++ b/tv/tuner/1.0/vts/functional/FrontendTests.h
@@ -31,6 +31,7 @@
#include <utils/Mutex.h>
#include <map>
+#include "DvrTests.h"
#include "VtsHalTvTunerV1_0TestConfigurations.h"
#define WAIT_TIMEOUT 3000000000
@@ -100,7 +101,10 @@
public:
sp<ITuner> mService;
- void setService(sp<ITuner> tuner) { mService = tuner; }
+ void setService(sp<ITuner> tuner) {
+ mService = tuner;
+ mDvrTests.setService(tuner);
+ }
AssertionResult getFrontendIds();
AssertionResult getFrontendInfo(uint32_t frontendId);
@@ -108,19 +112,43 @@
AssertionResult setFrontendCallback();
AssertionResult scanFrontend(FrontendConfig config, FrontendScanType type);
AssertionResult stopScanFrontend();
- AssertionResult tuneFrontend(FrontendConfig config);
+ AssertionResult tuneFrontend(FrontendConfig config, bool testWithDemux);
+ AssertionResult setLnb(uint32_t lnbId);
void verifyFrontendStatus(vector<FrontendStatusType> statusTypes,
vector<FrontendStatus> expectStatuses);
- AssertionResult stopTuneFrontend();
+ AssertionResult stopTuneFrontend(bool testWithDemux);
AssertionResult closeFrontend();
void getFrontendIdByType(FrontendType feType, uint32_t& feId);
void tuneTest(FrontendConfig frontendConf);
void scanTest(FrontendConfig frontend, FrontendScanType type);
+ void setDvrTests(DvrTests dvrTests) { mDvrTests = dvrTests; }
+ void setDemux(sp<IDemux> demux) { mDvrTests.setDemux(demux); }
+
protected:
+ static AssertionResult failure() { return ::testing::AssertionFailure(); }
+ static AssertionResult success() { return ::testing::AssertionSuccess(); }
+
+ void getSoftwareFrontendPlaybackConfig(DvrConfig& dvrConfig) {
+ PlaybackSettings playbackSettings{
+ .statusMask = 0xf,
+ .lowThreshold = 0x1000,
+ .highThreshold = 0x07fff,
+ .dataFormat = DataFormat::TS,
+ .packetSize = 188,
+ };
+ dvrConfig.type = DvrType::PLAYBACK;
+ dvrConfig.playbackInputFile = "/data/local/tmp/segment000000.ts";
+ dvrConfig.bufferSize = FMQ_SIZE_4M;
+ dvrConfig.settings.playback(playbackSettings);
+ }
+
sp<IFrontend> mFrontend;
FrontendInfo mFrontendInfo;
sp<FrontendCallback> mFrontendCallback;
hidl_vec<FrontendId> mFeIds;
+
+ DvrTests mDvrTests;
+ bool mIsSoftwareFe = false;
};
diff --git a/tv/tuner/1.0/vts/functional/LnbTests.cpp b/tv/tuner/1.0/vts/functional/LnbTests.cpp
new file mode 100644
index 0000000..9080f59
--- /dev/null
+++ b/tv/tuner/1.0/vts/functional/LnbTests.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "LnbTests.h"
+
+Return<void> LnbCallback::onEvent(LnbEventType lnbEventType) {
+ android::Mutex::Autolock autoLock(mMsgLock);
+ ALOGD("[vts] lnb event received. Type: %d", lnbEventType);
+ mEventReceived = true;
+ mMsgCondition.signal();
+ return Void();
+}
+
+Return<void> LnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) {
+ string msg(diseqcMessage.begin(), diseqcMessage.end());
+ ALOGD("[vts] onDiseqcMessage %s", msg.c_str());
+ return Void();
+}
+
+AssertionResult LnbTests::getLnbIds(vector<uint32_t>& ids) {
+ Result status;
+ mService->getLnbIds([&](Result result, const hidl_vec<uint32_t>& lnbIds) {
+ status = result;
+ ids = lnbIds;
+ });
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult LnbTests::openLnbById(uint32_t lnbId) {
+ Result status;
+ mService->openLnbById(lnbId, [&](Result result, const sp<ILnb>& lnb) {
+ mLnb = lnb;
+ status = result;
+ });
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult LnbTests::openLnbByName(string lnbName) {
+ Result status;
+ mService->openLnbByName(lnbName, [&](Result result, uint32_t /*lnbId*/, const sp<ILnb>& lnb) {
+ mLnb = lnb;
+ status = result;
+ });
+
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult LnbTests::setLnbCallback() {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ mLnbCallback = new LnbCallback();
+ auto callbackStatus = mLnb->setCallback(mLnbCallback);
+ return AssertionResult(callbackStatus.isOk());
+}
+
+AssertionResult LnbTests::setVoltage(LnbVoltage voltage) {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ Result status = mLnb->setVoltage(voltage);
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult LnbTests::setTone(LnbTone tone) {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ Result status = mLnb->setTone(tone);
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult LnbTests::setSatellitePosition(LnbPosition position) {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ Result status = mLnb->setSatellitePosition(position);
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult LnbTests::sendDiseqcMessage(vector<uint8_t> diseqcMsg) {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ Result status = mLnb->sendDiseqcMessage(diseqcMsg);
+ return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult LnbTests::closeLnb() {
+ if (!mLnb) {
+ ALOGW("[vts] Open Lnb first");
+ return failure();
+ }
+ Result status = mLnb->close();
+ mLnb = nullptr;
+ mLnbCallback = nullptr;
+ return AssertionResult(status == Result::SUCCESS);
+}
diff --git a/tv/tuner/1.0/vts/functional/LnbTests.h b/tv/tuner/1.0/vts/functional/LnbTests.h
new file mode 100644
index 0000000..2fdbe2c
--- /dev/null
+++ b/tv/tuner/1.0/vts/functional/LnbTests.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/tv/tuner/1.0/ILnb.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <android/hardware/tv/tuner/1.0/types.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <hidl/Status.h>
+#include <hidlmemory/FrameworkUtils.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <map>
+
+using android::Condition;
+using android::Mutex;
+using android::sp;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::tv::tuner::V1_0::ILnb;
+using android::hardware::tv::tuner::V1_0::ILnbCallback;
+using android::hardware::tv::tuner::V1_0::ITuner;
+using android::hardware::tv::tuner::V1_0::LnbEventType;
+using android::hardware::tv::tuner::V1_0::LnbPosition;
+using android::hardware::tv::tuner::V1_0::LnbTone;
+using android::hardware::tv::tuner::V1_0::LnbVoltage;
+using android::hardware::tv::tuner::V1_0::Result;
+
+using ::testing::AssertionResult;
+
+using namespace std;
+
+class LnbCallback : public ILnbCallback {
+ public:
+ virtual Return<void> onEvent(LnbEventType lnbEventType) override;
+ virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) override;
+
+ private:
+ bool mEventReceived = false;
+ android::Mutex mMsgLock;
+ android::Condition mMsgCondition;
+};
+
+class LnbTests {
+ public:
+ void setService(sp<ITuner> tuner) { mService = tuner; }
+
+ AssertionResult getLnbIds(vector<uint32_t>& ids);
+ AssertionResult openLnbById(uint32_t lnbId);
+ AssertionResult openLnbByName(string lnbName);
+ AssertionResult setLnbCallback();
+ AssertionResult setVoltage(LnbVoltage voltage);
+ AssertionResult setTone(LnbTone tone);
+ AssertionResult setSatellitePosition(LnbPosition position);
+ AssertionResult sendDiseqcMessage(vector<uint8_t> diseqcMsg);
+ AssertionResult closeLnb();
+
+ protected:
+ static AssertionResult failure() { return ::testing::AssertionFailure(); }
+
+ static AssertionResult success() { return ::testing::AssertionSuccess(); }
+
+ sp<ITuner> mService;
+ sp<ILnb> mLnb;
+ sp<LnbCallback> mLnbCallback;
+ hidl_vec<uint32_t> mLnbIds;
+};
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index a365282..6819659 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -56,6 +56,23 @@
ASSERT_TRUE(mFrontendTests.closeFrontend());
}
+void TunerFilterHidlTest::testTimeFilter(TimeFilterConfig filterConf) {
+ if (!filterConf.supportTimeFilter) {
+ return;
+ }
+ uint32_t demuxId;
+ sp<IDemux> demux;
+
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ mFilterTests.setDemux(demux);
+ ASSERT_TRUE(mFilterTests.openTimeFilterInDemux());
+ ASSERT_TRUE(mFilterTests.setTimeStamp(filterConf.timeStamp));
+ ASSERT_TRUE(mFilterTests.getTimeStamp());
+ ASSERT_TRUE(mFilterTests.clearTimeStamp());
+ ASSERT_TRUE(mFilterTests.closeTimeFilter());
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+}
+
void TunerBroadcastHidlTest::broadcastSingleFilterTest(FilterConfig filterConf,
FrontendConfig frontendConf) {
uint32_t feId;
@@ -72,8 +89,12 @@
}
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (mLnbId) {
+ ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
+ }
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFrontendTests.setDemux(demux);
mFilterTests.setDemux(demux);
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
@@ -81,15 +102,35 @@
ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
ASSERT_TRUE(mFilterTests.startFilter(filterId));
// tune test
- ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf));
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
- ASSERT_TRUE(mFrontendTests.stopTuneFrontend());
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
ASSERT_TRUE(mDemuxTests.closeDemux());
ASSERT_TRUE(mFrontendTests.closeFrontend());
}
+void TunerBroadcastHidlTest::broadcastSingleFilterTestWithLnb(FilterConfig filterConf,
+ FrontendConfig frontendConf,
+ LnbConfig lnbConf) {
+ vector<uint32_t> ids;
+ ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+ if (!lnbConf.usingLnb) {
+ return;
+ }
+ ASSERT_TRUE(ids.size() > 0);
+ ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ *mLnbId = ids[0];
+ ASSERT_TRUE(mLnbTests.setLnbCallback());
+ ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
+ ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
+ ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbConf.position));
+ broadcastSingleFilterTest(filterConf, frontendConf);
+ ASSERT_TRUE(mLnbTests.closeLnb());
+ mLnbId = nullptr;
+}
+
void TunerPlaybackHidlTest::playbackSingleFilterTest(FilterConfig filterConf, DvrConfig dvrConf) {
uint32_t demuxId;
sp<IDemux> demux;
@@ -99,21 +140,21 @@
mFilterTests.setDemux(demux);
mDvrTests.setDemux(demux);
ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
- ASSERT_TRUE(mDvrTests.configDvr(dvrConf.settings));
- ASSERT_TRUE(mDvrTests.getDvrMQDescriptor());
+ ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrConf.settings));
+ ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
mDvrTests.startPlaybackInputThread(dvrConf.playbackInputFile, dvrConf.settings.playback());
- ASSERT_TRUE(mDvrTests.startDvr());
+ ASSERT_TRUE(mDvrTests.startDvrPlayback());
ASSERT_TRUE(mFilterTests.startFilter(filterId));
ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
mDvrTests.stopPlaybackThread();
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
- ASSERT_TRUE(mDvrTests.stopDvr());
+ ASSERT_TRUE(mDvrTests.stopDvrPlayback());
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
- mDvrTests.closeDvr();
+ mDvrTests.closeDvrPlayback();
ASSERT_TRUE(mDemuxTests.closeDemux());
}
@@ -129,13 +170,17 @@
ASSERT_TRUE(feId != INVALID_ID);
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ if (mLnbId) {
+ ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
+ }
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFilterTests.setDemux(demux);
mDvrTests.setDemux(demux);
+ mFrontendTests.setDvrTests(mDvrTests);
ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
- ASSERT_TRUE(mDvrTests.configDvr(dvrConf.settings));
- ASSERT_TRUE(mDvrTests.getDvrMQDescriptor());
+ ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
+ ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
@@ -144,21 +189,41 @@
ASSERT_TRUE(filter != nullptr);
mDvrTests.startRecordOutputThread(dvrConf.settings.record());
ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
- ASSERT_TRUE(mDvrTests.startDvr());
+ ASSERT_TRUE(mDvrTests.startDvrRecord());
ASSERT_TRUE(mFilterTests.startFilter(filterId));
- ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf));
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
mDvrTests.testRecordOutput();
mDvrTests.stopRecordThread();
- ASSERT_TRUE(mFrontendTests.stopTuneFrontend());
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
- ASSERT_TRUE(mDvrTests.stopDvr());
+ ASSERT_TRUE(mDvrTests.stopDvrRecord());
ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
- mDvrTests.closeDvr();
+ mDvrTests.closeDvrRecord();
ASSERT_TRUE(mDemuxTests.closeDemux());
ASSERT_TRUE(mFrontendTests.closeFrontend());
}
+void TunerRecordHidlTest::recordSingleFilterTestWithLnb(FilterConfig filterConf,
+ FrontendConfig frontendConf,
+ DvrConfig dvrConf, LnbConfig lnbConf) {
+ vector<uint32_t> ids;
+ ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+ if (!lnbConf.usingLnb) {
+ return;
+ }
+ ASSERT_TRUE(ids.size() > 0);
+ ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ *mLnbId = ids[0];
+ ASSERT_TRUE(mLnbTests.setLnbCallback());
+ ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
+ ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
+ ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbConf.position));
+ recordSingleFilterTest(filterConf, frontendConf, dvrConf);
+ ASSERT_TRUE(mLnbTests.closeLnb());
+ mLnbId = nullptr;
+}
+
void TunerRecordHidlTest::attachSingleFilterToRecordDvrTest(FilterConfig filterConf,
FrontendConfig frontendConf,
DvrConfig dvrConf) {
@@ -177,8 +242,8 @@
mFilterTests.setDemux(demux);
mDvrTests.setDemux(demux);
ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
- ASSERT_TRUE(mDvrTests.configDvr(dvrConf.settings));
- ASSERT_TRUE(mDvrTests.getDvrMQDescriptor());
+ ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
+ ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
@@ -186,13 +251,13 @@
filter = mFilterTests.getFilterById(filterId);
ASSERT_TRUE(filter != nullptr);
ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
- ASSERT_TRUE(mDvrTests.startDvr());
+ ASSERT_TRUE(mDvrTests.startDvrRecord());
ASSERT_TRUE(mFilterTests.startFilter(filterId));
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
- ASSERT_TRUE(mDvrTests.stopDvr());
+ ASSERT_TRUE(mDvrTests.stopDvrRecord());
ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
- mDvrTests.closeDvr();
+ mDvrTests.closeDvrRecord();
ASSERT_TRUE(mDemuxTests.closeDemux());
ASSERT_TRUE(mFrontendTests.closeFrontend());
}
@@ -220,6 +285,7 @@
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFilterTests.setDemux(demux);
+ mFrontendTests.setDemux(demux);
for (config = mediaFilterConfs.begin(); config != mediaFilterConfs.end(); config++) {
ASSERT_TRUE(mFilterTests.openFilterInDemux((*config).type, (*config).bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
@@ -243,9 +309,9 @@
ASSERT_TRUE(mFilterTests.startFilter(*id));
}
// tune test
- ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf));
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
- ASSERT_TRUE(mFrontendTests.stopTuneFrontend());
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
for (id = filterIds.begin(); id != filterIds.end(); id++) {
ASSERT_TRUE(mFilterTests.stopFilter(*id));
}
@@ -275,6 +341,34 @@
mFrontendTests.scanTest(frontendScanArray[SCAN_DVBT], FrontendScanType::SCAN_BLIND);
}
+TEST_P(TunerLnbHidlTest, OpenLnbByName) {
+ description("Open and configure an Lnb with name then send a diseqc msg to it.");
+ ASSERT_TRUE(mLnbTests.openLnbByName(lnbArray[LNB_EXTERNAL].name));
+ ASSERT_TRUE(mLnbTests.setLnbCallback());
+ ASSERT_TRUE(mLnbTests.setVoltage(lnbArray[LNB_EXTERNAL].voltage));
+ ASSERT_TRUE(mLnbTests.setTone(lnbArray[LNB_EXTERNAL].tone));
+ ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbArray[LNB_EXTERNAL].position));
+ ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgArray[DISEQC_POWER_ON]));
+ ASSERT_TRUE(mLnbTests.closeLnb());
+}
+
+TEST_P(TunerLnbHidlTest, SendDiseqcMessageToLnb) {
+ description("Open and configure an Lnb with specific settings then send a diseqc msg to it.");
+ vector<uint32_t> ids;
+ ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+ if (!lnbArray[LNB0].usingLnb) {
+ return;
+ }
+ ASSERT_TRUE(ids.size() > 0);
+ ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+ ASSERT_TRUE(mLnbTests.setLnbCallback());
+ ASSERT_TRUE(mLnbTests.setVoltage(lnbArray[LNB0].voltage));
+ ASSERT_TRUE(mLnbTests.setTone(lnbArray[LNB0].tone));
+ ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbArray[LNB0].position));
+ ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgArray[DISEQC_POWER_ON]));
+ ASSERT_TRUE(mLnbTests.closeLnb());
+}
+
TEST_P(TunerDemuxHidlTest, openDemux) {
description("Open and close a Demux.");
uint32_t feId;
@@ -331,23 +425,64 @@
configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
}
+TEST_P(TunerFilterHidlTest, SetFilterLinkage) {
+ description("Pick up all the possible linkages from the demux caps and set them up.");
+ DemuxCapabilities caps;
+ uint32_t demuxId;
+ sp<IDemux> demux;
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.getDemuxCaps(caps));
+ mFilterTests.setDemux(demux);
+ for (int i = 0; i < caps.linkCaps.size(); i++) {
+ uint32_t bitMask = 1;
+ for (int j = 0; j < FILTER_MAIN_TYPE_BIT_COUNT; j++) {
+ if (caps.linkCaps[i] & (bitMask << j)) {
+ uint32_t sourceFilterId;
+ uint32_t sinkFilterId;
+ ASSERT_TRUE(mFilterTests.openFilterInDemux(filterLinkageTypes[SOURCE][i],
+ FMQ_SIZE_16M));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(sourceFilterId));
+ ASSERT_TRUE(
+ mFilterTests.openFilterInDemux(filterLinkageTypes[SINK][j], FMQ_SIZE_16M));
+ ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(sinkFilterId));
+ ASSERT_TRUE(mFilterTests.setFilterDataSource(sourceFilterId, sinkFilterId));
+ ASSERT_TRUE(mFilterTests.setFilterDataSourceToDemux(sinkFilterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(sinkFilterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(sourceFilterId));
+ }
+ }
+ }
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+}
+
+TEST_P(TunerFilterHidlTest, testTimeFilter) {
+ description("Open a timer filter in Demux and set time stamp.");
+ // TODO use paramterized tests
+ testTimeFilter(timeFilterArray[TIMER0]);
+}
+
TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowVideoFilterTest) {
description("Test Video Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_VIDEO1], frontendArray[DVBS]);
+ broadcastSingleFilterTest(filterArray[TS_VIDEO1], frontendArray[DVBT]);
}
TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowAudioFilterTest) {
description("Test Audio Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_AUDIO0], frontendArray[DVBS]);
+ broadcastSingleFilterTest(filterArray[TS_AUDIO0], frontendArray[DVBT]);
}
TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowSectionFilterTest) {
description("Test Section Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_SECTION0], frontendArray[DVBS]);
+ broadcastSingleFilterTest(filterArray[TS_SECTION0], frontendArray[DVBT]);
}
TEST_P(TunerBroadcastHidlTest, IonBufferTest) {
description("Test the av filter data bufferring.");
+ broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
+}
+
+TEST_P(TunerBroadcastHidlTest, LnbBroadcastDataFlowVideoFilterTest) {
+ description("Test Video Filter functionality in Broadcast with Lnb use case.");
broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[DVBS]);
}
@@ -368,6 +503,11 @@
recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[DVBT], dvrArray[DVR_RECORD0]);
}
+TEST_P(TunerRecordHidlTest, LnbRecordDataFlowWithTsRecordFilterTest) {
+ description("Feed ts data from Fe with Lnb to recording and test with ts record filter");
+ recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[DVBS], dvrArray[DVR_RECORD0]);
+}
+
TEST_P(TunerDescramblerHidlTest, CreateDescrambler) {
description("Create Descrambler");
uint32_t feId;
@@ -393,12 +533,17 @@
scrambledBroadcastTest(filterConfs, frontendArray[DVBT], descramblerArray[DESC_0]);
}
-/*INSTANTIATE_TEST_SUITE_P(
+INSTANTIATE_TEST_SUITE_P(
PerInstance, TunerFrontendHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
android::hardware::PrintInstanceNameToString);
INSTANTIATE_TEST_SUITE_P(
+ PerInstance, TunerLnbHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(
PerInstance, TunerDemuxHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
android::hardware::PrintInstanceNameToString);
@@ -421,7 +566,7 @@
INSTANTIATE_TEST_SUITE_P(
PerInstance, TunerRecordHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
- android::hardware::PrintInstanceNameToString);*/
+ android::hardware::PrintInstanceNameToString);
INSTANTIATE_TEST_SUITE_P(
PerInstance, TunerDescramblerHidlTest,
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
index 2bdb537..6804f3c 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
@@ -16,8 +16,8 @@
#include "DemuxTests.h"
#include "DescramblerTests.h"
-#include "DvrTests.h"
#include "FrontendTests.h"
+#include "LnbTests.h"
using android::hardware::tv::tuner::V1_0::DataFormat;
using android::hardware::tv::tuner::V1_0::IDescrambler;
@@ -31,7 +31,9 @@
void initConfiguration() {
initFrontendConfig();
initFrontendScanConfig();
+ initLnbConfig();
initFilterConfig();
+ initTimeFilterConfig();
initDvrConfig();
initDescramblerConfig();
}
@@ -65,6 +67,25 @@
FrontendTests mFrontendTests;
};
+class TunerLnbHidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ mService = ITuner::getService(GetParam());
+ ASSERT_NE(mService, nullptr);
+ initConfiguration();
+
+ mLnbTests.setService(mService);
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ sp<ITuner> mService;
+ LnbTests mLnbTests;
+};
+
class TunerDemuxHidlTest : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
@@ -106,6 +127,7 @@
}
void configSingleFilterInDemuxTest(FilterConfig filterConf, FrontendConfig frontendConf);
+ void testTimeFilter(TimeFilterConfig filterConf);
sp<ITuner> mService;
FrontendTests mFrontendTests;
@@ -123,6 +145,8 @@
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
mFilterTests.setService(mService);
+ mLnbTests.setService(mService);
+ mDvrTests.setService(mService);
}
protected:
@@ -134,10 +158,17 @@
FrontendTests mFrontendTests;
DemuxTests mDemuxTests;
FilterTests mFilterTests;
+ LnbTests mLnbTests;
+ DvrTests mDvrTests;
AssertionResult filterDataOutputTest(vector<string> goldenOutputFiles);
void broadcastSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf);
+ void broadcastSingleFilterTestWithLnb(FilterConfig filterConf, FrontendConfig frontendConf,
+ LnbConfig lnbConf);
+
+ private:
+ uint32_t* mLnbId = nullptr;
};
class TunerPlaybackHidlTest : public testing::TestWithParam<std::string> {
@@ -180,6 +211,7 @@
mDemuxTests.setService(mService);
mFilterTests.setService(mService);
mDvrTests.setService(mService);
+ mLnbTests.setService(mService);
}
protected:
@@ -191,12 +223,18 @@
DvrConfig dvrConf);
void recordSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf,
DvrConfig dvrConf);
+ void recordSingleFilterTestWithLnb(FilterConfig filterConf, FrontendConfig frontendConf,
+ DvrConfig dvrConf, LnbConfig lnbConf);
sp<ITuner> mService;
FrontendTests mFrontendTests;
DemuxTests mDemuxTests;
FilterTests mFilterTests;
DvrTests mDvrTests;
+ LnbTests mLnbTests;
+
+ private:
+ uint32_t* mLnbId = nullptr;
};
class TunerDescramblerHidlTest : public testing::TestWithParam<std::string> {
@@ -210,6 +248,7 @@
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
+ mDvrTests.setService(mService);
mDescramblerTests.setService(mService);
mDescramblerTests.setCasService(mCasService);
}
@@ -229,5 +268,6 @@
DemuxTests mDemuxTests;
FilterTests mFilterTests;
DescramblerTests mDescramblerTests;
+ DvrTests mDvrTests;
};
} // namespace
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index f2dd197..6c68e35 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -22,11 +22,15 @@
#include <hidlmemory/FrameworkUtils.h>
using android::hardware::tv::tuner::V1_0::DataFormat;
+using android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
using android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
using android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType;
+using android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
using android::hardware::tv::tuner::V1_0::DemuxTpid;
using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
using android::hardware::tv::tuner::V1_0::DvrSettings;
@@ -43,6 +47,9 @@
using android::hardware::tv::tuner::V1_0::FrontendStatus;
using android::hardware::tv::tuner::V1_0::FrontendStatusType;
using android::hardware::tv::tuner::V1_0::FrontendType;
+using android::hardware::tv::tuner::V1_0::LnbPosition;
+using android::hardware::tv::tuner::V1_0::LnbTone;
+using android::hardware::tv::tuner::V1_0::LnbVoltage;
using android::hardware::tv::tuner::V1_0::PlaybackSettings;
using android::hardware::tv::tuner::V1_0::RecordSettings;
@@ -53,6 +60,7 @@
const uint32_t FMQ_SIZE_16M = 0x1000000;
#define CLEAR_KEY_SYSTEM_ID 0xF6D8
+#define FILTER_MAIN_TYPE_BIT_COUNT 32
#define PROVISION_STR \
"{ " \
" \"id\": 21140844, " \
@@ -78,12 +86,34 @@
} Filter;
typedef enum {
+ TIMER0,
+ TIMER_MAX,
+} TimeFilter;
+
+typedef enum {
+ SOURCE,
+ SINK,
+ LINKAGE_DIR,
+} Linkage;
+
+typedef enum {
DVBT,
DVBS,
FRONTEND_MAX,
} Frontend;
typedef enum {
+ LNB0,
+ LNB_EXTERNAL,
+ LNB_MAX,
+} Lnb;
+
+typedef enum {
+ DISEQC_POWER_ON,
+ DISEQC_MAX,
+} Diseqc;
+
+typedef enum {
SCAN_DVBT,
SCAN_MAX,
} FrontendScan;
@@ -91,6 +121,7 @@
typedef enum {
DVR_RECORD0,
DVR_PLAYBACK0,
+ DVR_SOFTWARE_FE,
DVR_MAX,
} Dvr;
@@ -107,13 +138,27 @@
bool operator<(const FilterConfig& /*c*/) const { return false; }
};
+struct TimeFilterConfig {
+ bool supportTimeFilter;
+ uint64_t timeStamp;
+};
+
struct FrontendConfig {
+ bool isSoftwareFe;
FrontendType type;
FrontendSettings settings;
vector<FrontendStatusType> tuneStatusTypes;
vector<FrontendStatus> expectTuneStatuses;
};
+struct LnbConfig {
+ bool usingLnb;
+ string name;
+ LnbVoltage voltage;
+ LnbTone tone;
+ LnbPosition position;
+};
+
struct ChannelConfig {
int32_t frontendId;
int32_t channelId;
@@ -137,8 +182,12 @@
static FrontendConfig frontendArray[FILTER_MAX];
static FrontendConfig frontendScanArray[SCAN_MAX];
+static LnbConfig lnbArray[LNB_MAX];
+static vector<uint8_t> diseqcMsgArray[DISEQC_MAX];
static ChannelConfig channelArray[FRONTEND_MAX];
static FilterConfig filterArray[FILTER_MAX];
+static TimeFilterConfig timeFilterArray[TIMER_MAX];
+static DemuxFilterType filterLinkageTypes[LINKAGE_DIR][FILTER_MAIN_TYPE_BIT_COUNT];
static DvrConfig dvrArray[DVR_MAX];
static DescramblerConfig descramblerArray[DESC_MAX];
static vector<string> goldenOutputFiles;
@@ -166,7 +215,9 @@
statuses.push_back(status);
frontendArray[DVBT].tuneStatusTypes = types;
frontendArray[DVBT].expectTuneStatuses = statuses;
+ frontendArray[DVBT].isSoftwareFe = true;
frontendArray[DVBS].type = FrontendType::DVBS;
+ frontendArray[DVBS].isSoftwareFe = true;
};
/** Configuration array for the frontend scan test */
@@ -186,6 +237,24 @@
});
};
+/** Configuration array for the Lnb test */
+inline void initLnbConfig() {
+ lnbArray[LNB0].usingLnb = true;
+ lnbArray[LNB0].voltage = LnbVoltage::VOLTAGE_12V;
+ lnbArray[LNB0].tone = LnbTone::NONE;
+ lnbArray[LNB0].position = LnbPosition::UNDEFINED;
+ lnbArray[LNB_EXTERNAL].usingLnb = true;
+ lnbArray[LNB_EXTERNAL].name = "default_lnb_external";
+ lnbArray[LNB_EXTERNAL].voltage = LnbVoltage::VOLTAGE_5V;
+ lnbArray[LNB_EXTERNAL].tone = LnbTone::NONE;
+ lnbArray[LNB_EXTERNAL].position = LnbPosition::UNDEFINED;
+};
+
+/** Diseqc messages array for the Lnb test */
+inline void initDiseqcMsg() {
+ diseqcMsgArray[DISEQC_POWER_ON] = {0xE, 0x0, 0x0, 0x0, 0x0, 0x3};
+};
+
/** Configuration array for the filter test */
inline void initFilterConfig() {
// TS VIDEO filter setting for default implementation testing
@@ -241,8 +310,35 @@
filterArray[TS_RECORD0].settings.ts().filterSettings.record({
.scIndexType = DemuxRecordScIndexType::NONE,
});
+
+ // TS Linkage filter setting
+ filterLinkageTypes[SOURCE][0].mainType = DemuxFilterMainType::TS;
+ filterLinkageTypes[SOURCE][0].subType.tsFilterType(DemuxTsFilterType::TS);
+ filterLinkageTypes[SINK][0] = filterLinkageTypes[SOURCE][0];
+ // MMTP Linkage filter setting
+ filterLinkageTypes[SOURCE][1].mainType = DemuxFilterMainType::MMTP;
+ filterLinkageTypes[SOURCE][1].subType.mmtpFilterType(DemuxMmtpFilterType::AUDIO);
+ filterLinkageTypes[SINK][1] = filterLinkageTypes[SOURCE][1];
+ // IP Linkage filter setting
+ filterLinkageTypes[SOURCE][2].mainType = DemuxFilterMainType::IP;
+ filterLinkageTypes[SOURCE][2].subType.ipFilterType(DemuxIpFilterType::IP);
+ filterLinkageTypes[SINK][2] = filterLinkageTypes[SOURCE][2];
+ // TLV Linkage filter setting
+ filterLinkageTypes[SOURCE][3].mainType = DemuxFilterMainType::TLV;
+ filterLinkageTypes[SOURCE][3].subType.tlvFilterType(DemuxTlvFilterType::TLV);
+ filterLinkageTypes[SINK][3] = filterLinkageTypes[SOURCE][3];
+ // ALP Linkage PTP filter setting
+ filterLinkageTypes[SOURCE][4].mainType = DemuxFilterMainType::ALP;
+ filterLinkageTypes[SOURCE][4].subType.alpFilterType(DemuxAlpFilterType::PTP);
+ filterLinkageTypes[SINK][4] = filterLinkageTypes[SOURCE][4];
};
+/** Configuration array for the timer filter test */
+inline void initTimeFilterConfig() {
+ timeFilterArray[TIMER0].supportTimeFilter = true;
+ timeFilterArray[TIMER0].timeStamp = 1;
+}
+
/** Configuration array for the dvr test */
inline void initDvrConfig() {
RecordSettings recordSettings{
@@ -263,9 +359,20 @@
.packetSize = 188,
};
dvrArray[DVR_PLAYBACK0].type = DvrType::PLAYBACK;
- dvrArray[DVR_PLAYBACK0].playbackInputFile = "/vendor/etc/segment000000.ts";
+ dvrArray[DVR_PLAYBACK0].playbackInputFile = "/data/local/tmp/segment000000.ts";
dvrArray[DVR_PLAYBACK0].bufferSize = FMQ_SIZE_4M;
dvrArray[DVR_PLAYBACK0].settings.playback(playbackSettings);
+ PlaybackSettings softwareFePlaybackSettings{
+ .statusMask = 0xf,
+ .lowThreshold = 0x1000,
+ .highThreshold = 0x07fff,
+ .dataFormat = DataFormat::TS,
+ .packetSize = 188,
+ };
+ dvrArray[DVR_SOFTWARE_FE].type = DvrType::PLAYBACK;
+ dvrArray[DVR_SOFTWARE_FE].playbackInputFile = "/data/local/tmp/segment000000.ts";
+ dvrArray[DVR_SOFTWARE_FE].bufferSize = FMQ_SIZE_4M;
+ dvrArray[DVR_SOFTWARE_FE].settings.playback(softwareFePlaybackSettings);
};
/** Configuration array for the descrambler test */