Merge "Use StopServicesSetup to stop/start framework." into rvc-dev
diff --git a/audio/6.0/IStreamOutEventCallback.hal b/audio/6.0/IStreamOutEventCallback.hal
index de17d73..9c88713 100644
--- a/audio/6.0/IStreamOutEventCallback.hal
+++ b/audio/6.0/IStreamOutEventCallback.hal
@@ -25,11 +25,116 @@
/**
* Codec format changed.
*
+ * onCodecFormatChanged returns an AudioMetadata object in read-only ByteString format.
+ * It represents the most recent codec format decoded by a HW audio decoder.
+ *
+ * Codec format is an optional message from HW audio decoders. It serves to
+ * notify the application about the codec format and audio objects contained
+ * within the compressed audio stream for control, informational,
+ * and display purposes.
+ *
+ * audioMetadata ByteString is convertible to an AudioMetadata object through
+ * both a C++ and a C API present in Metadata.h [1], or through a Java API present
+ * in AudioMetadata.java [2].
+ *
+ * The ByteString format is a stable format used for parcelling (marshalling) across
+ * JNI, AIDL, and HIDL interfaces. The test for R compatibility for native marshalling
+ * is TEST(metadata_tests, compatibility_R) [3]. The test for R compatibility for JNI
+ * marshalling is android.media.cts.AudioMetadataTest#testCompatibilityR [4].
+ *
+ * R (audio HAL 6.0) defined keys are as follows [2]:
+ * "bitrate", int32
+ * "channel-mask", int32
+ * "mime", string
+ * "sample-rate", int32
+ * "bit-width", int32
+ * "has-atmos", int32
+ * "audio-encoding", int32
+ *
+ * Parceling Format:
+ * All values are native endian order. [1]
+ *
+ * using type_size_t = uint32_t;
+ * using index_size_t = uint32_t;
+ * using datum_size_t = uint32_t;
+ *
+ * Permitted type indexes are
+ * TYPE_NONE = 0, // Reserved
+ * TYPE_INT32 = 1,
+ * TYPE_INT64 = 2,
+ * TYPE_FLOAT = 3,
+ * TYPE_DOUBLE = 4,
+ * TYPE_STRING = 5,
+ * TYPE_DATA = 6, // A data table of <String, Datum>
+ *
+ * Datum = {
+ * (type_size_t) Type (the type index from type_as_value<T>.)
+ * (datum_size_t) Size (size of the Payload)
+ * (byte string) Payload<Type>
+ * }
+ *
+ * The data is specified in native endian order.
+ * Since the size of the Payload is always present, unknown types may be skipped.
+ *
+ * Payload<Fixed-size Primitive_Value>
+ * [ sizeof(Primitive_Value) in raw bytes ]
+ *
+ * Example of Payload<Int32> of 123:
+ * Payload<Int32>
+ * [ value of 123 ] = 0x7b 0x00 0x00 0x00 123
+ *
+ * Payload<String>
+ * [ (index_size_t) length, not including zero terminator.]
+ * [ (length) raw bytes ]
+ *
+ * Example of Payload<String> of std::string("hi"):
+ * [ (index_size_t) length ] = 0x02 0x00 0x00 0x00 2 strlen("hi")
+ * [ raw bytes "hi" ] = 0x68 0x69 "hi"
+ *
+ * Payload<Data>
+ * [ (index_size_t) entries ]
+ * [ raw bytes (entry 1) Key (Payload<String>)
+ * Value (Datum)
+ * ... (until #entries) ]
+ *
+ * Example of Payload<Data> of {{"hello", "world"},
+ * {"value", (int32_t)1000}};
+ * [ (index_size_t) #entries ] = 0x02 0x00 0x00 0x00 2 entries
+ * Key (Payload<String>)
+ * [ index_size_t length ] = 0x05 0x00 0x00 0x00 5 strlen("hello")
+ * [ raw bytes "hello" ] = 0x68 0x65 0x6c 0x6c 0x6f "hello"
+ * Value (Datum)
+ * [ (type_size_t) type ] = 0x05 0x00 0x00 0x00 5 (TYPE_STRING)
+ * [ (datum_size_t) size ] = 0x09 0x00 0x00 0x00 sizeof(index_size_t) +
+ * strlen("world")
+ * Payload<String>
+ * [ (index_size_t) length ] = 0x05 0x00 0x00 0x00 5 strlen("world")
+ * [ raw bytes "world" ] = 0x77 0x6f 0x72 0x6c 0x64 "world"
+ * Key (Payload<String>)
+ * [ index_size_t length ] = 0x05 0x00 0x00 0x00 5 strlen("value")
+ * [ raw bytes "value" ] = 0x76 0x61 0x6c 0x75 0x65 "value"
+ * Value (Datum)
+ * [ (type_size_t) type ] = 0x01 0x00 0x00 0x00 1 (TYPE_INT32)
+ * [ (datum_size_t) size ] = 0x04 0x00 0x00 0x00 4 sizeof(int32_t)
+ * Payload<Int32>
+ * [ raw bytes 1000 ] = 0xe8 0x03 0x00 0x00 1000
+ *
+ * The contents of audioMetadata is a Payload<Data>.
+ * An implementation dependent detail is that the Keys are always
+ * stored sorted, so the byte string representation generated is unique.
+ *
+ * Vendor keys are allowed for informational and debugging purposes.
+ * Vendor keys should consist of the vendor company name followed
+ * by a dot; for example, "vendorCompany.someVolume" [2].
+ *
+ * [1] system/media/audio_utils/include/audio_utils/Metadata.h
+ * [2] frameworks/base/media/java/android/media/AudioMetadata.java
+ * [3] system/media/audio_utils/tests/metadata_tests.cpp
+ * [4] cts/tests/tests/media/src/android/media/cts/AudioMetadataTest.java
+ *
* @param audioMetadata is a buffer containing decoded format changes
* reported by codec. The buffer contains data that can be transformed
- * to audio metadata, which is a C++ object based map. See
- * `system/media/audio_utils/include/audio_utils/Metadata.h` for
- * more details.
+ * to audio metadata, which is a C++ object based map.
*/
oneway onCodecFormatChanged(vec<uint8_t> audioMetadata);
};
diff --git a/audio/common/all-versions/default/service/android.hardware.audio.service.rc b/audio/common/all-versions/default/service/android.hardware.audio.service.rc
index 63d2542..f7e1e24 100644
--- a/audio/common/all-versions/default/service/android.hardware.audio.service.rc
+++ b/audio/common/all-versions/default/service/android.hardware.audio.service.rc
@@ -5,5 +5,5 @@
group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock
capabilities BLOCK_SUSPEND
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ task_profiles ProcessCapacityHigh HighPerformance
onrestart restart audioserver
diff --git a/automotive/audiocontrol/2.0/default/AudioControl.cpp b/automotive/audiocontrol/2.0/default/AudioControl.cpp
index 5bde839..b7c11cd 100644
--- a/automotive/audiocontrol/2.0/default/AudioControl.cpp
+++ b/automotive/audiocontrol/2.0/default/AudioControl.cpp
@@ -69,7 +69,7 @@
}
Return<void> AudioControl::setFadeTowardFront(float value) {
- if (!isValidValue(value)) {
+ if (isValidValue(value)) {
// Just log in this default mock implementation
LOG(INFO) << "Fader set to " << value;
} else {
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 872b35b..a4fd641 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -15,10 +15,12 @@
cc_defaults {
name: "vhal_v2_0_defaults",
shared_libs: [
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libutils",
"android.hardware.automotive.vehicle@2.0",
+ "carwatchdog_aidl_interface-ndk_platform",
],
cflags: [
"-Wall",
@@ -46,6 +48,7 @@
"common/src/VehiclePropertyStore.cpp",
"common/src/VehicleUtils.cpp",
"common/src/VmsUtils.cpp",
+ "common/src/WatchdogClient.cpp",
],
shared_libs: [
"libbase",
diff --git a/automotive/vehicle/2.0/default/VehicleService.cpp b/automotive/vehicle/2.0/default/VehicleService.cpp
index 127eb98..47133fd 100644
--- a/automotive/vehicle/2.0/default/VehicleService.cpp
+++ b/automotive/vehicle/2.0/default/VehicleService.cpp
@@ -20,9 +20,13 @@
#include <iostream>
+#include <android/binder_process.h>
+#include <utils/Looper.h>
+#include <vhal_v2_0/EmulatedUserHal.h>
#include <vhal_v2_0/EmulatedVehicleConnector.h>
#include <vhal_v2_0/EmulatedVehicleHal.h>
#include <vhal_v2_0/VehicleHalManager.h>
+#include <vhal_v2_0/WatchdogClient.h>
using namespace android;
using namespace android::hardware;
@@ -31,12 +35,13 @@
int main(int /* argc */, char* /* argv */ []) {
auto store = std::make_unique<VehiclePropertyStore>();
auto connector = impl::makeEmulatedPassthroughConnector();
- auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get());
+ auto userHal = connector->getEmulatedUserHal();
+ auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get(), userHal);
auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
auto service = std::make_unique<VehicleHalManager>(hal.get());
connector->setValuePool(hal->getValuePool());
- configureRpcThreadpool(4, true /* callerWillJoin */);
+ configureRpcThreadpool(4, false /* callerWillJoin */);
ALOGI("Registering as service...");
status_t status = service->registerAsService();
@@ -46,8 +51,22 @@
return 1;
}
+ // Setup a binder thread pool to be a car watchdog client.
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ sp<Looper> looper(Looper::prepare(0 /* opts */));
+ std::shared_ptr<WatchdogClient> watchdogClient =
+ ndk::SharedRefBase::make<WatchdogClient>(looper, service.get());
+ // The current health check is done in the main thread, so it falls short of capturing the real
+ // situation. Checking through HAL binder thread should be considered.
+ if (!watchdogClient->initialize()) {
+ ALOGE("Failed to initialize car watchdog client");
+ return 1;
+ }
ALOGI("Ready");
- joinRpcThreadpool();
+ while (true) {
+ looper->pollAll(-1 /* timeoutMillis */);
+ }
return 1;
}
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/WatchdogClient.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/WatchdogClient.h
new file mode 100644
index 0000000..578606d
--- /dev/null
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/WatchdogClient.h
@@ -0,0 +1,74 @@
+/*
+ * 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_automotive_vehicle_V2_0_WatchdogClient_H_
+#define android_hardware_automotive_vehicle_V2_0_WatchdogClient_H_
+
+#include "VehicleHalManager.h"
+
+#include <aidl/android/automotive/watchdog/BnCarWatchdog.h>
+#include <aidl/android/automotive/watchdog/BnCarWatchdogClient.h>
+#include <utils/Looper.h>
+#include <utils/Mutex.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+class WatchdogClient : public aidl::android::automotive::watchdog::BnCarWatchdogClient {
+ public:
+ explicit WatchdogClient(const ::android::sp<::android::Looper>& handlerLooper,
+ VehicleHalManager* vhalManager);
+
+ ndk::ScopedAStatus checkIfAlive(
+ int32_t sessionId, aidl::android::automotive::watchdog::TimeoutLength timeout) override;
+ ndk::ScopedAStatus prepareProcessTermination() override;
+
+ bool initialize();
+
+ private:
+ class MessageHandlerImpl : public ::android::MessageHandler {
+ public:
+ explicit MessageHandlerImpl(WatchdogClient* client);
+ void handleMessage(const ::android::Message& message) override;
+
+ private:
+ WatchdogClient* mClient;
+ };
+
+ private:
+ void respondToWatchdog();
+ bool isClientHealthy() const;
+
+ private:
+ ::android::sp<::android::Looper> mHandlerLooper;
+ ::android::sp<MessageHandlerImpl> mMessageHandler;
+ std::shared_ptr<aidl::android::automotive::watchdog::ICarWatchdog> mWatchdogServer;
+ std::shared_ptr<aidl::android::automotive::watchdog::ICarWatchdogClient> mTestClient;
+ VehicleHalManager* mVhalManager;
+ ::android::Mutex mMutex;
+ int mCurrentSessionId GUARDED_BY(mMutex);
+};
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // android_hardware_automotive_vehicle_V2_0_WatchdogClient_H_
diff --git a/automotive/vehicle/2.0/default/common/src/WatchdogClient.cpp b/automotive/vehicle/2.0/default/common/src/WatchdogClient.cpp
new file mode 100644
index 0000000..c067216
--- /dev/null
+++ b/automotive/vehicle/2.0/default/common/src/WatchdogClient.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "automotive.vehicle@2.0-watchdog"
+
+#include <common/include/vhal_v2_0/WatchdogClient.h>
+
+#include <android/binder_manager.h>
+#include <android/hardware/automotive/vehicle/2.0/types.h>
+
+using aidl::android::automotive::watchdog::ICarWatchdog;
+using aidl::android::automotive::watchdog::TimeoutLength;
+
+namespace {
+
+enum { WHAT_CHECK_ALIVE = 1 };
+
+} // namespace
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+namespace V2_0 {
+
+WatchdogClient::WatchdogClient(const sp<Looper>& handlerLooper, VehicleHalManager* vhalManager)
+ : mHandlerLooper(handlerLooper), mVhalManager(vhalManager), mCurrentSessionId(-1) {
+ mMessageHandler = new MessageHandlerImpl(this);
+}
+
+ndk::ScopedAStatus WatchdogClient::checkIfAlive(int32_t sessionId, TimeoutLength /*timeout*/) {
+ mHandlerLooper->removeMessages(mMessageHandler, WHAT_CHECK_ALIVE);
+ {
+ Mutex::Autolock lock(mMutex);
+ mCurrentSessionId = sessionId;
+ }
+ mHandlerLooper->sendMessage(mMessageHandler, Message(WHAT_CHECK_ALIVE));
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WatchdogClient::prepareProcessTermination() {
+ return ndk::ScopedAStatus::ok();
+}
+
+bool WatchdogClient::initialize() {
+ ndk::SpAIBinder binder(
+ AServiceManager_getService("android.automotive.watchdog.ICarWatchdog/default"));
+ if (binder.get() == nullptr) {
+ ALOGE("Failed to get carwatchdog daemon");
+ return false;
+ }
+ std::shared_ptr<ICarWatchdog> server = ICarWatchdog::fromBinder(binder);
+ if (server == nullptr) {
+ ALOGE("Failed to connect to carwatchdog daemon");
+ return false;
+ }
+ mWatchdogServer = server;
+
+ binder = this->asBinder();
+ if (binder.get() == nullptr) {
+ ALOGE("Failed to get car watchdog client binder object");
+ return false;
+ }
+ std::shared_ptr<ICarWatchdogClient> client = ICarWatchdogClient::fromBinder(binder);
+ if (client == nullptr) {
+ ALOGE("Failed to get ICarWatchdogClient from binder");
+ return false;
+ }
+ mTestClient = client;
+ mWatchdogServer->registerClient(client, TimeoutLength::TIMEOUT_NORMAL);
+ ALOGI("Successfully registered the client to car watchdog server");
+ return true;
+}
+
+void WatchdogClient::respondToWatchdog() {
+ if (mWatchdogServer == nullptr) {
+ ALOGW("Cannot respond to car watchdog daemon: car watchdog daemon is not connected");
+ return;
+ }
+ int sessionId;
+ {
+ Mutex::Autolock lock(mMutex);
+ sessionId = mCurrentSessionId;
+ }
+ if (isClientHealthy()) {
+ ndk::ScopedAStatus status = mWatchdogServer->tellClientAlive(mTestClient, sessionId);
+ if (!status.isOk()) {
+ ALOGE("Failed to call tellClientAlive(session id = %d): %d", sessionId,
+ status.getStatus());
+ return;
+ }
+ }
+}
+
+bool WatchdogClient::isClientHealthy() const {
+ // We consider that default vehicle HAL is healthy if we can get PERF_VEHICLE_SPEED value.
+ StatusCode status = StatusCode::TRY_AGAIN;
+ VehiclePropValue propValue = {.prop = (int32_t)VehicleProperty::PERF_VEHICLE_SPEED};
+ while (status == StatusCode::TRY_AGAIN) {
+ mVhalManager->get(propValue,
+ [&propValue, &status](StatusCode s, const VehiclePropValue& v) {
+ status = s;
+ if (s == StatusCode::OK) {
+ propValue = v;
+ }
+ });
+ }
+ return status == StatusCode::OK;
+}
+
+WatchdogClient::MessageHandlerImpl::MessageHandlerImpl(WatchdogClient* client) : mClient(client) {}
+
+void WatchdogClient::MessageHandlerImpl::handleMessage(const Message& message) {
+ switch (message.what) {
+ case WHAT_CHECK_ALIVE:
+ mClient->respondToWatchdog();
+ break;
+ default:
+ ALOGW("Unknown message: %d", message.what);
+ }
+}
+
+} // namespace V2_0
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android
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 c49fadc..d744a06 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
@@ -30,11 +30,14 @@
constexpr int INITIAL_USER_INFO = static_cast<int>(VehicleProperty::INITIAL_USER_INFO);
constexpr int SWITCH_USER = static_cast<int>(VehicleProperty::SWITCH_USER);
+constexpr int USER_IDENTIFICATION_ASSOCIATION =
+ static_cast<int>(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
bool EmulatedUserHal::isSupported(int32_t prop) {
switch (prop) {
case INITIAL_USER_INFO:
case SWITCH_USER:
+ case USER_IDENTIFICATION_ASSOCIATION:
return true;
default:
return false;
@@ -50,12 +53,41 @@
return onSetInitialUserInfoResponse(value);
case SWITCH_USER:
return onSetSwitchUserResponse(value);
+ case USER_IDENTIFICATION_ASSOCIATION:
+ return onSetUserIdentificationAssociation(value);
default:
return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
<< "Unsupported property: " << toString(value);
}
}
+android::base::Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onGetProperty(
+ int32_t prop) {
+ ALOGV("onGetProperty(%d)", prop);
+ switch (prop) {
+ case INITIAL_USER_INFO:
+ case SWITCH_USER:
+ ALOGE("onGetProperty(): %d is only supported on SET", prop);
+ return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
+ << "only supported on SET";
+ case USER_IDENTIFICATION_ASSOCIATION:
+ if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
+ ALOGI("onGetProperty(%d): returning %s", prop,
+ toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
+ auto value = std::unique_ptr<VehiclePropValue>(
+ new VehiclePropValue(*mSetUserIdentificationAssociationResponseFromCmd));
+ return value;
+ }
+ ALOGE("onGetProperty(%d): USER_IDENTIFICATION_ASSOCIATION not set by lshal", prop);
+ return android::base::Error(static_cast<int>(StatusCode::NOT_AVAILABLE))
+ << "not set by lshal";
+ default:
+ ALOGE("onGetProperty(): %d is not supported", prop);
+ return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
+ << "not supported by User HAL";
+ }
+}
+
android::base::Result<std::unique_ptr<VehiclePropValue>>
EmulatedUserHal::onSetInitialUserInfoResponse(const VehiclePropValue& value) {
if (value.value.int32Values.size() == 0) {
@@ -130,6 +162,46 @@
return updatedValue;
}
+android::base::Result<std::unique_ptr<VehiclePropValue>>
+EmulatedUserHal::onSetUserIdentificationAssociation(const VehiclePropValue& value) {
+ if (value.value.int32Values.size() == 0) {
+ ALOGE("set(USER_IDENTIFICATION_ASSOCIATION): no int32values, ignoring it: %s",
+ toString(value).c_str());
+ return android::base::Error(static_cast<int>(StatusCode::INVALID_ARG))
+ << "no int32values on " << toString(value);
+ }
+
+ if (value.areaId != 0) {
+ ALOGD("set(USER_IDENTIFICATION_ASSOCIATION) called from lshal; storing it: %s",
+ toString(value).c_str());
+ mSetUserIdentificationAssociationResponseFromCmd.reset(new VehiclePropValue(value));
+ return {};
+ }
+ ALOGD("set(USER_IDENTIFICATION_ASSOCIATION) called from Android: %s", toString(value).c_str());
+
+ int32_t requestId = value.value.int32Values[0];
+ if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
+ ALOGI("replying USER_IDENTIFICATION_ASSOCIATION with lshal value: %s",
+ toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
+ // Not moving response so it can be used on GET requests
+ auto copy = std::unique_ptr<VehiclePropValue>(
+ new VehiclePropValue(*mSetUserIdentificationAssociationResponseFromCmd));
+ return sendUserHalResponse(std::move(copy), requestId);
+ }
+
+ // Returns default response
+ auto updatedValue = std::unique_ptr<VehiclePropValue>(new VehiclePropValue);
+ updatedValue->prop = USER_IDENTIFICATION_ASSOCIATION;
+ updatedValue->timestamp = elapsedRealtimeNano();
+ updatedValue->value.int32Values.resize(1);
+ updatedValue->value.int32Values[0] = requestId;
+ updatedValue->value.stringValue = "Response not set by LSHAL";
+
+ ALOGI("no lshal response; replying with an error message: %s", toString(*updatedValue).c_str());
+
+ return updatedValue;
+}
+
android::base::Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::sendUserHalResponse(
std::unique_ptr<VehiclePropValue> response, int32_t requestId) {
switch (response->areaId) {
@@ -175,6 +247,12 @@
} else {
dprintf(fd, "%sNo SwitchUser response\n", indent.c_str());
}
+ if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
+ dprintf(fd, "%sSetUserIdentificationAssociation response: %s\n", indent.c_str(),
+ toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
+ } else {
+ dprintf(fd, "%sNo SetUserIdentificationAssociation response\n", indent.c_str());
+ }
}
} // namespace impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.h
index b25efcb..3168d75 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedUserHal.h
@@ -46,7 +46,7 @@
bool isSupported(int32_t prop);
/**
- * Lets the emulator handle the property.
+ * Lets the emulator set the property.
*
* @return updated property and StatusCode
*/
@@ -54,6 +54,13 @@
const VehiclePropValue& value);
/**
+ * Gets the property value from the emulator.
+ *
+ * @return property value and StatusCode
+ */
+ android::base::Result<std::unique_ptr<VehiclePropValue>> onGetProperty(int32_t prop);
+
+ /**
* Shows the User HAL emulation help.
*/
void showDumpHelp(int fd);
@@ -97,11 +104,19 @@
android::base::Result<std::unique_ptr<VehiclePropValue>> onSetSwitchUserResponse(
const VehiclePropValue& value);
+ /**
+ * Used to emulate USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for
+ * usage.
+ */
+ android::base::Result<std::unique_ptr<VehiclePropValue>> onSetUserIdentificationAssociation(
+ const VehiclePropValue& value);
+
android::base::Result<std::unique_ptr<VehiclePropValue>> sendUserHalResponse(
std::unique_ptr<VehiclePropValue> response, int32_t requestId);
std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
std::unique_ptr<VehiclePropValue> mSwitchUserResponseFromCmd;
+ std::unique_ptr<VehiclePropValue> mSetUserIdentificationAssociationResponseFromCmd;
};
} // namespace impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index bdc5244..9cfcc1c 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -92,12 +92,14 @@
return sensorStore;
}
-EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client)
+EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client,
+ EmulatedUserHal* emulatedUserHal)
: mPropStore(propStore),
mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
mRecurrentTimer(std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this,
std::placeholders::_1)),
- mVehicleClient(client) {
+ mVehicleClient(client),
+ mEmulatedUserHal(emulatedUserHal) {
initStaticConfig();
for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
mPropStore->registerProperty(kVehicleProperties[i].config);
@@ -134,6 +136,8 @@
VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
auto propId = requestedPropValue.prop;
+ ALOGV("get(%d)", propId);
+
auto& pool = *getValuePool();
VehiclePropValuePtr v = nullptr;
@@ -147,6 +151,26 @@
*outStatus = fillObd2DtcInfo(v.get());
break;
default:
+ if (mEmulatedUserHal != nullptr && mEmulatedUserHal->isSupported(propId)) {
+ ALOGI("get(): getting value for prop %d from User HAL", propId);
+ const auto& ret = mEmulatedUserHal->onGetProperty(propId);
+ if (!ret.ok()) {
+ ALOGE("get(): User HAL returned error: %s", ret.error().message().c_str());
+ *outStatus = StatusCode(ret.error().code());
+ } else {
+ auto value = ret.value().get();
+ if (value != nullptr) {
+ ALOGI("get(): User HAL returned value: %s", toString(*value).c_str());
+ v = getValuePool()->obtain(*value);
+ *outStatus = StatusCode::OK;
+ } else {
+ ALOGE("get(): User HAL returned null value");
+ *outStatus = StatusCode::INTERNAL_ERROR;
+ }
+ }
+ break;
+ }
+
auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
if (internalPropValue != nullptr) {
v = getValuePool()->obtain(*internalPropValue);
@@ -175,9 +199,7 @@
// here, since we never send the control signal back, the value of 'updateStatus' flag
// does not matter here.
auto status = mVehicleClient->setProperty(propValue, updateStatus);
- if (status != StatusCode::OK) {
- return status;
- }
+ return status;
} else if (mHvacPowerProps.count(propValue.prop)) {
auto hvacPowerOn = mPropStore->readValueOrNull(
toInt(VehicleProperty::HVAC_POWER_ON),
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
index cba4b8a..eb38d7d 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
@@ -30,6 +30,7 @@
#include "vhal_v2_0/VehiclePropertyStore.h"
#include "DefaultConfig.h"
+#include "EmulatedUserHal.h"
#include "EmulatedVehicleConnector.h"
#include "GeneratorHub.h"
#include "VehicleEmulator.h"
@@ -45,8 +46,8 @@
/** Implementation of VehicleHal that connected to emulator instead of real vehicle network. */
class EmulatedVehicleHal : public EmulatedVehicleHalIface {
public:
- EmulatedVehicleHal(VehiclePropertyStore* propStore,
- VehicleHalClient* client);
+ EmulatedVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client,
+ EmulatedUserHal* emulatedUserHal = nullptr);
~EmulatedVehicleHal() = default;
// Methods from VehicleHal
@@ -90,6 +91,7 @@
bool mInEmulator;
bool mInitVhalValueOverride;
std::vector<VehiclePropValue> mVehiclePropertiesOverride;
+ EmulatedUserHal* mEmulatedUserHal;
};
} // impl
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.cpp
index ad5096e..36f2534 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.cpp
@@ -41,6 +41,10 @@
return mValuePool;
}
+EmulatedUserHal* VehicleHalServer::getEmulatedUserHal() {
+ return &mEmulatedUserHal;
+}
+
void VehicleHalServer::setValuePool(VehiclePropValuePool* valuePool) {
if (!valuePool) {
LOG(WARNING) << __func__ << ": Setting value pool to nullptr!";
@@ -197,6 +201,7 @@
}
return StatusCode::OK;
}
+ LOG(DEBUG) << "onSetProperty(" << value.prop << ")";
// Some properties need to be treated non-trivially
switch (value.prop) {
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h
index 2841fbe..fca78bc 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleHalServer.h
@@ -38,6 +38,8 @@
// Set the Property Value Pool used in this server
void setValuePool(VehiclePropValuePool* valuePool);
+ EmulatedUserHal* getEmulatedUserHal();
+
private:
using VehiclePropValuePtr = recyclable_ptr<VehiclePropValue>;
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 82f938c..2e6fa25 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -606,8 +606,23 @@
/**
* Tire pressure
*
- * min/max value indicates tire pressure sensor range. Each tire will have a separate min/max
- * value denoted by its areaConfig.areaId.
+ * Each tires is identified by its areaConfig.areaId config and their
+ * minFloatValue/maxFloatValue are used to store OEM recommended pressure
+ * range.
+ * The Min value in the areaConfig data represents the lower bound of
+ * the recommended tire pressure.
+ * The Max value in the areaConfig data represents the upper bound of
+ * the recommended tire pressure.
+ * For example:
+ * The following areaConfig indicates the recommended tire pressure
+ * of left_front tire is from 200.0 KILOPASCAL to 240.0 KILOPASCAL.
+ * .areaConfigs = {
+ * VehicleAreaConfig {
+ * .areaId = VehicleAreaWheel::LEFT_FRONT,
+ * .minFloatValue = 200.0,
+ * .maxFloatValue = 240.0,
+ * }
+ * },
*
* @change_mode VehiclePropertyChangeMode:CONTINUOUS
* @access VehiclePropertyAccess:READ
@@ -786,7 +801,8 @@
/*
* HVAC Properties
*
- * Additional rules for mapping a zoned HVAC property to AreaIDs:
+ * Additional rules for mapping a zoned HVAC property (except
+ * HVAC_MAX_DEFROST_ON) to AreaIDs:
* - Every seat in VehicleAreaSeat that is available in the car, must be
* part of an AreaID in the AreaID array.
*
@@ -919,6 +935,11 @@
* possible. Any parameters modified as a side effect of turning on/off
* the MAX DEFROST parameter shall generate onPropertyEvent() callbacks to
* the VHAL.
+ * The AreaIDs for HVAC_MAX_DEFROST_ON indicate MAX DEFROST can be controlled
+ * in the area.
+ * For example:
+ * areaConfig.areaId = {ROW_1_LEFT | ROW_1_RIGHT} indicates HVAC_MAX_DEFROST_ON
+ * only can be controlled for the front rows.
*
* @change_mode VehiclePropertyChangeMode:ON_CHANGE
* @access VehiclePropertyAccess:READ_WRITE
@@ -2792,7 +2813,7 @@
*
* 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, updating the VehiclePropValue
+ * UserIdentificationGetRequest. The HAL must return right away, returning a VehiclePropValue
* with a UserIdentificationResponse. Notice that user identification should have already
* happened while system is booting up and the VHAL implementation should only return the
* already identified association (like the key FOB used to unlock the car), instead of starting
@@ -2807,45 +2828,50 @@
* For example, to query if the current user (10) is associated with the FOB that unlocked the
* car and a custom mechanism provided by the OEM, the request would be:
*
- * int32[0]: 10 (Android user id)
- * int32[1]: 0 (Android user flags)
- * int32[2]: 2 (number of types queried)
- * int32[3]: 1 (1st type queried, UserIdentificationAssociationType::KEY_FOB)
- * int32[4]: 101 (2nd type queried, UserIdentificationAssociationType::CUSTOM_1)
+ * int32[0]: 42 // request id
+ * int32[1]: 10 (Android user id)
+ * int32[2]: 0 (Android user flags)
+ * int32[3]: 2 (number of types queried)
+ * int32[4]: 1 (1st type queried, UserIdentificationAssociationType::KEY_FOB)
+ * int32[5]: 101 (2nd type queried, UserIdentificationAssociationType::CUSTOM_1)
*
* If the user is associated with the FOB but not with the custom mechanism, the response would
* be:
*
- * int32[9]: 2 (number of associations in the response)
- * int32[1]: 1 (1st type: UserIdentificationAssociationType::KEY_FOB)
- * int32[2]: 2 (1st value: UserIdentificationAssociationValue::ASSOCIATED_CURRENT_USER)
- * int32[3]: 101 (2st type: UserIdentificationAssociationType::CUSTOM_1)
- * int32[4]: 4 (2nd value: UserIdentificationAssociationValue::NOT_ASSOCIATED_ANY_USER)
+ * int32[0]: 42 // request id
+ * int32[1]: 2 (number of associations in the response)
+ * int32[2]: 1 (1st type: UserIdentificationAssociationType::KEY_FOB)
+ * int32[3]: 2 (1st value: UserIdentificationAssociationValue::ASSOCIATED_CURRENT_USER)
+ * int32[4]: 101 (2st type: UserIdentificationAssociationType::CUSTOM_1)
+ * int32[5]: 4 (2nd value: UserIdentificationAssociationValue::NOT_ASSOCIATED_ANY_USER)
*
* Then to associate the user with the custom mechanism, a set request would be made:
*
- * int32[0]: 10 (Android user id)
- * int32[0]: 0 (Android user flags)
- * int32[1]: 1 (number of associations being set)
- * int32[2]: 101 (1st type: UserIdentificationAssociationType::CUSTOM_1)
- * int32[3]: 1 (1st value: UserIdentificationAssociationSetValue::ASSOCIATE_CURRENT_USER)
+ * int32[0]: 43 // request id
+ * int32[1]: 10 (Android user id)
+ * int32[2]: 0 (Android user flags)
+ * int32[3]: 1 (number of associations being set)
+ * int32[4]: 101 (1st type: UserIdentificationAssociationType::CUSTOM_1)
+ * int32[5]: 1 (1st value: UserIdentificationAssociationSetValue::ASSOCIATE_CURRENT_USER)
*
* If the request succeeded, the response would be simply:
*
- * int32[0]: 2 (number of associations in the response)
- * int32[1]: 101 (1st type: UserIdentificationAssociationType::CUSTOM_1)
- * int32[2]: 1 (1st value: UserIdentificationAssociationValue::ASSOCIATED_CURRENT_USER)
+ * int32[0]: 43 // request id
+ * int32[1]: 1 (number of associations in the response)
+ * int32[2]: 101 (1st type: UserIdentificationAssociationType::CUSTOM_1)
+ * int32[3]: 1 (1st value: UserIdentificationAssociationValue::ASSOCIATED_CURRENT_USER)
*
* Notice that the set request adds associations, but doesn't remove the existing ones. In the
* example above, the end state would be 2 associations (FOB and CUSTOM_1). If we wanted to
* associate the user with just CUSTOM_1 but not FOB, then the request should have been:
*
- * int32[0]: 10 (Android user id)
- * int32[1]: 2 (number of types set)
- * int32[2]: 1 (1st type: UserIdentificationAssociationType::KEY_FOB)
- * int32[3]: 2 (1st value: UserIdentificationAssociationValue::DISASSOCIATE_CURRENT_USER)
- * int32[3]: 101 (2nd type: UserIdentificationAssociationType::CUSTOM_1)
- * int32[5]: 1 (2nd value: UserIdentificationAssociationValue::ASSOCIATE_CURRENT_USER)
+ * int32[0]: 43 // request id
+ * int32[1]: 10 (Android user id)
+ * int32[2]: 2 (number of types set)
+ * int32[3]: 1 (1st type: UserIdentificationAssociationType::KEY_FOB)
+ * int32[4]: 2 (1st value: UserIdentificationAssociationValue::DISASSOCIATE_CURRENT_USER)
+ * int32[5]: 101 (2nd type: UserIdentificationAssociationType::CUSTOM_1)
+ * int32[6]: 1 (2nd value: UserIdentificationAssociationValue::ASSOCIATE_CURRENT_USER)
*
* @change_mode VehiclePropertyChangeMode:ON_CHANGE
* @access VehiclePropertyAccess:READ_WRITE
@@ -4618,6 +4644,11 @@
*/
struct UserIdentificationGetRequest {
/**
+ * Id of the request being responded.
+ */
+ UserRequestId requestId;
+
+ /**
* Information about the current foreground Android user.
*/
UserInfo userInfo;
@@ -4641,6 +4672,11 @@
*/
struct UserIdentificationSetRequest {
/**
+ * Id of the request being responded.
+ */
+ UserRequestId requestId;
+
+ /**
* Information about the current foreground Android user.
*/
UserInfo userInfo;
@@ -4653,7 +4689,7 @@
/**
* Associations being set.
*/
- vec<UserIdentificationAssociationSetValue> associations;
+ vec<UserIdentificationSetAssociation> associations;
};
/**
@@ -4664,6 +4700,11 @@
*/
struct UserIdentificationResponse {
/**
+ * Id of the request being responded.
+ */
+ UserRequestId requestId;
+
+ /**
* Number of associations being returned.
*/
int32_t numberAssociation;
diff --git a/bluetooth/1.0/default/android.hardware.bluetooth@1.0-service.rc b/bluetooth/1.0/default/android.hardware.bluetooth@1.0-service.rc
index 9fa128d..def59de 100644
--- a/bluetooth/1.0/default/android.hardware.bluetooth@1.0-service.rc
+++ b/bluetooth/1.0/default/android.hardware.bluetooth@1.0-service.rc
@@ -4,5 +4,5 @@
capabilities BLOCK_SUSPEND NET_ADMIN SYS_NICE
user bluetooth
group bluetooth
- writepid /dev/stune/foreground/tasks
+ task_profiles HighPerformance
diff --git a/bluetooth/1.1/default/android.hardware.bluetooth@1.1-service.rc b/bluetooth/1.1/default/android.hardware.bluetooth@1.1-service.rc
index 49f0be3..5c7cbf4 100644
--- a/bluetooth/1.1/default/android.hardware.bluetooth@1.1-service.rc
+++ b/bluetooth/1.1/default/android.hardware.bluetooth@1.1-service.rc
@@ -5,5 +5,5 @@
capabilities BLOCK_SUSPEND NET_ADMIN SYS_NICE
user bluetooth
group bluetooth
- writepid /dev/stune/foreground/tasks
+ task_profiles HighPerformance
diff --git a/boot/1.1/vts/functional/VtsHalBootV1_1TargetTest.cpp b/boot/1.1/vts/functional/VtsHalBootV1_1TargetTest.cpp
index 7c58ef3..30b965d 100644
--- a/boot/1.1/vts/functional/VtsHalBootV1_1TargetTest.cpp
+++ b/boot/1.1/vts/functional/VtsHalBootV1_1TargetTest.cpp
@@ -76,7 +76,11 @@
for (const auto value : ValidMergeStatusValues()) {
EXPECT_TRUE(boot->setSnapshotMergeStatus(value).withDefault(false));
auto status = boot->getSnapshotMergeStatus();
- EXPECT_EQ(status, value);
+ if (value == MergeStatus::SNAPSHOTTED) {
+ EXPECT_TRUE(status == MergeStatus::SNAPSHOTTED || status == MergeStatus::NONE);
+ } else {
+ EXPECT_EQ(status, value);
+ }
}
}
diff --git a/camera/common/1.0/default/CameraModule.cpp b/camera/common/1.0/default/CameraModule.cpp
index 467c121..86f26e4 100644
--- a/camera/common/1.0/default/CameraModule.cpp
+++ b/camera/common/1.0/default/CameraModule.cpp
@@ -194,16 +194,6 @@
}
}
- // Always add a default for the pre-correction active array if the vendor chooses to omit this
- camera_metadata_entry entry = chars.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
- if (entry.count == 0) {
- Vector<int32_t> preCorrectionArray;
- entry = chars.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- preCorrectionArray.appendArray(entry.data.i32, entry.count);
- chars.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, preCorrectionArray);
- derivedCharKeys.push(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
- }
-
// Add those newly added keys to AVAILABLE_CHARACTERISTICS_KEYS
// This has to be done at this end of this function.
if (derivedCharKeys.size() > 0) {
diff --git a/camera/common/1.0/default/HandleImporter.cpp b/camera/common/1.0/default/HandleImporter.cpp
index 7792b31..40cb4e0 100644
--- a/camera/common/1.0/default/HandleImporter.cpp
+++ b/camera/common/1.0/default/HandleImporter.cpp
@@ -232,13 +232,20 @@
void* HandleImporter::lock(
buffer_handle_t& buf, uint64_t cpuUsage, size_t size) {
+ IMapper::Rect accessRegion{0, 0, static_cast<int>(size), 1};
+ return lock(buf, cpuUsage, accessRegion);
+}
+
+void* HandleImporter::lock(buffer_handle_t& buf, uint64_t cpuUsage,
+ const IMapper::Rect& accessRegion) {
Mutex::Autolock lock(mLock);
- void *ret = 0;
if (!mInitialized) {
initializeLocked();
}
+ void* ret = nullptr;
+
if (mMapperV4 == nullptr && mMapperV3 == nullptr && mMapperV2 == nullptr) {
ALOGE("%s: mMapperV4, mMapperV3 and mMapperV2 are all null!", __FUNCTION__);
return ret;
@@ -247,10 +254,10 @@
hidl_handle acquireFenceHandle;
auto buffer = const_cast<native_handle_t*>(buf);
if (mMapperV4 != nullptr) {
- IMapperV4::Rect accessRegion{0, 0, static_cast<int>(size), 1};
- // No need to use bytesPerPixel and bytesPerStride because we are using
- // an 1-D buffer and accressRegion.
- mMapperV4->lock(buffer, cpuUsage, accessRegion, acquireFenceHandle,
+ IMapperV4::Rect accessRegionV4{accessRegion.left, accessRegion.top, accessRegion.width,
+ accessRegion.height};
+
+ mMapperV4->lock(buffer, cpuUsage, accessRegionV4, acquireFenceHandle,
[&](const auto& tmpError, const auto& tmpPtr) {
if (tmpError == MapperErrorV4::NONE) {
ret = tmpPtr;
@@ -259,33 +266,33 @@
}
});
} else if (mMapperV3 != nullptr) {
- IMapperV3::Rect accessRegion { 0, 0, static_cast<int>(size), 1 };
- // No need to use bytesPerPixel and bytesPerStride because we are using
- // an 1-D buffer and accressRegion.
- mMapperV3->lock(buffer, cpuUsage, accessRegion, acquireFenceHandle,
- [&](const auto& tmpError, const auto& tmpPtr, const auto& /*bytesPerPixel*/,
- const auto& /*bytesPerStride*/) {
- if (tmpError == MapperErrorV3::NONE) {
- ret = tmpPtr;
- } else {
- ALOGE("%s: failed to lock error %d!",
- __FUNCTION__, tmpError);
- }
- });
+ IMapperV3::Rect accessRegionV3{accessRegion.left, accessRegion.top, accessRegion.width,
+ accessRegion.height};
+
+ mMapperV3->lock(buffer, cpuUsage, accessRegionV3, acquireFenceHandle,
+ [&](const auto& tmpError, const auto& tmpPtr, const auto& /*bytesPerPixel*/,
+ const auto& /*bytesPerStride*/) {
+ if (tmpError == MapperErrorV3::NONE) {
+ ret = tmpPtr;
+ } else {
+ ALOGE("%s: failed to lock error %d!", __FUNCTION__, tmpError);
+ }
+ });
} else {
- IMapper::Rect accessRegion { 0, 0, static_cast<int>(size), 1 };
mMapperV2->lock(buffer, cpuUsage, accessRegion, acquireFenceHandle,
[&](const auto& tmpError, const auto& tmpPtr) {
if (tmpError == MapperErrorV2::NONE) {
ret = tmpPtr;
} else {
- ALOGE("%s: failed to lock error %d!",
- __FUNCTION__, tmpError);
+ ALOGE("%s: failed to lock error %d!", __FUNCTION__, tmpError);
}
});
}
- ALOGV("%s: ptr %p size: %zu", __FUNCTION__, ret, size);
+ ALOGV("%s: ptr %p accessRegion.top: %d accessRegion.left: %d accessRegion.width: %d "
+ "accessRegion.height: %d",
+ __FUNCTION__, ret, accessRegion.top, accessRegion.left, accessRegion.width,
+ accessRegion.height);
return ret;
}
diff --git a/camera/common/1.0/default/include/HandleImporter.h b/camera/common/1.0/default/include/HandleImporter.h
index fc2bbd1..edc97ad 100644
--- a/camera/common/1.0/default/include/HandleImporter.h
+++ b/camera/common/1.0/default/include/HandleImporter.h
@@ -46,10 +46,13 @@
bool importFence(const native_handle_t* handle, int& fd) const;
void closeFence(int fd) const;
- // Assume caller has done waiting for acquire fences
+ // Locks 1-D buffer. Assumes caller has waited for acquire fences.
void* lock(buffer_handle_t& buf, uint64_t cpuUsage, size_t size);
- // Assume caller has done waiting for acquire fences
+ // Locks 2-D buffer. Assumes caller has waited for acquire fences.
+ void* lock(buffer_handle_t& buf, uint64_t cpuUsage, const IMapper::Rect& accessRegion);
+
+ // Assumes caller has waited for acquire fences.
YCbCrLayout lockYCbCr(buffer_handle_t& buf, uint64_t cpuUsage,
const IMapper::Rect& accessRegion);
diff --git a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-external-service.rc b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-external-service.rc
index 64cf321..52ade97 100644
--- a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-external-service.rc
+++ b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-external-service.rc
@@ -5,4 +5,4 @@
group audio camera input drmrpc usb
ioprio rt 4
capabilities SYS_NICE
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
diff --git a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service-lazy.rc b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service-lazy.rc
index e8549ed..63ded90 100644
--- a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service-lazy.rc
+++ b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service-lazy.rc
@@ -7,4 +7,4 @@
group audio camera input drmrpc
ioprio rt 4
capabilities SYS_NICE
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
diff --git a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service-lazy_64.rc b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service-lazy_64.rc
index 2dfac76..953d1af 100644
--- a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service-lazy_64.rc
+++ b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service-lazy_64.rc
@@ -7,4 +7,4 @@
group audio camera input drmrpc
ioprio rt 4
capabilities SYS_NICE
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
diff --git a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service.rc b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service.rc
index 913561b..f7ac9f8 100644
--- a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service.rc
+++ b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service.rc
@@ -5,4 +5,4 @@
group audio camera input drmrpc
ioprio rt 4
capabilities SYS_NICE
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
diff --git a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service_64.rc b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service_64.rc
index fd4826e..a32dd46 100644
--- a/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service_64.rc
+++ b/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service_64.rc
@@ -5,4 +5,4 @@
group audio camera input drmrpc
ioprio rt 4
capabilities SYS_NICE
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 05b8b47..bf5fbfe 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -18,12 +18,13 @@
#include <algorithm>
#include <chrono>
+#include <condition_variable>
+#include <list>
#include <mutex>
#include <regex>
#include <string>
#include <unordered_map>
#include <unordered_set>
-#include <condition_variable>
#include <inttypes.h>
@@ -165,6 +166,26 @@
YUV_REPROCESS,
};
+enum SystemCameraKind {
+ /**
+ * These camera devices are visible to all apps and system components alike
+ */
+ PUBLIC = 0,
+
+ /**
+ * These camera devices are visible only to processes having the
+ * android.permission.SYSTEM_CAMERA permission. They are not exposed to 3P
+ * apps.
+ */
+ SYSTEM_ONLY_CAMERA,
+
+ /**
+ * These camera devices are visible only to HAL clients (that try to connect
+ * on a hwbinder thread).
+ */
+ HIDDEN_SECURE_CAMERA
+};
+
namespace {
// "device@<version>/legacy/<id>"
const char *kDeviceNameRE = "device@([0-9]+\\.[0-9]+)/%s/(.+)";
@@ -851,6 +872,8 @@
static Status isAutoFocusModeAvailable(
CameraParameters &cameraParams, const char *mode) ;
static Status isMonochromeCamera(const camera_metadata_t *staticMeta);
+ static Status getSystemCameraKind(const camera_metadata_t* staticMeta,
+ SystemCameraKind* systemCameraKind);
// Used by switchToOffline where a new result queue is created for offline reqs
void updateInflightResultQueue(std::shared_ptr<ResultMetadataQueue> resultQueue);
@@ -2555,6 +2578,90 @@
}
}
+TEST_P(CameraHidlTest, systemCameraTest) {
+ hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
+ std::map<std::string, std::list<SystemCameraKind>> hiddenPhysicalIdToLogicalMap;
+ for (const auto& name : cameraDeviceNames) {
+ int deviceVersion = getCameraDeviceVersion(name, mProviderType);
+ switch (deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_3_6:
+ case CAMERA_DEVICE_API_VERSION_3_5:
+ case CAMERA_DEVICE_API_VERSION_3_4:
+ case CAMERA_DEVICE_API_VERSION_3_3:
+ case CAMERA_DEVICE_API_VERSION_3_2: {
+ ::android::sp<::android::hardware::camera::device::V3_2::ICameraDevice> device3_x;
+ ALOGI("getCameraCharacteristics: Testing camera device %s", name.c_str());
+ Return<void> ret;
+ ret = mProvider->getCameraDeviceInterface_V3_x(
+ name, [&](auto status, const auto& device) {
+ ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
+ ASSERT_EQ(Status::OK, status);
+ ASSERT_NE(device, nullptr);
+ device3_x = device;
+ });
+ ASSERT_TRUE(ret.isOk());
+
+ ret = device3_x->getCameraCharacteristics([&](auto status, const auto& chars) {
+ ASSERT_EQ(status, Status::OK);
+ const camera_metadata_t* staticMeta =
+ reinterpret_cast<const camera_metadata_t*>(chars.data());
+ ASSERT_NE(staticMeta, nullptr);
+ Status rc = isLogicalMultiCamera(staticMeta);
+ ASSERT_TRUE(Status::OK == rc || Status::METHOD_NOT_SUPPORTED == rc);
+ if (Status::METHOD_NOT_SUPPORTED == rc) {
+ return;
+ }
+ std::unordered_set<std::string> physicalIds;
+ ASSERT_EQ(Status::OK, getPhysicalCameraIds(staticMeta, &physicalIds));
+ SystemCameraKind systemCameraKind = SystemCameraKind::PUBLIC;
+ rc = getSystemCameraKind(staticMeta, &systemCameraKind);
+ ASSERT_EQ(rc, Status::OK);
+ for (auto physicalId : physicalIds) {
+ bool isPublicId = false;
+ for (auto& deviceName : cameraDeviceNames) {
+ std::string publicVersion, publicId;
+ ASSERT_TRUE(::matchDeviceName(deviceName, mProviderType, &publicVersion,
+ &publicId));
+ if (physicalId == publicId) {
+ isPublicId = true;
+ break;
+ }
+ }
+ // For hidden physical cameras, collect their associated logical cameras
+ // and store the system camera kind.
+ if (!isPublicId) {
+ auto it = hiddenPhysicalIdToLogicalMap.find(physicalId);
+ if (it == hiddenPhysicalIdToLogicalMap.end()) {
+ hiddenPhysicalIdToLogicalMap.insert(std::make_pair(
+ physicalId, std::list<SystemCameraKind>(systemCameraKind)));
+ } else {
+ it->second.push_back(systemCameraKind);
+ }
+ }
+ }
+ });
+ ASSERT_TRUE(ret.isOk());
+ } break;
+ case CAMERA_DEVICE_API_VERSION_1_0: {
+ // Not applicable
+ } break;
+ default: {
+ ALOGE("%s: Unsupported device version %d", __func__, deviceVersion);
+ ADD_FAILURE();
+ } break;
+ }
+ }
+
+ // Check that the system camera kind of the logical cameras associated with
+ // each hidden physical camera is the same.
+ for (const auto& it : hiddenPhysicalIdToLogicalMap) {
+ SystemCameraKind neededSystemCameraKind = it.second.front();
+ for (auto foundSystemCamera : it.second) {
+ ASSERT_EQ(neededSystemCameraKind, foundSystemCamera);
+ }
+ }
+}
+
// Verify that the static camera characteristics can be retrieved
// successfully.
TEST_P(CameraHidlTest, getCameraCharacteristics) {
@@ -5671,6 +5778,39 @@
return ret;
}
+Status CameraHidlTest::getSystemCameraKind(const camera_metadata_t* staticMeta,
+ SystemCameraKind* systemCameraKind) {
+ Status ret = Status::OK;
+ if (nullptr == staticMeta || nullptr == systemCameraKind) {
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ camera_metadata_ro_entry entry;
+ int rc = find_camera_metadata_ro_entry(staticMeta, ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+ &entry);
+ if (0 != rc) {
+ return Status::ILLEGAL_ARGUMENT;
+ }
+
+ if (entry.count == 1 &&
+ entry.data.u8[0] == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA) {
+ *systemCameraKind = SystemCameraKind::HIDDEN_SECURE_CAMERA;
+ return ret;
+ }
+
+ // Go through the capabilities and check if it has
+ // ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA
+ for (size_t i = 0; i < entry.count; ++i) {
+ uint8_t capability = entry.data.u8[i];
+ if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA) {
+ *systemCameraKind = SystemCameraKind::SYSTEM_ONLY_CAMERA;
+ return ret;
+ }
+ }
+ *systemCameraKind = SystemCameraKind::PUBLIC;
+ return ret;
+}
+
// Check whether this is a monochrome camera using the static camera characteristics.
Status CameraHidlTest::isMonochromeCamera(const camera_metadata_t *staticMeta) {
Status ret = Status::METHOD_NOT_SUPPORTED;
@@ -6391,8 +6531,10 @@
const hidl_vec<hidl_string>& deviceNames) {
const camera_metadata_t* metadata = (camera_metadata_t*)chars.data();
ASSERT_NE(nullptr, metadata);
-
- Status rc = isLogicalMultiCamera(metadata);
+ SystemCameraKind systemCameraKind = SystemCameraKind::PUBLIC;
+ Status rc = getSystemCameraKind(metadata, &systemCameraKind);
+ ASSERT_EQ(rc, Status::OK);
+ rc = isLogicalMultiCamera(metadata);
ASSERT_TRUE(Status::OK == rc || Status::METHOD_NOT_SUPPORTED == rc);
if (Status::METHOD_NOT_SUPPORTED == rc) {
return;
@@ -6411,6 +6553,7 @@
ASSERT_NE(physicalId, cameraId);
bool isPublicId = false;
std::string fullPublicId;
+ SystemCameraKind physSystemCameraKind = SystemCameraKind::PUBLIC;
for (auto& deviceName : deviceNames) {
std::string publicVersion, publicId;
ASSERT_TRUE(::matchDeviceName(deviceName, mProviderType, &publicVersion, &publicId));
@@ -6434,9 +6577,16 @@
ret = subDevice->getCameraCharacteristics(
[&](auto status, const auto& chars) {
ASSERT_EQ(Status::OK, status);
- retcode = find_camera_metadata_ro_entry(
- (const camera_metadata_t *)chars.data(),
- ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
+ const camera_metadata_t* staticMeta =
+ reinterpret_cast<const camera_metadata_t*>(chars.data());
+ rc = getSystemCameraKind(staticMeta, &physSystemCameraKind);
+ ASSERT_EQ(rc, Status::OK);
+ // Make sure that the system camera kind of a non-hidden
+ // physical cameras is the same as the logical camera associated
+ // with it.
+ ASSERT_EQ(physSystemCameraKind, systemCameraKind);
+ retcode = find_camera_metadata_ro_entry(staticMeta,
+ ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
bool subCameraHasZoomRatioRange = (0 == retcode && entry.count == 2);
ASSERT_EQ(hasZoomRatioRange, subCameraHasZoomRatioRange);
});
@@ -6452,17 +6602,16 @@
ASSERT_NE(device3_5, nullptr);
// Check camera characteristics for hidden camera id
- Return<void> ret = device3_5->getPhysicalCameraCharacteristics(physicalId,
- [&](auto status, const auto& chars) {
- verifyCameraCharacteristics(status, chars);
- verifyMonochromeCharacteristics(chars, deviceVersion);
-
- retcode = find_camera_metadata_ro_entry(
- (const camera_metadata_t *)chars.data(),
- ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
- bool subCameraHasZoomRatioRange = (0 == retcode && entry.count == 2);
- ASSERT_EQ(hasZoomRatioRange, subCameraHasZoomRatioRange);
- });
+ Return<void> ret = device3_5->getPhysicalCameraCharacteristics(
+ physicalId, [&](auto status, const auto& chars) {
+ verifyCameraCharacteristics(status, chars);
+ verifyMonochromeCharacteristics(chars, deviceVersion);
+ retcode =
+ find_camera_metadata_ro_entry((const camera_metadata_t*)chars.data(),
+ ANDROID_CONTROL_ZOOM_RATIO_RANGE, &entry);
+ bool subCameraHasZoomRatioRange = (0 == retcode && entry.count == 2);
+ ASSERT_EQ(hasZoomRatioRange, subCameraHasZoomRatioRange);
+ });
ASSERT_TRUE(ret.isOk());
// Check calling getCameraDeviceInterface_V3_x() on hidden camera id returns
diff --git a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-external-service.rc b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-external-service.rc
index 107097e..b3b06b2 100644
--- a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-external-service.rc
+++ b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-external-service.rc
@@ -6,4 +6,4 @@
group audio camera input drmrpc usb
ioprio rt 4
capabilities SYS_NICE
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
diff --git a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy.rc b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy.rc
index b45158a..7c5e69b 100644
--- a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy.rc
+++ b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy.rc
@@ -8,4 +8,4 @@
group audio camera input drmrpc
ioprio rt 4
capabilities SYS_NICE
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
diff --git a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy_64.rc b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy_64.rc
index 955b28e..49bca8f 100644
--- a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy_64.rc
+++ b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service-lazy_64.rc
@@ -8,4 +8,4 @@
group audio camera input drmrpc
ioprio rt 4
capabilities SYS_NICE
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
diff --git a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service.rc b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service.rc
index c065815..4bd1fb4 100644
--- a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service.rc
+++ b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service.rc
@@ -6,4 +6,4 @@
group audio camera input drmrpc
ioprio rt 4
capabilities SYS_NICE
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
diff --git a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service_64.rc b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service_64.rc
index 63dd11d..b444325 100644
--- a/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service_64.rc
+++ b/camera/provider/2.5/default/android.hardware.camera.provider@2.5-service_64.rc
@@ -6,4 +6,4 @@
group audio camera input drmrpc
ioprio rt 4
capabilities SYS_NICE
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
diff --git a/current.txt b/current.txt
index d2b7206..edf96b2 100644
--- a/current.txt
+++ b/current.txt
@@ -619,6 +619,7 @@
164826a380f4c1700183003f62d7532e367b67381c30ea44f946c0cf00008f85 android.hardware.audio@6.0::IStreamOut
997fdaad7a9d17ee7e01feb7031a753e2365e72ad30b11d950e9183fabdf3844 android.hardware.audio@6.0::IStreamOutCallback
e7ca0db9a1098210f327a9b152fa6afe6bf019c41e5264c64829d04d50c0a526 android.hardware.audio@6.0::IStreamOutEventCallback
+aa2211abd803e03d05ea11c18749db068f785fe026f8d99bce64bd764f63d194 android.hardware.audio@6.0::IStreamOutEventCallback # b/150175043
822369cf4dc16a6f6b9622bcf86cbdc0b692dc82193fc15e967767175cbfdd8f android.hardware.audio@6.0::types
bee662c62d997d8065e2bcb5c1e7a9578931f22ce28fd02c219fdb4d0630abf7 android.hardware.audio.common@6.0::types
525bec6b44f1103869c269a128d51b8dccd73af5340ba863c8886c68357c7faf android.hardware.audio.effect@6.0::IAcousticEchoCancelerEffect
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index 7a00ed2..27b633a 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -204,7 +204,7 @@
void Test_setActiveConfigWithConstraints(
const IComposerClient::VsyncPeriodChangeConstraints& constraints, bool refreshMiss);
- void sendRefreshFrame(const VsyncPeriodChangeTimeline&);
+ void sendRefreshFrame(const VsyncPeriodChangeTimeline*);
void waitForVsyncPeriodChange(Display display, const VsyncPeriodChangeTimeline& timeline,
int64_t desiredTimeNanos, int64_t oldPeriodNanos,
@@ -294,7 +294,7 @@
display, config, constraints, &timeline));
if (timeline.refreshRequired) {
- sendRefreshFrame(timeline);
+ sendRefreshFrame(&timeline);
}
waitForVsyncPeriodChange(display, timeline, constraints.desiredTimeNanos, 0,
expectedVsyncPeriodNanos);
@@ -350,7 +350,7 @@
}
}
-TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_SeamlessNotAllowed) {
+TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints_SeamlessNotAllowed) {
VsyncPeriodChangeTimeline timeline;
IComposerClient::VsyncPeriodChangeConstraints constraints;
@@ -365,6 +365,7 @@
display, config2, IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
if (configGroup1 != configGroup2) {
mComposerClient->setActiveConfig(display, config1);
+ sendRefreshFrame(nullptr);
EXPECT_EQ(Error::SEAMLESS_NOT_ALLOWED,
mComposerClient->setActiveConfigWithConstraints(display, config2,
constraints, &timeline));
@@ -377,11 +378,13 @@
return std::chrono::time_point<std::chrono::steady_clock>(std::chrono::nanoseconds(time));
}
-void GraphicsComposerHidlCommandTest::sendRefreshFrame(const VsyncPeriodChangeTimeline& timeline) {
- // Refresh time should be before newVsyncAppliedTimeNanos
- EXPECT_LT(timeline.refreshTimeNanos, timeline.newVsyncAppliedTimeNanos);
+void GraphicsComposerHidlCommandTest::sendRefreshFrame(const VsyncPeriodChangeTimeline* timeline) {
+ if (timeline != nullptr) {
+ // Refresh time should be before newVsyncAppliedTimeNanos
+ EXPECT_LT(timeline->refreshTimeNanos, timeline->newVsyncAppliedTimeNanos);
- std::this_thread::sleep_until(toTimePoint(timeline.refreshTimeNanos));
+ std::this_thread::sleep_until(toTimePoint(timeline->refreshTimeNanos));
+ }
mWriter->selectDisplay(mPrimaryDisplay);
mComposerClient->setPowerMode(mPrimaryDisplay, V2_1::IComposerClient::PowerMode::ON);
@@ -453,6 +456,7 @@
for (Display display : mComposerCallback->getDisplays()) {
forEachTwoConfigs(display, [&](Config config1, Config config2) {
mComposerClient->setActiveConfig(display, config1);
+ sendRefreshFrame(nullptr);
int32_t vsyncPeriod1 = mComposerClient->getDisplayAttribute_2_4(
display, config1, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
@@ -478,7 +482,7 @@
// callback
std::this_thread::sleep_until(toTimePoint(timeline.refreshTimeNanos) + 100ms);
}
- sendRefreshFrame(timeline);
+ sendRefreshFrame(&timeline);
}
waitForVsyncPeriodChange(display, timeline, constraints.desiredTimeNanos, vsyncPeriod1,
vsyncPeriod2);
@@ -493,7 +497,7 @@
if (newTimelime.has_value()) {
if (timeline.refreshRequired) {
- sendRefreshFrame(newTimelime.value());
+ sendRefreshFrame(&newTimelime.value());
}
waitForVsyncPeriodChange(display, newTimelime.value(), constraints.desiredTimeNanos,
vsyncPeriod1, vsyncPeriod2);
diff --git a/graphics/mapper/4.0/utils/vts/MapperVts.cpp b/graphics/mapper/4.0/utils/vts/MapperVts.cpp
index cb90fa0..5b2a94e 100644
--- a/graphics/mapper/4.0/utils/vts/MapperVts.cpp
+++ b/graphics/mapper/4.0/utils/vts/MapperVts.cpp
@@ -71,7 +71,8 @@
return mAllocator;
}
-const native_handle_t* Gralloc::cloneBuffer(const hidl_handle& rawHandle) {
+const native_handle_t* Gralloc::cloneBuffer(const hidl_handle& rawHandle,
+ enum Tolerance /*tolerance*/) {
const native_handle_t* bufferHandle = native_handle_clone(rawHandle.getNativeHandle());
EXPECT_NE(nullptr, bufferHandle);
@@ -84,39 +85,37 @@
std::vector<const native_handle_t*> Gralloc::allocate(const BufferDescriptor& descriptor,
uint32_t count, bool import,
- bool allowFailure, uint32_t* outStride) {
+ enum Tolerance tolerance,
+ uint32_t* outStride) {
std::vector<const native_handle_t*> bufferHandles;
bufferHandles.reserve(count);
- mAllocator->allocate(
- descriptor, count,
- [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) {
- ASSERT_EQ(Error::NONE, tmpError) << "failed to allocate buffers";
- ASSERT_EQ(count, tmpBuffers.size()) << "invalid buffer array";
+ mAllocator->allocate(descriptor, count,
+ [&](const auto& tmpError, const auto& tmpStride, const auto& tmpBuffers) {
+ if (canTolerate(tolerance, tmpError)) {
+ return;
+ }
- for (uint32_t i = 0; i < count; i++) {
- const native_handle_t* bufferHandle = nullptr;
- if (import) {
- if (allowFailure) {
- bufferHandle = importBuffer(tmpBuffers[i]);
- } else {
- ASSERT_NO_FATAL_FAILURE(bufferHandle = importBuffer(tmpBuffers[i]));
- }
- } else {
- if (allowFailure) {
- bufferHandle = cloneBuffer(tmpBuffers[i]);
- } else {
- ASSERT_NO_FATAL_FAILURE(bufferHandle = cloneBuffer(tmpBuffers[i]));
- }
- }
- if (bufferHandle) {
- bufferHandles.push_back(bufferHandle);
- }
- }
+ ASSERT_EQ(Error::NONE, tmpError) << "failed to allocate buffers";
+ ASSERT_EQ(count, tmpBuffers.size()) << "invalid buffer array";
- if (outStride) {
- *outStride = tmpStride;
- }
- });
+ for (uint32_t i = 0; i < count; i++) {
+ const native_handle_t* bufferHandle = nullptr;
+ if (import) {
+ ASSERT_NO_FATAL_FAILURE(
+ bufferHandle = importBuffer(tmpBuffers[i], tolerance));
+ } else {
+ ASSERT_NO_FATAL_FAILURE(
+ bufferHandle = cloneBuffer(tmpBuffers[i], tolerance));
+ }
+ if (bufferHandle) {
+ bufferHandles.push_back(bufferHandle);
+ }
+ }
+
+ if (outStride) {
+ *outStride = tmpStride;
+ }
+ });
if (::testing::Test::HasFatalFailure()) {
bufferHandles.clear();
@@ -126,13 +125,14 @@
}
const native_handle_t* Gralloc::allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
- bool import, bool allowFailure, uint32_t* outStride) {
+ bool import, enum Tolerance tolerance,
+ uint32_t* outStride) {
BufferDescriptor descriptor = createDescriptor(descriptorInfo);
if (::testing::Test::HasFatalFailure()) {
return nullptr;
}
- auto buffers = allocate(descriptor, 1, import, allowFailure, outStride);
+ auto buffers = allocate(descriptor, 1, import, tolerance, outStride);
if (::testing::Test::HasFatalFailure()) {
return nullptr;
}
@@ -157,11 +157,14 @@
return descriptor;
}
-const native_handle_t* Gralloc::importBuffer(const hidl_handle& rawHandle) {
+const native_handle_t* Gralloc::importBuffer(const hidl_handle& rawHandle,
+ enum Tolerance tolerance) {
const native_handle_t* bufferHandle = nullptr;
mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) {
- ASSERT_EQ(Error::NONE, tmpError)
- << "failed to import buffer %p" << rawHandle.getNativeHandle();
+ if (!canTolerate(tolerance, tmpError)) {
+ ASSERT_EQ(Error::NONE, tmpError)
+ << "failed to import buffer %p" << rawHandle.getNativeHandle();
+ }
bufferHandle = static_cast<const native_handle_t*>(tmpBuffer);
});
diff --git a/graphics/mapper/4.0/utils/vts/include/mapper-vts/4.0/MapperVts.h b/graphics/mapper/4.0/utils/vts/include/mapper-vts/4.0/MapperVts.h
index cd40aa4..22a935f 100644
--- a/graphics/mapper/4.0/utils/vts/include/mapper-vts/4.0/MapperVts.h
+++ b/graphics/mapper/4.0/utils/vts/include/mapper-vts/4.0/MapperVts.h
@@ -37,6 +37,16 @@
// A wrapper to IAllocator and IMapper.
class Gralloc {
public:
+ enum class Tolerance : uint32_t {
+ kToleranceStrict = 0x0U,
+ kToleranceBadDescriptor = 0x1U << std::underlying_type_t<Error>(Error::BAD_DESCRIPTOR),
+ kToleranceBadBuffer = 0x1U << std::underlying_type_t<Error>(Error::BAD_BUFFER),
+ kToleranceBadValue = 0x1U << std::underlying_type_t<Error>(Error::BAD_VALUE),
+ kToleranceNoResource = 0x1U << std::underlying_type_t<Error>(Error::NO_RESOURCES),
+ kToleranceUnSupported = 0x1U << std::underlying_type_t<Error>(Error::UNSUPPORTED),
+ kToleranceAllErrors = ~0x0U,
+ };
+
Gralloc(const std::string& allocatorServiceName = "default",
const std::string& mapperServiceName = "default", bool errOnFailure = true);
~Gralloc();
@@ -49,12 +59,27 @@
// is true, the returned buffers are also imported into the mapper.
//
// Either case, the returned buffers must be freed with freeBuffer.
- std::vector<const native_handle_t*> allocate(const BufferDescriptor& descriptor, uint32_t count,
- bool import = true, bool allowFailure = false,
- uint32_t* outStride = nullptr);
+ std::vector<const native_handle_t*> allocate(
+ const BufferDescriptor& descriptor, uint32_t count, bool import = true,
+ enum Tolerance tolerance = Tolerance::kToleranceStrict, uint32_t* outStride = nullptr);
+
const native_handle_t* allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
- bool import = true, bool allowFailure = false,
- uint32_t* outStride = nullptr);
+ bool import, enum Tolerance tolerance, uint32_t* outStride);
+
+ const native_handle_t* allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
+ bool import) {
+ return allocate(descriptorInfo, import, Tolerance::kToleranceStrict);
+ }
+
+ const native_handle_t* allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
+ bool import, enum Tolerance tolerance) {
+ return allocate(descriptorInfo, import, tolerance, nullptr);
+ }
+
+ const native_handle_t* allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
+ bool import, uint32_t* outStride) {
+ return allocate(descriptorInfo, import, Tolerance::kToleranceStrict, outStride);
+ }
// IMapper methods
@@ -62,7 +87,11 @@
BufferDescriptor createDescriptor(const IMapper::BufferDescriptorInfo& descriptorInfo);
- const native_handle_t* importBuffer(const hidl_handle& rawHandle);
+ const native_handle_t* importBuffer(const hidl_handle& rawHandle, enum Tolerance tolerance);
+ const native_handle_t* importBuffer(const hidl_handle& rawHandle) {
+ return importBuffer(rawHandle, Tolerance::kToleranceStrict);
+ }
+
void freeBuffer(const native_handle_t* bufferHandle);
// We use fd instead of hidl_handle in these functions to pass fences
@@ -96,11 +125,19 @@
uint64_t* outReservedSize);
private:
+ bool canTolerate(Tolerance tolerance, Error error) {
+ return (std::underlying_type_t<Tolerance>(tolerance) &
+ 0x1U << std::underlying_type_t<Error>(error)) != 0;
+ }
+
void init(const std::string& allocatorServiceName, const std::string& mapperServiceName);
// initialize without checking for failure to get service
void initNoErr(const std::string& allocatorServiceName, const std::string& mapperServiceName);
- const native_handle_t* cloneBuffer(const hidl_handle& rawHandle);
+ const native_handle_t* cloneBuffer(const hidl_handle& rawHandle, enum Tolerance tolerance);
+ const native_handle_t* cloneBuffer(const hidl_handle& rawHandle) {
+ return cloneBuffer(rawHandle, Tolerance::kToleranceStrict);
+ }
sp<IAllocator> mAllocator;
sp<IMapper> mMapper;
diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
index 8247ded..9e56534 100644
--- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
+++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
@@ -24,6 +24,7 @@
#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include <android/sync.h>
#include <gralloctypes/Gralloc4.h>
#include <gtest/gtest.h>
@@ -40,8 +41,10 @@
namespace vts {
namespace {
+using ::android::base::unique_fd;
using android::hardware::graphics::common::V1_2::BufferUsage;
using android::hardware::graphics::common::V1_2::PixelFormat;
+using Tolerance = ::android::hardware::graphics::mapper::V4_0::vts::Gralloc::Tolerance;
using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
using aidl::android::hardware::graphics::common::BlendMode;
using aidl::android::hardware::graphics::common::Cta861_3;
@@ -276,7 +279,7 @@
}
}
- void verifyRGBA8888(const native_handle_t* bufferHandle, uint8_t* data, uint32_t height,
+ void verifyRGBA8888(const native_handle_t* bufferHandle, const uint8_t* data, uint32_t height,
size_t strideInBytes, size_t widthInBytes, uint32_t seed = 0) {
hidl_vec<uint8_t> vec;
ASSERT_EQ(Error::NONE,
@@ -294,6 +297,49 @@
}
}
+ void traverseYCbCr888Data(const android_ycbcr& yCbCr, int32_t width, int32_t height,
+ int64_t hSubsampling, int64_t vSubsampling,
+ std::function<void(uint8_t*, uint8_t)> traverseFuncion) {
+ auto yData = static_cast<uint8_t*>(yCbCr.y);
+ auto cbData = static_cast<uint8_t*>(yCbCr.cb);
+ auto crData = static_cast<uint8_t*>(yCbCr.cr);
+ auto yStride = yCbCr.ystride;
+ auto cStride = yCbCr.cstride;
+ auto chromaStep = yCbCr.chroma_step;
+
+ for (uint32_t y = 0; y < height; y++) {
+ for (uint32_t x = 0; x < width; x++) {
+ auto val = static_cast<uint8_t>(height * y + x);
+
+ traverseFuncion(yData + yStride * y + x, val);
+
+ if (y % vSubsampling == 0 && x % hSubsampling == 0) {
+ uint32_t subSampleX = x / hSubsampling;
+ uint32_t subSampleY = y / vSubsampling;
+ const auto subSampleOffset = cStride * subSampleY + chromaStep * subSampleX;
+ const auto subSampleVal =
+ static_cast<uint8_t>(height * subSampleY + subSampleX);
+
+ traverseFuncion(cbData + subSampleOffset, subSampleVal);
+ traverseFuncion(crData + subSampleOffset, subSampleVal + 1);
+ }
+ }
+ }
+ }
+
+ void fillYCbCr888Data(const android_ycbcr& yCbCr, int32_t width, int32_t height,
+ int64_t hSubsampling, int64_t vSubsampling) {
+ traverseYCbCr888Data(yCbCr, width, height, hSubsampling, vSubsampling,
+ [](auto address, auto fillingData) { *address = fillingData; });
+ }
+
+ void verifyYCbCr888Data(const android_ycbcr& yCbCr, int32_t width, int32_t height,
+ int64_t hSubsampling, int64_t vSubsampling) {
+ traverseYCbCr888Data(
+ yCbCr, width, height, hSubsampling, vSubsampling,
+ [](auto address, auto expectedData) { EXPECT_EQ(*address, expectedData); });
+ }
+
bool isEqual(float a, float b) { return abs(a - b) < 0.0001; }
std::unique_ptr<Gralloc> mGralloc;
@@ -331,8 +377,9 @@
for (uint32_t count = 0; count < 5; count++) {
std::vector<const native_handle_t*> bufferHandles;
uint32_t stride;
- ASSERT_NO_FATAL_FAILURE(
- bufferHandles = mGralloc->allocate(descriptor, count, false, false, &stride));
+ ASSERT_NO_FATAL_FAILURE(bufferHandles =
+ mGralloc->allocate(descriptor, count, false,
+ Tolerance::kToleranceStrict, &stride));
if (count >= 1) {
EXPECT_LE(mDummyDescriptorInfo.width, stride) << "invalid buffer stride";
@@ -532,32 +579,30 @@
const native_handle_t* bufferHandle;
uint32_t stride;
- ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true, false, &stride));
+ ASSERT_NO_FATAL_FAILURE(
+ bufferHandle = mGralloc->allocate(info, true, Tolerance::kToleranceStrict, &stride));
// lock buffer for writing
const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
static_cast<int32_t>(info.height)};
- int fence = -1;
+ unique_fd fence;
uint8_t* data;
- ASSERT_NO_FATAL_FAILURE(
- data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence)));
+ ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
+ mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
// RGBA_8888
fillRGBA8888(data, info.height, stride * 4, info.width * 4);
- ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle));
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
// lock again for reading
- ASSERT_NO_FATAL_FAILURE(
- data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence)));
+ ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
+ mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
ASSERT_NO_FATAL_FAILURE(
verifyRGBA8888(bufferHandle, data, info.height, stride * 4, info.width * 4));
- ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle));
- if (fence >= 0) {
- close(fence);
- }
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
}
TEST_P(GraphicsMapperHidlTest, Lock_YCRCB_420_SP) {
@@ -566,16 +611,21 @@
const native_handle_t* bufferHandle;
uint32_t stride;
- ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(info, true, false, &stride));
+ ASSERT_NO_FATAL_FAILURE(bufferHandle = mGralloc->allocate(
+ info, true, Tolerance::kToleranceUnSupported, &stride));
+ if (bufferHandle == nullptr) {
+ GTEST_SUCCEED() << "YCRCB_420_SP format is unsupported";
+ return;
+ }
// lock buffer for writing
const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
static_cast<int32_t>(info.height)};
- int fence = -1;
+ unique_fd fence;
uint8_t* data;
- ASSERT_NO_FATAL_FAILURE(
- data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence)));
+ ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
+ mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
android_ycbcr yCbCr;
int64_t hSubsampling = 0;
@@ -583,74 +633,122 @@
ASSERT_NO_FATAL_FAILURE(
getAndroidYCbCr(bufferHandle, data, &yCbCr, &hSubsampling, &vSubsampling));
- auto yData = static_cast<uint8_t*>(yCbCr.y);
- auto cbData = static_cast<uint8_t*>(yCbCr.cb);
- auto crData = static_cast<uint8_t*>(yCbCr.cr);
- auto yStride = yCbCr.ystride;
- auto cStride = yCbCr.cstride;
- auto chromaStep = yCbCr.chroma_step;
-
constexpr uint32_t kCbCrSubSampleFactor = 2;
- ASSERT_EQ(crData + 1, cbData);
- ASSERT_EQ(2, chromaStep);
ASSERT_EQ(kCbCrSubSampleFactor, hSubsampling);
ASSERT_EQ(kCbCrSubSampleFactor, vSubsampling);
- for (uint32_t y = 0; y < info.height; y++) {
- for (uint32_t x = 0; x < info.width; x++) {
- auto val = static_cast<uint8_t>(info.height * y + x);
+ auto cbData = static_cast<uint8_t*>(yCbCr.cb);
+ auto crData = static_cast<uint8_t*>(yCbCr.cr);
+ ASSERT_EQ(crData + 1, cbData);
+ ASSERT_EQ(2, yCbCr.chroma_step);
- yData[yStride * y + x] = val;
+ fillYCbCr888Data(yCbCr, info.width, info.height, hSubsampling, vSubsampling);
- if (y % vSubsampling == 0 && x % hSubsampling == 0) {
- uint32_t subSampleX = x / hSubsampling;
- uint32_t subSampleY = y / vSubsampling;
- const auto subSampleOffset = cStride * subSampleY + chromaStep * subSampleX;
- const auto subSampleVal =
- static_cast<uint8_t>(info.height * subSampleY + subSampleX);
-
- cbData[subSampleOffset] = subSampleVal;
- crData[subSampleOffset] = subSampleVal + 1;
- }
- }
- }
-
- ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle));
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
// lock again for reading
- ASSERT_NO_FATAL_FAILURE(
- data = static_cast<uint8_t*>(mGralloc->lock(bufferHandle, info.usage, region, fence)));
+ ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
+ mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
ASSERT_NO_FATAL_FAILURE(
getAndroidYCbCr(bufferHandle, data, &yCbCr, &hSubsampling, &vSubsampling));
- yData = static_cast<uint8_t*>(yCbCr.y);
- cbData = static_cast<uint8_t*>(yCbCr.cb);
- crData = static_cast<uint8_t*>(yCbCr.cr);
+ verifyYCbCr888Data(yCbCr, info.width, info.height, hSubsampling, vSubsampling);
- for (uint32_t y = 0; y < info.height; y++) {
- for (uint32_t x = 0; x < info.width; x++) {
- auto val = static_cast<uint8_t>(info.height * y + x);
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
+}
- EXPECT_EQ(val, yData[yStride * y + x]);
+TEST_P(GraphicsMapperHidlTest, Lock_YV12) {
+ auto info = mDummyDescriptorInfo;
+ info.format = PixelFormat::YV12;
- if (y % vSubsampling == 0 && x % hSubsampling == 0) {
- uint32_t subSampleX = x / hSubsampling;
- uint32_t subSampleY = y / vSubsampling;
- const auto subSampleOffset = cStride * subSampleY + chromaStep * subSampleX;
- const auto subSampleVal =
- static_cast<uint8_t>(info.height * subSampleY + subSampleX);
+ const native_handle_t* bufferHandle;
+ uint32_t stride;
+ ASSERT_NO_FATAL_FAILURE(
+ bufferHandle = mGralloc->allocate(info, true, Tolerance::kToleranceStrict, &stride));
- EXPECT_EQ(subSampleVal, cbData[subSampleOffset]);
- EXPECT_EQ(subSampleVal + 1, crData[subSampleOffset]);
- }
- }
- }
+ // lock buffer for writing
+ const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
+ static_cast<int32_t>(info.height)};
+ unique_fd fence;
+ uint8_t* data;
- ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(bufferHandle));
- if (fence >= 0) {
- close(fence);
- }
+ ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
+ mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+
+ android_ycbcr yCbCr;
+ int64_t hSubsampling = 0;
+ int64_t vSubsampling = 0;
+ ASSERT_NO_FATAL_FAILURE(
+ getAndroidYCbCr(bufferHandle, data, &yCbCr, &hSubsampling, &vSubsampling));
+
+ constexpr uint32_t kCbCrSubSampleFactor = 2;
+ ASSERT_EQ(kCbCrSubSampleFactor, hSubsampling);
+ ASSERT_EQ(kCbCrSubSampleFactor, vSubsampling);
+
+ auto cbData = static_cast<uint8_t*>(yCbCr.cb);
+ auto crData = static_cast<uint8_t*>(yCbCr.cr);
+ ASSERT_EQ(crData + yCbCr.cstride * info.height / vSubsampling, cbData);
+ ASSERT_EQ(1, yCbCr.chroma_step);
+
+ fillYCbCr888Data(yCbCr, info.width, info.height, hSubsampling, vSubsampling);
+
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
+
+ // lock again for reading
+ ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
+ mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+
+ ASSERT_NO_FATAL_FAILURE(
+ getAndroidYCbCr(bufferHandle, data, &yCbCr, &hSubsampling, &vSubsampling));
+
+ verifyYCbCr888Data(yCbCr, info.width, info.height, hSubsampling, vSubsampling);
+
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
+}
+
+TEST_P(GraphicsMapperHidlTest, Lock_YCBCR_420_888) {
+ auto info = mDummyDescriptorInfo;
+ info.format = PixelFormat::YCBCR_420_888;
+
+ const native_handle_t* bufferHandle;
+ uint32_t stride;
+ ASSERT_NO_FATAL_FAILURE(
+ bufferHandle = mGralloc->allocate(info, true, Tolerance::kToleranceStrict, &stride));
+
+ // lock buffer for writing
+ const IMapper::Rect region{0, 0, static_cast<int32_t>(info.width),
+ static_cast<int32_t>(info.height)};
+ unique_fd fence;
+ uint8_t* data;
+
+ ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
+ mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+
+ android_ycbcr yCbCr;
+ int64_t hSubsampling = 0;
+ int64_t vSubsampling = 0;
+ ASSERT_NO_FATAL_FAILURE(
+ getAndroidYCbCr(bufferHandle, data, &yCbCr, &hSubsampling, &vSubsampling));
+
+ constexpr uint32_t kCbCrSubSampleFactor = 2;
+ ASSERT_EQ(kCbCrSubSampleFactor, hSubsampling);
+ ASSERT_EQ(kCbCrSubSampleFactor, vSubsampling);
+
+ fillYCbCr888Data(yCbCr, info.width, info.height, hSubsampling, vSubsampling);
+
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
+
+ // lock again for reading
+ ASSERT_NO_FATAL_FAILURE(data = static_cast<uint8_t*>(
+ mGralloc->lock(bufferHandle, info.usage, region, fence.get())));
+
+ ASSERT_NO_FATAL_FAILURE(
+ getAndroidYCbCr(bufferHandle, data, &yCbCr, &hSubsampling, &vSubsampling));
+
+ verifyYCbCr888Data(yCbCr, info.width, info.height, hSubsampling, vSubsampling);
+
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(bufferHandle)));
}
/**
@@ -741,8 +839,8 @@
const native_handle_t* rawHandle;
uint32_t stride;
- ASSERT_NO_FATAL_FAILURE(
- rawHandle = mGralloc->allocate(mDummyDescriptorInfo, false, false, &stride));
+ ASSERT_NO_FATAL_FAILURE(rawHandle = mGralloc->allocate(mDummyDescriptorInfo, false,
+ Tolerance::kToleranceStrict, &stride));
const native_handle_t* writeBufferHandle;
const native_handle_t* readBufferHandle;
@@ -766,11 +864,10 @@
fillRGBA8888(writeData, info.height, stride * 4, info.width * 4);
- int fence;
- ASSERT_NO_FATAL_FAILURE(fence = mGralloc->flushLockedBuffer(writeBufferHandle));
+ unique_fd fence;
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->flushLockedBuffer(writeBufferHandle)));
if (fence >= 0) {
ASSERT_EQ(0, sync_wait(fence, 3500));
- close(fence);
}
ASSERT_NO_FATAL_FAILURE(mGralloc->rereadLockedBuffer(readBufferHandle));
@@ -778,14 +875,9 @@
ASSERT_NO_FATAL_FAILURE(
verifyRGBA8888(readBufferHandle, readData, info.height, stride * 4, info.width * 4));
- ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(readBufferHandle));
- if (fence >= 0) {
- close(fence);
- }
- ASSERT_NO_FATAL_FAILURE(fence = mGralloc->unlock(writeBufferHandle));
- if (fence >= 0) {
- close(fence);
- }
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(readBufferHandle)));
+
+ ASSERT_NO_FATAL_FAILURE(fence.reset(mGralloc->unlock(writeBufferHandle)));
}
/**
@@ -964,8 +1056,8 @@
info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY;
const native_handle_t* bufferHandle = nullptr;
- bufferHandle = mGralloc->allocate(info, true, true);
- if (bufferHandle) {
+ bufferHandle = mGralloc->allocate(info, true, Tolerance::kToleranceAllErrors);
+ if (!bufferHandle) {
GTEST_SUCCEED() << "unable to allocate protected content";
return;
}
@@ -1267,8 +1359,8 @@
auto info = mDummyDescriptorInfo;
info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY;
- bufferHandle = mGralloc->allocate(info, true, true);
- if (bufferHandle) {
+ bufferHandle = mGralloc->allocate(info, true, Tolerance::kToleranceAllErrors);
+ if (!bufferHandle) {
GTEST_SUCCEED() << "unable to allocate protected content";
return;
}
diff --git a/health/utils/libhealth2impl/include/health2impl/HalHealthLoop.h b/health/utils/libhealth2impl/include/health2impl/HalHealthLoop.h
index d9b5580..362581e 100644
--- a/health/utils/libhealth2impl/include/health2impl/HalHealthLoop.h
+++ b/health/utils/libhealth2impl/include/health2impl/HalHealthLoop.h
@@ -55,7 +55,7 @@
void set_charger_online(const HealthInfo& health_info);
private:
- const std::string& instance_name_;
+ std::string instance_name_;
sp<IHealth> service_;
bool charger_online_ = false;
};
diff --git a/keymaster/4.1/support/attestation_record.cpp b/keymaster/4.1/support/attestation_record.cpp
index 63bf854..598b6b5 100644
--- a/keymaster/4.1/support/attestation_record.cpp
+++ b/keymaster/4.1/support/attestation_record.cpp
@@ -296,6 +296,10 @@
std::tuple<ErrorCode, AttestationRecord> parse_attestation_record(const hidl_vec<uint8_t>& cert) {
const uint8_t* p = cert.data();
X509_Ptr x509(d2i_X509(nullptr, &p, cert.size()));
+ if (!x509.get()) {
+ LOG(ERROR) << "Error converting DER";
+ return {ErrorCode::INVALID_ARGUMENT, {}};
+ }
ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */));
if (!oid.get()) {
diff --git a/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp b/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
index 7ea3275..495de0f 100644
--- a/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#define LOG_TAG "keymaster_hidl_hal_test"
+#include <cutils/log.h>
+
#include "Keymaster4_1HidlTest.h"
#include <cutils/properties.h>
@@ -23,6 +26,10 @@
#include <keymasterV4_1/attestation_record.h>
#include <keymasterV4_1/authorization_set.h>
+// Not to dump the attestation by default. Can enable by specify the parameter
+// "--dump_attestations" on lunching VTS
+static bool dumpAttestations = false;
+
namespace android::hardware::keymaster::V4_0 {
bool operator==(const AuthorizationSet& a, const AuthorizationSet& b) {
@@ -57,6 +64,10 @@
return retval;
}
+inline void dumpContent(string content) {
+ std::cout << content << std::endl;
+}
+
struct AuthorizationSetDifferences {
string aName;
string bName;
@@ -126,6 +137,23 @@
}
}
+bool tag_in_list(const KeyParameter& entry) {
+ // Attestations don't contain everything in key authorization lists, so we need to filter
+ // the key lists to produce the lists that we expect to match the attestations.
+ auto tag_list = {
+ Tag::INCLUDE_UNIQUE_ID, Tag::BLOB_USAGE_REQUIREMENTS, Tag::EC_CURVE,
+ Tag::HARDWARE_TYPE, Tag::VENDOR_PATCHLEVEL, Tag::BOOT_PATCHLEVEL,
+ Tag::CREATION_DATETIME,
+ };
+ return std::find(tag_list.begin(), tag_list.end(), (V4_1::Tag)entry.tag) != tag_list.end();
+}
+
+AuthorizationSet filter_tags(const AuthorizationSet& set) {
+ AuthorizationSet filtered;
+ std::remove_copy_if(set.begin(), set.end(), std::back_inserter(filtered), tag_in_list);
+ return filtered;
+}
+
void check_attestation_record(AttestationRecord attestation, const HidlBuf& challenge,
AuthorizationSet expected_sw_enforced,
AuthorizationSet expected_hw_enforced,
@@ -144,9 +172,9 @@
attestation.software_enforced.Sort();
attestation.hardware_enforced.Sort();
- EXPECT_EQ(expected_sw_enforced, attestation.software_enforced)
+ EXPECT_EQ(filter_tags(expected_sw_enforced), filter_tags(attestation.software_enforced))
<< DIFFERENCE(expected_sw_enforced, attestation.software_enforced);
- EXPECT_EQ(expected_hw_enforced, attestation.hardware_enforced)
+ EXPECT_EQ(filter_tags(expected_hw_enforced), filter_tags(attestation.hardware_enforced))
<< DIFFERENCE(expected_hw_enforced, attestation.hardware_enforced);
}
@@ -155,8 +183,8 @@
using std::string;
using DeviceUniqueAttestationTest = Keymaster4_1HidlTest;
-TEST_P(DeviceUniqueAttestationTest, StrongBoxOnly) {
- if (SecLevel() != SecurityLevel::STRONGBOX) return;
+TEST_P(DeviceUniqueAttestationTest, NonStrongBoxOnly) {
+ if (SecLevel() == SecurityLevel::STRONGBOX) return;
ASSERT_EQ(ErrorCode::OK, convert(GenerateKey(AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
@@ -193,11 +221,11 @@
if (SecLevel() != SecurityLevel::STRONGBOX) return;
ASSERT_EQ(ErrorCode::OK,
convert(GenerateKey(AuthorizationSetBuilder()
- .Authorization(TAG_NO_AUTH_REQUIRED)
- .RsaSigningKey(2048, 65537)
- .Digest(Digest::SHA_2_256)
- .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
- .Authorization(TAG_CREATION_DATETIME, 1))));
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .Authorization(TAG_INCLUDE_UNIQUE_ID))));
hidl_vec<hidl_vec<uint8_t>> cert_chain;
HidlBuf challenge("challenge");
@@ -209,14 +237,14 @@
.Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
&cert_chain)));
- EXPECT_EQ(1U, cert_chain.size());
+ EXPECT_EQ(2U, cert_chain.size());
+ if (dumpAttestations) dumpContent(bin2hex(cert_chain[0]));
auto [err, attestation] = parse_attestation_record(cert_chain[0]);
- EXPECT_EQ(ErrorCode::OK, err);
+ ASSERT_EQ(ErrorCode::OK, err);
check_attestation_record(attestation, challenge,
/* sw_enforced */
AuthorizationSetBuilder()
- .Authorization(TAG_CREATION_DATETIME, 1)
.Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
/* hw_enforced */
AuthorizationSetBuilder()
@@ -238,7 +266,7 @@
.Authorization(TAG_NO_AUTH_REQUIRED)
.EcdsaSigningKey(256)
.Digest(Digest::SHA_2_256)
- .Authorization(TAG_CREATION_DATETIME, 1))));
+ .Authorization(TAG_INCLUDE_UNIQUE_ID))));
hidl_vec<hidl_vec<uint8_t>> cert_chain;
HidlBuf challenge("challenge");
@@ -250,29 +278,42 @@
.Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
&cert_chain)));
- EXPECT_EQ(1U, cert_chain.size());
+ EXPECT_EQ(2U, cert_chain.size());
+ if (dumpAttestations) dumpContent(bin2hex(cert_chain[0]));
auto [err, attestation] = parse_attestation_record(cert_chain[0]);
- EXPECT_EQ(ErrorCode::OK, err);
+ ASSERT_EQ(ErrorCode::OK, err);
check_attestation_record(attestation, challenge,
- /* sw_enforced */
- AuthorizationSetBuilder()
- .Authorization(TAG_CREATION_DATETIME, 1)
- .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
- /* hw_enforced */
- AuthorizationSetBuilder()
- .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
- .Authorization(TAG_NO_AUTH_REQUIRED)
- .EcdsaSigningKey(256)
- .Digest(Digest::SHA_2_256)
- .Authorization(TAG_EC_CURVE, EcCurve::P_256)
- .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
- .Authorization(TAG_OS_VERSION, os_version())
- .Authorization(TAG_OS_PATCHLEVEL, os_patch_level()),
- SecLevel());
+ /* sw_enforced */
+ AuthorizationSetBuilder().Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
+ /* hw_enforced */
+ AuthorizationSetBuilder()
+ .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(256)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_EC_CURVE, EcCurve::P_256)
+ .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
+ .Authorization(TAG_OS_VERSION, os_version())
+ .Authorization(TAG_OS_PATCHLEVEL, os_patch_level()),
+ SecLevel());
}
INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(DeviceUniqueAttestationTest);
} // namespace test
} // namespace android::hardware::keymaster::V4_1
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ for (int i = 1; i < argc; ++i) {
+ if (argv[i][0] == '-') {
+ if (std::string(argv[i]) == "--dump_attestations") {
+ dumpAttestations = true;
+ }
+ }
+ }
+ int status = RUN_ALL_TESTS();
+ ALOGI("Test result = %d", status);
+ return status;
+}
diff --git a/neuralnetworks/1.0/vts/functional/AndroidTest.xml b/neuralnetworks/1.0/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..54e6e91
--- /dev/null
+++ b/neuralnetworks/1.0/vts/functional/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?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 VtsHalNeuralnetworksV1_0TargetTest.">
+ <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="VtsHalNeuralnetworksV1_0TargetTest->/data/local/tmp/VtsHalNeuralnetworksV1_0TargetTest" />
+ </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>
+</configuration>
diff --git a/neuralnetworks/1.0/vts/functional/BasicTests.cpp b/neuralnetworks/1.0/vts/functional/BasicTests.cpp
index cc44c9e..bda43b1 100644
--- a/neuralnetworks/1.0/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.0/vts/functional/BasicTests.cpp
@@ -18,8 +18,12 @@
#include "VtsHalNeuralnetworks.h"
+#include "1.0/Callbacks.h"
+
namespace android::hardware::neuralnetworks::V1_0::vts::functional {
+using implementation::PreparedModelCallback;
+
// create device test
TEST_P(NeuralnetworksHidlTest, CreateDevice) {}
@@ -43,4 +47,136 @@
EXPECT_TRUE(ret.isOk());
}
+// detect cycle
+TEST_P(NeuralnetworksHidlTest, CycleTest) {
+ // opnd0 = TENSOR_FLOAT32 // model input
+ // opnd1 = TENSOR_FLOAT32 // model input
+ // opnd2 = INT32 // model input
+ // opnd3 = ADD(opnd0, opnd4, opnd2)
+ // opnd4 = ADD(opnd1, opnd3, opnd2)
+ // opnd5 = ADD(opnd4, opnd0, opnd2) // model output
+ //
+ // +-----+
+ // | |
+ // v |
+ // 3 = ADD(0, 4, 2) |
+ // | |
+ // +----------+ |
+ // | |
+ // v |
+ // 4 = ADD(1, 3, 2) |
+ // | |
+ // +----------------+
+ // |
+ // |
+ // +-------+
+ // |
+ // v
+ // 5 = ADD(4, 0, 2)
+
+ const std::vector<Operand> operands = {
+ {
+ // operands[0]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 2,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[1]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[2]
+ .type = OperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 3,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[3]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[4]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 2,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[5]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 0,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_OUTPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ };
+
+ const std::vector<Operation> operations = {
+ {.type = OperationType::ADD, .inputs = {0, 4, 2}, .outputs = {3}},
+ {.type = OperationType::ADD, .inputs = {1, 3, 2}, .outputs = {4}},
+ {.type = OperationType::ADD, .inputs = {4, 0, 2}, .outputs = {5}},
+ };
+
+ const Model model = {
+ .operands = operands,
+ .operations = operations,
+ .inputIndexes = {0, 1, 2},
+ .outputIndexes = {5},
+ .operandValues = {},
+ .pools = {},
+ };
+
+ // ensure that getSupportedOperations() checks model validity
+ ErrorStatus supportedOpsErrorStatus = ErrorStatus::GENERAL_FAILURE;
+ Return<void> supportedOpsReturn = kDevice->getSupportedOperations(
+ model, [&model, &supportedOpsErrorStatus](ErrorStatus status,
+ const hidl_vec<bool>& supported) {
+ supportedOpsErrorStatus = status;
+ if (status == ErrorStatus::NONE) {
+ ASSERT_EQ(supported.size(), model.operations.size());
+ }
+ });
+ ASSERT_TRUE(supportedOpsReturn.isOk());
+ ASSERT_EQ(supportedOpsErrorStatus, ErrorStatus::INVALID_ARGUMENT);
+
+ // ensure that prepareModel() checks model validity
+ sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback;
+ Return<ErrorStatus> prepareLaunchReturn = kDevice->prepareModel(model, preparedModelCallback);
+ ASSERT_TRUE(prepareLaunchReturn.isOk());
+ // Note that preparation can fail for reasons other than an
+ // invalid model (invalid model should result in
+ // INVALID_ARGUMENT) -- for example, perhaps not all
+ // operations are supported, or perhaps the device hit some
+ // kind of capacity limit.
+ EXPECT_NE(prepareLaunchReturn, ErrorStatus::NONE);
+ EXPECT_NE(preparedModelCallback->getStatus(), ErrorStatus::NONE);
+ EXPECT_EQ(preparedModelCallback->getPreparedModel(), nullptr);
+}
+
} // namespace android::hardware::neuralnetworks::V1_0::vts::functional
diff --git a/neuralnetworks/1.0/vts/functional/Utils.cpp b/neuralnetworks/1.0/vts/functional/Utils.cpp
index 3613e69..32850b0 100644
--- a/neuralnetworks/1.0/vts/functional/Utils.cpp
+++ b/neuralnetworks/1.0/vts/functional/Utils.cpp
@@ -29,7 +29,11 @@
#include <gtest/gtest.h>
#include <algorithm>
+#include <cstring>
+#include <functional>
#include <iostream>
+#include <map>
+#include <numeric>
#include <vector>
namespace android::hardware::neuralnetworks {
@@ -172,6 +176,45 @@
return outputBuffers;
}
+uint32_t sizeOfData(V1_0::OperandType type) {
+ switch (type) {
+ case V1_0::OperandType::FLOAT32:
+ case V1_0::OperandType::INT32:
+ case V1_0::OperandType::UINT32:
+ case V1_0::OperandType::TENSOR_FLOAT32:
+ case V1_0::OperandType::TENSOR_INT32:
+ return 4;
+ case V1_0::OperandType::TENSOR_QUANT8_ASYMM:
+ return 1;
+ default:
+ CHECK(false) << "Invalid OperandType " << static_cast<uint32_t>(type);
+ return 0;
+ }
+}
+
+static bool isTensor(V1_0::OperandType type) {
+ switch (type) {
+ case V1_0::OperandType::FLOAT32:
+ case V1_0::OperandType::INT32:
+ case V1_0::OperandType::UINT32:
+ return false;
+ case V1_0::OperandType::TENSOR_FLOAT32:
+ case V1_0::OperandType::TENSOR_INT32:
+ case V1_0::OperandType::TENSOR_QUANT8_ASYMM:
+ return true;
+ default:
+ CHECK(false) << "Invalid OperandType " << static_cast<uint32_t>(type);
+ return false;
+ }
+}
+
+uint32_t sizeOfData(const V1_0::Operand& operand) {
+ const uint32_t dataSize = sizeOfData(operand.type);
+ if (isTensor(operand.type) && operand.dimensions.size() == 0) return 0;
+ return std::accumulate(operand.dimensions.begin(), operand.dimensions.end(), dataSize,
+ std::multiplies<>{});
+}
+
std::string gtestCompliantName(std::string name) {
// gtest test names must only contain alphanumeric characters
std::replace_if(
diff --git a/neuralnetworks/1.0/vts/functional/ValidateModel.cpp b/neuralnetworks/1.0/vts/functional/ValidateModel.cpp
index 79d8594..5ffbd43 100644
--- a/neuralnetworks/1.0/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.0/vts/functional/ValidateModel.cpp
@@ -17,9 +17,14 @@
#define LOG_TAG "neuralnetworks_hidl_hal_test"
#include "1.0/Callbacks.h"
+#include "1.0/Utils.h"
#include "GeneratedTestHarness.h"
#include "VtsHalNeuralnetworks.h"
+#include <optional>
+#include <type_traits>
+#include <utility>
+
namespace android::hardware::neuralnetworks::V1_0::vts::functional {
using implementation::PreparedModelCallback;
@@ -67,26 +72,6 @@
validatePrepareModel(device, message, model);
}
-// Delete element from hidl_vec. hidl_vec doesn't support a "remove" operation,
-// so this is efficiently accomplished by moving the element to the end and
-// resizing the hidl_vec to one less.
-template <typename Type>
-static void hidl_vec_removeAt(hidl_vec<Type>* vec, uint32_t index) {
- if (vec) {
- std::rotate(vec->begin() + index, vec->begin() + index + 1, vec->end());
- vec->resize(vec->size() - 1);
- }
-}
-
-template <typename Type>
-static uint32_t hidl_vec_push_back(hidl_vec<Type>* vec, const Type& value) {
- // assume vec is valid
- const uint32_t index = vec->size();
- vec->resize(index + 1);
- (*vec)[index] = value;
- return index;
-}
-
static uint32_t addOperand(Model* model) {
return hidl_vec_push_back(&model->operands,
{
@@ -107,6 +92,211 @@
return index;
}
+// If we introduce a CONSTANT_COPY for an operand of size operandSize,
+// how much will this increase the size of the model? This assumes
+// that we can (re)use all of model.operandValues for the operand
+// value.
+static size_t constantCopyExtraSize(const Model& model, size_t operandSize) {
+ const size_t operandValuesSize = model.operandValues.size();
+ return (operandValuesSize < operandSize) ? (operandSize - operandValuesSize) : 0;
+}
+
+// Highly specialized utility routine for converting an operand to
+// CONSTANT_COPY lifetime.
+//
+// Expects that:
+// - operand has a known size
+// - operand->lifetime has already been set to CONSTANT_COPY
+// - operand->location has been zeroed out
+//
+// Does the following:
+// - initializes operand->location to point to the beginning of model->operandValues
+// - resizes model->operandValues (if necessary) to be large enough for the operand
+// value, padding it with zeroes on the end
+//
+// Potential problem:
+// By changing the operand to CONSTANT_COPY lifetime, this function is effectively initializing the
+// operand with unspecified (but deterministic) data. This means that the model may be invalidated
+// in two ways: not only is the lifetime of CONSTANT_COPY invalid, but the operand's value in the
+// graph may also be invalid (e.g., if the operand is used as an activation code and has an invalid
+// value). For now, this should be fine because it just means we're not testing what we think we're
+// testing in certain cases; but we can handwave this and assume we're probabilistically likely to
+// exercise the validation code over the span of the entire test set and operand space.
+//
+// Aborts if the specified operand type is an extension type or OEM type.
+static void becomeConstantCopy(Model* model, Operand* operand) {
+ // sizeOfData will abort if the specified type is an extension type or OEM type.
+ const size_t sizeOfOperand = sizeOfData(*operand);
+ EXPECT_NE(sizeOfOperand, size_t(0));
+ operand->location.poolIndex = 0;
+ operand->location.offset = 0;
+ operand->location.length = sizeOfOperand;
+ if (model->operandValues.size() < sizeOfOperand) {
+ model->operandValues.resize(sizeOfOperand);
+ }
+}
+
+// The sizeForBinder() functions estimate the size of the
+// representation of a value when sent to binder. It's probably a bit
+// of an under-estimate, because we don't know the size of the
+// metadata in the binder format (e.g., representation of the size of
+// a vector); but at least it adds up "big" things like vector
+// contents. However, it doesn't treat inter-field or end-of-struct
+// padding in a methodical way -- there's no attempt to be consistent
+// in whether or not padding in the native (C++) representation
+// contributes to the estimated size for the binder representation;
+// and there's no attempt to understand what padding (if any) is
+// needed in the binder representation.
+//
+// This assumes that non-metadata uses a fixed length encoding (e.g.,
+// a uint32_t is always encoded in sizeof(uint32_t) bytes, rather than
+// using an encoding whose length is related to the magnitude of the
+// encoded value).
+
+template <typename Type>
+static size_t sizeForBinder(const Type& val) {
+ static_assert(std::is_trivially_copyable_v<std::remove_reference_t<Type>>,
+ "expected a trivially copyable type");
+ return sizeof(val);
+}
+
+template <typename Type>
+static size_t sizeForBinder(const hidl_vec<Type>& vec) {
+ return std::accumulate(vec.begin(), vec.end(), 0,
+ [](size_t acc, const Type& x) { return acc + sizeForBinder(x); });
+}
+
+template <>
+size_t sizeForBinder(const Operand& operand) {
+ size_t size = 0;
+
+ size += sizeForBinder(operand.type);
+ size += sizeForBinder(operand.dimensions);
+ size += sizeForBinder(operand.numberOfConsumers);
+ size += sizeForBinder(operand.scale);
+ size += sizeForBinder(operand.zeroPoint);
+ size += sizeForBinder(operand.lifetime);
+ size += sizeForBinder(operand.location);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const Operation& operation) {
+ size_t size = 0;
+
+ size += sizeForBinder(operation.type);
+ size += sizeForBinder(operation.inputs);
+ size += sizeForBinder(operation.outputs);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const hidl_string& name) {
+ return name.size();
+}
+
+template <>
+size_t sizeForBinder(const hidl_memory& memory) {
+ // This is just a guess.
+
+ size_t size = 0;
+
+ if (const native_handle_t* handle = memory.handle()) {
+ size += sizeof(*handle);
+ size += sizeof(handle->data[0] * (handle->numFds + handle->numInts));
+ }
+ size += sizeForBinder(memory.name());
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const Model& model) {
+ size_t size = 0;
+
+ size += sizeForBinder(model.operands);
+ size += sizeForBinder(model.operations);
+ size += sizeForBinder(model.inputIndexes);
+ size += sizeForBinder(model.outputIndexes);
+ size += sizeForBinder(model.operandValues);
+ size += sizeForBinder(model.pools);
+
+ return size;
+}
+
+// https://developer.android.com/reference/android/os/TransactionTooLargeException.html
+//
+// "The Binder transaction buffer has a limited fixed size,
+// currently 1Mb, which is shared by all transactions in progress
+// for the process."
+//
+// Will our representation fit under this limit? There are two complications:
+// - Our representation size is just approximate (see sizeForBinder()).
+// - This object may not be the only occupant of the Binder transaction buffer.
+// So we'll be very conservative: We want the representation size to be no
+// larger than half the transaction buffer size.
+//
+// If our representation grows large enough that it still fits within
+// the transaction buffer but combined with other transactions may
+// exceed the buffer size, then we may see intermittent HAL transport
+// errors.
+static bool exceedsBinderSizeLimit(size_t representationSize) {
+ // Instead of using this fixed buffer size, we might instead be able to use
+ // ProcessState::self()->getMmapSize(). However, this has a potential
+ // problem: The binder/mmap size of the current process does not necessarily
+ // indicate the binder/mmap size of the service (i.e., the other process).
+ // The only way it would be a good indication is if both the current process
+ // and the service use the default size.
+ static const size_t kHalfBufferSize = 1024 * 1024 / 2;
+
+ return representationSize > kHalfBufferSize;
+}
+
+///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
+
+static void mutateExecutionOrderTest(const sp<IDevice>& device, const V1_0::Model& model) {
+ for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ const Operation& operationObj = model.operations[operation];
+ for (uint32_t input : operationObj.inputs) {
+ if (model.operands[input].lifetime == OperandLifeTime::TEMPORARY_VARIABLE ||
+ model.operands[input].lifetime == OperandLifeTime::MODEL_OUTPUT) {
+ // This operation reads an operand written by some
+ // other operation. Move this operation to the
+ // beginning of the sequence, ensuring that it reads
+ // the operand before that operand is written, thereby
+ // violating execution order rules.
+ const std::string message = "mutateExecutionOrderTest: operation " +
+ std::to_string(operation) + " is a reader";
+ validate(device, message, model, [operation](Model* model) {
+ auto& operations = model->operations;
+ std::rotate(operations.begin(), operations.begin() + operation,
+ operations.begin() + operation + 1);
+ });
+ break; // only need to do this once per operation
+ }
+ }
+ for (uint32_t output : operationObj.outputs) {
+ if (model.operands[output].numberOfConsumers > 0) {
+ // This operation writes an operand read by some other
+ // operation. Move this operation to the end of the
+ // sequence, ensuring that it writes the operand after
+ // that operand is read, thereby violating execution
+ // order rules.
+ const std::string message = "mutateExecutionOrderTest: operation " +
+ std::to_string(operation) + " is a writer";
+ validate(device, message, model, [operation](Model* model) {
+ auto& operations = model->operations;
+ std::rotate(operations.begin() + operation, operations.begin() + operation + 1,
+ operations.end());
+ });
+ break; // only need to do this once per operation
+ }
+ }
+ }
+}
+
///////////////////////// VALIDATE MODEL OPERAND TYPE /////////////////////////
static const int32_t invalidOperandTypes[] = {
@@ -218,9 +408,233 @@
}
}
+///////////////////////// VALIDATE OPERAND LIFETIME /////////////////////////////////////////////
+
+static std::vector<OperandLifeTime> getInvalidLifeTimes(const Model& model, size_t modelSize,
+ const Operand& operand) {
+ // TODO: Support OperandLifeTime::CONSTANT_REFERENCE as an invalid lifetime
+ // TODO: Support OperandLifeTime::NO_VALUE as an invalid lifetime
+
+ // Ways to get an invalid lifetime:
+ // - change whether a lifetime means an operand should have a writer
+ std::vector<OperandLifeTime> ret;
+ switch (operand.lifetime) {
+ case OperandLifeTime::MODEL_OUTPUT:
+ case OperandLifeTime::TEMPORARY_VARIABLE:
+ ret = {
+ OperandLifeTime::MODEL_INPUT,
+ OperandLifeTime::CONSTANT_COPY,
+ };
+ break;
+ case OperandLifeTime::CONSTANT_COPY:
+ case OperandLifeTime::CONSTANT_REFERENCE:
+ case OperandLifeTime::MODEL_INPUT:
+ ret = {
+ OperandLifeTime::TEMPORARY_VARIABLE,
+ OperandLifeTime::MODEL_OUTPUT,
+ };
+ break;
+ case OperandLifeTime::NO_VALUE:
+ // Not enough information to know whether
+ // TEMPORARY_VARIABLE or CONSTANT_COPY would be invalid --
+ // is this operand written (then CONSTANT_COPY would be
+ // invalid) or not (then TEMPORARY_VARIABLE would be
+ // invalid)?
+ break;
+ default:
+ ADD_FAILURE();
+ break;
+ }
+
+ const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
+ if (!operandSize ||
+ exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
+ // Unknown size or too-large size
+ ret.erase(std::remove(ret.begin(), ret.end(), OperandLifeTime::CONSTANT_COPY), ret.end());
+ }
+
+ return ret;
+}
+
+static void mutateOperandLifeTimeTest(const sp<IDevice>& device, const V1_0::Model& model) {
+ const size_t modelSize = sizeForBinder(model);
+ for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ const std::vector<OperandLifeTime> invalidLifeTimes =
+ getInvalidLifeTimes(model, modelSize, model.operands[operand]);
+ for (OperandLifeTime invalidLifeTime : invalidLifeTimes) {
+ const std::string message = "mutateOperandLifetimeTest: operand " +
+ std::to_string(operand) + " has lifetime " +
+ toString(invalidLifeTime) + " instead of lifetime " +
+ toString(model.operands[operand].lifetime);
+ validate(device, message, model, [operand, invalidLifeTime](Model* model) {
+ static const DataLocation kZeroDataLocation = {};
+ Operand& operandObj = model->operands[operand];
+ switch (operandObj.lifetime) {
+ case OperandLifeTime::MODEL_INPUT: {
+ hidl_vec_remove(&model->inputIndexes, uint32_t(operand));
+ break;
+ }
+ case OperandLifeTime::MODEL_OUTPUT: {
+ hidl_vec_remove(&model->outputIndexes, uint32_t(operand));
+ break;
+ }
+ default:
+ break;
+ }
+ operandObj.lifetime = invalidLifeTime;
+ operandObj.location = kZeroDataLocation;
+ switch (invalidLifeTime) {
+ case OperandLifeTime::CONSTANT_COPY: {
+ becomeConstantCopy(model, &operandObj);
+ break;
+ }
+ case OperandLifeTime::MODEL_INPUT:
+ hidl_vec_push_back(&model->inputIndexes, uint32_t(operand));
+ break;
+ case OperandLifeTime::MODEL_OUTPUT:
+ hidl_vec_push_back(&model->outputIndexes, uint32_t(operand));
+ break;
+ default:
+ break;
+ }
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND INPUT-or-OUTPUT //////////////////////////////////////
+
+static std::optional<OperandLifeTime> getInputOutputLifeTime(const Model& model, size_t modelSize,
+ const Operand& operand) {
+ // Ways to get an invalid lifetime (with respect to model inputIndexes and outputIndexes):
+ // - change whether a lifetime means an operand is a model input, a model output, or neither
+ // - preserve whether or not a lifetime means an operand should have a writer
+ switch (operand.lifetime) {
+ case OperandLifeTime::CONSTANT_COPY:
+ case OperandLifeTime::CONSTANT_REFERENCE:
+ return OperandLifeTime::MODEL_INPUT;
+ case OperandLifeTime::MODEL_INPUT: {
+ const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
+ if (!operandSize ||
+ exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
+ // Unknown size or too-large size
+ break;
+ }
+ return OperandLifeTime::CONSTANT_COPY;
+ }
+ case OperandLifeTime::MODEL_OUTPUT:
+ return OperandLifeTime::TEMPORARY_VARIABLE;
+ case OperandLifeTime::TEMPORARY_VARIABLE:
+ return OperandLifeTime::MODEL_OUTPUT;
+ case OperandLifeTime::NO_VALUE:
+ // Not enough information to know whether
+ // TEMPORARY_VARIABLE or CONSTANT_COPY would be an
+ // appropriate choice -- is this operand written (then
+ // TEMPORARY_VARIABLE would be appropriate) or not (then
+ // CONSTANT_COPY would be appropriate)?
+ break;
+ default:
+ ADD_FAILURE();
+ break;
+ }
+
+ return std::nullopt;
+}
+
+static void mutateOperandInputOutputTest(const sp<IDevice>& device, const V1_0::Model& model) {
+ const size_t modelSize = sizeForBinder(model);
+ for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ const std::optional<OperandLifeTime> changedLifeTime =
+ getInputOutputLifeTime(model, modelSize, model.operands[operand]);
+ if (changedLifeTime) {
+ const std::string message = "mutateOperandInputOutputTest: operand " +
+ std::to_string(operand) + " has lifetime " +
+ toString(*changedLifeTime) + " instead of lifetime " +
+ toString(model.operands[operand].lifetime);
+ validate(device, message, model, [operand, changedLifeTime](Model* model) {
+ static const DataLocation kZeroDataLocation = {};
+ Operand& operandObj = model->operands[operand];
+ operandObj.lifetime = *changedLifeTime;
+ operandObj.location = kZeroDataLocation;
+ if (*changedLifeTime == OperandLifeTime::CONSTANT_COPY) {
+ becomeConstantCopy(model, &operandObj);
+ }
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND NUMBER OF CONSUMERS //////////////////////////////////
+
+static std::vector<uint32_t> getInvalidNumberOfConsumers(uint32_t numberOfConsumers) {
+ if (numberOfConsumers == 0) {
+ return {1};
+ } else {
+ return {numberOfConsumers - 1, numberOfConsumers + 1};
+ }
+}
+
+static void mutateOperandNumberOfConsumersTest(const sp<IDevice>& device,
+ const V1_0::Model& model) {
+ for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ const std::vector<uint32_t> invalidNumberOfConsumersVec =
+ getInvalidNumberOfConsumers(model.operands[operand].numberOfConsumers);
+ for (uint32_t invalidNumberOfConsumers : invalidNumberOfConsumersVec) {
+ const std::string message =
+ "mutateOperandNumberOfConsumersTest: operand " + std::to_string(operand) +
+ " numberOfConsumers = " + std::to_string(invalidNumberOfConsumers);
+ validate(device, message, model, [operand, invalidNumberOfConsumers](Model* model) {
+ model->operands[operand].numberOfConsumers = invalidNumberOfConsumers;
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND NUMBER OF WRITERS ////////////////////////////////////
+
+static void mutateOperandAddWriterTest(const sp<IDevice>& device, const V1_0::Model& model) {
+ for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ for (size_t badOutputNum = 0; badOutputNum < model.operations[operation].outputs.size();
+ ++badOutputNum) {
+ const uint32_t outputOperandIndex = model.operations[operation].outputs[badOutputNum];
+ const std::string message = "mutateOperandAddWriterTest: operation " +
+ std::to_string(operation) + " writes to " +
+ std::to_string(outputOperandIndex);
+ // We'll insert a copy of the operation, all of whose
+ // OTHER output operands are newly-created -- i.e.,
+ // there'll only be a duplicate write of ONE of that
+ // operation's output operands.
+ validate(device, message, model, [operation, badOutputNum](Model* model) {
+ Operation newOperation = model->operations[operation];
+ for (uint32_t input : newOperation.inputs) {
+ ++model->operands[input].numberOfConsumers;
+ }
+ for (size_t outputNum = 0; outputNum < newOperation.outputs.size(); ++outputNum) {
+ if (outputNum == badOutputNum) continue;
+
+ Operand operandValue = model->operands[newOperation.outputs[outputNum]];
+ operandValue.numberOfConsumers = 0;
+ if (operandValue.lifetime == OperandLifeTime::MODEL_OUTPUT) {
+ operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
+ } else {
+ ASSERT_EQ(operandValue.lifetime, OperandLifeTime::TEMPORARY_VARIABLE);
+ }
+ newOperation.outputs[outputNum] =
+ hidl_vec_push_back(&model->operands, operandValue);
+ }
+ // Where do we insert the extra writer (a new
+ // operation)? It has to be later than all the
+ // writers of its inputs. The easiest thing to do
+ // is to insert it at the end of the operation
+ // sequence.
+ hidl_vec_push_back(&model->operations, newOperation);
+ });
+ }
+ }
+}
+
///////////////////////// VALIDATE EXTRA ??? /////////////////////////
-// TODO: Operand::lifetime
// TODO: Operand::location
///////////////////////// VALIDATE OPERATION OPERAND TYPE /////////////////////////
@@ -351,6 +765,33 @@
}
}
+///////////////////////// VALIDATE MODEL OPERANDS WRITTEN ///////////////////////////////////////
+
+static void mutateOperationRemoveWriteTest(const sp<IDevice>& device, const V1_0::Model& model) {
+ for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ for (size_t outputNum = 0; outputNum < model.operations[operation].outputs.size();
+ ++outputNum) {
+ const uint32_t outputOperandIndex = model.operations[operation].outputs[outputNum];
+ if (model.operands[outputOperandIndex].numberOfConsumers > 0) {
+ const std::string message = "mutateOperationRemoveWriteTest: operation " +
+ std::to_string(operation) + " writes to " +
+ std::to_string(outputOperandIndex);
+ validate(device, message, model, [operation, outputNum](Model* model) {
+ uint32_t& outputOperandIndex = model->operations[operation].outputs[outputNum];
+ Operand operandValue = model->operands[outputOperandIndex];
+ operandValue.numberOfConsumers = 0;
+ if (operandValue.lifetime == OperandLifeTime::MODEL_OUTPUT) {
+ operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
+ } else {
+ ASSERT_EQ(operandValue.lifetime, OperandLifeTime::TEMPORARY_VARIABLE);
+ }
+ outputOperandIndex = hidl_vec_push_back(&model->operands, operandValue);
+ });
+ }
+ }
+ }
+}
+
///////////////////////// REMOVE OPERAND FROM EVERYTHING /////////////////////////
static void removeValueAndDecrementGreaterValues(hidl_vec<uint32_t>* vec, uint32_t value) {
@@ -476,14 +917,20 @@
////////////////////////// ENTRY POINT //////////////////////////////
void validateModel(const sp<IDevice>& device, const Model& model) {
+ mutateExecutionOrderTest(device, model);
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
mutateOperandZeroPointTest(device, model);
+ mutateOperandLifeTimeTest(device, model);
+ mutateOperandInputOutputTest(device, model);
+ mutateOperandNumberOfConsumersTest(device, model);
+ mutateOperandAddWriterTest(device, model);
mutateOperationOperandTypeTest(device, model);
mutateOperationTypeTest(device, model);
mutateOperationInputOperandIndexTest(device, model);
mutateOperationOutputOperandIndexTest(device, model);
+ mutateOperationRemoveWriteTest(device, model);
removeOperandTest(device, model);
removeOperationTest(device, model);
removeOperationInputTest(device, model);
diff --git a/neuralnetworks/1.0/vts/functional/include/1.0/Utils.h b/neuralnetworks/1.0/vts/functional/include/1.0/Utils.h
index 3292f79..7bd0460 100644
--- a/neuralnetworks/1.0/vts/functional/include/1.0/Utils.h
+++ b/neuralnetworks/1.0/vts/functional/include/1.0/Utils.h
@@ -21,6 +21,7 @@
#include <android/hardware/neuralnetworks/1.0/types.h>
#include <android/hardware_buffer.h>
#include <android/hidl/memory/1.0/IMemory.h>
+#include <gtest/gtest.h>
#include <algorithm>
#include <iosfwd>
#include <string>
@@ -108,6 +109,15 @@
vec->resize(vec->size() - 1);
}
+// Assumes there is exactly one instance of the value in the vector.
+template <typename Type>
+inline void hidl_vec_remove(hidl_vec<Type>* vec, const Type& val) {
+ CHECK(vec != nullptr);
+ auto where = std::find(vec->begin(), vec->end(), val);
+ ASSERT_NE(where, vec->end());
+ hidl_vec_removeAt(vec, where - vec->begin());
+}
+
template <typename Type>
inline uint32_t hidl_vec_push_back(hidl_vec<Type>* vec, const Type& value) {
CHECK(vec != nullptr);
@@ -117,6 +127,18 @@
return index;
}
+// Returns the amount of space needed to store a value of the specified type.
+//
+// Aborts if the specified type is an extension type or OEM type.
+uint32_t sizeOfData(V1_0::OperandType type);
+
+// Returns the amount of space needed to store a value of the dimensions and
+// type of this operand. For a non-extension, non-OEM tensor with unspecified
+// rank or at least one unspecified dimension, returns zero.
+//
+// Aborts if the specified type is an extension type or OEM type.
+uint32_t sizeOfData(const V1_0::Operand& operand);
+
template <typename Type>
using Named = std::pair<std::string, Type>;
diff --git a/neuralnetworks/1.1/vts/functional/AndroidTest.xml b/neuralnetworks/1.1/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..a6f812f
--- /dev/null
+++ b/neuralnetworks/1.1/vts/functional/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?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 VtsHalNeuralnetworksV1_1TargetTest.">
+ <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="VtsHalNeuralnetworksV1_1TargetTest->/data/local/tmp/VtsHalNeuralnetworksV1_1TargetTest" />
+ </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>
+</configuration>
diff --git a/neuralnetworks/1.1/vts/functional/BasicTests.cpp b/neuralnetworks/1.1/vts/functional/BasicTests.cpp
index 44836f0..baadd1b 100644
--- a/neuralnetworks/1.1/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.1/vts/functional/BasicTests.cpp
@@ -18,10 +18,16 @@
#include "VtsHalNeuralnetworks.h"
+#include "1.0/Callbacks.h"
+
namespace android::hardware::neuralnetworks::V1_1::vts::functional {
using V1_0::DeviceStatus;
using V1_0::ErrorStatus;
+using V1_0::Operand;
+using V1_0::OperandLifeTime;
+using V1_0::OperandType;
+using V1_0::implementation::PreparedModelCallback;
// create device test
TEST_P(NeuralnetworksHidlTest, CreateDevice) {}
@@ -48,4 +54,137 @@
EXPECT_TRUE(ret.isOk());
}
+// detect cycle
+TEST_P(NeuralnetworksHidlTest, CycleTest) {
+ // opnd0 = TENSOR_FLOAT32 // model input
+ // opnd1 = TENSOR_FLOAT32 // model input
+ // opnd2 = INT32 // model input
+ // opnd3 = ADD(opnd0, opnd4, opnd2)
+ // opnd4 = ADD(opnd1, opnd3, opnd2)
+ // opnd5 = ADD(opnd4, opnd0, opnd2) // model output
+ //
+ // +-----+
+ // | |
+ // v |
+ // 3 = ADD(0, 4, 2) |
+ // | |
+ // +----------+ |
+ // | |
+ // v |
+ // 4 = ADD(1, 3, 2) |
+ // | |
+ // +----------------+
+ // |
+ // |
+ // +-------+
+ // |
+ // v
+ // 5 = ADD(4, 0, 2)
+
+ const std::vector<Operand> operands = {
+ {
+ // operands[0]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 2,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[1]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[2]
+ .type = OperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 3,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[3]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[4]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 2,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[5]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 0,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_OUTPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ };
+
+ const std::vector<Operation> operations = {
+ {.type = OperationType::ADD, .inputs = {0, 4, 2}, .outputs = {3}},
+ {.type = OperationType::ADD, .inputs = {1, 3, 2}, .outputs = {4}},
+ {.type = OperationType::ADD, .inputs = {4, 0, 2}, .outputs = {5}},
+ };
+
+ const Model model = {
+ .operands = operands,
+ .operations = operations,
+ .inputIndexes = {0, 1, 2},
+ .outputIndexes = {5},
+ .operandValues = {},
+ .pools = {},
+ };
+
+ // ensure that getSupportedOperations_1_1() checks model validity
+ ErrorStatus supportedOpsErrorStatus = ErrorStatus::GENERAL_FAILURE;
+ Return<void> supportedOpsReturn = kDevice->getSupportedOperations_1_1(
+ model, [&model, &supportedOpsErrorStatus](ErrorStatus status,
+ const hidl_vec<bool>& supported) {
+ supportedOpsErrorStatus = status;
+ if (status == ErrorStatus::NONE) {
+ ASSERT_EQ(supported.size(), model.operations.size());
+ }
+ });
+ ASSERT_TRUE(supportedOpsReturn.isOk());
+ ASSERT_EQ(supportedOpsErrorStatus, ErrorStatus::INVALID_ARGUMENT);
+
+ // ensure that prepareModel_1_1() checks model validity
+ sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback;
+ Return<ErrorStatus> prepareLaunchReturn = kDevice->prepareModel_1_1(
+ model, ExecutionPreference::FAST_SINGLE_ANSWER, preparedModelCallback);
+ ASSERT_TRUE(prepareLaunchReturn.isOk());
+ // Note that preparation can fail for reasons other than an
+ // invalid model (invalid model should result in
+ // INVALID_ARGUMENT) -- for example, perhaps not all
+ // operations are supported, or perhaps the device hit some
+ // kind of capacity limit.
+ EXPECT_NE(prepareLaunchReturn, ErrorStatus::NONE);
+ EXPECT_NE(preparedModelCallback->getStatus(), ErrorStatus::NONE);
+ EXPECT_EQ(preparedModelCallback->getPreparedModel(), nullptr);
+}
+
} // namespace android::hardware::neuralnetworks::V1_1::vts::functional
diff --git a/neuralnetworks/1.1/vts/functional/ValidateModel.cpp b/neuralnetworks/1.1/vts/functional/ValidateModel.cpp
index 3b6f0f8..1f4e4ed 100644
--- a/neuralnetworks/1.1/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.1/vts/functional/ValidateModel.cpp
@@ -16,13 +16,19 @@
#define LOG_TAG "neuralnetworks_hidl_hal_test"
+#include <android/hardware/neuralnetworks/1.1/types.h>
#include "1.0/Callbacks.h"
#include "1.0/Utils.h"
#include "GeneratedTestHarness.h"
#include "VtsHalNeuralnetworks.h"
+#include <optional>
+#include <type_traits>
+#include <utility>
+
namespace android::hardware::neuralnetworks::V1_1::vts::functional {
+using V1_0::DataLocation;
using V1_0::ErrorStatus;
using V1_0::IPreparedModel;
using V1_0::Operand;
@@ -105,6 +111,212 @@
return index;
}
+// If we introduce a CONSTANT_COPY for an operand of size operandSize,
+// how much will this increase the size of the model? This assumes
+// that we can (re)use all of model.operandValues for the operand
+// value.
+static size_t constantCopyExtraSize(const Model& model, size_t operandSize) {
+ const size_t operandValuesSize = model.operandValues.size();
+ return (operandValuesSize < operandSize) ? (operandSize - operandValuesSize) : 0;
+}
+
+// Highly specialized utility routine for converting an operand to
+// CONSTANT_COPY lifetime.
+//
+// Expects that:
+// - operand has a known size
+// - operand->lifetime has already been set to CONSTANT_COPY
+// - operand->location has been zeroed out
+//
+// Does the following:
+// - initializes operand->location to point to the beginning of model->operandValues
+// - resizes model->operandValues (if necessary) to be large enough for the operand
+// value, padding it with zeroes on the end
+//
+// Potential problem:
+// By changing the operand to CONSTANT_COPY lifetime, this function is effectively initializing the
+// operand with unspecified (but deterministic) data. This means that the model may be invalidated
+// in two ways: not only is the lifetime of CONSTANT_COPY invalid, but the operand's value in the
+// graph may also be invalid (e.g., if the operand is used as an activation code and has an invalid
+// value). For now, this should be fine because it just means we're not testing what we think we're
+// testing in certain cases; but we can handwave this and assume we're probabilistically likely to
+// exercise the validation code over the span of the entire test set and operand space.
+//
+// Aborts if the specified operand type is an extension type or OEM type.
+static void becomeConstantCopy(Model* model, Operand* operand) {
+ // sizeOfData will abort if the specified type is an extension type or OEM type.
+ const size_t sizeOfOperand = sizeOfData(*operand);
+ EXPECT_NE(sizeOfOperand, size_t(0));
+ operand->location.poolIndex = 0;
+ operand->location.offset = 0;
+ operand->location.length = sizeOfOperand;
+ if (model->operandValues.size() < sizeOfOperand) {
+ model->operandValues.resize(sizeOfOperand);
+ }
+}
+
+// The sizeForBinder() functions estimate the size of the
+// representation of a value when sent to binder. It's probably a bit
+// of an under-estimate, because we don't know the size of the
+// metadata in the binder format (e.g., representation of the size of
+// a vector); but at least it adds up "big" things like vector
+// contents. However, it doesn't treat inter-field or end-of-struct
+// padding in a methodical way -- there's no attempt to be consistent
+// in whether or not padding in the native (C++) representation
+// contributes to the estimated size for the binder representation;
+// and there's no attempt to understand what padding (if any) is
+// needed in the binder representation.
+//
+// This assumes that non-metadata uses a fixed length encoding (e.g.,
+// a uint32_t is always encoded in sizeof(uint32_t) bytes, rather than
+// using an encoding whose length is related to the magnitude of the
+// encoded value).
+
+template <typename Type>
+static size_t sizeForBinder(const Type& val) {
+ static_assert(std::is_trivially_copyable_v<std::remove_reference_t<Type>>,
+ "expected a trivially copyable type");
+ return sizeof(val);
+}
+
+template <typename Type>
+static size_t sizeForBinder(const hidl_vec<Type>& vec) {
+ return std::accumulate(vec.begin(), vec.end(), 0,
+ [](size_t acc, const Type& x) { return acc + sizeForBinder(x); });
+}
+
+template <>
+size_t sizeForBinder(const Operand& operand) {
+ size_t size = 0;
+
+ size += sizeForBinder(operand.type);
+ size += sizeForBinder(operand.dimensions);
+ size += sizeForBinder(operand.numberOfConsumers);
+ size += sizeForBinder(operand.scale);
+ size += sizeForBinder(operand.zeroPoint);
+ size += sizeForBinder(operand.lifetime);
+ size += sizeForBinder(operand.location);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const Operation& operation) {
+ size_t size = 0;
+
+ size += sizeForBinder(operation.type);
+ size += sizeForBinder(operation.inputs);
+ size += sizeForBinder(operation.outputs);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const hidl_string& name) {
+ return name.size();
+}
+
+template <>
+size_t sizeForBinder(const hidl_memory& memory) {
+ // This is just a guess.
+
+ size_t size = 0;
+
+ if (const native_handle_t* handle = memory.handle()) {
+ size += sizeof(*handle);
+ size += sizeof(handle->data[0] * (handle->numFds + handle->numInts));
+ }
+ size += sizeForBinder(memory.name());
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const Model& model) {
+ size_t size = 0;
+
+ size += sizeForBinder(model.operands);
+ size += sizeForBinder(model.operations);
+ size += sizeForBinder(model.inputIndexes);
+ size += sizeForBinder(model.outputIndexes);
+ size += sizeForBinder(model.operandValues);
+ size += sizeForBinder(model.pools);
+ size += sizeForBinder(model.relaxComputationFloat32toFloat16);
+
+ return size;
+}
+
+// https://developer.android.com/reference/android/os/TransactionTooLargeException.html
+//
+// "The Binder transaction buffer has a limited fixed size,
+// currently 1Mb, which is shared by all transactions in progress
+// for the process."
+//
+// Will our representation fit under this limit? There are two complications:
+// - Our representation size is just approximate (see sizeForBinder()).
+// - This object may not be the only occupant of the Binder transaction buffer.
+// So we'll be very conservative: We want the representation size to be no
+// larger than half the transaction buffer size.
+//
+// If our representation grows large enough that it still fits within
+// the transaction buffer but combined with other transactions may
+// exceed the buffer size, then we may see intermittent HAL transport
+// errors.
+static bool exceedsBinderSizeLimit(size_t representationSize) {
+ // Instead of using this fixed buffer size, we might instead be able to use
+ // ProcessState::self()->getMmapSize(). However, this has a potential
+ // problem: The binder/mmap size of the current process does not necessarily
+ // indicate the binder/mmap size of the service (i.e., the other process).
+ // The only way it would be a good indication is if both the current process
+ // and the service use the default size.
+ static const size_t kHalfBufferSize = 1024 * 1024 / 2;
+
+ return representationSize > kHalfBufferSize;
+}
+
+///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
+
+static void mutateExecutionOrderTest(const sp<IDevice>& device, const V1_1::Model& model) {
+ for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ const Operation& operationObj = model.operations[operation];
+ for (uint32_t input : operationObj.inputs) {
+ if (model.operands[input].lifetime == OperandLifeTime::TEMPORARY_VARIABLE ||
+ model.operands[input].lifetime == OperandLifeTime::MODEL_OUTPUT) {
+ // This operation reads an operand written by some
+ // other operation. Move this operation to the
+ // beginning of the sequence, ensuring that it reads
+ // the operand before that operand is written, thereby
+ // violating execution order rules.
+ const std::string message = "mutateExecutionOrderTest: operation " +
+ std::to_string(operation) + " is a reader";
+ validate(device, message, model, [operation](Model* model, ExecutionPreference*) {
+ auto& operations = model->operations;
+ std::rotate(operations.begin(), operations.begin() + operation,
+ operations.begin() + operation + 1);
+ });
+ break; // only need to do this once per operation
+ }
+ }
+ for (uint32_t output : operationObj.outputs) {
+ if (model.operands[output].numberOfConsumers > 0) {
+ // This operation writes an operand read by some other
+ // operation. Move this operation to the end of the
+ // sequence, ensuring that it writes the operand after
+ // that operand is read, thereby violating execution
+ // order rules.
+ const std::string message = "mutateExecutionOrderTest: operation " +
+ std::to_string(operation) + " is a writer";
+ validate(device, message, model, [operation](Model* model, ExecutionPreference*) {
+ auto& operations = model->operations;
+ std::rotate(operations.begin() + operation, operations.begin() + operation + 1,
+ operations.end());
+ });
+ break; // only need to do this once per operation
+ }
+ }
+ }
+}
+
///////////////////////// VALIDATE MODEL OPERAND TYPE /////////////////////////
static const int32_t invalidOperandTypes[] = {
@@ -221,9 +433,240 @@
}
}
+///////////////////////// VALIDATE OPERAND LIFETIME /////////////////////////////////////////////
+
+static std::vector<OperandLifeTime> getInvalidLifeTimes(const Model& model, size_t modelSize,
+ const Operand& operand) {
+ // TODO: Support OperandLifeTime::CONSTANT_REFERENCE as an invalid lifetime
+ // TODO: Support OperandLifeTime::NO_VALUE as an invalid lifetime
+
+ // Ways to get an invalid lifetime:
+ // - change whether a lifetime means an operand should have a writer
+ std::vector<OperandLifeTime> ret;
+ switch (operand.lifetime) {
+ case OperandLifeTime::MODEL_OUTPUT:
+ case OperandLifeTime::TEMPORARY_VARIABLE:
+ ret = {
+ OperandLifeTime::MODEL_INPUT,
+ OperandLifeTime::CONSTANT_COPY,
+ };
+ break;
+ case OperandLifeTime::CONSTANT_COPY:
+ case OperandLifeTime::CONSTANT_REFERENCE:
+ case OperandLifeTime::MODEL_INPUT:
+ ret = {
+ OperandLifeTime::TEMPORARY_VARIABLE,
+ OperandLifeTime::MODEL_OUTPUT,
+ };
+ break;
+ case OperandLifeTime::NO_VALUE:
+ // Not enough information to know whether
+ // TEMPORARY_VARIABLE or CONSTANT_COPY would be invalid --
+ // is this operand written (then CONSTANT_COPY would be
+ // invalid) or not (then TEMPORARY_VARIABLE would be
+ // invalid)?
+ break;
+ default:
+ ADD_FAILURE();
+ break;
+ }
+
+ const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
+ if (!operandSize ||
+ exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
+ // Unknown size or too-large size
+ ret.erase(std::remove(ret.begin(), ret.end(), OperandLifeTime::CONSTANT_COPY), ret.end());
+ }
+
+ return ret;
+}
+
+static void mutateOperandLifeTimeTest(const sp<IDevice>& device, const V1_1::Model& model) {
+ const size_t modelSize = sizeForBinder(model);
+ for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ const std::vector<OperandLifeTime> invalidLifeTimes =
+ getInvalidLifeTimes(model, modelSize, model.operands[operand]);
+ for (OperandLifeTime invalidLifeTime : invalidLifeTimes) {
+ const std::string message = "mutateOperandLifetimeTest: operand " +
+ std::to_string(operand) + " has lifetime " +
+ toString(invalidLifeTime) + " instead of lifetime " +
+ toString(model.operands[operand].lifetime);
+ validate(device, message, model,
+ [operand, invalidLifeTime](Model* model, ExecutionPreference*) {
+ static const DataLocation kZeroDataLocation = {};
+ Operand& operandObj = model->operands[operand];
+ switch (operandObj.lifetime) {
+ case OperandLifeTime::MODEL_INPUT: {
+ hidl_vec_remove(&model->inputIndexes, uint32_t(operand));
+ break;
+ }
+ case OperandLifeTime::MODEL_OUTPUT: {
+ hidl_vec_remove(&model->outputIndexes, uint32_t(operand));
+ break;
+ }
+ default:
+ break;
+ }
+ operandObj.lifetime = invalidLifeTime;
+ operandObj.location = kZeroDataLocation;
+ switch (invalidLifeTime) {
+ case OperandLifeTime::CONSTANT_COPY: {
+ becomeConstantCopy(model, &operandObj);
+ break;
+ }
+ case OperandLifeTime::MODEL_INPUT:
+ hidl_vec_push_back(&model->inputIndexes, uint32_t(operand));
+ break;
+ case OperandLifeTime::MODEL_OUTPUT:
+ hidl_vec_push_back(&model->outputIndexes, uint32_t(operand));
+ break;
+ default:
+ break;
+ }
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND INPUT-or-OUTPUT //////////////////////////////////////
+
+static std::optional<OperandLifeTime> getInputOutputLifeTime(const Model& model, size_t modelSize,
+ const Operand& operand) {
+ // Ways to get an invalid lifetime (with respect to model inputIndexes and outputIndexes):
+ // - change whether a lifetime means an operand is a model input, a model output, or neither
+ // - preserve whether or not a lifetime means an operand should have a writer
+ switch (operand.lifetime) {
+ case OperandLifeTime::CONSTANT_COPY:
+ case OperandLifeTime::CONSTANT_REFERENCE:
+ return OperandLifeTime::MODEL_INPUT;
+ case OperandLifeTime::MODEL_INPUT: {
+ const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
+ if (!operandSize ||
+ exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
+ // Unknown size or too-large size
+ break;
+ }
+ return OperandLifeTime::CONSTANT_COPY;
+ }
+ case OperandLifeTime::MODEL_OUTPUT:
+ return OperandLifeTime::TEMPORARY_VARIABLE;
+ case OperandLifeTime::TEMPORARY_VARIABLE:
+ return OperandLifeTime::MODEL_OUTPUT;
+ case OperandLifeTime::NO_VALUE:
+ // Not enough information to know whether
+ // TEMPORARY_VARIABLE or CONSTANT_COPY would be an
+ // appropriate choice -- is this operand written (then
+ // TEMPORARY_VARIABLE would be appropriate) or not (then
+ // CONSTANT_COPY would be appropriate)?
+ break;
+ default:
+ ADD_FAILURE();
+ break;
+ }
+
+ return std::nullopt;
+}
+
+static void mutateOperandInputOutputTest(const sp<IDevice>& device, const V1_1::Model& model) {
+ const size_t modelSize = sizeForBinder(model);
+ for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ const std::optional<OperandLifeTime> changedLifeTime =
+ getInputOutputLifeTime(model, modelSize, model.operands[operand]);
+ if (changedLifeTime) {
+ const std::string message = "mutateOperandInputOutputTest: operand " +
+ std::to_string(operand) + " has lifetime " +
+ toString(*changedLifeTime) + " instead of lifetime " +
+ toString(model.operands[operand].lifetime);
+ validate(device, message, model,
+ [operand, changedLifeTime](Model* model, ExecutionPreference*) {
+ static const DataLocation kZeroDataLocation = {};
+ Operand& operandObj = model->operands[operand];
+ operandObj.lifetime = *changedLifeTime;
+ operandObj.location = kZeroDataLocation;
+ if (*changedLifeTime == OperandLifeTime::CONSTANT_COPY) {
+ becomeConstantCopy(model, &operandObj);
+ }
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND NUMBER OF CONSUMERS //////////////////////////////////
+
+static std::vector<uint32_t> getInvalidNumberOfConsumers(uint32_t numberOfConsumers) {
+ if (numberOfConsumers == 0) {
+ return {1};
+ } else {
+ return {numberOfConsumers - 1, numberOfConsumers + 1};
+ }
+}
+
+static void mutateOperandNumberOfConsumersTest(const sp<IDevice>& device,
+ const V1_1::Model& model) {
+ for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ const std::vector<uint32_t> invalidNumberOfConsumersVec =
+ getInvalidNumberOfConsumers(model.operands[operand].numberOfConsumers);
+ for (uint32_t invalidNumberOfConsumers : invalidNumberOfConsumersVec) {
+ const std::string message =
+ "mutateOperandNumberOfConsumersTest: operand " + std::to_string(operand) +
+ " numberOfConsumers = " + std::to_string(invalidNumberOfConsumers);
+ validate(device, message, model,
+ [operand, invalidNumberOfConsumers](Model* model, ExecutionPreference*) {
+ model->operands[operand].numberOfConsumers = invalidNumberOfConsumers;
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND NUMBER OF WRITERS ////////////////////////////////////
+
+static void mutateOperandAddWriterTest(const sp<IDevice>& device, const V1_1::Model& model) {
+ for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ for (size_t badOutputNum = 0; badOutputNum < model.operations[operation].outputs.size();
+ ++badOutputNum) {
+ const uint32_t outputOperandIndex = model.operations[operation].outputs[badOutputNum];
+ const std::string message = "mutateOperandAddWriterTest: operation " +
+ std::to_string(operation) + " writes to " +
+ std::to_string(outputOperandIndex);
+ // We'll insert a copy of the operation, all of whose
+ // OTHER output operands are newly-created -- i.e.,
+ // there'll only be a duplicate write of ONE of that
+ // operation's output operands.
+ validate(device, message, model,
+ [operation, badOutputNum](Model* model, ExecutionPreference*) {
+ Operation newOperation = model->operations[operation];
+ for (uint32_t input : newOperation.inputs) {
+ ++model->operands[input].numberOfConsumers;
+ }
+ for (size_t outputNum = 0; outputNum < newOperation.outputs.size();
+ ++outputNum) {
+ if (outputNum == badOutputNum) continue;
+
+ Operand operandValue =
+ model->operands[newOperation.outputs[outputNum]];
+ operandValue.numberOfConsumers = 0;
+ if (operandValue.lifetime == OperandLifeTime::MODEL_OUTPUT) {
+ operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
+ } else {
+ ASSERT_EQ(operandValue.lifetime,
+ OperandLifeTime::TEMPORARY_VARIABLE);
+ }
+ newOperation.outputs[outputNum] =
+ hidl_vec_push_back(&model->operands, operandValue);
+ }
+ // Where do we insert the extra writer (a new
+ // operation)? It has to be later than all the
+ // writers of its inputs. The easiest thing to do
+ // is to insert it at the end of the operation
+ // sequence.
+ hidl_vec_push_back(&model->operations, newOperation);
+ });
+ }
+ }
+}
+
///////////////////////// VALIDATE EXTRA ??? /////////////////////////
-// TODO: Operand::lifetime
// TODO: Operand::location
///////////////////////// VALIDATE OPERATION OPERAND TYPE /////////////////////////
@@ -358,6 +801,37 @@
}
}
+///////////////////////// VALIDATE MODEL OPERANDS WRITTEN ///////////////////////////////////////
+
+static void mutateOperationRemoveWriteTest(const sp<IDevice>& device, const V1_1::Model& model) {
+ for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ for (size_t outputNum = 0; outputNum < model.operations[operation].outputs.size();
+ ++outputNum) {
+ const uint32_t outputOperandIndex = model.operations[operation].outputs[outputNum];
+ if (model.operands[outputOperandIndex].numberOfConsumers > 0) {
+ const std::string message = "mutateOperationRemoveWriteTest: operation " +
+ std::to_string(operation) + " writes to " +
+ std::to_string(outputOperandIndex);
+ validate(device, message, model,
+ [operation, outputNum](Model* model, ExecutionPreference*) {
+ uint32_t& outputOperandIndex =
+ model->operations[operation].outputs[outputNum];
+ Operand operandValue = model->operands[outputOperandIndex];
+ operandValue.numberOfConsumers = 0;
+ if (operandValue.lifetime == OperandLifeTime::MODEL_OUTPUT) {
+ operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
+ } else {
+ ASSERT_EQ(operandValue.lifetime,
+ OperandLifeTime::TEMPORARY_VARIABLE);
+ }
+ outputOperandIndex =
+ hidl_vec_push_back(&model->operands, operandValue);
+ });
+ }
+ }
+ }
+}
+
///////////////////////// REMOVE OPERAND FROM EVERYTHING /////////////////////////
static void removeValueAndDecrementGreaterValues(hidl_vec<uint32_t>* vec, uint32_t value) {
@@ -504,14 +978,20 @@
////////////////////////// ENTRY POINT //////////////////////////////
void validateModel(const sp<IDevice>& device, const Model& model) {
+ mutateExecutionOrderTest(device, model);
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
mutateOperandZeroPointTest(device, model);
+ mutateOperandLifeTimeTest(device, model);
+ mutateOperandInputOutputTest(device, model);
+ mutateOperandNumberOfConsumersTest(device, model);
+ mutateOperandAddWriterTest(device, model);
mutateOperationOperandTypeTest(device, model);
mutateOperationTypeTest(device, model);
mutateOperationInputOperandIndexTest(device, model);
mutateOperationOutputOperandIndexTest(device, model);
+ mutateOperationRemoveWriteTest(device, model);
removeOperandTest(device, model);
removeOperationTest(device, model);
removeOperationInputTest(device, model);
diff --git a/neuralnetworks/1.2/vts/functional/Android.bp b/neuralnetworks/1.2/vts/functional/Android.bp
index 481eb80..182f716 100644
--- a/neuralnetworks/1.2/vts/functional/Android.bp
+++ b/neuralnetworks/1.2/vts/functional/Android.bp
@@ -15,11 +15,12 @@
//
cc_library_static {
- name: "VtsHalNeuralNetworksV1_2Callbacks",
+ name: "VtsHalNeuralNetworksV1_2_utils",
defaults: ["neuralnetworks_vts_functional_defaults"],
export_include_dirs: ["include"],
srcs: [
"Callbacks.cpp",
+ "Utils.cpp",
],
static_libs: [
"android.hardware.neuralnetworks@1.0",
@@ -51,7 +52,7 @@
],
static_libs: [
"VtsHalNeuralNetworksV1_0_utils",
- "VtsHalNeuralNetworksV1_2Callbacks",
+ "VtsHalNeuralNetworksV1_2_utils",
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
diff --git a/neuralnetworks/1.2/vts/functional/AndroidTest.xml b/neuralnetworks/1.2/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..adbdf40
--- /dev/null
+++ b/neuralnetworks/1.2/vts/functional/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?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 VtsHalNeuralnetworksV1_2TargetTest.">
+ <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="VtsHalNeuralnetworksV1_2TargetTest->/data/local/tmp/VtsHalNeuralnetworksV1_2TargetTest" />
+ </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>
+</configuration>
diff --git a/neuralnetworks/1.2/vts/functional/BasicTests.cpp b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
index 58d3c4a..77340e7 100644
--- a/neuralnetworks/1.2/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
@@ -20,9 +20,13 @@
namespace android::hardware::neuralnetworks::V1_2::vts::functional {
+using implementation::PreparedModelCallback;
using V1_0::DeviceStatus;
using V1_0::ErrorStatus;
+using V1_0::OperandLifeTime;
using V1_0::PerformanceInfo;
+using V1_1::ExecutionPreference;
+using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
// create device test
TEST_P(NeuralnetworksHidlTest, CreateDevice) {}
@@ -123,4 +127,139 @@
});
EXPECT_TRUE(ret.isOk());
}
+
+// detect cycle
+TEST_P(NeuralnetworksHidlTest, CycleTest) {
+ // opnd0 = TENSOR_FLOAT32 // model input
+ // opnd1 = TENSOR_FLOAT32 // model input
+ // opnd2 = INT32 // model input
+ // opnd3 = ADD(opnd0, opnd4, opnd2)
+ // opnd4 = ADD(opnd1, opnd3, opnd2)
+ // opnd5 = ADD(opnd4, opnd0, opnd2) // model output
+ //
+ // +-----+
+ // | |
+ // v |
+ // 3 = ADD(0, 4, 2) |
+ // | |
+ // +----------+ |
+ // | |
+ // v |
+ // 4 = ADD(1, 3, 2) |
+ // | |
+ // +----------------+
+ // |
+ // |
+ // +-------+
+ // |
+ // v
+ // 5 = ADD(4, 0, 2)
+
+ const std::vector<Operand> operands = {
+ {
+ // operands[0]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 2,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[1]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[2]
+ .type = OperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 3,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[3]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[4]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 2,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[5]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 0,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::MODEL_OUTPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ };
+
+ const std::vector<Operation> operations = {
+ {.type = OperationType::ADD, .inputs = {0, 4, 2}, .outputs = {3}},
+ {.type = OperationType::ADD, .inputs = {1, 3, 2}, .outputs = {4}},
+ {.type = OperationType::ADD, .inputs = {4, 0, 2}, .outputs = {5}},
+ };
+
+ const Model model = {
+ .operands = operands,
+ .operations = operations,
+ .inputIndexes = {0, 1, 2},
+ .outputIndexes = {5},
+ .operandValues = {},
+ .pools = {},
+ };
+
+ // ensure that getSupportedOperations_1_2() checks model validity
+ ErrorStatus supportedOpsErrorStatus = ErrorStatus::GENERAL_FAILURE;
+ Return<void> supportedOpsReturn = kDevice->getSupportedOperations_1_2(
+ model, [&model, &supportedOpsErrorStatus](ErrorStatus status,
+ const hidl_vec<bool>& supported) {
+ supportedOpsErrorStatus = status;
+ if (status == ErrorStatus::NONE) {
+ ASSERT_EQ(supported.size(), model.operations.size());
+ }
+ });
+ ASSERT_TRUE(supportedOpsReturn.isOk());
+ ASSERT_EQ(supportedOpsErrorStatus, ErrorStatus::INVALID_ARGUMENT);
+
+ // ensure that prepareModel_1_2() checks model validity
+ sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback;
+ Return<ErrorStatus> prepareLaunchReturn = kDevice->prepareModel_1_2(
+ model, ExecutionPreference::FAST_SINGLE_ANSWER, hidl_vec<hidl_handle>(),
+ hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
+ ASSERT_TRUE(prepareLaunchReturn.isOk());
+ // Note that preparation can fail for reasons other than an
+ // invalid model (invalid model should result in
+ // INVALID_ARGUMENT) -- for example, perhaps not all
+ // operations are supported, or perhaps the device hit some
+ // kind of capacity limit.
+ EXPECT_NE(prepareLaunchReturn, ErrorStatus::NONE);
+ EXPECT_NE(preparedModelCallback->getStatus(), ErrorStatus::NONE);
+ EXPECT_EQ(preparedModelCallback->getPreparedModel(), nullptr);
+}
+
} // namespace android::hardware::neuralnetworks::V1_2::vts::functional
diff --git a/neuralnetworks/1.2/vts/functional/Utils.cpp b/neuralnetworks/1.2/vts/functional/Utils.cpp
new file mode 100644
index 0000000..cc654f2
--- /dev/null
+++ b/neuralnetworks/1.2/vts/functional/Utils.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+
+#include <functional>
+#include <numeric>
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+
+uint32_t sizeOfData(V1_2::OperandType type) {
+ switch (type) {
+ case V1_2::OperandType::FLOAT32:
+ case V1_2::OperandType::INT32:
+ case V1_2::OperandType::UINT32:
+ case V1_2::OperandType::TENSOR_FLOAT32:
+ case V1_2::OperandType::TENSOR_INT32:
+ return 4;
+ case V1_2::OperandType::TENSOR_QUANT16_SYMM:
+ case V1_2::OperandType::TENSOR_FLOAT16:
+ case V1_2::OperandType::FLOAT16:
+ case V1_2::OperandType::TENSOR_QUANT16_ASYMM:
+ return 2;
+ case V1_2::OperandType::TENSOR_QUANT8_ASYMM:
+ case V1_2::OperandType::BOOL:
+ case V1_2::OperandType::TENSOR_BOOL8:
+ case V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+ case V1_2::OperandType::TENSOR_QUANT8_SYMM:
+ return 1;
+ default:
+ CHECK(false) << "Invalid OperandType " << static_cast<uint32_t>(type);
+ return 0;
+ }
+}
+
+static bool isTensor(V1_2::OperandType type) {
+ switch (type) {
+ case V1_2::OperandType::FLOAT32:
+ case V1_2::OperandType::INT32:
+ case V1_2::OperandType::UINT32:
+ case V1_2::OperandType::FLOAT16:
+ case V1_2::OperandType::BOOL:
+ return false;
+ case V1_2::OperandType::TENSOR_FLOAT32:
+ case V1_2::OperandType::TENSOR_INT32:
+ case V1_2::OperandType::TENSOR_QUANT16_SYMM:
+ case V1_2::OperandType::TENSOR_FLOAT16:
+ case V1_2::OperandType::TENSOR_QUANT16_ASYMM:
+ case V1_2::OperandType::TENSOR_QUANT8_ASYMM:
+ case V1_2::OperandType::TENSOR_BOOL8:
+ case V1_2::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+ case V1_2::OperandType::TENSOR_QUANT8_SYMM:
+ return true;
+ default:
+ CHECK(false) << "Invalid OperandType " << static_cast<uint32_t>(type);
+ return false;
+ }
+}
+
+uint32_t sizeOfData(const V1_2::Operand& operand) {
+ const uint32_t dataSize = sizeOfData(operand.type);
+ if (isTensor(operand.type) && operand.dimensions.size() == 0) return 0;
+ return std::accumulate(operand.dimensions.begin(), operand.dimensions.end(), dataSize,
+ std::multiplies<>{});
+}
+
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
diff --git a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
index 7451f09..3375602 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
@@ -16,14 +16,21 @@
#define LOG_TAG "neuralnetworks_hidl_hal_test"
+#include <android/hardware/neuralnetworks/1.1/types.h>
#include "1.0/Utils.h"
#include "1.2/Callbacks.h"
+#include "1.2/Utils.h"
#include "GeneratedTestHarness.h"
#include "VtsHalNeuralnetworks.h"
+#include <optional>
+#include <type_traits>
+#include <utility>
+
namespace android::hardware::neuralnetworks::V1_2::vts::functional {
using implementation::PreparedModelCallback;
+using V1_0::DataLocation;
using V1_0::ErrorStatus;
using V1_0::OperandLifeTime;
using V1_1::ExecutionPreference;
@@ -105,6 +112,250 @@
return index;
}
+// If we introduce a CONSTANT_COPY for an operand of size operandSize,
+// how much will this increase the size of the model? This assumes
+// that we can (re)use all of model.operandValues for the operand
+// value.
+static size_t constantCopyExtraSize(const Model& model, size_t operandSize) {
+ const size_t operandValuesSize = model.operandValues.size();
+ return (operandValuesSize < operandSize) ? (operandSize - operandValuesSize) : 0;
+}
+
+// Highly specialized utility routine for converting an operand to
+// CONSTANT_COPY lifetime.
+//
+// Expects that:
+// - operand has a known size
+// - operand->lifetime has already been set to CONSTANT_COPY
+// - operand->location has been zeroed out
+//
+// Does the following:
+// - initializes operand->location to point to the beginning of model->operandValues
+// - resizes model->operandValues (if necessary) to be large enough for the operand
+// value, padding it with zeroes on the end
+//
+// Potential problem:
+// By changing the operand to CONSTANT_COPY lifetime, this function is effectively initializing the
+// operand with unspecified (but deterministic) data. This means that the model may be invalidated
+// in two ways: not only is the lifetime of CONSTANT_COPY invalid, but the operand's value in the
+// graph may also be invalid (e.g., if the operand is used as an activation code and has an invalid
+// value). For now, this should be fine because it just means we're not testing what we think we're
+// testing in certain cases; but we can handwave this and assume we're probabilistically likely to
+// exercise the validation code over the span of the entire test set and operand space.
+//
+// Aborts if the specified operand type is an extension type or OEM type.
+static void becomeConstantCopy(Model* model, Operand* operand) {
+ // sizeOfData will abort if the specified type is an extension type or OEM type.
+ const size_t sizeOfOperand = sizeOfData(*operand);
+ EXPECT_NE(sizeOfOperand, size_t(0));
+ operand->location.poolIndex = 0;
+ operand->location.offset = 0;
+ operand->location.length = sizeOfOperand;
+ if (model->operandValues.size() < sizeOfOperand) {
+ model->operandValues.resize(sizeOfOperand);
+ }
+}
+
+// The sizeForBinder() functions estimate the size of the
+// representation of a value when sent to binder. It's probably a bit
+// of an under-estimate, because we don't know the size of the
+// metadata in the binder format (e.g., representation of the size of
+// a vector); but at least it adds up "big" things like vector
+// contents. However, it doesn't treat inter-field or end-of-struct
+// padding in a methodical way -- there's no attempt to be consistent
+// in whether or not padding in the native (C++) representation
+// contributes to the estimated size for the binder representation;
+// and there's no attempt to understand what padding (if any) is
+// needed in the binder representation.
+//
+// This assumes that non-metadata uses a fixed length encoding (e.g.,
+// a uint32_t is always encoded in sizeof(uint32_t) bytes, rather than
+// using an encoding whose length is related to the magnitude of the
+// encoded value).
+
+template <typename Type>
+static size_t sizeForBinder(const Type& val) {
+ static_assert(std::is_trivially_copyable_v<std::remove_reference_t<Type>>,
+ "expected a trivially copyable type");
+ return sizeof(val);
+}
+
+template <typename Type>
+static size_t sizeForBinder(const hidl_vec<Type>& vec) {
+ return std::accumulate(vec.begin(), vec.end(), 0,
+ [](size_t acc, const Type& x) { return acc + sizeForBinder(x); });
+}
+
+template <>
+size_t sizeForBinder(const SymmPerChannelQuantParams& symmPerChannelQuantParams) {
+ size_t size = 0;
+
+ size += sizeForBinder(symmPerChannelQuantParams.scales);
+ size += sizeForBinder(symmPerChannelQuantParams.channelDim);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const Operand::ExtraParams& extraParams) {
+ using Discriminator = Operand::ExtraParams::hidl_discriminator;
+ switch (extraParams.getDiscriminator()) {
+ case Discriminator::none:
+ return 0;
+ case Discriminator::channelQuant:
+ return sizeForBinder(extraParams.channelQuant());
+ case Discriminator::extension:
+ return sizeForBinder(extraParams.extension());
+ }
+ LOG(FATAL) << "Unrecognized extraParams enum: "
+ << static_cast<int>(extraParams.getDiscriminator());
+ return 0;
+}
+
+template <>
+size_t sizeForBinder(const Operand& operand) {
+ size_t size = 0;
+
+ size += sizeForBinder(operand.type);
+ size += sizeForBinder(operand.dimensions);
+ size += sizeForBinder(operand.numberOfConsumers);
+ size += sizeForBinder(operand.scale);
+ size += sizeForBinder(operand.zeroPoint);
+ size += sizeForBinder(operand.lifetime);
+ size += sizeForBinder(operand.location);
+ size += sizeForBinder(operand.extraParams);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const Operation& operation) {
+ size_t size = 0;
+
+ size += sizeForBinder(operation.type);
+ size += sizeForBinder(operation.inputs);
+ size += sizeForBinder(operation.outputs);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const hidl_string& name) {
+ return name.size();
+}
+
+template <>
+size_t sizeForBinder(const hidl_memory& memory) {
+ // This is just a guess.
+
+ size_t size = 0;
+
+ if (const native_handle_t* handle = memory.handle()) {
+ size += sizeof(*handle);
+ size += sizeof(handle->data[0] * (handle->numFds + handle->numInts));
+ }
+ size += sizeForBinder(memory.name());
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const Model::ExtensionNameAndPrefix& extensionNameToPrefix) {
+ size_t size = 0;
+
+ size += sizeForBinder(extensionNameToPrefix.name);
+ size += sizeForBinder(extensionNameToPrefix.prefix);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const Model& model) {
+ size_t size = 0;
+
+ size += sizeForBinder(model.operands);
+ size += sizeForBinder(model.operations);
+ size += sizeForBinder(model.inputIndexes);
+ size += sizeForBinder(model.outputIndexes);
+ size += sizeForBinder(model.operandValues);
+ size += sizeForBinder(model.pools);
+ size += sizeForBinder(model.relaxComputationFloat32toFloat16);
+ size += sizeForBinder(model.extensionNameToPrefix);
+
+ return size;
+}
+
+// https://developer.android.com/reference/android/os/TransactionTooLargeException.html
+//
+// "The Binder transaction buffer has a limited fixed size,
+// currently 1Mb, which is shared by all transactions in progress
+// for the process."
+//
+// Will our representation fit under this limit? There are two complications:
+// - Our representation size is just approximate (see sizeForBinder()).
+// - This object may not be the only occupant of the Binder transaction buffer.
+// So we'll be very conservative: We want the representation size to be no
+// larger than half the transaction buffer size.
+//
+// If our representation grows large enough that it still fits within
+// the transaction buffer but combined with other transactions may
+// exceed the buffer size, then we may see intermittent HAL transport
+// errors.
+static bool exceedsBinderSizeLimit(size_t representationSize) {
+ // Instead of using this fixed buffer size, we might instead be able to use
+ // ProcessState::self()->getMmapSize(). However, this has a potential
+ // problem: The binder/mmap size of the current process does not necessarily
+ // indicate the binder/mmap size of the service (i.e., the other process).
+ // The only way it would be a good indication is if both the current process
+ // and the service use the default size.
+ static const size_t kHalfBufferSize = 1024 * 1024 / 2;
+
+ return representationSize > kHalfBufferSize;
+}
+
+///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
+
+static void mutateExecutionOrderTest(const sp<IDevice>& device, const Model& model) {
+ for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ const Operation& operationObj = model.operations[operation];
+ for (uint32_t input : operationObj.inputs) {
+ if (model.operands[input].lifetime == OperandLifeTime::TEMPORARY_VARIABLE ||
+ model.operands[input].lifetime == OperandLifeTime::MODEL_OUTPUT) {
+ // This operation reads an operand written by some
+ // other operation. Move this operation to the
+ // beginning of the sequence, ensuring that it reads
+ // the operand before that operand is written, thereby
+ // violating execution order rules.
+ const std::string message = "mutateExecutionOrderTest: operation " +
+ std::to_string(operation) + " is a reader";
+ validate(device, message, model, [operation](Model* model, ExecutionPreference*) {
+ auto& operations = model->operations;
+ std::rotate(operations.begin(), operations.begin() + operation,
+ operations.begin() + operation + 1);
+ });
+ break; // only need to do this once per operation
+ }
+ }
+ for (uint32_t output : operationObj.outputs) {
+ if (model.operands[output].numberOfConsumers > 0) {
+ // This operation writes an operand read by some other
+ // operation. Move this operation to the end of the
+ // sequence, ensuring that it writes the operand after
+ // that operand is read, thereby violating execution
+ // order rules.
+ const std::string message = "mutateExecutionOrderTest: operation " +
+ std::to_string(operation) + " is a writer";
+ validate(device, message, model, [operation](Model* model, ExecutionPreference*) {
+ auto& operations = model->operations;
+ std::rotate(operations.begin() + operation, operations.begin() + operation + 1,
+ operations.end());
+ });
+ break; // only need to do this once per operation
+ }
+ }
+ }
+}
+
///////////////////////// VALIDATE MODEL OPERAND TYPE /////////////////////////
static const uint32_t invalidOperandTypes[] = {
@@ -251,9 +502,239 @@
}
}
+///////////////////////// VALIDATE OPERAND LIFETIME /////////////////////////////////////////////
+
+static std::vector<OperandLifeTime> getInvalidLifeTimes(const Model& model, size_t modelSize,
+ const Operand& operand) {
+ // TODO: Support OperandLifeTime::CONSTANT_REFERENCE as an invalid lifetime
+ // TODO: Support OperandLifeTime::NO_VALUE as an invalid lifetime
+
+ // Ways to get an invalid lifetime:
+ // - change whether a lifetime means an operand should have a writer
+ std::vector<OperandLifeTime> ret;
+ switch (operand.lifetime) {
+ case OperandLifeTime::MODEL_OUTPUT:
+ case OperandLifeTime::TEMPORARY_VARIABLE:
+ ret = {
+ OperandLifeTime::MODEL_INPUT,
+ OperandLifeTime::CONSTANT_COPY,
+ };
+ break;
+ case OperandLifeTime::CONSTANT_COPY:
+ case OperandLifeTime::CONSTANT_REFERENCE:
+ case OperandLifeTime::MODEL_INPUT:
+ ret = {
+ OperandLifeTime::TEMPORARY_VARIABLE,
+ OperandLifeTime::MODEL_OUTPUT,
+ };
+ break;
+ case OperandLifeTime::NO_VALUE:
+ // Not enough information to know whether
+ // TEMPORARY_VARIABLE or CONSTANT_COPY would be invalid --
+ // is this operand written (then CONSTANT_COPY would be
+ // invalid) or not (then TEMPORARY_VARIABLE would be
+ // invalid)?
+ break;
+ default:
+ ADD_FAILURE();
+ break;
+ }
+
+ const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
+ if (!operandSize ||
+ exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
+ // Unknown size or too-large size
+ ret.erase(std::remove(ret.begin(), ret.end(), OperandLifeTime::CONSTANT_COPY), ret.end());
+ }
+
+ return ret;
+}
+
+static void mutateOperandLifeTimeTest(const sp<IDevice>& device, const Model& model) {
+ const size_t modelSize = sizeForBinder(model);
+ for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ const std::vector<OperandLifeTime> invalidLifeTimes =
+ getInvalidLifeTimes(model, modelSize, model.operands[operand]);
+ for (OperandLifeTime invalidLifeTime : invalidLifeTimes) {
+ const std::string message = "mutateOperandLifetimeTest: operand " +
+ std::to_string(operand) + " has lifetime " +
+ toString(invalidLifeTime) + " instead of lifetime " +
+ toString(model.operands[operand].lifetime);
+ validate(device, message, model,
+ [operand, invalidLifeTime](Model* model, ExecutionPreference*) {
+ static const DataLocation kZeroDataLocation = {};
+ Operand& operandObj = model->operands[operand];
+ switch (operandObj.lifetime) {
+ case OperandLifeTime::MODEL_INPUT: {
+ hidl_vec_remove(&model->inputIndexes, uint32_t(operand));
+ break;
+ }
+ case OperandLifeTime::MODEL_OUTPUT: {
+ hidl_vec_remove(&model->outputIndexes, uint32_t(operand));
+ break;
+ }
+ default:
+ break;
+ }
+ operandObj.lifetime = invalidLifeTime;
+ operandObj.location = kZeroDataLocation;
+ switch (invalidLifeTime) {
+ case OperandLifeTime::CONSTANT_COPY: {
+ becomeConstantCopy(model, &operandObj);
+ break;
+ }
+ case OperandLifeTime::MODEL_INPUT:
+ hidl_vec_push_back(&model->inputIndexes, uint32_t(operand));
+ break;
+ case OperandLifeTime::MODEL_OUTPUT:
+ hidl_vec_push_back(&model->outputIndexes, uint32_t(operand));
+ break;
+ default:
+ break;
+ }
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND INPUT-or-OUTPUT //////////////////////////////////////
+
+static std::optional<OperandLifeTime> getInputOutputLifeTime(const Model& model, size_t modelSize,
+ const Operand& operand) {
+ // Ways to get an invalid lifetime (with respect to model inputIndexes and outputIndexes):
+ // - change whether a lifetime means an operand is a model input, a model output, or neither
+ // - preserve whether or not a lifetime means an operand should have a writer
+ switch (operand.lifetime) {
+ case OperandLifeTime::CONSTANT_COPY:
+ case OperandLifeTime::CONSTANT_REFERENCE:
+ return OperandLifeTime::MODEL_INPUT;
+ case OperandLifeTime::MODEL_INPUT: {
+ const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
+ if (!operandSize ||
+ exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
+ // Unknown size or too-large size
+ break;
+ }
+ return OperandLifeTime::CONSTANT_COPY;
+ }
+ case OperandLifeTime::MODEL_OUTPUT:
+ return OperandLifeTime::TEMPORARY_VARIABLE;
+ case OperandLifeTime::TEMPORARY_VARIABLE:
+ return OperandLifeTime::MODEL_OUTPUT;
+ case OperandLifeTime::NO_VALUE:
+ // Not enough information to know whether
+ // TEMPORARY_VARIABLE or CONSTANT_COPY would be an
+ // appropriate choice -- is this operand written (then
+ // TEMPORARY_VARIABLE would be appropriate) or not (then
+ // CONSTANT_COPY would be appropriate)?
+ break;
+ default:
+ ADD_FAILURE();
+ break;
+ }
+
+ return std::nullopt;
+}
+
+static void mutateOperandInputOutputTest(const sp<IDevice>& device, const Model& model) {
+ const size_t modelSize = sizeForBinder(model);
+ for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ const std::optional<OperandLifeTime> changedLifeTime =
+ getInputOutputLifeTime(model, modelSize, model.operands[operand]);
+ if (changedLifeTime) {
+ const std::string message = "mutateOperandInputOutputTest: operand " +
+ std::to_string(operand) + " has lifetime " +
+ toString(*changedLifeTime) + " instead of lifetime " +
+ toString(model.operands[operand].lifetime);
+ validate(device, message, model,
+ [operand, changedLifeTime](Model* model, ExecutionPreference*) {
+ static const DataLocation kZeroDataLocation = {};
+ Operand& operandObj = model->operands[operand];
+ operandObj.lifetime = *changedLifeTime;
+ operandObj.location = kZeroDataLocation;
+ if (*changedLifeTime == OperandLifeTime::CONSTANT_COPY) {
+ becomeConstantCopy(model, &operandObj);
+ }
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND NUMBER OF CONSUMERS //////////////////////////////////
+
+static std::vector<uint32_t> getInvalidNumberOfConsumers(uint32_t numberOfConsumers) {
+ if (numberOfConsumers == 0) {
+ return {1};
+ } else {
+ return {numberOfConsumers - 1, numberOfConsumers + 1};
+ }
+}
+
+static void mutateOperandNumberOfConsumersTest(const sp<IDevice>& device, const Model& model) {
+ for (size_t operand = 0; operand < model.operands.size(); ++operand) {
+ const std::vector<uint32_t> invalidNumberOfConsumersVec =
+ getInvalidNumberOfConsumers(model.operands[operand].numberOfConsumers);
+ for (uint32_t invalidNumberOfConsumers : invalidNumberOfConsumersVec) {
+ const std::string message =
+ "mutateOperandNumberOfConsumersTest: operand " + std::to_string(operand) +
+ " numberOfConsumers = " + std::to_string(invalidNumberOfConsumers);
+ validate(device, message, model,
+ [operand, invalidNumberOfConsumers](Model* model, ExecutionPreference*) {
+ model->operands[operand].numberOfConsumers = invalidNumberOfConsumers;
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND NUMBER OF WRITERS ////////////////////////////////////
+
+static void mutateOperandAddWriterTest(const sp<IDevice>& device, const Model& model) {
+ for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ for (size_t badOutputNum = 0; badOutputNum < model.operations[operation].outputs.size();
+ ++badOutputNum) {
+ const uint32_t outputOperandIndex = model.operations[operation].outputs[badOutputNum];
+ const std::string message = "mutateOperandAddWriterTest: operation " +
+ std::to_string(operation) + " writes to " +
+ std::to_string(outputOperandIndex);
+ // We'll insert a copy of the operation, all of whose
+ // OTHER output operands are newly-created -- i.e.,
+ // there'll only be a duplicate write of ONE of that
+ // operation's output operands.
+ validate(device, message, model,
+ [operation, badOutputNum](Model* model, ExecutionPreference*) {
+ Operation newOperation = model->operations[operation];
+ for (uint32_t input : newOperation.inputs) {
+ ++model->operands[input].numberOfConsumers;
+ }
+ for (size_t outputNum = 0; outputNum < newOperation.outputs.size();
+ ++outputNum) {
+ if (outputNum == badOutputNum) continue;
+
+ Operand operandValue =
+ model->operands[newOperation.outputs[outputNum]];
+ operandValue.numberOfConsumers = 0;
+ if (operandValue.lifetime == OperandLifeTime::MODEL_OUTPUT) {
+ operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
+ } else {
+ ASSERT_EQ(operandValue.lifetime,
+ OperandLifeTime::TEMPORARY_VARIABLE);
+ }
+ newOperation.outputs[outputNum] =
+ hidl_vec_push_back(&model->operands, operandValue);
+ }
+ // Where do we insert the extra writer (a new
+ // operation)? It has to be later than all the
+ // writers of its inputs. The easiest thing to do
+ // is to insert it at the end of the operation
+ // sequence.
+ hidl_vec_push_back(&model->operations, newOperation);
+ });
+ }
+ }
+}
+
///////////////////////// VALIDATE EXTRA ??? /////////////////////////
-// TODO: Operand::lifetime
// TODO: Operand::location
///////////////////////// VALIDATE OPERATION OPERAND TYPE /////////////////////////
@@ -461,6 +942,37 @@
}
}
+///////////////////////// VALIDATE MODEL OPERANDS WRITTEN ///////////////////////////////////////
+
+static void mutateOperationRemoveWriteTest(const sp<IDevice>& device, const Model& model) {
+ for (size_t operation = 0; operation < model.operations.size(); ++operation) {
+ for (size_t outputNum = 0; outputNum < model.operations[operation].outputs.size();
+ ++outputNum) {
+ const uint32_t outputOperandIndex = model.operations[operation].outputs[outputNum];
+ if (model.operands[outputOperandIndex].numberOfConsumers > 0) {
+ const std::string message = "mutateOperationRemoveWriteTest: operation " +
+ std::to_string(operation) + " writes to " +
+ std::to_string(outputOperandIndex);
+ validate(device, message, model,
+ [operation, outputNum](Model* model, ExecutionPreference*) {
+ uint32_t& outputOperandIndex =
+ model->operations[operation].outputs[outputNum];
+ Operand operandValue = model->operands[outputOperandIndex];
+ operandValue.numberOfConsumers = 0;
+ if (operandValue.lifetime == OperandLifeTime::MODEL_OUTPUT) {
+ operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
+ } else {
+ ASSERT_EQ(operandValue.lifetime,
+ OperandLifeTime::TEMPORARY_VARIABLE);
+ }
+ outputOperandIndex =
+ hidl_vec_push_back(&model->operands, operandValue);
+ });
+ }
+ }
+ }
+}
+
///////////////////////// REMOVE OPERAND FROM EVERYTHING /////////////////////////
static void removeValueAndDecrementGreaterValues(hidl_vec<uint32_t>* vec, uint32_t value) {
@@ -711,14 +1223,20 @@
////////////////////////// ENTRY POINT //////////////////////////////
void validateModel(const sp<IDevice>& device, const Model& model) {
+ mutateExecutionOrderTest(device, model);
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
mutateOperandZeroPointTest(device, model);
+ mutateOperandLifeTimeTest(device, model);
+ mutateOperandInputOutputTest(device, model);
+ mutateOperandNumberOfConsumersTest(device, model);
+ mutateOperandAddWriterTest(device, model);
mutateOperationOperandTypeTest(device, model);
mutateOperationTypeTest(device, model);
mutateOperationInputOperandIndexTest(device, model);
mutateOperationOutputOperandIndexTest(device, model);
+ mutateOperationRemoveWriteTest(device, model);
removeOperandTest(device, model);
removeOperationTest(device, model);
removeOperationInputTest(device, model);
diff --git a/neuralnetworks/1.2/vts/functional/include/1.2/Utils.h b/neuralnetworks/1.2/vts/functional/include/1.2/Utils.h
new file mode 100644
index 0000000..61a8d74
--- /dev/null
+++ b/neuralnetworks/1.2/vts/functional/include/1.2/Utils.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_NEURALNETWORKS_V1_2_UTILS_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_V1_2_UTILS_H
+
+#include <android/hardware/neuralnetworks/1.2/types.h>
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+
+// Returns the amount of space needed to store a value of the specified type.
+//
+// Aborts if the specified type is an extension type or OEM type.
+uint32_t sizeOfData(V1_2::OperandType type);
+
+// Returns the amount of space needed to store a value of the dimensions and
+// type of this operand. For a non-extension, non-OEM tensor with unspecified
+// rank or at least one unspecified dimension, returns zero.
+//
+// Aborts if the specified type is an extension type or OEM type.
+uint32_t sizeOfData(const V1_2::Operand& operand);
+
+} // namespace neuralnetworks
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_NEURALNETWORKS_V1_2_UTILS_H
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index 2c1be0b..771fc54 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -54,7 +54,7 @@
],
static_libs: [
"VtsHalNeuralNetworksV1_0_utils",
- "VtsHalNeuralNetworksV1_2Callbacks",
+ "VtsHalNeuralNetworksV1_2_utils",
"VtsHalNeuralNetworksV1_3_utils",
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
diff --git a/neuralnetworks/1.3/vts/functional/BasicTests.cpp b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
index 1c25369..6fcfc34 100644
--- a/neuralnetworks/1.3/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/BasicTests.cpp
@@ -20,11 +20,14 @@
namespace android::hardware::neuralnetworks::V1_3::vts::functional {
+using implementation::PreparedModelCallback;
using V1_0::DeviceStatus;
using V1_0::PerformanceInfo;
+using V1_1::ExecutionPreference;
using V1_2::Constant;
using V1_2::DeviceType;
using V1_2::Extension;
+using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
// create device test
TEST_P(NeuralnetworksHidlTest, CreateDevice) {}
@@ -65,4 +68,143 @@
});
EXPECT_TRUE(ret.isOk());
}
+
+// detect cycle
+TEST_P(NeuralnetworksHidlTest, CycleTest) {
+ // opnd0 = TENSOR_FLOAT32 // model input
+ // opnd1 = TENSOR_FLOAT32 // model input
+ // opnd2 = INT32 // model input
+ // opnd3 = ADD(opnd0, opnd4, opnd2)
+ // opnd4 = ADD(opnd1, opnd3, opnd2)
+ // opnd5 = ADD(opnd4, opnd0, opnd2) // model output
+ //
+ // +-----+
+ // | |
+ // v |
+ // 3 = ADD(0, 4, 2) |
+ // | |
+ // +----------+ |
+ // | |
+ // v |
+ // 4 = ADD(1, 3, 2) |
+ // | |
+ // +----------------+
+ // |
+ // |
+ // +-------+
+ // |
+ // v
+ // 5 = ADD(4, 0, 2)
+
+ const std::vector<Operand> operands = {
+ {
+ // operands[0]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 2,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::SUBGRAPH_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[1]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::SUBGRAPH_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[2]
+ .type = OperandType::INT32,
+ .dimensions = {},
+ .numberOfConsumers = 3,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::SUBGRAPH_INPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[3]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[4]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 2,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ {
+ // operands[5]
+ .type = OperandType::TENSOR_FLOAT32,
+ .dimensions = {1},
+ .numberOfConsumers = 0,
+ .scale = 0.0f,
+ .zeroPoint = 0,
+ .lifetime = OperandLifeTime::SUBGRAPH_OUTPUT,
+ .location = {.poolIndex = 0, .offset = 0, .length = 0},
+ },
+ };
+
+ const std::vector<Operation> operations = {
+ {.type = OperationType::ADD, .inputs = {0, 4, 2}, .outputs = {3}},
+ {.type = OperationType::ADD, .inputs = {1, 3, 2}, .outputs = {4}},
+ {.type = OperationType::ADD, .inputs = {4, 0, 2}, .outputs = {5}},
+ };
+
+ Subgraph subgraph = {
+ .operands = operands,
+ .operations = operations,
+ .inputIndexes = {0, 1, 2},
+ .outputIndexes = {5},
+ };
+ const Model model = {
+ .main = std::move(subgraph),
+ .referenced = {},
+ .operandValues = {},
+ .pools = {},
+ };
+
+ // ensure that getSupportedOperations_1_2() checks model validity
+ ErrorStatus supportedOpsErrorStatus = ErrorStatus::GENERAL_FAILURE;
+ Return<void> supportedOpsReturn = kDevice->getSupportedOperations_1_3(
+ model, [&model, &supportedOpsErrorStatus](ErrorStatus status,
+ const hidl_vec<bool>& supported) {
+ supportedOpsErrorStatus = status;
+ if (status == ErrorStatus::NONE) {
+ ASSERT_EQ(supported.size(), model.main.operations.size());
+ }
+ });
+ ASSERT_TRUE(supportedOpsReturn.isOk());
+ ASSERT_EQ(supportedOpsErrorStatus, ErrorStatus::INVALID_ARGUMENT);
+
+ // ensure that prepareModel_1_3() checks model validity
+ sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback;
+ Return<ErrorStatus> prepareLaunchReturn = kDevice->prepareModel_1_3(
+ model, ExecutionPreference::FAST_SINGLE_ANSWER, Priority::MEDIUM, {},
+ hidl_vec<hidl_handle>(), hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
+ ASSERT_TRUE(prepareLaunchReturn.isOk());
+ // Note that preparation can fail for reasons other than an
+ // invalid model (invalid model should result in
+ // INVALID_ARGUMENT) -- for example, perhaps not all
+ // operations are supported, or perhaps the device hit some
+ // kind of capacity limit.
+ EXPECT_NE(prepareLaunchReturn, ErrorStatus::NONE);
+ EXPECT_NE(preparedModelCallback->getStatus(), ErrorStatus::NONE);
+ EXPECT_EQ(preparedModelCallback->getPreparedModel(), nullptr);
+}
+
} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/Utils.cpp b/neuralnetworks/1.3/vts/functional/Utils.cpp
index 23e2af8..c460e11 100644
--- a/neuralnetworks/1.3/vts/functional/Utils.cpp
+++ b/neuralnetworks/1.3/vts/functional/Utils.cpp
@@ -17,11 +17,78 @@
#include "1.3/Utils.h"
#include <iostream>
+#include <numeric>
+#include "android-base/logging.h"
+#include "android/hardware/neuralnetworks/1.3/types.h"
-namespace android::hardware::neuralnetworks::V1_3 {
+namespace android::hardware::neuralnetworks {
+
+uint32_t sizeOfData(V1_3::OperandType type) {
+ switch (type) {
+ case V1_3::OperandType::FLOAT32:
+ case V1_3::OperandType::INT32:
+ case V1_3::OperandType::UINT32:
+ case V1_3::OperandType::TENSOR_FLOAT32:
+ case V1_3::OperandType::TENSOR_INT32:
+ return 4;
+ case V1_3::OperandType::TENSOR_QUANT16_SYMM:
+ case V1_3::OperandType::TENSOR_FLOAT16:
+ case V1_3::OperandType::FLOAT16:
+ case V1_3::OperandType::TENSOR_QUANT16_ASYMM:
+ return 2;
+ case V1_3::OperandType::TENSOR_QUANT8_ASYMM:
+ case V1_3::OperandType::BOOL:
+ case V1_3::OperandType::TENSOR_BOOL8:
+ case V1_3::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+ case V1_3::OperandType::TENSOR_QUANT8_SYMM:
+ case V1_3::OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
+ return 1;
+ case V1_3::OperandType::SUBGRAPH:
+ return 0;
+ default:
+ CHECK(false) << "Invalid OperandType " << static_cast<uint32_t>(type);
+ return 0;
+ }
+}
+
+static bool isTensor(V1_3::OperandType type) {
+ switch (type) {
+ case V1_3::OperandType::FLOAT32:
+ case V1_3::OperandType::INT32:
+ case V1_3::OperandType::UINT32:
+ case V1_3::OperandType::FLOAT16:
+ case V1_3::OperandType::BOOL:
+ case V1_3::OperandType::SUBGRAPH:
+ return false;
+ case V1_3::OperandType::TENSOR_FLOAT32:
+ case V1_3::OperandType::TENSOR_INT32:
+ case V1_3::OperandType::TENSOR_QUANT16_SYMM:
+ case V1_3::OperandType::TENSOR_FLOAT16:
+ case V1_3::OperandType::TENSOR_QUANT16_ASYMM:
+ case V1_3::OperandType::TENSOR_QUANT8_ASYMM:
+ case V1_3::OperandType::TENSOR_BOOL8:
+ case V1_3::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+ case V1_3::OperandType::TENSOR_QUANT8_SYMM:
+ case V1_3::OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
+ return true;
+ default:
+ CHECK(false) << "Invalid OperandType " << static_cast<uint32_t>(type);
+ return false;
+ }
+}
+
+uint32_t sizeOfData(const V1_3::Operand& operand) {
+ const uint32_t dataSize = sizeOfData(operand.type);
+ if (isTensor(operand.type) && operand.dimensions.size() == 0) return 0;
+ return std::accumulate(operand.dimensions.begin(), operand.dimensions.end(), dataSize,
+ std::multiplies<>{});
+}
+
+namespace V1_3 {
::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
return os << toString(errorStatus);
}
-} // namespace android::hardware::neuralnetworks::V1_3
+} // namespace V1_3
+} // namespace android::hardware::neuralnetworks
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index e590fda..849ef7b 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -16,15 +16,22 @@
#define LOG_TAG "neuralnetworks_hidl_hal_test"
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
#include "1.0/Utils.h"
#include "1.3/Callbacks.h"
#include "1.3/Utils.h"
#include "GeneratedTestHarness.h"
#include "VtsHalNeuralnetworks.h"
+#include <optional>
+#include <type_traits>
+#include <utility>
+
namespace android::hardware::neuralnetworks::V1_3::vts::functional {
using implementation::PreparedModelCallback;
+using V1_0::DataLocation;
using V1_1::ExecutionPreference;
using V1_2::SymmPerChannelQuantParams;
using HidlToken =
@@ -112,6 +119,262 @@
return index;
}
+// If we introduce a CONSTANT_COPY for an operand of size operandSize,
+// how much will this increase the size of the model? This assumes
+// that we can (re)use all of model.operandValues for the operand
+// value.
+static size_t constantCopyExtraSize(const Model& model, size_t operandSize) {
+ const size_t operandValuesSize = model.operandValues.size();
+ return (operandValuesSize < operandSize) ? (operandSize - operandValuesSize) : 0;
+}
+
+// Highly specialized utility routine for converting an operand to
+// CONSTANT_COPY lifetime.
+//
+// Expects that:
+// - operand has a known size
+// - operand->lifetime has already been set to CONSTANT_COPY
+// - operand->location has been zeroed out
+//
+// Does the following:
+// - initializes operand->location to point to the beginning of model->operandValues
+// - resizes model->operandValues (if necessary) to be large enough for the operand
+// value, padding it with zeroes on the end
+//
+// Potential problem:
+// By changing the operand to CONSTANT_COPY lifetime, this function is effectively initializing the
+// operand with unspecified (but deterministic) data. This means that the model may be invalidated
+// in two ways: not only is the lifetime of CONSTANT_COPY invalid, but the operand's value in the
+// graph may also be invalid (e.g., if the operand is used as an activation code and has an invalid
+// value). For now, this should be fine because it just means we're not testing what we think we're
+// testing in certain cases; but we can handwave this and assume we're probabilistically likely to
+// exercise the validation code over the span of the entire test set and operand space.
+//
+// Aborts if the specified operand type is an extension type or OEM type.
+static void becomeConstantCopy(Model* model, Operand* operand) {
+ // sizeOfData will abort if the specified type is an extension type or OEM type.
+ const size_t sizeOfOperand = sizeOfData(*operand);
+ EXPECT_NE(sizeOfOperand, size_t(0));
+ operand->location.poolIndex = 0;
+ operand->location.offset = 0;
+ operand->location.length = sizeOfOperand;
+ if (model->operandValues.size() < sizeOfOperand) {
+ model->operandValues.resize(sizeOfOperand);
+ }
+}
+
+// The sizeForBinder() functions estimate the size of the
+// representation of a value when sent to binder. It's probably a bit
+// of an under-estimate, because we don't know the size of the
+// metadata in the binder format (e.g., representation of the size of
+// a vector); but at least it adds up "big" things like vector
+// contents. However, it doesn't treat inter-field or end-of-struct
+// padding in a methodical way -- there's no attempt to be consistent
+// in whether or not padding in the native (C++) representation
+// contributes to the estimated size for the binder representation;
+// and there's no attempt to understand what padding (if any) is
+// needed in the binder representation.
+//
+// This assumes that non-metadata uses a fixed length encoding (e.g.,
+// a uint32_t is always encoded in sizeof(uint32_t) bytes, rather than
+// using an encoding whose length is related to the magnitude of the
+// encoded value).
+
+template <typename Type>
+static size_t sizeForBinder(const Type& val) {
+ static_assert(std::is_trivially_copyable_v<std::remove_reference_t<Type>>,
+ "expected a trivially copyable type");
+ return sizeof(val);
+}
+
+template <typename Type>
+static size_t sizeForBinder(const hidl_vec<Type>& vec) {
+ return std::accumulate(vec.begin(), vec.end(), 0,
+ [](size_t acc, const Type& x) { return acc + sizeForBinder(x); });
+}
+
+template <>
+size_t sizeForBinder(const SymmPerChannelQuantParams& symmPerChannelQuantParams) {
+ size_t size = 0;
+
+ size += sizeForBinder(symmPerChannelQuantParams.scales);
+ size += sizeForBinder(symmPerChannelQuantParams.channelDim);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const V1_2::Operand::ExtraParams& extraParams) {
+ using Discriminator = V1_2::Operand::ExtraParams::hidl_discriminator;
+ switch (extraParams.getDiscriminator()) {
+ case Discriminator::none:
+ return 0;
+ case Discriminator::channelQuant:
+ return sizeForBinder(extraParams.channelQuant());
+ case Discriminator::extension:
+ return sizeForBinder(extraParams.extension());
+ }
+ LOG(FATAL) << "Unrecognized extraParams enum: "
+ << static_cast<int>(extraParams.getDiscriminator());
+ return 0;
+}
+
+template <>
+size_t sizeForBinder(const Operand& operand) {
+ size_t size = 0;
+
+ size += sizeForBinder(operand.type);
+ size += sizeForBinder(operand.dimensions);
+ size += sizeForBinder(operand.numberOfConsumers);
+ size += sizeForBinder(operand.scale);
+ size += sizeForBinder(operand.zeroPoint);
+ size += sizeForBinder(operand.lifetime);
+ size += sizeForBinder(operand.location);
+ size += sizeForBinder(operand.extraParams);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const Operation& operation) {
+ size_t size = 0;
+
+ size += sizeForBinder(operation.type);
+ size += sizeForBinder(operation.inputs);
+ size += sizeForBinder(operation.outputs);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const hidl_string& name) {
+ return name.size();
+}
+
+template <>
+size_t sizeForBinder(const hidl_memory& memory) {
+ // This is just a guess.
+
+ size_t size = 0;
+
+ if (const native_handle_t* handle = memory.handle()) {
+ size += sizeof(*handle);
+ size += sizeof(handle->data[0] * (handle->numFds + handle->numInts));
+ }
+ size += sizeForBinder(memory.name());
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const Subgraph& subgraph) {
+ size_t size = 0;
+
+ size += sizeForBinder(subgraph.operands);
+ size += sizeForBinder(subgraph.operations);
+ size += sizeForBinder(subgraph.inputIndexes);
+ size += sizeForBinder(subgraph.outputIndexes);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const V1_2::Model::ExtensionNameAndPrefix& extensionNameToPrefix) {
+ size_t size = 0;
+
+ size += sizeForBinder(extensionNameToPrefix.name);
+ size += sizeForBinder(extensionNameToPrefix.prefix);
+
+ return size;
+}
+
+template <>
+size_t sizeForBinder(const Model& model) {
+ size_t size = 0;
+
+ size += sizeForBinder(model.main);
+ size += sizeForBinder(model.referenced);
+ size += sizeForBinder(model.operandValues);
+ size += sizeForBinder(model.pools);
+ size += sizeForBinder(model.relaxComputationFloat32toFloat16);
+ size += sizeForBinder(model.extensionNameToPrefix);
+
+ return size;
+}
+
+// https://developer.android.com/reference/android/os/TransactionTooLargeException.html
+//
+// "The Binder transaction buffer has a limited fixed size,
+// currently 1Mb, which is shared by all transactions in progress
+// for the process."
+//
+// Will our representation fit under this limit? There are two complications:
+// - Our representation size is just approximate (see sizeForBinder()).
+// - This object may not be the only occupant of the Binder transaction buffer.
+// So we'll be very conservative: We want the representation size to be no
+// larger than half the transaction buffer size.
+//
+// If our representation grows large enough that it still fits within
+// the transaction buffer but combined with other transactions may
+// exceed the buffer size, then we may see intermittent HAL transport
+// errors.
+static bool exceedsBinderSizeLimit(size_t representationSize) {
+ // Instead of using this fixed buffer size, we might instead be able to use
+ // ProcessState::self()->getMmapSize(). However, this has a potential
+ // problem: The binder/mmap size of the current process does not necessarily
+ // indicate the binder/mmap size of the service (i.e., the other process).
+ // The only way it would be a good indication is if both the current process
+ // and the service use the default size.
+ static const size_t kHalfBufferSize = 1024 * 1024 / 2;
+
+ return representationSize > kHalfBufferSize;
+}
+
+///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
+
+static void mutateExecutionOrderTest(const sp<IDevice>& device, const Model& model) {
+ for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+ const Operation& operationObj = model.main.operations[operation];
+ for (uint32_t input : operationObj.inputs) {
+ if (model.main.operands[input].lifetime == OperandLifeTime::TEMPORARY_VARIABLE ||
+ model.main.operands[input].lifetime == OperandLifeTime::SUBGRAPH_OUTPUT) {
+ // This operation reads an operand written by some
+ // other operation. Move this operation to the
+ // beginning of the sequence, ensuring that it reads
+ // the operand before that operand is written, thereby
+ // violating execution order rules.
+ const std::string message = "mutateExecutionOrderTest: operation " +
+ std::to_string(operation) + " is a reader";
+ validate(device, message, model,
+ [operation](Model* model, ExecutionPreference*, Priority*) {
+ auto& operations = model->main.operations;
+ std::rotate(operations.begin(), operations.begin() + operation,
+ operations.begin() + operation + 1);
+ });
+ break; // only need to do this once per operation
+ }
+ }
+ for (uint32_t output : operationObj.outputs) {
+ if (model.main.operands[output].numberOfConsumers > 0) {
+ // This operation writes an operand read by some other
+ // operation. Move this operation to the end of the
+ // sequence, ensuring that it writes the operand after
+ // that operand is read, thereby violating execution
+ // order rules.
+ const std::string message = "mutateExecutionOrderTest: operation " +
+ std::to_string(operation) + " is a writer";
+ validate(device, message, model,
+ [operation](Model* model, ExecutionPreference*, Priority*) {
+ auto& operations = model->main.operations;
+ std::rotate(operations.begin() + operation,
+ operations.begin() + operation + 1, operations.end());
+ });
+ break; // only need to do this once per operation
+ }
+ }
+ }
+}
+
///////////////////////// VALIDATE MODEL OPERAND TYPE /////////////////////////
static const uint32_t invalidOperandTypes[] = {
@@ -261,9 +524,245 @@
}
}
+///////////////////////// VALIDATE OPERAND LIFETIME /////////////////////////////////////////////
+
+static std::vector<OperandLifeTime> getInvalidLifeTimes(const Model& model, size_t modelSize,
+ const Operand& operand) {
+ // TODO: Support OperandLifeTime::CONSTANT_REFERENCE as an invalid lifetime
+ // TODO: Support OperandLifeTime::NO_VALUE as an invalid lifetime
+
+ // Ways to get an invalid lifetime:
+ // - change whether a lifetime means an operand should have a writer
+ std::vector<OperandLifeTime> ret;
+ switch (operand.lifetime) {
+ case OperandLifeTime::SUBGRAPH_OUTPUT:
+ case OperandLifeTime::TEMPORARY_VARIABLE:
+ ret = {
+ OperandLifeTime::SUBGRAPH_INPUT,
+ OperandLifeTime::CONSTANT_COPY,
+ };
+ break;
+ case OperandLifeTime::CONSTANT_COPY:
+ case OperandLifeTime::CONSTANT_REFERENCE:
+ case OperandLifeTime::SUBGRAPH_INPUT:
+ ret = {
+ OperandLifeTime::TEMPORARY_VARIABLE,
+ OperandLifeTime::SUBGRAPH_OUTPUT,
+ };
+ break;
+ case OperandLifeTime::NO_VALUE:
+ // Not enough information to know whether
+ // TEMPORARY_VARIABLE or CONSTANT_COPY would be invalid --
+ // is this operand written (then CONSTANT_COPY would be
+ // invalid) or not (then TEMPORARY_VARIABLE would be
+ // invalid)?
+ break;
+ case OperandLifeTime::SUBGRAPH:
+ break;
+ default:
+ ADD_FAILURE();
+ break;
+ }
+
+ const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
+ if (!operandSize ||
+ exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
+ // Unknown size or too-large size
+ ret.erase(std::remove(ret.begin(), ret.end(), OperandLifeTime::CONSTANT_COPY), ret.end());
+ }
+
+ return ret;
+}
+
+static void mutateOperandLifeTimeTest(const sp<IDevice>& device, const Model& model) {
+ const size_t modelSize = sizeForBinder(model);
+ for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+ const std::vector<OperandLifeTime> invalidLifeTimes =
+ getInvalidLifeTimes(model, modelSize, model.main.operands[operand]);
+ for (OperandLifeTime invalidLifeTime : invalidLifeTimes) {
+ const std::string message = "mutateOperandLifetimeTest: operand " +
+ std::to_string(operand) + " has lifetime " +
+ toString(invalidLifeTime) + " instead of lifetime " +
+ toString(model.main.operands[operand].lifetime);
+ validate(device, message, model,
+ [operand, invalidLifeTime](Model* model, ExecutionPreference*, Priority*) {
+ static const DataLocation kZeroDataLocation = {};
+ Operand& operandObj = model->main.operands[operand];
+ switch (operandObj.lifetime) {
+ case OperandLifeTime::SUBGRAPH_INPUT: {
+ hidl_vec_remove(&model->main.inputIndexes, uint32_t(operand));
+ break;
+ }
+ case OperandLifeTime::SUBGRAPH_OUTPUT: {
+ hidl_vec_remove(&model->main.outputIndexes, uint32_t(operand));
+ break;
+ }
+ default:
+ break;
+ }
+ operandObj.lifetime = invalidLifeTime;
+ operandObj.location = kZeroDataLocation;
+ switch (invalidLifeTime) {
+ case OperandLifeTime::CONSTANT_COPY: {
+ becomeConstantCopy(model, &operandObj);
+ break;
+ }
+ case OperandLifeTime::SUBGRAPH_INPUT:
+ hidl_vec_push_back(&model->main.inputIndexes, uint32_t(operand));
+ break;
+ case OperandLifeTime::SUBGRAPH_OUTPUT:
+ hidl_vec_push_back(&model->main.outputIndexes, uint32_t(operand));
+ break;
+ default:
+ break;
+ }
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND INPUT-or-OUTPUT //////////////////////////////////////
+
+static std::optional<OperandLifeTime> getInputOutputLifeTime(const Model& model, size_t modelSize,
+ const Operand& operand) {
+ // Ways to get an invalid lifetime (with respect to model inputIndexes and outputIndexes):
+ // - change whether a lifetime means an operand is a model input, a model output, or neither
+ // - preserve whether or not a lifetime means an operand should have a writer
+ switch (operand.lifetime) {
+ case OperandLifeTime::CONSTANT_COPY:
+ case OperandLifeTime::CONSTANT_REFERENCE:
+ return OperandLifeTime::SUBGRAPH_INPUT;
+ case OperandLifeTime::SUBGRAPH_INPUT: {
+ const size_t operandSize = sizeOfData(operand); // will be zero if shape is unknown
+ if (!operandSize ||
+ exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
+ // Unknown size or too-large size
+ break;
+ }
+ return OperandLifeTime::CONSTANT_COPY;
+ }
+ case OperandLifeTime::SUBGRAPH_OUTPUT:
+ return OperandLifeTime::TEMPORARY_VARIABLE;
+ case OperandLifeTime::TEMPORARY_VARIABLE:
+ return OperandLifeTime::SUBGRAPH_OUTPUT;
+ case OperandLifeTime::NO_VALUE:
+ // Not enough information to know whether
+ // TEMPORARY_VARIABLE or CONSTANT_COPY would be an
+ // appropriate choice -- is this operand written (then
+ // TEMPORARY_VARIABLE would be appropriate) or not (then
+ // CONSTANT_COPY would be appropriate)?
+ break;
+ case OperandLifeTime::SUBGRAPH:
+ break;
+ default:
+ ADD_FAILURE();
+ break;
+ }
+
+ return std::nullopt;
+}
+
+static void mutateOperandInputOutputTest(const sp<IDevice>& device, const Model& model) {
+ const size_t modelSize = sizeForBinder(model);
+ for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+ const std::optional<OperandLifeTime> changedLifeTime =
+ getInputOutputLifeTime(model, modelSize, model.main.operands[operand]);
+ if (changedLifeTime) {
+ const std::string message = "mutateOperandInputOutputTest: operand " +
+ std::to_string(operand) + " has lifetime " +
+ toString(*changedLifeTime) + " instead of lifetime " +
+ toString(model.main.operands[operand].lifetime);
+ validate(device, message, model,
+ [operand, changedLifeTime](Model* model, ExecutionPreference*, Priority*) {
+ static const DataLocation kZeroDataLocation = {};
+ Operand& operandObj = model->main.operands[operand];
+ operandObj.lifetime = *changedLifeTime;
+ operandObj.location = kZeroDataLocation;
+ if (*changedLifeTime == OperandLifeTime::CONSTANT_COPY) {
+ becomeConstantCopy(model, &operandObj);
+ }
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND NUMBER OF CONSUMERS //////////////////////////////////
+
+static std::vector<uint32_t> getInvalidNumberOfConsumers(uint32_t numberOfConsumers) {
+ if (numberOfConsumers == 0) {
+ return {1};
+ } else {
+ return {numberOfConsumers - 1, numberOfConsumers + 1};
+ }
+}
+
+static void mutateOperandNumberOfConsumersTest(const sp<IDevice>& device, const Model& model) {
+ for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+ const std::vector<uint32_t> invalidNumberOfConsumersVec =
+ getInvalidNumberOfConsumers(model.main.operands[operand].numberOfConsumers);
+ for (uint32_t invalidNumberOfConsumers : invalidNumberOfConsumersVec) {
+ const std::string message =
+ "mutateOperandNumberOfConsumersTest: operand " + std::to_string(operand) +
+ " numberOfConsumers = " + std::to_string(invalidNumberOfConsumers);
+ validate(device, message, model,
+ [operand, invalidNumberOfConsumers](Model* model, ExecutionPreference*,
+ Priority*) {
+ model->main.operands[operand].numberOfConsumers = invalidNumberOfConsumers;
+ });
+ }
+ }
+}
+
+///////////////////////// VALIDATE OPERAND NUMBER OF WRITERS ////////////////////////////////////
+
+static void mutateOperandAddWriterTest(const sp<IDevice>& device, const Model& model) {
+ for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+ for (size_t badOutputNum = 0;
+ badOutputNum < model.main.operations[operation].outputs.size(); ++badOutputNum) {
+ const uint32_t outputOperandIndex =
+ model.main.operations[operation].outputs[badOutputNum];
+ const std::string message = "mutateOperandAddWriterTest: operation " +
+ std::to_string(operation) + " writes to " +
+ std::to_string(outputOperandIndex);
+ // We'll insert a copy of the operation, all of whose
+ // OTHER output operands are newly-created -- i.e.,
+ // there'll only be a duplicate write of ONE of that
+ // operation's output operands.
+ validate(device, message, model,
+ [operation, badOutputNum](Model* model, ExecutionPreference*, Priority*) {
+ Operation newOperation = model->main.operations[operation];
+ for (uint32_t input : newOperation.inputs) {
+ ++model->main.operands[input].numberOfConsumers;
+ }
+ for (size_t outputNum = 0; outputNum < newOperation.outputs.size();
+ ++outputNum) {
+ if (outputNum == badOutputNum) continue;
+
+ Operand operandValue =
+ model->main.operands[newOperation.outputs[outputNum]];
+ operandValue.numberOfConsumers = 0;
+ if (operandValue.lifetime == OperandLifeTime::SUBGRAPH_OUTPUT) {
+ operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
+ } else {
+ ASSERT_EQ(operandValue.lifetime,
+ OperandLifeTime::TEMPORARY_VARIABLE);
+ }
+ newOperation.outputs[outputNum] =
+ hidl_vec_push_back(&model->main.operands, operandValue);
+ }
+ // Where do we insert the extra writer (a new
+ // operation)? It has to be later than all the
+ // writers of its inputs. The easiest thing to do
+ // is to insert it at the end of the operation
+ // sequence.
+ hidl_vec_push_back(&model->main.operations, newOperation);
+ });
+ }
+ }
+}
+
///////////////////////// VALIDATE EXTRA ??? /////////////////////////
-// TODO: Operand::lifetime
// TODO: Operand::location
///////////////////////// VALIDATE OPERATION OPERAND TYPE /////////////////////////
@@ -511,6 +1010,37 @@
}
}
+///////////////////////// VALIDATE MODEL OPERANDS WRITTEN ///////////////////////////////////////
+
+static void mutateOperationRemoveWriteTest(const sp<IDevice>& device, const Model& model) {
+ for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+ for (size_t outputNum = 0; outputNum < model.main.operations[operation].outputs.size();
+ ++outputNum) {
+ const uint32_t outputOperandIndex = model.main.operations[operation].outputs[outputNum];
+ if (model.main.operands[outputOperandIndex].numberOfConsumers > 0) {
+ const std::string message = "mutateOperationRemoveWriteTest: operation " +
+ std::to_string(operation) + " writes to " +
+ std::to_string(outputOperandIndex);
+ validate(device, message, model,
+ [operation, outputNum](Model* model, ExecutionPreference*, Priority*) {
+ uint32_t& outputOperandIndex =
+ model->main.operations[operation].outputs[outputNum];
+ Operand operandValue = model->main.operands[outputOperandIndex];
+ operandValue.numberOfConsumers = 0;
+ if (operandValue.lifetime == OperandLifeTime::SUBGRAPH_OUTPUT) {
+ operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
+ } else {
+ ASSERT_EQ(operandValue.lifetime,
+ OperandLifeTime::TEMPORARY_VARIABLE);
+ }
+ outputOperandIndex =
+ hidl_vec_push_back(&model->main.operands, operandValue);
+ });
+ }
+ }
+ }
+}
+
///////////////////////// REMOVE OPERAND FROM EVERYTHING /////////////////////////
static void removeValueAndDecrementGreaterValues(hidl_vec<uint32_t>* vec, uint32_t value) {
@@ -804,14 +1334,20 @@
////////////////////////// ENTRY POINT //////////////////////////////
void validateModel(const sp<IDevice>& device, const Model& model) {
+ mutateExecutionOrderTest(device, model);
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
mutateOperandZeroPointTest(device, model);
+ mutateOperandLifeTimeTest(device, model);
+ mutateOperandInputOutputTest(device, model);
+ mutateOperandNumberOfConsumersTest(device, model);
+ mutateOperandAddWriterTest(device, model);
mutateOperationOperandTypeTest(device, model);
mutateOperationTypeTest(device, model);
mutateOperationInputOperandIndexTest(device, model);
mutateOperationOutputOperandIndexTest(device, model);
+ mutateOperationRemoveWriteTest(device, model);
removeOperandTest(device, model);
removeOperationTest(device, model);
removeOperationInputTest(device, model);
diff --git a/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h b/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h
index 3661b66..e07e73b 100644
--- a/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h
+++ b/neuralnetworks/1.3/vts/functional/include/1.3/Utils.h
@@ -24,6 +24,18 @@
inline constexpr V1_3::Priority kDefaultPriority = V1_3::Priority::MEDIUM;
+// Returns the amount of space needed to store a value of the specified type.
+//
+// Aborts if the specified type is an extension type or OEM type.
+uint32_t sizeOfData(V1_3::OperandType type);
+
+// Returns the amount of space needed to store a value of the dimensions and
+// type of this operand. For a non-extension, non-OEM tensor with unspecified
+// rank or at least one unspecified dimension, returns zero.
+//
+// Aborts if the specified type is an extension type or OEM type.
+uint32_t sizeOfData(const V1_3::Operand& operand);
+
} // namespace android::hardware::neuralnetworks
namespace android::hardware::neuralnetworks::V1_3 {
diff --git a/radio/1.0/vts/functional/vts_hal_radio_target_test.xml b/radio/1.0/vts/functional/vts_hal_radio_target_test.xml
index 5e4a1cd..b91119d 100644
--- a/radio/1.0/vts/functional/vts_hal_radio_target_test.xml
+++ b/radio/1.0/vts/functional/vts_hal_radio_target_test.xml
@@ -17,9 +17,10 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
+ <!-- TODO: b/154638140, b/152655658: bad interactions -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.MultiSimPreparer" />
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
diff --git a/radio/1.0/vts/functional/vts_hal_sap_target_test.xml b/radio/1.0/vts/functional/vts_hal_sap_target_test.xml
index 457d700..876e1fb 100644
--- a/radio/1.0/vts/functional/vts_hal_sap_target_test.xml
+++ b/radio/1.0/vts/functional/vts_hal_sap_target_test.xml
@@ -17,9 +17,10 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
+ <!-- TODO: b/154638140, b/152655658: bad interactions -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.MultiSimPreparer" />
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
diff --git a/radio/1.1/vts/functional/AndroidTest.xml b/radio/1.1/vts/functional/AndroidTest.xml
index 5badadd..3699575 100644
--- a/radio/1.1/vts/functional/AndroidTest.xml
+++ b/radio/1.1/vts/functional/AndroidTest.xml
@@ -17,9 +17,10 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
+ <!-- TODO: b/154638140, b/152655658: bad interactions -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.MultiSimPreparer" />
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
diff --git a/radio/1.2/vts/functional/AndroidTest.xml b/radio/1.2/vts/functional/AndroidTest.xml
index 5d92248..9904760 100644
--- a/radio/1.2/vts/functional/AndroidTest.xml
+++ b/radio/1.2/vts/functional/AndroidTest.xml
@@ -17,9 +17,10 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
+ <!-- TODO: b/154638140, b/152655658: bad interactions -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.MultiSimPreparer" />
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
diff --git a/radio/1.3/vts/functional/AndroidTest.xml b/radio/1.3/vts/functional/AndroidTest.xml
index c910047..9df8f9c 100644
--- a/radio/1.3/vts/functional/AndroidTest.xml
+++ b/radio/1.3/vts/functional/AndroidTest.xml
@@ -17,9 +17,10 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
+ <!-- TODO: b/154638140, b/152655658: bad interactions -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.MultiSimPreparer" />
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
diff --git a/radio/1.4/vts/functional/AndroidTest.xml b/radio/1.4/vts/functional/AndroidTest.xml
index c910047..9df8f9c 100644
--- a/radio/1.4/vts/functional/AndroidTest.xml
+++ b/radio/1.4/vts/functional/AndroidTest.xml
@@ -17,9 +17,10 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-native" />
+ <!-- TODO: b/154638140, b/152655658: bad interactions -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.MultiSimPreparer" />
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
- </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
diff --git a/sensors/2.0/multihal/Android.bp b/sensors/2.0/multihal/Android.bp
index 3ce3390..bf51fcd 100644
--- a/sensors/2.0/multihal/Android.bp
+++ b/sensors/2.0/multihal/Android.bp
@@ -25,6 +25,9 @@
],
init_rc: ["android.hardware.sensors@2.0-service-multihal.rc"],
vintf_fragments: ["android.hardware.sensors@2.0-multihal.xml"],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
shared_libs: [
"android.hardware.sensors@2.0",
"android.hardware.sensors@2.0-ScopedWakelock",
@@ -37,5 +40,8 @@
"libpower",
"libutils",
],
- static_libs: ["android.hardware.sensors@2.X-multihal"],
+ static_libs: [
+ "android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors@2.X-multihal",
+ ],
}
diff --git a/sensors/2.0/multihal/service.cpp b/sensors/2.0/multihal/service.cpp
index ef77048..f50ad7e 100644
--- a/sensors/2.0/multihal/service.cpp
+++ b/sensors/2.0/multihal/service.cpp
@@ -23,12 +23,12 @@
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::hardware::sensors::V2_0::ISensors;
-using android::hardware::sensors::V2_0::implementation::HalProxy;
+using android::hardware::sensors::V2_1::implementation::HalProxyV2_0;
int main(int /* argc */, char** /* argv */) {
configureRpcThreadpool(1, true);
- android::sp<ISensors> halProxy = new HalProxy();
+ android::sp<ISensors> halProxy = new HalProxyV2_0();
if (halProxy->registerAsService() != ::android::OK) {
ALOGE("Failed to register Sensors HAL instance");
return -1;
diff --git a/sensors/2.1/multihal/Android.bp b/sensors/2.1/multihal/Android.bp
new file mode 100644
index 0000000..6a7cac9
--- /dev/null
+++ b/sensors/2.1/multihal/Android.bp
@@ -0,0 +1,47 @@
+//
+// 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.
+
+cc_binary {
+ name: "android.hardware.sensors@2.1-service.multihal",
+ defaults: [
+ "hidl_defaults",
+ ],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "service.cpp",
+ ],
+ init_rc: ["android.hardware.sensors@2.1-service-multihal.rc"],
+ vintf_fragments: ["android.hardware.sensors@2.1-multihal.xml"],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
+ shared_libs: [
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.0-ScopedWakelock",
+ "android.hardware.sensors@2.1",
+ "libbase",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libpower",
+ "libutils",
+ ],
+ static_libs: [
+ "android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors@2.X-multihal",
+ ],
+}
diff --git a/sensors/2.1/multihal/OWNERS b/sensors/2.1/multihal/OWNERS
new file mode 100644
index 0000000..e955670
--- /dev/null
+++ b/sensors/2.1/multihal/OWNERS
@@ -0,0 +1,3 @@
+arthuri@google.com
+bduddie@google.com
+stange@google.com
\ No newline at end of file
diff --git a/sensors/2.1/multihal/android.hardware.sensors@2.1-multihal.xml b/sensors/2.1/multihal/android.hardware.sensors@2.1-multihal.xml
new file mode 100644
index 0000000..18bd3ae
--- /dev/null
+++ b/sensors/2.1/multihal/android.hardware.sensors@2.1-multihal.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.sensors</name>
+ <transport>hwbinder</transport>
+ <version>2.1</version>
+ <interface>
+ <name>ISensors</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/sensors/2.1/multihal/android.hardware.sensors@2.1-service-multihal.rc b/sensors/2.1/multihal/android.hardware.sensors@2.1-service-multihal.rc
new file mode 100644
index 0000000..fc99ee7
--- /dev/null
+++ b/sensors/2.1/multihal/android.hardware.sensors@2.1-service-multihal.rc
@@ -0,0 +1,7 @@
+service vendor.sensors-hal-2-1-multihal /vendor/bin/hw/android.hardware.sensors@2.1-service.multihal
+ class hal
+ user system
+ group system wakelock context_hub
+ writepid /dev/cpuset/system-background/tasks
+ capabilities BLOCK_SUSPEND
+ rlimit rtprio 10 10
diff --git a/sensors/2.1/multihal/service.cpp b/sensors/2.1/multihal/service.cpp
new file mode 100644
index 0000000..d68d9a3
--- /dev/null
+++ b/sensors/2.1/multihal/service.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 <android/hardware/sensors/2.1/ISensors.h>
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+#include "HalProxy.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::sensors::V2_1::ISensors;
+using android::hardware::sensors::V2_1::implementation::HalProxyV2_1;
+
+int main(int /* argc */, char** /* argv */) {
+ configureRpcThreadpool(1, true);
+
+ android::sp<ISensors> halProxy = new HalProxyV2_1();
+ if (halProxy->registerAsService() != ::android::OK) {
+ ALOGE("Failed to register Sensors HAL instance");
+ return -1;
+ }
+
+ joinRpcThreadpool();
+ return 1; // joinRpcThreadpool shouldn't exit
+}
diff --git a/sensors/common/default/2.X/multihal/Android.bp b/sensors/common/default/2.X/multihal/Android.bp
index 6122323..c80c47a 100644
--- a/sensors/common/default/2.X/multihal/Android.bp
+++ b/sensors/common/default/2.X/multihal/Android.bp
@@ -17,6 +17,7 @@
name: "android.hardware.sensors@2.X-multihal-defaults",
header_libs: [
"android.hardware.sensors@2.X-multihal.header",
+ "android.hardware.sensors@2.X-shared-utils",
],
shared_libs: [
"android.hardware.sensors@1.0",
@@ -30,6 +31,9 @@
"libpower",
"libutils",
],
+ static_libs: [
+ "android.hardware.sensors@1.0-convert",
+ ],
cflags: ["-DLOG_TAG=\"SensorsMultiHal\""],
}
@@ -62,6 +66,7 @@
],
srcs: [
"HalProxy.cpp",
+ "HalProxyCallback.cpp",
],
vendor_available: true,
export_header_lib_headers: [
diff --git a/sensors/common/default/2.X/multihal/HalProxy.cpp b/sensors/common/default/2.X/multihal/HalProxy.cpp
index 869c033..75ffc17 100644
--- a/sensors/common/default/2.X/multihal/HalProxy.cpp
+++ b/sensors/common/default/2.X/multihal/HalProxy.cpp
@@ -32,15 +32,17 @@
namespace android {
namespace hardware {
namespace sensors {
-namespace V2_0 {
+namespace V2_1 {
namespace implementation {
+using ::android::hardware::sensors::V1_0::Result;
using ::android::hardware::sensors::V2_0::EventQueueFlagBits;
using ::android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
using ::android::hardware::sensors::V2_0::implementation::getTimeNow;
using ::android::hardware::sensors::V2_0::implementation::kWakelockTimeoutNs;
-typedef ISensorsSubHal*(SensorsHalGetSubHalFunc)(uint32_t*);
+typedef V2_0::implementation::ISensorsSubHal*(SensorsHalGetSubHalFunc)(uint32_t*);
+typedef V2_1::implementation::ISensorsSubHal*(SensorsHalGetSubHalV2_1Func)(uint32_t*);
static constexpr int32_t kBitsAfterSubHalIndex = 24;
@@ -85,7 +87,24 @@
init();
}
-HalProxy::HalProxy(std::vector<ISensorsSubHal*>& subHalList) : mSubHalList(subHalList) {
+HalProxy::HalProxy(std::vector<ISensorsSubHalV2_0*>& subHalList) {
+ for (ISensorsSubHalV2_0* subHal : subHalList) {
+ mSubHalList.push_back(std::make_unique<SubHalWrapperV2_0>(subHal));
+ }
+
+ init();
+}
+
+HalProxy::HalProxy(std::vector<ISensorsSubHalV2_0*>& subHalList,
+ std::vector<ISensorsSubHalV2_1*>& subHalListV2_1) {
+ for (ISensorsSubHalV2_0* subHal : subHalList) {
+ mSubHalList.push_back(std::make_unique<SubHalWrapperV2_0>(subHal));
+ }
+
+ for (ISensorsSubHalV2_1* subHal : subHalListV2_1) {
+ mSubHalList.push_back(std::make_unique<SubHalWrapperV2_1>(subHal));
+ }
+
init();
}
@@ -93,8 +112,8 @@
stopThreads();
}
-Return<void> HalProxy::getSensorsList(getSensorsList_cb _hidl_cb) {
- std::vector<SensorInfo> sensors;
+Return<void> HalProxy::getSensorsList_2_1(ISensorsV2_1::getSensorsList_2_1_cb _hidl_cb) {
+ std::vector<V2_1::SensorInfo> sensors;
for (const auto& iter : mSensors) {
sensors.push_back(iter.second);
}
@@ -102,22 +121,31 @@
return Void();
}
+Return<void> HalProxy::getSensorsList(ISensorsV2_0::getSensorsList_cb _hidl_cb) {
+ std::vector<V1_0::SensorInfo> sensors;
+ for (const auto& iter : mSensors) {
+ sensors.push_back(convertToOldSensorInfo(iter.second));
+ }
+ _hidl_cb(sensors);
+ return Void();
+}
+
Return<Result> HalProxy::setOperationMode(OperationMode mode) {
Result result = Result::OK;
size_t subHalIndex;
for (subHalIndex = 0; subHalIndex < mSubHalList.size(); subHalIndex++) {
- ISensorsSubHal* subHal = mSubHalList[subHalIndex];
- result = subHal->setOperationMode(mode);
+ result = mSubHalList[subHalIndex]->setOperationMode(mode);
if (result != Result::OK) {
- ALOGE("setOperationMode failed for SubHal: %s", subHal->getName().c_str());
+ ALOGE("setOperationMode failed for SubHal: %s",
+ mSubHalList[subHalIndex]->getName().c_str());
break;
}
}
+
if (result != Result::OK) {
// Reset the subhal operation modes that have been flipped
for (size_t i = 0; i < subHalIndex; i++) {
- ISensorsSubHal* subHal = mSubHalList[i];
- subHal->setOperationMode(mCurrentOperationMode);
+ mSubHalList[i]->setOperationMode(mCurrentOperationMode);
}
} else {
mCurrentOperationMode = mode;
@@ -133,10 +161,42 @@
->activate(clearSubHalIndex(sensorHandle), enabled);
}
-Return<Result> HalProxy::initialize(
- const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
+Return<Result> HalProxy::initialize_2_1(
+ const ::android::hardware::MQDescriptorSync<V2_1::Event>& eventQueueDescriptor,
const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
- const sp<ISensorsCallback>& sensorsCallback) {
+ const sp<V2_1::ISensorsCallback>& sensorsCallback) {
+ sp<ISensorsCallbackWrapperBase> dynamicCallback =
+ new ISensorsCallbackWrapperV2_1(sensorsCallback);
+
+ // Create the Event FMQ from the eventQueueDescriptor. Reset the read/write positions.
+ auto eventQueue =
+ std::make_unique<EventMessageQueueV2_1>(eventQueueDescriptor, true /* resetPointers */);
+ std::unique_ptr<EventMessageQueueWrapperBase> queue =
+ std::make_unique<EventMessageQueueWrapperV2_1>(eventQueue);
+
+ return initializeCommon(queue, wakeLockDescriptor, dynamicCallback);
+}
+
+Return<Result> HalProxy::initialize(
+ const ::android::hardware::MQDescriptorSync<V1_0::Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<V2_0::ISensorsCallback>& sensorsCallback) {
+ sp<ISensorsCallbackWrapperBase> dynamicCallback =
+ new ISensorsCallbackWrapperV2_0(sensorsCallback);
+
+ // Create the Event FMQ from the eventQueueDescriptor. Reset the read/write positions.
+ auto eventQueue =
+ std::make_unique<EventMessageQueueV2_0>(eventQueueDescriptor, true /* resetPointers */);
+ std::unique_ptr<EventMessageQueueWrapperBase> queue =
+ std::make_unique<EventMessageQueueWrapperV1_0>(eventQueue);
+
+ return initializeCommon(queue, wakeLockDescriptor, dynamicCallback);
+}
+
+Return<Result> HalProxy::initializeCommon(
+ std::unique_ptr<EventMessageQueueWrapperBase>& eventQueue,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<ISensorsCallbackWrapperBase>& sensorsCallback) {
Result result = Result::OK;
stopThreads();
@@ -147,7 +207,7 @@
disableAllSensors();
// Clears the queue if any events were pending write before.
- mPendingWriteEventsQueue = std::queue<std::pair<std::vector<Event>, size_t>>();
+ mPendingWriteEventsQueue = std::queue<std::pair<std::vector<V2_1::Event>, size_t>>();
mSizePendingWriteEventsQueue = 0;
// Clears previously connected dynamic sensors
@@ -156,8 +216,7 @@
mDynamicSensorsCallback = sensorsCallback;
// Create the Event FMQ from the eventQueueDescriptor. Reset the read/write positions.
- mEventQueue =
- std::make_unique<EventMessageQueue>(eventQueueDescriptor, true /* resetPointers */);
+ mEventQueue = std::move(eventQueue);
// Create the Wake Lock FMQ that is used by the framework to communicate whenever WAKE_UP
// events have been successfully read and handled by the framework.
@@ -186,12 +245,10 @@
mWakelockThread = std::thread(startWakelockThread, this);
for (size_t i = 0; i < mSubHalList.size(); i++) {
- auto subHal = mSubHalList[i];
- const auto& subHalCallback = mSubHalCallbacks[i];
- Result currRes = subHal->initialize(subHalCallback);
+ Result currRes = mSubHalList[i]->initialize(this, this, i);
if (currRes != Result::OK) {
result = currRes;
- ALOGE("Subhal '%s' failed to initialize.", subHal->getName().c_str());
+ ALOGE("Subhal '%s' failed to initialize.", mSubHalList[i]->getName().c_str());
break;
}
}
@@ -217,7 +274,11 @@
return getSubHalForSensorHandle(sensorHandle)->flush(clearSubHalIndex(sensorHandle));
}
-Return<Result> HalProxy::injectSensorData(const Event& event) {
+Return<Result> HalProxy::injectSensorData_2_1(const V2_1::Event& event) {
+ return injectSensorData(convertToOldEvent(event));
+}
+
+Return<Result> HalProxy::injectSensorData(const V1_0::Event& event) {
Result result = Result::OK;
if (mCurrentOperationMode == OperationMode::NORMAL &&
event.sensorType != V1_0::SensorType::ADDITIONAL_INFO) {
@@ -226,18 +287,19 @@
result = Result::BAD_VALUE;
}
if (result == Result::OK) {
- Event subHalEvent = event;
+ V1_0::Event subHalEvent = event;
if (!isSubHalIndexValid(event.sensorHandle)) {
return Result::BAD_VALUE;
}
subHalEvent.sensorHandle = clearSubHalIndex(event.sensorHandle);
- result = getSubHalForSensorHandle(event.sensorHandle)->injectSensorData(subHalEvent);
+ result = getSubHalForSensorHandle(event.sensorHandle)
+ ->injectSensorData(convertToNewEvent(subHalEvent));
}
return result;
}
Return<void> HalProxy::registerDirectChannel(const SharedMemInfo& mem,
- registerDirectChannel_cb _hidl_cb) {
+ ISensorsV2_0::registerDirectChannel_cb _hidl_cb) {
if (mDirectChannelSubHal == nullptr) {
_hidl_cb(Result::INVALID_OPERATION, -1 /* channelHandle */);
} else {
@@ -257,7 +319,8 @@
}
Return<void> HalProxy::configDirectReport(int32_t sensorHandle, int32_t channelHandle,
- RateLevel rate, configDirectReport_cb _hidl_cb) {
+ RateLevel rate,
+ ISensorsV2_0::configDirectReport_cb _hidl_cb) {
if (mDirectChannelSubHal == nullptr) {
_hidl_cb(Result::INVALID_OPERATION, -1 /* reportToken */);
} else if (sensorHandle == -1 && rate != RateLevel::STOP) {
@@ -302,7 +365,7 @@
stream << " # of non-dynamic sensors across all subhals: " << mSensors.size() << std::endl;
stream << " # of dynamic sensors across all subhals: " << mDynamicSensors.size() << std::endl;
stream << "SubHals (" << mSubHalList.size() << "):" << std::endl;
- for (ISensorsSubHal* subHal : mSubHalList) {
+ for (auto& subHal : mSubHalList) {
stream << " Name: " << subHal->getName() << std::endl;
stream << " Debug dump: " << std::endl;
android::base::WriteStringToFd(stream.str(), writeFd);
@@ -363,26 +426,43 @@
} else {
std::string subHalLibraryFile;
while (subHalConfigStream >> subHalLibraryFile) {
- void* handle = dlopen(subHalLibraryFile.c_str(), RTLD_NOW);
+ void* handle = getHandleForSubHalSharedObject(subHalLibraryFile);
if (handle == nullptr) {
ALOGE("dlopen failed for library: %s", subHalLibraryFile.c_str());
} else {
SensorsHalGetSubHalFunc* sensorsHalGetSubHalPtr =
(SensorsHalGetSubHalFunc*)dlsym(handle, "sensorsHalGetSubHal");
- if (sensorsHalGetSubHalPtr == nullptr) {
- ALOGE("Failed to locate sensorsHalGetSubHal function for library: %s",
- subHalLibraryFile.c_str());
- } else {
+ if (sensorsHalGetSubHalPtr != nullptr) {
std::function<SensorsHalGetSubHalFunc> sensorsHalGetSubHal =
*sensorsHalGetSubHalPtr;
uint32_t version;
- ISensorsSubHal* subHal = sensorsHalGetSubHal(&version);
+ ISensorsSubHalV2_0* subHal = sensorsHalGetSubHal(&version);
if (version != SUB_HAL_2_0_VERSION) {
ALOGE("SubHal version was not 2.0 for library: %s",
subHalLibraryFile.c_str());
} else {
ALOGV("Loaded SubHal from library: %s", subHalLibraryFile.c_str());
- mSubHalList.push_back(subHal);
+ mSubHalList.push_back(std::make_unique<SubHalWrapperV2_0>(subHal));
+ }
+ } else {
+ SensorsHalGetSubHalV2_1Func* getSubHalV2_1Ptr =
+ (SensorsHalGetSubHalV2_1Func*)dlsym(handle, "sensorsHalGetSubHal_2_1");
+
+ if (getSubHalV2_1Ptr == nullptr) {
+ ALOGE("Failed to locate sensorsHalGetSubHal function for library: %s",
+ subHalLibraryFile.c_str());
+ } else {
+ std::function<SensorsHalGetSubHalV2_1Func> sensorsHalGetSubHal_2_1 =
+ *getSubHalV2_1Ptr;
+ uint32_t version;
+ ISensorsSubHalV2_1* subHal = sensorsHalGetSubHal_2_1(&version);
+ if (version != SUB_HAL_2_1_VERSION) {
+ ALOGE("SubHal version was not 2.1 for library: %s",
+ subHalLibraryFile.c_str());
+ } else {
+ ALOGV("Loaded SubHal from library: %s", subHalLibraryFile.c_str());
+ mSubHalList.push_back(std::make_unique<SubHalWrapperV2_1>(subHal));
+ }
}
}
}
@@ -390,36 +470,47 @@
}
}
-void HalProxy::initializeSubHalCallbacks() {
- for (size_t subHalIndex = 0; subHalIndex < mSubHalList.size(); subHalIndex++) {
- sp<IHalProxyCallback> callback = new HalProxyCallback(this, subHalIndex);
- mSubHalCallbacks.push_back(callback);
- }
-}
-
void HalProxy::initializeSensorList() {
for (size_t subHalIndex = 0; subHalIndex < mSubHalList.size(); subHalIndex++) {
- ISensorsSubHal* subHal = mSubHalList[subHalIndex];
- auto result = subHal->getSensorsList([&](const auto& list) {
+ auto result = mSubHalList[subHalIndex]->getSensorsList([&](const auto& list) {
for (SensorInfo sensor : list) {
if (!subHalIndexIsClear(sensor.sensorHandle)) {
ALOGE("SubHal sensorHandle's first byte was not 0");
} else {
ALOGV("Loaded sensor: %s", sensor.name.c_str());
sensor.sensorHandle = setSubHalIndex(sensor.sensorHandle, subHalIndex);
- setDirectChannelFlags(&sensor, subHal);
+ setDirectChannelFlags(&sensor, mSubHalList[subHalIndex]);
mSensors[sensor.sensorHandle] = sensor;
}
}
});
if (!result.isOk()) {
- ALOGE("getSensorsList call failed for SubHal: %s", subHal->getName().c_str());
+ ALOGE("getSensorsList call failed for SubHal: %s",
+ mSubHalList[subHalIndex]->getName().c_str());
}
}
}
+void* HalProxy::getHandleForSubHalSharedObject(const std::string& filename) {
+ static const std::string kSubHalShareObjectLocations[] = {
+ "", // Default locations will be searched
+#ifdef __LP64__
+ "/vendor/lib64/hw/", "/odm/lib64/hw/"
+#else
+ "/vendor/lib/hw/", "/odm/lib/hw/"
+#endif
+ };
+
+ for (const std::string& dir : kSubHalShareObjectLocations) {
+ void* handle = dlopen((dir + filename).c_str(), RTLD_NOW);
+ if (handle != nullptr) {
+ return handle;
+ }
+ }
+ return nullptr;
+}
+
void HalProxy::init() {
- initializeSubHalCallbacks();
initializeSensorList();
}
@@ -552,7 +643,7 @@
}
void HalProxy::postEventsToMessageQueue(const std::vector<Event>& events, size_t numWakeupEvents,
- ScopedWakelock wakelock) {
+ V2_0::implementation::ScopedWakelock wakelock) {
size_t numToWrite = 0;
std::lock_guard<std::mutex> lock(mEventQueueWriteMutex);
if (wakelock.isLocked()) {
@@ -610,7 +701,8 @@
}
}
-void HalProxy::setDirectChannelFlags(SensorInfo* sensorInfo, ISensorsSubHal* subHal) {
+void HalProxy::setDirectChannelFlags(SensorInfo* sensorInfo,
+ std::shared_ptr<ISubHalWrapperBase> subHal) {
bool sensorSupportsDirectChannel =
(sensorInfo->flags & (V1_0::SensorFlagBits::MASK_DIRECT_REPORT |
V1_0::SensorFlagBits::MASK_DIRECT_CHANNEL)) != 0;
@@ -624,7 +716,7 @@
}
}
-ISensorsSubHal* HalProxy::getSubHalForSensorHandle(int32_t sensorHandle) {
+std::shared_ptr<ISubHalWrapperBase> HalProxy::getSubHalForSensorHandle(int32_t sensorHandle) {
return mSubHalList[extractSubHalIndex(sensorHandle)];
}
@@ -651,46 +743,8 @@
return (sensorHandle & kSensorHandleSubHalIndexMask) == 0;
}
-void HalProxyCallback::postEvents(const std::vector<Event>& events, ScopedWakelock wakelock) {
- if (events.empty() || !mHalProxy->areThreadsRunning()) return;
- size_t numWakeupEvents;
- std::vector<Event> processedEvents = processEvents(events, &numWakeupEvents);
- if (numWakeupEvents > 0) {
- ALOG_ASSERT(wakelock.isLocked(),
- "Wakeup events posted while wakelock unlocked for subhal"
- " w/ index %" PRId32 ".",
- mSubHalIndex);
- } else {
- ALOG_ASSERT(!wakelock.isLocked(),
- "No Wakeup events posted but wakelock locked for subhal"
- " w/ index %" PRId32 ".",
- mSubHalIndex);
- }
- mHalProxy->postEventsToMessageQueue(processedEvents, numWakeupEvents, std::move(wakelock));
-}
-
-ScopedWakelock HalProxyCallback::createScopedWakelock(bool lock) {
- ScopedWakelock wakelock(mHalProxy, lock);
- return wakelock;
-}
-
-std::vector<Event> HalProxyCallback::processEvents(const std::vector<Event>& events,
- size_t* numWakeupEvents) const {
- *numWakeupEvents = 0;
- std::vector<Event> eventsOut;
- for (Event event : events) {
- event.sensorHandle = setSubHalIndex(event.sensorHandle, mSubHalIndex);
- eventsOut.push_back(event);
- const SensorInfo& sensor = mHalProxy->getSensorInfo(event.sensorHandle);
- if ((sensor.flags & V1_0::SensorFlagBits::WAKE_UP) != 0) {
- (*numWakeupEvents)++;
- }
- }
- return eventsOut;
-}
-
} // namespace implementation
-} // namespace V2_0
+} // namespace V2_1
} // namespace sensors
} // namespace hardware
} // namespace android
diff --git a/sensors/common/default/2.X/multihal/HalProxyCallback.cpp b/sensors/common/default/2.X/multihal/HalProxyCallback.cpp
new file mode 100644
index 0000000..3c1b17c
--- /dev/null
+++ b/sensors/common/default/2.X/multihal/HalProxyCallback.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HalProxyCallback.h"
+
+#include <cinttypes>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_0 {
+namespace implementation {
+
+static constexpr int32_t kBitsAfterSubHalIndex = 24;
+
+/**
+ * Set the subhal index as first byte of sensor handle and return this modified version.
+ *
+ * @param sensorHandle The sensor handle to modify.
+ * @param subHalIndex The index in the hal proxy of the sub hal this sensor belongs to.
+ *
+ * @return The modified sensor handle.
+ */
+int32_t setSubHalIndex(int32_t sensorHandle, size_t subHalIndex) {
+ return sensorHandle | (static_cast<int32_t>(subHalIndex) << kBitsAfterSubHalIndex);
+}
+
+void HalProxyCallbackBase::postEvents(const std::vector<V2_1::Event>& events,
+ ScopedWakelock wakelock) {
+ if (events.empty() || !mCallback->areThreadsRunning()) return;
+ size_t numWakeupEvents;
+ std::vector<V2_1::Event> processedEvents = processEvents(events, &numWakeupEvents);
+ if (numWakeupEvents > 0) {
+ ALOG_ASSERT(wakelock.isLocked(),
+ "Wakeup events posted while wakelock unlocked for subhal"
+ " w/ index %" PRId32 ".",
+ mSubHalIndex);
+ } else {
+ ALOG_ASSERT(!wakelock.isLocked(),
+ "No Wakeup events posted but wakelock locked for subhal"
+ " w/ index %" PRId32 ".",
+ mSubHalIndex);
+ }
+ mCallback->postEventsToMessageQueue(processedEvents, numWakeupEvents, std::move(wakelock));
+}
+
+ScopedWakelock HalProxyCallbackBase::createScopedWakelock(bool lock) {
+ ScopedWakelock wakelock(mRefCounter, lock);
+ return wakelock;
+}
+
+std::vector<V2_1::Event> HalProxyCallbackBase::processEvents(const std::vector<V2_1::Event>& events,
+ size_t* numWakeupEvents) const {
+ *numWakeupEvents = 0;
+ std::vector<V2_1::Event> eventsOut;
+ for (V2_1::Event event : events) {
+ event.sensorHandle = setSubHalIndex(event.sensorHandle, mSubHalIndex);
+ eventsOut.push_back(event);
+ const V2_1::SensorInfo& sensor = mCallback->getSensorInfo(event.sensorHandle);
+ if ((sensor.flags & V1_0::SensorFlagBits::WAKE_UP) != 0) {
+ (*numWakeupEvents)++;
+ }
+ }
+ return eventsOut;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace sensors
+} // namespace hardware
+} // namespace android
diff --git a/sensors/common/default/2.X/multihal/include/HalProxy.h b/sensors/common/default/2.X/multihal/include/HalProxy.h
index d7e8795..35d7c8b 100644
--- a/sensors/common/default/2.X/multihal/include/HalProxy.h
+++ b/sensors/common/default/2.X/multihal/include/HalProxy.h
@@ -16,12 +16,17 @@
#pragma once
+#include "EventMessageQueueWrapper.h"
+#include "HalProxyCallback.h"
+#include "ISensorsCallbackWrapper.h"
+#include "SubHalWrapper.h"
#include "V2_0/ScopedWakelock.h"
#include "V2_0/SubHal.h"
#include "V2_1/SubHal.h"
+#include "convertV2_1.h"
-#include <android/hardware/sensors/2.0/ISensors.h>
-#include <android/hardware/sensors/2.0/types.h>
+#include <android/hardware/sensors/2.1/ISensors.h>
+#include <android/hardware/sensors/2.1/types.h>
#include <fmq/MessageQueue.h>
#include <hardware_legacy/power.h>
#include <hidl/MQDescriptor.h>
@@ -38,96 +43,97 @@
namespace android {
namespace hardware {
namespace sensors {
-namespace V2_0 {
+namespace V2_1 {
namespace implementation {
-using ::android::sp;
-using ::android::hardware::EventFlag;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::MessageQueue;
-using ::android::hardware::MQDescriptor;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-class HalProxy : public ISensors, public IScopedWakelockRefCounter {
+/**
+ * HalProxy is the main interface for Multi-HAL. It is responsible for managing subHALs and
+ * proxying function calls to/from the subHAL APIs from the sensors framework. It also manages any
+ * wakelocks allocated through the IHalProxyCallback and manages posting events to the sensors
+ * framework.
+ */
+class HalProxy : public V2_0::implementation::IScopedWakelockRefCounter,
+ public V2_0::implementation::ISubHalCallback {
public:
- using Event = ::android::hardware::sensors::V1_0::Event;
+ using Event = ::android::hardware::sensors::V2_1::Event;
using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
using RateLevel = ::android::hardware::sensors::V1_0::RateLevel;
using Result = ::android::hardware::sensors::V1_0::Result;
- using SensorInfo = ::android::hardware::sensors::V1_0::SensorInfo;
+ using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo;
- using ISensorsSubHal = ::android::hardware::sensors::V2_0::implementation::ISensorsSubHal;
+ using IHalProxyCallbackV2_0 = V2_0::implementation::IHalProxyCallback;
+ using IHalProxyCallbackV2_1 = V2_1::implementation::IHalProxyCallback;
+ using ISensorsSubHalV2_0 = V2_0::implementation::ISensorsSubHal;
+ using ISensorsSubHalV2_1 = V2_1::implementation::ISensorsSubHal;
+ using ISensorsV2_0 = V2_0::ISensors;
+ using ISensorsV2_1 = V2_1::ISensors;
+ using HalProxyCallbackBase = V2_0::implementation::HalProxyCallbackBase;
explicit HalProxy();
// Test only constructor.
- explicit HalProxy(std::vector<ISensorsSubHal*>& subHalList);
+ explicit HalProxy(std::vector<ISensorsSubHalV2_0*>& subHalList);
+ explicit HalProxy(std::vector<ISensorsSubHalV2_0*>& subHalList,
+ std::vector<ISensorsSubHalV2_1*>& subHalListV2_1);
~HalProxy();
+ // Methods from ::android::hardware::sensors::V2_1::ISensors follow.
+ Return<void> getSensorsList_2_1(ISensorsV2_1::getSensorsList_2_1_cb _hidl_cb);
+
+ Return<Result> initialize_2_1(
+ const ::android::hardware::MQDescriptorSync<V2_1::Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<V2_1::ISensorsCallback>& sensorsCallback);
+
+ Return<Result> injectSensorData_2_1(const Event& event);
+
// Methods from ::android::hardware::sensors::V2_0::ISensors follow.
- Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
+ Return<void> getSensorsList(ISensorsV2_0::getSensorsList_cb _hidl_cb);
- Return<Result> setOperationMode(OperationMode mode) override;
+ Return<Result> setOperationMode(OperationMode mode);
- Return<Result> activate(int32_t sensorHandle, bool enabled) override;
+ Return<Result> activate(int32_t sensorHandle, bool enabled);
Return<Result> initialize(
- const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<V1_0::Event>& eventQueueDescriptor,
const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
- const sp<ISensorsCallback>& sensorsCallback) override;
+ const sp<V2_0::ISensorsCallback>& sensorsCallback);
+
+ Return<Result> initializeCommon(
+ std::unique_ptr<EventMessageQueueWrapperBase>& eventQueue,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<ISensorsCallbackWrapperBase>& sensorsCallback);
Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
- int64_t maxReportLatencyNs) override;
+ int64_t maxReportLatencyNs);
- Return<Result> flush(int32_t sensorHandle) override;
+ Return<Result> flush(int32_t sensorHandle);
- Return<Result> injectSensorData(const Event& event) override;
+ Return<Result> injectSensorData(const V1_0::Event& event);
Return<void> registerDirectChannel(const SharedMemInfo& mem,
- registerDirectChannel_cb _hidl_cb) override;
+ ISensorsV2_0::registerDirectChannel_cb _hidl_cb);
- Return<Result> unregisterDirectChannel(int32_t channelHandle) override;
+ Return<Result> unregisterDirectChannel(int32_t channelHandle);
Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
- configDirectReport_cb _hidl_cb) override;
+ ISensorsV2_0::configDirectReport_cb _hidl_cb);
- Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args);
- // Below methods from ::android::hardware::sensors::V2_0::ISensorsCallback with a minor change
- // to pass in the sub-HAL index. While the above methods are invoked from the sensors framework
- // via the binder, these methods are invoked from a callback provided to sub-HALs inside the
- // same process as the HalProxy, but potentially running on different threads.
Return<void> onDynamicSensorsConnected(const hidl_vec<SensorInfo>& dynamicSensorsAdded,
- int32_t subHalIndex);
+ int32_t subHalIndex) override;
Return<void> onDynamicSensorsDisconnected(const hidl_vec<int32_t>& dynamicSensorHandlesRemoved,
- int32_t subHalIndex);
+ int32_t subHalIndex) override;
- // Below methods are for HalProxyCallback
-
- /**
- * Post events to the event message queue if there is room to write them. Otherwise post the
- * remaining events to a background thread for a blocking write with a kPendingWriteTimeoutNs
- * timeout.
- *
- * @param events The list of events to post to the message queue.
- * @param numWakeupEvents The number of wakeup events in events.
- * @param wakelock The wakelock associated with this post of events.
- */
void postEventsToMessageQueue(const std::vector<Event>& events, size_t numWakeupEvents,
- ScopedWakelock wakelock);
+ V2_0::implementation::ScopedWakelock wakelock) override;
- /**
- * Get the sensor info associated with that sensorHandle.
- *
- * @param sensorHandle The sensor handle.
- *
- * @return The sensor info object in the mapping.
- */
- const SensorInfo& getSensorInfo(int32_t sensorHandle) { return mSensors[sensorHandle]; }
+ const SensorInfo& getSensorInfo(int32_t sensorHandle) override {
+ return mSensors[sensorHandle];
+ }
- bool areThreadsRunning() { return mThreadsRun.load(); }
+ bool areThreadsRunning() override { return mThreadsRun.load(); }
// Below methods are from IScopedWakelockRefCounter interface
bool incrementRefCountAndMaybeAcquireWakelock(size_t delta,
@@ -136,13 +142,14 @@
void decrementRefCountAndMaybeReleaseWakelock(size_t delta, int64_t timeoutStart = -1) override;
private:
- using EventMessageQueue = MessageQueue<Event, kSynchronizedReadWrite>;
+ using EventMessageQueueV2_1 = MessageQueue<V2_1::Event, kSynchronizedReadWrite>;
+ using EventMessageQueueV2_0 = MessageQueue<V1_0::Event, kSynchronizedReadWrite>;
using WakeLockMessageQueue = MessageQueue<uint32_t, kSynchronizedReadWrite>;
/**
* The Event FMQ where sensor events are written
*/
- std::unique_ptr<EventMessageQueue> mEventQueue;
+ std::unique_ptr<EventMessageQueueWrapperBase> mEventQueue;
/**
* The Wake Lock FMQ that is read to determine when the framework has handled WAKE_UP events
@@ -161,15 +168,12 @@
/**
* Callback to the sensors framework to inform it that new sensors have been added or removed.
*/
- sp<ISensorsCallback> mDynamicSensorsCallback;
+ sp<ISensorsCallbackWrapperBase> mDynamicSensorsCallback;
/**
- * SubHal object pointers that have been saved from vendor dynamic libraries.
+ * SubHal objects that have been saved from vendor dynamic libraries.
*/
- std::vector<ISensorsSubHal*> mSubHalList;
-
- //! The list of subhal callbacks for each subhal where the indices correlate with mSubHalList
- std::vector<const sp<IHalProxyCallback>> mSubHalCallbacks;
+ std::vector<std::shared_ptr<ISubHalWrapperBase>> mSubHalList;
/**
* Map of sensor handles to SensorInfo objects that contains the sensor info from subhals as
@@ -187,7 +191,7 @@
OperationMode mCurrentOperationMode = OperationMode::NORMAL;
//! The single subHal that supports directChannel reporting.
- ISensorsSubHal* mDirectChannelSubHal = nullptr;
+ std::shared_ptr<ISubHalWrapperBase> mDirectChannelSubHal;
//! The timeout for each pending write on background thread for events.
static const int64_t kPendingWriteTimeoutNs = 5 * INT64_C(1000000000) /* 5 seconds */;
@@ -239,9 +243,9 @@
//! The refcount of how many ScopedWakelocks and pending wakeup events are active
size_t mWakelockRefCount = 0;
- int64_t mWakelockTimeoutStartTime = getTimeNow();
+ int64_t mWakelockTimeoutStartTime = V2_0::implementation::getTimeNow();
- int64_t mWakelockTimeoutResetTime = getTimeNow();
+ int64_t mWakelockTimeoutResetTime = V2_0::implementation::getTimeNow();
const char* kWakelockName = "SensorsHAL_WAKEUP";
@@ -263,6 +267,16 @@
void initializeSensorList();
/**
+ * Try using the default include directories as well as the directories defined in
+ * kSubHalShareObjectLocations to get a handle for dlsym for a subhal.
+ *
+ * @param filename The file name to search for.
+ *
+ * @return The handle or nullptr if search failed.
+ */
+ void* getHandleForSubHalSharedObject(const std::string& filename);
+
+ /**
* Calls the helper methods that all ctors use.
*/
void init();
@@ -321,7 +335,7 @@
* disabled.
* @param subHal The subhal pointer that the current sensorInfo object came from.
*/
- void setDirectChannelFlags(SensorInfo* sensorInfo, ISensorsSubHal* subHal);
+ void setDirectChannelFlags(SensorInfo* sensorInfo, std::shared_ptr<ISubHalWrapperBase> subHal);
/*
* Get the subhal pointer which can be found by indexing into the mSubHalList vector
@@ -329,7 +343,7 @@
*
* @param sensorHandle The handle used to identify a sensor in one of the subhals.
*/
- ISensorsSubHal* getSubHalForSensorHandle(int32_t sensorHandle);
+ std::shared_ptr<ISubHalWrapperBase> getSubHalForSensorHandle(int32_t sensorHandle);
/**
* Checks that sensorHandle's subhal index byte is within bounds of mSubHalList.
@@ -368,39 +382,81 @@
};
/**
- * Callback class used to provide the HalProxy with the index of which subHal is invoking
+ * Since a newer HAL can't masquerade as a older HAL, IHalProxy enables the HalProxy to be compiled
+ * either for HAL 2.0 or HAL 2.1 depending on the build configuration.
*/
-class HalProxyCallback : public IHalProxyCallback {
- using SensorInfo = ::android::hardware::sensors::V1_0::SensorInfo;
-
- public:
- HalProxyCallback(HalProxy* halProxy, int32_t subHalIndex)
- : mHalProxy(halProxy), mSubHalIndex(subHalIndex) {}
-
- Return<void> onDynamicSensorsConnected(
- const hidl_vec<SensorInfo>& dynamicSensorsAdded) override {
- return mHalProxy->onDynamicSensorsConnected(dynamicSensorsAdded, mSubHalIndex);
+template <class ISensorsVersion>
+class IHalProxy : public HalProxy, public ISensorsVersion {
+ Return<void> getSensorsList(ISensorsV2_0::getSensorsList_cb _hidl_cb) override {
+ return HalProxy::getSensorsList(_hidl_cb);
}
- Return<void> onDynamicSensorsDisconnected(
- const hidl_vec<int32_t>& dynamicSensorHandlesRemoved) override {
- return mHalProxy->onDynamicSensorsDisconnected(dynamicSensorHandlesRemoved, mSubHalIndex);
+ Return<Result> setOperationMode(OperationMode mode) override {
+ return HalProxy::setOperationMode(mode);
}
- void postEvents(const std::vector<Event>& events, ScopedWakelock wakelock);
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override {
+ return HalProxy::activate(sensorHandle, enabled);
+ }
- ScopedWakelock createScopedWakelock(bool lock);
+ Return<Result> initialize(
+ const ::android::hardware::MQDescriptorSync<V1_0::Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<V2_0::ISensorsCallback>& sensorsCallback) override {
+ return HalProxy::initialize(eventQueueDescriptor, wakeLockDescriptor, sensorsCallback);
+ }
- private:
- HalProxy* mHalProxy;
- int32_t mSubHalIndex;
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) override {
+ return HalProxy::batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
+ }
- std::vector<Event> processEvents(const std::vector<Event>& events,
- size_t* numWakeupEvents) const;
+ Return<Result> flush(int32_t sensorHandle) override { return HalProxy::flush(sensorHandle); }
+
+ Return<Result> injectSensorData(const V1_0::Event& event) override {
+ return HalProxy::injectSensorData(event);
+ }
+
+ Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ ISensorsV2_0::registerDirectChannel_cb _hidl_cb) override {
+ return HalProxy::registerDirectChannel(mem, _hidl_cb);
+ }
+
+ Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
+ return HalProxy::unregisterDirectChannel(channelHandle);
+ }
+
+ Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
+ ISensorsV2_0::configDirectReport_cb _hidl_cb) override {
+ return HalProxy::configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
+ }
+
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override {
+ return HalProxy::debug(fd, args);
+ }
+};
+
+class HalProxyV2_0 : public IHalProxy<V2_0::ISensors> {};
+
+class HalProxyV2_1 : public IHalProxy<V2_1::ISensors> {
+ Return<void> getSensorsList_2_1(ISensorsV2_1::getSensorsList_2_1_cb _hidl_cb) override {
+ return HalProxy::getSensorsList_2_1(_hidl_cb);
+ }
+
+ Return<Result> initialize_2_1(
+ const ::android::hardware::MQDescriptorSync<V2_1::Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<V2_1::ISensorsCallback>& sensorsCallback) override {
+ return HalProxy::initialize_2_1(eventQueueDescriptor, wakeLockDescriptor, sensorsCallback);
+ }
+
+ Return<Result> injectSensorData_2_1(const Event& event) override {
+ return HalProxy::injectSensorData_2_1(event);
+ }
};
} // namespace implementation
-} // namespace V2_0
+} // namespace V2_1
} // namespace sensors
} // namespace hardware
} // namespace android
diff --git a/sensors/common/default/2.X/multihal/include/HalProxyCallback.h b/sensors/common/default/2.X/multihal/include/HalProxyCallback.h
new file mode 100644
index 0000000..e62b7d1
--- /dev/null
+++ b/sensors/common/default/2.X/multihal/include/HalProxyCallback.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "V2_0/ScopedWakelock.h"
+#include "V2_0/SubHal.h"
+#include "V2_1/SubHal.h"
+#include "convertV2_1.h"
+
+#include <android/hardware/sensors/2.1/ISensors.h>
+#include <android/hardware/sensors/2.1/types.h>
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_0 {
+namespace implementation {
+
+/**
+ * Interface used to communicate with the HalProxy when subHals interact with their provided
+ * callback.
+ */
+class ISubHalCallback {
+ public:
+ virtual ~ISubHalCallback() {}
+
+ // Below methods from ::android::hardware::sensors::V2_0::ISensorsCallback with a minor change
+ // to pass in the sub-HAL index. While the above methods are invoked from the sensors framework
+ // via the binder, these methods are invoked from a callback provided to sub-HALs inside the
+ // same process as the HalProxy, but potentially running on different threads.
+ virtual Return<void> onDynamicSensorsConnected(
+ const hidl_vec<V2_1::SensorInfo>& dynamicSensorsAdded, int32_t subHalIndex) = 0;
+
+ virtual Return<void> onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t>& dynamicSensorHandlesRemoved, int32_t subHalIndex) = 0;
+
+ /**
+ * Post events to the event message queue if there is room to write them. Otherwise post the
+ * remaining events to a background thread for a blocking write with a kPendingWriteTimeoutNs
+ * timeout.
+ *
+ * @param events The list of events to post to the message queue.
+ * @param numWakeupEvents The number of wakeup events in events.
+ * @param wakelock The wakelock associated with this post of events.
+ */
+ virtual void postEventsToMessageQueue(const std::vector<V2_1::Event>& events,
+ size_t numWakeupEvents,
+ V2_0::implementation::ScopedWakelock wakelock) = 0;
+
+ /**
+ * Get the sensor info associated with that sensorHandle.
+ *
+ * @param sensorHandle The sensor handle.
+ *
+ * @return The sensor info object in the mapping.
+ */
+ virtual const V2_1::SensorInfo& getSensorInfo(int32_t sensorHandle) = 0;
+
+ virtual bool areThreadsRunning() = 0;
+};
+
+/**
+ * Callback class given to subhals that allows the HalProxy to know which subhal a given invocation
+ * is coming from.
+ */
+class HalProxyCallbackBase : public VirtualLightRefBase {
+ public:
+ HalProxyCallbackBase(ISubHalCallback* callback,
+ V2_0::implementation::IScopedWakelockRefCounter* refCounter,
+ int32_t subHalIndex)
+ : mCallback(callback), mRefCounter(refCounter), mSubHalIndex(subHalIndex) {}
+
+ void postEvents(const std::vector<V2_1::Event>& events,
+ V2_0::implementation::ScopedWakelock wakelock);
+
+ V2_0::implementation::ScopedWakelock createScopedWakelock(bool lock);
+
+ protected:
+ ISubHalCallback* mCallback;
+ V2_0::implementation::IScopedWakelockRefCounter* mRefCounter;
+ int32_t mSubHalIndex;
+
+ private:
+ std::vector<V2_1::Event> processEvents(const std::vector<V2_1::Event>& events,
+ size_t* numWakeupEvents) const;
+};
+
+class HalProxyCallbackV2_0 : public HalProxyCallbackBase,
+ public V2_0::implementation::IHalProxyCallback {
+ public:
+ HalProxyCallbackV2_0(ISubHalCallback* callback,
+ V2_0::implementation::IScopedWakelockRefCounter* refCounter,
+ int32_t subHalIndex)
+ : HalProxyCallbackBase(callback, refCounter, subHalIndex) {}
+
+ Return<void> onDynamicSensorsConnected(
+ const hidl_vec<V1_0::SensorInfo>& dynamicSensorsAdded) override {
+ return mCallback->onDynamicSensorsConnected(
+ V2_1::implementation::convertToNewSensorInfos(dynamicSensorsAdded), mSubHalIndex);
+ }
+
+ Return<void> onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t>& dynamicSensorHandlesRemoved) override {
+ return mCallback->onDynamicSensorsDisconnected(dynamicSensorHandlesRemoved, mSubHalIndex);
+ }
+
+ void postEvents(const std::vector<V1_0::Event>& events,
+ V2_0::implementation::ScopedWakelock wakelock) override {
+ HalProxyCallbackBase::postEvents(V2_1::implementation::convertToNewEvents(events),
+ std::move(wakelock));
+ }
+
+ V2_0::implementation::ScopedWakelock createScopedWakelock(bool lock) override {
+ return HalProxyCallbackBase::createScopedWakelock(lock);
+ }
+};
+
+class HalProxyCallbackV2_1 : public HalProxyCallbackBase,
+ public V2_1::implementation::IHalProxyCallback {
+ public:
+ HalProxyCallbackV2_1(ISubHalCallback* callback,
+ V2_0::implementation::IScopedWakelockRefCounter* refCounter,
+ int32_t subHalIndex)
+ : HalProxyCallbackBase(callback, refCounter, subHalIndex) {}
+
+ Return<void> onDynamicSensorsConnected_2_1(
+ const hidl_vec<V2_1::SensorInfo>& dynamicSensorsAdded) override {
+ return mCallback->onDynamicSensorsConnected(dynamicSensorsAdded, mSubHalIndex);
+ }
+
+ Return<void> onDynamicSensorsConnected(
+ const hidl_vec<V1_0::SensorInfo>& /* dynamicSensorsAdded */) override {
+ LOG_ALWAYS_FATAL("Old dynamic sensors method can't be used");
+ return Void();
+ }
+
+ Return<void> onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t>& dynamicSensorHandlesRemoved) override {
+ return mCallback->onDynamicSensorsDisconnected(dynamicSensorHandlesRemoved, mSubHalIndex);
+ }
+
+ void postEvents(const std::vector<V2_1::Event>& events,
+ V2_0::implementation::ScopedWakelock wakelock) override {
+ return HalProxyCallbackBase::postEvents(events, std::move(wakelock));
+ }
+
+ V2_0::implementation::ScopedWakelock createScopedWakelock(bool lock) override {
+ return HalProxyCallbackBase::createScopedWakelock(lock);
+ }
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace sensors
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/sensors/common/default/2.X/multihal/include/SubHalWrapper.h b/sensors/common/default/2.X/multihal/include/SubHalWrapper.h
new file mode 100644
index 0000000..149bb5e
--- /dev/null
+++ b/sensors/common/default/2.X/multihal/include/SubHalWrapper.h
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "HalProxyCallback.h"
+#include "V2_0/SubHal.h"
+#include "V2_1/SubHal.h"
+
+#include "android/hardware/sensors/1.0/ISensors.h"
+#include "android/hardware/sensors/1.0/types.h"
+#include "android/hardware/sensors/2.0/ISensors.h"
+#include "android/hardware/sensors/2.0/ISensorsCallback.h"
+#include "android/hardware/sensors/2.1/ISensors.h"
+#include "android/hardware/sensors/2.1/ISensorsCallback.h"
+#include "android/hardware/sensors/2.1/types.h"
+
+#include <utils/LightRefBase.h>
+
+#include <cassert>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+/**
+ * The following subHal wrapper classes abstract away common functionality across V2.0 and V2.1
+ * subHal interfaces. Much of the logic is common between the two versions and this allows users of
+ * the classes to only care about the type used at initialization and then interact with either
+ * version of the subHal interface without worrying about the type.
+ */
+class ISubHalWrapperBase {
+ protected:
+ using Event = ::android::hardware::sensors::V2_1::Event;
+ using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
+ using RateLevel = ::android::hardware::sensors::V1_0::RateLevel;
+ using Result = ::android::hardware::sensors::V1_0::Result;
+ using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
+ using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo;
+
+ public:
+ virtual ~ISubHalWrapperBase() {}
+
+ virtual bool supportsNewEvents() = 0;
+
+ virtual Return<Result> initialize(V2_0::implementation::ISubHalCallback* callback,
+ V2_0::implementation::IScopedWakelockRefCounter* refCounter,
+ int32_t subHalIndex) = 0;
+
+ virtual Return<void> getSensorsList(
+ ::android::hardware::sensors::V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb) = 0;
+
+ virtual Return<Result> setOperationMode(OperationMode mode) = 0;
+
+ virtual Return<Result> activate(int32_t sensorHandle, bool enabled) = 0;
+
+ virtual Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) = 0;
+
+ virtual Return<Result> flush(int32_t sensorHandle) = 0;
+
+ virtual Return<Result> injectSensorData(const Event& event) = 0;
+
+ virtual Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ ISensors::registerDirectChannel_cb _hidl_cb) = 0;
+
+ virtual Return<Result> unregisterDirectChannel(int32_t channelHandle) = 0;
+
+ virtual Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle,
+ RateLevel rate,
+ ISensors::configDirectReport_cb _hidl_cb) = 0;
+
+ virtual Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) = 0;
+
+ virtual const std::string getName() = 0;
+};
+
+template <typename T>
+class SubHalWrapperBase : public ISubHalWrapperBase {
+ public:
+ SubHalWrapperBase(T* subHal) : mSubHal(subHal){};
+
+ virtual bool supportsNewEvents() override { return false; }
+
+ virtual Return<void> getSensorsList(
+ ::android::hardware::sensors::V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb) override {
+ return mSubHal->getSensorsList(
+ [&](const auto& list) { _hidl_cb(convertToNewSensorInfos(list)); });
+ }
+
+ Return<Result> setOperationMode(OperationMode mode) override {
+ return mSubHal->setOperationMode(mode);
+ }
+
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override {
+ return mSubHal->activate(sensorHandle, enabled);
+ }
+
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) override {
+ return mSubHal->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
+ }
+
+ Return<Result> flush(int32_t sensorHandle) override { return mSubHal->flush(sensorHandle); }
+
+ virtual Return<Result> injectSensorData(const Event& event) override {
+ return mSubHal->injectSensorData(convertToOldEvent(event));
+ }
+
+ Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ ISensors::registerDirectChannel_cb _hidl_cb) override {
+ return mSubHal->registerDirectChannel(mem, _hidl_cb);
+ }
+
+ Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
+ return mSubHal->unregisterDirectChannel(channelHandle);
+ }
+
+ Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
+ ISensors::configDirectReport_cb _hidl_cb) override {
+ return mSubHal->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
+ }
+
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override {
+ return mSubHal->debug(fd, args);
+ }
+
+ const std::string getName() override { return mSubHal->getName(); }
+
+ protected:
+ T* mSubHal;
+};
+
+class SubHalWrapperV2_0 : public SubHalWrapperBase<V2_0::implementation::ISensorsSubHal> {
+ public:
+ SubHalWrapperV2_0(V2_0::implementation::ISensorsSubHal* subHal) : SubHalWrapperBase(subHal){};
+
+ Return<Result> initialize(V2_0::implementation::ISubHalCallback* callback,
+ V2_0::implementation::IScopedWakelockRefCounter* refCounter,
+ int32_t subHalIndex) override {
+ return mSubHal->initialize(
+ new V2_0::implementation::HalProxyCallbackV2_0(callback, refCounter, subHalIndex));
+ }
+};
+
+class SubHalWrapperV2_1 : public SubHalWrapperBase<V2_1::implementation::ISensorsSubHal> {
+ public:
+ SubHalWrapperV2_1(V2_1::implementation::ISensorsSubHal* subHal) : SubHalWrapperBase(subHal) {}
+
+ bool supportsNewEvents() override { return true; }
+
+ virtual Return<void> getSensorsList(
+ ::android::hardware::sensors::V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb) override {
+ return mSubHal->getSensorsList_2_1([&](const auto& list) { _hidl_cb(list); });
+ }
+
+ virtual Return<Result> injectSensorData(const Event& event) override {
+ return mSubHal->injectSensorData_2_1(event);
+ }
+
+ Return<Result> initialize(V2_0::implementation::ISubHalCallback* callback,
+ V2_0::implementation::IScopedWakelockRefCounter* refCounter,
+ int32_t subHalIndex) override {
+ return mSubHal->initialize(
+ new V2_0::implementation::HalProxyCallbackV2_1(callback, refCounter, subHalIndex));
+ }
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
diff --git a/sensors/common/default/2.X/multihal/include/V2_0/ScopedWakelock.h b/sensors/common/default/2.X/multihal/include/V2_0/ScopedWakelock.h
index aa6d9db..1cc5cd5 100644
--- a/sensors/common/default/2.X/multihal/include/V2_0/ScopedWakelock.h
+++ b/sensors/common/default/2.X/multihal/include/V2_0/ScopedWakelock.h
@@ -88,7 +88,7 @@
bool isLocked() const { return mLocked; }
private:
- friend class HalProxyCallback;
+ friend class HalProxyCallbackBase;
IScopedWakelockRefCounter* mRefCounter;
int64_t mCreatedAtTimeNs;
bool mLocked;
diff --git a/sensors/common/default/2.X/multihal/tests/Android.bp b/sensors/common/default/2.X/multihal/tests/Android.bp
index 7692b51..a15faed 100644
--- a/sensors/common/default/2.X/multihal/tests/Android.bp
+++ b/sensors/common/default/2.X/multihal/tests/Android.bp
@@ -20,6 +20,7 @@
],
header_libs: [
"android.hardware.sensors@2.0-multihal.header",
+ "android.hardware.sensors@2.X-shared-utils",
],
export_include_dirs: ["fake_subhal"],
shared_libs: [
@@ -36,6 +37,7 @@
"libutils",
],
static_libs: [
+ "android.hardware.sensors@1.0-convert",
"android.hardware.sensors@2.X-multihal",
],
cflags: [
@@ -48,6 +50,7 @@
vendor: true,
defaults: ["android.hardware.sensors@2.X-fakesubhal-defaults"],
cflags: [
+ "-DSUB_HAL_VERSION_2_0",
"-DSUPPORT_CONTINUOUS_SENSORS",
"-DSUB_HAL_NAME=\"FakeSubHal-Continuous\"",
],
@@ -58,6 +61,17 @@
vendor: true,
defaults: ["android.hardware.sensors@2.X-fakesubhal-defaults"],
cflags: [
+ "-DSUB_HAL_VERSION_2_0",
+ "-DSUPPORT_ON_CHANGE_SENSORS",
+ "-DSUB_HAL_NAME=\"FakeSubHal-OnChange\"",
+ ],
+}
+
+cc_library {
+ name: "android.hardware.sensors@2.X-fakesubhal-config3",
+ vendor: true,
+ defaults: ["android.hardware.sensors@2.X-fakesubhal-defaults"],
+ cflags: [
"-DSUPPORT_ON_CHANGE_SENSORS",
"-DSUB_HAL_NAME=\"FakeSubHal-OnChange\"",
],
@@ -78,7 +92,11 @@
name: "android.hardware.sensors@2.X-halproxy-unit-tests",
srcs: ["HalProxy_test.cpp"],
vendor: true,
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
static_libs: [
+ "android.hardware.sensors@1.0-convert",
"android.hardware.sensors@2.0-ScopedWakelock.testlib",
"android.hardware.sensors@2.X-multihal",
"android.hardware.sensors@2.X-fakesubhal-unittest",
@@ -86,7 +104,6 @@
shared_libs: [
"android.hardware.sensors@1.0",
"android.hardware.sensors@2.0",
- "android.hardware.sensors@2.0-ScopedWakelock",
"android.hardware.sensors@2.1",
"libbase",
"libcutils",
diff --git a/sensors/common/default/2.X/multihal/tests/HalProxy_test.cpp b/sensors/common/default/2.X/multihal/tests/HalProxy_test.cpp
index 867c4a1..858786a 100644
--- a/sensors/common/default/2.X/multihal/tests/HalProxy_test.cpp
+++ b/sensors/common/default/2.X/multihal/tests/HalProxy_test.cpp
@@ -15,12 +15,15 @@
#include <gtest/gtest.h>
+#include <android/hardware/sensors/1.0/types.h>
#include <android/hardware/sensors/2.0/types.h>
+#include <android/hardware/sensors/2.1/types.h>
#include <fmq/MessageQueue.h>
#include "HalProxy.h"
#include "SensorsSubHal.h"
#include "V2_0/ScopedWakelock.h"
+#include "convertV2_1.h"
#include <chrono>
#include <set>
@@ -38,27 +41,35 @@
using ::android::hardware::sensors::V1_0::SensorInfo;
using ::android::hardware::sensors::V1_0::SensorType;
using ::android::hardware::sensors::V2_0::EventQueueFlagBits;
-using ::android::hardware::sensors::V2_0::ISensorsCallback;
using ::android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
-using ::android::hardware::sensors::V2_0::implementation::HalProxy;
-using ::android::hardware::sensors::V2_0::implementation::HalProxyCallback;
-using ::android::hardware::sensors::V2_0::subhal::implementation::AddAndRemoveDynamicSensorsSubHal;
-using ::android::hardware::sensors::V2_0::subhal::implementation::AllSensorsSubHal;
-using ::android::hardware::sensors::V2_0::subhal::implementation::
+using ::android::hardware::sensors::V2_0::implementation::HalProxyCallbackBase;
+using ::android::hardware::sensors::V2_0::implementation::ScopedWakelock;
+using ::android::hardware::sensors::V2_1::implementation::convertToNewEvents;
+using ::android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos;
+using ::android::hardware::sensors::V2_1::implementation::HalProxy;
+using ::android::hardware::sensors::V2_1::subhal::implementation::AddAndRemoveDynamicSensorsSubHal;
+using ::android::hardware::sensors::V2_1::subhal::implementation::AllSensorsSubHal;
+using ::android::hardware::sensors::V2_1::subhal::implementation::
AllSupportDirectChannelSensorsSubHal;
-using ::android::hardware::sensors::V2_0::subhal::implementation::ContinuousSensorsSubHal;
-using ::android::hardware::sensors::V2_0::subhal::implementation::
+using ::android::hardware::sensors::V2_1::subhal::implementation::ContinuousSensorsSubHal;
+using ::android::hardware::sensors::V2_1::subhal::implementation::
DoesNotSupportDirectChannelSensorsSubHal;
-using ::android::hardware::sensors::V2_0::subhal::implementation::OnChangeSensorsSubHal;
-using ::android::hardware::sensors::V2_0::subhal::implementation::SensorsSubHal;
-using ::android::hardware::sensors::V2_0::subhal::implementation::
+using ::android::hardware::sensors::V2_1::subhal::implementation::OnChangeSensorsSubHal;
+using ::android::hardware::sensors::V2_1::subhal::implementation::SensorsSubHalV2_0;
+using ::android::hardware::sensors::V2_1::subhal::implementation::SensorsSubHalV2_1;
+using ::android::hardware::sensors::V2_1::subhal::implementation::
SetOperationModeFailingSensorsSubHal;
-using EventMessageQueue = MessageQueue<Event, ::android::hardware::kSynchronizedReadWrite>;
+using ISensorsCallbackV2_0 = ::android::hardware::sensors::V2_0::ISensorsCallback;
+using ISensorsCallbackV2_1 = ::android::hardware::sensors::V2_1::ISensorsCallback;
+using EventV1_0 = ::android::hardware::sensors::V1_0::Event;
+using EventV2_1 = ::android::hardware::sensors::V2_1::Event;
+using EventMessageQueueV2_1 = MessageQueue<EventV2_1, ::android::hardware::kSynchronizedReadWrite>;
+using EventMessageQueueV2_0 = MessageQueue<EventV1_0, ::android::hardware::kSynchronizedReadWrite>;
using WakeupMessageQueue = MessageQueue<uint32_t, ::android::hardware::kSynchronizedReadWrite>;
// The barebones sensors callback class passed into halproxy initialize calls
-class SensorsCallback : public ISensorsCallback {
+class SensorsCallback : public ISensorsCallbackV2_0 {
public:
Return<void> onDynamicSensorsConnected(
const hidl_vec<SensorInfo>& /*dynamicSensorsAdded*/) override {
@@ -73,8 +84,30 @@
}
};
+class SensorsCallbackV2_1 : public ISensorsCallbackV2_1 {
+ public:
+ Return<void> onDynamicSensorsConnected_2_1(
+ const hidl_vec<::android::hardware::sensors::V2_1::SensorInfo>& /*dynamicSensorsAdded*/)
+ override {
+ // Nothing yet
+ return Return<void>();
+ }
+
+ Return<void> onDynamicSensorsConnected(
+ const hidl_vec<SensorInfo>& /*dynamicSensorsAdded*/) override {
+ // Nothing yet
+ return Return<void>();
+ }
+
+ Return<void> onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t>& /*dynamicSensorHandlesRemoved*/) override {
+ // Nothing yet
+ return Return<void>();
+ }
+};
+
// The sensors callback that expects a variable list of sensors to be added
-class TestSensorsCallback : public ISensorsCallback {
+class TestSensorsCallback : public ISensorsCallbackV2_0 {
public:
Return<void> onDynamicSensorsConnected(
const hidl_vec<SensorInfo>& dynamicSensorsAdded) override {
@@ -129,10 +162,10 @@
void ackWakeupEventsToHalProxy(size_t numEvents, std::unique_ptr<WakeupMessageQueue>& wakelockQueue,
EventFlag* wakelockQueueFlag);
-bool readEventsOutOfQueue(size_t numEvents, std::unique_ptr<EventMessageQueue>& eventQueue,
+bool readEventsOutOfQueue(size_t numEvents, std::unique_ptr<EventMessageQueueV2_0>& eventQueue,
EventFlag* eventQueueFlag);
-std::unique_ptr<EventMessageQueue> makeEventFMQ(size_t size);
+std::unique_ptr<EventMessageQueueV2_0> makeEventFMQ(size_t size);
std::unique_ptr<WakeupMessageQueue> makeWakelockFMQ(size_t size);
@@ -142,7 +175,7 @@
*
* @return A proximity event.
*/
-Event makeProximityEvent();
+EventV1_0 makeProximityEvent();
/**
* Construct and return a HIDL Event type thats sensorHandle refers to a proximity sensor
@@ -150,7 +183,7 @@
*
* @return A proximity event.
*/
-Event makeAccelerometerEvent();
+EventV1_0 makeAccelerometerEvent();
/**
* Make a certain number of proximity type events with the sensorHandle field set to
@@ -160,7 +193,7 @@
*
* @return The created list of events.
*/
-std::vector<Event> makeMultipleProximityEvents(size_t numEvents);
+std::vector<EventV1_0> makeMultipleProximityEvents(size_t numEvents);
/**
* Make a certain number of accelerometer type events with the sensorHandle field set to
@@ -170,7 +203,7 @@
*
* @return The created list of events.
*/
-std::vector<Event> makeMultipleAccelerometerEvents(size_t numEvents);
+std::vector<EventV1_0> makeMultipleAccelerometerEvents(size_t numEvents);
/**
* Given a SensorInfo vector and a sensor handles vector populate 'sensors' with SensorInfo
@@ -188,7 +221,7 @@
// Tests follow
TEST(HalProxyTest, GetSensorsListOneSubHalTest) {
- AllSensorsSubHal subHal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal;
std::vector<ISensorsSubHal*> fakeSubHals{&subHal};
HalProxy proxy(fakeSubHals);
@@ -200,8 +233,8 @@
}
TEST(HalProxyTest, GetSensorsListTwoSubHalTest) {
- ContinuousSensorsSubHal continuousSubHal;
- OnChangeSensorsSubHal onChangeSubHal;
+ ContinuousSensorsSubHal<SensorsSubHalV2_0> continuousSubHal;
+ OnChangeSensorsSubHal<SensorsSubHalV2_0> onChangeSubHal;
std::vector<ISensorsSubHal*> fakeSubHals;
fakeSubHals.push_back(&continuousSubHal);
fakeSubHals.push_back(&onChangeSubHal);
@@ -221,8 +254,8 @@
}
TEST(HalProxyTest, SetOperationModeTwoSubHalSuccessTest) {
- ContinuousSensorsSubHal subHal1;
- OnChangeSensorsSubHal subHal2;
+ ContinuousSensorsSubHal<SensorsSubHalV2_0> subHal1;
+ OnChangeSensorsSubHal<SensorsSubHalV2_0> subHal2;
std::vector<ISensorsSubHal*> fakeSubHals{&subHal1, &subHal2};
HalProxy proxy(fakeSubHals);
@@ -238,7 +271,7 @@
}
TEST(HalProxyTest, SetOperationModeTwoSubHalFailTest) {
- AllSensorsSubHal subHal1;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal1;
SetOperationModeFailingSensorsSubHal subHal2;
std::vector<ISensorsSubHal*> fakeSubHals{&subHal1, &subHal2};
@@ -279,16 +312,16 @@
TEST(HalProxyTest, PostSingleNonWakeupEvent) {
constexpr size_t kQueueSize = 5;
- AllSensorsSubHal subHal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
HalProxy proxy(subHals);
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
- std::vector<Event> events{makeAccelerometerEvent()};
- subHal.postEvents(events, false /* wakeup */);
+ std::vector<EventV1_0> events{makeAccelerometerEvent()};
+ subHal.postEvents(convertToNewEvents(events), false /* wakeup */);
EXPECT_EQ(eventQueue->availableToRead(), 1);
}
@@ -296,28 +329,28 @@
TEST(HalProxyTest, PostMultipleNonWakeupEvent) {
constexpr size_t kQueueSize = 5;
constexpr size_t kNumEvents = 3;
- AllSensorsSubHal subHal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
HalProxy proxy(subHals);
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
- std::vector<Event> events = makeMultipleAccelerometerEvents(kNumEvents);
- subHal.postEvents(events, false /* wakeup */);
+ std::vector<EventV1_0> events = makeMultipleAccelerometerEvents(kNumEvents);
+ subHal.postEvents(convertToNewEvents(events), false /* wakeup */);
EXPECT_EQ(eventQueue->availableToRead(), kNumEvents);
}
TEST(HalProxyTest, PostSingleWakeupEvent) {
constexpr size_t kQueueSize = 5;
- AllSensorsSubHal subHal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
HalProxy proxy(subHals);
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
EventFlag* eventQueueFlag;
@@ -326,8 +359,8 @@
EventFlag* wakelockQueueFlag;
EventFlag::createEventFlag(wakeLockQueue->getEventFlagWord(), &wakelockQueueFlag);
- std::vector<Event> events{makeProximityEvent()};
- subHal.postEvents(events, true /* wakeup */);
+ std::vector<EventV1_0> events{makeProximityEvent()};
+ subHal.postEvents(convertToNewEvents(events), true /* wakeup */);
EXPECT_EQ(eventQueue->availableToRead(), 1);
@@ -338,12 +371,12 @@
TEST(HalProxyTest, PostMultipleWakeupEvents) {
constexpr size_t kQueueSize = 5;
constexpr size_t kNumEvents = 3;
- AllSensorsSubHal subHal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
HalProxy proxy(subHals);
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
EventFlag* eventQueueFlag;
@@ -352,8 +385,8 @@
EventFlag* wakelockQueueFlag;
EventFlag::createEventFlag(wakeLockQueue->getEventFlagWord(), &wakelockQueueFlag);
- std::vector<Event> events = makeMultipleProximityEvents(kNumEvents);
- subHal.postEvents(events, true /* wakeup */);
+ std::vector<EventV1_0> events = makeMultipleProximityEvents(kNumEvents);
+ subHal.postEvents(convertToNewEvents(events), true /* wakeup */);
EXPECT_EQ(eventQueue->availableToRead(), kNumEvents);
@@ -364,20 +397,20 @@
TEST(HalProxyTest, PostEventsMultipleSubhals) {
constexpr size_t kQueueSize = 5;
constexpr size_t kNumEvents = 2;
- AllSensorsSubHal subHal1, subHal2;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal1, subHal2;
std::vector<ISensorsSubHal*> subHals{&subHal1, &subHal2};
HalProxy proxy(subHals);
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
- std::vector<Event> events = makeMultipleAccelerometerEvents(kNumEvents);
- subHal1.postEvents(events, false /* wakeup */);
+ std::vector<EventV1_0> events = makeMultipleAccelerometerEvents(kNumEvents);
+ subHal1.postEvents(convertToNewEvents(events), false /* wakeup */);
EXPECT_EQ(eventQueue->availableToRead(), kNumEvents);
- subHal2.postEvents(events, false /* wakeup */);
+ subHal2.postEvents(convertToNewEvents(events), false /* wakeup */);
EXPECT_EQ(eventQueue->availableToRead(), kNumEvents * 2);
}
@@ -385,19 +418,19 @@
TEST(HalProxyTest, PostEventsDelayedWrite) {
constexpr size_t kQueueSize = 5;
constexpr size_t kNumEvents = 6;
- AllSensorsSubHal subHal1, subHal2;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal1, subHal2;
std::vector<ISensorsSubHal*> subHals{&subHal1, &subHal2};
HalProxy proxy(subHals);
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
EventFlag* eventQueueFlag;
EventFlag::createEventFlag(eventQueue->getEventFlagWord(), &eventQueueFlag);
- std::vector<Event> events = makeMultipleAccelerometerEvents(kNumEvents);
- subHal1.postEvents(events, false /* wakeup */);
+ std::vector<EventV1_0> events = makeMultipleAccelerometerEvents(kNumEvents);
+ subHal1.postEvents(convertToNewEvents(events), false /* wakeup */);
EXPECT_EQ(eventQueue->availableToRead(), kQueueSize);
@@ -413,18 +446,20 @@
TEST(HalProxyTest, PostEventsMultipleSubhalsThreaded) {
constexpr size_t kQueueSize = 5;
constexpr size_t kNumEvents = 2;
- AllSensorsSubHal subHal1, subHal2;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal1, subHal2;
std::vector<ISensorsSubHal*> subHals{&subHal1, &subHal2};
HalProxy proxy(subHals);
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
- std::vector<Event> events = makeMultipleAccelerometerEvents(kNumEvents);
+ std::vector<EventV1_0> events = makeMultipleAccelerometerEvents(kNumEvents);
- std::thread t1(&AllSensorsSubHal::postEvents, &subHal1, events, false);
- std::thread t2(&AllSensorsSubHal::postEvents, &subHal2, events, false);
+ std::thread t1(&AllSensorsSubHal<SensorsSubHalV2_0>::postEvents, &subHal1,
+ convertToNewEvents(events), false);
+ std::thread t2(&AllSensorsSubHal<SensorsSubHalV2_0>::postEvents, &subHal2,
+ convertToNewEvents(events), false);
t1.join();
t2.join();
@@ -435,34 +470,34 @@
TEST(HalProxyTest, DestructingWithEventsPendingOnBackgroundThread) {
constexpr size_t kQueueSize = 5;
constexpr size_t kNumEvents = 6;
- AllSensorsSubHal subHal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
HalProxy proxy(subHals);
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
- std::vector<Event> events = makeMultipleAccelerometerEvents(kNumEvents);
- subHal.postEvents(events, false /* wakeup */);
+ std::vector<EventV1_0> events = makeMultipleAccelerometerEvents(kNumEvents);
+ subHal.postEvents(convertToNewEvents(events), false /* wakeup */);
// Destructing HalProxy object with events on the background thread
}
TEST(HalProxyTest, DestructingWithUnackedWakeupEventsPosted) {
constexpr size_t kQueueSize = 5;
- AllSensorsSubHal subHal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
HalProxy proxy(subHals);
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
- std::vector<Event> events{makeProximityEvent()};
- subHal.postEvents(events, true /* wakeup */);
+ std::vector<EventV1_0> events{makeProximityEvent()};
+ subHal.postEvents(convertToNewEvents(events), true /* wakeup */);
// Not sending any acks back through wakeLockQueue
@@ -472,17 +507,17 @@
TEST(HalProxyTest, ReinitializeWithEventsPendingOnBackgroundThread) {
constexpr size_t kQueueSize = 5;
constexpr size_t kNumEvents = 10;
- AllSensorsSubHal subHal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
HalProxy proxy(subHals);
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
- std::vector<Event> events = makeMultipleAccelerometerEvents(kNumEvents);
- subHal.postEvents(events, false /* wakeup */);
+ std::vector<EventV1_0> events = makeMultipleAccelerometerEvents(kNumEvents);
+ subHal.postEvents(convertToNewEvents(events), false /* wakeup */);
eventQueue = makeEventFMQ(kQueueSize);
wakeLockQueue = makeWakelockFMQ(kQueueSize);
@@ -492,23 +527,23 @@
EXPECT_EQ(secondInitResult, Result::OK);
// Small sleep so that pending writes thread has a change to hit writeBlocking call.
std::this_thread::sleep_for(std::chrono::milliseconds(5));
- Event eventOut;
+ EventV1_0 eventOut;
EXPECT_FALSE(eventQueue->read(&eventOut));
}
TEST(HalProxyTest, ReinitializingWithUnackedWakeupEventsPosted) {
constexpr size_t kQueueSize = 5;
- AllSensorsSubHal subHal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
HalProxy proxy(subHals);
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
- std::vector<Event> events{makeProximityEvent()};
- subHal.postEvents(events, true /* wakeup */);
+ std::vector<EventV1_0> events{makeProximityEvent()};
+ subHal.postEvents(convertToNewEvents(events), true /* wakeup */);
// Not sending any acks back through wakeLockQueue
@@ -523,12 +558,12 @@
TEST(HalProxyTest, InitializeManyTimesInARow) {
constexpr size_t kQueueSize = 5;
constexpr size_t kNumTimesToInit = 100;
- AllSensorsSubHal subHal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
HalProxy proxy(subHals);
for (size_t i = 0; i < kNumTimesToInit; i++) {
@@ -540,15 +575,15 @@
TEST(HalProxyTest, OperationModeResetOnInitialize) {
constexpr size_t kQueueSize = 5;
- AllSensorsSubHal subHal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
HalProxy proxy(subHals);
proxy.setOperationMode(OperationMode::DATA_INJECTION);
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
- Event event = makeAccelerometerEvent();
+ EventV1_0 event = makeAccelerometerEvent();
// Should not be able to inject a non AdditionInfo type event because operation mode should
// have been reset to NORMAL
EXPECT_EQ(proxy.injectSensorData(event), Result::BAD_VALUE);
@@ -559,7 +594,7 @@
constexpr size_t kNumSensors = 5;
AddAndRemoveDynamicSensorsSubHal subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
HalProxy proxy(subHals);
@@ -574,9 +609,9 @@
}
TestSensorsCallback* callback = new TestSensorsCallback();
- ::android::sp<ISensorsCallback> callbackPtr = callback;
+ ::android::sp<ISensorsCallbackV2_0> callbackPtr = callback;
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr);
- subHal.addDynamicSensors(sensorsToConnect);
+ subHal.addDynamicSensors(convertToNewSensorInfos(sensorsToConnect));
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr);
subHal.removeDynamicSensors(sensorHandlesToAttemptToRemove);
@@ -593,7 +628,7 @@
AddAndRemoveDynamicSensorsSubHal subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
HalProxy proxy(subHals);
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(0);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(0);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(0);
std::vector<SensorInfo> sensorsToConnect;
@@ -602,9 +637,9 @@
sensorHandlesToExpect);
TestSensorsCallback* callback = new TestSensorsCallback();
- ::android::sp<ISensorsCallback> callbackPtr = callback;
+ ::android::sp<ISensorsCallbackV2_0> callbackPtr = callback;
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr);
- subHal.addDynamicSensors(sensorsToConnect);
+ subHal.addDynamicSensors(convertToNewSensorInfos(sensorsToConnect));
std::vector<SensorInfo> sensorsSeen = callback->getSensorsConnected();
EXPECT_EQ(kNumSensors, sensorsSeen.size());
@@ -621,7 +656,7 @@
AddAndRemoveDynamicSensorsSubHal subHal;
std::vector<ISensorsSubHal*> subHals{&subHal};
HalProxy proxy(subHals);
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(0);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(0);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(0);
std::vector<SensorInfo> sensorsToConnect;
@@ -646,9 +681,9 @@
nonDynamicSensorHandles.end());
TestSensorsCallback* callback = new TestSensorsCallback();
- ::android::sp<ISensorsCallback> callbackPtr = callback;
+ ::android::sp<ISensorsCallbackV2_0> callbackPtr = callback;
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callbackPtr);
- subHal.addDynamicSensors(sensorsToConnect);
+ subHal.addDynamicSensors(convertToNewSensorInfos(sensorsToConnect));
subHal.removeDynamicSensors(sensorHandlesToAttemptToRemove);
std::vector<int32_t> sensorHandlesSeen = callback->getSensorHandlesDisconnected();
@@ -667,15 +702,15 @@
constexpr size_t kNumSubHals = 3;
constexpr size_t kQueueSize = 5;
int32_t kNumSubHalsInt32 = static_cast<int32_t>(kNumSubHals);
- std::vector<AllSensorsSubHal> subHalObjs(kNumSubHals);
+ std::vector<AllSensorsSubHal<SensorsSubHalV2_0>> subHalObjs(kNumSubHals);
std::vector<ISensorsSubHal*> subHals;
for (const auto& subHal : subHalObjs) {
subHals.push_back((ISensorsSubHal*)(&subHal));
}
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
HalProxy proxy(subHals);
// Initialize for the injectSensorData call so callback postEvents is valid
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
@@ -687,7 +722,7 @@
EXPECT_EQ(proxy.activate(0x00000001 | (kNumSubHalsInt32 << 24), true), Result::BAD_VALUE);
EXPECT_EQ(proxy.batch(0x00000001 | (kNumSubHalsInt32 << 24), 0, 0), Result::BAD_VALUE);
EXPECT_EQ(proxy.flush(0x00000001 | (kNumSubHalsInt32 << 24)), Result::BAD_VALUE);
- Event event;
+ EventV1_0 event;
event.sensorHandle = 0x00000001 | (kNumSubHalsInt32 << 24);
EXPECT_EQ(proxy.injectSensorData(event), Result::BAD_VALUE);
}
@@ -696,28 +731,28 @@
constexpr size_t kQueueSize = 5;
constexpr int32_t subhal1Index = 0;
constexpr int32_t subhal2Index = 1;
- AllSensorsSubHal subhal1;
- AllSensorsSubHal subhal2;
+ AllSensorsSubHal<SensorsSubHalV2_0> subhal1;
+ AllSensorsSubHal<SensorsSubHalV2_0> subhal2;
std::vector<ISensorsSubHal*> subHals{&subhal1, &subhal2};
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
HalProxy proxy(subHals);
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
int32_t sensorHandleToPost = 0x00000001;
- Event eventIn = makeAccelerometerEvent();
+ EventV1_0 eventIn = makeAccelerometerEvent();
eventIn.sensorHandle = sensorHandleToPost;
- std::vector<Event> eventsToPost{eventIn};
- subhal1.postEvents(eventsToPost, false);
+ std::vector<EventV1_0> eventsToPost{eventIn};
+ subhal1.postEvents(convertToNewEvents(eventsToPost), false);
- Event eventOut;
+ EventV1_0 eventOut;
EXPECT_TRUE(eventQueue->read(&eventOut));
EXPECT_EQ(eventOut.sensorHandle, (subhal1Index << 24) | sensorHandleToPost);
- subhal2.postEvents(eventsToPost, false);
+ subhal2.postEvents(convertToNewEvents(eventsToPost), false);
EXPECT_TRUE(eventQueue->read(&eventOut));
@@ -728,22 +763,22 @@
constexpr size_t kQueueSize = 5;
// TODO: Make this constant linked to same limit in HalProxy.h
constexpr size_t kMaxPendingQueueSize = 100000;
- AllSensorsSubHal subhal;
+ AllSensorsSubHal<SensorsSubHalV2_0> subhal;
std::vector<ISensorsSubHal*> subHals{&subhal};
- std::unique_ptr<EventMessageQueue> eventQueue = makeEventFMQ(kQueueSize);
+ std::unique_ptr<EventMessageQueueV2_0> eventQueue = makeEventFMQ(kQueueSize);
std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
- ::android::sp<ISensorsCallback> callback = new SensorsCallback();
+ ::android::sp<ISensorsCallbackV2_0> callback = new SensorsCallback();
EventFlag* eventQueueFlag;
EventFlag::createEventFlag(eventQueue->getEventFlagWord(), &eventQueueFlag);
HalProxy proxy(subHals);
proxy.initialize(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
// Fill pending queue
- std::vector<Event> events = makeMultipleAccelerometerEvents(kQueueSize);
- subhal.postEvents(events, false);
+ std::vector<EventV1_0> events = makeMultipleAccelerometerEvents(kQueueSize);
+ subhal.postEvents(convertToNewEvents(events), false);
events = makeMultipleAccelerometerEvents(kMaxPendingQueueSize);
- subhal.postEvents(events, false);
+ subhal.postEvents(convertToNewEvents(events), false);
// Drain pending queue
for (int i = 0; i < kMaxPendingQueueSize + kQueueSize; i += kQueueSize) {
@@ -752,9 +787,9 @@
// Put one event on pending queue
events = makeMultipleAccelerometerEvents(kQueueSize);
- subhal.postEvents(events, false);
+ subhal.postEvents(convertToNewEvents(events), false);
events = {makeAccelerometerEvent()};
- subhal.postEvents(events, false);
+ subhal.postEvents(convertToNewEvents(events), false);
// Read out to make room for one event on pending queue to write to FMQ
ASSERT_TRUE(readEventsOutOfQueue(kQueueSize, eventQueue, eventQueueFlag));
@@ -763,6 +798,35 @@
EXPECT_TRUE(readEventsOutOfQueue(1, eventQueue, eventQueueFlag));
}
+TEST(HalProxyTest, PostEventsMultipleSubhalsThreadedV2_1) {
+ constexpr size_t kQueueSize = 5;
+ constexpr size_t kNumEvents = 2;
+ AllSensorsSubHal<SensorsSubHalV2_0> subHal1;
+ AllSensorsSubHal<SensorsSubHalV2_1> subHal2;
+ std::vector<::android::hardware::sensors::V2_0::implementation::ISensorsSubHal*> subHalsV2_0{
+ &subHal1};
+ std::vector<::android::hardware::sensors::V2_1::implementation::ISensorsSubHal*> subHalsV2_1{
+ &subHal2};
+ HalProxy proxy(subHalsV2_0, subHalsV2_1);
+ std::unique_ptr<EventMessageQueueV2_1> eventQueue =
+ std::make_unique<EventMessageQueueV2_1>(kQueueSize, true);
+ std::unique_ptr<WakeupMessageQueue> wakeLockQueue = makeWakelockFMQ(kQueueSize);
+ ::android::sp<ISensorsCallbackV2_1> callback = new SensorsCallbackV2_1();
+ proxy.initialize_2_1(*eventQueue->getDesc(), *wakeLockQueue->getDesc(), callback);
+
+ std::vector<EventV1_0> events = makeMultipleAccelerometerEvents(kNumEvents);
+
+ std::thread t1(&AllSensorsSubHal<SensorsSubHalV2_0>::postEvents, &subHal1,
+ convertToNewEvents(events), false);
+ std::thread t2(&AllSensorsSubHal<SensorsSubHalV2_1>::postEvents, &subHal2,
+ convertToNewEvents(events), false);
+
+ t1.join();
+ t2.join();
+
+ EXPECT_EQ(eventQueue->availableToRead(), kNumEvents * 2);
+}
+
// Helper implementations follow
void testSensorsListFromProxyAndSubHal(const std::vector<SensorInfo>& proxySensorsList,
const std::vector<SensorInfo>& subHalSensorsList) {
@@ -801,26 +865,26 @@
wakelockQueueFlag->wake(static_cast<uint32_t>(WakeLockQueueFlagBits::DATA_WRITTEN));
}
-bool readEventsOutOfQueue(size_t numEvents, std::unique_ptr<EventMessageQueue>& eventQueue,
+bool readEventsOutOfQueue(size_t numEvents, std::unique_ptr<EventMessageQueueV2_0>& eventQueue,
EventFlag* eventQueueFlag) {
constexpr int64_t kReadBlockingTimeout = INT64_C(500000000);
- std::vector<Event> events(numEvents);
+ std::vector<EventV1_0> events(numEvents);
return eventQueue->readBlocking(events.data(), numEvents,
static_cast<uint32_t>(EventQueueFlagBits::EVENTS_READ),
static_cast<uint32_t>(EventQueueFlagBits::READ_AND_PROCESS),
kReadBlockingTimeout, eventQueueFlag);
}
-std::unique_ptr<EventMessageQueue> makeEventFMQ(size_t size) {
- return std::make_unique<EventMessageQueue>(size, true);
+std::unique_ptr<EventMessageQueueV2_0> makeEventFMQ(size_t size) {
+ return std::make_unique<EventMessageQueueV2_0>(size, true);
}
std::unique_ptr<WakeupMessageQueue> makeWakelockFMQ(size_t size) {
return std::make_unique<WakeupMessageQueue>(size, true);
}
-Event makeProximityEvent() {
- Event event;
+EventV1_0 makeProximityEvent() {
+ EventV1_0 event;
event.timestamp = 0xFF00FF00;
// This is the sensorhandle of proximity, which is wakeup type
event.sensorHandle = 0x00000008;
@@ -829,8 +893,8 @@
return event;
}
-Event makeAccelerometerEvent() {
- Event event;
+EventV1_0 makeAccelerometerEvent() {
+ EventV1_0 event;
event.timestamp = 0xFF00FF00;
// This is the sensorhandle of proximity, which is wakeup type
event.sensorHandle = 0x00000001;
@@ -839,16 +903,16 @@
return event;
}
-std::vector<Event> makeMultipleProximityEvents(size_t numEvents) {
- std::vector<Event> events;
+std::vector<EventV1_0> makeMultipleProximityEvents(size_t numEvents) {
+ std::vector<EventV1_0> events;
for (size_t i = 0; i < numEvents; i++) {
events.push_back(makeProximityEvent());
}
return events;
}
-std::vector<Event> makeMultipleAccelerometerEvents(size_t numEvents) {
- std::vector<Event> events;
+std::vector<EventV1_0> makeMultipleAccelerometerEvents(size_t numEvents) {
+ std::vector<EventV1_0> events;
for (size_t i = 0; i < numEvents; i++) {
events.push_back(makeAccelerometerEvent());
}
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/IHalProxyCallbackWrapper.h b/sensors/common/default/2.X/multihal/tests/fake_subhal/IHalProxyCallbackWrapper.h
new file mode 100644
index 0000000..4542bfd
--- /dev/null
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/IHalProxyCallbackWrapper.h
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "V2_0/SubHal.h"
+#include "V2_1/SubHal.h"
+#include "convertV2_1.h"
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace subhal {
+namespace implementation {
+
+/**
+ * The following callback wrapper classes abstract away common functionality across V2.0 and V2.1
+ * interfaces. Much of the logic is common between the two versions and this allows users of the
+ * classes to only care about the type used at initialization and then interact with either version
+ * of the callback interface without worrying about the type.
+ */
+class IHalProxyCallbackWrapperBase {
+ protected:
+ using ScopedWakelock = V2_0::implementation::ScopedWakelock;
+
+ public:
+ virtual ~IHalProxyCallbackWrapperBase() {}
+
+ virtual Return<void> onDynamicSensorsConnected(
+ const hidl_vec<V2_1::SensorInfo>& sensorInfos) = 0;
+
+ virtual Return<void> onDynamicSensorsDisconnected(const hidl_vec<int32_t>& sensorHandles) = 0;
+
+ virtual void postEvents(const std::vector<V2_1::Event>& events, ScopedWakelock wakelock) = 0;
+
+ virtual ScopedWakelock createScopedWakelock(bool lock) = 0;
+};
+
+template <typename T>
+class HalProxyCallbackWrapperBase : public IHalProxyCallbackWrapperBase {
+ public:
+ HalProxyCallbackWrapperBase(sp<T> callback) : mCallback(callback){};
+
+ Return<void> onDynamicSensorsDisconnected(const hidl_vec<int32_t>& sensorHandles) override {
+ return mCallback->onDynamicSensorsDisconnected(sensorHandles);
+ }
+
+ ScopedWakelock createScopedWakelock(bool lock) override {
+ return mCallback->createScopedWakelock(lock);
+ }
+
+ protected:
+ sp<T> mCallback;
+};
+
+class HalProxyCallbackWrapperV2_0
+ : public HalProxyCallbackWrapperBase<V2_0::implementation::IHalProxyCallback> {
+ public:
+ HalProxyCallbackWrapperV2_0(sp<V2_0::implementation::IHalProxyCallback> callback)
+ : HalProxyCallbackWrapperBase(callback){};
+
+ Return<void> onDynamicSensorsConnected(const hidl_vec<V2_1::SensorInfo>& sensorInfos) override {
+ return mCallback->onDynamicSensorsConnected(
+ V2_1::implementation::convertToOldSensorInfos(sensorInfos));
+ }
+
+ void postEvents(const std::vector<V2_1::Event>& events, ScopedWakelock wakelock) override {
+ return mCallback->postEvents(V2_1::implementation::convertToOldEvents(events),
+ std::move(wakelock));
+ }
+};
+
+class HalProxyCallbackWrapperV2_1
+ : public HalProxyCallbackWrapperBase<V2_1::implementation::IHalProxyCallback> {
+ public:
+ HalProxyCallbackWrapperV2_1(sp<V2_1::implementation::IHalProxyCallback> callback)
+ : HalProxyCallbackWrapperBase(callback){};
+
+ Return<void> onDynamicSensorsConnected(const hidl_vec<V2_1::SensorInfo>& sensorInfos) override {
+ return mCallback->onDynamicSensorsConnected_2_1(sensorInfos);
+ }
+
+ void postEvents(const std::vector<V2_1::Event>& events, ScopedWakelock wakelock) {
+ return mCallback->postEvents(events, std::move(wakelock));
+ }
+};
+
+} // namespace implementation
+} // namespace subhal
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
index de89a00..1efd971 100644
--- a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
@@ -24,13 +24,18 @@
namespace android {
namespace hardware {
namespace sensors {
-namespace V2_0 {
+namespace V2_1 {
namespace subhal {
namespace implementation {
using ::android::hardware::sensors::V1_0::MetaDataEventType;
+using ::android::hardware::sensors::V1_0::OperationMode;
+using ::android::hardware::sensors::V1_0::Result;
using ::android::hardware::sensors::V1_0::SensorFlagBits;
using ::android::hardware::sensors::V1_0::SensorStatus;
+using ::android::hardware::sensors::V2_1::Event;
+using ::android::hardware::sensors::V2_1::SensorInfo;
+using ::android::hardware::sensors::V2_1::SensorType;
Sensor::Sensor(int32_t sensorHandle, ISensorsEventCallback* callback)
: mIsEnabled(false),
@@ -343,7 +348,7 @@
} // namespace implementation
} // namespace subhal
-} // namespace V2_0
+} // namespace V2_1
} // namespace sensors
} // namespace hardware
} // namespace android
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h
index 60f5d3d..5cf9f83 100644
--- a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h
@@ -16,7 +16,7 @@
#pragma once
-#include <android/hardware/sensors/1.0/types.h>
+#include <android/hardware/sensors/2.1/types.h>
#include <condition_variable>
#include <memory>
@@ -24,16 +24,16 @@
#include <thread>
#include <vector>
-using ::android::hardware::sensors::V1_0::Event;
using ::android::hardware::sensors::V1_0::OperationMode;
using ::android::hardware::sensors::V1_0::Result;
-using ::android::hardware::sensors::V1_0::SensorInfo;
-using ::android::hardware::sensors::V1_0::SensorType;
+using ::android::hardware::sensors::V2_1::Event;
+using ::android::hardware::sensors::V2_1::SensorInfo;
+using ::android::hardware::sensors::V2_1::SensorType;
namespace android {
namespace hardware {
namespace sensors {
-namespace V2_0 {
+namespace V2_1 {
namespace subhal {
namespace implementation {
@@ -151,7 +151,7 @@
} // namespace implementation
} // namespace subhal
-} // namespace V2_0
+} // namespace V2_1
} // namespace sensors
} // namespace hardware
} // namespace android
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.cpp b/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.cpp
index ff5ff38..20a4e9d 100644
--- a/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.cpp
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.cpp
@@ -16,33 +16,66 @@
#include "SensorsSubHal.h"
-#include <android/hardware/sensors/2.0/types.h>
+#include <android/hardware/sensors/2.1/types.h>
#include <log/log.h>
-ISensorsSubHal* sensorsHalGetSubHal(uint32_t* version) {
+#ifdef SUB_HAL_VERSION_2_0
+::android::hardware::sensors::V2_0::implementation::ISensorsSubHal* sensorsHalGetSubHal(
+ uint32_t* version) {
#if defined SUPPORT_CONTINUOUS_SENSORS && defined SUPPORT_ON_CHANGE_SENSORS
- static ::android::hardware::sensors::V2_0::subhal::implementation::AllSensorsSubHal subHal;
+ static ::android::hardware::sensors::V2_1::subhal::implementation::AllSensorsSubHal<
+ ::android::hardware::sensors::V2_1::subhal::implementation::SensorsSubHalV2_0>
+ subHal;
#elif defined SUPPORT_CONTINUOUS_SENSORS
- static ::android::hardware::sensors::V2_0::subhal::implementation::ContinuousSensorsSubHal
+ static ::android::hardware::sensors::V2_1::subhal::implementation::ContinuousSensorsSubHal<
+ ::android::hardware::sensors::V2_1::subhal::implementation::SensorsSubHalV2_0>
subHal;
#elif defined SUPPORT_ON_CHANGE_SENSORS
- static ::android::hardware::sensors::V2_0::subhal::implementation::OnChangeSensorsSubHal subHal;
+ static ::android::hardware::sensors::V2_1::subhal::implementation::OnChangeSensorsSubHal<
+ ::android::hardware::sensors::V2_1::subhal::implementation::SensorsSubHalV2_0>
+ subHal;
#else
- static ::android::hardware::sensors::V2_0::subhal::implementation::SensorsSubHal subHal;
+ static ::android::hardware::sensors::V2_1::subhal::implementation::SensorsSubHal<
+ ::android::hardware::sensors::V2_1::subhal::implementation::SensorsSubHalV2_0>
+ subHal;
#endif // defined SUPPORT_CONTINUOUS_SENSORS && defined SUPPORT_ON_CHANGE_SENSORS
*version = SUB_HAL_2_0_VERSION;
return &subHal;
}
+#else // SUB_HAL_VERSION_2_0
+
+::android::hardware::sensors::V2_1::implementation::ISensorsSubHal* sensorsHalGetSubHal_2_1(
+ uint32_t* version) {
+#if defined SUPPORT_CONTINUOUS_SENSORS && defined SUPPORT_ON_CHANGE_SENSORS
+ static ::android::hardware::sensors::V2_1::subhal::implementation::AllSensorsSubHal<
+ ::android::hardware::sensors::V2_1::subhal::implementation::SensorsSubHalV2_1>
+ subHal;
+#elif defined SUPPORT_CONTINUOUS_SENSORS
+ static ::android::hardware::sensors::V2_1::subhal::implementation::ContinuousSensorsSubHal<
+ ::android::hardware::sensors::V2_1::subhal::implementation::SensorsSubHalV2_1>
+ subHal;
+#elif defined SUPPORT_ON_CHANGE_SENSORS
+ static ::android::hardware::sensors::V2_1::subhal::implementation::OnChangeSensorsSubHal<
+ ::android::hardware::sensors::V2_1::subhal::implementation::SensorsSubHalV2_1>
+ subHal;
+#else
+ static ::android::hardware::sensors::V2_1::subhal::implementation::SensorsSubHalV2_1 subHal;
+#endif // defined SUPPORT_CONTINUOUS_SENSORS && defined SUPPORT_ON_CHANGE_SENSORS
+ *version = SUB_HAL_2_1_VERSION;
+ return &subHal;
+}
+
+#endif // SUB_HAL_VERSION_2_0
+
namespace android {
namespace hardware {
namespace sensors {
-namespace V2_0 {
+namespace V2_1 {
namespace subhal {
namespace implementation {
using ::android::hardware::Void;
-using ::android::hardware::sensors::V1_0::Event;
using ::android::hardware::sensors::V1_0::OperationMode;
using ::android::hardware::sensors::V1_0::RateLevel;
using ::android::hardware::sensors::V1_0::Result;
@@ -50,11 +83,12 @@
using ::android::hardware::sensors::V2_0::SensorTimeout;
using ::android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
using ::android::hardware::sensors::V2_0::implementation::ScopedWakelock;
+using ::android::hardware::sensors::V2_1::Event;
-SensorsSubHal::SensorsSubHal() : mCallback(nullptr), mNextHandle(1) {}
+ISensorsSubHalBase::ISensorsSubHalBase() : mCallback(nullptr), mNextHandle(1) {}
// Methods from ::android::hardware::sensors::V2_0::ISensors follow.
-Return<void> SensorsSubHal::getSensorsList(getSensorsList_cb _hidl_cb) {
+Return<void> ISensorsSubHalBase::getSensorsList(V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb) {
std::vector<SensorInfo> sensors;
for (const auto& sensor : mSensors) {
sensors.push_back(sensor.second->getSensorInfo());
@@ -64,7 +98,7 @@
return Void();
}
-Return<Result> SensorsSubHal::setOperationMode(OperationMode mode) {
+Return<Result> ISensorsSubHalBase::setOperationMode(OperationMode mode) {
for (auto sensor : mSensors) {
sensor.second->setOperationMode(mode);
}
@@ -72,7 +106,7 @@
return Result::OK;
}
-Return<Result> SensorsSubHal::activate(int32_t sensorHandle, bool enabled) {
+Return<Result> ISensorsSubHalBase::activate(int32_t sensorHandle, bool enabled) {
auto sensor = mSensors.find(sensorHandle);
if (sensor != mSensors.end()) {
sensor->second->activate(enabled);
@@ -81,8 +115,8 @@
return Result::BAD_VALUE;
}
-Return<Result> SensorsSubHal::batch(int32_t sensorHandle, int64_t samplingPeriodNs,
- int64_t /* maxReportLatencyNs */) {
+Return<Result> ISensorsSubHalBase::batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t /* maxReportLatencyNs */) {
auto sensor = mSensors.find(sensorHandle);
if (sensor != mSensors.end()) {
sensor->second->batch(samplingPeriodNs);
@@ -91,7 +125,7 @@
return Result::BAD_VALUE;
}
-Return<Result> SensorsSubHal::flush(int32_t sensorHandle) {
+Return<Result> ISensorsSubHalBase::flush(int32_t sensorHandle) {
auto sensor = mSensors.find(sensorHandle);
if (sensor != mSensors.end()) {
return sensor->second->flush();
@@ -99,7 +133,7 @@
return Result::BAD_VALUE;
}
-Return<Result> SensorsSubHal::injectSensorData(const Event& event) {
+Return<Result> ISensorsSubHalBase::injectSensorData(const Event& event) {
auto sensor = mSensors.find(event.sensorHandle);
if (sensor != mSensors.end()) {
return sensor->second->injectEvent(event);
@@ -108,24 +142,24 @@
return Result::BAD_VALUE;
}
-Return<void> SensorsSubHal::registerDirectChannel(const SharedMemInfo& /* mem */,
- registerDirectChannel_cb _hidl_cb) {
+Return<void> ISensorsSubHalBase::registerDirectChannel(
+ const SharedMemInfo& /* mem */, V2_0::ISensors::registerDirectChannel_cb _hidl_cb) {
_hidl_cb(Result::INVALID_OPERATION, -1 /* channelHandle */);
return Return<void>();
}
-Return<Result> SensorsSubHal::unregisterDirectChannel(int32_t /* channelHandle */) {
+Return<Result> ISensorsSubHalBase::unregisterDirectChannel(int32_t /* channelHandle */) {
return Result::INVALID_OPERATION;
}
-Return<void> SensorsSubHal::configDirectReport(int32_t /* sensorHandle */,
- int32_t /* channelHandle */, RateLevel /* rate */,
- configDirectReport_cb _hidl_cb) {
+Return<void> ISensorsSubHalBase::configDirectReport(
+ int32_t /* sensorHandle */, int32_t /* channelHandle */, RateLevel /* rate */,
+ V2_0::ISensors::configDirectReport_cb _hidl_cb) {
_hidl_cb(Result::INVALID_OPERATION, 0 /* reportToken */);
return Return<void>();
}
-Return<void> SensorsSubHal::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) {
+Return<void> ISensorsSubHalBase::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) {
if (fd.getNativeHandle() == nullptr || fd->numFds < 1) {
ALOGE("%s: missing fd for writing", __FUNCTION__);
return Void();
@@ -156,44 +190,18 @@
return Return<void>();
}
-Return<Result> SensorsSubHal::initialize(const sp<IHalProxyCallback>& halProxyCallback) {
- mCallback = halProxyCallback;
+Return<Result> ISensorsSubHalBase::initialize(
+ std::unique_ptr<IHalProxyCallbackWrapperBase>& halProxyCallback) {
+ mCallback = std::move(halProxyCallback);
setOperationMode(OperationMode::NORMAL);
return Result::OK;
}
-void SensorsSubHal::postEvents(const std::vector<Event>& events, bool wakeup) {
+void ISensorsSubHalBase::postEvents(const std::vector<Event>& events, bool wakeup) {
ScopedWakelock wakelock = mCallback->createScopedWakelock(wakeup);
mCallback->postEvents(events, std::move(wakelock));
}
-ContinuousSensorsSubHal::ContinuousSensorsSubHal() {
- AddSensor<AccelSensor>();
- AddSensor<GyroSensor>();
- AddSensor<MagnetometerSensor>();
- AddSensor<PressureSensor>();
- AddSensor<DeviceTempSensor>();
-}
-
-OnChangeSensorsSubHal::OnChangeSensorsSubHal() {
- AddSensor<AmbientTempSensor>();
- AddSensor<LightSensor>();
- AddSensor<ProximitySensor>();
- AddSensor<RelativeHumiditySensor>();
-}
-
-AllSensorsSubHal::AllSensorsSubHal() {
- AddSensor<AccelSensor>();
- AddSensor<GyroSensor>();
- AddSensor<MagnetometerSensor>();
- AddSensor<PressureSensor>();
- AddSensor<DeviceTempSensor>();
- AddSensor<AmbientTempSensor>();
- AddSensor<LightSensor>();
- AddSensor<ProximitySensor>();
- AddSensor<RelativeHumiditySensor>();
-}
-
Return<Result> SetOperationModeFailingSensorsSubHal::setOperationMode(OperationMode /*mode*/) {
return Result::BAD_VALUE;
}
@@ -206,7 +214,7 @@
sensorInfo.flags |= V1_0::SensorFlagBits::MASK_DIRECT_REPORT;
sensors.push_back(sensorInfo);
}
- _hidl_cb(sensors);
+ _hidl_cb(V2_1::implementation::convertToOldSensorInfos(sensors));
return Void();
}
@@ -218,7 +226,7 @@
sensorInfo.flags &= ~static_cast<uint32_t>(V1_0::SensorFlagBits::MASK_DIRECT_REPORT);
sensors.push_back(sensorInfo);
}
- _hidl_cb(sensors);
+ _hidl_cb(V2_1::implementation::convertToOldSensorInfos(sensors));
return Void();
}
@@ -234,7 +242,7 @@
} // namespace implementation
} // namespace subhal
-} // namespace V2_0
+} // namespace V2_1
} // namespace sensors
} // namespace hardware
} // namespace android
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h b/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h
index 6da4404..1a78e84 100644
--- a/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h
@@ -17,7 +17,9 @@
#pragma once
#include "V2_0/SubHal.h"
+#include "V2_1/SubHal.h"
+#include "IHalProxyCallbackWrapper.h"
#include "Sensor.h"
#include <vector>
@@ -25,54 +27,54 @@
namespace android {
namespace hardware {
namespace sensors {
-namespace V2_0 {
+namespace V2_1 {
namespace subhal {
namespace implementation {
using ::android::hardware::sensors::V1_0::OperationMode;
using ::android::hardware::sensors::V1_0::Result;
-using ::android::hardware::sensors::V2_0::implementation::IHalProxyCallback;
/**
* Implementation of a ISensorsSubHal that can be used to test the implementation of multihal 2.0.
* See the README file for more details on how this class can be used for testing.
*/
-class SensorsSubHal : public ISensorsSubHal, public ISensorsEventCallback {
- using Event = ::android::hardware::sensors::V1_0::Event;
+class ISensorsSubHalBase : public ISensorsEventCallback {
+ protected:
+ using Event = ::android::hardware::sensors::V2_1::Event;
using RateLevel = ::android::hardware::sensors::V1_0::RateLevel;
using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo;
public:
- SensorsSubHal();
+ ISensorsSubHalBase();
+
+ Return<void> getSensorsList(V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb);
+ Return<Result> injectSensorData(const Event& event);
+ Return<Result> initialize(std::unique_ptr<IHalProxyCallbackWrapperBase>& halProxyCallback);
// Methods from ::android::hardware::sensors::V2_0::ISensors follow.
- virtual Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
-
- virtual Return<Result> setOperationMode(OperationMode mode) override;
+ virtual Return<Result> setOperationMode(OperationMode mode);
OperationMode getOperationMode() const { return mCurrentOperationMode; }
- Return<Result> activate(int32_t sensorHandle, bool enabled) override;
+ Return<Result> activate(int32_t sensorHandle, bool enabled);
Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
- int64_t maxReportLatencyNs) override;
+ int64_t maxReportLatencyNs);
- Return<Result> flush(int32_t sensorHandle) override;
-
- Return<Result> injectSensorData(const Event& event) override;
+ Return<Result> flush(int32_t sensorHandle);
Return<void> registerDirectChannel(const SharedMemInfo& mem,
- registerDirectChannel_cb _hidl_cb) override;
+ V2_0::ISensors::registerDirectChannel_cb _hidl_cb);
- Return<Result> unregisterDirectChannel(int32_t channelHandle) override;
+ Return<Result> unregisterDirectChannel(int32_t channelHandle);
Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
- configDirectReport_cb _hidl_cb) override;
+ V2_0::ISensors::configDirectReport_cb _hidl_cb);
- Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args);
// Methods from ::android::hardware::sensors::V2_0::implementation::ISensorsSubHal follow.
- const std::string getName() override {
+ const std::string getName() {
#ifdef SUB_HAL_NAME
return SUB_HAL_NAME;
#else // SUB_HAL_NAME
@@ -80,8 +82,6 @@
#endif // SUB_HAL_NAME
}
- Return<Result> initialize(const sp<IHalProxyCallback>& halProxyCallback) override;
-
// Method from ISensorsEventCallback.
void postEvents(const std::vector<Event>& events, bool wakeup) override;
@@ -103,7 +103,7 @@
* disconnected, sensor events need to be sent to the framework, and when a wakelock should be
* acquired.
*/
- sp<IHalProxyCallback> mCallback;
+ std::unique_ptr<IHalProxyCallbackWrapperBase> mCallback;
private:
/**
@@ -118,40 +118,143 @@
int32_t mNextHandle;
};
-// SubHal that has continuous sensors for testing purposes.
-class ContinuousSensorsSubHal : public SensorsSubHal {
+template <class SubHalClass>
+class SensorsSubHalBase : public ISensorsSubHalBase, public SubHalClass {
public:
- ContinuousSensorsSubHal();
+ Return<Result> setOperationMode(OperationMode mode) override {
+ return ISensorsSubHalBase::setOperationMode(mode);
+ }
+
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override {
+ return ISensorsSubHalBase::activate(sensorHandle, enabled);
+ }
+
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) override {
+ return ISensorsSubHalBase::batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
+ }
+
+ Return<Result> flush(int32_t sensorHandle) override {
+ return ISensorsSubHalBase::flush(sensorHandle);
+ }
+
+ Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ V2_0::ISensors::registerDirectChannel_cb _hidl_cb) override {
+ return ISensorsSubHalBase::registerDirectChannel(mem, _hidl_cb);
+ }
+
+ Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
+ return ISensorsSubHalBase::unregisterDirectChannel(channelHandle);
+ }
+
+ Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
+ V2_0::ISensors::configDirectReport_cb _hidl_cb) override {
+ return ISensorsSubHalBase::configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
+ }
+
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override {
+ return ISensorsSubHalBase::debug(fd, args);
+ }
+
+ const std::string getName() override { return ISensorsSubHalBase::getName(); }
+};
+
+class SensorsSubHalV2_0 : public SensorsSubHalBase<V2_0::implementation::ISensorsSubHal> {
+ public:
+ virtual Return<void> getSensorsList(V2_0::ISensors::getSensorsList_cb _hidl_cb) override {
+ return ISensorsSubHalBase::getSensorsList([&](const auto& list) {
+ _hidl_cb(V2_1::implementation::convertToOldSensorInfos(list));
+ });
+ }
+
+ Return<Result> injectSensorData(const V1_0::Event& event) override {
+ return ISensorsSubHalBase::injectSensorData(V2_1::implementation::convertToNewEvent(event));
+ }
+
+ Return<Result> initialize(
+ const sp<V2_0::implementation::IHalProxyCallback>& halProxyCallback) override {
+ std::unique_ptr<IHalProxyCallbackWrapperBase> wrapper =
+ std::make_unique<HalProxyCallbackWrapperV2_0>(halProxyCallback);
+ return ISensorsSubHalBase::initialize(wrapper);
+ }
+};
+
+class SensorsSubHalV2_1 : public SensorsSubHalBase<V2_1::implementation::ISensorsSubHal> {
+ public:
+ Return<void> getSensorsList_2_1(V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb) override {
+ return ISensorsSubHalBase::getSensorsList(_hidl_cb);
+ }
+
+ Return<Result> injectSensorData_2_1(const V2_1::Event& event) override {
+ return ISensorsSubHalBase::injectSensorData(event);
+ }
+
+ Return<Result> initialize(
+ const sp<V2_1::implementation::IHalProxyCallback>& halProxyCallback) override {
+ std::unique_ptr<IHalProxyCallbackWrapperBase> wrapper =
+ std::make_unique<HalProxyCallbackWrapperV2_1>(halProxyCallback);
+ return ISensorsSubHalBase::initialize(wrapper);
+ }
+};
+
+// SubHal that has continuous sensors for testing purposes.
+template <class SubHalVersion>
+class ContinuousSensorsSubHal : public SubHalVersion {
+ public:
+ ContinuousSensorsSubHal() {
+ ISensorsSubHalBase::AddSensor<AccelSensor>();
+ ISensorsSubHalBase::AddSensor<GyroSensor>();
+ ISensorsSubHalBase::AddSensor<MagnetometerSensor>();
+ ISensorsSubHalBase::AddSensor<PressureSensor>();
+ ISensorsSubHalBase::AddSensor<DeviceTempSensor>();
+ }
};
// SubHal that has on-change sensors for testing purposes.
-class OnChangeSensorsSubHal : public SensorsSubHal {
+template <class SubHalVersion>
+class OnChangeSensorsSubHal : public SubHalVersion {
public:
- OnChangeSensorsSubHal();
+ OnChangeSensorsSubHal() {
+ ISensorsSubHalBase::AddSensor<AmbientTempSensor>();
+ ISensorsSubHalBase::AddSensor<LightSensor>();
+ ISensorsSubHalBase::AddSensor<ProximitySensor>();
+ ISensorsSubHalBase::AddSensor<RelativeHumiditySensor>();
+ }
};
// SubHal that has both continuous and on-change sensors for testing purposes.
-class AllSensorsSubHal : public SensorsSubHal {
+template <class SubHalVersion>
+class AllSensorsSubHal : public SubHalVersion {
public:
- AllSensorsSubHal();
+ AllSensorsSubHal() {
+ ISensorsSubHalBase::AddSensor<AccelSensor>();
+ ISensorsSubHalBase::AddSensor<GyroSensor>();
+ ISensorsSubHalBase::AddSensor<MagnetometerSensor>();
+ ISensorsSubHalBase::AddSensor<PressureSensor>();
+ ISensorsSubHalBase::AddSensor<DeviceTempSensor>();
+ ISensorsSubHalBase::AddSensor<AmbientTempSensor>();
+ ISensorsSubHalBase::AddSensor<LightSensor>();
+ ISensorsSubHalBase::AddSensor<ProximitySensor>();
+ ISensorsSubHalBase::AddSensor<RelativeHumiditySensor>();
+ }
};
-class SetOperationModeFailingSensorsSubHal : public AllSensorsSubHal {
+class SetOperationModeFailingSensorsSubHal : public AllSensorsSubHal<SensorsSubHalV2_0> {
public:
Return<Result> setOperationMode(OperationMode mode) override;
};
-class AllSupportDirectChannelSensorsSubHal : public AllSensorsSubHal {
+class AllSupportDirectChannelSensorsSubHal : public AllSensorsSubHal<SensorsSubHalV2_0> {
public:
- Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
+ Return<void> getSensorsList(V2_0::ISensors::getSensorsList_cb _hidl_cb) override;
};
-class DoesNotSupportDirectChannelSensorsSubHal : public AllSensorsSubHal {
+class DoesNotSupportDirectChannelSensorsSubHal : public AllSensorsSubHal<SensorsSubHalV2_0> {
public:
- Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
+ Return<void> getSensorsList(V2_0::ISensors::getSensorsList_cb _hidl_cb) override;
};
-class AddAndRemoveDynamicSensorsSubHal : public AllSensorsSubHal {
+class AddAndRemoveDynamicSensorsSubHal : public AllSensorsSubHal<SensorsSubHalV2_0> {
public:
void addDynamicSensors(const std::vector<SensorInfo>& sensorsAdded);
void removeDynamicSensors(const std::vector<int32_t>& sensorHandlesAdded);
@@ -159,7 +262,7 @@
} // namespace implementation
} // namespace subhal
-} // namespace V2_0
+} // namespace V2_1
} // namespace sensors
} // namespace hardware
} // namespace android
diff --git a/sensors/common/utils/EventMessageQueueWrapper.h b/sensors/common/utils/EventMessageQueueWrapper.h
index bf3261f..c4f92c8 100644
--- a/sensors/common/utils/EventMessageQueueWrapper.h
+++ b/sensors/common/utils/EventMessageQueueWrapper.h
@@ -39,8 +39,14 @@
virtual std::atomic<uint32_t>* getEventFlagWord() = 0;
virtual size_t availableToRead() = 0;
+ virtual size_t availableToWrite() = 0;
virtual bool read(V2_1::Event* events, size_t numToRead) = 0;
+ virtual bool write(const V2_1::Event* events, size_t numToWrite) = 0;
virtual bool write(const std::vector<V2_1::Event>& events) = 0;
+ virtual bool writeBlocking(const V2_1::Event* events, size_t count, uint32_t readNotification,
+ uint32_t writeNotification, int64_t timeOutNanos,
+ android::hardware::EventFlag* evFlag) = 0;
+ virtual size_t getQuantumCount() = 0;
};
class EventMessageQueueWrapperV1_0 : public EventMessageQueueWrapperBase {
@@ -60,15 +66,30 @@
virtual size_t availableToRead() override { return mQueue->availableToRead(); }
+ size_t availableToWrite() override { return mQueue->availableToWrite(); }
+
virtual bool read(V2_1::Event* events, size_t numToRead) override {
return mQueue->read(reinterpret_cast<V1_0::Event*>(events), numToRead);
}
+ bool write(const V2_1::Event* events, size_t numToWrite) override {
+ return mQueue->write(reinterpret_cast<const V1_0::Event*>(events), numToWrite);
+ }
+
virtual bool write(const std::vector<V2_1::Event>& events) override {
const std::vector<V1_0::Event>& oldEvents = convertToOldEvents(events);
return mQueue->write(oldEvents.data(), oldEvents.size());
}
+ bool writeBlocking(const V2_1::Event* events, size_t count, uint32_t readNotification,
+ uint32_t writeNotification, int64_t timeOutNanos,
+ android::hardware::EventFlag* evFlag) override {
+ return mQueue->writeBlocking(reinterpret_cast<const V1_0::Event*>(events), count,
+ readNotification, writeNotification, timeOutNanos, evFlag);
+ }
+
+ size_t getQuantumCount() override { return mQueue->getQuantumCount(); }
+
private:
std::unique_ptr<EventMessageQueue> mQueue;
};
@@ -88,14 +109,29 @@
virtual size_t availableToRead() override { return mQueue->availableToRead(); }
+ size_t availableToWrite() override { return mQueue->availableToWrite(); }
+
virtual bool read(V2_1::Event* events, size_t numToRead) override {
return mQueue->read(events, numToRead);
}
+ bool write(const V2_1::Event* events, size_t numToWrite) override {
+ return mQueue->write(events, numToWrite);
+ }
+
bool write(const std::vector<V2_1::Event>& events) override {
return mQueue->write(events.data(), events.size());
}
+ bool writeBlocking(const V2_1::Event* events, size_t count, uint32_t readNotification,
+ uint32_t writeNotification, int64_t timeOutNanos,
+ android::hardware::EventFlag* evFlag) override {
+ return mQueue->writeBlocking(events, count, readNotification, writeNotification,
+ timeOutNanos, evFlag);
+ }
+
+ size_t getQuantumCount() override { return mQueue->getQuantumCount(); }
+
private:
std::unique_ptr<EventMessageQueue> mQueue;
};
diff --git a/sensors/common/utils/ISensorsCallbackWrapper.h b/sensors/common/utils/ISensorsCallbackWrapper.h
new file mode 100644
index 0000000..816b225
--- /dev/null
+++ b/sensors/common/utils/ISensorsCallbackWrapper.h
@@ -0,0 +1,96 @@
+/*
+ * 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_SENSORS_V2_1_ISENSORSCALLBACKWRAPPER_H
+#define ANDROID_HARDWARE_SENSORS_V2_1_ISENSORSCALLBACKWRAPPER_H
+
+#include "convertV2_1.h"
+
+#include "android/hardware/sensors/1.0/ISensors.h"
+#include "android/hardware/sensors/1.0/types.h"
+#include "android/hardware/sensors/2.0/ISensors.h"
+#include "android/hardware/sensors/2.0/ISensorsCallback.h"
+#include "android/hardware/sensors/2.1/ISensors.h"
+#include "android/hardware/sensors/2.1/ISensorsCallback.h"
+#include "android/hardware/sensors/2.1/types.h"
+
+#include <utils/LightRefBase.h>
+
+#include <cassert>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+/**
+ * The ISensorsCallbackWrapper classes below abstract away the common logic between both the V2.0
+ * and V2.1 versions of the Sensors HAL interface. This allows users of these classes to only care
+ * about the HAL version at init time and then interact with either version of the callback without
+ * worrying about the class type by utilizing the base class.
+ */
+class ISensorsCallbackWrapperBase : public VirtualLightRefBase {
+ public:
+ virtual Return<void> onDynamicSensorsConnected(
+ const hidl_vec<V2_1::SensorInfo>& sensorInfos) = 0;
+
+ virtual Return<void> onDynamicSensorsDisconnected(const hidl_vec<int32_t>& sensorHandles) = 0;
+};
+
+template <typename T>
+class SensorsCallbackWrapperBase : public ISensorsCallbackWrapperBase {
+ public:
+ SensorsCallbackWrapperBase(sp<T> sensorsCallback) : mSensorsCallback(sensorsCallback){};
+
+ virtual Return<void> onDynamicSensorsConnected(
+ const hidl_vec<V2_1::SensorInfo>& sensorInfos) override {
+ return mSensorsCallback->onDynamicSensorsConnected(convertToOldSensorInfos(sensorInfos));
+ }
+
+ Return<void> onDynamicSensorsDisconnected(const hidl_vec<int32_t>& sensorHandles) {
+ return mSensorsCallback->onDynamicSensorsDisconnected(sensorHandles);
+ }
+
+ protected:
+ sp<T> mSensorsCallback;
+};
+
+class ISensorsCallbackWrapperV2_0
+ : public SensorsCallbackWrapperBase<hardware::sensors::V2_0::ISensorsCallback> {
+ public:
+ ISensorsCallbackWrapperV2_0(sp<hardware::sensors::V2_0::ISensorsCallback> sensorsCallback)
+ : SensorsCallbackWrapperBase(sensorsCallback){};
+};
+
+class ISensorsCallbackWrapperV2_1
+ : public SensorsCallbackWrapperBase<hardware::sensors::V2_1::ISensorsCallback> {
+ public:
+ ISensorsCallbackWrapperV2_1(sp<hardware::sensors::V2_1::ISensorsCallback> sensorsCallback)
+ : SensorsCallbackWrapperBase(sensorsCallback) {}
+
+ Return<void> onDynamicSensorsConnected(const hidl_vec<V2_1::SensorInfo>& sensorInfos) override {
+ return mSensorsCallback->onDynamicSensorsConnected_2_1(sensorInfos);
+ }
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_1_ISENSORSCALLBACKWRAPPER_H
\ No newline at end of file
diff --git a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
index 75f2c28..2e5aca4 100644
--- a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
+++ b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
@@ -367,11 +367,13 @@
<< s.sensorHandle << std::dec << " type=" << static_cast<int>(s.type)
<< " name=" << s.name);
- // Test non-empty type string
- EXPECT_FALSE(s.typeAsString.empty());
-
- // Test defined type matches defined string type
- EXPECT_NO_FATAL_FAILURE(assertTypeMatchStringType(s.type, s.typeAsString));
+ // Test type string non-empty only for private sensor types.
+ if (s.type >= SensorTypeVersion::DEVICE_PRIVATE_BASE) {
+ EXPECT_FALSE(s.typeAsString.empty());
+ } else if (!s.typeAsString.empty()) {
+ // Test type string matches framework string if specified for non-private types.
+ EXPECT_NO_FATAL_FAILURE(assertTypeMatchStringType(s.type, s.typeAsString));
+ }
// Test if all sensor has name and vendor
EXPECT_FALSE(s.name.empty());
@@ -450,6 +452,10 @@
for (const auto& s : sensors) {
auto events = callback.getEvents(s.sensorHandle);
auto lastEvent = events.back();
+ SCOPED_TRACE(::testing::Message()
+ << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << s.sensorHandle << std::dec << " type=" << static_cast<int>(s.type)
+ << " name=" << s.name);
// Verify that only a single event has been received
ASSERT_EQ(events.size(), 1);
@@ -576,6 +582,12 @@
// Flush the sensor
for (int32_t i = 0; i < flushCalls; i++) {
+ SCOPED_TRACE(::testing::Message()
+ << "Flush " << i << "/" << flushCalls << ": "
+ << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << sensor.sensorHandle << std::dec
+ << " type=" << static_cast<int>(sensor.type) << " name=" << sensor.name);
+
Result flushResult = flush(sensor.sensorHandle);
ASSERT_EQ(flushResult, expectedResponse);
}
@@ -593,6 +605,10 @@
// Check that the correct number of flushes are present for each sensor
for (const SensorInfoType& sensor : sensors) {
+ SCOPED_TRACE(::testing::Message()
+ << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << sensor.sensorHandle << std::dec << " type=" << static_cast<int>(sensor.type)
+ << " name=" << sensor.name);
ASSERT_EQ(callback.getFlushCount(sensor.sensorHandle), expectedFlushCount);
}
}
@@ -641,6 +657,11 @@
activateAllSensors(false /* enable */);
for (const SensorInfoType& sensor : getSensorsList()) {
+ SCOPED_TRACE(::testing::Message()
+ << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << sensor.sensorHandle << std::dec << " type=" << static_cast<int>(sensor.type)
+ << " name=" << sensor.name);
+
// Call batch on inactive sensor
// One shot sensors have minDelay set to -1 which is an invalid
// parameter. Use 0 instead to avoid errors.
@@ -673,6 +694,11 @@
// Verify that sensor events are generated when activate is called
for (const SensorInfoType& sensor : getSensorsList()) {
+ SCOPED_TRACE(::testing::Message()
+ << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << sensor.sensorHandle << std::dec << " type=" << static_cast<int>(sensor.type)
+ << " name=" << sensor.name);
+
batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */);
ASSERT_EQ(activate(sensor.sensorHandle, true), Result::OK);
@@ -720,6 +746,10 @@
// Save the last received event for each sensor
std::map<int32_t, int64_t> lastEventTimestampMap;
for (const SensorInfoType& sensor : sensors) {
+ SCOPED_TRACE(::testing::Message()
+ << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << sensor.sensorHandle << std::dec << " type=" << static_cast<int>(sensor.type)
+ << " name=" << sensor.name);
// Some on-change sensors may not report an event without stimulus
if (extractReportMode(sensor.flags) != SensorFlagBits::ON_CHANGE_MODE) {
ASSERT_GE(callback.getEvents(sensor.sensorHandle).size(), 1);
@@ -740,6 +770,11 @@
getEnvironment()->unregisterCallback();
for (const SensorInfoType& sensor : sensors) {
+ SCOPED_TRACE(::testing::Message()
+ << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << sensor.sensorHandle << std::dec << " type=" << static_cast<int>(sensor.type)
+ << " name=" << sensor.name);
+
// Skip sensors that did not previously report an event
if (lastEventTimestampMap.find(sensor.sensorHandle) == lastEventTimestampMap.end()) {
continue;
@@ -762,6 +797,12 @@
RateLevel rateLevel) {
configDirectReport(sensor.sensorHandle, directChannelHandle, rateLevel,
[&](Result result, int32_t reportToken) {
+ SCOPED_TRACE(::testing::Message()
+ << " handle=0x" << std::hex << std::setw(8)
+ << std::setfill('0') << sensor.sensorHandle << std::dec
+ << " type=" << static_cast<int>(sensor.type)
+ << " name=" << sensor.name);
+
if (isDirectReportRateSupported(sensor, rateLevel)) {
ASSERT_EQ(result, Result::OK);
if (rateLevel != RateLevel::STOP) {
@@ -819,6 +860,11 @@
void SensorsHidlTest::verifyConfigure(const SensorInfoType& sensor, SharedMemType memType,
int32_t directChannelHandle, bool supportsAnyDirectChannel) {
+ SCOPED_TRACE(::testing::Message()
+ << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << sensor.sensorHandle << std::dec << " type=" << static_cast<int>(sensor.type)
+ << " name=" << sensor.name);
+
if (isDirectChannelTypeSupported(sensor, memType)) {
// Verify that each rate level is properly supported
checkRateLevel(sensor, directChannelHandle, RateLevel::NORMAL);
diff --git a/tv/tuner/1.0/default/Demux.cpp b/tv/tuner/1.0/default/Demux.cpp
index 95b4ebc..4e5ae4b 100644
--- a/tv/tuner/1.0/default/Demux.cpp
+++ b/tv/tuner/1.0/default/Demux.cpp
@@ -71,7 +71,7 @@
mUsedFilterIds.insert(filterId);
if (cb == nullptr) {
- ALOGW("callback can't be null");
+ ALOGW("[Demux] callback can't be null");
_hidl_cb(Result::INVALID_ARGUMENT, new Filter());
return Void();
}
@@ -82,9 +82,14 @@
_hidl_cb(Result::UNKNOWN_ERROR, filter);
return Void();
}
- mFilters[filterId] = filter;
- _hidl_cb(Result::SUCCESS, filter);
+ mFilters[filterId] = filter;
+ bool result = true;
+ if (mDvr != nullptr && mDvr->getType() == DvrType::PLAYBACK) {
+ result = mDvr->addPlaybackFilter(filter);
+ }
+
+ _hidl_cb(result ? Result::SUCCESS : Result::INVALID_ARGUMENT, filter);
return Void();
}
@@ -130,7 +135,7 @@
ALOGV("%s", __FUNCTION__);
if (cb == nullptr) {
- ALOGW("DVR callback can't be null");
+ ALOGW("[Demux] DVR callback can't be null");
_hidl_cb(Result::INVALID_ARGUMENT, new Dvr());
return Void();
}
@@ -174,11 +179,11 @@
void Demux::startBroadcastTsFilter(vector<uint8_t> data) {
set<uint32_t>::iterator it;
+ uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
+ if (DEBUG_DEMUX) {
+ ALOGW("[Demux] start ts filter pid: %d", pid);
+ }
for (it = mUsedFilterIds.begin(); it != mUsedFilterIds.end(); it++) {
- uint16_t pid = ((data[1] & 0x1f) << 8) | ((data[2] & 0xff));
- if (DEBUG_FILTER) {
- ALOGW("start ts filter pid: %d", pid);
- }
if (pid == mFilters[*it]->getTpid()) {
mFilters[*it]->updateFilterOutput(data);
}
@@ -187,10 +192,10 @@
void Demux::sendFrontendInputToRecord(vector<uint8_t> data) {
set<uint32_t>::iterator it;
+ if (DEBUG_DEMUX) {
+ ALOGW("[Demux] update record filter output");
+ }
for (it = mRecordFilterIds.begin(); it != mRecordFilterIds.end(); it++) {
- if (DEBUG_FILTER) {
- ALOGW("update record filter output");
- }
mFilters[*it]->updateRecordOutput(data);
}
}
diff --git a/tv/tuner/1.0/default/Demux.h b/tv/tuner/1.0/default/Demux.h
index 759e348..3c91daf 100644
--- a/tv/tuner/1.0/default/Demux.h
+++ b/tv/tuner/1.0/default/Demux.h
@@ -188,7 +188,7 @@
int mPesSizeLeft = 0;
vector<uint8_t> mPesOutput;
- const bool DEBUG_FILTER = false;
+ const bool DEBUG_DEMUX = false;
};
} // namespace implementation
diff --git a/tv/tuner/1.0/default/Dvr.cpp b/tv/tuner/1.0/default/Dvr.cpp
index 3088a9d..adb2635 100644
--- a/tv/tuner/1.0/default/Dvr.cpp
+++ b/tv/tuner/1.0/default/Dvr.cpp
@@ -71,13 +71,10 @@
}
// check if the attached filter is a record filter
-
mFilters[filterId] = filter;
- mIsRecordFilterAttached = true;
if (!mDemux->attachRecordFilter(filterId)) {
return Result::INVALID_ARGUMENT;
}
- mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
return Result::SUCCESS;
}
@@ -110,7 +107,6 @@
// If all the filters are detached, record can't be started
if (mFilters.empty()) {
mIsRecordFilterAttached = false;
- mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
}
return Result::SUCCESS;
@@ -132,8 +128,7 @@
pthread_setname_np(mDvrThread, "playback_waiting_loop");
} else if (mType == DvrType::RECORD) {
mRecordStatus = RecordStatus::DATA_READY;
- mIsRecordStarted = true;
- mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
+ mDemux->setIsRecording(mType == DvrType::RECORD);
}
// TODO start another thread to send filter status callback to the framework
@@ -149,7 +144,7 @@
std::lock_guard<std::mutex> lock(mDvrThreadLock);
mIsRecordStarted = false;
- mDemux->setIsRecording(mIsRecordStarted | mIsRecordFilterAttached);
+ mDemux->setIsRecording(false);
return Result::SUCCESS;
}
@@ -175,7 +170,7 @@
std::unique_ptr<DvrMQ> tmpDvrMQ =
std::unique_ptr<DvrMQ>(new (std::nothrow) DvrMQ(mBufferSize, true));
if (!tmpDvrMQ->isValid()) {
- ALOGW("Failed to create FMQ of DVR");
+ ALOGW("[Dvr] Failed to create FMQ of DVR");
return false;
}
@@ -256,7 +251,6 @@
int playbackPacketSize = mDvrSettings.playback().packetSize;
vector<uint8_t> dataOutputBuffer;
dataOutputBuffer.resize(playbackPacketSize);
-
// Dispatch the packet to the PID matching filter output buffer
for (int i = 0; i < size / playbackPacketSize; i++) {
if (!mDvrMQ->read(dataOutputBuffer.data(), playbackPacketSize)) {
@@ -283,7 +277,6 @@
bool Dvr::startFilterDispatcher() {
std::map<uint32_t, sp<IFilter>>::iterator it;
-
// Handle the output data per filter type
for (it = mFilters.begin(); it != mFilters.end(); it++) {
if (mDemux->startFilterHandler(it->first) != Result::SUCCESS) {
@@ -296,7 +289,10 @@
bool Dvr::writeRecordFMQ(const std::vector<uint8_t>& data) {
std::lock_guard<std::mutex> lock(mWriteLock);
- ALOGW("[Dvr] write record FMQ");
+ if (mRecordStatus == RecordStatus::OVERFLOW) {
+ ALOGW("[Dvr] stops writing and wait for the client side flushing.");
+ return true;
+ }
if (mDvrMQ->write(data.data(), data.size())) {
mDvrEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
maySendRecordStatusCallback();
@@ -333,6 +329,27 @@
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;
+ }
+
+ mFilters[filterId] = filter;
+ return true;
+}
+
+DvrType Dvr::getType() {
+ return mType;
+}
+
} // 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 f39d8db..08afd5d 100644
--- a/tv/tuner/1.0/default/Dvr.h
+++ b/tv/tuner/1.0/default/Dvr.h
@@ -81,6 +81,8 @@
bool createDvrMQ();
void sendBroadcastInputToDvrRecord(vector<uint8_t> byteBuffer);
bool writeRecordFMQ(const std::vector<uint8_t>& data);
+ DvrType getType();
+ bool addPlaybackFilter(sp<IFilter> filter);
private:
// Demux service
diff --git a/tv/tuner/1.0/default/Filter.cpp b/tv/tuner/1.0/default/Filter.cpp
index f610c60..fef7a35 100644
--- a/tv/tuner/1.0/default/Filter.cpp
+++ b/tv/tuner/1.0/default/Filter.cpp
@@ -73,18 +73,22 @@
switch (mType.mainType) {
case DemuxFilterMainType::TS:
mTpid = settings.ts().tpid;
+ if (mType.subType.tsFilterType() == DemuxTsFilterType::AUDIO ||
+ mType.subType.tsFilterType() == DemuxTsFilterType::VIDEO) {
+ mIsMediaFilter = true;
+ }
break;
case DemuxFilterMainType::MMTP:
- /*mmtpSettings*/
+ if (mType.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO ||
+ mType.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) {
+ mIsMediaFilter = true;
+ }
break;
case DemuxFilterMainType::IP:
- /*ipSettings*/
break;
case DemuxFilterMainType::TLV:
- /*tlvSettings*/
break;
case DemuxFilterMainType::ALP:
- /*alpSettings*/
break;
default:
break;
@@ -145,7 +149,7 @@
std::unique_ptr<FilterMQ> tmpFilterMQ =
std::unique_ptr<FilterMQ>(new (std::nothrow) FilterMQ(mBufferSize, true));
if (!tmpFilterMQ->isValid()) {
- ALOGW("Failed to create FMQ of filter with id: %d", mFilterId);
+ ALOGW("[Filter] Failed to create FMQ of filter with id: %d", mFilterId);
return false;
}
@@ -241,9 +245,7 @@
}
void Filter::freeAvHandle() {
- if (mType.mainType != DemuxFilterMainType::TS ||
- (mType.subType.tsFilterType() == DemuxTsFilterType::AUDIO &&
- mType.subType.tsFilterType() == DemuxTsFilterType::VIDEO)) {
+ if (!mIsMediaFilter) {
return;
}
for (int i = 0; i < mFilterEvent.events.size(); i++) {
@@ -288,13 +290,11 @@
void Filter::updateFilterOutput(vector<uint8_t> data) {
std::lock_guard<std::mutex> lock(mFilterOutputLock);
- ALOGD("[Filter] filter output updated");
mFilterOutput.insert(mFilterOutput.end(), data.begin(), data.end());
}
void Filter::updateRecordOutput(vector<uint8_t> data) {
std::lock_guard<std::mutex> lock(mRecordFilterOutputLock);
- ALOGD("[Filter] record filter output updated");
mRecordFilterOutput.insert(mRecordFilterOutput.end(), data.begin(), data.end());
}
@@ -436,7 +436,6 @@
if (mFilterOutput.empty()) {
return Result::SUCCESS;
}
-
for (int i = 0; i < mFilterOutput.size(); i += 188) {
if (mPesSizeLeft == 0) {
uint32_t prefix = (mFilterOutput[i + 4] << 16) | (mFilterOutput[i + 5] << 8) |
diff --git a/tv/tuner/1.0/default/Filter.h b/tv/tuner/1.0/default/Filter.h
index afed98e..9b49ad8 100644
--- a/tv/tuner/1.0/default/Filter.h
+++ b/tv/tuner/1.0/default/Filter.h
@@ -103,6 +103,7 @@
uint32_t mFilterId;
uint32_t mBufferSize;
DemuxFilterType mType;
+ bool mIsMediaFilter = 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 b509599..996b6ef 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -139,6 +139,30 @@
status.snr(221);
break;
}
+ case FrontendStatusType::BER: {
+ status.ber(1);
+ break;
+ }
+ case FrontendStatusType::PER: {
+ status.per(2);
+ break;
+ }
+ case FrontendStatusType::PRE_BER: {
+ status.preBer(3);
+ break;
+ }
+ case FrontendStatusType::SIGNAL_QUALITY: {
+ status.signalQuality(4);
+ break;
+ }
+ case FrontendStatusType::SIGNAL_STRENGTH: {
+ status.signalStrength(5);
+ break;
+ }
+ case FrontendStatusType::SYMBOL_RATE: {
+ status.symbolRate(6);
+ break;
+ }
case FrontendStatusType::FEC: {
status.innerFec(FrontendInnerFec::FEC_2_9); // value = 1 << 7
break;
@@ -149,15 +173,51 @@
status.modulation(modulationStatus);
break;
}
+ case FrontendStatusType::SPECTRAL: {
+ status.inversion(FrontendDvbcSpectralInversion::NORMAL);
+ break;
+ }
+ case FrontendStatusType::LNB_VOLTAGE: {
+ status.lnbVoltage(LnbVoltage::VOLTAGE_5V);
+ break;
+ }
case FrontendStatusType::PLP_ID: {
status.plpId(101); // type uint8_t
break;
}
+ case FrontendStatusType::EWBS: {
+ status.isEWBS(false);
+ break;
+ }
+ case FrontendStatusType::AGC: {
+ status.agc(7);
+ break;
+ }
+ case FrontendStatusType::LNA: {
+ status.isLnaOn(false);
+ break;
+ }
case FrontendStatusType::LAYER_ERROR: {
vector<bool> v = {false, true, true};
status.isLayerError(v);
break;
}
+ case FrontendStatusType::MER: {
+ status.mer(8);
+ break;
+ }
+ case FrontendStatusType::FREQ_OFFSET: {
+ status.freqOffset(9);
+ break;
+ }
+ case FrontendStatusType::HIERARCHY: {
+ status.hierarchy(FrontendDvbtHierarchy::HIERARCHY_1_NATIVE);
+ break;
+ }
+ case FrontendStatusType::RF_LOCK: {
+ status.isRfLocked(false);
+ break;
+ }
case FrontendStatusType::ATSC3_PLP_INFO: {
vector<FrontendStatusAtsc3PlpInfo> v;
FrontendStatusAtsc3PlpInfo info1{
diff --git a/tv/tuner/1.0/default/Frontend.h b/tv/tuner/1.0/default/Frontend.h
index 8a30b91..65537d7 100644
--- a/tv/tuner/1.0/default/Frontend.h
+++ b/tv/tuner/1.0/default/Frontend.h
@@ -76,7 +76,7 @@
FrontendId mId = 0;
bool mIsLocked = false;
- const string FRONTEND_STREAM_FILE = "/vendor/etc/dumpTs3.ts";
+ const string FRONTEND_STREAM_FILE = "/vendor/etc/segment000000.ts";
std::ifstream mFrontendData;
};
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index b1f2490..821d83f 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -161,34 +161,27 @@
return Void();
}
- switch (mFrontends[frontendId]->getFrontendType()) {
- case FrontendType::DVBT:
- info.type = FrontendType::DVBT;
- break;
- default:
- vector<FrontendStatusType> statusCaps = {
- FrontendStatusType::DEMOD_LOCK,
- FrontendStatusType::SNR,
- FrontendStatusType::FEC,
- FrontendStatusType::MODULATION,
- FrontendStatusType::PLP_ID,
- FrontendStatusType::LAYER_ERROR,
- FrontendStatusType::ATSC3_PLP_INFO,
- };
- // assign randomly selected values for testing.
- info = {
- .type = mFrontends[frontendId]->getFrontendType(),
- .minFrequency = 139,
- .maxFrequency = 1139,
- .minSymbolRate = 45,
- .maxSymbolRate = 1145,
- .acquireRange = 30,
- .exclusiveGroupId = 57,
- .statusCaps = statusCaps,
- .frontendCaps = mFrontendCaps[frontendId],
- };
- break;
- }
+ vector<FrontendStatusType> statusCaps = {
+ FrontendStatusType::DEMOD_LOCK,
+ FrontendStatusType::SNR,
+ FrontendStatusType::FEC,
+ FrontendStatusType::MODULATION,
+ FrontendStatusType::PLP_ID,
+ FrontendStatusType::LAYER_ERROR,
+ FrontendStatusType::ATSC3_PLP_INFO,
+ };
+ // assign randomly selected values for testing.
+ info = {
+ .type = mFrontends[frontendId]->getFrontendType(),
+ .minFrequency = 139,
+ .maxFrequency = 1139,
+ .minSymbolRate = 45,
+ .maxSymbolRate = 1145,
+ .acquireRange = 30,
+ .exclusiveGroupId = 57,
+ .statusCaps = statusCaps,
+ .frontendCaps = mFrontendCaps[frontendId],
+ };
_hidl_cb(Result::SUCCESS, info);
return Void();
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.cpp b/tv/tuner/1.0/vts/functional/DvrTests.cpp
index a1ce23d..7e7f8e6 100644
--- a/tv/tuner/1.0/vts/functional/DvrTests.cpp
+++ b/tv/tuner/1.0/vts/functional/DvrTests.cpp
@@ -16,17 +16,13 @@
#include "DvrTests.h"
-void DvrCallback::startPlaybackInputThread(PlaybackConf playbackConf,
+void DvrCallback::startPlaybackInputThread(string& dataInputFile, PlaybackSettings& settings,
MQDesc& playbackMQDescriptor) {
+ mInputDataFile = dataInputFile;
+ mPlaybackSettings = settings;
mPlaybackMQ = std::make_unique<FilterMQ>(playbackMQDescriptor, true /* resetPointers */);
EXPECT_TRUE(mPlaybackMQ);
- struct PlaybackThreadArgs* threadArgs =
- (struct PlaybackThreadArgs*)malloc(sizeof(struct PlaybackThreadArgs));
- threadArgs->user = this;
- threadArgs->playbackConf = &playbackConf;
- threadArgs->keepWritingPlaybackFMQ = &mKeepWritingPlaybackFMQ;
-
- pthread_create(&mPlaybackThread, NULL, __threadLoopPlayback, (void*)threadArgs);
+ pthread_create(&mPlaybackThread, NULL, __threadLoopPlayback, this);
pthread_setname_np(mPlaybackThread, "test_playback_input_loop");
}
@@ -37,15 +33,13 @@
android::Mutex::Autolock autoLock(mPlaybackThreadLock);
}
-void* DvrCallback::__threadLoopPlayback(void* threadArgs) {
- DvrCallback* const self =
- static_cast<DvrCallback*>(((struct PlaybackThreadArgs*)threadArgs)->user);
- self->playbackThreadLoop(((struct PlaybackThreadArgs*)threadArgs)->playbackConf,
- ((struct PlaybackThreadArgs*)threadArgs)->keepWritingPlaybackFMQ);
+void* DvrCallback::__threadLoopPlayback(void* user) {
+ DvrCallback* const self = static_cast<DvrCallback*>(user);
+ self->playbackThreadLoop();
return 0;
}
-void DvrCallback::playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ) {
+void DvrCallback::playbackThreadLoop() {
android::Mutex::Autolock autoLock(mPlaybackThreadLock);
mPlaybackThreadRunning = true;
@@ -56,10 +50,10 @@
android::OK);
// open the stream and get its length
- std::ifstream inputData(playbackConf->inputDataFile, std::ifstream::binary);
- int writeSize = playbackConf->setting.packetSize * 6;
+ 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", playbackConf->inputDataFile.c_str());
+ ALOGW("[vts] playback thread loop start %s!", mInputDataFile.c_str());
if (!inputData.is_open()) {
mPlaybackThreadRunning = false;
ALOGW("[vts] Error %s", strerror(errno));
@@ -67,7 +61,7 @@
while (mPlaybackThreadRunning) {
// move the stream pointer for packet size * 6 every read until the end
- while (*keepWritingPlaybackFMQ) {
+ while (mKeepWritingPlaybackFMQ) {
inputData.read(buffer, writeSize);
if (!inputData) {
int leftSize = inputData.gcount();
@@ -105,6 +99,7 @@
while (mDataOutputBuffer.empty()) {
if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
EXPECT_TRUE(false) << "record output matching pid does not output within timeout";
+ stopRecordThread();
return;
}
}
@@ -138,6 +133,7 @@
ALOGD("[vts] DvrCallback record threadLoop start.");
android::Mutex::Autolock autoLock(mRecordThreadLock);
mRecordThreadRunning = true;
+ mKeepReadingRecordFMQ = true;
// Create the EventFlag that is used to signal the HAL impl that data have been
// read from the Record FMQ
@@ -183,21 +179,23 @@
void DvrCallback::stopRecordThread() {
mKeepReadingRecordFMQ = false;
mRecordThreadRunning = false;
- android::Mutex::Autolock autoLock(mRecordThreadLock);
}
-AssertionResult DvrTests::openDvrInDemux(DvrType type) {
+AssertionResult DvrTests::openDvrInDemux(DvrType type, uint32_t bufferSize) {
Result status;
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
// Create dvr callback
mDvrCallback = new DvrCallback();
- mDemux->openDvr(type, FMQ_SIZE_1M, mDvrCallback, [&](Result result, const sp<IDvr>& dvr) {
+ mDemux->openDvr(type, bufferSize, mDvrCallback, [&](Result result, const sp<IDvr>& dvr) {
mDvr = dvr;
status = result;
});
+ if (status == Result::SUCCESS) {
+ mDvrCallback->setDvr(mDvr);
+ }
return AssertionResult(status == Result::SUCCESS);
}
@@ -264,4 +262,4 @@
ASSERT_TRUE(mDemux);
ASSERT_TRUE(mDvr);
ASSERT_TRUE(mDvr->close() == Result::SUCCESS);
-}
\ No newline at end of file
+}
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.h b/tv/tuner/1.0/vts/functional/DvrTests.h
index 74ef58e..dd00c27 100644
--- a/tv/tuner/1.0/vts/functional/DvrTests.h
+++ b/tv/tuner/1.0/vts/functional/DvrTests.h
@@ -54,15 +54,10 @@
#define WAIT_TIMEOUT 3000000000
-struct PlaybackConf {
- string inputDataFile;
- PlaybackSettings setting;
-};
-
class DvrCallback : public IDvrCallback {
public:
virtual Return<void> onRecordStatus(DemuxFilterStatus status) override {
- ALOGW("[vts] record status %hhu", status);
+ ALOGD("[vts] record status %hhu", status);
switch (status) {
case DemuxFilterStatus::DATA_READY:
break;
@@ -70,7 +65,12 @@
break;
case DemuxFilterStatus::HIGH_WATER:
case DemuxFilterStatus::OVERFLOW:
- ALOGW("[vts] record overflow. Flushing");
+ ALOGD("[vts] record overflow. Flushing.");
+ EXPECT_TRUE(mDvr) << "Dvr callback is not set with an IDvr";
+ if (mDvr) {
+ Result result = mDvr->flush();
+ ALOGD("[vts] Flushing result %d.", result);
+ }
break;
}
return Void();
@@ -78,16 +78,16 @@
virtual Return<void> onPlaybackStatus(PlaybackStatus status) override {
// android::Mutex::Autolock autoLock(mMsgLock);
- ALOGW("[vts] playback status %d", status);
+ ALOGD("[vts] playback status %d", status);
switch (status) {
case PlaybackStatus::SPACE_EMPTY:
case PlaybackStatus::SPACE_ALMOST_EMPTY:
- ALOGW("[vts] keep playback inputing %d", status);
+ ALOGD("[vts] keep playback inputing %d", status);
mKeepWritingPlaybackFMQ = true;
break;
case PlaybackStatus::SPACE_ALMOST_FULL:
case PlaybackStatus::SPACE_FULL:
- ALOGW("[vts] stop playback inputing %d", status);
+ ALOGD("[vts] stop playback inputing %d", status);
mKeepWritingPlaybackFMQ = false;
break;
}
@@ -98,21 +98,19 @@
void testRecordOutput();
void stopRecordThread();
- void startPlaybackInputThread(PlaybackConf playbackConf, MQDesc& playbackMQDescriptor);
+ void startPlaybackInputThread(string& dataInputFile, PlaybackSettings& settings,
+ MQDesc& playbackMQDescriptor);
void startRecordOutputThread(RecordSettings recordSettings, MQDesc& recordMQDescriptor);
- static void* __threadLoopPlayback(void* threadArgs);
+ static void* __threadLoopPlayback(void* user);
static void* __threadLoopRecord(void* threadArgs);
- void playbackThreadLoop(PlaybackConf* playbackConf, bool* keepWritingPlaybackFMQ);
+ void playbackThreadLoop();
void recordThreadLoop(RecordSettings* recordSetting, bool* keepWritingPlaybackFMQ);
bool readRecordFMQ();
+ void setDvr(sp<IDvr> dvr) { mDvr = dvr; }
+
private:
- struct PlaybackThreadArgs {
- DvrCallback* user;
- PlaybackConf* playbackConf;
- bool* keepWritingPlaybackFMQ;
- };
struct RecordThreadArgs {
DvrCallback* user;
RecordSettings* recordSettings;
@@ -137,6 +135,10 @@
bool mRecordThreadRunning;
pthread_t mPlaybackThread;
pthread_t mRecordThread;
+ string mInputDataFile;
+ PlaybackSettings mPlaybackSettings;
+
+ sp<IDvr> mDvr = nullptr;
// int mPidFilterOutputCount = 0;
};
@@ -146,12 +148,8 @@
void setService(sp<ITuner> tuner) { mService = tuner; }
void setDemux(sp<IDemux> demux) { mDemux = demux; }
- void startPlaybackInputThread(string dataInputFile, PlaybackSettings settings) {
- PlaybackConf conf{
- .inputDataFile = dataInputFile,
- .setting = settings,
- };
- mDvrCallback->startPlaybackInputThread(conf, mDvrMQDescriptor);
+ void startPlaybackInputThread(string& dataInputFile, PlaybackSettings& settings) {
+ mDvrCallback->startPlaybackInputThread(dataInputFile, settings, mDvrMQDescriptor);
};
void startRecordOutputThread(RecordSettings settings) {
@@ -162,7 +160,7 @@
void testRecordOutput() { mDvrCallback->testRecordOutput(); }
void stopRecordThread() { mDvrCallback->stopPlaybackThread(); }
- AssertionResult openDvrInDemux(DvrType type);
+ AssertionResult openDvrInDemux(DvrType type, uint32_t bufferSize);
AssertionResult configDvr(DvrSettings setting);
AssertionResult getDvrMQDescriptor();
AssertionResult attachFilterToDvr(sp<IFilter> filter);
@@ -184,4 +182,4 @@
pthread_t mPlaybackshread;
bool mPlaybackThreadRunning;
-};
\ No newline at end of file
+};
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.cpp b/tv/tuner/1.0/vts/functional/FilterTests.cpp
index 82e955d..4639e59 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.0/vts/functional/FilterTests.cpp
@@ -128,7 +128,7 @@
return true;
}
-AssertionResult FilterTests::openFilterInDemux(DemuxFilterType type) {
+AssertionResult FilterTests::openFilterInDemux(DemuxFilterType type, uint32_t bufferSize) {
Result status;
EXPECT_TRUE(mDemux) << "Test with openDemux first.";
@@ -136,7 +136,7 @@
mFilterCallback = new FilterCallback();
// Add filter to the local demux
- mDemux->openFilter(type, FMQ_SIZE_16M, mFilterCallback,
+ mDemux->openFilter(type, bufferSize, mFilterCallback,
[&](Result result, const sp<IFilter>& filter) {
mFilter = filter;
status = result;
@@ -223,4 +223,4 @@
mFilters.erase(filterId);
}
return AssertionResult(status == Result::SUCCESS);
-}
\ No newline at end of file
+}
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.h b/tv/tuner/1.0/vts/functional/FilterTests.h
index dc798c9..71efce4 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.h
+++ b/tv/tuner/1.0/vts/functional/FilterTests.h
@@ -78,9 +78,6 @@
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
using MQDesc = MQDescriptorSync<uint8_t>;
-const uint32_t FMQ_SIZE_1M = 0x100000;
-const uint32_t FMQ_SIZE_16M = 0x1000000;
-
#define WAIT_TIMEOUT 3000000000
class FilterCallback : public IFilterCallback {
@@ -153,7 +150,7 @@
std::map<uint32_t, sp<FilterCallback>> getFilterCallbacks() { return mFilterCallbacks; }
- AssertionResult openFilterInDemux(DemuxFilterType type);
+ AssertionResult openFilterInDemux(DemuxFilterType type, uint32_t bufferSize);
AssertionResult getNewlyOpenedFilterId(uint32_t& filterId);
AssertionResult configFilter(DemuxFilterSettings setting, uint32_t filterId);
AssertionResult getFilterMQDescriptor(uint32_t filterId);
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index f211be2..c44f77d 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -17,7 +17,7 @@
#include "VtsHalTvTunerV1_0TargetTest.h"
namespace {
-/*======================== Start Descrambler APIs Tests Implementation ========================*/
+
AssertionResult TunerHidlTest::createDescrambler(uint32_t demuxId) {
Result status;
mService->openDescrambler([&](Result result, const sp<IDescrambler>& descrambler) {
@@ -46,10 +46,8 @@
mDescrambler = nullptr;
return AssertionResult(status == Result::SUCCESS);
}
-/*========================= End Descrambler APIs Tests Implementation =========================*/
-/*========================== Start Data Flow Tests Implementation ==========================*/
-AssertionResult TunerHidlTest::broadcastDataFlowTest(vector<string> /*goldenOutputFiles*/) {
+AssertionResult TunerBroadcastHidlTest::filterDataOutputTest(vector<string> /*goldenOutputFiles*/) {
// Data Verify Module
std::map<uint32_t, sp<FilterCallback>>::iterator it;
std::map<uint32_t, sp<FilterCallback>> filterCallbacks = mFilterTests.getFilterCallbacks();
@@ -59,154 +57,43 @@
return success();
}
-/*
- * TODO: re-enable the tests after finalizing the test refactoring.
- */
-/*AssertionResult TunerHidlTest::playbackDataFlowTest(
- vector<FilterConf> filterConf, PlaybackConf playbackConf,
- vector<string> \/\*goldenOutputFiles\*\/) {
- Result status;
- int filterIdsSize;
- // Filter Configuration Module
- for (int i = 0; i < filterConf.size(); i++) {
- if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
- failure() ||
- // TODO use a map to save the FMQs/EvenFlags and pass to callback
- getFilterMQDescriptor() == failure()) {
- return failure();
- }
- filterIdsSize = mUsedFilterIds.size();
- mUsedFilterIds.resize(filterIdsSize + 1);
- mUsedFilterIds[filterIdsSize] = mFilterId;
- mFilters[mFilterId] = mFilter;
- mFilterCallbacks[mFilterId] = mFilterCallback;
- mFilterCallback->updateFilterMQ(mFilterMQDescriptor);
- // mDemuxCallback->updateGoldenOutputMap(goldenOutputFiles[i]);
- status = mFilter->start();
- if (status != Result::SUCCESS) {
- return failure();
- }
- }
-
- // Playback Input Module
- PlaybackSettings playbackSetting = playbackConf.setting;
- if (addPlaybackToDemux(playbackSetting) == failure() ||
- getPlaybackMQDescriptor() == failure()) {
- return failure();
- }
- for (int i = 0; i <= filterIdsSize; i++) {
- if (mDvr->attachFilter(mFilters[mUsedFilterIds[i]]) != Result::SUCCESS) {
- return failure();
- }
- }
- mDvrCallback->startPlaybackInputThread(playbackConf, mPlaybackMQDescriptor);
- status = mDvr->start();
- if (status != Result::SUCCESS) {
- return failure();
- }
-
+AssertionResult TunerPlaybackHidlTest::filterDataOutputTest(vector<string> /*goldenOutputFiles*/) {
// Data Verify Module
std::map<uint32_t, sp<FilterCallback>>::iterator it;
- for (it = mFilterCallbacks.begin(); it != mFilterCallbacks.end(); it++) {
+ std::map<uint32_t, sp<FilterCallback>> filterCallbacks = mFilterTests.getFilterCallbacks();
+ for (it = filterCallbacks.begin(); it != filterCallbacks.end(); it++) {
it->second->testFilterDataOutput();
}
- mDvrCallback->stopPlaybackThread();
-
- // Clean Up Module
- for (int i = 0; i <= filterIdsSize; i++) {
- if (mFilters[mUsedFilterIds[i]]->stop() != Result::SUCCESS) {
- return failure();
- }
- }
- if (mDvr->stop() != Result::SUCCESS) {
- return failure();
- }
- mUsedFilterIds.clear();
- mFilterCallbacks.clear();
- mFilters.clear();
- return closeDemux();
+ return success();
}
-AssertionResult TunerHidlTest::recordDataFlowTest(vector<FilterConf> filterConf,
- RecordSettings recordSetting,
- vector<string> goldenOutputFiles) {
- Result status;
- hidl_vec<FrontendId> feIds;
+void TunerFilterHidlTest::configSingleFilterInDemuxTest(FilterConfig filterConf,
+ FrontendConfig frontendConf) {
+ uint32_t feId;
+ uint32_t demuxId;
+ sp<IDemux> demux;
+ uint32_t filterId;
- mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
- status = result;
- feIds = frontendIds;
- });
+ mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+ ASSERT_TRUE(feId != INVALID_ID);
+ ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+ ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+ mFilterTests.setDemux(demux);
+ 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));
+ ASSERT_TRUE(mFilterTests.startFilter(filterId));
+ ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
- if (feIds.size() == 0) {
- ALOGW("[ WARN ] Frontend isn't available");
- return failure();
- }
-
- FrontendDvbtSettings dvbt{
- .frequency = 1000,
- };
- FrontendSettings settings;
- settings.dvbt(dvbt);
-
- int filterIdsSize;
- // Filter Configuration Module
- for (int i = 0; i < filterConf.size(); i++) {
- if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
- failure() ||
- // TODO use a map to save the FMQs/EvenFlags and pass to callback
- getFilterMQDescriptor() == failure()) {
- return failure();
- }
- filterIdsSize = mUsedFilterIds.size();
- mUsedFilterIds.resize(filterIdsSize + 1);
- mUsedFilterIds[filterIdsSize] = mFilterId;
- mFilters[mFilterId] = mFilter;
- }
-
- // Record Config Module
- if (addRecordToDemux(recordSetting) == failure() ||
- getRecordMQDescriptor() == failure()) {
- return failure();
- }
- for (int i = 0; i <= filterIdsSize; i++) {
- if (mDvr->attachFilter(mFilters[mUsedFilterIds[i]]) != Result::SUCCESS) {
- return failure();
- }
- }
-
- mDvrCallback->startRecordOutputThread(recordSetting, mRecordMQDescriptor);
- status = mDvr->start();
- if (status != Result::SUCCESS) {
- return failure();
- }
-
- if (setDemuxFrontendDataSource(feIds[0]) != success()) {
- return failure();
- }
-
- // Data Verify Module
- mDvrCallback->testRecordOutput();
-
- // Clean Up Module
- for (int i = 0; i <= filterIdsSize; i++) {
- if (mFilters[mUsedFilterIds[i]]->stop() != Result::SUCCESS) {
- return failure();
- }
- }
- if (mFrontend->stopTune() != Result::SUCCESS) {
- return failure();
- }
- mUsedFilterIds.clear();
- mFilterCallbacks.clear();
- mFilters.clear();
- return closeDemux();
-}*/
-/*========================= End Data Flow Tests Implementation =========================*/
-
-/*================================= Start Test Module =================================*/
-void TunerHidlTest::broadcastSingleFilterTest(FilterConfig filterConf,
- FrontendConfig frontendConf) {
+void TunerBroadcastHidlTest::broadcastSingleFilterTest(FilterConfig filterConf,
+ FrontendConfig frontendConf) {
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -224,15 +111,14 @@
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFilterTests.setDemux(demux);
- ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type));
+ 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));
ASSERT_TRUE(mFilterTests.startFilter(filterId));
// tune test
ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf));
- // broadcast data flow test
- ASSERT_TRUE(broadcastDataFlowTest(goldenOutputFiles));
+ ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
ASSERT_TRUE(mFrontendTests.stopTuneFrontend());
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
@@ -240,9 +126,35 @@
ASSERT_TRUE(mFrontendTests.closeFrontend());
}
-void TunerDvrHidlTest::attachSingleFilterToDvrTest(FilterConfig filterConf,
- FrontendConfig frontendConf, DvrConfig dvrConf) {
- description("Open and configure a Dvr in Demux.");
+void TunerPlaybackHidlTest::playbackSingleFilterTest(FilterConfig filterConf, DvrConfig dvrConf) {
+ uint32_t demuxId;
+ sp<IDemux> demux;
+ uint32_t filterId;
+
+ ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+ 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(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(mFilterTests.startFilter(filterId));
+ ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
+ mDvrTests.stopPlaybackThread();
+ ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mDvrTests.stopDvr());
+ ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+ mDvrTests.closeDvr();
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+}
+
+void TunerRecordHidlTest::recordSingleFilterTest(FilterConfig filterConf,
+ FrontendConfig frontendConf, DvrConfig dvrConf) {
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -257,31 +169,40 @@
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFilterTests.setDemux(demux);
mDvrTests.setDemux(demux);
- ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type));
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
ASSERT_TRUE(mDvrTests.configDvr(dvrConf.settings));
ASSERT_TRUE(mDvrTests.getDvrMQDescriptor());
- ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type));
+ 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));
- ASSERT_TRUE(mFilterTests.startFilter(filterId));
filter = mFilterTests.getFilterById(filterId);
ASSERT_TRUE(filter != nullptr);
+ mDvrTests.startRecordOutputThread(dvrConf.settings.record());
ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
- ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
+ ASSERT_TRUE(mDvrTests.startDvr());
+ ASSERT_TRUE(mFilterTests.startFilter(filterId));
+ ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf));
+ mDvrTests.testRecordOutput();
+ mDvrTests.stopRecordThread();
+ ASSERT_TRUE(mFrontendTests.stopTuneFrontend());
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mDvrTests.stopDvr());
+ ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
mDvrTests.closeDvr();
ASSERT_TRUE(mDemuxTests.closeDemux());
ASSERT_TRUE(mFrontendTests.closeFrontend());
}
-void TunerFilterHidlTest::configSingleFilterInDemuxTest(FilterConfig filterConf,
- FrontendConfig frontendConf) {
+void TunerRecordHidlTest::attachSingleFilterToRecordDvrTest(FilterConfig filterConf,
+ FrontendConfig frontendConf,
+ DvrConfig dvrConf) {
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
uint32_t filterId;
+ sp<IFilter> filter;
mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
ASSERT_TRUE(feId != INVALID_ID);
@@ -290,20 +211,28 @@
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
mFilterTests.setDemux(demux);
- ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type));
+ mDvrTests.setDemux(demux);
+ ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
+ ASSERT_TRUE(mDvrTests.configDvr(dvrConf.settings));
+ ASSERT_TRUE(mDvrTests.getDvrMQDescriptor());
+ 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));
+ filter = mFilterTests.getFilterById(filterId);
+ ASSERT_TRUE(filter != nullptr);
+ ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
+ ASSERT_TRUE(mDvrTests.startDvr());
ASSERT_TRUE(mFilterTests.startFilter(filterId));
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+ ASSERT_TRUE(mDvrTests.stopDvr());
+ ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+ mDvrTests.closeDvr();
ASSERT_TRUE(mDemuxTests.closeDemux());
ASSERT_TRUE(mFrontendTests.closeFrontend());
}
-/*================================== End Test Module ==================================*/
-/***************************** End Test Implementation *****************************/
-/******************************** Start Test Entry **********************************/
TEST_P(TunerFrontendHidlTest, TuneFrontend) {
description("Tune one Frontend with specific setting and check Lock event");
mFrontendTests.tuneTest(frontendArray[DVBT]);
@@ -331,6 +260,7 @@
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
ASSERT_TRUE(mDemuxTests.closeDemux());
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
}
TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
@@ -339,23 +269,43 @@
configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
}
-TEST_P(TunerDvrHidlTest, AttachFiltersToRecordTest) {
+TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowVideoFilterTest) {
+ description("Test Video Filter functionality in Broadcast use case.");
+ broadcastSingleFilterTest(filterArray[TS_VIDEO1], frontendArray[DVBS]);
+}
+
+TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowAudioFilterTest) {
+ description("Test Audio Filter functionality in Broadcast use case.");
+ broadcastSingleFilterTest(filterArray[TS_AUDIO0], frontendArray[DVBS]);
+}
+
+TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowSectionFilterTest) {
+ description("Test Section Filter functionality in Broadcast use case.");
+ broadcastSingleFilterTest(filterArray[TS_SECTION0], frontendArray[DVBS]);
+}
+
+TEST_P(TunerBroadcastHidlTest, IonBufferTest) {
+ description("Test the av filter data bufferring.");
+ broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[DVBS]);
+}
+
+TEST_P(TunerPlaybackHidlTest, PlaybackDataFlowWithTsSectionFilterTest) {
+ description("Feed ts data from playback and configure Ts section filter to get output");
+ playbackSingleFilterTest(filterArray[TS_SECTION0], dvrArray[DVR_PLAYBACK0]);
+}
+
+TEST_P(TunerRecordHidlTest, AttachFiltersToRecordTest) {
description("Attach a single filter to the record dvr test.");
// TODO use paramterized tests
- attachSingleFilterToDvrTest(filterArray[TS_VIDEO0], frontendArray[DVBT], dvrArray[DVR_RECORD0]);
+ attachSingleFilterToRecordDvrTest(filterArray[TS_RECORD0], frontendArray[DVBT],
+ dvrArray[DVR_RECORD0]);
}
-TEST_P(TunerDvrHidlTest, AttachFiltersToPlaybackTest) {
- description("Attach a single filter to the playback dvr test.");
- // TODO use paramterized tests
- attachSingleFilterToDvrTest(filterArray[TS_VIDEO0], frontendArray[DVBT],
- dvrArray[DVR_PLAYBACK0]);
+TEST_P(TunerRecordHidlTest, RecordDataFlowWithTsRecordFilterTest) {
+ description("Feed ts data from frontend to recording and test with ts record filter");
+ recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[DVBT], dvrArray[DVR_RECORD0]);
}
-/*============================ Start Descrambler Tests ============================*/
-/*
- * TODO: re-enable the tests after finalizing the test refactoring.
- */
TEST_P(TunerHidlTest, CreateDescrambler) {
description("Create Descrambler");
uint32_t feId;
@@ -368,133 +318,17 @@
ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
ASSERT_TRUE(createDescrambler(demuxId));
- ASSERT_TRUE(mDemuxTests.closeDemux());
ASSERT_TRUE(closeDescrambler());
+ ASSERT_TRUE(mDemuxTests.closeDemux());
+ ASSERT_TRUE(mFrontendTests.closeFrontend());
}
-/*============================== End Descrambler Tests ==============================*/
-
-/*============================== Start Data Flow Tests ==============================*/
-TEST_P(TunerHidlTest, BroadcastDataFlowVideoFilterTest) {
- description("Test Video Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_VIDEO1], frontendArray[DVBS]);
-}
-
-TEST_P(TunerHidlTest, BroadcastDataFlowAudioFilterTest) {
- description("Test Audio Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_AUDIO0], frontendArray[DVBS]);
-}
-
-TEST_P(TunerHidlTest, BroadcastDataFlowTsFilterTest) {
- description("Test TS Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_TS0], frontendArray[DVBS]);
-}
-
-TEST_P(TunerHidlTest, BroadcastDataFlowSectionFilterTest) {
- description("Test Section Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_SECTION0], frontendArray[DVBS]);
-}
-
-TEST_P(TunerHidlTest, IonBufferTest) {
- description("Test the av filter data bufferring.");
- broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[DVBS]);
-}
-/*
- * TODO: re-enable the tests after finalizing the testing stream.
- */
-/*TEST_P(TunerHidlTest, PlaybackDataFlowWithSectionFilterTest) {
- description("Feed ts data from playback and configure pes filter to get output");
-
- // todo modulize the filter conf parser
- vector<FilterConf> filterConf;
- filterConf.resize(1);
-
- DemuxFilterSettings filterSetting;
- DemuxTsFilterSettings tsFilterSetting{
- .tpid = 18,
- };
- DemuxFilterSectionSettings sectionFilterSetting;
- tsFilterSetting.filterSettings.section(sectionFilterSetting);
- filterSetting.ts(tsFilterSetting);
-
- DemuxFilterType type{
- .mainType = DemuxFilterMainType::TS,
- };
- type.subType.tsFilterType(DemuxTsFilterType::SECTION);
- FilterConf sectionFilterConf{
- .type = type,
- .setting = filterSetting,
- };
- filterConf[0] = sectionFilterConf;
-
- PlaybackSettings playbackSetting{
- .statusMask = 0xf,
- .lowThreshold = 0x1000,
- .highThreshold = 0x07fff,
- .dataFormat = DataFormat::TS,
- .packetSize = 188,
- };
-
- PlaybackConf playbackConf{
- .inputDataFile = "/vendor/etc/test1.ts",
- .setting = playbackSetting,
- };
-
- vector<string> goldenOutputFiles;
-
- ASSERT_TRUE(playbackDataFlowTest(filterConf, playbackConf, goldenOutputFiles));
-}
-
-TEST_P(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) {
- description("Feed ts data from frontend to recording and test with ts record filter");
-
- // todo modulize the filter conf parser
- vector<FilterConf> filterConf;
- filterConf.resize(1);
-
- DemuxFilterSettings filterSetting;
- DemuxTsFilterSettings tsFilterSetting{
- .tpid = 119,
- };
- DemuxFilterRecordSettings recordFilterSetting;
- tsFilterSetting.filterSettings.record(recordFilterSetting);
- filterSetting.ts(tsFilterSetting);
-
- DemuxFilterType type{
- .mainType = DemuxFilterMainType::TS,
- };
- type.subType.tsFilterType(DemuxTsFilterType::RECORD);
- FilterConf recordFilterConf{
- .type = type,
- .setting = filterSetting,
- };
- filterConf[0] = recordFilterConf;
-
- RecordSettings recordSetting{
- .statusMask = 0xf,
- .lowThreshold = 0x1000,
- .highThreshold = 0x07fff,
- .dataFormat = DataFormat::TS,
- .packetSize = 188,
- };
-
- vector<string> goldenOutputFiles;
-
- ASSERT_TRUE(recordDataFlowTest(filterConf, recordSetting, goldenOutputFiles));
-}*/
-/*============================== End Data Flow Tests ==============================*/
-/******************************** End Test Entry **********************************/
INSTANTIATE_TEST_SUITE_P(
PerInstance, TunerFrontendHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
android::hardware::PrintInstanceNameToString);
INSTANTIATE_TEST_SUITE_P(
- PerInstance, TunerHidlTest,
- 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);
@@ -505,7 +339,22 @@
android::hardware::PrintInstanceNameToString);
INSTANTIATE_TEST_SUITE_P(
- PerInstance, TunerDvrHidlTest,
+ PerInstance, TunerBroadcastHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, TunerPlaybackHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, TunerRecordHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, TunerHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITuner::descriptor)),
android::hardware::PrintInstanceNameToString);
} // namespace
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
index 37b2866..21a9855 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
@@ -33,13 +33,19 @@
namespace {
+void initConfiguration() {
+ initFrontendConfig();
+ initFrontendScanConfig();
+ initFilterConfig();
+ initDvrConfig();
+}
+
class TunerFrontendHidlTest : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initFrontendConfig();
- initFrontendScanConfig();
+ initConfiguration();
mFrontendTests.setService(mService);
}
@@ -58,9 +64,7 @@
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initFrontendConfig();
- initFrontendScanConfig();
- initFilterConfig();
+ initConfiguration();
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
@@ -81,9 +85,7 @@
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initFrontendConfig();
- initFrontendScanConfig();
- initFilterConfig();
+ initConfiguration();
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
@@ -103,15 +105,39 @@
FilterTests mFilterTests;
};
-class TunerDvrHidlTest : public testing::TestWithParam<std::string> {
+class TunerBroadcastHidlTest : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initFrontendConfig();
- initFrontendScanConfig();
- initFilterConfig();
- initDvrConfig();
+ initConfiguration();
+
+ mFrontendTests.setService(mService);
+ mDemuxTests.setService(mService);
+ mFilterTests.setService(mService);
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ sp<ITuner> mService;
+ FrontendTests mFrontendTests;
+ DemuxTests mDemuxTests;
+ FilterTests mFilterTests;
+
+ AssertionResult filterDataOutputTest(vector<string> goldenOutputFiles);
+
+ void broadcastSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf);
+};
+
+class TunerPlaybackHidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ mService = ITuner::getService(GetParam());
+ ASSERT_NE(mService, nullptr);
+ initConfiguration();
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
@@ -124,8 +150,39 @@
RecordProperty("description", description);
}
- void attachSingleFilterToDvrTest(FilterConfig filterConf, FrontendConfig frontendConf,
- DvrConfig dvrConf);
+ sp<ITuner> mService;
+ FrontendTests mFrontendTests;
+ DemuxTests mDemuxTests;
+ FilterTests mFilterTests;
+ DvrTests mDvrTests;
+
+ AssertionResult filterDataOutputTest(vector<string> goldenOutputFiles);
+
+ void playbackSingleFilterTest(FilterConfig filterConf, DvrConfig dvrConf);
+};
+
+class TunerRecordHidlTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ mService = ITuner::getService(GetParam());
+ ASSERT_NE(mService, nullptr);
+ initConfiguration();
+
+ mFrontendTests.setService(mService);
+ mDemuxTests.setService(mService);
+ mFilterTests.setService(mService);
+ mDvrTests.setService(mService);
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ void attachSingleFilterToRecordDvrTest(FilterConfig filterConf, FrontendConfig frontendConf,
+ DvrConfig dvrConf);
+ void recordSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf,
+ DvrConfig dvrConf);
sp<ITuner> mService;
FrontendTests mFrontendTests;
@@ -139,13 +196,10 @@
virtual void SetUp() override {
mService = ITuner::getService(GetParam());
ASSERT_NE(mService, nullptr);
- initFrontendConfig();
- initFrontendScanConfig();
- initFilterConfig();
+ initConfiguration();
mFrontendTests.setService(mService);
mDemuxTests.setService(mService);
- mFilterTests.setService(mService);
}
protected:
@@ -156,20 +210,10 @@
sp<ITuner> mService;
FrontendTests mFrontendTests;
DemuxTests mDemuxTests;
- FilterTests mFilterTests;
sp<IDescrambler> mDescrambler;
AssertionResult createDescrambler(uint32_t demuxId);
AssertionResult closeDescrambler();
-
- AssertionResult playbackDataFlowTest(vector<FilterConfig> filterConf, PlaybackConf playbackConf,
- vector<string> goldenOutputFiles);
- AssertionResult recordDataFlowTest(vector<FilterConfig> filterConf,
- RecordSettings recordSetting,
- vector<string> goldenOutputFiles);
- AssertionResult broadcastDataFlowTest(vector<string> goldenOutputFiles);
-
- void broadcastSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf);
};
} // namespace
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index a74fa02..b84013b 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -26,6 +26,7 @@
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::DemuxRecordScIndexType;
using android::hardware::tv::tuner::V1_0::DemuxTpid;
using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
using android::hardware::tv::tuner::V1_0::DvrSettings;
@@ -45,6 +46,10 @@
using namespace std;
+const uint32_t FMQ_SIZE_1M = 0x100000;
+const uint32_t FMQ_SIZE_4M = 0x400000;
+const uint32_t FMQ_SIZE_16M = 0x1000000;
+
typedef enum {
TS_VIDEO0,
TS_VIDEO1,
@@ -53,6 +58,7 @@
TS_PCR0,
TS_SECTION0,
TS_TS0,
+ TS_RECORD0,
FILTER_MAX,
} Filter;
@@ -74,6 +80,7 @@
} Dvr;
struct FilterConfig {
+ uint32_t bufferSize;
DemuxFilterType type;
DemuxFilterSettings settings;
};
@@ -93,7 +100,9 @@
struct DvrConfig {
DvrType type;
+ uint32_t bufferSize;
DvrSettings settings;
+ string playbackInputFile;
};
static FrontendConfig frontendArray[FILTER_MAX];
@@ -143,20 +152,24 @@
// TS VIDEO filter setting for default implementation testing
filterArray[TS_VIDEO0].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_VIDEO0].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
- filterArray[TS_VIDEO0].settings.ts().tpid = 119;
+ filterArray[TS_VIDEO0].bufferSize = FMQ_SIZE_16M;
+ filterArray[TS_VIDEO0].settings.ts().tpid = 256;
filterArray[TS_VIDEO0].settings.ts().filterSettings.av({.isPassthrough = false});
filterArray[TS_VIDEO1].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_VIDEO1].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
- filterArray[TS_VIDEO1].settings.ts().tpid = 81;
+ filterArray[TS_VIDEO1].bufferSize = FMQ_SIZE_16M;
+ filterArray[TS_VIDEO1].settings.ts().tpid = 256;
filterArray[TS_VIDEO1].settings.ts().filterSettings.av({.isPassthrough = false});
// TS AUDIO filter setting
filterArray[TS_AUDIO0].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_AUDIO0].type.subType.tsFilterType(DemuxTsFilterType::AUDIO);
- filterArray[TS_AUDIO0].settings.ts().tpid = 84;
+ filterArray[TS_AUDIO0].bufferSize = FMQ_SIZE_16M;
+ filterArray[TS_AUDIO0].settings.ts().tpid = 256;
filterArray[TS_AUDIO0].settings.ts().filterSettings.av({.isPassthrough = false});
// TS PES filter setting
filterArray[TS_PES0].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_PES0].type.subType.tsFilterType(DemuxTsFilterType::PES);
+ filterArray[TS_PES0].bufferSize = FMQ_SIZE_16M;
filterArray[TS_PES0].settings.ts().tpid = 256;
filterArray[TS_PES0].settings.ts().filterSettings.pesData({
.isRaw = false,
@@ -165,20 +178,30 @@
// TS PCR filter setting
filterArray[TS_PCR0].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_PCR0].type.subType.tsFilterType(DemuxTsFilterType::PCR);
- filterArray[TS_PCR0].settings.ts().tpid = 81;
+ filterArray[TS_PCR0].bufferSize = FMQ_SIZE_16M;
+ filterArray[TS_PCR0].settings.ts().tpid = 256;
filterArray[TS_PCR0].settings.ts().filterSettings.noinit();
// TS filter setting
filterArray[TS_TS0].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_TS0].type.subType.tsFilterType(DemuxTsFilterType::TS);
- filterArray[TS_TS0].settings.ts().tpid = 48;
+ filterArray[TS_TS0].bufferSize = FMQ_SIZE_16M;
+ filterArray[TS_TS0].settings.ts().tpid = 256;
filterArray[TS_TS0].settings.ts().filterSettings.noinit();
// TS SECTION filter setting
filterArray[TS_SECTION0].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_SECTION0].type.subType.tsFilterType(DemuxTsFilterType::SECTION);
- filterArray[TS_SECTION0].settings.ts().tpid = 48;
+ filterArray[TS_SECTION0].bufferSize = FMQ_SIZE_16M;
+ filterArray[TS_SECTION0].settings.ts().tpid = 256;
filterArray[TS_SECTION0].settings.ts().filterSettings.section({
.isRaw = false,
});
+ // TS RECORD filter setting
+ filterArray[TS_RECORD0].type.mainType = DemuxFilterMainType::TS;
+ filterArray[TS_RECORD0].type.subType.tsFilterType(DemuxTsFilterType::RECORD);
+ filterArray[TS_RECORD0].settings.ts().tpid = 81;
+ filterArray[TS_RECORD0].settings.ts().filterSettings.record({
+ .scIndexType = DemuxRecordScIndexType::NONE,
+ });
};
/** Configuration array for the dvr test */
@@ -191,6 +214,7 @@
.packetSize = 188,
};
dvrArray[DVR_RECORD0].type = DvrType::RECORD;
+ dvrArray[DVR_RECORD0].bufferSize = FMQ_SIZE_4M;
dvrArray[DVR_RECORD0].settings.record(recordSettings);
PlaybackSettings playbackSettings{
.statusMask = 0xf,
@@ -200,5 +224,7 @@
.packetSize = 188,
};
dvrArray[DVR_PLAYBACK0].type = DvrType::PLAYBACK;
+ dvrArray[DVR_PLAYBACK0].playbackInputFile = "/vendor/etc/segment000000.ts";
+ dvrArray[DVR_PLAYBACK0].bufferSize = FMQ_SIZE_4M;
dvrArray[DVR_PLAYBACK0].settings.playback(playbackSettings);
-};
\ No newline at end of file
+};
diff --git a/wifi/1.0/vts/functional/wifi_chip_hidl_nan_test.cpp b/wifi/1.0/vts/functional/wifi_chip_hidl_nan_test.cpp
index c95f4d2..bb7a3a6 100644
--- a/wifi/1.0/vts/functional/wifi_chip_hidl_nan_test.cpp
+++ b/wifi/1.0/vts/functional/wifi_chip_hidl_nan_test.cpp
@@ -16,6 +16,7 @@
#include <android-base/logging.h>
+#include <VtsCoreUtil.h>
#include <android/hardware/wifi/1.0/IWifi.h>
#include <android/hardware/wifi/1.0/IWifiChip.h>
#include <gtest/gtest.h>
@@ -41,6 +42,9 @@
class WifiChipHidlNanTest : public ::testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
+ if (!::testing::deviceSupportsFeature("android.hardware.wifi.aware"))
+ GTEST_SKIP() << "Skipping this test since NAN is not supported.";
+
// Make sure test starts with a clean state
stopWifi(GetInstanceName());
diff --git a/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp
index 47a1938..2b63ddc 100644
--- a/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp
+++ b/wifi/1.0/vts/functional/wifi_nan_iface_hidl_test.cpp
@@ -16,6 +16,7 @@
#include <android-base/logging.h>
+#include <VtsCoreUtil.h>
#include <android/hardware/wifi/1.0/IWifi.h>
#include <android/hardware/wifi/1.0/IWifiNanIface.h>
#include <android/hardware/wifi/1.0/IWifiNanIfaceEventCallback.h>
@@ -44,6 +45,9 @@
class WifiNanIfaceHidlTest : public ::testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
+ if (!::testing::deviceSupportsFeature("android.hardware.wifi.aware"))
+ GTEST_SKIP() << "Skipping this test since NAN is not supported.";
+
// Make sure test starts with a clean state
stopWifi(GetInstanceName());
diff --git a/wifi/1.4/default/Android.mk b/wifi/1.4/default/Android.mk
index 8573e8e..6be7dad 100644
--- a/wifi/1.4/default/Android.mk
+++ b/wifi/1.4/default/Android.mk
@@ -36,6 +36,9 @@
ifdef WIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
LOCAL_CPPFLAGS += -DWIFI_HIDL_FEATURE_DISABLE_AP_MAC_RANDOMIZATION
endif
+ifdef WIFI_AVOID_IFACE_RESET_MAC_CHANGE
+LOCAL_CPPFLAGS += -DWIFI_AVOID_IFACE_RESET_MAC_CHANGE
+endif
# Allow implicit fallthroughs in wifi_legacy_hal.cpp until they are fixed.
LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
LOCAL_SRC_FILES := \
diff --git a/wifi/1.4/default/wifi_chip.cpp b/wifi/1.4/default/wifi_chip.cpp
index 23dd13b..61912a5 100644
--- a/wifi/1.4/default/wifi_chip.cpp
+++ b/wifi/1.4/default/wifi_chip.cpp
@@ -101,6 +101,16 @@
return "wlan" + std::to_string(idx);
}
+// Returns the dedicated iface name if one is defined.
+std::string getApIfaceName() {
+ std::array<char, PROPERTY_VALUE_MAX> buffer;
+ if (property_get("ro.vendor.wifi.sap.interface", buffer.data(), nullptr) ==
+ 0) {
+ return {};
+ }
+ return buffer.data();
+}
+
std::string getP2pIfaceName() {
std::array<char, PROPERTY_VALUE_MAX> buffer;
property_get("wifi.direct.interface", buffer.data(), "p2p0");
@@ -1582,6 +1592,11 @@
// AP iface names start with idx 1 for modes supporting
// concurrent STA and not dual AP, else start with idx 0.
std::string WifiChip::allocateApIfaceName() {
+ // Check if we have a dedicated iface for AP.
+ std::string ifname = getApIfaceName();
+ if (!ifname.empty()) {
+ return ifname;
+ }
return allocateApOrStaIfaceName((isStaApConcurrencyAllowedInCurrentMode() &&
!isDualApAllowedInCurrentMode())
? 1
diff --git a/wifi/1.4/default/wifi_iface_util.cpp b/wifi/1.4/default/wifi_iface_util.cpp
index 036c97b..13ba022 100644
--- a/wifi/1.4/default/wifi_iface_util.cpp
+++ b/wifi/1.4/default/wifi_iface_util.cpp
@@ -52,18 +52,22 @@
bool WifiIfaceUtil::setMacAddress(const std::string& iface_name,
const std::array<uint8_t, 6>& mac) {
+#ifndef WIFI_AVOID_IFACE_RESET_MAC_CHANGE
if (!iface_tool_.lock()->SetUpState(iface_name.c_str(), false)) {
LOG(ERROR) << "SetUpState(false) failed.";
return false;
}
+#endif
if (!iface_tool_.lock()->SetMacAddress(iface_name.c_str(), mac)) {
LOG(ERROR) << "SetMacAddress failed.";
return false;
}
+#ifndef WIFI_AVOID_IFACE_RESET_MAC_CHANGE
if (!iface_tool_.lock()->SetUpState(iface_name.c_str(), true)) {
LOG(ERROR) << "SetUpState(true) failed.";
return false;
}
+#endif
IfaceEventHandlers event_handlers = {};
const auto it = event_handlers_map_.find(iface_name);
if (it != event_handlers_map_.end()) {
diff --git a/wifi/1.4/vts/functional/Android.bp b/wifi/1.4/vts/functional/Android.bp
index 3824c3a..59a35e0 100644
--- a/wifi/1.4/vts/functional/Android.bp
+++ b/wifi/1.4/vts/functional/Android.bp
@@ -21,7 +21,51 @@
srcs: [
"wifi_ap_iface_hidl_test.cpp",
"wifi_chip_hidl_test.cpp",
+ ],
+ static_libs: [
+ "VtsHalWifiV1_0TargetTestUtil",
+ "android.hardware.wifi@1.0",
+ "android.hardware.wifi@1.1",
+ "android.hardware.wifi@1.2",
+ "android.hardware.wifi@1.3",
+ "android.hardware.wifi@1.4",
+ "libwifi-system-iface",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
+
+// These tests are split out so that they can be conditioned on presence of the
+// "android.hardware.wifi.aware" feature.
+cc_test {
+ name: "VtsHalWifiNanV1_4TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
"wifi_nan_iface_hidl_test.cpp",
+ ],
+ static_libs: [
+ "VtsHalWifiV1_0TargetTestUtil",
+ "android.hardware.wifi@1.0",
+ "android.hardware.wifi@1.1",
+ "android.hardware.wifi@1.2",
+ "android.hardware.wifi@1.3",
+ "android.hardware.wifi@1.4",
+ "libwifi-system-iface",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
+
+// These tests are split out so that they can be conditioned on presence of the
+// "android.hardware.wifi.rtt" feature.
+cc_test {
+ name: "VtsHalWifiRttV1_4TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
"wifi_rtt_controller_hidl_test.cpp",
],
static_libs: [
diff --git a/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
index 1c39550..be5c3bd 100644
--- a/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
+++ b/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
@@ -150,28 +150,6 @@
}
}
-/*
- * createRttController_1_4
- * Ensures that an instance of the IWifiRttController proxy object is
- * successfully created.
- */
-TEST_P(WifiChipHidlTest, createRttController_1_4) {
- configureChipForIfaceType(IfaceType::STA, true);
-
- const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createStaIface);
- EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface.first.code);
- sp<IWifiStaIface> iface = IWifiStaIface::castFrom(status_and_iface.second);
- EXPECT_NE(nullptr, iface.get());
-
- const auto& status_and_controller =
- HIDL_INVOKE(wifi_chip_, createRttController_1_4, iface);
- if (status_and_controller.first.code !=
- WifiStatusCode::ERROR_NOT_SUPPORTED) {
- EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_controller.first.code);
- EXPECT_NE(nullptr, status_and_controller.second.get());
- }
-}
-
INSTANTIATE_TEST_SUITE_P(
PerInstance, WifiChipHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(
diff --git a/wifi/supplicant/1.2/vts/functional/VtsHalWifiSupplicantP2pV1_2TargetTest.cpp b/wifi/supplicant/1.2/vts/functional/VtsHalWifiSupplicantP2pV1_2TargetTest.cpp
deleted file mode 100644
index 7edec47..0000000
--- a/wifi/supplicant/1.2/vts/functional/VtsHalWifiSupplicantP2pV1_2TargetTest.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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 <VtsCoreUtil.h>
-#include "supplicant_hidl_test_utils.h"
-
-int main(int argc, char** argv) {
- if (!::testing::deviceSupportsFeature("android.hardware.wifi.direct"))
- return 0;
-
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
diff --git a/wifi/supplicant/1.2/vts/functional/supplicant_p2p_iface_hidl_test.cpp b/wifi/supplicant/1.2/vts/functional/supplicant_p2p_iface_hidl_test.cpp
index 7b96b87..1eb8eea 100644
--- a/wifi/supplicant/1.2/vts/functional/supplicant_p2p_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.2/vts/functional/supplicant_p2p_iface_hidl_test.cpp
@@ -43,6 +43,9 @@
virtual void SetUp() override {
SupplicantHidlTestBase::SetUp();
EXPECT_TRUE(turnOnExcessiveLogging(supplicant_));
+ if (!::testing::deviceSupportsFeature("android.hardware.wifi.direct")) {
+ GTEST_SKIP() << "Wi-Fi Direct is not supported, skip this test.";
+ }
p2p_iface_ = getSupplicantP2pIface_1_2(supplicant_);
ASSERT_NE(p2p_iface_.get(), nullptr);
}
diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
index 3754520..4020298 100644
--- a/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -64,6 +64,7 @@
isP2pOn_ =
testing::deviceSupportsFeature("android.hardware.wifi.direct");
+ stopSupplicant(wifi_v1_0_instance_name_);
startSupplicantAndWaitForHidlService(wifi_v1_0_instance_name_,
supplicant_v1_3_instance_name_);
supplicant_ =
diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
index 9c40de1..25091a5 100644
--- a/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
+++ b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
@@ -51,6 +51,8 @@
supplicant_v1_3_instance_name_ = std::get<1>(GetParam());
isP2pOn_ =
testing::deviceSupportsFeature("android.hardware.wifi.direct");
+
+ stopSupplicant(wifi_v1_0_instance_name_);
startSupplicantAndWaitForHidlService(wifi_v1_0_instance_name_,
supplicant_v1_3_instance_name_);
supplicant_ =
@@ -286,6 +288,49 @@
});
}
}
+
+/*
+ * SetGetWapiPsk
+ */
+TEST_P(SupplicantStaNetworkHidlTest, SetGetWapiPsk) {
+ uint32_t keyMgmt = (uint32_t)ISupplicantStaNetwork::KeyMgmtMask::WAPI_PSK;
+ char kTestPskPassphrase[] = "\"123456780abcdef0123456780abcdef0deadbeef\"";
+ char kTestPskHex[] = "12345678";
+
+ if (!isWapiSupported()) {
+ GTEST_SKIP() << "Skipping test since WAPI is not supported.";
+ }
+
+ sta_network_->setKeyMgmt_1_3(keyMgmt, [](const SupplicantStatus &status) {
+ if (SupplicantStatusCode::SUCCESS != status.code) {
+ // for unsupport case
+ EXPECT_EQ(SupplicantStatusCode::FAILURE_UNKNOWN, status.code);
+ }
+ });
+
+ sta_network_->setPskPassphrase(
+ kTestPskPassphrase, [](const SupplicantStatus &status) {
+ EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ });
+
+ sta_network_->getPskPassphrase(
+ [&](const SupplicantStatus &status, const hidl_string &psk) {
+ EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ EXPECT_EQ(kTestPskPassphrase, std::string(psk.c_str()));
+ });
+
+ sta_network_->setPskPassphrase(
+ kTestPskHex, [](const SupplicantStatus &status) {
+ EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ });
+
+ sta_network_->getPskPassphrase(
+ [&](const SupplicantStatus &status, const hidl_string &psk) {
+ EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
+ EXPECT_EQ(kTestPskHex, std::string(psk.c_str()));
+ });
+}
+
/*
* SetEapErp
*/