Merge "Expanding VTS test coverage"
diff --git a/atrace/1.0/default/AtraceDevice.cpp b/atrace/1.0/default/AtraceDevice.cpp
index 43bcd9a..35d11e9 100644
--- a/atrace/1.0/default/AtraceDevice.cpp
+++ b/atrace/1.0/default/AtraceDevice.cpp
@@ -30,24 +30,25 @@
struct TracingConfig {
std::string description;
+ // path and if error on failure
std::vector<std::pair<std::string, bool>> paths;
};
// This is a map stores categories and their sysfs paths with required flags
const std::map<std::string, TracingConfig> kTracingMap = {
- // gfx
- {
- "gfx",
- {"Graphics",
- {{"/sys/kernel/debug/tracing/events/mdss/enable", false},
- {"/sys/kernel/debug/tracing/events/sde/enable", false},
- {"/sys/kernel/debug/tracing/events/mali_systrace/enable", false}}},
- },
- {
- "ion",
- {"ION allocation",
- {{"/sys/kernel/debug/tracing/events/kmem/ion_alloc_buffer_start/enable", true}}},
- },
+ // gfx
+ {
+ "gfx",
+ {"Graphics",
+ {{"/sys/kernel/debug/tracing/events/mdss/enable", false},
+ {"/sys/kernel/debug/tracing/events/sde/enable", false},
+ {"/sys/kernel/debug/tracing/events/mali_systrace/enable", false}}},
+ },
+ {
+ "ion",
+ {"ION allocation",
+ {{"/sys/kernel/debug/tracing/events/kmem/ion_alloc_buffer_start/enable", false}}},
+ },
};
// Methods from ::android::hardware::atrace::V1_0::IAtraceDevice follow.
diff --git a/audio/5.0/IStreamIn.hal b/audio/5.0/IStreamIn.hal
index b042960..e15b034 100644
--- a/audio/5.0/IStreamIn.hal
+++ b/audio/5.0/IStreamIn.hal
@@ -169,6 +169,10 @@
/**
* Specifies the logical microphone (for processing).
*
+ * If the feature is not supported an error should be returned
+ * If multiple microphones are present, this should be treated as a preference
+ * for their combined direction.
+ *
* Optional method
*
* @param Direction constant
@@ -180,6 +184,10 @@
/**
* Specifies the zoom factor for the selected microphone (for processing).
*
+ * If the feature is not supported an error should be returned
+ * If multiple microphones are present, this should be treated as a preference
+ * for their combined field dimension.
+ *
* Optional method
*
* @param the desired field dimension of microphone capture. Range is from -1 (wide angle),
diff --git a/audio/common/5.0/types.hal b/audio/common/5.0/types.hal
index 0cbf35e..e1279ee 100644
--- a/audio/common/5.0/types.hal
+++ b/audio/common/5.0/types.hal
@@ -146,6 +146,7 @@
*/
ECHO_REFERENCE = 1997,
FM_TUNER = 1998,
+ HOTWORD = 1999,
};
typedef int32_t AudioSession;
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h
index 9e32bb5..258dbd9 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VmsUtils.h
@@ -19,6 +19,7 @@
#include <memory>
#include <string>
+#include <unordered_set>
#include <android/hardware/automotive/vehicle/2.0/types.h>
@@ -42,6 +43,20 @@
int type;
int subtype;
int version;
+ bool operator==(const VmsLayer& layer) const {
+ return this->type == layer.type && this->subtype == layer.subtype &&
+ this->version == layer.version;
+ }
+
+ // Class for hash function
+ class VmsLayerHashFunction {
+ public:
+ // Hash of the variables is returned.
+ size_t operator()(const VmsLayer& layer) const {
+ return std::hash<int>()(layer.type) ^ std::hash<int>()(layer.type) ^
+ std::hash<int>()(layer.type);
+ }
+ };
};
struct VmsLayerAndPublisher {
@@ -66,6 +81,15 @@
std::vector<VmsLayer> dependencies;
};
+// A VmsOffers refers to a list of layers that can be published by the publisher
+// with the specified publisher ID.
+struct VmsOffers {
+ VmsOffers(int publisher_id, std::vector<VmsLayerOffering> offerings)
+ : publisher_id(publisher_id), offerings(offerings) {}
+ int publisher_id;
+ std::vector<VmsLayerOffering> offerings;
+};
+
// A VmsSubscriptionsState is delivered in response to a
// VmsMessageType.SUBSCRIPTIONS_REQUEST or on the first SUBSCRIBE or last
// UNSUBSCRIBE for a layer. It indicates which layers or associated_layers are
@@ -81,6 +105,9 @@
std::vector<VmsAssociatedLayer> associated_layers;
};
+// Creates an empty base VMS message with some pre-populated default fields.
+std::unique_ptr<VehiclePropValue> createBaseVmsMessage(size_t message_size);
+
// Creates a VehiclePropValue containing a message of type
// VmsMessageType.SUBSCRIBE, specifying to the VMS service
// which layer to subscribe to.
@@ -106,8 +133,7 @@
// Creates a VehiclePropValue containing a message of type
// VmsMessageType.OFFERING, specifying to the VMS service which layers are being
// offered and their dependencies, if any.
-std::unique_ptr<VehiclePropValue> createOfferingMessage(
- const std::vector<VmsLayerOffering>& offering);
+std::unique_ptr<VehiclePropValue> createOfferingMessage(const VmsOffers& offers);
// Creates a VehiclePropValue containing a message of type
// VmsMessageType.AVAILABILITY_REQUEST.
@@ -143,8 +169,40 @@
// function to ParseFromString.
std::string parseData(const VehiclePropValue& value);
-// TODO(aditin): Need to implement additional parsing functions per message
-// type.
+// Creates a VehiclePropValue containing a message of type
+// VmsMessageType.PUBLISHER_ID_REQUEST with the given publisher information.
+// Returns a nullptr if the input is empty.
+std::unique_ptr<VehiclePropValue> createPublisherIdRequest(
+ const std::string& vms_provider_description);
+
+// Returns the publisher ID by parsing the VehiclePropValue containing the ID.
+// Returns null if the message is invalid.
+int32_t parsePublisherIdResponse(const VehiclePropValue& publisher_id_response);
+
+// Returns true if the new sequence number is greater than the last seen
+// sequence number.
+bool isSequenceNumberNewer(const VehiclePropValue& subscription_change,
+ const int last_seen_sequence_number);
+
+// Returns sequence number of the message.
+int32_t getSequenceNumberForSubscriptionsState(const VehiclePropValue& subscription_change);
+
+// Takes a subscription change message and returns the layers that have active
+// subscriptions of the layers that are offered by your HAL client/publisher.
+//
+// A publisher can use this function when receiving a subscription change message
+// to determine which layers to publish data on.
+// The caller of this function can optionally decide to not consume these layers
+// if the subscription change has the sequence number less than the last seen
+// sequence number.
+std::vector<VmsLayer> getSubscribedLayers(const VehiclePropValue& subscription_change,
+ const VmsOffers& offers);
+
+// Takes an availability change message and returns true if the parsed message implies that
+// the service has newly started or restarted.
+// If the message has a sequence number 0, it means that the service
+// has newly started or restarted.
+bool hasServiceNewlyStarted(const VehiclePropValue& availability_change);
} // namespace vms
} // namespace V2_0
diff --git a/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp b/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp
index f001a32..1863191 100644
--- a/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VmsUtils.cpp
@@ -27,16 +27,23 @@
static constexpr int kMessageIndex = toInt(VmsBaseMessageIntegerValuesIndex::MESSAGE_TYPE);
static constexpr int kMessageTypeSize = 1;
+static constexpr int kPublisherIdSize = 1;
static constexpr int kLayerNumberSize = 1;
static constexpr int kLayerSize = 3;
static constexpr int kLayerAndPublisherSize = 4;
+static constexpr int kPublisherIdIndex =
+ toInt(VmsPublisherInformationIntegerValuesIndex::PUBLISHER_ID);
+static constexpr int kSubscriptionStateSequenceNumberIndex =
+ toInt(VmsSubscriptionsStateIntegerValuesIndex::SEQUENCE_NUMBER);
+static constexpr int kAvailabilitySequenceNumberIndex =
+ toInt(VmsAvailabilityStateIntegerValuesIndex::SEQUENCE_NUMBER);
// TODO(aditin): We should extend the VmsMessageType enum to include a first and
// last, which would prevent breakages in this API. However, for all of the
// functions in this module, we only need to guarantee that the message type is
-// between SUBSCRIBE and DATA.
+// between SUBSCRIBE and PUBLISHER_ID_RESPONSE.
static constexpr int kFirstMessageType = toInt(VmsMessageType::SUBSCRIBE);
-static constexpr int kLastMessageType = toInt(VmsMessageType::DATA);
+static constexpr int kLastMessageType = toInt(VmsMessageType::PUBLISHER_ID_RESPONSE);
std::unique_ptr<VehiclePropValue> createBaseVmsMessage(size_t message_size) {
auto result = createVehiclePropValue(VehiclePropertyType::INT32, message_size);
@@ -77,17 +84,16 @@
return result;
}
-std::unique_ptr<VehiclePropValue> createOfferingMessage(
- const std::vector<VmsLayerOffering>& offering) {
- int message_size = kMessageTypeSize + kLayerNumberSize;
- for (const auto& offer : offering) {
- message_size += kLayerNumberSize + (1 + offer.dependencies.size()) * kLayerSize;
+std::unique_ptr<VehiclePropValue> createOfferingMessage(const VmsOffers& offers) {
+ int message_size = kMessageTypeSize + kPublisherIdSize + kLayerNumberSize;
+ for (const auto& offer : offers.offerings) {
+ message_size += kLayerSize + kLayerNumberSize + (offer.dependencies.size() * kLayerSize);
}
auto result = createBaseVmsMessage(message_size);
- std::vector<int32_t> offers = {toInt(VmsMessageType::OFFERING),
- static_cast<int>(offering.size())};
- for (const auto& offer : offering) {
+ std::vector<int32_t> offerings = {toInt(VmsMessageType::OFFERING), offers.publisher_id,
+ static_cast<int>(offers.offerings.size())};
+ for (const auto& offer : offers.offerings) {
std::vector<int32_t> layer_vector = {offer.layer.type, offer.layer.subtype,
offer.layer.version,
static_cast<int32_t>(offer.dependencies.size())};
@@ -97,9 +103,9 @@
layer_vector.insert(layer_vector.end(), dependency_layer.begin(),
dependency_layer.end());
}
- offers.insert(offers.end(), layer_vector.begin(), layer_vector.end());
+ offerings.insert(offerings.end(), layer_vector.begin(), layer_vector.end());
}
- result->value.int32Values = offers;
+ result->value.int32Values = offerings;
return result;
}
@@ -153,6 +159,103 @@
}
}
+std::unique_ptr<VehiclePropValue> createPublisherIdRequest(
+ const std::string& vms_provider_description) {
+ auto result = createBaseVmsMessage(kMessageTypeSize);
+ result->value.int32Values = hidl_vec<int32_t>{
+ toInt(VmsMessageType::PUBLISHER_ID_REQUEST),
+ };
+ result->value.bytes =
+ std::vector<uint8_t>(vms_provider_description.begin(), vms_provider_description.end());
+ return result;
+}
+
+int32_t parsePublisherIdResponse(const VehiclePropValue& publisher_id_response) {
+ if (isValidVmsMessage(publisher_id_response) &&
+ parseMessageType(publisher_id_response) == VmsMessageType::PUBLISHER_ID_RESPONSE &&
+ publisher_id_response.value.int32Values.size() > kPublisherIdIndex) {
+ return publisher_id_response.value.int32Values[kPublisherIdIndex];
+ }
+ return -1;
+}
+
+bool isSequenceNumberNewer(const VehiclePropValue& subscription_change,
+ const int last_seen_sequence_number) {
+ return (isValidVmsMessage(subscription_change) &&
+ parseMessageType(subscription_change) == VmsMessageType::SUBSCRIPTIONS_CHANGE &&
+ subscription_change.value.int32Values.size() > kSubscriptionStateSequenceNumberIndex &&
+ subscription_change.value.int32Values[kSubscriptionStateSequenceNumberIndex] >
+ last_seen_sequence_number);
+}
+
+int32_t getSequenceNumberForSubscriptionsState(const VehiclePropValue& subscription_change) {
+ if (isValidVmsMessage(subscription_change) &&
+ parseMessageType(subscription_change) == VmsMessageType::SUBSCRIPTIONS_CHANGE &&
+ subscription_change.value.int32Values.size() > kSubscriptionStateSequenceNumberIndex) {
+ return subscription_change.value.int32Values[kSubscriptionStateSequenceNumberIndex];
+ }
+ return -1;
+}
+
+std::vector<VmsLayer> getSubscribedLayers(const VehiclePropValue& subscription_change,
+ const VmsOffers& offers) {
+ if (isValidVmsMessage(subscription_change) &&
+ parseMessageType(subscription_change) == VmsMessageType::SUBSCRIPTIONS_CHANGE &&
+ subscription_change.value.int32Values.size() > kSubscriptionStateSequenceNumberIndex) {
+ const int32_t num_of_layers = subscription_change.value.int32Values[toInt(
+ VmsSubscriptionsStateIntegerValuesIndex::NUMBER_OF_LAYERS)];
+ const int32_t num_of_associated_layers = subscription_change.value.int32Values[toInt(
+ VmsSubscriptionsStateIntegerValuesIndex ::NUMBER_OF_ASSOCIATED_LAYERS)];
+
+ std::unordered_set<VmsLayer, VmsLayer::VmsLayerHashFunction> offered_layers;
+ for (const auto& offer : offers.offerings) {
+ offered_layers.insert(offer.layer);
+ }
+ std::vector<VmsLayer> subscribed_layers;
+
+ int current_index = toInt(VmsSubscriptionsStateIntegerValuesIndex::SUBSCRIPTIONS_START);
+ // Add all subscribed layers which are offered by the current publisher.
+ for (int i = 0; i < num_of_layers; i++) {
+ VmsLayer layer = VmsLayer(subscription_change.value.int32Values[current_index],
+ subscription_change.value.int32Values[current_index + 1],
+ subscription_change.value.int32Values[current_index + 2]);
+ if (offered_layers.find(layer) != offered_layers.end()) {
+ subscribed_layers.push_back(layer);
+ }
+ current_index += kLayerSize;
+ }
+ // Add all subscribed associated layers which are offered by the current publisher.
+ // For this, we need to check if the associated layer has a publisher ID which is
+ // same as that of the current publisher.
+ for (int i = 0; i < num_of_associated_layers; i++) {
+ VmsLayer layer = VmsLayer(subscription_change.value.int32Values[current_index],
+ subscription_change.value.int32Values[current_index + 1],
+ subscription_change.value.int32Values[current_index + 2]);
+ current_index += kLayerSize;
+ if (offered_layers.find(layer) != offered_layers.end()) {
+ int32_t num_of_publisher_ids = subscription_change.value.int32Values[current_index];
+ current_index++;
+ for (int j = 0; j < num_of_publisher_ids; j++) {
+ if (subscription_change.value.int32Values[current_index] ==
+ offers.publisher_id) {
+ subscribed_layers.push_back(layer);
+ }
+ current_index++;
+ }
+ }
+ }
+ return subscribed_layers;
+ }
+ return {};
+}
+
+bool hasServiceNewlyStarted(const VehiclePropValue& availability_change) {
+ return (isValidVmsMessage(availability_change) &&
+ parseMessageType(availability_change) == VmsMessageType::AVAILABILITY_CHANGE &&
+ availability_change.value.int32Values.size() > kAvailabilitySequenceNumberIndex &&
+ availability_change.value.int32Values[kAvailabilitySequenceNumberIndex] == 0);
+}
+
} // namespace vms
} // namespace V2_0
} // namespace vehicle
diff --git a/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp b/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
index f64eab5..0975071 100644
--- a/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
+++ b/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
@@ -278,7 +278,6 @@
cb->reset();
VehiclePropValue actualValue(*subscribedValue.get());
- actualValue.status = VehiclePropertyStatus::AVAILABLE;
hal->sendPropEvent(std::move(subscribedValue));
ASSERT_TRUE(cb->waitForExpectedEvents(1)) << "Events received: "
diff --git a/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp b/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp
index 414c5c2..5ea5bd4 100644
--- a/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp
+++ b/automotive/vehicle/2.0/default/tests/VmsUtils_test.cpp
@@ -60,52 +60,64 @@
}
TEST(VmsUtilsTest, singleOfferingMessage) {
- std::vector<VmsLayerOffering> offering = {VmsLayerOffering(VmsLayer(1, 0, 2))};
- auto message = createOfferingMessage(offering);
+ VmsOffers offers = {123, {VmsLayerOffering(VmsLayer(1, 0, 2))}};
+ auto message = createOfferingMessage(offers);
ASSERT_NE(message, nullptr);
EXPECT_TRUE(isValidVmsMessage(*message));
EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE));
- EXPECT_EQ(message->value.int32Values.size(), 0x6ul);
+ EXPECT_EQ(message->value.int32Values.size(), 0x7ul);
EXPECT_EQ(parseMessageType(*message), VmsMessageType::OFFERING);
+ // Publisher ID
+ EXPECT_EQ(message->value.int32Values[1], 123);
+
// Number of layer offerings
- EXPECT_EQ(message->value.int32Values[1], 1);
+ EXPECT_EQ(message->value.int32Values[2], 1);
// Layer
- EXPECT_EQ(message->value.int32Values[2], 1);
- EXPECT_EQ(message->value.int32Values[3], 0);
- EXPECT_EQ(message->value.int32Values[4], 2);
+ EXPECT_EQ(message->value.int32Values[3], 1);
+ EXPECT_EQ(message->value.int32Values[4], 0);
+ EXPECT_EQ(message->value.int32Values[5], 2);
// Number of dependencies
- EXPECT_EQ(message->value.int32Values[5], 0);
+ EXPECT_EQ(message->value.int32Values[6], 0);
}
TEST(VmsUtilsTest, offeringWithDependencies) {
VmsLayer layer(1, 0, 2);
- std::vector<VmsLayer> dependencies = {VmsLayer(2, 0, 2)};
+ std::vector<VmsLayer> dependencies = {VmsLayer(2, 0, 2), VmsLayer(3, 0, 3)};
std::vector<VmsLayerOffering> offering = {VmsLayerOffering(layer, dependencies)};
- auto message = createOfferingMessage(offering);
+ VmsOffers offers = {123, offering};
+ auto message = createOfferingMessage(offers);
ASSERT_NE(message, nullptr);
EXPECT_TRUE(isValidVmsMessage(*message));
EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE));
- EXPECT_EQ(message->value.int32Values.size(), 0x9ul);
+ EXPECT_EQ(message->value.int32Values.size(), 0xdul);
EXPECT_EQ(parseMessageType(*message), VmsMessageType::OFFERING);
+ // Publisher ID
+ EXPECT_EQ(message->value.int32Values[1], 123);
+
// Number of layer offerings
- EXPECT_EQ(message->value.int32Values[1], 1);
+ EXPECT_EQ(message->value.int32Values[2], 1);
// Layer
- EXPECT_EQ(message->value.int32Values[2], 1);
- EXPECT_EQ(message->value.int32Values[3], 0);
- EXPECT_EQ(message->value.int32Values[4], 2);
+ EXPECT_EQ(message->value.int32Values[3], 1);
+ EXPECT_EQ(message->value.int32Values[4], 0);
+ EXPECT_EQ(message->value.int32Values[5], 2);
// Number of dependencies
- EXPECT_EQ(message->value.int32Values[5], 1);
+ EXPECT_EQ(message->value.int32Values[6], 2);
// Dependency 1
- EXPECT_EQ(message->value.int32Values[6], 2);
- EXPECT_EQ(message->value.int32Values[7], 0);
- EXPECT_EQ(message->value.int32Values[8], 2);
+ EXPECT_EQ(message->value.int32Values[7], 2);
+ EXPECT_EQ(message->value.int32Values[8], 0);
+ EXPECT_EQ(message->value.int32Values[9], 2);
+
+ // Dependency 2
+ EXPECT_EQ(message->value.int32Values[10], 3);
+ EXPECT_EQ(message->value.int32Values[11], 0);
+ EXPECT_EQ(message->value.int32Values[12], 3);
}
TEST(VmsUtilsTest, availabilityMessage) {
@@ -166,6 +178,153 @@
EXPECT_TRUE(data_str.empty());
}
+TEST(VmsUtilsTest, publisherIdRequest) {
+ std::string bytes = "pub_id";
+ auto message = createPublisherIdRequest(bytes);
+ ASSERT_NE(message, nullptr);
+ EXPECT_TRUE(isValidVmsMessage(*message));
+ EXPECT_EQ(message->prop, toInt(VehicleProperty::VEHICLE_MAP_SERVICE));
+ EXPECT_EQ(message->value.int32Values.size(), 0x1ul);
+ EXPECT_EQ(parseMessageType(*message), VmsMessageType::PUBLISHER_ID_REQUEST);
+ EXPECT_EQ(message->value.bytes.size(), bytes.size());
+ EXPECT_EQ(memcmp(message->value.bytes.data(), bytes.data(), bytes.size()), 0);
+}
+
+TEST(VmsUtilsTest, validPublisherIdResponse) {
+ auto message = createBaseVmsMessage(2);
+ message->value.int32Values =
+ hidl_vec<int32_t>{toInt(VmsMessageType::PUBLISHER_ID_RESPONSE), 1234};
+ EXPECT_EQ(parsePublisherIdResponse(*message), 1234);
+}
+
+TEST(VmsUtilsTest, invalidPublisherIdResponse) {
+ auto message = createBaseVmsMessage(1);
+ EXPECT_EQ(parsePublisherIdResponse(*message), -1);
+}
+
+TEST(VmsUtilsTest, validSequenceNumberForSubscriptionsState) {
+ auto message = createBaseVmsMessage(2);
+ message->value.int32Values =
+ hidl_vec<int32_t>{toInt(VmsMessageType::SUBSCRIPTIONS_CHANGE), 1234};
+ EXPECT_EQ(getSequenceNumberForSubscriptionsState(*message), 1234);
+}
+
+TEST(VmsUtilsTest, invalidSubscriptionsState) {
+ auto message = createBaseVmsMessage(1);
+ EXPECT_EQ(getSequenceNumberForSubscriptionsState(*message), -1);
+}
+
+TEST(VmsUtilsTest, newSequenceNumberForExistingSmallerNumber) {
+ auto message = createBaseVmsMessage(2);
+ message->value.int32Values =
+ hidl_vec<int32_t>{toInt(VmsMessageType::SUBSCRIPTIONS_CHANGE), 1234};
+ EXPECT_TRUE(isSequenceNumberNewer(*message, 1233));
+}
+
+TEST(VmsUtilsTest, newSequenceNumberForExistingGreaterNumber) {
+ auto message = createBaseVmsMessage(2);
+ message->value.int32Values =
+ hidl_vec<int32_t>{toInt(VmsMessageType::SUBSCRIPTIONS_CHANGE), 1234};
+ EXPECT_FALSE(isSequenceNumberNewer(*message, 1235));
+}
+
+TEST(VmsUtilsTest, newSequenceNumberForSameNumber) {
+ auto message = createBaseVmsMessage(2);
+ message->value.int32Values =
+ hidl_vec<int32_t>{toInt(VmsMessageType::SUBSCRIPTIONS_CHANGE), 1234};
+ EXPECT_FALSE(isSequenceNumberNewer(*message, 1234));
+}
+
+TEST(VmsUtilsTest, subscribedLayers) {
+ VmsOffers offers = {123,
+ {VmsLayerOffering(VmsLayer(1, 0, 1), {VmsLayer(4, 1, 1)}),
+ VmsLayerOffering(VmsLayer(2, 0, 1))}};
+ auto message = createBaseVmsMessage(2);
+ message->value.int32Values = hidl_vec<int32_t>{toInt(VmsMessageType::SUBSCRIPTIONS_CHANGE),
+ 1234, // sequence number
+ 2, // number of layers
+ 1, // number of associated layers
+ 1, // layer 1
+ 0,
+ 1,
+ 4, // layer 2
+ 1,
+ 1,
+ 2, // associated layer
+ 0,
+ 1,
+ 2, // number of publisher IDs
+ 111, // publisher IDs
+ 123};
+ EXPECT_TRUE(isValidVmsMessage(*message));
+ auto result = getSubscribedLayers(*message, offers);
+ EXPECT_EQ(static_cast<int>(result.size()), 2);
+ EXPECT_EQ(result.at(0), VmsLayer(1, 0, 1));
+ EXPECT_EQ(result.at(1), VmsLayer(2, 0, 1));
+}
+
+TEST(VmsUtilsTest, subscribedLayersWithDifferentSubtype) {
+ VmsOffers offers = {123, {VmsLayerOffering(VmsLayer(1, 0, 1))}};
+ auto message = createBaseVmsMessage(2);
+ message->value.int32Values = hidl_vec<int32_t>{toInt(VmsMessageType::SUBSCRIPTIONS_CHANGE),
+ 1234, // sequence number
+ 1, // number of layers
+ 0, // number of associated layers
+ 1, // layer 1
+ 1, // different subtype
+ 1};
+ EXPECT_TRUE(isValidVmsMessage(*message));
+ EXPECT_TRUE(getSubscribedLayers(*message, offers).empty());
+}
+
+TEST(VmsUtilsTest, subscribedLayersWithDifferentVersion) {
+ VmsOffers offers = {123, {VmsLayerOffering(VmsLayer(1, 0, 1))}};
+ auto message = createBaseVmsMessage(2);
+ message->value.int32Values = hidl_vec<int32_t>{toInt(VmsMessageType::SUBSCRIPTIONS_CHANGE),
+ 1234, // sequence number
+ 1, // number of layers
+ 0, // number of associated layers
+ 1, // layer 1
+ 0,
+ 2}; // different version
+ EXPECT_TRUE(isValidVmsMessage(*message));
+ EXPECT_TRUE(getSubscribedLayers(*message, offers).empty());
+}
+
+TEST(VmsUtilsTest, subscribedLayersWithDifferentPublisherId) {
+ VmsOffers offers = {123, {VmsLayerOffering(VmsLayer(1, 0, 1))}};
+ auto message = createBaseVmsMessage(2);
+ message->value.int32Values = hidl_vec<int32_t>{toInt(VmsMessageType::SUBSCRIPTIONS_CHANGE),
+ 1234, // sequence number
+ 0, // number of layers
+ 1, // number of associated layers
+ 1, // associated layer 1
+ 0,
+ 1,
+ 1, // number of publisher IDs
+ 234}; // publisher ID 1
+ EXPECT_TRUE(isValidVmsMessage(*message));
+ EXPECT_TRUE(getSubscribedLayers(*message, offers).empty());
+}
+
+TEST(VmsUtilsTest, serviceNewlyStarted) {
+ auto message = createBaseVmsMessage(2);
+ message->value.int32Values = hidl_vec<int32_t>{toInt(VmsMessageType::AVAILABILITY_CHANGE), 0};
+ EXPECT_TRUE(hasServiceNewlyStarted(*message));
+}
+
+TEST(VmsUtilsTest, serviceNotNewlyStarted) {
+ auto message = createBaseVmsMessage(2);
+ message->value.int32Values =
+ hidl_vec<int32_t>{toInt(VmsMessageType::AVAILABILITY_CHANGE), 1234};
+ EXPECT_FALSE(hasServiceNewlyStarted(*message));
+}
+
+TEST(VmsUtilsTest, invalidAvailabilityChange) {
+ auto message = createBaseVmsMessage(1);
+ EXPECT_FALSE(hasServiceNewlyStarted(*message));
+}
+
} // namespace
} // namespace vms
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 435e19c..b04d096 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -1139,7 +1139,6 @@
* Indicates which units the car is using to display fuel volume to the user. Eg. Liter or
* Gallon.
*
- * Distance units are defined in VehicleUnit.
* VehiclePropConfig.configArray is used to indicate the supported fuel volume display units.
* Volume units are defined in VehicleUnit.
* For example: configArray[0] = 0x41 // LITER
@@ -1160,7 +1159,6 @@
* Indicates which units the car is using to display tire pressure to the user. Eg. PSI, Bar or
* Kilopascal.
*
- * Distance units are defined in VehicleUnit.
* VehiclePropConfig.configArray is used to indicate the supported pressure display units.
* Pressure units are defined in VehicleUnit.
* For example: configArray[0] = 0x70 // KILOPASCAL
@@ -1182,7 +1180,6 @@
* Indicates which units the car is using to display EV battery information to the user. Eg.
* watt-hours(Wh), kilowatt-hours(kWh) or ampere-hours(Ah).
*
- * Distance units are defined in VehicleUnit.
* VehiclePropConfig.configArray is used to indicate the supported electrical energy units.
* Electrical energy units are defined in VehicleUnit.
* For example: configArray[0] = 0x60 // watt-hours
@@ -1199,6 +1196,22 @@
| VehicleArea:GLOBAL),
/**
+ * Fuel consumption units for display
+ *
+ * Indicates type of units the car is using to display fuel consumption information to user
+ * True indicates units are distance over volume such as MPG.
+ * False indicates units are volume over distance such as L/100KM.
+ *
+ * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @access VehiclePropertyAccess:READ_WRITE
+ */
+ FUEL_CONSUMPTION_UNITS_DISTANCE_OVER_VOLUME = (
+ 0x0604
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:BOOLEAN
+ | VehicleArea:GLOBAL),
+
+ /**
* Outside temperature
*
* @change_mode VehiclePropertyChangeMode:CONTINUOUS
@@ -2588,7 +2601,11 @@
KELVIN = 0x32,
MILLILITER = 0x40,
LITER = 0x41,
+
+ /** deprecated. Use US_GALLON instead. */
GALLON = 0x42,
+ US_GALLON = 0x42,
+ IMPERIAL_GALLON= 0x43,
NANO_SECS = 0x50,
SECS = 0x53,
YEAR = 0x59,
diff --git a/broadcastradio/2.0/default/BroadcastRadio.cpp b/broadcastradio/2.0/default/BroadcastRadio.cpp
index 28a0dd5..88a726f 100644
--- a/broadcastradio/2.0/default/BroadcastRadio.cpp
+++ b/broadcastradio/2.0/default/BroadcastRadio.cpp
@@ -49,6 +49,7 @@
static_cast<uint32_t>(IdentifierType::AMFM_FREQUENCY),
static_cast<uint32_t>(IdentifierType::RDS_PI),
static_cast<uint32_t>(IdentifierType::HD_STATION_ID_EXT),
+ static_cast<uint32_t>(IdentifierType::DAB_SID_EXT),
});
prop.vendorInfo = hidl_vec<VendorKeyValue>({
{"com.google.dummy", "dummy"},
diff --git a/broadcastradio/2.0/default/VirtualRadio.cpp b/broadcastradio/2.0/default/VirtualRadio.cpp
index 0b65979..c59fd8f 100644
--- a/broadcastradio/2.0/default/VirtualRadio.cpp
+++ b/broadcastradio/2.0/default/VirtualRadio.cpp
@@ -28,6 +28,7 @@
using std::mutex;
using std::vector;
using utils::make_selector_amfm;
+using utils::make_selector_dab;
VirtualRadio gAmFmRadio(
"AM/FM radio mock",
@@ -41,6 +42,16 @@
{make_selector_amfm(106100), "106 KMEL", "Drake", "Marvins Room"},
});
+// clang-format off
+VirtualRadio gDabRadio(
+ "DAB radio mock",
+ {
+ {make_selector_dab(12345, 225648), "BBC Radio 1", "Khalid", "Talk"}, // 12B
+ {make_selector_dab(22345, 222064), "Classic FM", "Jean Sibelius", "Andante Festivo"}, // 11D
+ {make_selector_dab(32345, 222064), "Absolute Radio", "Coldplay", "Clocks"}, // 11D
+ });
+// clang-format on
+
VirtualRadio::VirtualRadio(const std::string& name, const vector<VirtualProgram>& initialList)
: mName(name), mPrograms(initialList) {}
diff --git a/broadcastradio/2.0/default/VirtualRadio.h b/broadcastradio/2.0/default/VirtualRadio.h
index 9c07816..6fa70c5 100644
--- a/broadcastradio/2.0/default/VirtualRadio.h
+++ b/broadcastradio/2.0/default/VirtualRadio.h
@@ -52,6 +52,9 @@
/** AM/FM virtual radio space. */
extern VirtualRadio gAmFmRadio;
+/** DAB virtual radio space. */
+extern VirtualRadio gDabRadio;
+
} // namespace implementation
} // namespace V2_0
} // namespace broadcastradio
diff --git a/broadcastradio/2.0/default/service.cpp b/broadcastradio/2.0/default/service.cpp
index af96dad..349aba2 100644
--- a/broadcastradio/2.0/default/service.cpp
+++ b/broadcastradio/2.0/default/service.cpp
@@ -23,6 +23,7 @@
using android::hardware::joinRpcThreadpool;
using android::hardware::broadcastradio::V2_0::implementation::BroadcastRadio;
using android::hardware::broadcastradio::V2_0::implementation::gAmFmRadio;
+using android::hardware::broadcastradio::V2_0::implementation::gDabRadio;
int main() {
android::base::SetDefaultTag("BcRadioDef");
@@ -30,8 +31,13 @@
configureRpcThreadpool(4, true);
BroadcastRadio broadcastRadio(gAmFmRadio);
- auto status = broadcastRadio.registerAsService();
- CHECK_EQ(status, android::OK) << "Failed to register Broadcast Radio HAL implementation";
+ auto amFmStatus = broadcastRadio.registerAsService("amfm");
+ CHECK_EQ(amFmStatus, android::OK)
+ << "Failed to register Broadcast Radio AM/FM HAL implementation";
+
+ BroadcastRadio dabRadio(gDabRadio);
+ auto dabStatus = dabRadio.registerAsService("dab");
+ CHECK_EQ(dabStatus, android::OK) << "Failed to register Broadcast Radio DAB HAL implementation";
joinRpcThreadpool();
return 1; // joinRpcThreadpool shouldn't exit
diff --git a/broadcastradio/common/utils2x/Utils.cpp b/broadcastradio/common/utils2x/Utils.cpp
index 7892653..43f272e 100644
--- a/broadcastradio/common/utils2x/Utils.cpp
+++ b/broadcastradio/common/utils2x/Utils.cpp
@@ -299,6 +299,20 @@
return sel;
}
+ProgramSelector make_selector_dab(uint32_t sidExt, uint32_t ensemble) {
+ ProgramSelector sel = {};
+ // TODO(maryabad): Have a helper function to create the sidExt instead of
+ // passing the whole identifier here. Something like make_dab_sid_ext.
+ sel.primaryId = make_identifier(IdentifierType::DAB_SID_EXT, sidExt);
+ hidl_vec<ProgramIdentifier> secondaryIds = {
+ make_identifier(IdentifierType::DAB_ENSEMBLE, ensemble),
+ // TODO(maryabad): Include frequency here when the helper method to
+ // translate between ensemble and frequency is implemented.
+ };
+ sel.secondaryIds = secondaryIds;
+ return sel;
+}
+
Metadata make_metadata(MetadataKey key, int64_t value) {
Metadata meta = {};
meta.key = static_cast<uint32_t>(key);
diff --git a/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h b/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
index c4aecb2..f4e0732 100644
--- a/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
+++ b/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
@@ -126,6 +126,7 @@
V2_0::ProgramIdentifier make_identifier(V2_0::IdentifierType type, uint64_t value);
V2_0::ProgramSelector make_selector_amfm(uint32_t frequency);
+V2_0::ProgramSelector make_selector_dab(uint32_t sidExt, uint32_t ensemble);
V2_0::Metadata make_metadata(V2_0::MetadataKey key, int64_t value);
V2_0::Metadata make_metadata(V2_0::MetadataKey key, std::string value);
diff --git a/camera/common/1.0/default/CameraMetadata.cpp b/camera/common/1.0/default/CameraMetadata.cpp
index 4c54931..eb1bd1c 100644
--- a/camera/common/1.0/default/CameraMetadata.cpp
+++ b/camera/common/1.0/default/CameraMetadata.cpp
@@ -171,17 +171,16 @@
}
status_t CameraMetadata::checkType(uint32_t tag, uint8_t expectedType) {
- int tagType = get_camera_metadata_tag_type(tag);
+ int tagType = get_local_camera_metadata_tag_type(tag, mBuffer);
if ( CC_UNLIKELY(tagType == -1)) {
ALOGE("Update metadata entry: Unknown tag %d", tag);
return INVALID_OPERATION;
}
if ( CC_UNLIKELY(tagType != expectedType) ) {
ALOGE("Mismatched tag type when updating entry %s (%d) of type %s; "
- "got type %s data instead ",
- get_camera_metadata_tag_name(tag), tag,
- camera_metadata_type_names[tagType],
- camera_metadata_type_names[expectedType]);
+ "got type %s data instead ",
+ get_local_camera_metadata_tag_name(tag, mBuffer), tag,
+ camera_metadata_type_names[tagType], camera_metadata_type_names[expectedType]);
return INVALID_OPERATION;
}
return OK;
@@ -298,7 +297,7 @@
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
}
- int type = get_camera_metadata_tag_type(tag);
+ int type = get_local_camera_metadata_tag_type(tag, mBuffer);
if (type == -1) {
ALOGE("%s: Tag %d not found", __FUNCTION__, tag);
return BAD_VALUE;
@@ -332,9 +331,9 @@
}
if (res != OK) {
- ALOGE("%s: Unable to update metadata entry %s.%s (%x): %s (%d)",
- __FUNCTION__, get_camera_metadata_section_name(tag),
- get_camera_metadata_tag_name(tag), tag, strerror(-res), res);
+ ALOGE("%s: Unable to update metadata entry %s.%s (%x): %s (%d)", __FUNCTION__,
+ get_local_camera_metadata_section_name(tag, mBuffer),
+ get_local_camera_metadata_tag_name(tag, mBuffer), tag, strerror(-res), res);
}
IF_ALOGV() {
@@ -391,18 +390,16 @@
if (res == NAME_NOT_FOUND) {
return OK;
} else if (res != OK) {
- ALOGE("%s: Error looking for entry %s.%s (%x): %s %d",
- __FUNCTION__,
- get_camera_metadata_section_name(tag),
- get_camera_metadata_tag_name(tag), tag, strerror(-res), res);
+ ALOGE("%s: Error looking for entry %s.%s (%x): %s %d", __FUNCTION__,
+ get_local_camera_metadata_section_name(tag, mBuffer),
+ get_local_camera_metadata_tag_name(tag, mBuffer), tag, strerror(-res), res);
return res;
}
res = delete_camera_metadata_entry(mBuffer, entry.index);
if (res != OK) {
- ALOGE("%s: Error deleting entry %s.%s (%x): %s %d",
- __FUNCTION__,
- get_camera_metadata_section_name(tag),
- get_camera_metadata_tag_name(tag), tag, strerror(-res), res);
+ ALOGE("%s: Error deleting entry %s.%s (%x): %s %d", __FUNCTION__,
+ get_local_camera_metadata_section_name(tag, mBuffer),
+ get_local_camera_metadata_tag_name(tag, mBuffer), tag, strerror(-res), res);
}
return res;
}
diff --git a/camera/common/1.0/default/OWNERS b/camera/common/1.0/default/OWNERS
index f112576..70128c7 100644
--- a/camera/common/1.0/default/OWNERS
+++ b/camera/common/1.0/default/OWNERS
@@ -1 +1 @@
-include platform/frameworks/av/camera:/OWNERS
+include platform/frameworks/av:camera/OWNERS
diff --git a/camera/device/3.4/default/CameraDeviceSession.cpp b/camera/device/3.4/default/CameraDeviceSession.cpp
index e00b3f8..03b6050 100644
--- a/camera/device/3.4/default/CameraDeviceSession.cpp
+++ b/camera/device/3.4/default/CameraDeviceSession.cpp
@@ -187,7 +187,6 @@
mPhysicalCameraIdMap[id] = requestedConfiguration.streams[i].physicalCameraId;
mStreamMap[id].data_space = mapToLegacyDataspace(
mStreamMap[id].data_space);
- mStreamMap[id].physical_camera_id = mPhysicalCameraIdMap[id].c_str();
mCirculatingBuffers.emplace(stream.mId, CirculatingBuffers{});
} else {
// width/height/format must not change, but usage/rotation might need to change
@@ -206,6 +205,11 @@
mStreamMap[id].rotation = (int) requestedConfiguration.streams[i].v3_2.rotation;
mStreamMap[id].usage = (uint32_t) requestedConfiguration.streams[i].v3_2.usage;
}
+ // It is possible for the entry in 'mStreamMap' to get initialized by an older
+ // HIDL API. Make sure that the physical id is always initialized when using
+ // a more recent API call.
+ mStreamMap[id].physical_camera_id = mPhysicalCameraIdMap[id].c_str();
+
(*streams)[i] = &mStreamMap[id];
}
diff --git a/camera/device/3.5/types.hal b/camera/device/3.5/types.hal
index f98c603..6d861e2 100644
--- a/camera/device/3.5/types.hal
+++ b/camera/device/3.5/types.hal
@@ -21,6 +21,14 @@
import @3.2::CameraBlobId;
/**
+ * If the result metadata cannot be produced for a physical camera device part of a logical
+ * multi-camera, then HAL must invoke the notification callback and pass a message with ERROR_RESULT
+ * code and errorStreamId that contains the stream id associated with that physical device.
+ * The behavior during absent result metadata remains unchanged for a logical or a non-logical
+ * camera device and the errorStreamId must be set to -1.
+ */
+
+/**
* StreamConfiguration:
*
* Identical to @3.4::StreamConfiguration, except that it contains streamConfigCounter
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index d77bb0e..814b88b 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -767,7 +767,7 @@
const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& metadata);
void verifyStreamCombination(sp<device::V3_5::ICameraDevice> cameraDevice3_5,
const ::android::hardware::camera::device::V3_4::StreamConfiguration &config3_4,
- bool expectedStatus);
+ bool expectedStatus, bool expectStreamCombQuery);
void verifyLogicalCameraResult(const camera_metadata_t* staticMetadata,
const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& resultMetadata);
@@ -1364,19 +1364,20 @@
}
hidl_vec<StreamBuffer> tmpRetBuffers(bufReq.numBuffersRequested);
- for (size_t i = 0; i < bufReq.numBuffersRequested; i++) {
+ for (size_t j = 0; j < bufReq.numBuffersRequested; j++) {
hidl_handle buffer_handle;
mParent->allocateGraphicBuffer(stream.width, stream.height,
android_convertGralloc1To0Usage(
halStream.producerUsage, halStream.consumerUsage),
halStream.overrideFormat, &buffer_handle);
- tmpRetBuffers[i] = {stream.id, mNextBufferId, buffer_handle, BufferStatus::OK,
+ tmpRetBuffers[j] = {stream.id, mNextBufferId, buffer_handle, BufferStatus::OK,
nullptr, nullptr};
mOutstandingBufferIds[idx].insert(std::make_pair(mNextBufferId++, buffer_handle));
}
atLeastOneStreamOk = true;
- bufRets[0].val.buffers(std::move(tmpRetBuffers));
+ bufRets[i].streamId = stream.id;
+ bufRets[i].val.buffers(std::move(tmpRetBuffers));
}
if (allStreamOk) {
@@ -2877,8 +2878,9 @@
createStreamConfiguration(streams3_2, StreamConfigurationMode::NORMAL_MODE,
&config3_2, &config3_4, &config3_5, jpegBufferSize);
if (session3_5 != nullptr) {
+ bool expectStreamCombQuery = (isLogicalMultiCamera(staticMeta) == Status::OK);
verifyStreamCombination(cameraDevice3_5, config3_4,
- /*expectedStatus*/ true);
+ /*expectedStatus*/ true, expectStreamCombQuery);
config3_5.streamConfigCounter = streamConfigCounter++;
ret = session3_5->configureStreams_3_5(config3_5,
[streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
@@ -2971,7 +2973,8 @@
createStreamConfiguration(streams, StreamConfigurationMode::NORMAL_MODE,
&config3_2, &config3_4, &config3_5, jpegBufferSize);
if (session3_5 != nullptr) {
- verifyStreamCombination(cameraDevice3_5, config3_4, /*expectedStatus*/ false);
+ verifyStreamCombination(cameraDevice3_5, config3_4, /*expectedStatus*/ false,
+ /*expectStreamCombQuery*/false);
config3_5.streamConfigCounter = streamConfigCounter++;
ret = session3_5->configureStreams_3_5(config3_5,
[](Status s, device::V3_4::HalStreamConfiguration) {
@@ -3232,7 +3235,7 @@
&config3_2, &config3_4, &config3_5, jpegBufferSize);
if (session3_5 != nullptr) {
verifyStreamCombination(cameraDevice3_5, config3_4,
- /*expectedStatus*/ true);
+ /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
config3_5.streamConfigCounter = streamConfigCounter++;
ret = session3_5->configureStreams_3_5(config3_5,
[](Status s, device::V3_4::HalStreamConfiguration halConfig) {
@@ -3483,7 +3486,7 @@
&config3_2, &config3_4, &config3_5, jpegBufferSize);
if (session3_5 != nullptr) {
verifyStreamCombination(cameraDevice3_5, config3_4,
- /*expectedStatus*/ true);
+ /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
config3_5.streamConfigCounter = streamConfigCounter++;
ret = session3_5->configureStreams_3_5(config3_5,
[](Status s, device::V3_4::HalStreamConfiguration halConfig) {
@@ -3578,7 +3581,7 @@
&config3_2, &config3_4, &config3_5);
if (session3_5 != nullptr) {
verifyStreamCombination(cameraDevice3_5, config3_4,
- /*expectedStatus*/ true);
+ /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
config3_5.streamConfigCounter = streamConfigCounter++;
ret = session3_5->configureStreams_3_5(config3_5,
[streamId](Status s, device::V3_4::HalStreamConfiguration halConfig) {
@@ -3811,7 +3814,7 @@
&config3_2, &config3_4, &config3_5, jpegBufferSize);
if (session3_5 != nullptr) {
verifyStreamCombination(cameraDevice3_5, config3_4,
- /*expectedStatus*/ true);
+ /*expectedStatus*/ true, /*expectStreamCombQuery*/ false);
config3_5.streamConfigCounter = streamConfigCounter++;
ret = session3_5->configureStreams_3_5(config3_5,
[](Status s, device::V3_4::HalStreamConfiguration halConfig) {
@@ -5535,11 +5538,12 @@
void CameraHidlTest::verifyStreamCombination(sp<device::V3_5::ICameraDevice> cameraDevice3_5,
const ::android::hardware::camera::device::V3_4::StreamConfiguration &config3_4,
- bool expectedStatus) {
+ bool expectedStatus, bool expectMethodSupported) {
if (cameraDevice3_5.get() != nullptr) {
auto ret = cameraDevice3_5->isStreamCombinationSupported(config3_4,
- [expectedStatus] (Status s, bool combStatus) {
- ASSERT_TRUE((Status::OK == s) || (Status::METHOD_NOT_SUPPORTED == s));
+ [expectedStatus, expectMethodSupported] (Status s, bool combStatus) {
+ ASSERT_TRUE((Status::OK == s) ||
+ (!expectMethodSupported && Status::METHOD_NOT_SUPPORTED == s));
if (Status::OK == s) {
ASSERT_TRUE(combStatus == expectedStatus);
}
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 566bd48..e48cc79 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -271,7 +271,16 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="false">
+ <hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IComponentStore</name>
+ <regex-instance>default[0-9]*</regex-instance>
+ <regex-instance>vendor[0-9]*_software</regex-instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
<name>android.hardware.media.omx</name>
<version>1.0</version>
<interface>
@@ -301,7 +310,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.nfc</name>
- <version>1.1</version>
+ <version>1.2</version>
<interface>
<name>INfc</name>
<instance>default</instance>
diff --git a/current.txt b/current.txt
index 99100f1..fcb8447 100644
--- a/current.txt
+++ b/current.txt
@@ -396,6 +396,8 @@
d702fb01dc2a0733aa820b7eb65435ee3334f75632ef880bafd2fb8803a20a58 android.hardware.gnss@1.0::IGnssMeasurementCallback
7c7721c0f773fcf422b71a4f558545e9e36acc973e58ca51e5bd53905cf46bc0 android.hardware.graphics.bufferqueue@1.0::IGraphicBufferProducer
d4fea995378bb4f421b4e24ccf68cad2734ab07fe4f874a126ba558b99df5766 android.hardware.graphics.composer@2.1::IComposerClient
+f7d7cb747dc01a9fdb2d39a80003b4d8df9be733d65f5842198802eb6209db69 android.hardware.graphics.mapper@2.0::IMapper
+65a021fa89085b62fc96b2b6d3bef2f9103cf4d63379c68bc154fd9eef672852 android.hardware.health@1.0::types
b7ecf29927055ec422ec44bf776223f07d79ad9f92ccf9becf167e62c2607e7a android.hardware.keymaster@4.0::IKeymasterDevice
574e8f1499436fb4075894dcae0b36682427956ecb114f17f1fe22d116a83c6b android.hardware.neuralnetworks@1.0::IPreparedModel
417ab60fe1ef786778047e4486f3d868ebce570d91addd8fe4251515213072de android.hardware.neuralnetworks@1.0::types
@@ -416,11 +418,11 @@
0a911297821854985cfcdb17b63d7948af0f0f51ce8c68cc86367c185bbc772e android.hardware.audio@5.0::IDevicesFactory
ce2e8c6c8559fd42bd69e0dee27b4d9c93cd9b2eff487b4e6b6395b6a1a993d6 android.hardware.audio@5.0::IPrimaryDevice
4a4e5e5d9357004a1256bde8d36010ee00c51cea811a1c1e0dd969a9fc0bf862 android.hardware.audio@5.0::IStream
-e05e48c583de14c1e5a6fa9d48ea50244e3e0924b76b342374e7471dc8007ba9 android.hardware.audio@5.0::IStreamIn
+b9d41ff4031266de1ecef394a8a64de7d857634dd08dc6be855fca2fe3075975 android.hardware.audio@5.0::IStreamIn
9471b12b1c255bb530695720bc4174bd74987b75b1f820854af8944bc8c215c9 android.hardware.audio@5.0::IStreamOut
1b0500367ed2b32a841667ac3200edf3d3a164e8004aca445ff1b085ac831e93 android.hardware.audio@5.0::IStreamOutCallback
83e365479cc77d8717c155e1787ee668cd2ae4c557b467cf75b8e7cd53697ad8 android.hardware.audio@5.0::types
-a0df6961e65444e1ca40a206d7f31304d313e8b7e5b122855e3272ab02720cd4 android.hardware.audio.common@5.0::types
+07d17800b298331e90d4ea5d8ba19a1ae3fe9c1dbff08d9f75fd3ade09496d67 android.hardware.audio.common@5.0::types
f269297866765b95ddd1825676cc8a772f0c7c9863286df596fc302781a42ff5 android.hardware.audio.effect@5.0::IAcousticEchoCancelerEffect
fa187b602d8939644ef708ed7627f2e3deac97899a4bda1de07f2ff126abe243 android.hardware.audio.effect@5.0::IAutomaticGainControlEffect
e1bf864ccb8458c0da1dcc74a2e748b1dca8ac360df590591cf82d98292d7981 android.hardware.audio.effect@5.0::IBassBoostEffect
@@ -446,7 +448,7 @@
09ab9b24994429d9bb32a3fb420b6f6be3e47eb655139a2c08c4e80d3f33ff95 android.hardware.camera.device@3.5::ICameraDevice
06237de53c42890029e3f8fe7d1480d078469c0d07608e51c37b4d485d342992 android.hardware.camera.device@3.5::ICameraDeviceCallback
08c68b196e2fc4e5ba67ba0d0917bde828a87cbe2cffec19d04733972da9eb49 android.hardware.camera.device@3.5::ICameraDeviceSession
-a848f7cb3cb3d080cf175bf08412322bfddb535168253796cdf777afdbf05b38 android.hardware.camera.device@3.5::types
+f9b8b388c0c76669e4b9189e4943efd2982f9bda5c10e276f96cc91bc8e818d6 android.hardware.camera.device@3.5::types
f727d5f350f55a6d3354aad2feb64e43200de77c10d9d642465566bc260bb8ec android.hardware.camera.metadata@3.4::types
0fb39a7809ad1c52b3efbbed5ef4749b06c2a4f1f19cdc3efa2e3d9b28f1205c android.hardware.camera.provider@2.5::ICameraProvider
f5777403d65135a5407723671bc7a864cdca83aea13ee3ce2894b95e6588ca3a android.hardware.camera.provider@2.5::types
@@ -464,12 +466,15 @@
7f460e795f5d1ed5e378935f98c6db4d39497de988aef1b4c2a4a07a6c400392 android.hardware.gnss@2.0::IAGnss
2e5ad983734069e84a760004b32da0d09e4170c05380abe27e6eb80e4aa70d5a android.hardware.gnss@2.0::IAGnssCallback
1f4ac068a88a72360280d94a7f6fd7c63813c1eea4891a0eb01394d3e7e775f2 android.hardware.gnss@2.0::IAGnssRil
-6e2f9a44375a0ae0b49ca7d711cb88945189d398535078408269e1e85889061d android.hardware.gnss@2.0::IGnss
-782dfc724272f279985de348c824197357941382f73c0083f0344d8ec594d2a8 android.hardware.gnss@2.0::IGnssCallback
+4deafcdcffa2d002119e7f58810b767a84666e76475aae68e757ec2845d9756d android.hardware.gnss@2.0::IGnss
+db6bdf6dfc5edf6c85d2944976db899227abb51079c893874353c322342c50b6 android.hardware.gnss@2.0::IGnssBatching
+1f89392f1ebb693d8fa6f50324b1635fc79fab246d31900e63998e1b0e17511c android.hardware.gnss@2.0::IGnssBatchingCallback
+64232037109a5e5f53ab0377e755ec494ae93fcb5279e6eea71dec2e7ac6fbfc android.hardware.gnss@2.0::IGnssCallback
ecc966c68bddbd95c8dae782b84204cf01c75734675e8769963f3b5106ec128b android.hardware.gnss@2.0::IGnssConfiguration
+b670bae2ab8517336290532e364502b4db9120340d75474ccc8442b1b15d6ab7 android.hardware.gnss@2.0::IGnssDebug
c67759f5d6387d273b66729180d03690e827f0b6b8d4e13ce2ff42d31b224065 android.hardware.gnss@2.0::IGnssMeasurement
-3dd30a3ca77ef5ab109a55ba603ff816ae5019436886093dccf8fd6a068f85f1 android.hardware.gnss@2.0::IGnssMeasurementCallback
-4bcd767dd05304b4722c6521c7ed8d4a05faf6022f228f2c088379c647871f7c android.hardware.gnss@2.0::types
+15e09903748857f4beb5f485784606931fa5a6277cd070baa6d584df485b7948 android.hardware.gnss@2.0::IGnssMeasurementCallback
+a49c973f21ddf41bc402de55d7c8dffacf4dce06b0bbca4f5ffd3b09a471317e android.hardware.gnss@2.0::types
d4cc8d91930d5a1a62deb0d97d398510a115ce3ede2d2978738651b9d01b11c3 android.hardware.gnss.measurement_corrections@1.0::IMeasurementCorrections
3eec9763db9b101644f14175b77c9954047445a468e9c743fd402d472d4aa97e android.hardware.gnss.measurement_corrections@1.0::IMeasurementCorrectionsCallback
6ef12cd95df73f8f80c25eb035d98ca4594f9cee571fdabea838a0b6016dd908 android.hardware.gnss.measurement_corrections@1.0::types
@@ -502,14 +507,14 @@
21aa259585caaa27b6470ebcd8509aabde0ef5d039160aa6425d589cb787488b android.hardware.media.c2@1.0::IInputSink
b9422a9aca84df1ff9623dc12c0562abce97716e28d63a965f2bfb88f9ad9607 android.hardware.media.c2@1.0::IInputSurface
0a786a19e6753f9774a7ca7781c2a2edfe5c0b5fa112355dfa0e50ebedeb08b9 android.hardware.media.c2@1.0::IInputSurfaceConnection
-4cb139f729c29d8d6f4ecdab149c4feb571dad8a06e56cd57fcb52e70208bab4 android.hardware.media.c2@1.0::types
+7d3c292ca75ec3e22a8fd4ae72d2edb0659d280257e763786e766f3429954dd1 android.hardware.media.c2@1.0::types
4880af120fc1640225abdc2c60bda6d79617d73484d5124913c7278af3b11e2d android.hardware.neuralnetworks@1.2::IBurstCallback
19877e466ad8c6ed42b38050b77bd010cf7800ff365fdc8574f45bbfda03a758 android.hardware.neuralnetworks@1.2::IBurstContext
-96249c852dabeefa3a9496ecdfc44681a071c665bfbf88527bf775c88bf1ab1b android.hardware.neuralnetworks@1.2::IDevice
+b83317b66721241887d2770b5ae95fd5af1e77c5daa7530ecb08fae8892f2b43 android.hardware.neuralnetworks@1.2::IDevice
92714960d1a53fc2ec557302b41c7cc93d2636d8364a44bd0f85be0c92927ff8 android.hardware.neuralnetworks@1.2::IExecutionCallback
-83885d366f22ada42c00d8854f0b7e7ba4cf73ddf80bb0d8e168ce132cec57ea android.hardware.neuralnetworks@1.2::IPreparedModel
+36e1064c869965dee533c537cefbe87e54db8bd8cd45be7e0e93e00e8a43863a android.hardware.neuralnetworks@1.2::IPreparedModel
e1c734d1545e1a4ae749ff1dd9704a8e594c59aea7c8363159dc258e93e0df3b android.hardware.neuralnetworks@1.2::IPreparedModelCallback
-114056b3b9303e0e858f28e718ba45722de5678d1d54eec0dcd10788604bf2bb android.hardware.neuralnetworks@1.2::types
+9b3963253e521cca19fd81aeca83aee6dcfe3bdf2805c07cb2d3f64381709b71 android.hardware.neuralnetworks@1.2::types
cf7a4ba516a638f9b82a249c91fb603042c2d9ca43fd5aad9cf6c0401ed2a5d7 android.hardware.nfc@1.2::INfc
abf98c2ae08bf765db54edc8068e36d52eb558cff6706b6fd7c18c65a1f3fc18 android.hardware.nfc@1.2::types
4cb252dc6372a874aef666b92a6e9529915aa187521a700f0789065c3c702ead android.hardware.power.stats@1.0::IPowerStats
@@ -541,9 +546,10 @@
61bc302e7c974c59b25898c585c6e9685e8a81021b1bed3eedf5224198f2785a android.hardware.usb@1.2::IUsb
46996cd2a1c66261a75a1f6ecada77eeb5861eb264fa39b996548fe0a7f22dd3 android.hardware.usb@1.2::IUsbCallback
3bbaa8cbc5d6b1da21f5509b2b641e05fc7eeca1354751eb1bb3cf37f89aa32f android.hardware.usb@1.2::types
-92c1a726c80970d623b891f7c2f9a989a40a15ee1244092b49f4eb6adcdce4e9 android.hardware.vibrator@1.3::IVibrator
+0f7ff73793548d5154014059b7e0fe9ef6355d32218ace157954d02055f5248b android.hardware.vibrator@1.3::IVibrator
+2e313dc27a1327a29862ab3e085917f75c9e996f7c8df5a0ce37b9a0ed076b80 android.hardware.vibrator@1.3::types
f19832856a3f53ced5ef91d3cc630a57fb7f4d4ce15f364dbed09099b89f6830 android.hardware.wifi@1.3::IWifi
-7c6799c19bfdb3dec016b751556fe246cf7d37191ee7bb82a0091ab9fbf6f2fb android.hardware.wifi@1.3::IWifiChip
+64be084b6e1ef330b75fa916593dc0b94b0ec7a16d5cfaa5a31e6c9143c8288d android.hardware.wifi@1.3::IWifiChip
3bef30e8b61ab050c0f6fd26572712be5ebb7707d624c9aa6c74bbb9d6a5b4a9 android.hardware.wifi@1.3::IWifiStaIface
f3dbd8dd0d6333c005610288a4785d0ef79a72a7bbe6d0a46d46fa89fc886f1e android.hardware.wifi@1.3::types
2fae61e962f68091335f7ff4581fcfe2e28ce7f6132d7a712fa13d7965543e4d android.hardware.wifi.hostapd@1.1::IHostapd
diff --git a/gnss/2.0/Android.bp b/gnss/2.0/Android.bp
index c01ec55..6cfd346 100644
--- a/gnss/2.0/Android.bp
+++ b/gnss/2.0/Android.bp
@@ -12,8 +12,11 @@
"IAGnssCallback.hal",
"IAGnssRil.hal",
"IGnss.hal",
+ "IGnssBatching.hal",
+ "IGnssBatchingCallback.hal",
"IGnssCallback.hal",
"IGnssConfiguration.hal",
+ "IGnssDebug.hal",
"IGnssMeasurement.hal",
"IGnssMeasurementCallback.hal",
],
diff --git a/gnss/2.0/IGnss.hal b/gnss/2.0/IGnss.hal
index 2c149b7..f19f8d0 100644
--- a/gnss/2.0/IGnss.hal
+++ b/gnss/2.0/IGnss.hal
@@ -23,9 +23,11 @@
import GnssLocation;
import IGnssCallback;
import IGnssConfiguration;
+import IGnssDebug;
import IGnssMeasurement;
import IAGnss;
import IAGnssRil;
+import IGnssBatching;
/**
* Represents the standard GNSS (Global Navigation Satellite System) interface.
@@ -55,6 +57,13 @@
getExtensionGnssConfiguration_2_0() generates (IGnssConfiguration gnssConfigurationIface);
/**
+ * This method returns the IGnssDebug interface.
+ *
+ * @return gnssDebugIface Handle to the IGnssDebug interface.
+ */
+ getExtensionGnssDebug_2_0() generates (IGnssDebug gnssDebugIface);
+
+ /**
* This method returns the IAGnss Interface.
*
* The getExtensionAGnss() must return nullptr as the @1.0::IAGnss interface is
@@ -97,6 +106,13 @@
getExtensionVisibilityControl() generates (IGnssVisibilityControl visibilityControlIface);
/**
+ * This method returns the IGnssBatching interface.
+ *
+ * @return batchingIface Handle to the IGnssBatching interface.
+ */
+ getExtensionGnssBatching_2_0() generates (IGnssBatching batchingIface);
+
+ /**
* Injects current location from the best available location provider.
*
* Unlike injectLocation, this method may inject a recent GNSS location from the HAL
diff --git a/gnss/2.0/IGnssBatching.hal b/gnss/2.0/IGnssBatching.hal
new file mode 100644
index 0000000..961fa69
--- /dev/null
+++ b/gnss/2.0/IGnssBatching.hal
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss@2.0;
+
+import @1.0::IGnssBatching;
+import IGnssBatchingCallback;
+
+/**
+ * Extended interface for GNSS Batching support.
+ *
+ * If this interface is supported, this batching request must be able to run in
+ * parallel with, or without, non-batched location requested by the
+ * IGnss start() & stop() - i.e. both requests must be handled independently,
+ * and not interfere with each other.
+ *
+ * For example, if a 1Hz continuous output is underway on the IGnssCallback,
+ * due to an IGnss start() operation,
+ * and then a IGnssBatching start() is called for a location every 10
+ * seconds, the newly added batching request must not disrupt the 1Hz
+ * continuous location output on the IGnssCallback.
+ *
+ * As with GNSS Location outputs, source of location must be GNSS satellite
+ * measurements, optionally using interial and baro sensors to improve
+ * relative motion filtering. No additional absolute positioning information,
+ * such as WiFi derived location, may be mixed with the GNSS information.
+ */
+interface IGnssBatching extends @1.0::IGnssBatching {
+ /**
+ * Opens the interface and provides the callback routines
+ * to the implementation of this interface.
+ *
+ * @param callback Callback interface for IGnssBatching.
+ *
+ * @return success Returns true on success.
+ */
+ init_2_0(IGnssBatchingCallback callback) generates (bool success);
+};
\ No newline at end of file
diff --git a/gnss/2.0/IGnssBatchingCallback.hal b/gnss/2.0/IGnssBatchingCallback.hal
new file mode 100644
index 0000000..4f8b4ec
--- /dev/null
+++ b/gnss/2.0/IGnssBatchingCallback.hal
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss@2.0;
+
+/** The callback interface to report measurements from the HAL. */
+interface IGnssBatchingCallback {
+ /**
+ * Called when a batch of locations is output, by various means, including
+ * a flush request, as well as the buffer becoming full (if appropriate option
+ * is set.)
+ *
+ * All locations returned by this callback must be cleared from the hardware
+ * buffer, such the sequential calls of this callback do not return any
+ * redundant locations. (Same lat/lon, at a new time, is acceptable.)
+ *
+ * The GnssLocation struct in gnss@2.0 is extended to include elapsed realtime
+ * information.
+ *
+ * @param locations GNSS Location information from HAL.
+ */
+ gnssLocationBatchCb(vec<GnssLocation> locations);
+};
diff --git a/gnss/2.0/IGnssCallback.hal b/gnss/2.0/IGnssCallback.hal
index 4c31cf5..d6db9cc 100644
--- a/gnss/2.0/IGnssCallback.hal
+++ b/gnss/2.0/IGnssCallback.hal
@@ -19,6 +19,7 @@
import @1.0::IGnssCallback;
import @1.1::IGnssCallback;
import GnssLocation;
+import GnssConstellationType;
/**
* This interface is required for the HAL to communicate certain information
@@ -29,32 +30,13 @@
/** Flags for the gnssSetCapabilities callback. */
@export(name="", value_prefix="GPS_CAPABILITY_")
- enum Capabilities : uint32_t {
- /**
- * GNSS HAL schedules fixes for RECURRENCE_PERIODIC mode.
- * If this is not set, then the framework will use 1000ms for
- * minInterval and must call start() and stop() to schedule the GNSS.
- */
- SCHEDULING = 1 << 0,
- /** GNSS supports MS-Based AGNSS mode */
- MSB = 1 << 1,
- /** GNSS supports MS-Assisted AGNSS mode */
- MSA = 1 << 2,
- /** GNSS supports single-shot fixes */
- SINGLE_SHOT = 1 << 3,
- /** GNSS supports on demand time injection */
- ON_DEMAND_TIME = 1 << 4,
- /**
- * Values for the flags removed from IGnssCallback.hal@1.0 Capabilities
- * enum are marked as reserved and not reused here to avoid confusion.
- */
- RESERVED_1 = 1 << 5,
- RESERVED_2 = 1 << 6,
- RESERVED_3 = 1 << 7,
+ enum Capabilities : @1.0::IGnssCallback.Capabilities {
/** GNSS supports low power mode */
- LOW_POWER_MODE = 1 << 8,
+ LOW_POWER_MODE = 1 << 8,
/** GNSS supports blacklisting satellites */
- SATELLITE_BLACKLIST = 1 << 9
+ SATELLITE_BLACKLIST = 1 << 9,
+ /** GNSS supports measurement corrections */
+ MEASUREMENT_CORRECTIONS = 1 << 10
};
/**
@@ -94,4 +76,26 @@
* during-call to E911, or up to 5 minutes after end-of-call or text to E911).
*/
gnssRequestLocationCb_2_0(bool independentFromGnss, bool isUserEmergency);
+
+ /** Extends a GnssSvInfo, replacing the GnssConstellationType. */
+ struct GnssSvInfo {
+ /**
+ * GNSS satellite information for a single satellite and frequency.
+ *
+ * In this version of the HAL, the field 'constellation' in the v1_0 struct is deprecated,
+ * and is no longer used by the framework. The constellation type is instead reported in
+ * @2.0::IGnssCallback.GnssSvInfo.constellation.
+ */
+ @1.0::IGnssCallback.GnssSvInfo v1_0;
+
+ /** Defines the constellation of the given SV. */
+ GnssConstellationType constellation;
+ };
+
+ /**
+ * Callback for the HAL to pass a vector of GnssSvInfo back to the client.
+ *
+ * @param svInfo SV status information from HAL.
+ */
+ gnssSvStatusCb_2_0(vec<GnssSvInfo> svInfoList);
};
diff --git a/gnss/2.0/IGnssDebug.hal b/gnss/2.0/IGnssDebug.hal
new file mode 100644
index 0000000..a3138ba
--- /dev/null
+++ b/gnss/2.0/IGnssDebug.hal
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss@2.0;
+
+import @1.0::IGnssDebug;
+
+/** Extended interface for DEBUG support. */
+interface IGnssDebug extends @1.0::IGnssDebug {
+
+ /** Extending SatelliteData, replacing the GnssConstellationType. */
+ struct SatelliteData {
+ /**
+ * GNSS Satellite info.
+ *
+ * In this version of the HAL, the field 'constellation' in the v1_0 struct is deprecated,
+ * and is no longer used by the framework. The constellation type is instead reported in
+ * @2.0::IGnssDebug.SatelliteData.constellation.
+ */
+ @1.0::IGnssDebug.SatelliteData v1_0;
+
+ /** Defines the constellation type of the given SV. */
+ GnssConstellationType constellation;
+ };
+
+ /**
+ * Provides a set of debug information that is filled by the GNSS chipset when the method
+ * getDebugData() is invoked.
+ */
+ struct DebugData {
+ /** Current best known position. */
+ @1.0::IGnssDebug.PositionDebug position;
+
+ /** Current best know time estimate. */
+ @1.0::IGnssDebug.TimeDebug time;
+
+ /**
+ * Provides a list of the available satellite data, for all
+ * satellites and constellations the device can track,
+ * including GnssConstellationType UNKNOWN.
+ */
+ vec<SatelliteData> satelliteDataArray;
+ };
+
+ /**
+ * This methods requests position, time and satellite ephemeris debug information from the HAL.
+ *
+ * @return ret debugData information from GNSS Hal that contains the current best known
+ * position, best known time estimate and a complete list of constellations that the device can
+ * track.
+ */
+ getDebugData_2_0() generates (DebugData debugData);
+};
diff --git a/gnss/2.0/IGnssMeasurementCallback.hal b/gnss/2.0/IGnssMeasurementCallback.hal
index d9751d3..e055f7a 100644
--- a/gnss/2.0/IGnssMeasurementCallback.hal
+++ b/gnss/2.0/IGnssMeasurementCallback.hal
@@ -19,6 +19,7 @@
import @1.0::IGnssMeasurementCallback;
import @1.1::IGnssMeasurementCallback;
import ElapsedRealtime;
+import GnssConstellationType;
/** The callback interface to report measurements from the HAL. */
interface IGnssMeasurementCallback extends @1.1::IGnssMeasurementCallback {
@@ -365,7 +366,8 @@
};
/**
- * Extends a GNSS Measurement, adding a GnssMeasurementCodeType.
+ * Extends a GNSS Measurement, adding a GnssMeasurementCodeType, a GnssMeasurementState, and
+ * replacing the GnssConstellationType.
*/
struct GnssMeasurement {
/**
@@ -380,6 +382,10 @@
* In this version of the HAL, the field 'state' in the v1_1.v1_0 struct is deprecated, and
* is no longer used by the framework. The satellite sync state is instead reported in
* @2.0::IGnssMeasurementCallback.GnssMeasurement.state.
+ *
+ * In this version of the HAL, the field 'constellation' in the v1_1.v1_0 struct is
+ * deprecated, and is no longer used by the framework. The constellation type is instead
+ * reported in @2.0::IGnssMeasurementCallback.GnssMeasurement.constellation.
*/
@1.1::IGnssMeasurementCallback.GnssMeasurement v1_1;
@@ -442,6 +448,11 @@
* This value is mandatory.
*/
bitfield<GnssMeasurementState> state;
+
+ /**
+ * The constellation type of the GNSS measurement.
+ */
+ GnssConstellationType constellation;
};
/**
diff --git a/gnss/2.0/default/Android.bp b/gnss/2.0/default/Android.bp
index 64187e2..0fcd764 100644
--- a/gnss/2.0/default/Android.bp
+++ b/gnss/2.0/default/Android.bp
@@ -25,6 +25,7 @@
"AGnss.cpp",
"AGnssRil.cpp",
"Gnss.cpp",
+ "GnssBatching.cpp",
"GnssMeasurement.cpp",
"GnssMeasurementCorrections.cpp",
"GnssVisibilityControl.cpp",
diff --git a/gnss/2.0/default/Gnss.cpp b/gnss/2.0/default/Gnss.cpp
index 1dfdadb..3d64fc3 100644
--- a/gnss/2.0/default/Gnss.cpp
+++ b/gnss/2.0/default/Gnss.cpp
@@ -23,6 +23,7 @@
#include "AGnss.h"
#include "AGnssRil.h"
+#include "GnssBatching.h"
#include "GnssConfiguration.h"
#include "GnssMeasurement.h"
#include "GnssMeasurementCorrections.h"
@@ -236,6 +237,11 @@
return new GnssConfiguration{};
}
+Return<sp<V2_0::IGnssDebug>> Gnss::getExtensionGnssDebug_2_0() {
+ // TODO(b/124012850): Implement function.
+ return sp<V2_0::IGnssDebug>{};
+}
+
Return<sp<V2_0::IAGnss>> Gnss::getExtensionAGnss_2_0() {
return new AGnss{};
}
@@ -260,6 +266,10 @@
return new GnssVisibilityControl();
}
+Return<sp<V2_0::IGnssBatching>> Gnss::getExtensionGnssBatching_2_0() {
+ return new GnssBatching();
+}
+
Return<bool> Gnss::setCallback_2_0(const sp<V2_0::IGnssCallback>& callback) {
ALOGD("Gnss::setCallback_2_0");
if (callback == nullptr) {
@@ -270,7 +280,8 @@
sGnssCallback_2_0 = callback;
using Capabilities = V2_0::IGnssCallback::Capabilities;
- const auto capabilities = Capabilities::LOW_POWER_MODE | Capabilities::SATELLITE_BLACKLIST;
+ const auto capabilities = Capabilities::MEASUREMENTS | Capabilities::MEASUREMENT_CORRECTIONS |
+ Capabilities::LOW_POWER_MODE | Capabilities::SATELLITE_BLACKLIST;
auto ret = sGnssCallback_2_0->gnssSetCapabilitiesCb_2_0(capabilities);
if (!ret.isOk()) {
ALOGE("%s: Unable to invoke callback", __func__);
diff --git a/gnss/2.0/default/Gnss.h b/gnss/2.0/default/Gnss.h
index f02ab0a..72f7797 100644
--- a/gnss/2.0/default/Gnss.h
+++ b/gnss/2.0/default/Gnss.h
@@ -83,6 +83,7 @@
// Methods from V2_0::IGnss follow.
Return<sp<V2_0::IGnssConfiguration>> getExtensionGnssConfiguration_2_0() override;
+ Return<sp<V2_0::IGnssDebug>> getExtensionGnssDebug_2_0() override;
Return<sp<V2_0::IAGnss>> getExtensionAGnss_2_0() override;
Return<sp<V2_0::IAGnssRil>> getExtensionAGnssRil_2_0() override;
Return<sp<V2_0::IGnssMeasurement>> getExtensionGnssMeasurement_2_0() override;
@@ -91,6 +92,7 @@
getExtensionMeasurementCorrections() override;
Return<sp<visibility_control::V1_0::IGnssVisibilityControl>> getExtensionVisibilityControl()
override;
+ Return<sp<V2_0::IGnssBatching>> getExtensionGnssBatching_2_0() override;
Return<bool> injectBestLocation_2_0(const V2_0::GnssLocation& location) override;
private:
diff --git a/gnss/2.0/default/GnssBatching.cpp b/gnss/2.0/default/GnssBatching.cpp
new file mode 100644
index 0000000..d56cdfb
--- /dev/null
+++ b/gnss/2.0/default/GnssBatching.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GnssBatching"
+
+#include "GnssBatching.h"
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace V2_0 {
+namespace implementation {
+
+sp<V2_0::IGnssBatchingCallback> GnssBatching::sCallback = nullptr;
+
+// Methods from ::android::hardware::gnss::V1_0::IGnssBatching follow.
+Return<bool> GnssBatching::init(const sp<V1_0::IGnssBatchingCallback>&) {
+ // TODO implement
+ return bool{};
+}
+
+Return<uint16_t> GnssBatching::getBatchSize() {
+ // TODO implement
+ return uint16_t{};
+}
+
+Return<bool> GnssBatching::start(const V1_0::IGnssBatching::Options&) {
+ // TODO implement
+ return bool{};
+}
+
+Return<void> GnssBatching::flush() {
+ // TODO implement
+ return Void();
+}
+
+Return<bool> GnssBatching::stop() {
+ // TODO implement
+ return bool{};
+}
+
+Return<void> GnssBatching::cleanup() {
+ // TODO implement
+ return Void();
+}
+
+// Methods from V2_0::IGnssBatching follow.
+Return<bool> GnssBatching::init_2_0(const sp<V2_0::IGnssBatchingCallback>& callback) {
+ sCallback = callback;
+ return true;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace gnss
+} // namespace hardware
+} // namespace android
diff --git a/gnss/2.0/default/GnssBatching.h b/gnss/2.0/default/GnssBatching.h
new file mode 100644
index 0000000..62ac580
--- /dev/null
+++ b/gnss/2.0/default/GnssBatching.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/gnss/2.0/IGnssBatching.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace gnss {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+struct GnssBatching : public IGnssBatching {
+ // Methods from ::android::hardware::gnss::V1_0::IGnssBatching follow.
+ Return<bool> init(const sp<V1_0::IGnssBatchingCallback>& callback) override;
+ Return<uint16_t> getBatchSize() override;
+ Return<bool> start(const V1_0::IGnssBatching::Options& options) override;
+ Return<void> flush() override;
+ Return<bool> stop() override;
+ Return<void> cleanup() override;
+
+ // Methods from V2_0::IGnssBatching follow.
+ Return<bool> init_2_0(const sp<V2_0::IGnssBatchingCallback>& callback) override;
+
+ private:
+ static sp<IGnssBatchingCallback> sCallback;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace gnss
+} // namespace hardware
+} // namespace android
diff --git a/gnss/2.0/default/GnssConfiguration.cpp b/gnss/2.0/default/GnssConfiguration.cpp
index 4389dd2..6bf1712 100644
--- a/gnss/2.0/default/GnssConfiguration.cpp
+++ b/gnss/2.0/default/GnssConfiguration.cpp
@@ -33,13 +33,11 @@
}
Return<bool> GnssConfiguration::setSuplVersion(uint32_t) {
- // TODO implement
- return bool{};
+ return true;
}
Return<bool> GnssConfiguration::setSuplMode(hidl_bitfield<SuplMode>) {
- // TODO implement
- return bool{};
+ return true;
}
Return<bool> GnssConfiguration::setGpsLock(hidl_bitfield<GpsLock> gpsLock) {
@@ -49,18 +47,15 @@
}
Return<bool> GnssConfiguration::setLppProfile(hidl_bitfield<LppProfile>) {
- // TODO implement
- return bool{};
+ return true;
}
Return<bool> GnssConfiguration::setGlonassPositioningProtocol(hidl_bitfield<GlonassPosProtocol>) {
- // TODO implement
- return bool{};
+ return true;
}
Return<bool> GnssConfiguration::setEmergencySuplPdn(bool) {
- // TODO implement
- return bool{};
+ return true;
}
// Methods from ::android::hardware::gnss::V1_1::IGnssConfiguration follow.
diff --git a/gnss/2.0/default/GnssMeasurement.cpp b/gnss/2.0/default/GnssMeasurement.cpp
index a62c2dd..93de89c 100644
--- a/gnss/2.0/default/GnssMeasurement.cpp
+++ b/gnss/2.0/default/GnssMeasurement.cpp
@@ -26,7 +26,7 @@
namespace V2_0 {
namespace implementation {
-using GnssConstellationType = V1_0::GnssConstellationType;
+using GnssConstellationType = V2_0::GnssConstellationType;
using GnssMeasurementFlags = V1_0::IGnssMeasurementCallback::GnssMeasurementFlags;
using GnssMeasurementState = V2_0::IGnssMeasurementCallback::GnssMeasurementState;
@@ -46,6 +46,7 @@
}
Return<void> GnssMeasurement::close() {
+ ALOGD("close");
std::unique_lock<std::mutex> lock(mMutex);
stop();
sCallback = nullptr;
@@ -62,6 +63,7 @@
// Methods from V2_0::IGnssMeasurement follow.
Return<V1_0::IGnssMeasurement::GnssMeasurementStatus> GnssMeasurement::setCallback_2_0(
const sp<V2_0::IGnssMeasurementCallback>& callback, bool) {
+ ALOGD("setCallback_2_0");
std::unique_lock<std::mutex> lock(mMutex);
sCallback = callback;
@@ -75,6 +77,7 @@
}
void GnssMeasurement::start() {
+ ALOGD("start");
mIsActive = true;
mThread = std::thread([this]() {
while (mIsActive == true) {
@@ -87,6 +90,7 @@
}
void GnssMeasurement::stop() {
+ ALOGD("stop");
mIsActive = false;
if (mThread.joinable()) {
mThread.join();
@@ -95,26 +99,27 @@
GnssData GnssMeasurement::getMockMeasurement() {
V1_0::IGnssMeasurementCallback::GnssMeasurement measurement_1_0 = {
- .flags = (uint32_t)GnssMeasurementFlags::HAS_CARRIER_FREQUENCY,
- .svid = (int16_t)6,
- .constellation = GnssConstellationType::GLONASS,
- .timeOffsetNs = 0.0,
- .receivedSvTimeInNs = 8195997131077,
- .receivedSvTimeUncertaintyInNs = 15,
- .cN0DbHz = 30.0,
- .pseudorangeRateMps = -484.13739013671875,
- .pseudorangeRateUncertaintyMps = 1.0379999876022339,
- .accumulatedDeltaRangeState = (uint32_t)
- V1_0::IGnssMeasurementCallback::GnssAccumulatedDeltaRangeState::ADR_STATE_UNKNOWN,
- .accumulatedDeltaRangeM = 0.0,
- .accumulatedDeltaRangeUncertaintyM = 0.0,
- .carrierFrequencyHz = 1.59975e+09,
- .multipathIndicator =
- V1_0::IGnssMeasurementCallback::GnssMultipathIndicator::INDICATOR_UNKNOWN};
+ .flags = (uint32_t)GnssMeasurementFlags::HAS_CARRIER_FREQUENCY,
+ .svid = (int16_t)6,
+ .constellation = V1_0::GnssConstellationType::UNKNOWN,
+ .timeOffsetNs = 0.0,
+ .receivedSvTimeInNs = 8195997131077,
+ .receivedSvTimeUncertaintyInNs = 15,
+ .cN0DbHz = 30.0,
+ .pseudorangeRateMps = -484.13739013671875,
+ .pseudorangeRateUncertaintyMps = 1.0379999876022339,
+ .accumulatedDeltaRangeState = (uint32_t)V1_0::IGnssMeasurementCallback::
+ GnssAccumulatedDeltaRangeState::ADR_STATE_UNKNOWN,
+ .accumulatedDeltaRangeM = 0.0,
+ .accumulatedDeltaRangeUncertaintyM = 0.0,
+ .carrierFrequencyHz = 1.59975e+09,
+ .multipathIndicator =
+ V1_0::IGnssMeasurementCallback::GnssMultipathIndicator::INDICATOR_UNKNOWN};
V1_1::IGnssMeasurementCallback::GnssMeasurement measurement_1_1 = {.v1_0 = measurement_1_0};
V2_0::IGnssMeasurementCallback::GnssMeasurement measurement_2_0 = {
.v1_1 = measurement_1_1,
.codeType = "C",
+ .constellation = GnssConstellationType::GLONASS,
.state = GnssMeasurementState::STATE_CODE_LOCK | GnssMeasurementState::STATE_BIT_SYNC |
GnssMeasurementState::STATE_SUBFRAME_SYNC |
GnssMeasurementState::STATE_TOW_DECODED |
diff --git a/gnss/2.0/types.hal b/gnss/2.0/types.hal
index 21b64f9..3865727 100644
--- a/gnss/2.0/types.hal
+++ b/gnss/2.0/types.hal
@@ -72,4 +72,28 @@
* needs to be estimated by syncing the notion of time via PTP or some other mechanism.
*/
ElapsedRealtime elapsedRealtime;
-};
\ No newline at end of file
+};
+
+/**
+ * GNSS constellation type
+ *
+ * This is to specify the navigation satellite system, for example, as listed in Section 3.5 in
+ * RINEX Version 3.04.
+ */
+enum GnssConstellationType : uint8_t {
+ UNKNOWN = 0,
+ /** Global Positioning System. */
+ GPS = 1,
+ /** Satellite-Based Augmentation System. */
+ SBAS = 2,
+ /** Global Navigation Satellite System. */
+ GLONASS = 3,
+ /** Quasi-Zenith Satellite System. */
+ QZSS = 4,
+ /** BeiDou Navigation Satellite System. */
+ BEIDOU = 5,
+ /** Galileo Navigation Satellite System. */
+ GALILEO = 6,
+ /** Indian Regional Navigation Satellite System. */
+ IRNSS = 7,
+};
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.cpp b/gnss/2.0/vts/functional/gnss_hal_test.cpp
index b2b62fc..da6092b 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test.cpp
@@ -26,6 +26,7 @@
GnssHalTest::GnssHalTest()
: info_called_count_(0),
capabilities_called_count_(0),
+ measurement_corrections_capabilities_called_count_(0),
location_called_count_(0),
name_called_count_(0),
notify_count_(0) {}
@@ -33,7 +34,7 @@
void GnssHalTest::SetUp() {
gnss_hal_ = ::testing::VtsHalHidlTargetTestBase::getService<IGnss>(
GnssHidlEnvironment::Instance()->getServiceName<IGnss>());
- list_gnss_sv_status_.clear();
+ list_vec_gnss_sv_info_.clear();
ASSERT_NE(gnss_hal_, nullptr);
SetUpGnssCallback();
@@ -43,6 +44,7 @@
// Reset counters
info_called_count_ = 0;
capabilities_called_count_ = 0;
+ measurement_corrections_capabilities_called_count_ = 0;
location_called_count_ = 0;
name_called_count_ = 0;
measurement_called_count_ = 0;
@@ -59,7 +61,7 @@
gnss_cb_ = new GnssCallback(*this);
ASSERT_NE(gnss_cb_, nullptr);
- auto result = gnss_hal_->setCallback_1_1(gnss_cb_);
+ auto result = gnss_hal_->setCallback_2_0(gnss_cb_);
if (!result.isOk()) {
ALOGE("result of failed setCallback %s", result.description().c_str());
}
@@ -77,16 +79,6 @@
EXPECT_EQ(capabilities_called_count_, 1);
EXPECT_EQ(info_called_count_, 1);
EXPECT_EQ(name_called_count_, 1);
-
- // Setup measurement corrections callback.
- auto measurementCorrections = gnss_hal_->getExtensionMeasurementCorrections();
- ASSERT_TRUE(measurementCorrections.isOk());
- sp<IMeasurementCorrections> iMeasurementCorrections = measurementCorrections;
- if (iMeasurementCorrections != nullptr) {
- sp<IMeasurementCorrectionsCallback> iMeasurementCorrectionsCallback =
- new GnssMeasurementCorrectionsCallback(*this);
- iMeasurementCorrections->setCallback(iMeasurementCorrectionsCallback);
- }
}
void GnssHalTest::StopAndClearLocations() {
@@ -193,11 +185,12 @@
status = cv_.wait_for(lock, std::chrono::seconds(timeout_seconds));
if (status == std::cv_status::timeout) return status;
}
+ notify_count_--;
return status;
}
Return<void> GnssHalTest::GnssCallback::gnssSetSystemInfoCb(
- const IGnssCallback::GnssSystemInfo& info) {
+ const IGnssCallback_1_0::GnssSystemInfo& info) {
ALOGI("Info received, year %d", info.yearOfHw);
parent_.info_called_count_++;
parent_.last_info_ = info;
@@ -248,10 +241,9 @@
return Void();
}
-Return<void> GnssHalTest::GnssCallback::gnssSvStatusCb(
- const IGnssCallback::GnssSvStatus& svStatus) {
- ALOGI("GnssSvStatus received");
- parent_.list_gnss_sv_status_.emplace_back(svStatus);
+Return<void> GnssHalTest::GnssCallback::gnssSvStatusCb(const IGnssCallback_1_0::GnssSvStatus&) {
+ ALOGI("gnssSvStatusCb");
+
return Void();
}
@@ -272,3 +264,11 @@
parent_.notify();
return Void();
}
+
+Return<void> GnssHalTest::GnssCallback::gnssSvStatusCb_2_0(
+ const hidl_vec<IGnssCallback_2_0::GnssSvInfo>& svInfoList) {
+ ALOGI("gnssSvStatusCb_2_0. Size = %d", (int)svInfoList.size());
+ parent_.list_vec_gnss_sv_info_.emplace_back(svInfoList);
+ parent_.notify();
+ return Void();
+}
diff --git a/gnss/2.0/vts/functional/gnss_hal_test.h b/gnss/2.0/vts/functional/gnss_hal_test.h
index 7354aea..737815f 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test.h
+++ b/gnss/2.0/vts/functional/gnss_hal_test.h
@@ -25,17 +25,20 @@
#include <list>
#include <mutex>
+using android::hardware::hidl_vec;
using android::hardware::Return;
using android::hardware::Void;
using android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrectionsCallback;
using android::hardware::gnss::V1_0::GnssLocationFlags;
using android::hardware::gnss::V2_0::IGnss;
-using android::hardware::gnss::V2_0::IGnssCallback;
using GnssLocation_1_0 = android::hardware::gnss::V1_0::GnssLocation;
using GnssLocation_2_0 = android::hardware::gnss::V2_0::GnssLocation;
+using IGnssCallback_1_0 = android::hardware::gnss::V1_0::IGnssCallback;
+using IGnssCallback_2_0 = android::hardware::gnss::V2_0::IGnssCallback;
+
using IGnssMeasurementCallback_1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback;
using IGnssMeasurementCallback_1_1 = android::hardware::gnss::V1_1::IGnssMeasurementCallback;
using IGnssMeasurementCallback_2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback;
@@ -77,8 +80,8 @@
std::cv_status waitForMeasurementCorrectionsCapabilities(int timeout_seconds);
/* Callback class for data & Event. */
- class GnssCallback : public IGnssCallback {
- public:
+ class GnssCallback : public IGnssCallback_2_0 {
+ public:
GnssHalTest& parent_;
GnssCallback(GnssHalTest& parent) : parent_(parent){};
@@ -86,7 +89,7 @@
virtual ~GnssCallback() = default;
// Dummy callback handlers
- Return<void> gnssStatusCb(const IGnssCallback::GnssStatusValue /* status */) override {
+ Return<void> gnssStatusCb(const IGnssCallback_1_0::GnssStatusValue /* status */) override {
return Void();
}
Return<void> gnssNmeaCb(int64_t /* timestamp */,
@@ -103,8 +106,8 @@
Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
Return<void> gnssLocationCb(const GnssLocation_1_0& location) override;
Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override;
- Return<void> gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override;
- Return<void> gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override;
+ Return<void> gnssSetSystemInfoCb(const IGnssCallback_1_0::GnssSystemInfo& info) override;
+ Return<void> gnssSvStatusCb(const IGnssCallback_1_0::GnssSvStatus& svStatus) override;
// New in v2.0
Return<void> gnssLocationCb_2_0(const GnssLocation_2_0& location) override;
@@ -113,6 +116,8 @@
return Void();
}
Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
+ Return<void> gnssSvStatusCb_2_0(
+ const hidl_vec<IGnssCallback_2_0::GnssSvInfo>& svInfoList) override;
private:
Return<void> gnssLocationCbImpl(const GnssLocation_2_0& location);
@@ -198,7 +203,7 @@
void SetPositionMode(const int min_interval_msec, const bool low_power_mode);
sp<IGnss> gnss_hal_; // GNSS HAL to call into
- sp<IGnssCallback> gnss_cb_; // Primary callback interface
+ sp<IGnssCallback_2_0> gnss_cb_; // Primary callback interface
// TODO: make these variables thread-safe.
/* Count of calls to set the following items, and the latest item (used by
@@ -211,16 +216,16 @@
int measurement_called_count_;
int name_called_count_;
- IGnssCallback::GnssSystemInfo last_info_;
+ IGnssCallback_1_0::GnssSystemInfo last_info_;
uint32_t last_capabilities_;
uint32_t last_measurement_corrections_capabilities_;
GnssLocation_2_0 last_location_;
IGnssMeasurementCallback_2_0::GnssData last_measurement_;
android::hardware::hidl_string last_name_;
- list<IGnssCallback::GnssSvStatus> list_gnss_sv_status_;
+ list<hidl_vec<IGnssCallback_2_0::GnssSvInfo>> list_vec_gnss_sv_info_;
- private:
+ private:
std::mutex mtx_;
std::condition_variable cv_;
int notify_count_;
diff --git a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
index f3559c5..be182a9 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
@@ -32,12 +32,15 @@
using IAGnss_2_0 = android::hardware::gnss::V2_0::IAGnss;
using IAGnss_1_0 = android::hardware::gnss::V1_0::IAGnss;
using IAGnssCallback_2_0 = android::hardware::gnss::V2_0::IAGnssCallback;
+using IGnssBatching_V1_0 = android::hardware::gnss::V1_0::IGnssBatching;
+using IGnssBatching_V2_0 = android::hardware::gnss::V2_0::IGnssBatching;
using android::hardware::gnss::common::Utils;
using android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections;
using android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
using android::hardware::gnss::V1_0::IGnssNi;
using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
+using android::hardware::gnss::V2_0::GnssConstellationType;
using android::hardware::gnss::V2_0::IGnssCallback;
using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
@@ -163,10 +166,13 @@
}
/*
- * TestGnssMeasurementCodeType:
- * Sets a GnssMeasurementCallback, waits for a measurement, and verifies the codeType is valid.
+ * TestGnssMeasurementFields:
+ * Sets a GnssMeasurementCallback, waits for a measurement, and verifies
+ * 1. codeType is valid,
+ * 2. constellation is valid.
+ * 3. state is valid.
*/
-TEST_F(GnssHalTest, TestGnssMeasurementCodeType) {
+TEST_F(GnssHalTest, TestGnssMeasurementFields) {
const int kFirstGnssMeasurementTimeoutSeconds = 10;
auto gnssMeasurement = gnss_hal_->getExtensionGnssMeasurement_2_0();
@@ -189,7 +195,23 @@
EXPECT_EQ(measurement_called_count_, 1);
ASSERT_TRUE(last_measurement_.measurements.size() > 0);
for (auto measurement : last_measurement_.measurements) {
+ // Verify CodeType is valid.
ASSERT_NE(measurement.codeType, "");
+
+ // Verify ConstellationType is valid.
+ ASSERT_TRUE(static_cast<uint8_t>(measurement.constellation) >=
+ static_cast<uint8_t>(GnssConstellationType::UNKNOWN) &&
+ static_cast<uint8_t>(measurement.constellation) <=
+ static_cast<uint8_t>(GnssConstellationType::IRNSS));
+
+ // Verify State is valid.
+ ASSERT_TRUE(
+ static_cast<uint32_t>(measurement.state) >=
+ static_cast<uint32_t>(IGnssMeasurementCallback_2_0::GnssMeasurementState::
+ STATE_UNKNOWN) &&
+ static_cast<uint32_t>(measurement.state) <=
+ static_cast<uint32_t>(IGnssMeasurementCallback_2_0::GnssMeasurementState::
+ STATE_2ND_CODE_LOCK));
}
iGnssMeasurement->close();
@@ -267,17 +289,24 @@
/*
* TestGnssMeasurementCorrectionsCapabilities:
- * If the GnssMeasurementCorrectionsExtension is not null, verifies that the measurement corrections
+ * If measurement corrections capability is supported, verifies that the measurement corrections
* capabilities are reported and the mandatory LOS_SATS or the EXCESS_PATH_LENGTH
* capability flag is set.
*/
TEST_F(GnssHalTest, TestGnssMeasurementCorrectionsCapabilities) {
+ if (!(last_capabilities_ & IGnssCallback::Capabilities::MEASUREMENT_CORRECTIONS)) {
+ return;
+ }
+
auto measurementCorrections = gnss_hal_->getExtensionMeasurementCorrections();
ASSERT_TRUE(measurementCorrections.isOk());
sp<IMeasurementCorrections> iMeasurementCorrections = measurementCorrections;
- if (iMeasurementCorrections == nullptr) {
- return;
- }
+ ASSERT_NE(iMeasurementCorrections, nullptr);
+
+ // Setup measurement corrections callback.
+ sp<IMeasurementCorrectionsCallback> iMeasurementCorrectionsCallback =
+ new GnssMeasurementCorrectionsCallback(*this);
+ iMeasurementCorrections->setCallback(iMeasurementCorrectionsCallback);
const int kMeasurementCorrectionsCapabilitiesTimeoutSeconds = 5;
waitForMeasurementCorrectionsCapabilities(kMeasurementCorrectionsCapabilitiesTimeoutSeconds);
@@ -289,17 +318,23 @@
/*
* TestGnssMeasurementCorrections:
- * If the GnssMeasurementCorrectionsExtension is not null, verifies that it supports the
+ * If measurement corrections capability is supported, verifies that it supports the
* gnss.measurement_corrections@1.0::IMeasurementCorrections interface by invoking a method.
*/
TEST_F(GnssHalTest, TestGnssMeasurementCorrections) {
+ if (!(last_capabilities_ & IGnssCallback::Capabilities::MEASUREMENT_CORRECTIONS)) {
+ return;
+ }
+
// Verify IMeasurementCorrections is supported.
auto measurementCorrections = gnss_hal_->getExtensionMeasurementCorrections();
ASSERT_TRUE(measurementCorrections.isOk());
sp<IMeasurementCorrections> iMeasurementCorrections = measurementCorrections;
- if (iMeasurementCorrections == nullptr) {
- return;
- }
+ ASSERT_NE(iMeasurementCorrections, nullptr);
+
+ sp<IMeasurementCorrectionsCallback> iMeasurementCorrectionsCallback =
+ new GnssMeasurementCorrectionsCallback(*this);
+ iMeasurementCorrections->setCallback(iMeasurementCorrectionsCallback);
const int kMeasurementCorrectionsCapabilitiesTimeoutSeconds = 5;
waitForMeasurementCorrectionsCapabilities(kMeasurementCorrectionsCapabilitiesTimeoutSeconds);
@@ -337,9 +372,9 @@
wait(kFirstGnssMeasurementTimeoutSeconds);
EXPECT_EQ(measurement_called_count_, 1);
- ASSERT_TRUE((int)last_measurement_.elapsedRealtime.flags >= 0 &&
- (int)last_measurement_.elapsedRealtime.flags <=
- (int)ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS);
+ ASSERT_TRUE((int)last_measurement_.elapsedRealtime.flags <=
+ (int)(ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
+ ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS));
// We expect a non-zero timestamp when set.
if (last_measurement_.elapsedRealtime.flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
@@ -352,9 +387,9 @@
TEST_F(GnssHalTest, TestGnssLocationElapsedRealtime) {
StartAndCheckFirstLocation();
- ASSERT_TRUE((int)last_location_.elapsedRealtime.flags >= 0 &&
- (int)last_location_.elapsedRealtime.flags <=
- (int)ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS);
+ ASSERT_TRUE((int)last_location_.elapsedRealtime.flags <=
+ (int)(ElapsedRealtimeFlags::HAS_TIMESTAMP_NS |
+ ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS));
// We expect a non-zero timestamp when set.
if (last_location_.elapsedRealtime.flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
@@ -370,3 +405,20 @@
gnss_hal_->injectBestLocation_2_0(last_location_);
StopAndClearLocations();
}
+
+/*
+ * TestGnssBatchingExtension:
+ * Gets the GnssBatchingExtension and verifies that it supports either the @1.0::IGnssBatching
+ * or @2.0::IGnssBatching extension.
+ */
+TEST_F(GnssHalTest, TestGnssBatchingExtension) {
+ auto gnssBatching_V2_0 = gnss_hal_->getExtensionGnssBatching_2_0();
+ ASSERT_TRUE(gnssBatching_V2_0.isOk());
+
+ auto gnssBatching_V1_0 = gnss_hal_->getExtensionGnssBatching();
+ ASSERT_TRUE(gnssBatching_V1_0.isOk());
+
+ sp<IGnssBatching_V1_0> iGnssBatching_V1_0 = gnssBatching_V1_0;
+ sp<IGnssBatching_V2_0> iGnssBatching_V2_0 = gnssBatching_V2_0;
+ ASSERT_TRUE(iGnssBatching_V1_0 != nullptr || iGnssBatching_V2_0 != nullptr);
+}
diff --git a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerResources.h b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerResources.h
index 3a73f84..18d184e 100644
--- a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerResources.h
+++ b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerResources.h
@@ -44,7 +44,7 @@
if (mMapper3) {
return true;
}
- ALOGW_IF(!mMapper3, "failed to get mapper 3.0 service");
+ ALOGD_IF(!mMapper3, "failed to get mapper 3.0 service, falling back to mapper 2.0");
mMapper2 = mapper::V2_0::IMapper::getService();
ALOGE_IF(!mMapper2, "failed to get mapper 2.0 service");
@@ -170,7 +170,7 @@
}
Error lookupCache(uint32_t slot, const native_handle_t** outHandle) {
- if (slot < mHandles.size()) {
+ if (slot >= 0 && slot < mHandles.size()) {
*outHandle = mHandles[slot];
return Error::NONE;
} else {
@@ -180,7 +180,7 @@
Error updateCache(uint32_t slot, const native_handle_t* handle,
const native_handle** outReplacedHandle) {
- if (slot < mHandles.size()) {
+ if (slot >= 0 && slot < mHandles.size()) {
auto& cachedHandle = mHandles[slot];
*outReplacedHandle = cachedHandle;
cachedHandle = handle;
diff --git a/graphics/composer/2.1/utils/passthrough/include/composer-passthrough/2.1/HwcHal.h b/graphics/composer/2.1/utils/passthrough/include/composer-passthrough/2.1/HwcHal.h
index 436e461..5826b12 100644
--- a/graphics/composer/2.1/utils/passthrough/include/composer-passthrough/2.1/HwcHal.h
+++ b/graphics/composer/2.1/utils/passthrough/include/composer-passthrough/2.1/HwcHal.h
@@ -162,6 +162,7 @@
Error destroyLayer(Display display, Layer layer) override {
int32_t err = mDispatch.destroyLayer(mDevice, display, layer);
+ onLayerDestroyed(display, layer);
return static_cast<Error>(err);
}
@@ -327,6 +328,7 @@
std::vector<IComposerClient::Composition>* outCompositionTypes,
uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
std::vector<uint32_t>* outRequestMasks) override {
+ onBeforeValidateDisplay(display);
uint32_t typesCount = 0;
uint32_t reqsCount = 0;
int32_t err = mDispatch.validateDisplay(mDevice, display, &typesCount, &reqsCount);
@@ -335,17 +337,15 @@
return static_cast<Error>(err);
}
- err = mDispatch.getChangedCompositionTypes(mDevice, display, &typesCount, nullptr, nullptr);
+ err = getChangedCompositionTypes(display, &typesCount, nullptr, nullptr);
if (err != HWC2_ERROR_NONE) {
return static_cast<Error>(err);
}
std::vector<Layer> changedLayers(typesCount);
std::vector<IComposerClient::Composition> compositionTypes(typesCount);
- err = mDispatch.getChangedCompositionTypes(
- mDevice, display, &typesCount, changedLayers.data(),
- reinterpret_cast<std::underlying_type<IComposerClient::Composition>::type*>(
- compositionTypes.data()));
+ err = getChangedCompositionTypes(display, &typesCount, changedLayers.data(),
+ compositionTypes.data());
if (err != HWC2_ERROR_NONE) {
return static_cast<Error>(err);
}
@@ -578,6 +578,15 @@
return true;
}
+ virtual int32_t getChangedCompositionTypes(Display display, uint32_t* outTypesCount,
+ Layer* outChangedLayers,
+ IComposerClient::Composition* outCompositionTypes) {
+ return getChangedCompositionTypesInternal(display, outTypesCount, outChangedLayers,
+ outCompositionTypes);
+ }
+ virtual void onLayerDestroyed(Display /* display */, Layer /* layer */) {}
+ virtual void onBeforeValidateDisplay(Display /* display */) {}
+
static void hotplugHook(hwc2_callback_data_t callbackData, hwc2_display_t display,
int32_t connected) {
auto hal = static_cast<HwcHalImpl*>(callbackData);
@@ -596,6 +605,15 @@
hal->mEventCallback->onVsync(display, timestamp);
}
+ int32_t getChangedCompositionTypesInternal(Display display, uint32_t* outTypesCount,
+ Layer* outChangedLayers,
+ IComposerClient::Composition* outCompositionTypes) {
+ return mDispatch.getChangedCompositionTypes(
+ mDevice, display, outTypesCount, outChangedLayers,
+ reinterpret_cast<std::underlying_type<IComposerClient::Composition>::type*>(
+ outCompositionTypes));
+ }
+
hwc2_device_t* mDevice = nullptr;
std::unordered_set<hwc2_capability_t> mCapabilities;
diff --git a/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcHal.h b/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcHal.h
index 070cf80..e2bed95 100644
--- a/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcHal.h
+++ b/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcHal.h
@@ -42,6 +42,20 @@
using V2_1::Display;
using V2_1::Error;
+namespace {
+
+bool isIdentityMatrix(const float* matrix) {
+ if (matrix[0] == 1.0 && matrix[1] == 0.0 && matrix[2] == 0.0 && matrix[3] == 0.0 &&
+ matrix[4] == 0.0 && matrix[5] == 1.0 && matrix[6] == 0.0 && matrix[7] == 0.0 &&
+ matrix[8] == 0.0 && matrix[9] == 0.0 && matrix[10] == 1.0 && matrix[11] == 0.0 &&
+ matrix[12] == 0.0 && matrix[13] == 0.0 && matrix[14] == 0.0 && matrix[15] == 1.0) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
// HwcHalImpl implements V2_*::hal::ComposerHal on top of hwcomposer2
template <typename Hal>
class HwcHalImpl : public V2_2::passthrough::detail::HwcHalImpl<Hal> {
@@ -130,6 +144,16 @@
Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override {
if (!mDispatch.setLayerColorTransform) {
+ if (isIdentityMatrix(matrix)) {
+ // If an identity matrix is set, then we can remove the layer from client
+ // composition list.
+ mClientCompositionLayers[display].erase(layer);
+ return Error::UNSUPPORTED;
+ }
+ // if setLayerColorTransform is not implemented, per spec we want to make sure the
+ // layer marked as client composition, and thus we maintain a list, and mark all these
+ // layers as client composition later before validate the display.
+ mClientCompositionLayers[display].insert(layer);
return Error::UNSUPPORTED;
}
int32_t err = mDispatch.setLayerColorTransform(mDevice, display, layer, matrix);
@@ -294,7 +318,79 @@
return true;
}
- private:
+ int32_t getChangedCompositionTypes(Display display, uint32_t* outTypesCount,
+ Layer* outChangedLayers,
+ IComposerClient::Composition* outCompositionTypes) override {
+ if (outChangedLayers == nullptr && outCompositionTypes == nullptr) {
+ uint32_t typesCount = 0;
+ int32_t error = BaseType2_1::getChangedCompositionTypesInternal(display, &typesCount,
+ nullptr, nullptr);
+ if (error != HWC2_ERROR_NONE) {
+ return error;
+ }
+ mChangedLayersCache[display].resize(typesCount);
+ mCompositionTypesCache[display].resize(typesCount);
+ error = BaseType2_1::getChangedCompositionTypesInternal(
+ display, &typesCount, mChangedLayersCache[display].data(),
+ mCompositionTypesCache[display].data());
+ if (error != HWC2_ERROR_NONE) {
+ return error;
+ }
+ for (Layer layer : mClientCompositionLayers[display]) {
+ bool exist = false;
+ for (uint32_t i = 0; i < typesCount; ++i) {
+ if (mChangedLayersCache[display][i] == layer) {
+ exist = true;
+ break;
+ }
+ }
+ if (!exist) {
+ mChangedLayersCache[display].push_back(layer);
+ mCompositionTypesCache[display].push_back(IComposerClient::Composition::CLIENT);
+ }
+ }
+ *outTypesCount = mChangedLayersCache[display].size();
+ return error;
+ }
+ for (uint32_t i = 0; i < *outTypesCount; ++i) {
+ if (outChangedLayers != nullptr) {
+ outChangedLayers[i] = mChangedLayersCache[display][i];
+ }
+ if (outCompositionTypes != nullptr) {
+ outCompositionTypes[i] = mCompositionTypesCache[display][i];
+ }
+ }
+ return HWC2_ERROR_NONE;
+ }
+
+ void onLayerDestroyed(Display display, Layer layer) override {
+ if (mClientCompositionLayers.find(display) == mClientCompositionLayers.end()) {
+ return;
+ }
+ mClientCompositionLayers[display].erase(layer);
+ }
+
+ void onBeforeValidateDisplay(Display display) override {
+ if (mClientCompositionLayers.find(display) == mClientCompositionLayers.end()) {
+ return;
+ }
+
+ // clear the cache proactively so that we don't hold too much memory over time.
+ mChangedLayersCache[display].clear();
+ mCompositionTypesCache[display].clear();
+
+ // SET_LAYER_COLOR_TRANSFORM is optional, and thus if it's not implemented, we need to
+ // follow the spec to make sure those layers marked as client composition before validate
+ // the display.
+ if (!mDispatch.setLayerColorTransform) {
+ for (Layer layer : mClientCompositionLayers[display]) {
+ BaseType2_1::setLayerCompositionType(
+ display, layer, static_cast<int32_t>(IComposerClient::Composition::CLIENT));
+ }
+ }
+ }
+
+ private:
struct {
HWC2_PFN_GET_DISPLAY_IDENTIFICATION_DATA getDisplayIdentificationData;
HWC2_PFN_SET_LAYER_COLOR_TRANSFORM setLayerColorTransform;
@@ -318,6 +414,9 @@
using BaseType2_2::getRenderIntents;
using BaseType2_2::setColorMode_2_2;
using BaseType2_2::setLayerPerFrameMetadata;
+ std::map<Display, std::set<Layer>> mClientCompositionLayers;
+ std::map<Display, std::vector<Layer>> mChangedLayersCache;
+ std::map<Display, std::vector<IComposerClient::Composition>> mCompositionTypesCache;
};
} // namespace detail
diff --git a/graphics/mapper/2.0/IMapper.hal b/graphics/mapper/2.0/IMapper.hal
index 4566135..0e18f42 100644
--- a/graphics/mapper/2.0/IMapper.hal
+++ b/graphics/mapper/2.0/IMapper.hal
@@ -147,6 +147,9 @@
* outside of accessRegion is undefined, except that it must not cause
* process termination.
*
+ * An accessRegion of all-zeros means the entire buffer. That is, it is
+ * equivalent to '(0,0)-(buffer width, buffer height)'.
+ *
* data will be filled with a pointer to the locked buffer memory. This
* address will represent the top-left corner of the entire buffer, even
* if accessRegion does not begin at the top-left corner.
diff --git a/health/1.0/types.hal b/health/1.0/types.hal
index 377d1bd..90b8bb1 100644
--- a/health/1.0/types.hal
+++ b/health/1.0/types.hal
@@ -190,7 +190,13 @@
/** Remaining battery capacity in percent */
int32_t batteryLevel;
- /** Instantaneous battery voltage in uV */
+ /**
+ * Instantaneous battery voltage in millivolts (mV).
+ *
+ * Historically, the unit of this field is microvolts (uV), but all
+ * clients and implementations uses millivolts in practice, making it
+ * the de-facto standard.
+ */
int32_t batteryVoltage;
/** Instantaneous battery temperature in tenths of degree celcius */
diff --git a/keymaster/4.0/support/keymaster_utils.cpp b/keymaster/4.0/support/keymaster_utils.cpp
index 729e1c1..e35fdd3 100644
--- a/keymaster/4.0/support/keymaster_utils.cpp
+++ b/keymaster/4.0/support/keymaster_utils.cpp
@@ -21,7 +21,9 @@
namespace hardware {
inline static bool operator<(const hidl_vec<uint8_t>& a, const hidl_vec<uint8_t>& b) {
- return memcmp(a.data(), b.data(), std::min(a.size(), b.size())) == -1;
+ auto result = memcmp(a.data(), b.data(), std::min(a.size(), b.size()));
+ if (!result) return a.size() < b.size();
+ return result < 0;
}
template <size_t SIZE>
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index 2e76604..fc96724 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -172,6 +172,20 @@
"E78E70BEFE930DB34818EE4D5C26259F5C6B8E28A652950F9F88D7B4B2C9"
"D9");
+string ec_256_key_rfc5915 =
+ hex2str("308193020100301306072a8648ce3d020106082a8648ce3d030107047930"
+ "770201010420782370a8c8ce5537baadd04dcff079c8158cfa9c67b818b3"
+ "8e8d21c9fa750c1da00a06082a8648ce3d030107a14403420004e2cc561e"
+ "e701da0ad0ef0d176bb0c919d42e79c393fdc1bd6c4010d85cf2cf8e68c9"
+ "05464666f98dad4f01573ba81078b3428570a439ba3229fbc026c550682f");
+
+string ec_256_key_sec1 =
+ hex2str("308187020100301306072a8648ce3d020106082a8648ce3d030107046d30"
+ "6b0201010420782370a8c8ce5537baadd04dcff079c8158cfa9c67b818b3"
+ "8e8d21c9fa750c1da14403420004e2cc561ee701da0ad0ef0d176bb0c919"
+ "d42e79c393fdc1bd6c4010d85cf2cf8e68c905464666f98dad4f01573ba8"
+ "1078b3428570a439ba3229fbc026c550682f");
+
struct RSA_Delete {
void operator()(RSA* p) { RSA_free(p); }
};
@@ -2026,6 +2040,56 @@
}
/*
+ * ImportKeyTest.EcdsaP256RFC5915Success
+ *
+ * Verifies that importing and using an ECDSA P-256 key pair encoded using RFC5915 works correctly.
+ */
+TEST_F(ImportKeyTest, EcdsaP256RFC5915Success) {
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(256)
+ .Digest(Digest::SHA_2_256),
+ KeyFormat::PKCS8, ec_256_key_rfc5915));
+
+ CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+ CheckCryptoParam(TAG_KEY_SIZE, 256U);
+ CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+ CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_256);
+
+ CheckOrigin();
+
+ string message(32, 'a');
+ auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
+ string signature = SignMessage(message, params);
+ VerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.EcdsaP256SEC1Success
+ *
+ * Verifies that importing and using an ECDSA P-256 key pair encoded using SEC1 works correctly.
+ */
+TEST_F(ImportKeyTest, EcdsaP256SEC1Success) {
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(256)
+ .Digest(Digest::SHA_2_256),
+ KeyFormat::PKCS8, ec_256_key_sec1));
+
+ CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+ CheckCryptoParam(TAG_KEY_SIZE, 256U);
+ CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+ CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_256);
+
+ CheckOrigin();
+
+ string message(32, 'a');
+ auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
+ string signature = SignMessage(message, params);
+ VerifyMessage(message, signature, params);
+}
+
+/*
* ImportKeyTest.Ecdsa521Success
*
* Verifies that importing and using an ECDSA P-521 key pair works correctly.
diff --git a/media/c2/1.0/Android.bp b/media/c2/1.0/Android.bp
index 7307a81..895e9db 100644
--- a/media/c2/1.0/Android.bp
+++ b/media/c2/1.0/Android.bp
@@ -27,6 +27,7 @@
"android.hardware.media.omx@1.0",
"android.hardware.media@1.0",
"android.hidl.base@1.0",
+ "android.hidl.safe_union@1.0",
],
gen_java: false,
}
diff --git a/media/c2/1.0/types.hal b/media/c2/1.0/types.hal
index ec422b1..f4f6f2f 100644
--- a/media/c2/1.0/types.hal
+++ b/media/c2/1.0/types.hal
@@ -17,6 +17,7 @@
package android.hardware.media.c2@1.0;
import android.hardware.media.bufferpool@2.0::BufferStatusMessage;
+import android.hidl.safe_union@1.0::Monostate;
/**
* Common return values for Codec2 operations.
@@ -190,89 +191,68 @@
*/
typedef uint64_t PrimitiveValue;
+/**
+ * Description of a set of values.
+ *
+ * If the `step` member is 0, and `num` and `denom` are both 1, the `Range`
+ * structure represents a closed interval bounded by `min` and `max`.
+ *
+ * Otherwise, the #ValueRange structure represents a finite sequence of numbers
+ * produced from the following recurrence relation:
+ *
+ * @code
+ * v[0] = min
+ * v[i] = v[i - 1] * num / denom + step ; i >= 1
+ * @endcode
+ *
+ * Both the ratio `num / denom` and the value `step` must be positive. The
+ * last number in the sequence described by this #Range structure is the
+ * largest number in the sequence that is smaller than or equal to `max`.
+ *
+ * @note
+ * The division in the formula may truncate the result if the data type of
+ * these values is an integral type.
+ */
+struct ValueRange {
+ /**
+ * Lower end of the range (inclusive).
+ */
+ PrimitiveValue min;
+ /**
+ * Upper end of the range (inclusive).
+ */
+ PrimitiveValue max;
+ /**
+ * The non-homogeneous term in the recurrence relation.
+ */
+ PrimitiveValue step;
+ /**
+ * The numerator of the scale coefficient in the recurrence relation.
+ */
+ PrimitiveValue num;
+ /**
+ * The denominator of the scale coefficient in the recurrence relation.
+ */
+ PrimitiveValue denom;
+};
+
/*
* Description of supported values for a field.
*
* This can be a continuous range or a discrete set of values.
+ *
+ * The intended type of values must be made clear in the context where
+ * `FieldSupportedValues` is used.
*/
-struct FieldSupportedValues {
- /**
- * Used if #type is `RANGE`.
- *
- * If the `step` member is 0, and `num` and `denom` are both 1, the `Range`
- * structure represents a closed interval bounded by `min` and `max`.
- *
- * Otherwise, the #Range structure represents a finite sequence of numbers
- * produced from the following recurrence relation:
- *
- * @code
- * v[0] = min
- * v[i] = v[i - 1] * num / denom + step ; i >= 1
- * @endcode
- *
- * Both the ratio `num / denom` and the value `step` must be positive. The
- * last number in the sequence described by this #Range structure is the
- * largest number in the sequence that is smaller than or equal to `max`.
- *
- * @note
- * The division in the formula may truncate the result if the data type of
- * these values is an integral type.
- */
- struct Range {
- /**
- * Lower end of the range (inclusive).
- */
- PrimitiveValue min;
- /**
- * Upper end of the range (inclusive).
- */
- PrimitiveValue max;
- /**
- * The non-homogeneous term in the recurrence relation.
- */
- PrimitiveValue step;
- /**
- * The numerator of the scale coefficient in the recurrence relation.
- */
- PrimitiveValue num;
- /**
- * The denominator of the scale coefficient in the recurrence relation.
- */
- PrimitiveValue denom;
- };
-
- enum Type : int32_t {
- /** No supported values */
- EMPTY = 0,
- /** Numeric range, described in a #Range structure */
- RANGE,
- /** List of values */
- VALUES,
- /** List of flags that can be OR-ed */
- FLAGS,
- };
- /**
- * Type of the supported values.
- */
- Type type;
-
- /**
- * When #type is #Type.RANGE, #range shall specify the range of possible
- * values.
- *
- * The intended type of members of #range shall be clear in the context
- * where `FieldSupportedValues` is used.
- */
- Range range;
-
- /**
- * When #type is #Type.VALUES or #Type.FLAGS, #value shall list supported
- * values/flags.
- *
- * The intended type of components of #value shall be clear in the context
- * where `FieldSupportedValues` is used.
- */
+safe_union FieldSupportedValues {
+ /** No supported values */
+ Monostate empty;
+ /** Numeric range, described in a #ValueRange structure */
+ ValueRange range;
+ /** List of values */
vec<PrimitiveValue> values;
+ /** List of flags that can be OR-ed */
+ vec<PrimitiveValue> flags;
};
/**
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
index f5cb0d7..c819b52 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
@@ -52,6 +52,7 @@
using ::test_helper::MixedTyped;
using ::test_helper::MixedTypedExample;
using ::test_helper::resize_accordingly;
+using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
template <typename T>
void copy_back_(std::map<int, std::vector<T>>* dst, const std::vector<RequestArgument>& ra,
@@ -124,9 +125,9 @@
ADD_FAILURE() << "asking for burst execution at V1_0";
return nullptr;
}
-static std::unique_ptr<::android::nn::ExecutionBurstController> CreateBurst(
+static std::shared_ptr<::android::nn::ExecutionBurstController> CreateBurst(
const sp<V1_2::IPreparedModel>& preparedModel) {
- return ::android::nn::createExecutionBurstController(preparedModel, /*blocking=*/true);
+ return ::android::nn::ExecutionBurstController::create(preparedModel, /*blocking=*/true);
}
enum class Executor { ASYNC, SYNC, BURST };
enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT };
@@ -285,7 +286,7 @@
SCOPED_TRACE("burst");
// create burst
- const std::unique_ptr<::android::nn::ExecutionBurstController> controller =
+ const std::shared_ptr<::android::nn::ExecutionBurstController> controller =
CreateBurst(preparedModel);
ASSERT_NE(nullptr, controller.get());
@@ -540,7 +541,8 @@
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
ASSERT_NE(nullptr, preparedModelCallback.get());
Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_2(
- model, ExecutionPreference::FAST_SINGLE_ANSWER, preparedModelCallback);
+ model, ExecutionPreference::FAST_SINGLE_ANSWER, hidl_vec<hidl_handle>(),
+ hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
diff --git a/neuralnetworks/1.2/IDevice.hal b/neuralnetworks/1.2/IDevice.hal
index b9fa388..d83f9e6 100644
--- a/neuralnetworks/1.2/IDevice.hal
+++ b/neuralnetworks/1.2/IDevice.hal
@@ -76,6 +76,17 @@
getType() generates (ErrorStatus status, DeviceType type);
/**
+ * Gets the capabilities of a driver.
+ *
+ * @return status Error status of the call, must be:
+ * - NONE if successful
+ * - DEVICE_UNAVAILABLE if driver is offline or busy
+ * - GENERAL_FAILURE if there is an unspecified error
+ * @return capabilities Capabilities of the driver.
+ */
+ getCapabilities_1_2() generates (ErrorStatus status, Capabilities capabilities);
+
+ /**
* Gets information about extensions supported by the driver implementation.
*
* All extension operations and operands must be fully supported for the
@@ -113,44 +124,83 @@
generates (ErrorStatus status, vec<bool> supportedOperations);
/**
- * Gets whether the driver supports compilation caching.
+ * Gets the caching requirements of the driver implementation.
*
- * isCachingSupported indicates whether the driver supports compilation caching.
- * Even if so, the driver may still choose not to cache certain compiled models.
+ * There are two types of cache file descriptors provided to the driver: model cache
+ * and data cache.
*
- * If the device reports the caching is not supported, the user may avoid calling
- * IDevice::prepareModelFromCache and IPreparedModel::saveToCache.
+ * The data cache is for caching constant data, possibly including preprocessed
+ * and transformed tensor buffers. Any modification to the data cache should
+ * have no worse effect than generating bad output values at execution time.
+ *
+ * The model cache is for caching security-sensitive data such as compiled
+ * executable machine code in the device's native binary format. A modification
+ * to the model cache may affect the driver's execution behavior, and a malicious
+ * client could make use of this to execute beyond the granted permission. Thus,
+ * the driver must always check whether the model cache is corrupted before
+ * preparing the model from cache.
+ *
+ * getNumberOfCacheFilesNeeded returns how many of each type of cache files the driver
+ * implementation needs to cache a single prepared model. Returning 0 for both types
+ * indicates compilation caching is not supported by this driver. The driver may
+ * still choose not to cache certain compiled models even if it reports that caching
+ * is supported.
+ *
+ * If the device reports that caching is not supported, the user may avoid calling
+ * IDevice::prepareModelFromCache or providing cache file descriptors to
+ * IDevice::prepareModel_1_2.
*
* @return status Error status of the call, must be:
* - NONE if successful
* - DEVICE_UNAVAILABLE if driver is offline or busy
* - GENERAL_FAILURE if there is an unspecified error
- * @return supported A boolean indicating whether the driver supports compilation
- * caching. Even on returning true, the driver may still choose
- * not to cache certain compiled models.
+ * @return numModelCache An unsigned integer indicating how many files for model cache
+ * the driver needs to cache a single prepared model. It must
+ * be less than or equal to Constant::MAX_NUMBER_OF_CACHE_FILES.
+ * @return numDataCache An unsigned integer indicating how many files for data cache
+ * the driver needs to cache a single prepared model. It must
+ * be less than or equal to Constant::MAX_NUMBER_OF_CACHE_FILES.
*/
- isCachingSupported() generates (ErrorStatus status, bool supported);
+ getNumberOfCacheFilesNeeded()
+ generates (ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache);
/**
- * Creates a prepared model for execution.
+ * Asynchronously creates a prepared model for execution and optionally saves it
+ * into cache files.
*
- * prepareModel is used to make any necessary transformations or alternative
+ * prepareModel is used to make any necessary transformations to or alternative
* representations to a model for execution, possibly including
* transformations on the constant data, optimization on the model's graph,
* or compilation into the device's native binary format. The model itself
* is not changed.
*
+ * Optionally, caching information may be provided for the driver to save
+ * the prepared model to cache files for faster model compilation time
+ * when the same model preparation is requested in the future. There are
+ * two types of cache file handles provided to the driver: model cache
+ * and data cache. For more information on the two types of cache handles,
+ * refer to getNumberOfCacheFilesNeeded.
+ *
+ * The file descriptors must be opened with read and write permission. A file may
+ * have any size, and the corresponding file descriptor may have any offset. The
+ * driver must truncate a file to zero size before writing to that file. The file
+ * descriptors may be closed by the client once the asynchronous preparation has
+ * finished. The driver must dup a file descriptor if it wants to get access to
+ * the cache file later.
+ *
* The model is prepared asynchronously with respect to the caller. The
- * prepareModel function must verify the inputs to the prepareModel function
- * are correct. If there is an error, prepareModel must immediately invoke
+ * prepareModel function must verify the inputs to the preparedModel function
+ * related to preparing the model (as opposed to saving the prepared model to
+ * cache) are correct. If there is an error, prepareModel must immediately invoke
* the callback with the appropriate ErrorStatus value and nullptr for the
- * IPreparedModel, then return with the same ErrorStatus. If the inputs to
- * the prepareModel function are valid and there is no error, prepareModel
- * must launch an asynchronous task to prepare the model in the background,
- * and immediately return from prepareModel with ErrorStatus::NONE. If the
- * asynchronous task fails to launch, prepareModel must immediately invoke
- * the callback with ErrorStatus::GENERAL_FAILURE and nullptr for the
- * IPreparedModel, then return with ErrorStatus::GENERAL_FAILURE.
+ * IPreparedModel, then return with the same ErrorStatus. If the inputs to the
+ * prepareModel function that are related to preparing the model are valid and
+ * there is no error, prepareModel must launch an asynchronous task
+ * to prepare the model in the background, and immediately return from
+ * prepareModel with ErrorStatus::NONE. If the asynchronous task fails to launch,
+ * prepareModel must immediately invoke the callback with
+ * ErrorStatus::GENERAL_FAILURE and nullptr for the IPreparedModel, then return
+ * with ErrorStatus::GENERAL_FAILURE.
*
* When the asynchronous task has finished preparing the model, it must
* immediately invoke the callback function provided as an input to
@@ -160,6 +210,14 @@
* the callback object must be invoked with the appropriate ErrorStatus
* value and nullptr for the IPreparedModel.
*
+ * Optionally, the driver may save the prepared model to cache during the
+ * asynchronous preparation. Any error that occurs when saving to cache must
+ * not affect the status of preparing the model. Even if the input arguments
+ * related to the cache may be invalid, or the driver may fail to save to cache,
+ * the prepareModel function must finish preparing the model. The driver
+ * may choose not to save to cache even if the caching information is
+ * provided and valid.
+ *
* The only information that may be unknown to the model at this stage is
* the shape of the tensors, which may only be known at execution time. As
* such, some driver services may return partially prepared models, where
@@ -173,6 +231,26 @@
* @param model The model to be prepared for execution.
* @param preference Indicates the intended execution behavior of a prepared
* model.
+ * @param modelCache A vector of handles with each entry holding exactly one
+ * cache file descriptor for the security-sensitive cache. The length of
+ * the vector must either be 0 indicating that caching information is not provided,
+ * or match the numModelCache returned from getNumberOfCacheFilesNeeded. The cache
+ * handles will be provided in the same order when retrieving the
+ * preparedModel from cache files with prepareModelFromCache.
+ * @param dataCache A vector of handles with each entry holding exactly one
+ * cache file descriptor for the constants' cache. The length of
+ * the vector must either be 0 indicating that caching information is not provided,
+ * or match the numDataCache returned from getNumberOfCacheFilesNeeded. The cache
+ * handles will be provided in the same order when retrieving the
+ * preparedModel from cache files with prepareModelFromCache.
+ * @param token A caching token of length Constant::BYTE_SIZE_OF_CACHE_TOKEN
+ * identifying the prepared model. The same token will be provided when retrieving
+ * the prepared model from the cache files with prepareModelFromCache.
+ * Tokens should be chosen to have a low rate of collision for a particular
+ * application. The driver cannot detect a collision; a collision will result
+ * in a failed execution or in a successful execution that produces incorrect
+ * output values. If both modelCache and dataCache are empty indicating that
+ * caching information is not provided, this token must be ignored.
* @param callback A callback object used to return the error status of
* preparing the model for execution and the prepared model if
* successful, nullptr otherwise. The callback object's notify function
@@ -182,9 +260,12 @@
* - NONE if preparation task is successfully launched
* - DEVICE_UNAVAILABLE if driver is offline or busy
* - GENERAL_FAILURE if there is an unspecified error
- * - INVALID_ARGUMENT if one of the input arguments is invalid
+ * - INVALID_ARGUMENT if one of the input arguments related to preparing the
+ * model is invalid
*/
prepareModel_1_2(Model model, ExecutionPreference preference,
+ vec<handle> modelCache, vec<handle> dataCache,
+ uint8_t[Constant:BYTE_SIZE_OF_CACHE_TOKEN] token,
IPreparedModelCallback callback)
generates (ErrorStatus status);
@@ -192,22 +273,17 @@
* Creates a prepared model from cache files for execution.
*
* prepareModelFromCache is used to retrieve a prepared model directly from
- * cache files to avoid slow model compilation time. There are exactly two
- * cache file descriptors provided to the driver: modelCache and dataCache.
+ * cache files to avoid slow model compilation time. There are
+ * two types of cache file handles provided to the driver: model cache
+ * and data cache. For more information on the two types of cache handles,
+ * refer to getNumberOfCacheFilesNeeded.
*
- * The dataCache is for caching constant data, possibly including preprocessed
- * and transformed tensor buffers. Any modification to the dataCache should
- * have no worse effect than generating bad output values at execution time.
- *
- * The modelCache is for caching security-sensitive data such as compiled
- * executable machine code in the device's native binary format. A modification
- * to the modelCache may affect the driver's execution behavior, and a malicious
- * client could make use of this to execute beyond the granted permission. Thus,
- * the driver must always check whether the modelCache is corrupted before preparing
- * the model from cache.
- *
- * The two file descriptors may be closed by the client once the asynchronous
- * preparation has finished. The driver has to copy all the data it needs.
+ * The file descriptors must be opened with read and write permission. A file may
+ * have any size, and the corresponding file descriptor may have any offset. The
+ * driver must truncate a file to zero size before writing to that file. The file
+ * descriptors may be closed by the client once the asynchronous preparation has
+ * finished. The driver must dup a file descriptor if it wants to get access to
+ * the cache file later.
*
* The model is prepared asynchronously with respect to the caller. The
* prepareModelFromCache function must verify the inputs to the
@@ -241,13 +317,17 @@
* used with different shapes of inputs on different (possibly concurrent)
* executions.
*
- * @param modelCache A handle holding exactly one cache file descriptor for the
- * security-sensitive cache.
- * @param dataCache A handle holding exactly one cache file descriptor for the
- * constants' cache.
+ * @param modelCache A vector of handles with each entry holding exactly one
+ * cache file descriptor for the security-sensitive cache. The length of
+ * the vector must match the numModelCache returned from getNumberOfCacheFilesNeeded.
+ * The cache handles will be provided in the same order as with prepareModel_1_2.
+ * @param dataCache A vector of handles with each entry holding exactly one
+ * cache file descriptor for the constants' cache. The length of the vector
+ * must match the numDataCache returned from getNumberOfCacheFilesNeeded.
+ * The cache handles will be provided in the same order as with prepareModel_1_2.
* @param token A caching token of length Constant::BYTE_SIZE_OF_CACHE_TOKEN
* identifying the prepared model. It is the same token provided when saving
- * the cache files with IPreparedModel::saveToCache. Tokens should be chosen
+ * the cache files with prepareModel_1_2. Tokens should be chosen
* to have a low rate of collision for a particular application. The driver
* cannot detect a collision; a collision will result in a failed execution
* or in a successful execution that produces incorrect output values.
@@ -263,7 +343,7 @@
* unspecified error
* - INVALID_ARGUMENT if one of the input arguments is invalid
*/
- prepareModelFromCache(handle modelCache, handle dataCache,
+ prepareModelFromCache(vec<handle> modelCache, vec<handle> dataCache,
uint8_t[Constant:BYTE_SIZE_OF_CACHE_TOKEN] token,
IPreparedModelCallback callback)
generates (ErrorStatus status);
diff --git a/neuralnetworks/1.2/IPreparedModel.hal b/neuralnetworks/1.2/IPreparedModel.hal
index 757d5f1..5d2d80f 100644
--- a/neuralnetworks/1.2/IPreparedModel.hal
+++ b/neuralnetworks/1.2/IPreparedModel.hal
@@ -157,62 +157,4 @@
fmq_sync<FmqRequestDatum> requestChannel,
fmq_sync<FmqResultDatum> resultChannel)
generates (ErrorStatus status, IBurstContext context);
-
- /*
- * Saves the prepared model to cache files.
- *
- * saveToCache is used to save a prepared model to cache files for faster
- * model compilation time when the same model preparation is requested in
- * the future. There are exactly two cache file descriptors provided to the
- * driver: modelCache and dataCache.
- *
- * The dataCache is for caching constant data, possibly including preprocessed
- * and transformed tensor buffers. Any modification to the dataCache should
- * have no worse effect than generating bad output values at execution time.
- *
- * The modelCache is for caching security-sensitive data such as compiled
- * executable machine code in the device's native binary format. A modification
- * to the modelCache may affect the driver's execution behavior, and a malicious
- * client could make use of this to execute beyond the granted permission. Thus,
- * the driver must always check whether the modelCache is corrupted before preparing
- * the model from cache.
- *
- * The two file descriptors must point to two zero-length files with offset
- * positioned at the beginning of the file. The file descriptors may be closed
- * by the client once the method has returned.
- *
- * If the driver decides not to save the prepared model without looking at the
- * input arguments to the saveToCache function, saveToCache must return with
- * ErrorStatus::GENERAL_FAILURE. Otherwise, the saveToCache function must verify
- * the input arguments to the saveToCache function are valid, and return with
- * ErrorStatus::INVALID_ARGUMENT if not. If the inputs are valid but the driver
- * could not save the prepared model, saveToCache must return with the appropriate
- * ErrorStatus. Otherwise, it must write the cache files and return
- * ErrorStatus::NONE. Unless saveToCache returns ErrorStatus::NONE, the contents
- * of the cache files are undefined.
- *
- * @param modelCache A handle holding exactly one cache file descriptor for the
- * security-sensitive cache.
- * @param dataCache A handle holding exactly one cache file descriptor for the
- * constants' cache.
- * @param token A caching token of length Constant::BYTE_SIZE_OF_CACHE_TOKEN
- * identifying the prepared model. The same token will be provided
- * when retrieving the prepared model from cache files with
- * IDevice::prepareModelFromCache. Tokens should be chosen to have
- * a low rate of collision for a particular application. The driver
- * cannot detect a collision; a collision will result in a failed
- * execution or in a successful execution that produces incorrect
- * output values.
- * @return status Error status of saveToCache, must be:
- * - NONE if saveToCache is performed successfully
- * - DEVICE_UNAVAILABLE if driver is offline or busy
- * - GENERAL_FAILURE if the driver could not save the
- * prepared model or if there is an unspecified error
- * - INVALID_ARGUMENT if one of the input arguments is invalid,
- * unless the driver decides not to save the prepared model
- * without looking at the input arguments
- */
- saveToCache(handle modelCache, handle dataCache,
- uint8_t[Constant:BYTE_SIZE_OF_CACHE_TOKEN] token)
- generates (ErrorStatus status);
};
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index f2e02b8..67a61c1 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -30,6 +30,11 @@
* The byte size of the cache token.
*/
BYTE_SIZE_OF_CACHE_TOKEN = 32,
+
+ /**
+ * The maximum number of files for each type of cache in compilation caching.
+ */
+ MAX_NUMBER_OF_CACHE_FILES = 32,
};
enum OperandType : @1.0::OperandType {
@@ -182,6 +187,10 @@
* input2.dimension = {5, 4, 3, 1}
* output.dimension = {5, 4, 3, 2}
*
+ * Since API level 29, generic zero-sized input tensor is supported. Zero
+ * dimension is only compatible with 0 or 1. The size of the output
+ * dimension is zero if either of corresponding input dimension is zero.
+ *
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
* * {@link OperandType::TENSOR_FLOAT32}
@@ -231,7 +240,8 @@
*
* Inputs (explicit padding):
* * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
- * the input.
+ * the input. Since API level 29, zero batches is supported for this
+ * tensor.
* * 1: An {@link OperandType::INT32} scalar, specifying the padding on
* the left, in the ‘width’ dimension.
* * 2: An {@link OperandType::INT32} scalar, specifying the padding on
@@ -257,7 +267,8 @@
*
* Inputs (implicit padding):
* * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
- * the input.
+ * the input. Since API level 29, zero batches is supported for this
+ * tensor.
* * 1: An {@link OperandType::INT32} scalar, specifying the implicit
* padding scheme, has to be one of the
* following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
@@ -304,6 +315,7 @@
* Before API level 29, all input tensors of
* {@link OperandType::TENSOR_QUANT8_ASYMM}
* must have the same scale and zeroPoint as the output tensor.
+ * Since API level 29, zero-sized tensors are supported.
* * n: An {@link OperandType::INT32} scalar, specifying the
* concatenation axis.
*
@@ -361,7 +373,8 @@
*
* Inputs (explicit padding):
* * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
- * specifying the input.
+ * specifying the input. Since API level 29, zero batches is supported
+ * for this tensor.
* * 1: A 4-D tensor, of shape
* [depth_out, filter_height, filter_width, depth_in], specifying the
* filter. For tensor of type
@@ -408,7 +421,8 @@
*
* Inputs (implicit padding):
* * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
- * specifying the input.
+ * specifying the input. Since API level 29, zero batches is supported
+ * for this tensor.
* * 1: A 4-D tensor, of shape
* [depth_out, filter_height, filter_width, depth_in], specifying the
* filter. For tensor of type
@@ -450,11 +464,10 @@
*
* Outputs:
* * 0: The output 4-D tensor, of shape
- * [batches, out_height, out_width, depth_out]. For output tensor of
- * {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
- * must be satisfied: output_scale > input_scale * filter_scale (for
- * filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
- * this condition must be true for all filter scales).
+ * [batches, out_height, out_width, depth_out]. Before API level 29,
+ * for output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}, the
+ * following condition must be satisfied:
+ * output_scale > input_scale * filter_scale
*
* Available since API level 27.
*/
@@ -600,11 +613,10 @@
*
* Outputs:
* * 0: The output 4-D tensor, of shape
- * [batches, out_height, out_width, depth_out]. For output tensor of
- * {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
- * must be satisfied: output_scale > input_scale * filter_scale (for
- * filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
- * this condition must be true for all filter scales).
+ * [batches, out_height, out_width, depth_out]. Before API level 29,
+ * for output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}, the
+ * following condition must be satisfied:
+ * output_scale > input_scale * filter_scale
*
* Available since API level 27.
*/
@@ -672,7 +684,7 @@
* Supported tensor rank: up to 4
*
* Inputs:
- * * 0: A tensor.
+ * * 0: A tensor. Since API level 29, this tensor may be zero-sized.
*
* Outputs:
* * 0: A tensor with the same shape as input0.
@@ -765,7 +777,8 @@
* [batch_size, input_size], where "input_size" corresponds to the
* number of inputs to the layer, matching the second dimension of
* weights, and "batch_size" is calculated by dividing the number of
- * elements by "input_size".
+ * elements by "input_size". Since API level 29, zero batch_size is
+ * supported for this tensor.
* * 1: A 2-D tensor, specifying the weights, of shape
* [num_units, input_size], where "num_units" corresponds to the number
* of output nodes.
@@ -780,10 +793,10 @@
* invoke on the result.
*
* Outputs:
- * * 0: The output tensor, of shape [batch_size, num_units]. For output
- * tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}, the following
- * condition must be satisfied:
- * output_scale > input_scale * filter_scale.
+ * * 0: The output tensor, of shape [batch_size, num_units]. Before API
+ * level 29, For output tensor of {@link
+ * OperandType::TENSOR_QUANT8_ASYMM}, the following condition must be
+ * satisfied: output_scale > input_scale * filter_scale.
*
* Available since API level 27.
*/
@@ -861,6 +874,7 @@
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
* * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since API level 29)
*
* Supported tensor rank: up to 4
* Tensors with rank less than 4 are only supported since API level 29.
@@ -875,6 +889,8 @@
*
* Outputs:
* * 0: A tensor of the same {@link OperandType} and same shape as input0.
+ * For {@link OperandType::TENSOR_QUANT8_ASYMM},
+ * the scale must be 1.f / 128 and the zeroPoint must be 128.
*
* Available since API level 27.
*/
@@ -905,7 +921,8 @@
*
* Inputs (explicit padding):
* * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
- * the input.
+ * the input. Since API level 29, zero batches is supported for this
+ * tensor.
* * 1: An {@link OperandType::INT32} scalar, specifying the padding on
* the left, in the ‘width’ dimension.
* * 2: An {@link OperandType::INT32} scalar, specifying the padding on
@@ -931,7 +948,8 @@
*
* Inputs (implicit padding):
* * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
- * the input.
+ * the input. Since API level 29, zero batches is supported for this
+ * tensor.
* * 1: An {@link OperandType::INT32} scalar, specifying the implicit
* padding scheme, has to be one of the
* following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
@@ -1021,7 +1039,8 @@
* Supported tensor rank: up to 4.
*
* Inputs:
- * * 0: A tensor, specifying the input.
+ * * 0: A tensor, specifying the input. Since API level 29, this tensor may
+ * be zero-sized.
*
* Outputs:
* * 0: The output tensor of same shape as input0.
@@ -1169,8 +1188,11 @@
* value if the recurrent projection layer exists, and should otherwise
* have no value.
* * (API level >= 29) The four layer normalization weights either all have
- * values or none of them have values. Layer normalization is used when
- * values are present.
+ * values or none of them have values. Additionally, if CIFG is used,
+ * input layer normalization weights tensor is omitted and the other layer
+ * normalization weights either all have values or none of them have
+ * values. Layer normalization is used when the values of all the layer
+ * normalization weights are present.
*
* References:
*
@@ -1333,7 +1355,8 @@
*
* Inputs (explicit padding):
* * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
- * the input.
+ * the input. Since API level 29, zero batches is supported for this
+ * tensor.
* * 1: An {@link OperandType::INT32} scalar, specifying the padding on
* the left, in the ‘width’ dimension.
* * 2: An {@link OperandType::INT32} scalar, specifying the padding on
@@ -1359,7 +1382,8 @@
*
* Inputs (implicit padding):
* * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
- * the input.
+ * the input. Since API level 29, zero batches is supported for this
+ * tensor.
* * 1: An {@link OperandType::INT32} scalar, specifying the implicit
* padding scheme, has to be one of the
* following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
@@ -1406,6 +1430,10 @@
* * {@link OperandType::TENSOR_FLOAT32}
* * {@link OperandType::TENSOR_QUANT8_ASYMM}
*
+ * Since API level 29, generic zero-sized input tensor is supported. Zero
+ * dimension is only compatible with 0 or 1. The size of the output
+ * dimension is zero if either of corresponding input dimension is zero.
+ *
* Supported tensor rank: up to 4
*
* Inputs:
@@ -1441,7 +1469,8 @@
* Supported tensor rank: up to 4.
*
* Inputs:
- * * 0: A tensor, specifying the input.
+ * * 0: A tensor, specifying the input. Since API level 29, this tensor may
+ * be zero-sized.
*
* Outputs:
* * 0: The output tensor of same shape as input0.
@@ -1465,7 +1494,8 @@
* Supported tensor rank: up to 4.
*
* Inputs:
- * * 0: A tensor, specifying the input.
+ * * 0: A tensor, specifying the input. Since API level 29, this tensor may
+ * be zero-sized.
*
* Outputs:
* * 0: The output tensor of same shape as input0.
@@ -1489,7 +1519,8 @@
* Supported tensor rank: up to 4.
*
* Inputs:
- * * 0: A tensor, specifying the input.
+ * * 0: A tensor, specifying the input. Since API level 29, this tensor may
+ * be zero-sized.
*
* Outputs:
* * 0: The output tensor of same shape as input0.
@@ -1541,9 +1572,12 @@
* [batch, height, width, channels]. Alternatively, the data layout could
* be NCHW, the data storage order of: [batch, channels, height, width].
*
- * Inputs:
+ * Both resizing by shape and resizing by scale are supported.
+ *
+ * Inputs (resizing by shape):
* * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
- * the input.
+ * the input. Since API level 29, zero batches is supported for this
+ * tensor.
* * 1: An {@link OperandType::INT32} scalar, specifying the output
* height of the output tensor.
* * 2: An {@link OperandType::INT32} scalar, specifying the output
@@ -1552,6 +1586,24 @@
* Set to true to specify NCHW data layout for input0 and output0.
* Available since API level 29.
*
+ * Inputs (resizing by scale, since API level 29):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+ * the input. Zero batches is supported for this tensor.
+ * * 1: A scalar, specifying height_scale, the scaling factor of the height
+ * dimension from the input tensor to the output tensor. The output
+ * height is calculated as new_height = floor(height * height_scale).
+ * The scalar must be of {@link OperandType::FLOAT16} if input0 is
+ * of {@link OperandType::TENSOR_FLOAT16} and of
+ * {@link OperandType::FLOAT32} otherwise.
+ * * 2: A scalar, specifying width_scale, the scaling factor of the width
+ * dimension from the input tensor to the output tensor. The output
+ * width is calculated as new_width = floor(width * width_scale).
+ * The scalar must be of {@link OperandType::FLOAT16} if input0 is
+ * of {@link OperandType::TENSOR_FLOAT16} and of
+ * {@link OperandType::FLOAT32} otherwise.
+ * * 3: An optional {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ *
* Outputs:
* * 0: The output 4-D tensor, of shape
* [batches, new_height, new_width, depth].
@@ -1637,7 +1689,8 @@
* Tensors with rank other than 2 or 4 are only supported since API level 29.
*
* Inputs:
- * * 0: A 2-D or 4-D tensor, specifying the tensor to be reshaped.
+ * * 0: A 2-D or 4-D tensor, specifying the tensor to be reshaped. Since
+ * API level 29, this tensor may be zero-sized.
* * 1: A scalar, specifying the positive scaling factor for the exponent,
* beta. If input0 is of {@link OperandType::TENSOR_FLOAT32} or
* {@link OperandType::TENSOR_QUANT8_ASYMM}, the scalar must be of
@@ -1795,7 +1848,8 @@
* Supported tensor rank: up to 4.
*
* Inputs:
- * * 0: A tensor, specifying the input.
+ * * 0: A tensor, specifying the input. Since API level 29, this tensor may
+ * be zero-sized.
*
* Outputs:
* * 0: The output tensor of same shape as input0.
@@ -1862,6 +1916,10 @@
* input2.dimension = {5, 4, 3, 1}
* output.dimension = {5, 4, 3, 2}
*
+ * Since API level 29, generic zero-sized input tensor is supported. Zero
+ * dimension is only compatible with 0 or 1. The size of the output
+ * dimension is zero if either of corresponding input dimension is zero.
+ *
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
* * {@link OperandType::TENSOR_FLOAT32}
@@ -2095,6 +2153,10 @@
* input2.dimension = {5, 4, 3, 1}
* output.dimension = {5, 4, 3, 2}
*
+ * Since API level 29, generic zero-sized input tensor is supported. Zero
+ * dimension is only compatible with 0 or 1. The size of the output
+ * dimension is zero if either of corresponding input dimension is zero.
+ *
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16} (since API level 29)
* * {@link OperandType::TENSOR_FLOAT32}
@@ -2135,6 +2197,7 @@
*
* Inputs:
* * 0: An n-D tensor, specifying the tensor to be transposed.
+ * Since API level 29, this tensor may be zero-sized.
* * 1: An optional 1-D Tensor of {@link OperandType::TENSOR_INT32},
* the permutation of the dimensions of the input tensor.
*
@@ -2231,7 +2294,8 @@
* * 0: A 2-D Tensor of shape [num_rois, 4], specifying the locations of the
* bounding box proposals, each line with format [x1, y1, x2, y2].
* For tensor of type {@link OperandType::TENSOR_QUANT16_ASYMM},
- * the zeroPoint must be 0 and the scale must be 0.125.
+ * the zeroPoint must be 0 and the scale must be 0.125. Zero num_rois
+ * is supported for this tensor.
* * 1: A 2-D Tensor of shape [num_rois, num_classes * 4], specifying the
* bounding box delta for each region of interest and each class. The
* bounding box deltas are organized in the following order
@@ -2240,10 +2304,12 @@
* and height, dw and dh is the log-scale relative correction factor
* for the width and height. For input0 of type
* {@link OperandType::TENSOR_QUANT16_ASYMM}, this tensor should be
- * of {@link OperandType::TENSOR_QUANT8_ASYMM}.
+ * of {@link OperandType::TENSOR_QUANT8_ASYMM}. Zero num_rois is
+ * supported for this tensor.
* * 2: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
* [num_rois], specifying the batch index of each box. Boxes with
- * the same batch index are grouped together.
+ * the same batch index are grouped together. Zero num_rois is
+ * supported for this tensor.
* * 3: A 2-D Tensor of shape [batches, 2], specifying the information of
* each image in the batch, each line with format
* [image_height, image_width].
@@ -2272,113 +2338,113 @@
* Inputs:
* * 0: The input.
* A 3-D tensor of shape:
- * If time-major: [max_time, batch_size, output_size]
- * If batch-major: [batch_size, max_time, output_size]
+ * If time-major: [max_time, batch_size, input_size]
+ * If batch-major: [batch_size, max_time, input_size]
* where "max_time" is the number of timesteps (sequence length),
* "batch_size" corresponds to the batching dimension, and
* "input_size" is the size of the input.
* * 1: The forward input-to-input weights. Optional.
- * A 2-D tensor of shape [num_units, input_size], where “num_units”
- * corresponds to the number of cell units.
+ * A 2-D tensor of shape [fw_num_units, input_size], where “fw_num_units”
+ * corresponds to the number of forward cell units.
* * 2: The forward input-to-forget weights.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [fw_num_units, input_size].
* * 3: The forward input-to-cell weights.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [fw_num_units, input_size].
* * 4: The forward input-to-output weights.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [fw_num_units, input_size].
* * 5: The forward recurrent-to-input weights. Optional.
- * A 2-D tensor of shape [num_units, output_size], where “output_size”
- * corresponds to either the number of cell units (i.e., “num_units”),
- * or the second dimension of the “projection_weights”, if defined.
+ * A 2-D tensor of shape [fw_num_units, fw_output_size], where “fw_output_size”
+ * corresponds to either the number of cell units (i.e., fw_num_units),
+ * or the second dimension of the “fw_projection_weights”, if defined.
* * 6: The forward recurrent-to-forget weights.
- * A 2-D tensor of shape [num_units, output_size].
+ * A 2-D tensor of shape [fw_num_units, fw_output_size].
* * 7: The forward recurrent-to-cell weights.
- * A 2-D tensor of shape [num_units, output_size].
+ * A 2-D tensor of shape [fw_num_units, fw_output_size].
* * 8: The forward recurrent-to-output weights.
- * A 2-D tensor of shape [num_units, output_size].
+ * A 2-D tensor of shape [fw_num_units, fw_output_size].
* * 9: The forward cell-to-input weights. Optional.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [fw_num_units].
* * 10: The forward cell-to-forget weights. Optional.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [fw_num_units].
* * 11: The forward cell-to-output weights. Optional.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [fw_num_units].
* * 12: The forward input gate bias. Optional.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [fw_num_units].
* * 13: The forward forget gate bias.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [fw_num_units].
* * 14: The forward cell gate bias.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [fw_num_units].
* * 15: The forward output gate bias.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [fw_num_units].
* * 16: The forward projection weights. Optional.
- * A 2-D tensor of shape [output_size, num_units].
+ * A 2-D tensor of shape [fw_output_size, fw_num_units].
* * 17: The forward projection bias. Optional.
- * A 1-D tensor of shape [output_size].
+ * A 1-D tensor of shape [fw_output_size].
* * 18: The backward input-to-input weights. Optional.
- * A 2-D tensor of shape [num_units, input_size], where “num_units”
- * corresponds to the number of cell units.
+ * A 2-D tensor of shape [bw_num_units, input_size], where “bw_num_units”
+ * corresponds to the number of backward cell units.
* * 19: The backward input-to-forget weights.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [bw_num_units, input_size].
* * 20: The backward input-to-cell weights.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [bw_num_units, input_size].
* * 21: The backward input-to-output weights.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [bw_num_units, input_size].
* * 22: The backward recurrent-to-input weights. Optional.
- * A 2-D tensor of shape [num_units, output_size], where “output_size”
- * corresponds to either the number of cell units (i.e., “num_units”),
- * or the second dimension of the “projection_weights”, if defined.
+ * A 2-D tensor of shape [bw_num_units, bw_output_size], where “bw_output_size”
+ * corresponds to either the number of cell units (i.e., “bw_num_units”),
+ * or the second dimension of the “bw_projection_weights”, if defined.
* * 23: The backward recurrent-to-forget weights.
- * A 2-D tensor of shape [num_units, output_size].
+ * A 2-D tensor of shape [bw_num_units, bw_output_size].
* * 24: The backward recurrent-to-cell weights.
- * A 2-D tensor of shape [num_units, output_size].
+ * A 2-D tensor of shape [bw_num_units, bw_output_size].
* * 25: The backward recurrent-to-output weights.
- * A 2-D tensor of shape [num_units, output_size].
+ * A 2-D tensor of shape [bw_num_units, bw_output_size].
* * 26: The backward cell-to-input weights. Optional.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [bw_num_units].
* * 27: The backward cell-to-forget weights. Optional.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [bw_num_units].
* * 28: The backward cell-to-output weights. Optional.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [bw_num_units].
* * 29: The backward input gate bias. Optional.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [bw_num_units].
* * 30: The backward forget gate bias.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [bw_num_units].
* * 31: The backward cell gate bias.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [bw_num_units].
* * 32: The backward output gate bias.
- * A 1-D tensor of shape [num_units].
+ * A 1-D tensor of shape [bw_num_units].
* * 33: The backward projection weights. Optional.
- * A 2-D tensor of shape [output_size, num_units].
+ * A 2-D tensor of shape [bw_output_size, bw_num_units].
* * 34: The backward projection bias. Optional.
- * A 1-D tensor of shape [output_size].
+ * A 1-D tensor of shape [bw_output_size].
* * 35: The forward input activation state.
- * A 2-D tensor of shape [batch_size, output_size].
+ * A 2-D tensor of shape [batch_size, bw_output_size].
* * 36: The forward input cell state.
- * A 2-D tensor of shape [batch_size, num_units].
+ * A 2-D tensor of shape [batch_size, bw_num_units].
* * 37: The backward input activation state.
- * A 2-D tensor of shape [batch_size, output_size].
+ * A 2-D tensor of shape [batch_size, bw_output_size].
* * 38: The backward input cell state.
- * A 2-D tensor of shape [batch_size, num_units].
+ * A 2-D tensor of shape [batch_size, bw_num_units].
* * 39: The auxiliary input. Optional.
* A 3-D tensor of shape [max_time, batch_size, input_size], where “batch_size”
* corresponds to the batching dimension, and “input_size” is the size
* of the input.
* * 40: The forward auxiliary input-to-input weights. Optional.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [fw_num_units, input_size].
* * 41: The forward auxiliary input-to-forget weights. Optional.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [fw_num_units, input_size].
* * 42: The forward auxiliary input-to-cell weights. Optional.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [fw_num_units, input_size].
* * 43: The forward auxiliary input-to-output weights. Optional.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [fw_num_units, input_size].
* * 44: The backward auxiliary input-to-input weights. Optional.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [bw_num_units, input_size].
* * 45: The backward auxiliary input-to-forget weights. Optional.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [bw_num_units, input_size].
* * 46: The backward auxiliary input-to-cell weights. Optional.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [bw_num_units, input_size].
* * 47: The backward auxiliary input-to-output weights. Optional.
- * A 2-D tensor of shape [num_units, input_size].
+ * A 2-D tensor of shape [bw_num_units, input_size].
* * 48: The activation function.
* A value indicating the activation function:
* <ul>
@@ -2410,16 +2476,46 @@
* * 52: time_major
* An {@link OperandType::BOOL} scalar specifying the shape format
* of input and output tensors.
+ * * 53: The forward input layer normalization weights. Optional.
+ * A 1-D tensor of shape [fw_num_units]. Used to rescale normalized inputs
+ * to activation at input gate.
+ * * 54: The forward forget layer normalization weights. Optional.
+ * A 1-D tensor of shape [fw_num_units]. Used to rescale normalized inputs
+ * to activation at forget gate.
+ * * 55: The forward cell layer normalization weights. Optional.
+ * A 1-D tensor of shape [fw_num_units]. Used to rescale normalized inputs
+ * to activation at cell gate.
+ * * 56: The forward output layer normalization weights. Optional.
+ * A 1-D tensor of shape [fw_num_units]. Used to rescale normalized inputs
+ * to activation at output gate.
+ * * 57: The backward input layer normalization weights. Optional.
+ * A 1-D tensor of shape [bw_num_units]. Used to rescale normalized inputs
+ * to activation at input gate.
+ * * 58: The backward forget layer normalization weights. Optional.
+ * A 1-D tensor of shape [bw_num_units]. Used to rescale normalized inputs
+ * to activation at forget gate.
+ * * 59: The backward cell layer normalization weights. Optional.
+ * A 1-D tensor of shape [bw_num_units]. Used to rescale normalized inputs
+ * to activation at cell gate.
+ * * 60: The backward output layer normalization weights. Optional.
+ * A 1-D tensor of shape [bw_num_units]. Used to rescale normalized inputs
+ * to activation at output gate.
*
* Outputs:
* * 0: The forward output.
* A 3-D tensor of shape:
- * If time-major: [max_time, batch_size, output_size]
- * If batch-major: [batch_size, max_time, output_size]
+ * If time-major and not merge_outputs:
+ * [max_time, batch_size, fw_output_size]
+ * If time-major and merge_outputs:
+ * [max_time, batch_size, fw_output_size + bw_output_size]
+ * If batch-major and not merge_outputs:
+ * [batch_size, max_time, fw_output_size]
+ * If batch-major and merge_outputs:
+ * [batch_size, max_time, fw_output_size + bw_output_size]
* * 1: The backward output. Unused if merge_outputs is true.
* A 3-D tensor of shape:
- * If time-major: [max_time, batch_size, output_size]
- * If batch-major: [batch_size, max_time, output_size]
+ * If time-major: [max_time, batch_size, bw_output_size]
+ * If batch-major: [batch_size, max_time, bw_output_size]
*
* Available since API level 29.
*/
@@ -2547,10 +2643,17 @@
/**
* Greedily selects a subset of bounding boxes in descending order of score.
*
- * This op applies hard NMS algorithm to each class. In each loop of
- * execution, the box with maximum score gets selected, and any boxes with
- * the intersection-over-union (IOU) greater than a threshold are removed
- * from the pending set.
+ * This op applies NMS algorithm to each class. In each loop of execution,
+ * the box with maximum score gets selected and removed from the pending set.
+ * The scores of the rest of boxes are lowered according to the
+ * intersection-over-union (IOU) overlapping with the previously selected
+ * boxes and a specified NMS kernel method. Any boxes with score less
+ * than a threshold are removed from the pending set.
+ *
+ * Three NMS kernels are supported:
+ * * Hard: score_new = score_old * (1 if IoU < threshold else 0)
+ * * Linear: score_new = score_old * (1 if IoU < threshold else 1 - IoU)
+ * * Gaussian: score_new = score_old * exp(- IoU^2 / sigma)
*
* Axis-aligned bounding boxes are represented by its upper-left corner
* coordinate (x1,y1) and lower-right corner coordinate (x2,y2). A valid
@@ -2564,25 +2667,34 @@
* Inputs:
* * 0: A 2-D Tensor of shape [num_rois, num_classes], specifying the score
* of each bounding box proposal. The boxes are grouped by batches in the
- * first dimension.
+ * first dimension. Zero num_rois is supported for this tensor.
* * 1: A 2-D Tensor specifying the bounding boxes of shape
* [num_rois, num_classes * 4], organized in the order [x1, y1, x2, y2].
* The boxes are grouped by batches in the first dimension. The sequential
* order of the boxes corresponds with input0. For input0 of type
* {@link OperandType::TENSOR_QUANT8_ASYMM}, this tensor should be of
* {@link OperandType::TENSOR_QUANT16_ASYMM}, with zeroPoint of 0 and
- * scale of 0.125.
+ * scale of 0.125. Zero num_rois is supported for this tensor.
* * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
* [num_rois], specifying the batch index of each box. Boxes with
* the same batch index are grouped together.
* * 3: An {@link OperandType::FLOAT32} scalar, score_threshold. Boxes
* with scores lower than the threshold are filtered before sending
* to the NMS algorithm.
- * * 4: An {@link OperandType::FLOAT32} scalar, specifying the IoU
- * threshold.
- * * 5: An {@link OperandType::INT32} scalar, specifying the maximum
+ * * 4: An {@link OperandType::INT32} scalar, specifying the maximum
* number of selected bounding boxes for each image. Set to a negative
* value for unlimited number of output bounding boxes.
+ * * 5: An {@link OperandType::INT32} scalar, specifying the NMS
+ * kernel method, options are 0:hard, 1:linear, 2:gaussian.
+ * * 6: An {@link OperandType::FLOAT32} scalar, specifying the IoU
+ * threshold in hard and linear NMS kernel. This field is ignored if
+ * gaussian kernel is selected.
+ * * 7: An {@link OperandType::FLOAT32} scalar, specifying the sigma in
+ * gaussian NMS kernel. This field is ignored if gaussian kernel is
+ * not selected.
+ * * 8: An {@link OperandType::FLOAT32} scalar, nms_score_threshold.
+ * Boxes with scores lower than the threshold are dropped during the
+ * score updating phase in soft NMS.
*
* Outputs:
* * 0: A 1-D Tensor of the same {@link OperandType} as input0, with shape
@@ -2600,8 +2712,8 @@
* [num_output_rois], specifying the class of each output box. The
* sequential order of the boxes corresponds with output0.
* * 3: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
- * [num_rois], specifying the batch index of each box. Boxes with
- * the same batch index are grouped together.
+ * [num_output_rois], specifying the batch index of each box. Boxes
+ * with the same batch index are grouped together.
*
* Available since API level 29.
*/
@@ -2937,8 +3049,8 @@
* For type of {@link OperandType::TENSOR_QUANT16_ASYMM}, the
* scale must be 0.125 and the zero point must be 0.
* * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
- * [num_rois], specifying the batch index of each box. Boxes with
- * the same batch index are grouped together.
+ * [num_output_rois], specifying the batch index of each box. Boxes
+ * with the same batch index are grouped together.
*
* Available since API level 29.
*/
@@ -3122,11 +3234,7 @@
*
* Outputs:
* * 0: The output 4-D tensor, of shape
- * [batches, out_height, out_width, depth_out]. For output tensor of
- * {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
- * must be satisfied: output_scale > input_scale * filter_scale (for
- * filter tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
- * this condition must be true for all filter scales).
+ * [batches, out_height, out_width, depth_out].
*
* Available since API level 29.
*/
@@ -3608,7 +3716,7 @@
* Supported tensor rank: from 1
*
* Inputs:
- * * 0: A tensor.
+ * * 0: A tensor, may be zero-sized.
*
* Outputs:
* * 0: The output tensor of same shape as input0, but with
@@ -3940,10 +4048,12 @@
* the regions of interest, each line with format [x1, y1, x2, y2].
* For input0 of type {@link OperandType::TENSOR_QUANT8_ASYMM},
* this tensor should be of {@link OperandType::TENSOR_QUANT16_ASYMM},
- * with zeroPoint of 0 and scale of 0.125.
+ * with zeroPoint of 0 and scale of 0.125. Zero num_rois is
+ * supported for this tensor.
* * 2: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
* [num_rois], specifying the batch index of each box. Boxes with
- * the same batch index are grouped together.
+ * the same batch index are grouped together. Zero num_rois is
+ * supported for this tensor.
* * 3: An {@link OperandType::INT32} scalar, specifying the output
* height of the output tensor.
* * 4: An {@link OperandType::INT32} scalar, specifying the output
@@ -4108,7 +4218,7 @@
* Supported tensor rank: from 1
*
* Inputs:
- * * 0: An n-D tensor to take slice from.
+ * * 0: An n-D tensor to take slice from, may be zero-sized.
* * 1: A 1-D tensor of type {@link OperandType::TENSOR_INT32} specifying
* the beginning indices of the slice in each dimension.
* * 2: A 1-D tensor of type {@link OperandType::TENSOR_INT32} specifying
@@ -4331,11 +4441,7 @@
*
* Outputs:
* * 0: The output 4-D tensor, of shape
- * [batches, out_height, out_width, depth_out]. For output tensor of
- * {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition
- * must be satisfied: output_scale > input_scale * filter_scale (for
- * filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
- * this condition must be true for all filter scales).
+ * [batches, out_height, out_width, depth_out].
*
* Available since API level 29.
*/
@@ -4367,9 +4473,9 @@
* Inputs:
* * 0: The input (\f$x_t\f$).
* A 3-D tensor of shape:
- * If time-major: [max_time, batch_size, output_size]
- * If batch-major: [batch_size, max_time, output_size]
- * where “max_size” is the number of timesteps (sequence length),
+ * If time-major: [max_time, batch_size, input_size]
+ * If batch-major: [batch_size, max_time, input_size]
+ * where “max_time” is the number of timesteps (sequence length),
* “batch_size” corresponds to the batching dimension, and
* “input_size” is the size of the input.
* * 1: The input-to-input weights (\f$W_{xi}\f$). Optional.
@@ -4429,16 +4535,16 @@
* projection layer, such that values are bound within
* [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
* * 23:Time-major if true, batch-major if false.
- * * 24:The input layer normalization weights.
+ * * 24:The input layer normalization weights. Optional.
* A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
* to activation at input gate.
- * * 25:The forget layer normalization weights.
+ * * 25:The forget layer normalization weights. Optional.
* A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
* to activation at forget gate.
- * * 26:The cell layer normalization weights.
+ * * 26:The cell layer normalization weights. Optional.
* A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
* to activation at cell gate.
- * * 27:The output layer normalization weights.
+ * * 27:The output layer normalization weights. Optional.
* A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
* to activation at output gate.
*
@@ -4526,9 +4632,11 @@
* [batch, height, width, channels]. Alternatively, the data layout could
* be NCHW, the data storage order of: [batch, channels, height, width].
*
- * Inputs:
+ * Both resizing by shape and resizing by scale are supported.
+ *
+ * Inputs (resizing by shape):
* * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
- * the input.
+ * the input. Zero batches is supported for this tensor.
* * 1: An {@link OperandType::INT32} scalar, specifying the output
* height of the output tensor.
* * 2: An {@link OperandType::INT32} scalar, specifying the output
@@ -4536,6 +4644,24 @@
* * 3: An {@link OperandType::BOOL} scalar, default to false.
* Set to true to specify NCHW data layout for input0 and output0.
*
+ * Inputs (resizing by scale):
+ * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+ * the input. Zero batches is supported for this tensor.
+ * * 1: A scalar, specifying height_scale, the scaling factor of the height
+ * dimension from the input tensor to the output tensor. The output
+ * height is calculated as new_height = floor(height * height_scale).
+ * The scalar must be of {@link OperandType::FLOAT16} if input0 is
+ * of {@link OperandType::TENSOR_FLOAT16} and of
+ * {@link OperandType::FLOAT32} otherwise.
+ * * 2: A scalar, specifying width_scale, the scaling factor of the width
+ * dimension from the input tensor to the output tensor. The output
+ * width is calculated as new_width = floor(width * width_scale).
+ * The scalar must be of {@link OperandType::FLOAT16} if input0 is
+ * of {@link OperandType::TENSOR_FLOAT16} and of
+ * {@link OperandType::FLOAT32} otherwise.
+ * * 3: An {@link OperandType::BOOL} scalar, default to false.
+ * Set to true to specify NCHW data layout for input0 and output0.
+ *
* Outputs:
* * 0: The output 4-D tensor, of shape
* [batches, new_height, new_width, depth].
@@ -4593,6 +4719,39 @@
};
/**
+ * The capabilities of a driver.
+ *
+ * Performance of an operation comes from the type of its first operand.
+ * This represents performance for non extension operand types.
+ */
+struct Capabilities {
+ /**
+ * Driver performance when operating on float32 data but performing
+ * calculations with range and/or precision as low as that of the IEEE
+ * 754 16-bit floating-point format.
+ */
+ PerformanceInfo relaxedFloat32toFloat16PerformanceScalar;
+ PerformanceInfo relaxedFloat32toFloat16PerformanceTensor;
+
+ /**
+ * Driver performance when operating on a particular data type.
+ * In the case of float32 data, this is used when the calculations
+ * are not relaxed.
+ */
+ struct OperandPerformance {
+ OperandType type;
+ PerformanceInfo info;
+ };
+
+ /**
+ * Performance by operand type. Must be sorted by OperandType.
+ * If a particular OperandType is not present in operandPerformance,
+ * its performance is treated as { .execTime = FLT_MAX, .powerUsage = FLT_MAX }.
+ */
+ vec<OperandPerformance> operandPerformance;
+};
+
+/**
* Describes one operation of the model's graph.
*/
struct Operation {
diff --git a/neuralnetworks/1.2/vts/functional/BasicTests.cpp b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
index 365a750..5c269df 100644
--- a/neuralnetworks/1.2/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
@@ -25,7 +25,7 @@
namespace vts {
namespace functional {
-using V1_1::Capabilities;
+using V1_0::PerformanceInfo;
// create device test
TEST_F(NeuralnetworksHidlTest, CreateDevice) {}
@@ -37,6 +37,31 @@
EXPECT_EQ(DeviceStatus::AVAILABLE, static_cast<DeviceStatus>(status));
}
+// initialization
+TEST_F(NeuralnetworksHidlTest, GetCapabilitiesTest) {
+ using OperandPerformance = Capabilities::OperandPerformance;
+ Return<void> ret = device->getCapabilities_1_2([](ErrorStatus status,
+ const Capabilities& capabilities) {
+ EXPECT_EQ(ErrorStatus::NONE, status);
+
+ auto isPositive = [](const PerformanceInfo& perf) {
+ return perf.execTime > 0.0f && perf.powerUsage > 0.0f;
+ };
+
+ EXPECT_TRUE(isPositive(capabilities.relaxedFloat32toFloat16PerformanceScalar));
+ EXPECT_TRUE(isPositive(capabilities.relaxedFloat32toFloat16PerformanceTensor));
+ const auto& opPerf = capabilities.operandPerformance;
+ EXPECT_TRUE(std::all_of(
+ opPerf.begin(), opPerf.end(),
+ [isPositive](const OperandPerformance& a) { return isPositive(a.info); }));
+ EXPECT_TRUE(std::is_sorted(opPerf.begin(), opPerf.end(),
+ [](const OperandPerformance& a, const OperandPerformance& b) {
+ return a.type < b.type;
+ }));
+ });
+ EXPECT_TRUE(ret.isOk());
+}
+
// device version test
TEST_F(NeuralnetworksHidlTest, GetDeviceVersionStringTest) {
Return<void> ret = device->getVersionString([](ErrorStatus status, const hidl_string& version) {
@@ -77,10 +102,15 @@
EXPECT_TRUE(ret.isOk());
}
-// isCachingSupported test
-TEST_F(NeuralnetworksHidlTest, IsCachingSupported) {
- Return<void> ret = device->isCachingSupported(
- [](ErrorStatus status, bool) { EXPECT_EQ(ErrorStatus::NONE, status); });
+// getNumberOfCacheFilesNeeded test
+TEST_F(NeuralnetworksHidlTest, getNumberOfCacheFilesNeeded) {
+ Return<void> ret = device->getNumberOfCacheFilesNeeded(
+ [](ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
+ EXPECT_EQ(ErrorStatus::NONE, status);
+ EXPECT_LE(numModelCache,
+ static_cast<uint32_t>(Constant::MAX_NUMBER_OF_CACHE_FILES));
+ EXPECT_LE(numDataCache, static_cast<uint32_t>(Constant::MAX_NUMBER_OF_CACHE_FILES));
+ });
EXPECT_TRUE(ret.isOk());
}
} // namespace functional
diff --git a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
index 00989e5..167fc09 100644
--- a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
@@ -54,29 +54,39 @@
[[maybe_unused]] auto dummy_createTestModel = createTestModel_dynamic_output_shape;
[[maybe_unused]] auto dummy_get_examples = get_examples_dynamic_output_shape;
-enum class AccessMode { READ_ONLY, WRITE_ONLY };
+enum class AccessMode { READ_WRITE, READ_ONLY, WRITE_ONLY };
-void createCacheHandle(const std::vector<std::string>& files, AccessMode mode,
- hidl_handle* handle) {
- std::vector<int> fds;
- for (const auto& file : files) {
- int fd;
- if (mode == AccessMode::READ_ONLY) {
- fd = open(file.c_str(), O_RDONLY);
- } else if (mode == AccessMode::WRITE_ONLY) {
- fd = open(file.c_str(), O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
- } else {
- FAIL();
+// Creates cache handles based on provided file groups.
+// The outer vector corresponds to handles and the inner vector is for fds held by each handle.
+void createCacheHandles(const std::vector<std::vector<std::string>>& fileGroups,
+ const std::vector<AccessMode>& mode, hidl_vec<hidl_handle>* handles) {
+ handles->resize(fileGroups.size());
+ for (uint32_t i = 0; i < fileGroups.size(); i++) {
+ std::vector<int> fds;
+ for (const auto& file : fileGroups[i]) {
+ int fd;
+ if (mode[i] == AccessMode::READ_ONLY) {
+ fd = open(file.c_str(), O_RDONLY);
+ } else if (mode[i] == AccessMode::WRITE_ONLY) {
+ fd = open(file.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+ } else if (mode[i] == AccessMode::READ_WRITE) {
+ fd = open(file.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ } else {
+ FAIL();
+ }
+ ASSERT_GE(fd, 0);
+ fds.push_back(fd);
}
- ASSERT_GE(fd, 0);
- fds.push_back(fd);
+ native_handle_t* cacheNativeHandle = native_handle_create(fds.size(), 0);
+ ASSERT_NE(cacheNativeHandle, nullptr);
+ std::copy(fds.begin(), fds.end(), &cacheNativeHandle->data[0]);
+ (*handles)[i].setTo(cacheNativeHandle, /*shouldOwn=*/true);
}
- native_handle_t* cacheNativeHandle = native_handle_create(fds.size(), 0);
- ASSERT_NE(cacheNativeHandle, nullptr);
- for (uint32_t i = 0; i < fds.size(); i++) {
- cacheNativeHandle->data[i] = fds[i];
- }
- handle->setTo(cacheNativeHandle, /*shouldOwn=*/true);
+}
+
+void createCacheHandles(const std::vector<std::vector<std::string>>& fileGroups, AccessMode mode,
+ hidl_vec<hidl_handle>* handles) {
+ createCacheHandles(fileGroups, std::vector<AccessMode>(fileGroups.size(), mode), handles);
}
} // namespace
@@ -88,38 +98,43 @@
NeuralnetworksHidlTest::SetUp();
ASSERT_NE(device.get(), nullptr);
- // Create cache directory. The cache directory and cache files are always created to test
- // the behavior of prepareModelFromCache, even when caching is not supported.
+ // Create cache directory. The cache directory and a temporary cache file is always created
+ // to test the behavior of prepareModelFromCache, even when caching is not supported.
char cacheDirTemp[] = "/data/local/tmp/TestCompilationCachingXXXXXX";
char* cacheDir = mkdtemp(cacheDirTemp);
ASSERT_NE(cacheDir, nullptr);
mCacheDir = cacheDir;
+ mCacheDir.push_back('/');
- // Create empty cache files.
- mCache1 = mCacheDir + "/cache1";
- mCache2 = mCacheDir + "/cache2";
- mCache3 = mCacheDir + "/cache3";
- // A dummy handle, use AccessMode::WRITE_ONLY for createCacheHandle to create files.
- hidl_handle handle;
- createCacheHandle({mCache1, mCache2, mCache3}, AccessMode::WRITE_ONLY, &handle);
-
- // Check if caching is supported.
- bool isCachingSupported;
- Return<void> ret = device->isCachingSupported(
- [&isCachingSupported](ErrorStatus status, bool supported) {
+ Return<void> ret = device->getNumberOfCacheFilesNeeded(
+ [this](ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
EXPECT_EQ(ErrorStatus::NONE, status);
- isCachingSupported = supported;
+ mNumModelCache = numModelCache;
+ mNumDataCache = numDataCache;
});
EXPECT_TRUE(ret.isOk());
- if (isCachingSupported) {
- mIsCachingSupported = true;
- } else {
+ mIsCachingSupported = mNumModelCache > 0 || mNumDataCache > 0;
+
+ // Create empty cache files.
+ mTmpCache = mCacheDir + "tmp";
+ for (uint32_t i = 0; i < mNumModelCache; i++) {
+ mModelCache.push_back({mCacheDir + "model" + std::to_string(i)});
+ }
+ for (uint32_t i = 0; i < mNumDataCache; i++) {
+ mDataCache.push_back({mCacheDir + "data" + std::to_string(i)});
+ }
+ // Dummy handles, use AccessMode::WRITE_ONLY for createCacheHandles to create files.
+ hidl_vec<hidl_handle> modelHandle, dataHandle, tmpHandle;
+ createCacheHandles(mModelCache, AccessMode::WRITE_ONLY, &modelHandle);
+ createCacheHandles(mDataCache, AccessMode::WRITE_ONLY, &dataHandle);
+ createCacheHandles({{mTmpCache}}, AccessMode::WRITE_ONLY, &tmpHandle);
+
+ if (!mIsCachingSupported) {
LOG(INFO) << "NN VTS: Early termination of test because vendor service does not "
"support compilation caching.";
std::cout << "[ ] Early termination of test because vendor service does not "
"support compilation caching."
<< std::endl;
- mIsCachingSupported = false;
}
}
@@ -127,22 +142,49 @@
// The tmp directory is only removed when the driver reports caching not supported,
// otherwise it is kept for debugging purpose.
if (!mIsCachingSupported) {
- remove(mCache1.c_str());
- remove(mCache2.c_str());
- remove(mCache3.c_str());
+ remove(mTmpCache.c_str());
rmdir(mCacheDir.c_str());
}
NeuralnetworksHidlTest::TearDown();
}
- void saveModelToCache(sp<IPreparedModel> preparedModel, const hidl_handle& cache1,
- const hidl_handle& cache2, ErrorStatus* status) {
- // Save IPreparedModel to cache.
+ void saveModelToCache(const V1_2::Model& model, const hidl_vec<hidl_handle>& modelCache,
+ const hidl_vec<hidl_handle>& dataCache, bool* supported,
+ sp<IPreparedModel>* preparedModel = nullptr) {
+ if (preparedModel != nullptr) *preparedModel = nullptr;
+
+ // See if service can handle model.
+ bool fullySupportsModel = false;
+ Return<void> supportedCall = device->getSupportedOperations_1_2(
+ model,
+ [&fullySupportsModel, &model](ErrorStatus status, const hidl_vec<bool>& supported) {
+ ASSERT_EQ(ErrorStatus::NONE, status);
+ ASSERT_EQ(supported.size(), model.operations.size());
+ fullySupportsModel = std::all_of(supported.begin(), supported.end(),
+ [](bool valid) { return valid; });
+ });
+ ASSERT_TRUE(supportedCall.isOk());
+ *supported = fullySupportsModel;
+ if (!fullySupportsModel) return;
+
+ // Launch prepare model.
+ sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
+ ASSERT_NE(nullptr, preparedModelCallback.get());
hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
- Return<ErrorStatus> saveToCacheStatus =
- preparedModel->saveToCache(cache1, cache2, cacheToken);
- ASSERT_TRUE(saveToCacheStatus.isOk());
- *status = static_cast<ErrorStatus>(saveToCacheStatus);
+ Return<ErrorStatus> prepareLaunchStatus =
+ device->prepareModel_1_2(model, ExecutionPreference::FAST_SINGLE_ANSWER, modelCache,
+ dataCache, cacheToken, preparedModelCallback);
+ ASSERT_TRUE(prepareLaunchStatus.isOk());
+ ASSERT_EQ(static_cast<ErrorStatus>(prepareLaunchStatus), ErrorStatus::NONE);
+
+ // Retrieve prepared model.
+ preparedModelCallback->wait();
+ ASSERT_EQ(preparedModelCallback->getStatus(), ErrorStatus::NONE);
+ if (preparedModel != nullptr) {
+ *preparedModel =
+ V1_2::IPreparedModel::castFrom(preparedModelCallback->getPreparedModel())
+ .withDefault(nullptr);
+ }
}
bool checkEarlyTermination(ErrorStatus status) {
@@ -157,14 +199,27 @@
return false;
}
- void prepareModelFromCache(const hidl_handle& cache1, const hidl_handle& cache2,
+ bool checkEarlyTermination(bool supported) {
+ if (!supported) {
+ LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
+ "prepare model that it does not support.";
+ std::cout << "[ ] Early termination of test because vendor service cannot "
+ "prepare model that it does not support."
+ << std::endl;
+ return true;
+ }
+ return false;
+ }
+
+ void prepareModelFromCache(const hidl_vec<hidl_handle>& modelCache,
+ const hidl_vec<hidl_handle>& dataCache,
sp<IPreparedModel>* preparedModel, ErrorStatus* status) {
// Launch prepare model from cache.
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
ASSERT_NE(nullptr, preparedModelCallback.get());
hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
- Return<ErrorStatus> prepareLaunchStatus =
- device->prepareModelFromCache(cache1, cache2, cacheToken, preparedModelCallback);
+ Return<ErrorStatus> prepareLaunchStatus = device->prepareModelFromCache(
+ modelCache, dataCache, cacheToken, preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
if (static_cast<ErrorStatus>(prepareLaunchStatus) != ErrorStatus::NONE) {
*preparedModel = nullptr;
@@ -179,49 +234,54 @@
.withDefault(nullptr);
}
+ // Absolute path to the temporary cache directory.
std::string mCacheDir;
- std::string mCache1;
- std::string mCache2;
- std::string mCache3;
+
+ // Groups of file paths for model and data cache in the tmp cache directory, initialized with
+ // outer_size = mNum{Model|Data}Cache, inner_size = 1. The outer vector corresponds to handles
+ // and the inner vector is for fds held by each handle.
+ std::vector<std::vector<std::string>> mModelCache;
+ std::vector<std::vector<std::string>> mDataCache;
+
+ // A separate temporary file path in the tmp cache directory.
+ std::string mTmpCache;
+
uint8_t mToken[static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)] = {};
- bool mIsCachingSupported;
+ uint32_t mNumModelCache;
+ uint32_t mNumDataCache;
+ uint32_t mIsCachingSupported;
};
TEST_F(CompilationCachingTest, CacheSavingAndRetrieval) {
// Create test HIDL model and compile.
Model testModel = createTestModel();
sp<IPreparedModel> preparedModel = nullptr;
- generated_tests::PrepareModel(device, testModel, &preparedModel);
- // Terminate early if the driver cannot prepare the model.
- if (preparedModel == nullptr) return;
// Save the compilation to cache.
{
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- if (!mIsCachingSupported) {
- EXPECT_EQ(status, ErrorStatus::GENERAL_FAILURE);
- } else {
- if (checkEarlyTermination(status)) return;
- ASSERT_EQ(status, ErrorStatus::NONE);
- }
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ saveModelToCache(testModel, modelCache, dataCache, &supported);
+ if (checkEarlyTermination(supported)) return;
}
// Retrieve preparedModel from cache.
{
preparedModel = nullptr;
ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
- prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
if (!mIsCachingSupported) {
ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
ASSERT_EQ(preparedModel, nullptr);
return;
+ } else if (checkEarlyTermination(status)) {
+ ASSERT_EQ(preparedModel, nullptr);
+ return;
} else {
ASSERT_EQ(status, ErrorStatus::NONE);
ASSERT_NE(preparedModel, nullptr);
@@ -238,41 +298,54 @@
// Create test HIDL model and compile.
Model testModel = createTestModel();
sp<IPreparedModel> preparedModel = nullptr;
- generated_tests::PrepareModel(device, testModel, &preparedModel);
- // Terminate early if the driver cannot prepare the model.
- if (preparedModel == nullptr) return;
// Save the compilation to cache.
{
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- if (!mIsCachingSupported) {
- EXPECT_EQ(status, ErrorStatus::GENERAL_FAILURE);
- } else {
- if (checkEarlyTermination(status)) return;
- ASSERT_EQ(status, ErrorStatus::NONE);
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ uint8_t dummyBytes[] = {0, 0};
+ // Write a dummy integer to the cache.
+ // The driver should be able to handle non-empty cache and non-zero fd offset.
+ for (uint32_t i = 0; i < modelCache.size(); i++) {
+ ASSERT_EQ(write(modelCache[i].getNativeHandle()->data[0], &dummyBytes,
+ sizeof(dummyBytes)),
+ sizeof(dummyBytes));
}
+ for (uint32_t i = 0; i < dataCache.size(); i++) {
+ ASSERT_EQ(
+ write(dataCache[i].getNativeHandle()->data[0], &dummyBytes, sizeof(dummyBytes)),
+ sizeof(dummyBytes));
+ }
+ saveModelToCache(testModel, modelCache, dataCache, &supported);
+ if (checkEarlyTermination(supported)) return;
}
// Retrieve preparedModel from cache.
{
preparedModel = nullptr;
ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
uint8_t dummyByte = 0;
- // Advance offset by one byte.
- ASSERT_GE(read(cache1.getNativeHandle()->data[0], &dummyByte, 1), 0);
- ASSERT_GE(read(cache2.getNativeHandle()->data[0], &dummyByte, 1), 0);
- prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ // Advance the offset of each handle by one byte.
+ // The driver should be able to handle non-zero fd offset.
+ for (uint32_t i = 0; i < modelCache.size(); i++) {
+ ASSERT_GE(read(modelCache[i].getNativeHandle()->data[0], &dummyByte, 1), 0);
+ }
+ for (uint32_t i = 0; i < dataCache.size(); i++) {
+ ASSERT_GE(read(dataCache[i].getNativeHandle()->data[0], &dummyByte, 1), 0);
+ }
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
if (!mIsCachingSupported) {
ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
ASSERT_EQ(preparedModel, nullptr);
return;
+ } else if (checkEarlyTermination(status)) {
+ ASSERT_EQ(preparedModel, nullptr);
+ return;
} else {
ASSERT_EQ(status, ErrorStatus::NONE);
ASSERT_NE(preparedModel, nullptr);
@@ -285,234 +358,512 @@
/*testDynamicOutputShape=*/false);
}
+TEST_F(CompilationCachingTest, SaveToCacheInvalidNumCache) {
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+
+ // Test with number of model cache files greater than mNumModelCache.
+ {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ // Pass an additional cache file for model cache.
+ mModelCache.push_back({mTmpCache});
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mModelCache.pop_back();
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(testModel, modelCache, dataCache, &supported, &preparedModel);
+ if (checkEarlyTermination(supported)) return;
+ ASSERT_NE(preparedModel, nullptr);
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; },
+ get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+ // Check if prepareModelFromCache fails.
+ preparedModel = nullptr;
+ ErrorStatus status;
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::INVALID_ARGUMENT) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+
+ // Test with number of model cache files smaller than mNumModelCache.
+ if (mModelCache.size() > 0) {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ // Pop out the last cache file.
+ auto tmp = mModelCache.back();
+ mModelCache.pop_back();
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mModelCache.push_back(tmp);
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(testModel, modelCache, dataCache, &supported, &preparedModel);
+ if (checkEarlyTermination(supported)) return;
+ ASSERT_NE(preparedModel, nullptr);
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; },
+ get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+ // Check if prepareModelFromCache fails.
+ preparedModel = nullptr;
+ ErrorStatus status;
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::INVALID_ARGUMENT) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+
+ // Test with number of data cache files greater than mNumDataCache.
+ {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ // Pass an additional cache file for data cache.
+ mDataCache.push_back({mTmpCache});
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mDataCache.pop_back();
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(testModel, modelCache, dataCache, &supported, &preparedModel);
+ if (checkEarlyTermination(supported)) return;
+ ASSERT_NE(preparedModel, nullptr);
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; },
+ get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+ // Check if prepareModelFromCache fails.
+ preparedModel = nullptr;
+ ErrorStatus status;
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::INVALID_ARGUMENT) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+
+ // Test with number of data cache files smaller than mNumDataCache.
+ if (mDataCache.size() > 0) {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ // Pop out the last cache file.
+ auto tmp = mDataCache.back();
+ mDataCache.pop_back();
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mDataCache.push_back(tmp);
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(testModel, modelCache, dataCache, &supported, &preparedModel);
+ if (checkEarlyTermination(supported)) return;
+ ASSERT_NE(preparedModel, nullptr);
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; },
+ get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+ // Check if prepareModelFromCache fails.
+ preparedModel = nullptr;
+ ErrorStatus status;
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::INVALID_ARGUMENT) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+}
+
+TEST_F(CompilationCachingTest, PrepareModelFromCacheInvalidNumCache) {
+ // Create test HIDL model and compile.
+ Model testModel = createTestModel();
+
+ // Save the compilation to cache.
+ {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ saveModelToCache(testModel, modelCache, dataCache, &supported);
+ if (checkEarlyTermination(supported)) return;
+ }
+
+ // Test with number of model cache files greater than mNumModelCache.
+ {
+ sp<IPreparedModel> preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ mModelCache.push_back({mTmpCache});
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mModelCache.pop_back();
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+
+ // Test with number of model cache files smaller than mNumModelCache.
+ if (mModelCache.size() > 0) {
+ sp<IPreparedModel> preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ auto tmp = mModelCache.back();
+ mModelCache.pop_back();
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mModelCache.push_back(tmp);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+
+ // Test with number of data cache files greater than mNumDataCache.
+ {
+ sp<IPreparedModel> preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ mDataCache.push_back({mTmpCache});
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mDataCache.pop_back();
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+
+ // Test with number of data cache files smaller than mNumDataCache.
+ if (mDataCache.size() > 0) {
+ sp<IPreparedModel> preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ auto tmp = mDataCache.back();
+ mDataCache.pop_back();
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mDataCache.push_back(tmp);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+}
+
TEST_F(CompilationCachingTest, SaveToCacheInvalidNumFd) {
// Create test HIDL model and compile.
Model testModel = createTestModel();
- sp<IPreparedModel> preparedModel = nullptr;
- generated_tests::PrepareModel(device, testModel, &preparedModel);
- // Terminate early if the driver cannot prepare the model.
- if (preparedModel == nullptr) return;
- // cache1 with invalid NumFd.
- {
+ // Go through each handle in model cache, test with NumFd greater than 1.
+ for (uint32_t i = 0; i < mNumModelCache; i++) {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ // Pass an invalid number of fds for handle i.
+ mModelCache[i].push_back(mTmpCache);
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mModelCache[i].pop_back();
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(testModel, modelCache, dataCache, &supported, &preparedModel);
+ if (checkEarlyTermination(supported)) return;
+ ASSERT_NE(preparedModel, nullptr);
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; },
+ get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+ // Check if prepareModelFromCache fails.
+ preparedModel = nullptr;
ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1, mCache3}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- if (status != ErrorStatus::GENERAL_FAILURE) {
- ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::INVALID_ARGUMENT) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
}
+ ASSERT_EQ(preparedModel, nullptr);
}
- // cache2 with invalid NumFd.
- {
+ // Go through each handle in model cache, test with NumFd equal to 0.
+ for (uint32_t i = 0; i < mNumModelCache; i++) {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ // Pass an invalid number of fds for handle i.
+ auto tmp = mModelCache[i].back();
+ mModelCache[i].pop_back();
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mModelCache[i].push_back(tmp);
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(testModel, modelCache, dataCache, &supported, &preparedModel);
+ if (checkEarlyTermination(supported)) return;
+ ASSERT_NE(preparedModel, nullptr);
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; },
+ get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+ // Check if prepareModelFromCache fails.
+ preparedModel = nullptr;
ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2, mCache3}, AccessMode::WRITE_ONLY, &cache2);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- if (status != ErrorStatus::GENERAL_FAILURE) {
- ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::INVALID_ARGUMENT) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
}
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+
+ // Go through each handle in data cache, test with NumFd greater than 1.
+ for (uint32_t i = 0; i < mNumDataCache; i++) {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ // Pass an invalid number of fds for handle i.
+ mDataCache[i].push_back(mTmpCache);
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mDataCache[i].pop_back();
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(testModel, modelCache, dataCache, &supported, &preparedModel);
+ if (checkEarlyTermination(supported)) return;
+ ASSERT_NE(preparedModel, nullptr);
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; },
+ get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+ // Check if prepareModelFromCache fails.
+ preparedModel = nullptr;
+ ErrorStatus status;
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::INVALID_ARGUMENT) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+
+ // Go through each handle in data cache, test with NumFd equal to 0.
+ for (uint32_t i = 0; i < mNumDataCache; i++) {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ // Pass an invalid number of fds for handle i.
+ auto tmp = mDataCache[i].back();
+ mDataCache[i].pop_back();
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mDataCache[i].push_back(tmp);
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(testModel, modelCache, dataCache, &supported, &preparedModel);
+ if (checkEarlyTermination(supported)) return;
+ ASSERT_NE(preparedModel, nullptr);
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; },
+ get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+ // Check if prepareModelFromCache fails.
+ preparedModel = nullptr;
+ ErrorStatus status;
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::INVALID_ARGUMENT) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
}
}
TEST_F(CompilationCachingTest, PrepareModelFromCacheInvalidNumFd) {
// Create test HIDL model and compile.
Model testModel = createTestModel();
- sp<IPreparedModel> preparedModel = nullptr;
- generated_tests::PrepareModel(device, testModel, &preparedModel);
- // Terminate early if the driver cannot prepare the model.
- if (preparedModel == nullptr) return;
// Save the compilation to cache.
{
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- if (status != ErrorStatus::GENERAL_FAILURE) {
- ASSERT_EQ(status, ErrorStatus::NONE);
- }
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ saveModelToCache(testModel, modelCache, dataCache, &supported);
+ if (checkEarlyTermination(supported)) return;
}
- // cache1 with invalid NumFd.
- {
- preparedModel = nullptr;
+ // Go through each handle in model cache, test with NumFd greater than 1.
+ for (uint32_t i = 0; i < mNumModelCache; i++) {
+ sp<IPreparedModel> preparedModel = nullptr;
ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1, mCache3}, AccessMode::READ_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
- prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ mModelCache[i].push_back(mTmpCache);
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mModelCache[i].pop_back();
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
if (status != ErrorStatus::GENERAL_FAILURE) {
ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
- ASSERT_EQ(preparedModel, nullptr);
}
+ ASSERT_EQ(preparedModel, nullptr);
}
- // cache2 with invalid NumFd.
- {
- preparedModel = nullptr;
+ // Go through each handle in model cache, test with NumFd equal to 0.
+ for (uint32_t i = 0; i < mNumModelCache; i++) {
+ sp<IPreparedModel> preparedModel = nullptr;
ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
- createCacheHandle({mCache2, mCache3}, AccessMode::READ_ONLY, &cache2);
- prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ auto tmp = mModelCache[i].back();
+ mModelCache[i].pop_back();
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mModelCache[i].push_back(tmp);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
if (status != ErrorStatus::GENERAL_FAILURE) {
ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
- ASSERT_EQ(preparedModel, nullptr);
}
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+
+ // Go through each handle in data cache, test with NumFd greater than 1.
+ for (uint32_t i = 0; i < mNumDataCache; i++) {
+ sp<IPreparedModel> preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ mDataCache[i].push_back(mTmpCache);
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mDataCache[i].pop_back();
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
+ }
+
+ // Go through each handle in data cache, test with NumFd equal to 0.
+ for (uint32_t i = 0; i < mNumDataCache; i++) {
+ sp<IPreparedModel> preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ auto tmp = mDataCache[i].back();
+ mDataCache[i].pop_back();
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ mDataCache[i].push_back(tmp);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::GENERAL_FAILURE) {
+ ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
}
}
TEST_F(CompilationCachingTest, SaveToCacheInvalidAccessMode) {
// Create test HIDL model and compile.
Model testModel = createTestModel();
- sp<IPreparedModel> preparedModel = nullptr;
- generated_tests::PrepareModel(device, testModel, &preparedModel);
- // Terminate early if the driver cannot prepare the model.
- if (preparedModel == nullptr) return;
+ std::vector<AccessMode> modelCacheMode(mNumModelCache, AccessMode::READ_WRITE);
+ std::vector<AccessMode> dataCacheMode(mNumDataCache, AccessMode::READ_WRITE);
- // cache1 with invalid access mode.
- {
+ // Go through each handle in model cache, test with invalid access mode.
+ for (uint32_t i = 0; i < mNumModelCache; i++) {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ modelCacheMode[i] = AccessMode::READ_ONLY;
+ createCacheHandles(mModelCache, modelCacheMode, &modelCache);
+ createCacheHandles(mDataCache, dataCacheMode, &dataCache);
+ modelCacheMode[i] = AccessMode::READ_WRITE;
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(testModel, modelCache, dataCache, &supported, &preparedModel);
+ if (checkEarlyTermination(supported)) return;
+ ASSERT_NE(preparedModel, nullptr);
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; },
+ get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+ // Check if prepareModelFromCache fails.
+ preparedModel = nullptr;
ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::INVALID_ARGUMENT) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
}
- // cache2 with invalid access mode.
- {
+ // Go through each handle in data cache, test with invalid access mode.
+ for (uint32_t i = 0; i < mNumDataCache; i++) {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ dataCacheMode[i] = AccessMode::READ_ONLY;
+ createCacheHandles(mModelCache, modelCacheMode, &modelCache);
+ createCacheHandles(mDataCache, dataCacheMode, &dataCache);
+ dataCacheMode[i] = AccessMode::READ_WRITE;
+ sp<IPreparedModel> preparedModel = nullptr;
+ saveModelToCache(testModel, modelCache, dataCache, &supported, &preparedModel);
+ if (checkEarlyTermination(supported)) return;
+ ASSERT_NE(preparedModel, nullptr);
+ // Execute and verify results.
+ generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; },
+ get_examples(),
+ testModel.relaxComputationFloat32toFloat16,
+ /*testDynamicOutputShape=*/false);
+ // Check if prepareModelFromCache fails.
+ preparedModel = nullptr;
ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ if (status != ErrorStatus::INVALID_ARGUMENT) {
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ }
+ ASSERT_EQ(preparedModel, nullptr);
}
}
TEST_F(CompilationCachingTest, PrepareModelFromCacheInvalidAccessMode) {
// Create test HIDL model and compile.
Model testModel = createTestModel();
- sp<IPreparedModel> preparedModel = nullptr;
- generated_tests::PrepareModel(device, testModel, &preparedModel);
- // Terminate early if the driver cannot prepare the model.
- if (preparedModel == nullptr) return;
+ std::vector<AccessMode> modelCacheMode(mNumModelCache, AccessMode::READ_WRITE);
+ std::vector<AccessMode> dataCacheMode(mNumDataCache, AccessMode::READ_WRITE);
// Save the compilation to cache.
{
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- if (status != ErrorStatus::GENERAL_FAILURE) {
- ASSERT_EQ(status, ErrorStatus::NONE);
- }
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ saveModelToCache(testModel, modelCache, dataCache, &supported);
+ if (checkEarlyTermination(supported)) return;
}
- // cache1 with invalid access mode.
- {
- preparedModel = nullptr;
+ // Go through each handle in model cache, test with invalid access mode.
+ for (uint32_t i = 0; i < mNumModelCache; i++) {
+ sp<IPreparedModel> preparedModel = nullptr;
ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
- prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ modelCacheMode[i] = AccessMode::WRITE_ONLY;
+ createCacheHandles(mModelCache, modelCacheMode, &modelCache);
+ createCacheHandles(mDataCache, dataCacheMode, &dataCache);
+ modelCacheMode[i] = AccessMode::READ_WRITE;
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
ASSERT_EQ(preparedModel, nullptr);
}
- // cache2 with invalid access mode.
- {
- preparedModel = nullptr;
+ // Go through each handle in data cache, test with invalid access mode.
+ for (uint32_t i = 0; i < mNumDataCache; i++) {
+ sp<IPreparedModel> preparedModel = nullptr;
ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ dataCacheMode[i] = AccessMode::WRITE_ONLY;
+ createCacheHandles(mModelCache, modelCacheMode, &modelCache);
+ createCacheHandles(mDataCache, dataCacheMode, &dataCache);
+ dataCacheMode[i] = AccessMode::READ_WRITE;
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
ASSERT_EQ(preparedModel, nullptr);
}
}
-TEST_F(CompilationCachingTest, SaveToCacheInvalidOffset) {
- // Create test HIDL model and compile.
- Model testModel = createTestModel();
- sp<IPreparedModel> preparedModel = nullptr;
- generated_tests::PrepareModel(device, testModel, &preparedModel);
- // Terminate early if the driver cannot prepare the model.
- if (preparedModel == nullptr) return;
-
- // cache1 with invalid file descriptor offset.
- {
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- uint8_t dummyByte = 0;
- // Advance offset by one byte.
- ASSERT_EQ(write(cache1.getNativeHandle()->data[0], &dummyByte, 1), 1);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
- }
-
- // cache2 with invalid file descriptor offset.
- {
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- uint8_t dummyByte = 0;
- // Advance offset by one byte.
- ASSERT_EQ(write(cache2.getNativeHandle()->data[0], &dummyByte, 1), 1);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
- }
-}
-
-TEST_F(CompilationCachingTest, SaveToCacheInvalidFileSize) {
- // Create test HIDL model and compile.
- Model testModel = createTestModel();
- sp<IPreparedModel> preparedModel = nullptr;
- generated_tests::PrepareModel(device, testModel, &preparedModel);
- // Terminate early if the driver cannot prepare the model.
- if (preparedModel == nullptr) return;
-
- // cache1 with invalid file size.
- {
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- uint8_t dummyByte = 0;
- // Write one byte and seek back to the beginning.
- ASSERT_EQ(write(cache1.getNativeHandle()->data[0], &dummyByte, 1), 1);
- ASSERT_EQ(lseek(cache1.getNativeHandle()->data[0], 0, SEEK_SET), 0);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
- }
-
- // cache2 with invalid file size.
- {
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- uint8_t dummyByte = 0;
- // Write one byte and seek back to the beginning.
- ASSERT_EQ(write(cache2.getNativeHandle()->data[0], &dummyByte, 1), 1);
- ASSERT_EQ(lseek(cache2.getNativeHandle()->data[0], 0, SEEK_SET), 0);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
- }
-}
-
class CompilationCachingSecurityTest : public CompilationCachingTest,
public ::testing::WithParamInterface<uint32_t> {
protected:
@@ -537,44 +888,44 @@
// Create test HIDL model and compile.
Model testModel = createTestModel();
- sp<IPreparedModel> preparedModel = nullptr;
- generated_tests::PrepareModel(device, testModel, &preparedModel);
- // Terminate early if the driver cannot prepare the model.
- if (preparedModel == nullptr) return;
- // Save the compilation to cache.
- {
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- if (checkEarlyTermination(status)) return;
- ASSERT_EQ(status, ErrorStatus::NONE);
- }
+ for (uint32_t i = 0; i < mNumModelCache; i++) {
+ // Save the compilation to cache.
+ {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ saveModelToCache(testModel, modelCache, dataCache, &supported);
+ if (checkEarlyTermination(supported)) return;
+ }
- // Randomly flip one single bit of the cache entry.
- FILE* pFile = fopen(mCache1.c_str(), "r+");
- ASSERT_EQ(fseek(pFile, 0, SEEK_END), 0);
- long int fileSize = ftell(pFile);
- ASSERT_GT(fileSize, 0);
- ASSERT_EQ(fseek(pFile, getRandomInt(0l, fileSize - 1), SEEK_SET), 0);
- int readByte = fgetc(pFile);
- ASSERT_NE(readByte, EOF);
- ASSERT_EQ(fseek(pFile, -1, SEEK_CUR), 0);
- ASSERT_NE(fputc(static_cast<uint8_t>(readByte) ^ (1U << getRandomInt(0, 7)), pFile), EOF);
- fclose(pFile);
+ // Randomly flip one single bit of the cache entry.
+ FILE* pFile = fopen(mModelCache[i][0].c_str(), "r+");
+ ASSERT_EQ(fseek(pFile, 0, SEEK_END), 0);
+ long int fileSize = ftell(pFile);
+ if (fileSize == 0) {
+ fclose(pFile);
+ continue;
+ }
+ ASSERT_EQ(fseek(pFile, getRandomInt(0l, fileSize - 1), SEEK_SET), 0);
+ int readByte = fgetc(pFile);
+ ASSERT_NE(readByte, EOF);
+ ASSERT_EQ(fseek(pFile, -1, SEEK_CUR), 0);
+ ASSERT_NE(fputc(static_cast<uint8_t>(readByte) ^ (1U << getRandomInt(0, 7)), pFile), EOF);
+ fclose(pFile);
- // Retrieve preparedModel from cache, expect failure.
- {
- preparedModel = nullptr;
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
- prepareModelFromCache(cache1, cache2, &preparedModel, &status);
- ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
- ASSERT_EQ(preparedModel, nullptr);
+ // Retrieve preparedModel from cache, expect failure.
+ {
+ sp<IPreparedModel> preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ ASSERT_EQ(preparedModel, nullptr);
+ }
}
}
@@ -583,40 +934,37 @@
// Create test HIDL model and compile.
Model testModel = createTestModel();
- sp<IPreparedModel> preparedModel = nullptr;
- generated_tests::PrepareModel(device, testModel, &preparedModel);
- // Terminate early if the driver cannot prepare the model.
- if (preparedModel == nullptr) return;
- // Save the compilation to cache.
- {
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- if (checkEarlyTermination(status)) return;
- ASSERT_EQ(status, ErrorStatus::NONE);
- }
+ for (uint32_t i = 0; i < mNumModelCache; i++) {
+ // Save the compilation to cache.
+ {
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ saveModelToCache(testModel, modelCache, dataCache, &supported);
+ if (checkEarlyTermination(supported)) return;
+ }
- // Randomly append bytes to the cache entry.
- FILE* pFile = fopen(mCache1.c_str(), "a");
- uint32_t appendLength = getRandomInt(1, 256);
- for (uint32_t i = 0; i < appendLength; i++) {
- ASSERT_NE(fputc(getRandomInt<uint8_t>(0, 255), pFile), EOF);
- }
- fclose(pFile);
+ // Randomly append bytes to the cache entry.
+ FILE* pFile = fopen(mModelCache[i][0].c_str(), "a");
+ uint32_t appendLength = getRandomInt(1, 256);
+ for (uint32_t i = 0; i < appendLength; i++) {
+ ASSERT_NE(fputc(getRandomInt<uint8_t>(0, 255), pFile), EOF);
+ }
+ fclose(pFile);
- // Retrieve preparedModel from cache, expect failure.
- {
- preparedModel = nullptr;
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
- prepareModelFromCache(cache1, cache2, &preparedModel, &status);
- ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
- ASSERT_EQ(preparedModel, nullptr);
+ // Retrieve preparedModel from cache, expect failure.
+ {
+ sp<IPreparedModel> preparedModel = nullptr;
+ ErrorStatus status;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+ ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+ ASSERT_EQ(preparedModel, nullptr);
+ }
}
}
@@ -625,20 +973,15 @@
// Create test HIDL model and compile.
Model testModel = createTestModel();
- sp<IPreparedModel> preparedModel = nullptr;
- generated_tests::PrepareModel(device, testModel, &preparedModel);
- // Terminate early if the driver cannot prepare the model.
- if (preparedModel == nullptr) return;
// Save the compilation to cache.
{
- ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
- saveModelToCache(preparedModel, cache1, cache2, &status);
- if (checkEarlyTermination(status)) return;
- ASSERT_EQ(status, ErrorStatus::NONE);
+ bool supported;
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ saveModelToCache(testModel, modelCache, dataCache, &supported);
+ if (checkEarlyTermination(supported)) return;
}
// Randomly flip one single bit in mToken.
@@ -647,12 +990,12 @@
// Retrieve the preparedModel from cache, expect failure.
{
- preparedModel = nullptr;
+ sp<IPreparedModel> preparedModel = nullptr;
ErrorStatus status;
- hidl_handle cache1, cache2;
- createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
- createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
- prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+ hidl_vec<hidl_handle> modelCache, dataCache;
+ createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+ createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+ prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
ASSERT_EQ(preparedModel, nullptr);
}
diff --git a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
index c2330b5..a0b6d9a 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
@@ -33,6 +33,7 @@
using ::android::hardware::neuralnetworks::V1_2::implementation::ExecutionCallback;
using ::android::hardware::neuralnetworks::V1_2::implementation::PreparedModelCallback;
+using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
///////////////////////// UTILITY FUNCTIONS /////////////////////////
@@ -54,7 +55,8 @@
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
ASSERT_NE(nullptr, preparedModelCallback.get());
Return<ErrorStatus> prepareLaunchStatus =
- device->prepareModel_1_2(model, preference, preparedModelCallback);
+ device->prepareModel_1_2(model, preference, hidl_vec<hidl_handle>(),
+ hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(prepareLaunchStatus));
@@ -506,9 +508,10 @@
}
}
}
- // BIDIRECTIONAL_SEQUENCE_RNN can have either on or two outputs
- // depending on a mergeOutputs parameter
- if (operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_RNN) {
+ // BIDIRECTIONAL_SEQUENCE_LSTM and BIDIRECTIONAL_SEQUENCE_RNN can have either one or two
+ // outputs depending on their mergeOutputs parameter.
+ if (operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_LSTM ||
+ operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_RNN) {
for (const size_t outOprand : operation.outputs) {
if (operand == outOprand) {
return true;
diff --git a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
index d411da4..870d017 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
@@ -37,6 +37,7 @@
using ::android::hardware::neuralnetworks::V1_2::implementation::ExecutionCallback;
using ::android::hardware::neuralnetworks::V1_2::implementation::PreparedModelCallback;
using ::android::hidl::memory::V1_0::IMemory;
+using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
using test_helper::for_all;
using test_helper::MixedTyped;
using test_helper::MixedTypedExample;
@@ -66,7 +67,8 @@
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
ASSERT_NE(nullptr, preparedModelCallback.get());
Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_2(
- model, ExecutionPreference::FAST_SINGLE_ANSWER, preparedModelCallback);
+ model, ExecutionPreference::FAST_SINGLE_ANSWER, hidl_vec<hidl_handle>(),
+ hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
@@ -153,8 +155,8 @@
SCOPED_TRACE(message + " [burst]");
// create burst
- std::unique_ptr<::android::nn::ExecutionBurstController> burst =
- ::android::nn::createExecutionBurstController(preparedModel, /*blocking=*/true);
+ std::shared_ptr<::android::nn::ExecutionBurstController> burst =
+ ::android::nn::ExecutionBurstController::create(preparedModel, /*blocking=*/true);
ASSERT_NE(nullptr, burst.get());
// create memory keys
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
index 76d8758..2093c25 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
@@ -758,3 +758,40 @@
{RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE}));
}
}
+
+/*
+ * Test IRadio.getDataRegistrationStateResponse_1_4() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_4, getDataRegistrationState_1_4) {
+ int rat;
+ serial = GetRandomSerialNumber();
+
+ Return<void> res = radio_v1_4->getDataRegistrationState(serial);
+ ASSERT_OK(res);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_4->rspInfo.type);
+ EXPECT_EQ(serial, radioRsp_v1_4->rspInfo.serial);
+
+ ALOGI("getDataRegistrationStateResponse_1_4, rspInfo.error = %s\n",
+ toString(radioRsp_v1_4->rspInfo.error).c_str());
+
+ ASSERT_TRUE(CheckAnyOfErrors(
+ radioRsp_v1_4->rspInfo.error,
+ {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::NOT_PROVISIONED}));
+
+ rat = radioRsp_v1_4->dataRegResp.base.rat;
+ /*
+ * - Expect Valid vopsinfo when device is on LTE
+ * - Expect empty vopsInfo when device is not on LTE
+ */
+ if (rat == ((int )::android::hardware::radio::V1_4::RadioTechnology::LTE)
+ || (rat == (int )::android::hardware::radio::V1_4::RadioTechnology::LTE_CA)) {
+
+ EXPECT_EQ(::android::hardware::radio::V1_4::DataRegStateResult::VopsInfo::hidl_discriminator
+ ::lteVopsInfo, radioRsp_v1_4->dataRegResp.vopsInfo.getDiscriminator());
+ } else {
+
+ EXPECT_EQ(::android::hardware::radio::V1_4::DataRegStateResult::VopsInfo::hidl_discriminator
+ ::noinit, radioRsp_v1_4->dataRegResp.vopsInfo.getDiscriminator());
+ }
+}
diff --git a/sensors/2.0/default/Android.bp b/sensors/2.0/default/Android.bp
index d05634b..05a34bb 100644
--- a/sensors/2.0/default/Android.bp
+++ b/sensors/2.0/default/Android.bp
@@ -23,7 +23,7 @@
"Sensor.cpp",
"Sensors.cpp",
],
- init_rc: ["android.hardware.sensors@2.0-service.rc"],
+ init_rc: ["android.hardware.sensors@2.0-service-mock.rc"],
shared_libs: [
"android.hardware.sensors@1.0",
"android.hardware.sensors@2.0",
diff --git a/sensors/2.0/default/android.hardware.sensors@2.0-service.rc b/sensors/2.0/default/android.hardware.sensors@2.0-service-mock.rc
similarity index 100%
rename from sensors/2.0/default/android.hardware.sensors@2.0-service.rc
rename to sensors/2.0/default/android.hardware.sensors@2.0-service-mock.rc
diff --git a/thermal/2.0/default/Thermal.cpp b/thermal/2.0/default/Thermal.cpp
index 0ef4b63..bbbecb8 100644
--- a/thermal/2.0/default/Thermal.cpp
+++ b/thermal/2.0/default/Thermal.cpp
@@ -38,46 +38,47 @@
std::set<sp<IThermalChangedCallback>> gCallbacks;
static const Temperature_1_0 kTemp_1_0 = {
- .type = static_cast<::android::hardware::thermal::V1_0::TemperatureType>(TemperatureType::CPU),
- .name = "test temperature sensor",
- .currentValue = 98.6,
- .throttlingThreshold = 58,
- .shutdownThreshold = 60.0,
- .vrThrottlingThreshold = 59.0,
+ .type = static_cast<::android::hardware::thermal::V1_0::TemperatureType>(
+ TemperatureType::SKIN),
+ .name = "test temperature sensor",
+ .currentValue = 30.8,
+ .throttlingThreshold = 48.0,
+ .shutdownThreshold = 60.0,
+ .vrThrottlingThreshold = 49.0,
};
static const Temperature_2_0 kTemp_2_0 = {
- .type = TemperatureType::SKIN,
- .name = "test temperature sensor",
- .value = 98.6,
- .throttlingStatus = ThrottlingSeverity::CRITICAL,
+ .type = TemperatureType::SKIN,
+ .name = "test temperature sensor",
+ .value = 30.8,
+ .throttlingStatus = ThrottlingSeverity::NONE,
};
static const TemperatureThreshold kTempThreshold = {
- .type = TemperatureType::SKIN,
- .name = "test temperature sensor",
- .hotThrottlingThresholds = {{NAN, NAN, NAN, NAN, NAN, NAN, NAN}},
- .coldThrottlingThresholds = {{NAN, NAN, NAN, NAN, NAN, NAN, NAN}},
- .vrThrottlingThreshold = NAN,
+ .type = TemperatureType::SKIN,
+ .name = "test temperature sensor",
+ .hotThrottlingThresholds = {{NAN, NAN, NAN, 48.0, NAN, NAN, 60.0}},
+ .coldThrottlingThresholds = {{NAN, NAN, NAN, NAN, NAN, NAN, NAN}},
+ .vrThrottlingThreshold = 49.0,
};
static const CoolingDevice_1_0 kCooling_1_0 = {
- .type = ::android::hardware::thermal::V1_0::CoolingType::FAN_RPM,
- .name = "test cooling device",
- .currentValue = 100.0,
+ .type = ::android::hardware::thermal::V1_0::CoolingType::FAN_RPM,
+ .name = "test cooling device",
+ .currentValue = 100.0,
};
static const CoolingDevice_2_0 kCooling_2_0 = {
- .type = CoolingType::CPU,
- .name = "test cooling device",
- .value = 1,
+ .type = CoolingType::FAN,
+ .name = "test cooling device",
+ .value = 100,
};
static const CpuUsage kCpuUsage = {
- .name = "cpu_name",
- .active = 0,
- .total = 0,
- .isOnline = true,
+ .name = "cpu_name",
+ .active = 0,
+ .total = 0,
+ .isOnline = true,
};
// Methods from ::android::hardware::thermal::V1_0::IThermal follow.
diff --git a/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp b/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp
index cc2f3df..d0f2e84 100644
--- a/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp
+++ b/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp
@@ -192,6 +192,9 @@
} else {
EXPECT_NE(ThermalStatusCode::SUCCESS, status.code);
}
+ for (int i = 0; i < temperatures.size(); ++i) {
+ EXPECT_LT(0u, temperatures[i].name.size());
+ }
});
auto types = hidl_enum_range<TemperatureType>();
for (const auto& type : types) {
@@ -204,6 +207,7 @@
}
for (int i = 0; i < temperatures.size(); ++i) {
EXPECT_EQ(type, temperatures[i].type);
+ EXPECT_LT(0u, temperatures[i].name.size());
}
});
}
@@ -246,6 +250,9 @@
} else {
EXPECT_NE(ThermalStatusCode::SUCCESS, status.code);
}
+ for (int i = 0; i < cooling_devices.size(); ++i) {
+ EXPECT_LT(0u, cooling_devices[i].name.size());
+ }
});
for (int i = 0; i <= static_cast<int>(CoolingType::COMPONENT); ++i) {
auto type = static_cast<CoolingType>(i);
@@ -258,6 +265,7 @@
}
for (int i = 0; i < cooling_devices.size(); ++i) {
EXPECT_EQ(type, cooling_devices[i].type);
+ EXPECT_LT(0u, cooling_devices[i].name.size());
}
});
}
diff --git a/vibrator/1.3/Android.bp b/vibrator/1.3/Android.bp
index 28370d6..a2ff784 100644
--- a/vibrator/1.3/Android.bp
+++ b/vibrator/1.3/Android.bp
@@ -8,6 +8,7 @@
},
srcs: [
"IVibrator.hal",
+ "types.hal",
],
interfaces: [
"android.hardware.vibrator@1.0",
diff --git a/vibrator/1.3/IVibrator.hal b/vibrator/1.3/IVibrator.hal
index 01c2801..1c870ee 100644
--- a/vibrator/1.3/IVibrator.hal
+++ b/vibrator/1.3/IVibrator.hal
@@ -16,6 +16,7 @@
package android.hardware.vibrator@1.3;
+import @1.0::EffectStrength;
import @1.0::Status;
import @1.2::IVibrator;
@@ -41,4 +42,18 @@
* not supported by the device.
*/
setExternalControl(bool enabled) generates (Status status);
+
+ /**
+ * Fire off a predefined haptic event.
+ *
+ * @param event The type of haptic event to trigger.
+ * @return status Whether the effect was successfully performed or not. Must
+ * return Status::UNSUPPORTED_OPERATION if the effect is not supported.
+ * @return lengthMs The length of time the event is expected to take in
+ * milliseconds. This doesn't need to be perfectly accurate, but should be a reasonable
+ * approximation. Should be a positive, non-zero value if the returned status is Status::OK,
+ * and set to 0 otherwise.
+ */
+ perform_1_3(Effect effect, EffectStrength strength)
+ generates (Status status, uint32_t lengthMs);
};
diff --git a/vibrator/1.3/example/OWNERS b/vibrator/1.3/example/OWNERS
new file mode 100644
index 0000000..4b34968
--- /dev/null
+++ b/vibrator/1.3/example/OWNERS
@@ -0,0 +1,2 @@
+eliptus@google.com
+michaelwr@google.com
diff --git a/vibrator/1.3/example/Vibrator.cpp b/vibrator/1.3/example/Vibrator.cpp
index bb9a057..0cb37e6 100644
--- a/vibrator/1.3/example/Vibrator.cpp
+++ b/vibrator/1.3/example/Vibrator.cpp
@@ -74,22 +74,9 @@
// Methods from ::android::hardware::vibrator::V1_2::IVibrator follow.
-Return<void> Vibrator::perform_1_2(Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
- uint8_t amplitude;
- uint32_t ms;
- Status status;
-
- ALOGI("Perform: Effect %s\n", effectToName(effect));
-
- amplitude = strengthToAmplitude(strength);
- setAmplitude(amplitude);
-
- ms = effectToMs(effect);
- status = activate(ms);
-
- _hidl_cb(status, ms);
-
- return Void();
+Return<void> Vibrator::perform_1_2(V1_2::Effect effect, EffectStrength strength,
+ perform_cb _hidl_cb) {
+ return perform_1_3(static_cast<V1_3::Effect>(effect), strength, _hidl_cb);
}
// Methods from ::android::hardware::vibrator::V1_3::IVibrator follow.
@@ -110,6 +97,32 @@
}
}
+Return<void> Vibrator::perform_1_3(Effect effect, EffectStrength strength, perform_cb _hidl_cb) {
+ uint8_t amplitude;
+ uint32_t ms;
+ Status status = Status::OK;
+
+ ALOGI("Perform: Effect %s\n", effectToName(effect).c_str());
+
+ amplitude = strengthToAmplitude(strength, &status);
+ if (status != Status::OK) {
+ _hidl_cb(status, 0);
+ return Void();
+ }
+ setAmplitude(amplitude);
+
+ ms = effectToMs(effect, &status);
+ if (status != Status::OK) {
+ _hidl_cb(status, 0);
+ return Void();
+ }
+ status = activate(ms);
+
+ _hidl_cb(status, ms);
+
+ return Void();
+}
+
// Private methods follow.
Status Vibrator::enable(bool enabled) {
@@ -173,17 +186,18 @@
static_cast<Vibrator*>(sigval.sival_ptr)->timeout();
}
-const char* Vibrator::effectToName(Effect effect) {
- return toString(effect).c_str();
+const std::string Vibrator::effectToName(Effect effect) {
+ return toString(effect);
}
-uint32_t Vibrator::effectToMs(Effect effect) {
+uint32_t Vibrator::effectToMs(Effect effect, Status* status) {
switch (effect) {
case Effect::CLICK:
return 10;
case Effect::DOUBLE_CLICK:
return 15;
case Effect::TICK:
+ case Effect::TEXTURE_TICK:
return 5;
case Effect::THUD:
return 5;
@@ -222,9 +236,11 @@
case Effect::RINGTONE_15:
return 30000;
}
+ *status = Status::UNSUPPORTED_OPERATION;
+ return 0;
}
-uint8_t Vibrator::strengthToAmplitude(EffectStrength strength) {
+uint8_t Vibrator::strengthToAmplitude(EffectStrength strength, Status* status) {
switch (strength) {
case EffectStrength::LIGHT:
return 128;
@@ -233,6 +249,8 @@
case EffectStrength::STRONG:
return 255;
}
+ *status = Status::UNSUPPORTED_OPERATION;
+ return 0;
}
} // namespace implementation
diff --git a/vibrator/1.3/example/Vibrator.h b/vibrator/1.3/example/Vibrator.h
index a931b63..64e8e1b 100644
--- a/vibrator/1.3/example/Vibrator.h
+++ b/vibrator/1.3/example/Vibrator.h
@@ -27,7 +27,6 @@
using android::hardware::vibrator::V1_0::EffectStrength;
using android::hardware::vibrator::V1_0::Status;
-using android::hardware::vibrator::V1_2::Effect;
class Vibrator : public IVibrator {
public:
@@ -46,11 +45,13 @@
perform_cb _hidl_cb) override;
// Methods from ::android::hardware::vibrator::V1_2::IVibrator follow.
- Return<void> perform_1_2(Effect effect, EffectStrength strength, perform_cb _hidl_cb) override;
+ Return<void> perform_1_2(V1_2::Effect effect, EffectStrength strength,
+ perform_cb _hidl_cb) override;
// Methods from ::android::hardware::vibrator::V1_3::IVibrator follow.
Return<bool> supportsExternalControl() override;
Return<Status> setExternalControl(bool enabled) override;
+ Return<void> perform_1_3(Effect effect, EffectStrength strength, perform_cb _hidl_cb) override;
private:
Status enable(bool enabled);
@@ -58,9 +59,9 @@
void timeout();
static void timerCallback(union sigval sigval);
- static const char* effectToName(Effect effect);
- static uint32_t effectToMs(Effect effect);
- static uint8_t strengthToAmplitude(EffectStrength strength);
+ static const std::string effectToName(Effect effect);
+ static uint32_t effectToMs(Effect effect, Status* status);
+ static uint8_t strengthToAmplitude(EffectStrength strength, Status* status);
private:
bool mEnabled{false};
diff --git a/vibrator/1.3/types.hal b/vibrator/1.3/types.hal
new file mode 100644
index 0000000..ceb62a5
--- /dev/null
+++ b/vibrator/1.3/types.hal
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.vibrator@1.3;
+
+import @1.2::Effect;
+
+enum Effect : @1.2::Effect {
+ /**
+ * A soft tick effect meant to be played as a texture.
+ *
+ * A soft, short sensation like the tick of a clock. Unlike regular effects, texture effects
+ * are expected to be played multiple times in quick succession, replicating a specific
+ * texture to the user as a form of haptic feedback.
+ */
+ TEXTURE_TICK
+};
diff --git a/vibrator/1.3/vts/functional/VtsHalVibratorV1_3TargetTest.cpp b/vibrator/1.3/vts/functional/VtsHalVibratorV1_3TargetTest.cpp
index a67d1dc..818f9c7 100644
--- a/vibrator/1.3/vts/functional/VtsHalVibratorV1_3TargetTest.cpp
+++ b/vibrator/1.3/vts/functional/VtsHalVibratorV1_3TargetTest.cpp
@@ -24,9 +24,16 @@
#include <unistd.h>
using ::android::sp;
+using ::android::hardware::hidl_enum_range;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::vibrator::V1_0::EffectStrength;
using ::android::hardware::vibrator::V1_0::Status;
+using ::android::hardware::vibrator::V1_3::Effect;
using ::android::hardware::vibrator::V1_3::IVibrator;
+#define EXPECT_OK(ret) ASSERT_TRUE((ret).isOk())
+
// Test environment for Vibrator HIDL HAL.
class VibratorHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
public:
@@ -71,6 +78,74 @@
}
}
+static void validatePerformEffectUnsupportedOperation(Status status, uint32_t lengthMs) {
+ ASSERT_EQ(Status::UNSUPPORTED_OPERATION, status);
+ ASSERT_EQ(static_cast<uint32_t>(0), lengthMs)
+ << "Effects that return UNSUPPORTED_OPERATION must have a duration of zero";
+}
+
+static void validatePerformEffect(Status status, uint32_t lengthMs) {
+ ASSERT_TRUE(status == Status::OK || status == Status::UNSUPPORTED_OPERATION);
+ if (status == Status::OK) {
+ ASSERT_LT(static_cast<uint32_t>(0), lengthMs)
+ << "Effects that return OK must return a positive duration";
+ } else {
+ validatePerformEffectUnsupportedOperation(status, lengthMs);
+ }
+}
+
+/*
+ * Test to make sure effects within the valid range return are either supported and return OK with
+ * a valid duration, or are unsupported and return UNSUPPORTED_OPERATION with a duration of 0.
+ */
+TEST_F(VibratorHidlTest_1_3, PerformEffect_1_3) {
+ for (const auto& effect : hidl_enum_range<Effect>()) {
+ for (const auto& strength : hidl_enum_range<EffectStrength>()) {
+ EXPECT_OK(vibrator->perform_1_3(effect, strength, validatePerformEffect));
+ }
+ }
+}
+
+/*
+ * Test to make sure effect values above the valid range are rejected.
+ */
+TEST_F(VibratorHidlTest_1_3, PerformEffect_1_3_BadEffects_AboveValidRange) {
+ Effect effect = *std::prev(hidl_enum_range<Effect>().end());
+ Effect badEffect = static_cast<Effect>(static_cast<int32_t>(effect) + 1);
+ EXPECT_OK(vibrator->perform_1_3(badEffect, EffectStrength::LIGHT,
+ validatePerformEffectUnsupportedOperation));
+}
+
+/*
+ * Test to make sure effect values below the valid range are rejected.
+ */
+TEST_F(VibratorHidlTest_1_3, PerformEffect_1_3_BadEffects_BelowValidRange) {
+ Effect effect = *hidl_enum_range<Effect>().begin();
+ Effect badEffect = static_cast<Effect>(static_cast<int32_t>(effect) - 1);
+ EXPECT_OK(vibrator->perform_1_3(badEffect, EffectStrength::LIGHT,
+ validatePerformEffectUnsupportedOperation));
+}
+
+/*
+ * Test to make sure strength values above the valid range are rejected.
+ */
+TEST_F(VibratorHidlTest_1_3, PerformEffect_1_3_BadStrength_AboveValidRange) {
+ EffectStrength strength = *std::prev(hidl_enum_range<EffectStrength>().end());
+ EffectStrength badStrength = static_cast<EffectStrength>(static_cast<int32_t>(strength) + 1);
+ EXPECT_OK(vibrator->perform_1_3(Effect::THUD, badStrength,
+ validatePerformEffectUnsupportedOperation));
+}
+
+/*
+ * Test to make sure strength values below the valid range are rejected.
+ */
+TEST_F(VibratorHidlTest_1_3, PerformEffect_1_3_BadStrength_BelowValidRange) {
+ EffectStrength strength = *hidl_enum_range<EffectStrength>().begin();
+ EffectStrength badStrength = static_cast<EffectStrength>(static_cast<int32_t>(strength) - 1);
+ EXPECT_OK(vibrator->perform_1_3(Effect::THUD, badStrength,
+ validatePerformEffectUnsupportedOperation));
+}
+
int main(int argc, char** argv) {
::testing::AddGlobalTestEnvironment(VibratorHidlEnvironment::Instance());
::testing::InitGoogleTest(&argc, argv);
diff --git a/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp
index 72cafd1..1b7e821 100644
--- a/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp
+++ b/wifi/1.0/vts/functional/wifi_chip_hidl_test.cpp
@@ -361,8 +361,8 @@
/*
* CreateApIface
- * Configures the chip in AP mode and ensures that only 1 iface creation
- * succeeds. The 2nd iface creation should be rejected.
+ * Configures the chip in AP mode and ensures that at least 1 iface creation
+ * succeeds.
*/
TEST_F(WifiChipHidlTest, CreateApIface) {
if (!gEnv->isSoftApOn) return;
@@ -371,8 +371,6 @@
sp<IWifiApIface> iface;
EXPECT_EQ(WifiStatusCode::SUCCESS, createApIface(&iface));
EXPECT_NE(nullptr, iface.get());
-
- EXPECT_EQ(WifiStatusCode::ERROR_NOT_AVAILABLE, createApIface(&iface));
}
/*
@@ -460,8 +458,8 @@
/*
* CreateNanIface
- * Configures the chip in NAN mode and ensures that only 1 iface creation
- * succeeds. The 2nd iface creation should be rejected.
+ * Configures the chip in NAN mode and ensures that at least 1 iface creation
+ * succeeds.
*/
TEST_F(WifiChipHidlTest, CreateNanIface) {
if (!gEnv->isNanOn) return;
@@ -470,8 +468,6 @@
sp<IWifiNanIface> iface;
ASSERT_EQ(WifiStatusCode::SUCCESS, createNanIface(&iface));
EXPECT_NE(nullptr, iface.get());
-
- EXPECT_EQ(WifiStatusCode::ERROR_NOT_AVAILABLE, createNanIface(&iface));
}
/*
@@ -560,8 +556,8 @@
/*
* CreateP2pIface
- * Configures the chip in P2P mode and ensures that only 1 iface creation
- * succeeds. The 2nd iface creation should be rejected.
+ * Configures the chip in P2P mode and ensures that at least 1 iface creation
+ * succeeds.
*/
TEST_F(WifiChipHidlTest, CreateP2pIface) {
configureChipForIfaceType(IfaceType::P2P, true);
@@ -569,8 +565,6 @@
sp<IWifiP2pIface> iface;
EXPECT_EQ(WifiStatusCode::SUCCESS, createP2pIface(&iface));
EXPECT_NE(nullptr, iface.get());
-
- EXPECT_EQ(WifiStatusCode::ERROR_NOT_AVAILABLE, createP2pIface(&iface));
}
/*
@@ -655,8 +649,8 @@
/*
* CreateStaIface
- * Configures the chip in STA mode and ensures that only 1 iface creation
- * succeeds. The 2nd iface creation should be rejected.
+ * Configures the chip in STA mode and ensures that at least 1 iface creation
+ * succeeds.
*/
TEST_F(WifiChipHidlTest, CreateStaIface) {
configureChipForIfaceType(IfaceType::STA, true);
@@ -664,8 +658,6 @@
sp<IWifiStaIface> iface;
EXPECT_EQ(WifiStatusCode::SUCCESS, createStaIface(&iface));
EXPECT_NE(nullptr, iface.get());
-
- EXPECT_EQ(WifiStatusCode::ERROR_NOT_AVAILABLE, createStaIface(&iface));
}
/*
diff --git a/wifi/1.3/IWifiChip.hal b/wifi/1.3/IWifiChip.hal
index fc6dbac..72cee89 100644
--- a/wifi/1.3/IWifiChip.hal
+++ b/wifi/1.3/IWifiChip.hal
@@ -65,10 +65,14 @@
/**
* API to set the wifi latency mode
*
- * Latency mode determines whether or not to optimize for reducing wifi
- * latency as a tradeoff with other wifi functionality such as scanning,
- * roaming, etc. This optimization is suitable for some applications such
- * as gaming and virtual reality applications.
+ * The latency mode is a hint to the HAL to enable or disable Wi-Fi latency
+ * optimization. The optimization should be enabled if the mode is set to |LOW|
+ * and should be disabled if the mode is set to |NORMAL|.
+ * Wi-Fi latency optimization may trade-off latency against other Wi-Fi
+ * functionality such as scanning, roaming, etc. but it should not result in
+ * completely halting this functionality.
+ *
+ * The low latency mode targets applications such as gaming and virtual reality.
*/
setLatencyMode(LatencyMode mode) generates (WifiStatus status);
diff --git a/wifi/1.3/default/tests/mock_wifi_legacy_hal.h b/wifi/1.3/default/tests/mock_wifi_legacy_hal.h
index deb3a5a..53fa8d6 100644
--- a/wifi/1.3/default/tests/mock_wifi_legacy_hal.h
+++ b/wifi/1.3/default/tests/mock_wifi_legacy_hal.h
@@ -39,6 +39,16 @@
MOCK_METHOD2(registerRadioModeChangeCallbackHandler,
wifi_error(const std::string&,
const on_radio_mode_change_callback&));
+ MOCK_METHOD1(getFirmwareVersion, std::pair<wifi_error, std::string>(
+ const std::string& iface_name));
+ MOCK_METHOD1(getDriverVersion, std::pair<wifi_error, std::string>(
+ const std::string& iface_name));
+
+ MOCK_METHOD2(selectTxPowerScenario,
+ wifi_error(const std::string& iface_name,
+ wifi_power_scenario scenario));
+ MOCK_METHOD1(resetTxPowerScenario,
+ wifi_error(const std::string& iface_name));
MOCK_METHOD2(nanRegisterCallbackHandlers,
wifi_error(const std::string&, const NanCallbackHandlers&));
MOCK_METHOD2(nanDisableRequest,
diff --git a/wifi/1.3/default/tests/wifi_chip_unit_tests.cpp b/wifi/1.3/default/tests/wifi_chip_unit_tests.cpp
index 134563c..7928328 100644
--- a/wifi/1.3/default/tests/wifi_chip_unit_tests.cpp
+++ b/wifi/1.3/default/tests/wifi_chip_unit_tests.cpp
@@ -122,7 +122,7 @@
void setup_MultiIfaceCombination() {
// clang-format off
const hidl_vec<V1_0::IWifiChip::ChipIfaceCombination> combinations = {
- {{{{IfaceType::STA}, 3}}}
+ {{{{IfaceType::STA}, 3}, {{IfaceType::AP}, 1}}}
};
const std::vector<V1_0::IWifiChip::ChipMode> modes = {
{feature_flags::chip_mode_ids::kV3, combinations}
@@ -261,6 +261,17 @@
return success;
}
+ sp<WifiChip> chip_;
+ ChipId chip_id_ = kFakeChipId;
+ std::shared_ptr<NiceMock<legacy_hal::MockWifiLegacyHal>> legacy_hal_{
+ new NiceMock<legacy_hal::MockWifiLegacyHal>};
+ std::shared_ptr<NiceMock<mode_controller::MockWifiModeController>>
+ mode_controller_{new NiceMock<mode_controller::MockWifiModeController>};
+ std::shared_ptr<NiceMock<iface_util::MockWifiIfaceUtil>> iface_util_{
+ new NiceMock<iface_util::MockWifiIfaceUtil>};
+ std::shared_ptr<NiceMock<feature_flags::MockWifiFeatureFlags>>
+ feature_flags_{new NiceMock<feature_flags::MockWifiFeatureFlags>};
+
public:
void SetUp() override {
chip_ = new WifiChip(chip_id_, legacy_hal_, mode_controller_,
@@ -272,17 +283,12 @@
.WillRepeatedly(testing::Return(legacy_hal::WIFI_SUCCESS));
}
- private:
- sp<WifiChip> chip_;
- ChipId chip_id_ = kFakeChipId;
- std::shared_ptr<NiceMock<legacy_hal::MockWifiLegacyHal>> legacy_hal_{
- new NiceMock<legacy_hal::MockWifiLegacyHal>};
- std::shared_ptr<NiceMock<mode_controller::MockWifiModeController>>
- mode_controller_{new NiceMock<mode_controller::MockWifiModeController>};
- std::shared_ptr<NiceMock<iface_util::MockWifiIfaceUtil>> iface_util_{
- new NiceMock<iface_util::MockWifiIfaceUtil>};
- std::shared_ptr<NiceMock<feature_flags::MockWifiFeatureFlags>>
- feature_flags_{new NiceMock<feature_flags::MockWifiFeatureFlags>};
+ void TearDown() override {
+ // Restore default system iface names (This should ideally be using a
+ // mock).
+ property_set("wifi.interface", "wlan0");
+ property_set("wifi.concurrent.interface", "wlan1");
+ }
};
////////// V1 Iface Combinations ////////////
@@ -300,7 +306,7 @@
TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateSta_ShouldSucceed) {
findModeAndConfigureForIfaceType(IfaceType::STA);
- ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
}
TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateP2p_ShouldSucceed) {
@@ -326,7 +332,7 @@
TEST_F(WifiChipV1IfaceCombinationTest, ApMode_CreateAp_ShouldSucceed) {
findModeAndConfigureForIfaceType(IfaceType::AP);
- ASSERT_FALSE(createIface(IfaceType::AP).empty());
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan0");
}
TEST_F(WifiChipV1IfaceCombinationTest, ApMode_CreateSta_ShouldFail) {
@@ -359,7 +365,7 @@
TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateSta_ShouldSucceed) {
findModeAndConfigureForIfaceType(IfaceType::STA);
- ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
}
TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateP2p_ShouldSucceed) {
@@ -427,7 +433,7 @@
TEST_F(WifiChipV1_AwareIfaceCombinationTest, ApMode_CreateAp_ShouldSucceed) {
findModeAndConfigureForIfaceType(IfaceType::AP);
- ASSERT_FALSE(createIface(IfaceType::AP).empty());
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan0");
}
TEST_F(WifiChipV1_AwareIfaceCombinationTest, ApMode_CreateSta_ShouldFail) {
@@ -445,18 +451,18 @@
ASSERT_TRUE(createIface(IfaceType::NAN).empty());
}
-TEST_F(WifiChipV1IfaceCombinationTest, RttControllerFlowStaModeNoSta) {
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, RttControllerFlowStaModeNoSta) {
findModeAndConfigureForIfaceType(IfaceType::STA);
ASSERT_TRUE(createRttController());
}
-TEST_F(WifiChipV1IfaceCombinationTest, RttControllerFlowStaModeWithSta) {
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, RttControllerFlowStaModeWithSta) {
findModeAndConfigureForIfaceType(IfaceType::STA);
ASSERT_FALSE(createIface(IfaceType::STA).empty());
ASSERT_TRUE(createRttController());
}
-TEST_F(WifiChipV1IfaceCombinationTest, RttControllerFlowApToSta) {
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, RttControllerFlowApToSta) {
findModeAndConfigureForIfaceType(IfaceType::AP);
const auto ap_iface_name = createIface(IfaceType::AP);
ASSERT_FALSE(ap_iface_name.empty());
@@ -468,6 +474,30 @@
ASSERT_TRUE(createRttController());
}
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, SelectTxScenarioWithOnlySta) {
+ findModeAndConfigureForIfaceType(IfaceType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ EXPECT_CALL(*legacy_hal_, selectTxPowerScenario("wlan0", testing::_))
+ .WillOnce(testing::Return(legacy_hal::WIFI_SUCCESS));
+ chip_->selectTxPowerScenario_1_2(
+ V1_2::IWifiChip::TxPowerScenario::ON_HEAD_CELL_OFF,
+ [](const WifiStatus& status) {
+ ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+ });
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, SelectTxScenarioWithOnlyAp) {
+ findModeAndConfigureForIfaceType(IfaceType::AP);
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan0");
+ EXPECT_CALL(*legacy_hal_, selectTxPowerScenario("wlan0", testing::_))
+ .WillOnce(testing::Return(legacy_hal::WIFI_SUCCESS));
+ chip_->selectTxPowerScenario_1_2(
+ V1_2::IWifiChip::TxPowerScenario::ON_HEAD_CELL_OFF,
+ [](const WifiStatus& status) {
+ ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+ });
+}
+
////////// V2 + Aware Iface Combinations ////////////
// Mode 1 - STA + STA/AP
// - STA + P2P/NAN
@@ -483,7 +513,7 @@
TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateSta_ShouldSucceed) {
findModeAndConfigureForIfaceType(IfaceType::STA);
- ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
}
TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateP2p_ShouldSucceed) {
@@ -498,19 +528,25 @@
TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateAp_ShouldSucceed) {
findModeAndConfigureForIfaceType(IfaceType::STA);
- ASSERT_FALSE(createIface(IfaceType::AP).empty());
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan1");
}
TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaSta_ShouldFail) {
findModeAndConfigureForIfaceType(IfaceType::AP);
- ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
ASSERT_TRUE(createIface(IfaceType::STA).empty());
}
TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaAp_ShouldSucceed) {
findModeAndConfigureForIfaceType(IfaceType::AP);
- ASSERT_FALSE(createIface(IfaceType::AP).empty());
- ASSERT_FALSE(createIface(IfaceType::STA).empty());
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan1");
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateApSta_ShouldSucceed) {
+ findModeAndConfigureForIfaceType(IfaceType::AP);
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan1");
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
}
TEST_F(WifiChipV2_AwareIfaceCombinationTest,
@@ -640,6 +676,30 @@
ASSERT_TRUE(createRttController());
}
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, SelectTxScenarioWithOnlySta) {
+ findModeAndConfigureForIfaceType(IfaceType::STA);
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ EXPECT_CALL(*legacy_hal_, selectTxPowerScenario("wlan0", testing::_))
+ .WillOnce(testing::Return(legacy_hal::WIFI_SUCCESS));
+ chip_->selectTxPowerScenario_1_2(
+ V1_2::IWifiChip::TxPowerScenario::ON_HEAD_CELL_OFF,
+ [](const WifiStatus& status) {
+ ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+ });
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, SelectTxScenarioWithOnlyAp) {
+ findModeAndConfigureForIfaceType(IfaceType::AP);
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan1");
+ EXPECT_CALL(*legacy_hal_, selectTxPowerScenario("wlan1", testing::_))
+ .WillOnce(testing::Return(legacy_hal::WIFI_SUCCESS));
+ chip_->selectTxPowerScenario_1_2(
+ V1_2::IWifiChip::TxPowerScenario::ON_HEAD_CELL_OFF,
+ [](const WifiStatus& status) {
+ ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+ });
+}
+
////////// V1 Iface Combinations when AP creation is disabled //////////
class WifiChipV1_AwareDisabledApIfaceCombinationTest : public WifiChipTest {
public:
@@ -707,8 +767,8 @@
property_set("wifi.interface", "bad0");
property_set("wifi.concurrent.interface", "bad1");
findModeAndConfigureForIfaceType(IfaceType::STA);
- ASSERT_EQ(createIface(IfaceType::STA), "test0");
- ASSERT_EQ(createIface(IfaceType::STA), "test1");
+ ASSERT_EQ(createIface(IfaceType::STA), "bad0");
+ ASSERT_EQ(createIface(IfaceType::STA), "bad1");
ASSERT_EQ(createIface(IfaceType::STA), "test2");
}
@@ -724,6 +784,16 @@
ASSERT_EQ(createIface(IfaceType::STA), "wlan2");
}
+TEST_F(WifiChip_MultiIfaceTest, CreateApStartsWithIdx1) {
+ findModeAndConfigureForIfaceType(IfaceType::STA);
+ // First AP will be slotted to wlan1.
+ ASSERT_EQ(createIface(IfaceType::AP), "wlan1");
+ // First STA will be slotted to wlan0.
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan0");
+ // All further STA will be slotted to the remaining free indices.
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan2");
+ ASSERT_EQ(createIface(IfaceType::STA), "wlan3");
+}
} // namespace implementation
} // namespace V1_3
} // namespace wifi
diff --git a/wifi/1.3/default/wifi_chip.cpp b/wifi/1.3/default/wifi_chip.cpp
index 3697d50..25eb289 100644
--- a/wifi/1.3/default/wifi_chip.cpp
+++ b/wifi/1.3/default/wifi_chip.cpp
@@ -41,7 +41,9 @@
constexpr uint32_t kMaxRingBufferFileAgeSeconds = 60 * 60 * 10;
constexpr uint32_t kMaxRingBufferFileNum = 20;
constexpr char kTombstoneFolderPath[] = "/data/vendor/tombstones/wifi/";
-constexpr unsigned kMaxWlanIfaces = 100;
+constexpr char kActiveWlanIfaceNameProperty[] = "wifi.active.interface";
+constexpr char kNoActiveWlanIfaceNamePropertyValue[] = "";
+constexpr unsigned kMaxWlanIfaces = 5;
template <typename Iface>
void invalidateAndClear(std::vector<sp<Iface>>& ifaces, sp<Iface> iface) {
@@ -105,6 +107,13 @@
return buffer.data();
}
+void setActiveWlanIfaceNameProperty(const std::string& ifname) {
+ auto res = property_set(kActiveWlanIfaceNameProperty, ifname.data());
+ if (res != 0) {
+ PLOG(ERROR) << "Failed to set active wlan iface name property";
+ }
+}
+
// delete files that meet either conditions:
// 1. older than a predefined time in the wifi tombstone dir.
// 2. Files in excess to a predefined amount, starting from the oldest ones
@@ -316,13 +325,16 @@
is_valid_(true),
current_mode_id_(feature_flags::chip_mode_ids::kInvalid),
modes_(feature_flags.lock()->getChipModes()),
- debug_ring_buffer_cb_registered_(false) {}
+ debug_ring_buffer_cb_registered_(false) {
+ setActiveWlanIfaceNameProperty(kNoActiveWlanIfaceNamePropertyValue);
+}
void WifiChip::invalidate() {
if (!writeRingbufferFilesInternal()) {
LOG(ERROR) << "Error writing files to flash";
}
invalidateAndRemoveAllIfaces();
+ setActiveWlanIfaceNameProperty(kNoActiveWlanIfaceNamePropertyValue);
legacy_hal_.reset();
event_cb_handler_.invalidate();
is_valid_ = false;
@@ -641,7 +653,7 @@
legacy_hal::wifi_error legacy_status;
uint32_t legacy_feature_set;
uint32_t legacy_logger_feature_set;
- const auto ifname = getWlanIfaceName(0);
+ const auto ifname = getFirstActiveWlanIfaceName();
std::tie(legacy_status, legacy_feature_set) =
legacy_hal_.lock()->getSupportedFeatureSet(ifname);
if (legacy_status != legacy_hal::WIFI_SUCCESS) {
@@ -693,6 +705,7 @@
}
current_mode_id_ = mode_id;
LOG(INFO) << "Configured chip in mode " << mode_id;
+ setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
return status;
}
@@ -709,7 +722,7 @@
IWifiChip::ChipDebugInfo result;
legacy_hal::wifi_error legacy_status;
std::string driver_desc;
- const auto ifname = getWlanIfaceName(0);
+ const auto ifname = getFirstActiveWlanIfaceName();
std::tie(legacy_status, driver_desc) =
legacy_hal_.lock()->getDriverVersion(ifname);
if (legacy_status != legacy_hal::WIFI_SUCCESS) {
@@ -741,7 +754,8 @@
legacy_hal::wifi_error legacy_status;
std::vector<uint8_t> driver_dump;
std::tie(legacy_status, driver_dump) =
- legacy_hal_.lock()->requestDriverMemoryDump(getWlanIfaceName(0));
+ legacy_hal_.lock()->requestDriverMemoryDump(
+ getFirstActiveWlanIfaceName());
if (legacy_status != legacy_hal::WIFI_SUCCESS) {
LOG(ERROR) << "Failed to get driver debug dump: "
<< legacyErrorToString(legacy_status);
@@ -756,7 +770,8 @@
legacy_hal::wifi_error legacy_status;
std::vector<uint8_t> firmware_dump;
std::tie(legacy_status, firmware_dump) =
- legacy_hal_.lock()->requestFirmwareMemoryDump(getWlanIfaceName(0));
+ legacy_hal_.lock()->requestFirmwareMemoryDump(
+ getFirstActiveWlanIfaceName());
if (legacy_status != legacy_hal::WIFI_SUCCESS) {
LOG(ERROR) << "Failed to get firmware debug dump: "
<< legacyErrorToString(legacy_status);
@@ -766,10 +781,10 @@
}
std::pair<WifiStatus, sp<IWifiApIface>> WifiChip::createApIfaceInternal() {
- if (!canCurrentModeSupportIfaceOfType(IfaceType::AP)) {
+ if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::AP)) {
return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
}
- std::string ifname = allocateApOrStaIfaceName();
+ std::string ifname = allocateApIfaceName();
sp<WifiApIface> iface =
new WifiApIface(ifname, legacy_hal_, iface_util_, feature_flags_);
ap_ifaces_.push_back(iface);
@@ -778,6 +793,7 @@
LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
}
}
+ setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
}
@@ -809,15 +825,16 @@
LOG(ERROR) << "Failed to invoke onIfaceRemoved callback";
}
}
+ setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
return createWifiStatus(WifiStatusCode::SUCCESS);
}
std::pair<WifiStatus, sp<IWifiNanIface>> WifiChip::createNanIfaceInternal() {
- if (!canCurrentModeSupportIfaceOfType(IfaceType::NAN)) {
+ if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::NAN)) {
return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
}
// These are still assumed to be based on wlan0.
- std::string ifname = getWlanIfaceName(0);
+ std::string ifname = getFirstActiveWlanIfaceName();
sp<WifiNanIface> iface = new WifiNanIface(ifname, legacy_hal_);
nan_ifaces_.push_back(iface);
for (const auto& callback : event_cb_handler_.getCallbacks()) {
@@ -860,7 +877,7 @@
}
std::pair<WifiStatus, sp<IWifiP2pIface>> WifiChip::createP2pIfaceInternal() {
- if (!canCurrentModeSupportIfaceOfType(IfaceType::P2P)) {
+ if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::P2P)) {
return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
}
std::string ifname = getP2pIfaceName();
@@ -906,10 +923,10 @@
}
std::pair<WifiStatus, sp<IWifiStaIface>> WifiChip::createStaIfaceInternal() {
- if (!canCurrentModeSupportIfaceOfType(IfaceType::STA)) {
+ if (!canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(IfaceType::STA)) {
return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
}
- std::string ifname = allocateApOrStaIfaceName();
+ std::string ifname = allocateStaIfaceName();
sp<WifiStaIface> iface = new WifiStaIface(ifname, legacy_hal_, iface_util_);
sta_ifaces_.push_back(iface);
for (const auto& callback : event_cb_handler_.getCallbacks()) {
@@ -917,6 +934,7 @@
LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
}
}
+ setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
}
@@ -948,6 +966,7 @@
LOG(ERROR) << "Failed to invoke onIfaceRemoved callback";
}
}
+ setActiveWlanIfaceNameProperty(getFirstActiveWlanIfaceName());
return createWifiStatus(WifiStatusCode::SUCCESS);
}
@@ -959,8 +978,8 @@
"(and RTT by extension)";
return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
}
- sp<WifiRttController> rtt =
- new WifiRttController(getWlanIfaceName(0), bound_iface, legacy_hal_);
+ sp<WifiRttController> rtt = new WifiRttController(
+ getFirstActiveWlanIfaceName(), bound_iface, legacy_hal_);
rtt_controllers_.emplace_back(rtt);
return {createWifiStatus(WifiStatusCode::SUCCESS), rtt};
}
@@ -971,7 +990,7 @@
std::vector<legacy_hal::wifi_ring_buffer_status>
legacy_ring_buffer_status_vec;
std::tie(legacy_status, legacy_ring_buffer_status_vec) =
- legacy_hal_.lock()->getRingBuffersStatus(getWlanIfaceName(0));
+ legacy_hal_.lock()->getRingBuffersStatus(getFirstActiveWlanIfaceName());
if (legacy_status != legacy_hal::WIFI_SUCCESS) {
return {createWifiStatusFromLegacyError(legacy_status), {}};
}
@@ -993,7 +1012,7 @@
}
legacy_hal::wifi_error legacy_status =
legacy_hal_.lock()->startRingBufferLogging(
- getWlanIfaceName(0), ring_name,
+ getFirstActiveWlanIfaceName(), ring_name,
static_cast<
std::underlying_type<WifiDebugRingBufferVerboseLevel>::type>(
verbose_level),
@@ -1010,7 +1029,8 @@
return status;
}
legacy_hal::wifi_error legacy_status =
- legacy_hal_.lock()->getRingBufferData(getWlanIfaceName(0), ring_name);
+ legacy_hal_.lock()->getRingBufferData(getFirstActiveWlanIfaceName(),
+ ring_name);
return createWifiStatusFromLegacyError(legacy_status);
}
@@ -1026,7 +1046,7 @@
WifiStatus WifiChip::stopLoggingToDebugRingBufferInternal() {
legacy_hal::wifi_error legacy_status =
legacy_hal_.lock()->deregisterRingBufferCallbackHandler(
- getWlanIfaceName(0));
+ getFirstActiveWlanIfaceName());
return createWifiStatusFromLegacyError(legacy_status);
}
@@ -1035,7 +1055,7 @@
legacy_hal::wifi_error legacy_status;
legacy_hal::WakeReasonStats legacy_stats;
std::tie(legacy_status, legacy_stats) =
- legacy_hal_.lock()->getWakeReasonStats(getWlanIfaceName(0));
+ legacy_hal_.lock()->getWakeReasonStats(getFirstActiveWlanIfaceName());
if (legacy_status != legacy_hal::WIFI_SUCCESS) {
return {createWifiStatusFromLegacyError(legacy_status), {}};
}
@@ -1067,10 +1087,10 @@
}
};
legacy_status = legacy_hal_.lock()->registerErrorAlertCallbackHandler(
- getWlanIfaceName(0), on_alert_callback);
+ getFirstActiveWlanIfaceName(), on_alert_callback);
} else {
legacy_status = legacy_hal_.lock()->deregisterErrorAlertCallbackHandler(
- getWlanIfaceName(0));
+ getFirstActiveWlanIfaceName());
}
return createWifiStatusFromLegacyError(legacy_status);
}
@@ -1078,20 +1098,20 @@
WifiStatus WifiChip::selectTxPowerScenarioInternal(
V1_1::IWifiChip::TxPowerScenario scenario) {
auto legacy_status = legacy_hal_.lock()->selectTxPowerScenario(
- getWlanIfaceName(0),
+ getFirstActiveWlanIfaceName(),
hidl_struct_util::convertHidlTxPowerScenarioToLegacy(scenario));
return createWifiStatusFromLegacyError(legacy_status);
}
WifiStatus WifiChip::resetTxPowerScenarioInternal() {
auto legacy_status =
- legacy_hal_.lock()->resetTxPowerScenario(getWlanIfaceName(0));
+ legacy_hal_.lock()->resetTxPowerScenario(getFirstActiveWlanIfaceName());
return createWifiStatusFromLegacyError(legacy_status);
}
WifiStatus WifiChip::setLatencyModeInternal(LatencyMode mode) {
auto legacy_status = legacy_hal_.lock()->setLatencyMode(
- getWlanIfaceName(0),
+ getFirstActiveWlanIfaceName(),
hidl_struct_util::convertHidlLatencyModeToLegacy(mode));
return createWifiStatusFromLegacyError(legacy_status);
}
@@ -1107,7 +1127,7 @@
WifiStatus WifiChip::selectTxPowerScenarioInternal_1_2(
TxPowerScenario scenario) {
auto legacy_status = legacy_hal_.lock()->selectTxPowerScenario(
- getWlanIfaceName(0),
+ getFirstActiveWlanIfaceName(),
hidl_struct_util::convertHidlTxPowerScenarioToLegacy_1_2(scenario));
return createWifiStatusFromLegacyError(legacy_status);
}
@@ -1197,7 +1217,7 @@
};
legacy_hal::wifi_error legacy_status =
legacy_hal_.lock()->registerRingBufferCallbackHandler(
- getWlanIfaceName(0), on_ring_buffer_data_callback);
+ getFirstActiveWlanIfaceName(), on_ring_buffer_data_callback);
if (legacy_status == legacy_hal::WIFI_SUCCESS) {
debug_ring_buffer_cb_registered_ = true;
@@ -1231,7 +1251,7 @@
};
legacy_hal::wifi_error legacy_status =
legacy_hal_.lock()->registerRadioModeChangeCallbackHandler(
- getWlanIfaceName(0), on_radio_mode_change_callback);
+ getFirstActiveWlanIfaceName(), on_radio_mode_change_callback);
return createWifiStatusFromLegacyError(legacy_status);
}
@@ -1298,8 +1318,9 @@
return expanded_combos;
}
-bool WifiChip::canExpandedIfaceCombinationSupportIfaceOfType(
- const std::map<IfaceType, size_t>& combo, IfaceType requested_type) {
+bool WifiChip::canExpandedIfaceComboSupportIfaceOfTypeWithCurrentIfaces(
+ const std::map<IfaceType, size_t>& expanded_combo,
+ IfaceType requested_type) {
const auto current_combo = getCurrentIfaceCombination();
// Check if we have space for 1 more iface of |type| in this combo
@@ -1309,7 +1330,7 @@
if (type == requested_type) {
num_ifaces_needed++;
}
- size_t num_ifaces_allowed = combo.at(type);
+ size_t num_ifaces_allowed = expanded_combo.at(type);
if (num_ifaces_needed > num_ifaces_allowed) {
return false;
}
@@ -1320,8 +1341,10 @@
// This method does the following:
// a) Enumerate all possible iface combos by expanding the current
// ChipIfaceCombination.
-// b) Check if the requested iface type can be added to the current mode.
-bool WifiChip::canCurrentModeSupportIfaceOfType(IfaceType type) {
+// b) Check if the requested iface type can be added to the current mode
+// with the iface combination that is already active.
+bool WifiChip::canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(
+ IfaceType requested_type) {
if (!isValidModeId(current_mode_id_)) {
LOG(ERROR) << "Chip not configured in a mode yet";
return false;
@@ -1330,8 +1353,8 @@
for (const auto& combination : combinations) {
const auto expanded_combos = expandIfaceCombinations(combination);
for (const auto& expanded_combo : expanded_combos) {
- if (canExpandedIfaceCombinationSupportIfaceOfType(expanded_combo,
- type)) {
+ if (canExpandedIfaceComboSupportIfaceOfTypeWithCurrentIfaces(
+ expanded_combo, requested_type)) {
return true;
}
}
@@ -1339,6 +1362,62 @@
return false;
}
+// Note: This does not consider ifaces already active. It only checks if the
+// provided expanded iface combination can support the requested combo.
+bool WifiChip::canExpandedIfaceComboSupportIfaceCombo(
+ const std::map<IfaceType, size_t>& expanded_combo,
+ const std::map<IfaceType, size_t>& req_combo) {
+ // Check if we have space for 1 more iface of |type| in this combo
+ for (const auto type :
+ {IfaceType::AP, IfaceType::NAN, IfaceType::P2P, IfaceType::STA}) {
+ if (req_combo.count(type) == 0) {
+ // Iface of "type" not in the req_combo.
+ continue;
+ }
+ size_t num_ifaces_needed = req_combo.at(type);
+ size_t num_ifaces_allowed = expanded_combo.at(type);
+ if (num_ifaces_needed > num_ifaces_allowed) {
+ return false;
+ }
+ }
+ return true;
+}
+// This method does the following:
+// a) Enumerate all possible iface combos by expanding the current
+// ChipIfaceCombination.
+// b) Check if the requested iface combo can be added to the current mode.
+// Note: This does not consider ifaces already active. It only checks if the
+// current mode can support the requested combo.
+bool WifiChip::canCurrentModeSupportIfaceCombo(
+ const std::map<IfaceType, size_t>& req_combo) {
+ if (!isValidModeId(current_mode_id_)) {
+ LOG(ERROR) << "Chip not configured in a mode yet";
+ return false;
+ }
+ const auto combinations = getCurrentModeIfaceCombinations();
+ for (const auto& combination : combinations) {
+ const auto expanded_combos = expandIfaceCombinations(combination);
+ for (const auto& expanded_combo : expanded_combos) {
+ if (canExpandedIfaceComboSupportIfaceCombo(expanded_combo,
+ req_combo)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// This method does the following:
+// a) Enumerate all possible iface combos by expanding the current
+// ChipIfaceCombination.
+// b) Check if the requested iface type can be added to the current mode.
+bool WifiChip::canCurrentModeSupportIfaceOfType(IfaceType requested_type) {
+ // Check if we can support atleast 1 iface of type.
+ std::map<IfaceType, size_t> req_iface_combo;
+ req_iface_combo[requested_type] = 1;
+ return canCurrentModeSupportIfaceCombo(req_iface_combo);
+}
+
bool WifiChip::isValidModeId(ChipModeId mode_id) {
for (const auto& mode : modes_) {
if (mode.id == mode_id) {
@@ -1348,11 +1427,32 @@
return false;
}
-// Return the first wlan (wlan0, wlan1 etc.) not already in use.
-// This doesn't check the actual presence of these interfaces.
-std::string WifiChip::allocateApOrStaIfaceName() {
- for (unsigned i = 0; i < kMaxWlanIfaces; i++) {
- const auto ifname = getWlanIfaceName(i);
+bool WifiChip::isStaApConcurrencyAllowedInCurrentMode() {
+ // Check if we can support atleast 1 STA & 1 AP concurrently.
+ std::map<IfaceType, size_t> req_iface_combo;
+ req_iface_combo[IfaceType::AP] = 1;
+ req_iface_combo[IfaceType::STA] = 1;
+ return canCurrentModeSupportIfaceCombo(req_iface_combo);
+}
+
+std::string WifiChip::getFirstActiveWlanIfaceName() {
+ for (unsigned idx = 0; idx < kMaxWlanIfaces; idx++) {
+ const auto ifname = getWlanIfaceName(idx);
+ if (findUsingName(sta_ifaces_, ifname)) return ifname;
+ if (findUsingName(ap_ifaces_, ifname)) return ifname;
+ }
+ // This could happen if the chip call is made before any STA/AP
+ // iface is created. Default to wlan0 for such cases.
+ LOG(WARNING) << "No active wlan interfaces in use!";
+ return getWlanIfaceName(0);
+}
+
+// Return the first wlan (wlan0, wlan1 etc.) starting from |start_idx|
+// not already in use.
+// Note: This doesn't check the actual presence of these interfaces.
+std::string WifiChip::allocateApOrStaIfaceName(uint32_t start_idx) {
+ for (unsigned idx = start_idx; idx < kMaxWlanIfaces; idx++) {
+ const auto ifname = getWlanIfaceName(idx);
if (findUsingName(ap_ifaces_, ifname)) continue;
if (findUsingName(sta_ifaces_, ifname)) continue;
return ifname;
@@ -1362,6 +1462,19 @@
return {};
}
+// AP iface names start with idx 1 for modes supporting
+// concurrent STA, else start with idx 0.
+std::string WifiChip::allocateApIfaceName() {
+ return allocateApOrStaIfaceName(
+ isStaApConcurrencyAllowedInCurrentMode() ? 1 : 0);
+}
+
+// STA iface names start with idx 0.
+// Primary STA iface will always be 0.
+std::string WifiChip::allocateStaIfaceName() {
+ return allocateApOrStaIfaceName(0);
+}
+
bool WifiChip::writeRingbufferFilesInternal() {
if (!removeOldFilesInternal()) {
LOG(ERROR) << "Error occurred while deleting old tombstone files";
diff --git a/wifi/1.3/default/wifi_chip.h b/wifi/1.3/default/wifi_chip.h
index 3eb0aee..3d12f4c 100644
--- a/wifi/1.3/default/wifi_chip.h
+++ b/wifi/1.3/default/wifi_chip.h
@@ -224,11 +224,23 @@
std::map<IfaceType, size_t> getCurrentIfaceCombination();
std::vector<std::map<IfaceType, size_t>> expandIfaceCombinations(
const IWifiChip::ChipIfaceCombination& combination);
- bool canExpandedIfaceCombinationSupportIfaceOfType(
- const std::map<IfaceType, size_t>& combo, IfaceType type);
- bool canCurrentModeSupportIfaceOfType(IfaceType type);
+ bool canExpandedIfaceComboSupportIfaceOfTypeWithCurrentIfaces(
+ const std::map<IfaceType, size_t>& expanded_combo,
+ IfaceType requested_type);
+ bool canCurrentModeSupportIfaceOfTypeWithCurrentIfaces(
+ IfaceType requested_type);
+ bool canExpandedIfaceComboSupportIfaceCombo(
+ const std::map<IfaceType, size_t>& expanded_combo,
+ const std::map<IfaceType, size_t>& req_combo);
+ bool canCurrentModeSupportIfaceCombo(
+ const std::map<IfaceType, size_t>& req_combo);
+ bool canCurrentModeSupportIfaceOfType(IfaceType requested_type);
bool isValidModeId(ChipModeId mode_id);
- std::string allocateApOrStaIfaceName();
+ bool isStaApConcurrencyAllowedInCurrentMode();
+ std::string getFirstActiveWlanIfaceName();
+ std::string allocateApOrStaIfaceName(uint32_t start_idx);
+ std::string allocateApIfaceName();
+ std::string allocateStaIfaceName();
bool writeRingbufferFilesInternal();
ChipId chip_id_;
diff --git a/wifi/1.3/default/wifi_legacy_hal.cpp b/wifi/1.3/default/wifi_legacy_hal.cpp
index 5aa98c4..7f9b635 100644
--- a/wifi/1.3/default/wifi_legacy_hal.cpp
+++ b/wifi/1.3/default/wifi_legacy_hal.cpp
@@ -777,7 +777,7 @@
}
wifi_error WifiLegacyHal::startSendingOffloadedPacket(
- const std::string& iface_name, uint32_t cmd_id,
+ const std::string& iface_name, uint32_t cmd_id, uint16_t ether_type,
const std::vector<uint8_t>& ip_packet_data,
const std::array<uint8_t, 6>& src_address,
const std::array<uint8_t, 6>& dst_address, uint32_t period_in_ms) {
@@ -787,9 +787,9 @@
std::vector<uint8_t> dst_address_internal(
dst_address.data(), dst_address.data() + dst_address.size());
return global_func_table_.wifi_start_sending_offloaded_packet(
- cmd_id, getIfaceHandle(iface_name), ip_packet_data_internal.data(),
- ip_packet_data_internal.size(), src_address_internal.data(),
- dst_address_internal.data(), period_in_ms);
+ cmd_id, getIfaceHandle(iface_name), ether_type,
+ ip_packet_data_internal.data(), ip_packet_data_internal.size(),
+ src_address_internal.data(), dst_address_internal.data(), period_in_ms);
}
wifi_error WifiLegacyHal::stopSendingOffloadedPacket(
diff --git a/wifi/1.3/default/wifi_legacy_hal.h b/wifi/1.3/default/wifi_legacy_hal.h
index 70a919f..a3ed460 100644
--- a/wifi/1.3/default/wifi_legacy_hal.h
+++ b/wifi/1.3/default/wifi_legacy_hal.h
@@ -184,9 +184,9 @@
// Checks if legacy HAL has successfully started
bool isStarted();
// Wrappers for all the functions in the legacy HAL function table.
- std::pair<wifi_error, std::string> getDriverVersion(
+ virtual std::pair<wifi_error, std::string> getDriverVersion(
const std::string& iface_name);
- std::pair<wifi_error, std::string> getFirmwareVersion(
+ virtual std::pair<wifi_error, std::string> getFirmwareVersion(
const std::string& iface_name);
std::pair<wifi_error, std::vector<uint8_t>> requestDriverMemoryDump(
const std::string& iface_name);
@@ -246,7 +246,7 @@
fw_roaming_state_t state);
wifi_error configureNdOffload(const std::string& iface_name, bool enable);
wifi_error startSendingOffloadedPacket(
- const std::string& iface_name, uint32_t cmd_id,
+ const std::string& iface_name, uint32_t cmd_id, uint16_t ether_type,
const std::vector<uint8_t>& ip_packet_data,
const std::array<uint8_t, 6>& src_address,
const std::array<uint8_t, 6>& dst_address, uint32_t period_in_ms);
@@ -254,9 +254,9 @@
uint32_t cmd_id);
wifi_error setScanningMacOui(const std::string& iface_name,
const std::array<uint8_t, 3>& oui);
- wifi_error selectTxPowerScenario(const std::string& iface_name,
- wifi_power_scenario scenario);
- wifi_error resetTxPowerScenario(const std::string& iface_name);
+ virtual wifi_error selectTxPowerScenario(const std::string& iface_name,
+ wifi_power_scenario scenario);
+ virtual wifi_error resetTxPowerScenario(const std::string& iface_name);
wifi_error setLatencyMode(const std::string& iface_name,
wifi_latency_mode mode);
// Logger/debug functions.
diff --git a/wifi/1.3/default/wifi_sta_iface.cpp b/wifi/1.3/default/wifi_sta_iface.cpp
index 17f3e3d..a6539e5 100644
--- a/wifi/1.3/default/wifi_sta_iface.cpp
+++ b/wifi/1.3/default/wifi_sta_iface.cpp
@@ -562,12 +562,12 @@
WifiStatus WifiStaIface::startSendingKeepAlivePacketsInternal(
uint32_t cmd_id, const std::vector<uint8_t>& ip_packet_data,
- uint16_t /* ether_type */, const std::array<uint8_t, 6>& src_address,
+ uint16_t ether_type, const std::array<uint8_t, 6>& src_address,
const std::array<uint8_t, 6>& dst_address, uint32_t period_in_ms) {
legacy_hal::wifi_error legacy_status =
legacy_hal_.lock()->startSendingOffloadedPacket(
- ifname_, cmd_id, ip_packet_data, src_address, dst_address,
- period_in_ms);
+ ifname_, cmd_id, ether_type, ip_packet_data, src_address,
+ dst_address, period_in_ms);
return createWifiStatusFromLegacyError(legacy_status);
}