Merge changes from topics "fix-b-133526565-setters-getters", "fix-b-133526565-setters-getters-2"
* changes:
audio: Add playback rate parameters to IStreamOut
audio: Add Dual Mono Mode and Audio Description Mix Level to IStreamOut
diff --git a/automotive/can/1.0/ICanController.hal b/automotive/can/1.0/ICanController.hal
index 0c6f53e..aaf85e9 100644
--- a/automotive/can/1.0/ICanController.hal
+++ b/automotive/can/1.0/ICanController.hal
@@ -27,51 +27,22 @@
*/
interface ICanController {
/**
- * Type of an interface, a mean to express the domain of device address.
+ * Type of an interface, an equivalent to BusConfig::InterfaceId
+ * union discriminator. Defines a number of specific standard hardware
+ * families and a generic catch-all type of {@see INDEXED}.
*/
enum InterfaceType : uint8_t {
- /**
- * Virtual SocketCAN interface.
- *
- * The address is an interface name, such as vcan0. If the interface
- * doesn't exist, HAL server must create it.
- *
- * Valid InterfaceIdentifier types:
- * - address.
- */
+ /** Virtual SocketCAN interface. */
VIRTUAL,
- /**
- * Native SocketCAN interface.
- *
- * The address is an interface name, such as can0.
- *
- * Valid InterfaceIdentifier types:
- * - address;
- * - serialno.
- */
+ /** Native SocketCAN interface. */
SOCKETCAN,
- /**
- * Serial-based interface.
- *
- * The address is a patch to a device, such as /dev/ttyUSB0.
- *
- * Valid InterfaceIdentifier types:
- * - address;
- * - serialno.
- */
+ /** Serial line CAN interface. */
SLCAN,
- /**
- * Proprietary interface, specific to the hardware system Android
- * is running on. Instead of using address field, the interface is
- * addressed with 0-based index.
- *
- * Valid InterfaceIdentifier types:
- * - index
- */
- INDEXED
+ /** Proprietary, device-specific interface. */
+ INDEXED,
};
enum Result : uint8_t {
@@ -92,13 +63,19 @@
NOT_SUPPORTED,
/**
- * Provided address (interface name, device path) doesn't exist or there
- * is no device with a given serial no.
+ * Provided interface ID (index, name, device path) doesn't exist or
+ * there is no device with a given serial number.
*/
- BAD_ADDRESS,
+ BAD_INTERFACE_ID,
/** Provided bit rate is not supported by the hardware. */
BAD_BITRATE,
+
+ /**
+ * Provided service name ({@see BusConfig#name}) either has invalid
+ * format or is not listed in device manifest file.
+ */
+ BAD_SERVICE_NAME,
};
/**
@@ -106,49 +83,76 @@
*
* ISO TP and CAN FD are currently not supported.
*/
- struct BusConfiguration {
+ struct BusConfig {
/**
* Name under which ICanBus HIDL service should be published.
*
* It must consist of only alphanumeric characters and underscore
* (a-z, A-Z, 0-9, '_'), at least 1 and at most 32 characters long.
+ *
+ * This field is *not* meant to distinguish between hardware interfaces
+ * nor preselect parameters like bitrate. The only intended side-effect
+ * of changing it should be a different ICanBus HIDL service name and
+ * the HIDL service should make no assumptions on its contents.
*/
string name;
/**
- * Type of the hardware (or virtual) CAN interface.
+ * Hardware interface configuration.
+ *
+ * This union's discriminator has an equivalent enum
+ * {@see InterfaceType} to express compatibility via
+ * getSupportedInterfaceTypes().
*/
- InterfaceType iftype;
+ safe_union InterfaceId {
+ /** Virtual SocketCAN interface. */
+ struct Virtual {
+ /** Interface name, such as vcan0. If the interface doesn't
+ * exist, HAL server must create it.
+ */
+ string ifname;
+ } virtualif;
- /**
- * Identification of hardware interface to configure.
- */
- safe_union InterfaceIdentifier {
- /**
- * Interface name or other mean of identification of the specific
- * interface port. Syntax depends on {@see iftype}, for details
- * {@see InterfaceType}.
- */
- string address;
+ /** Native SocketCAN interface. */
+ safe_union Socketcan {
+ /** Interface name, such as can0. */
+ string ifname;
+ /**
+ * Alternatively to providing {@see ifname}, one may provide a
+ * list of interface serial number suffixes. If there happens to
+ * be a device (like USB2CAN) with a matching serial number
+ * suffix, the HAL service will have to select it.
+ *
+ * Client may utilize this in two ways: by matching against the
+ * entire serial number, or the last few characters (usually
+ * one). The former is better for small-scale test deployments
+ * (with just a handful of vehicles), the latter is good for
+ * larger scale (where a small suffix list may support large
+ * test fleet).
+ */
+ vec<string> serialno;
+ } socketcan;
+
+ /** Serial line CAN interface. */
+ safe_union Slcan {
+ /** Path to a device, such as /dev/ttyUSB0. */
+ string ttyname;
+ /**
+ * List of interface serial number suffixes.
+ * {@see Socketcan::serialno}
+ */
+ vec<string> serialno;
+ } slcan;
/**
- * Numerical identifier of interface, used for InterfaceType#INDEXED.
- */
- uint8_t index;
-
- /**
- * Alternatively to providing {@see address}, one may provide a list
- * of interface serial number suffixes. If there happens to be
- * a device (like USB2CAN) with a matching serial number suffix,
- * it gets selected.
+ * Proprietary, device-specific interface.
*
- * Client may utilize this in two ways: by matching against the
- * entire serial number, or the last few characters (usually one).
- * The former is better for small-scale test deployments (with just
- * a handful of vehicles), the latter is good for larger scale
- * (where a small suffix list may support large test fleet).
+ * Non-SocketCAN interfaces should use this variant.
*/
- vec<string> serialno;
+ struct Indexed {
+ /** Interface number, 0-based. */
+ uint8_t index;
+ } indexed;
} interfaceId;
/**
@@ -156,7 +160,8 @@
*
* Typical bit rates are: 100000, 125000, 250000, 500000.
*
- * For virtual interfaces this value is ignored.
+ * For {@see interfaceId#virtual} and pre-configured
+ * {@see interfaceId#indexed} interfaces this value is ignored.
*/
uint32_t bitrate;
};
@@ -164,17 +169,17 @@
/**
* Fetches the list of interface types supported by this HAL server.
*
- * @return iftypes The list of supported interface types
+ * @return iftypes The list of supported interface types.
*/
getSupportedInterfaceTypes() generates (vec<InterfaceType> iftypes);
/**
* Bring up the CAN interface and publish ICanBus server instance.
*
- * @param config Configuration of the CAN interface
+ * @param config Configuration of the CAN interface.
* @return result OK if the operation succeeded; error code otherwise.
*/
- upInterface(BusConfiguration config) generates (Result result);
+ upInterface(BusConfig config) generates (Result result);
/**
* Unpublish ICanBus server instance and bring down the CAN interface.
@@ -182,9 +187,9 @@
* In case of failure, at least the ICanBus server instance must be
* unpublished and resources freed on best-effort basis.
*
- * @param name Name of the interface (@see BusConfiguration#name} to
- * bring down
- * @return success true in case of success, false otherwise
+ * @param name Name of the interface (@see BusConfig#name} to
+ * bring down.
+ * @return success true in case of success, false otherwise.
*/
downInterface(string name) generates (bool success);
};
diff --git a/automotive/can/1.0/default/CanBus.cpp b/automotive/can/1.0/default/CanBus.cpp
index 8fb09eb..9f704c1 100644
--- a/automotive/can/1.0/default/CanBus.cpp
+++ b/automotive/can/1.0/default/CanBus.cpp
@@ -124,7 +124,7 @@
if (!isUp.has_value()) {
// preUp() should prepare the interface (either create or make sure it's there)
LOG(ERROR) << "Interface " << mIfname << " didn't get prepared";
- return ICanController::Result::BAD_ADDRESS;
+ return ICanController::Result::BAD_INTERFACE_ID;
}
if (!*isUp && !netdevice::up(mIfname)) {
diff --git a/automotive/can/1.0/default/CanBusNative.cpp b/automotive/can/1.0/default/CanBusNative.cpp
index ef04d01..aafbecc 100644
--- a/automotive/can/1.0/default/CanBusNative.cpp
+++ b/automotive/can/1.0/default/CanBusNative.cpp
@@ -28,7 +28,7 @@
ICanController::Result CanBusNative::preUp() {
if (!netdevice::exists(mIfname)) {
LOG(ERROR) << "Interface " << mIfname << " doesn't exist";
- return ICanController::Result::BAD_ADDRESS;
+ return ICanController::Result::BAD_INTERFACE_ID;
}
if (mBitrate == 0) {
diff --git a/automotive/can/1.0/default/CanBusSlcan.cpp b/automotive/can/1.0/default/CanBusSlcan.cpp
index 0feee8f..d15905d 100644
--- a/automotive/can/1.0/default/CanBusSlcan.cpp
+++ b/automotive/can/1.0/default/CanBusSlcan.cpp
@@ -81,7 +81,7 @@
mFd = base::unique_fd(open(mUartName.c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY));
if (!mFd.ok()) {
LOG(ERROR) << "SLCAN Failed to open " << mUartName << ": " << strerror(errno);
- return ICanController::Result::BAD_ADDRESS;
+ return ICanController::Result::BAD_INTERFACE_ID;
}
// If the device is already up, update the iface name in our CanBusSlcan object
diff --git a/automotive/can/1.0/default/CanController.cpp b/automotive/can/1.0/default/CanController.cpp
index fb648c1..0700c77 100644
--- a/automotive/can/1.0/default/CanController.cpp
+++ b/automotive/can/1.0/default/CanController.cpp
@@ -27,7 +27,8 @@
namespace android::hardware::automotive::can::V1_0::implementation {
-using IfaceIdDisc = ICanController::BusConfiguration::InterfaceIdentifier::hidl_discriminator;
+using IfId = ICanController::BusConfig::InterfaceId;
+using IfIdDisc = ICanController::BusConfig::InterfaceId::hidl_discriminator;
Return<void> CanController::getSupportedInterfaceTypes(getSupportedInterfaceTypes_cb _hidl_cb) {
_hidl_cb({ICanController::InterfaceType::VIRTUAL, ICanController::InterfaceType::SOCKETCAN,
@@ -40,15 +41,14 @@
return std::regex_match(name, nameRE);
}
-Return<ICanController::Result> CanController::upInterface(
- const ICanController::BusConfiguration& config) {
+Return<ICanController::Result> CanController::upInterface(const ICanController::BusConfig& config) {
LOG(VERBOSE) << "Attempting to bring interface up: " << toString(config);
std::lock_guard<std::mutex> lck(mCanBusesGuard);
if (!isValidName(config.name)) {
LOG(ERROR) << "Bus name " << config.name << " is invalid";
- return ICanController::Result::UNKNOWN_ERROR;
+ return ICanController::Result::BAD_SERVICE_NAME;
}
if (mCanBuses.find(config.name) != mCanBuses.end()) {
@@ -58,24 +58,23 @@
sp<CanBus> busService;
- if (config.iftype == ICanController::InterfaceType::SOCKETCAN) {
- // TODO(b/135918744): support serialno
- if (config.interfaceId.getDiscriminator() == IfaceIdDisc::address) {
- busService = new CanBusNative(config.interfaceId.address(), config.bitrate);
+ if (config.interfaceId.getDiscriminator() == IfIdDisc::socketcan) {
+ // TODO(b/142654031): support serialno
+ auto& socketcan = config.interfaceId.socketcan();
+ if (socketcan.getDiscriminator() == IfId::Socketcan::hidl_discriminator::ifname) {
+ busService = new CanBusNative(socketcan.ifname(), config.bitrate);
} else {
- return ICanController::Result::BAD_ADDRESS;
+ return ICanController::Result::BAD_INTERFACE_ID;
}
- } else if (config.iftype == ICanController::InterfaceType::VIRTUAL) {
- if (config.interfaceId.getDiscriminator() == IfaceIdDisc::address) {
- busService = new CanBusVirtual(config.interfaceId.address());
+ } else if (config.interfaceId.getDiscriminator() == IfIdDisc::virtualif) {
+ busService = new CanBusVirtual(config.interfaceId.virtualif().ifname);
+ } else if (config.interfaceId.getDiscriminator() == IfIdDisc::slcan) {
+ // TODO(b/142654031): support serialno
+ auto& slcan = config.interfaceId.slcan();
+ if (slcan.getDiscriminator() == IfId::Slcan::hidl_discriminator::ttyname) {
+ busService = new CanBusSlcan(slcan.ttyname(), config.bitrate);
} else {
- return ICanController::Result::BAD_ADDRESS;
- }
- } else if (config.iftype == ICanController::InterfaceType::SLCAN) {
- if (config.interfaceId.getDiscriminator() == IfaceIdDisc::address) {
- busService = new CanBusSlcan(config.interfaceId.address(), config.bitrate);
- } else {
- return ICanController::Result::BAD_ADDRESS;
+ return ICanController::Result::BAD_INTERFACE_ID;
}
} else {
return ICanController::Result::NOT_SUPPORTED;
@@ -91,7 +90,7 @@
if (!busService->down()) {
LOG(WARNING) << "Failed to bring down CAN bus that failed to register";
}
- return ICanController::Result::UNKNOWN_ERROR;
+ return ICanController::Result::BAD_SERVICE_NAME;
}
mCanBuses[config.name] = busService;
diff --git a/automotive/can/1.0/default/CanController.h b/automotive/can/1.0/default/CanController.h
index 99a551a..27e82f3 100644
--- a/automotive/can/1.0/default/CanController.h
+++ b/automotive/can/1.0/default/CanController.h
@@ -25,8 +25,7 @@
struct CanController : public ICanController {
Return<void> getSupportedInterfaceTypes(getSupportedInterfaceTypes_cb _hidl_cb) override;
- Return<ICanController::Result> upInterface(
- const ICanController::BusConfiguration& config) override;
+ Return<ICanController::Result> upInterface(const ICanController::BusConfig& config) override;
Return<bool> downInterface(const hidl_string& name) override;
private:
diff --git a/automotive/can/1.0/tools/canhalctrl.cpp b/automotive/can/1.0/tools/canhalctrl.cpp
index 5494ba3..33755bf 100644
--- a/automotive/can/1.0/tools/canhalctrl.cpp
+++ b/automotive/can/1.0/tools/canhalctrl.cpp
@@ -71,15 +71,31 @@
if (!isSupported(ctrl, type)) continue;
anySupported = true;
- ICanController::BusConfiguration config = {};
+ ICanController::BusConfig config = {};
config.name = busName;
- config.iftype = type;
config.bitrate = bitrate;
- if (type == ICanController::InterfaceType::INDEXED) {
- config.interfaceId.index(std::stol(interface));
+ // TODO(b/146214370): move interfaceId constructors to a library
+ using IfCfg = ICanController::BusConfig::InterfaceId;
+ if (type == ICanController::InterfaceType::VIRTUAL) {
+ config.interfaceId.virtualif({interface});
+ } else if (type == ICanController::InterfaceType::SOCKETCAN) {
+ IfCfg::Socketcan socketcan = {};
+ socketcan.ifname(interface);
+ config.interfaceId.socketcan(socketcan);
+ } else if (type == ICanController::InterfaceType::SLCAN) {
+ IfCfg::Slcan slcan = {};
+ slcan.ttyname(interface);
+ config.interfaceId.slcan(slcan);
+ } else if (type == ICanController::InterfaceType::INDEXED) {
+ auto idx = std::stol(interface);
+ if (idx < 0 || idx > UINT8_MAX) {
+ std::cerr << "Interface index out of range: " << idx;
+ return -1;
+ }
+ config.interfaceId.indexed({uint8_t(idx)});
} else {
- config.interfaceId.address(interface);
+ CHECK(false) << "Unexpected interface type: " << toString(type);
}
const auto upresult = ctrl->upInterface(config);
diff --git a/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp b/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
index efaad53..68d555d 100644
--- a/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
+++ b/automotive/can/1.0/vts/functional/VtsHalCanBusVirtualV1_0TargetTest.cpp
@@ -81,7 +81,7 @@
struct Bus {
DISALLOW_COPY_AND_ASSIGN(Bus);
- Bus(sp<ICanController> controller, const ICanController::BusConfiguration& config)
+ Bus(sp<ICanController> controller, const ICanController::BusConfig& config)
: mIfname(config.name), mController(controller) {
const auto result = controller->upInterface(config);
EXPECT_EQ(ICanController::Result::OK, result);
@@ -122,6 +122,7 @@
void send(const CanMessage& msg) {
EXPECT_NE(mBus, nullptr);
+ if (!mBus) return;
const auto result = mBus->send(msg);
EXPECT_EQ(Result::OK, result);
}
@@ -196,10 +197,9 @@
const auto idx = mLastIface++;
EXPECT_LT(idx, mBusNames.size());
- ICanController::BusConfiguration config = {};
+ ICanController::BusConfig config = {};
config.name = mBusNames[idx];
- config.iftype = InterfaceType::VIRTUAL;
- config.interfaceId.address("vcan50");
+ config.interfaceId.virtualif({"vcan50"});
return Bus(mCanController, config);
}
diff --git a/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp b/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
index b2edd78..8397215 100644
--- a/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
+++ b/automotive/can/1.0/vts/functional/VtsHalCanControllerV1_0TargetTest.cpp
@@ -31,6 +31,7 @@
using hardware::hidl_vec;
using InterfaceType = ICanController::InterfaceType;
+using IfId = ICanController::BusConfig::InterfaceId;
static utils::SimpleHidlEnvironment<ICanController>* gEnv = nullptr;
@@ -89,10 +90,23 @@
bool CanControllerHalTest::up(InterfaceType iftype, std::string srvname, std::string ifname,
ICanController::Result expected) {
- ICanController::BusConfiguration config = {};
+ ICanController::BusConfig config = {};
config.name = srvname;
- config.iftype = iftype;
- config.interfaceId.address(ifname);
+
+ // TODO(b/146214370): move interfaceId constructors to a library
+ if (iftype == InterfaceType::SOCKETCAN) {
+ IfId::Socketcan socketcan = {};
+ socketcan.ifname(ifname);
+ config.interfaceId.socketcan(socketcan);
+ } else if (iftype == InterfaceType::SLCAN) {
+ IfId::Slcan slcan = {};
+ slcan.ttyname(ifname);
+ config.interfaceId.slcan(slcan);
+ } else if (iftype == InterfaceType::VIRTUAL) {
+ config.interfaceId.virtualif({ifname});
+ } else {
+ EXPECT_TRUE(false) << "Unexpected iftype: " << toString(iftype);
+ }
const auto upresult = mCanController->upInterface(config);
@@ -155,54 +169,64 @@
assertRegistered(name, false);
}
-TEST_F(CanControllerHalTest, IdentifierCompatibility) {
- using IdDisc = ICanController::BusConfiguration::InterfaceIdentifier::hidl_discriminator;
- static const std::map<InterfaceType, std::vector<IdDisc>> compatMatrix = {
- {InterfaceType::VIRTUAL, {IdDisc::address}},
- {InterfaceType::SOCKETCAN, {IdDisc::address, IdDisc::serialno}},
- {InterfaceType::SLCAN, {IdDisc::address, IdDisc::serialno}},
- {InterfaceType::INDEXED, {IdDisc::index}},
+TEST_F(CanControllerHalTest, ConfigCompatibility) {
+ // using random-ish addresses, which may not be valid - we can't test the success case
+ // TODO(b/146214370): move interfaceId constructors to a library
+ IfId virtualCfg = {};
+ virtualCfg.virtualif({"vcan70"});
+
+ IfId::Socketcan socketcanIfname = {};
+ socketcanIfname.ifname("can0");
+ IfId socketcanIfnameCfg = {};
+ socketcanIfnameCfg.socketcan(socketcanIfname);
+
+ IfId::Socketcan socketcanSerial = {};
+ socketcanSerial.serialno({"1234", "2345"});
+ IfId socketcanSerialCfg = {};
+ socketcanSerialCfg.socketcan(socketcanSerial);
+
+ IfId::Slcan slcanTtyname = {};
+ slcanTtyname.ttyname("/dev/ttyUSB0");
+ IfId slcanTtynameCfg = {};
+ slcanTtynameCfg.slcan(slcanTtyname);
+
+ IfId::Slcan slcanSerial = {};
+ slcanSerial.serialno({"dead", "beef"});
+ IfId slcanSerialCfg = {};
+ slcanSerialCfg.slcan(slcanSerial);
+
+ IfId indexedCfg = {};
+ indexedCfg.indexed({0});
+
+ static const std::vector<std::pair<InterfaceType, IfId>> compatMatrix = {
+ {InterfaceType::VIRTUAL, virtualCfg},
+ {InterfaceType::SOCKETCAN, socketcanIfnameCfg},
+ {InterfaceType::SOCKETCAN, socketcanSerialCfg},
+ {InterfaceType::SLCAN, slcanTtynameCfg},
+ {InterfaceType::SLCAN, slcanSerialCfg},
+ {InterfaceType::INDEXED, indexedCfg},
};
- static const std::vector<IdDisc> allDisc = {IdDisc::address, IdDisc::index, IdDisc::serialno};
- for (const auto [iftype, supported] : compatMatrix) {
- for (const auto iddisc : allDisc) {
- LOG(INFO) << "Compatibility testing: " << iftype << " / " << iddisc;
+ for (const auto [iftype, cfg] : compatMatrix) {
+ LOG(INFO) << "Compatibility testing: " << iftype << " / " << cfg;
- ICanController::BusConfiguration config = {};
- config.name = "compattestsrv";
- config.iftype = iftype;
- config.bitrate = 125000;
+ ICanController::BusConfig config = {};
+ config.name = "compattestsrv";
+ config.bitrate = 125000;
+ config.interfaceId = cfg;
- // using random-ish addresses, which may not be valid - we can't test the success case
- if (iddisc == IdDisc::address) {
- config.interfaceId.address("can0");
- } else if (iddisc == IdDisc::index) {
- config.interfaceId.index(0);
- } else if (iddisc == IdDisc::serialno) {
- config.interfaceId.serialno({"dummy", "dummier"});
- }
+ const auto upresult = mCanController->upInterface(config);
- const auto upresult = mCanController->upInterface(config);
+ if (!isSupported(iftype)) {
+ ASSERT_EQ(ICanController::Result::NOT_SUPPORTED, upresult);
+ continue;
+ }
+ ASSERT_NE(ICanController::Result::NOT_SUPPORTED, upresult);
- if (!isSupported(iftype)) {
- ASSERT_EQ(ICanController::Result::NOT_SUPPORTED, upresult);
- continue;
- }
- ASSERT_NE(ICanController::Result::NOT_SUPPORTED, upresult);
-
- bool isSupportedDisc =
- std::find(supported.begin(), supported.end(), iddisc) != supported.end();
- if (!isSupportedDisc) {
- ASSERT_EQ(ICanController::Result::BAD_ADDRESS, upresult);
- continue;
- }
-
- if (upresult == ICanController::Result::OK) {
- const auto dnresult = mCanController->downInterface(config.name);
- ASSERT_TRUE(dnresult);
- continue;
- }
+ if (upresult == ICanController::Result::OK) {
+ const auto dnresult = mCanController->downInterface(config.name);
+ ASSERT_TRUE(dnresult);
+ continue;
}
}
}
@@ -211,7 +235,7 @@
const std::string name = "";
assertRegistered(name, false);
- if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::UNKNOWN_ERROR)) {
+ if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::BAD_SERVICE_NAME)) {
GTEST_SKIP();
}
assertRegistered(name, false);
@@ -222,7 +246,7 @@
const std::string name = "ab012345678901234567890123456789c";
assertRegistered(name, false);
- if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::UNKNOWN_ERROR)) {
+ if (!up(InterfaceType::VIRTUAL, name, "vcan57", ICanController::Result::BAD_SERVICE_NAME)) {
GTEST_SKIP();
}
assertRegistered(name, false);
@@ -232,7 +256,9 @@
const std::string name = mBusNames[0];
assertRegistered(name, false);
- if (!up(InterfaceType::VIRTUAL, name, "", ICanController::Result::BAD_ADDRESS)) GTEST_SKIP();
+ if (!up(InterfaceType::VIRTUAL, name, "", ICanController::Result::BAD_INTERFACE_ID)) {
+ GTEST_SKIP();
+ }
assertRegistered(name, false);
}
@@ -240,10 +266,30 @@
const std::string name = mBusNames[0];
assertRegistered(name, false);
- if (!up(InterfaceType::SOCKETCAN, name, "can87", ICanController::Result::BAD_ADDRESS)) {
+ if (!up(InterfaceType::SOCKETCAN, name, "can87", ICanController::Result::BAD_INTERFACE_ID)) {
GTEST_SKIP();
}
assertRegistered(name, false);
+
+ auto supported =
+ up(InterfaceType::SOCKETCAN, name, "", ICanController::Result::BAD_INTERFACE_ID);
+ ASSERT_TRUE(supported);
+ assertRegistered(name, false);
+}
+
+TEST_F(CanControllerHalTest, FailBadSlcanAddress) {
+ const std::string name = mBusNames[0];
+
+ assertRegistered(name, false);
+ if (!up(InterfaceType::SLCAN, name, "/dev/shouldnotexist123",
+ ICanController::Result::BAD_INTERFACE_ID)) {
+ GTEST_SKIP();
+ }
+ assertRegistered(name, false);
+
+ auto supported = up(InterfaceType::SLCAN, name, "", ICanController::Result::BAD_INTERFACE_ID);
+ ASSERT_TRUE(supported);
+ assertRegistered(name, false);
}
} // namespace android::hardware::automotive::can::V1_0::vts
diff --git a/automotive/can/1.0/vts/utils/include/can-vts-utils/can-hal-printers.h b/automotive/can/1.0/vts/utils/include/can-vts-utils/can-hal-printers.h
index 3c30744..383b54c 100644
--- a/automotive/can/1.0/vts/utils/include/can-vts-utils/can-hal-printers.h
+++ b/automotive/can/1.0/vts/utils/include/can-vts-utils/can-hal-printers.h
@@ -35,8 +35,7 @@
DEFINE_CAN_HAL_PRINTER(CanMessage, toString)
DEFINE_CAN_HAL_PRINTER(ErrorEvent, toString)
-DEFINE_CAN_HAL_PRINTER_SIMPLE(
- ICanController::BusConfiguration::InterfaceIdentifier::hidl_discriminator, int)
+DEFINE_CAN_HAL_PRINTER(ICanController::BusConfig::InterfaceId, toString);
DEFINE_CAN_HAL_PRINTER(ICanController::InterfaceType, toString)
DEFINE_CAN_HAL_PRINTER(ICanController::Result, toString)
DEFINE_CAN_HAL_PRINTER(Result, toString)
diff --git a/automotive/evs/1.1/Android.bp b/automotive/evs/1.1/Android.bp
index 17f31e4..f9bccef 100644
--- a/automotive/evs/1.1/Android.bp
+++ b/automotive/evs/1.1/Android.bp
@@ -12,6 +12,8 @@
"IEvsCameraStream.hal",
"IEvsDisplay.hal",
"IEvsEnumerator.hal",
+ "IEvsUltrasonicsArray.hal",
+ "IEvsUltrasonicsArrayStream.hal",
],
interfaces: [
"android.frameworks.automotive.display@1.0",
diff --git a/automotive/evs/1.1/IEvsCamera.hal b/automotive/evs/1.1/IEvsCamera.hal
index fc68e60..38e6c42 100644
--- a/automotive/evs/1.1/IEvsCamera.hal
+++ b/automotive/evs/1.1/IEvsCamera.hal
@@ -178,4 +178,41 @@
* values as backing camera devices.
*/
getIntParameter(CameraParam id) generates(EvsResult result, vec<int32_t> value);
+
+ /**
+ * Request driver specific information from the HAL implementation.
+ *
+ * The values allowed for opaqueIdentifier are driver specific,
+ * but no value passed in may crash the driver. The driver should
+ * return EvsResult::INVALID_ARG for any unrecognized opaqueIdentifier.
+ *
+ * @param opaqueIdentifier An unique identifier of the information to
+ * request.
+ * @return result EvsResult::OK if the driver recognizes a given
+ * identifier.
+ * EvsResult::INVALID_ARG, otherwise.
+ * @return value Requested information. Zero-size vector is
+ * returned if the driver does not recognize a
+ * given identifier.
+ */
+ getExtendedInfo_1_1(uint32_t opaqueIdentifier)
+ generates (EvsResult result, vec<uint8_t> value);
+
+ /**
+ * Send a driver specific value to the HAL implementation.
+ *
+ * This extension is provided to facilitate car specific
+ * extensions, but no HAL implementation may require this call
+ * in order to function in a default state.
+ * INVALID_ARG is returned if the opaqueValue is not meaningful to
+ * the driver implementation.
+ *
+ * @param opaqueIdentifier An unique identifier of the information to
+ * program.
+ * opaqueValue A value to program.
+ * @return result EvsResult::OK is returned if this call is successful.
+ * EvsResult::INVALID_ARG, otherwise.
+ */
+ setExtendedInfo_1_1(uint32_t opaqueIdentifier, vec<uint8_t> opaqueValue)
+ generates (EvsResult result);
};
diff --git a/automotive/evs/1.1/IEvsEnumerator.hal b/automotive/evs/1.1/IEvsEnumerator.hal
index 84dd21f..d604e4f 100644
--- a/automotive/evs/1.1/IEvsEnumerator.hal
+++ b/automotive/evs/1.1/IEvsEnumerator.hal
@@ -18,12 +18,13 @@
import IEvsCamera;
import IEvsDisplay;
+import IEvsUltrasonicsArray;
import @1.0::IEvsEnumerator;
import @1.0::EvsResult;
import android.hardware.camera.device@3.2::Stream;
/**
- * Provides the mechanism for EVS camera discovery
+ * Provides the mechanism for EVS camera and ultrasonics array discovery
*/
interface IEvsEnumerator extends @1.0::IEvsEnumerator {
/**
@@ -76,4 +77,33 @@
* @return display EvsDisplay object to be used.
*/
openDisplay_1_1(uint8_t id) generates (IEvsDisplay display);
+
+ /**
+ * Returns a list of all ultrasonics array available to the system.
+ * Will return an empty vector if ultrasonics is not supported.
+ *
+ * @return ultrasonicsArrays A list of ultrasonics available for EVS service.
+ */
+ getUltrasonicsArrayList() generates (vec<UltrasonicsArrayDesc> ultrasonicsArrays);
+
+ /**
+ * Gets the IEvsUltrasonicsArray associated with a ultrasonicsArrayId from a
+ * UltrasonicsDataDesc
+ *
+ * @param ultrasonicsArrayId A unique identifier of the ultrasonic array.
+ * @return evsUltrasonicsArray IEvsUltrasonicsArray object associated with a
+ * given ultrasonicsArrayId.
+ */
+ openUltrasonicsArray(string ultrasonicsArrayId) generates (
+ IEvsUltrasonicsArray evsUltrasonicsArray);
+
+ /**
+ * Return the specified IEvsUltrasonicsArray interface as no longer in use
+ *
+ * When the IEvsUltrasonicsArray object is no longer required, it must be released.
+ * NOTE: Data streaming must be cleanly stopped before making this call.
+ *
+ * @param evsUltrasonicsArray EvsUltrasonics array object to be closed.
+ */
+ closeUltrasonicsArray(IEvsUltrasonicsArray evsUltrasonicsArray);
};
diff --git a/automotive/evs/1.1/IEvsUltrasonicsArray.hal b/automotive/evs/1.1/IEvsUltrasonicsArray.hal
new file mode 100644
index 0000000..ae4f941
--- /dev/null
+++ b/automotive/evs/1.1/IEvsUltrasonicsArray.hal
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.evs@1.1;
+
+import @1.0::EvsResult;
+import UltrasonicsArrayDesc;
+import UltrasonicsDataFrameDesc;
+import IEvsUltrasonicsArrayStream;
+
+/**
+ * HAL interface for ultrasonics sensor array.
+ */
+interface IEvsUltrasonicsArray {
+ /**
+ * Returns the ultrasonic sensor array information.
+ *
+ * @return info The description of this ultrasonic array. This must be the
+ * same value as reported by IEvsEnumerator::getUltrasonicsArrayList().
+ */
+ getUltrasonicArrayInfo() generates (UltrasonicsArrayDesc info);
+
+ /**
+ * Specifies the depth of the buffer chain the ultrasonic sensors is
+ * asked to support.
+ *
+ * Up to this many data frames may be held concurrently by the client of IEvsUltrasonicsArray.
+ * If this many frames have been delivered to the receiver without being returned
+ * by doneWithFrame, the stream must skip frames until a buffer is returned for reuse.
+ * It is legal for this call to come at any time, even while streams are already running,
+ * in which case buffers should be added or removed from the chain as appropriate.
+ * If no call is made to this entry point, the IEvsUltrasonicsArray must support at least one
+ * data frame by default. More is acceptable.
+ *
+ * @param bufferCount Number of buffers the client of
+ * IEvsUltrasonicsArray may hold concurrently.
+ * @return result EvsResult::OK is returned if this call is successful.
+ * Will return EvsResult::INVALID_ARG on invalid bufferCount.
+ */
+ setMaxFramesInFlight(uint32_t bufferCount) generates (EvsResult result);
+
+ /**
+ * Requests to start the stream.
+ *
+ * @param stream Implementation of IEvsUltrasonicsArrayStream.
+ * @return result EvsResult::OK is returned if this call is successful. Returns
+ * EvsResult::STREAM_ALREADY_RUNNING if stream is already running.
+ */
+ startStream(IEvsUltrasonicsArrayStream stream) generates (EvsResult result);
+
+ /**
+ * Requests to stop the delivery of the ultrasonic array data frames.
+ *
+ * Because delivery is asynchronous, frames may continue to arrive for
+ * some time after this call returns. Each must be returned until the
+ * closure of the stream is signaled to the IEvsCameraStream.
+ * This function cannot fail and is ignored if the stream isn't running.
+ */
+ stopStream();
+
+ /**
+ * Notifies the UltrasonicsDataDesc is consumed that was received from
+ * IEvsUltrasonicsArrayStream.
+ *
+ * @param dataFrameDesc Ultrasonics data descriptor.
+ */
+ doneWithDataFrame(UltrasonicsDataFrameDesc dataFrameDesc);
+};
diff --git a/automotive/evs/1.1/IEvsUltrasonicsArrayStream.hal b/automotive/evs/1.1/IEvsUltrasonicsArrayStream.hal
new file mode 100644
index 0000000..f95209f
--- /dev/null
+++ b/automotive/evs/1.1/IEvsUltrasonicsArrayStream.hal
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.evs@1.1;
+
+import UltrasonicsDataFrameDesc;
+import @1.1::EvsEventDesc;
+
+/**
+ * Implemented on client side to receive asynchronous ultrasonic data
+ * deliveries.
+ */
+interface IEvsUltrasonicsArrayStream {
+ /**
+ * Receives calls from the HAL each time a data frame is ready.
+ *
+ * @param dataFrameDesc Ultrasonic array data frame descriptor.
+ */
+ oneway deliverDataFrame(UltrasonicsDataFrameDesc dataFrameDesc);
+
+ /**
+ * Receives calls from the HAL each time an event happens.
+ *
+ * @param event Event EVS event with possible event information.
+ */
+ oneway notify(EvsEventDesc event);
+};
diff --git a/automotive/evs/1.1/default/Android.bp b/automotive/evs/1.1/default/Android.bp
index a35c9db..d843167 100644
--- a/automotive/evs/1.1/default/Android.bp
+++ b/automotive/evs/1.1/default/Android.bp
@@ -10,6 +10,7 @@
"EvsDisplay.cpp",
"ConfigManager.cpp",
"ConfigManagerUtil.cpp",
+ "EvsUltrasonicsArray.cpp",
],
init_rc: ["android.hardware.automotive.evs@1.1-service.rc"],
@@ -17,11 +18,14 @@
"android.hardware.automotive.evs@1.0",
"android.hardware.automotive.evs@1.1",
"android.hardware.camera.device@3.3",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
"libbase",
"libbinder",
"liblog",
"libhardware",
"libhidlbase",
+ "libhidlmemory",
"liblog",
"libui",
"libutils",
diff --git a/automotive/evs/1.1/default/EvsCamera.cpp b/automotive/evs/1.1/default/EvsCamera.cpp
index f9cdb88..5196c95 100644
--- a/automotive/evs/1.1/default/EvsCamera.cpp
+++ b/automotive/evs/1.1/default/EvsCamera.cpp
@@ -334,6 +334,27 @@
}
+Return<EvsResult> EvsCamera::setExtendedInfo_1_1(uint32_t opaqueIdentifier,
+ const hidl_vec<uint8_t>& opaqueValue) {
+ // Default implementation does not use an extended info.
+ (void)opaqueIdentifier;
+ (void)opaqueValue;
+ return EvsResult::INVALID_ARG;
+}
+
+
+Return<void> EvsCamera::getExtendedInfo_1_1(uint32_t opaqueIdentifier,
+ getExtendedInfo_1_1_cb _hidl_cb) {
+ // Default implementation does not use an extended info.
+ (void)opaqueIdentifier;
+
+ hidl_vec<uint8_t> value;
+ _hidl_cb(EvsResult::INVALID_ARG, value);
+ return Void();
+}
+
+
+
bool EvsCamera::setAvailableFrames_Locked(unsigned bufferCount) {
if (bufferCount < 1) {
ALOGE("Ignoring request to set buffer count to zero");
diff --git a/automotive/evs/1.1/default/EvsCamera.h b/automotive/evs/1.1/default/EvsCamera.h
index a49db46..0fa83b4 100644
--- a/automotive/evs/1.1/default/EvsCamera.h
+++ b/automotive/evs/1.1/default/EvsCamera.h
@@ -78,6 +78,10 @@
setIntParameter_cb _hidl_cb) override;
Return<void> getIntParameter(CameraParam id,
getIntParameter_cb _hidl_cb) override;
+ Return<EvsResult> setExtendedInfo_1_1(uint32_t opaqueIdentifier,
+ const hidl_vec<uint8_t>& opaqueValue) override;
+ Return<void> getExtendedInfo_1_1(uint32_t opaqueIdentifier,
+ getExtendedInfo_1_1_cb _hidl_cb) override;
static sp<EvsCamera> Create(const char *deviceName);
static sp<EvsCamera> Create(const char *deviceName,
diff --git a/automotive/evs/1.1/default/EvsEnumerator.cpp b/automotive/evs/1.1/default/EvsEnumerator.cpp
index 0319560..117ee7a 100644
--- a/automotive/evs/1.1/default/EvsEnumerator.cpp
+++ b/automotive/evs/1.1/default/EvsEnumerator.cpp
@@ -19,6 +19,7 @@
#include "EvsEnumerator.h"
#include "EvsCamera.h"
#include "EvsDisplay.h"
+#include "EvsUltrasonicsArray.h"
namespace android {
namespace hardware {
@@ -31,12 +32,12 @@
// NOTE: All members values are static so that all clients operate on the same state
// That is to say, this is effectively a singleton despite the fact that HIDL
// constructs a new instance for each client.
-std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
-wp<EvsDisplay> EvsEnumerator::sActiveDisplay;
-unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
-sp<IAutomotiveDisplayProxyService> EvsEnumerator::sDisplayProxyService;
-std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
-
+std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
+wp<EvsDisplay> EvsEnumerator::sActiveDisplay;
+unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
+sp<IAutomotiveDisplayProxyService> EvsEnumerator::sDisplayProxyService;
+std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
+std::list<EvsEnumerator::UltrasonicsArrayRecord> EvsEnumerator::sUltrasonicsArrayRecordList;
EvsEnumerator::EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService) {
ALOGD("EvsEnumerator created");
@@ -66,6 +67,10 @@
}
});
}
+
+ // Add ultrasonics array desc.
+ sUltrasonicsArrayRecordList.emplace_back(
+ EvsUltrasonicsArray::GetDummyArrayDesc("front_array"));
}
@@ -355,6 +360,102 @@
return pRecord;
}
+EvsEnumerator::UltrasonicsArrayRecord* EvsEnumerator::findUltrasonicsArrayById(
+ const std::string& ultrasonicsArrayId) {
+ auto recordIt = std::find_if(
+ sUltrasonicsArrayRecordList.begin(), sUltrasonicsArrayRecordList.end(),
+ [&ultrasonicsArrayId](const UltrasonicsArrayRecord& record) {
+ return ultrasonicsArrayId == record.desc.ultrasonicsArrayId;});
+
+ return (recordIt != sUltrasonicsArrayRecordList.end()) ? &*recordIt : nullptr;
+}
+
+Return<void> EvsEnumerator::getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb) {
+ hidl_vec<UltrasonicsArrayDesc> desc;
+ desc.resize(sUltrasonicsArrayRecordList.size());
+
+ // Copy over desc from sUltrasonicsArrayRecordList.
+ for (auto p = std::make_pair(sUltrasonicsArrayRecordList.begin(), desc.begin());
+ p.first != sUltrasonicsArrayRecordList.end(); p.first++, p.second++) {
+ *p.second = p.first->desc;
+ }
+
+ // Send back the results
+ ALOGD("reporting %zu ultrasonics arrays available", desc.size());
+ _hidl_cb(desc);
+
+ // HIDL convention says we return Void if we sent our result back via callback
+ return Void();
+}
+
+Return<sp<IEvsUltrasonicsArray>> EvsEnumerator::openUltrasonicsArray(
+ const hidl_string& ultrasonicsArrayId) {
+ // Find the named ultrasonic array.
+ UltrasonicsArrayRecord* pRecord = findUltrasonicsArrayById(ultrasonicsArrayId);
+
+ // Is this a recognized ultrasonic array id?
+ if (!pRecord) {
+ ALOGE("Requested ultrasonics array %s not found", ultrasonicsArrayId.c_str());
+ return nullptr;
+ }
+
+ // Has this ultrasonic array already been instantiated by another caller?
+ sp<EvsUltrasonicsArray> pActiveUltrasonicsArray = pRecord->activeInstance.promote();
+ if (pActiveUltrasonicsArray != nullptr) {
+ ALOGW("Killing previous ultrasonics array because of new caller");
+ closeUltrasonicsArray(pActiveUltrasonicsArray);
+ }
+
+ // Construct a ultrasonic array instance for the caller
+ pActiveUltrasonicsArray = EvsUltrasonicsArray::Create(ultrasonicsArrayId.c_str());
+ pRecord->activeInstance = pActiveUltrasonicsArray;
+ if (pActiveUltrasonicsArray == nullptr) {
+ ALOGE("Failed to allocate new EvsUltrasonicsArray object for %s\n",
+ ultrasonicsArrayId.c_str());
+ }
+
+ return pActiveUltrasonicsArray;
+}
+
+Return<void> EvsEnumerator::closeUltrasonicsArray(
+ const sp<IEvsUltrasonicsArray>& pEvsUltrasonicsArray) {
+
+ if (pEvsUltrasonicsArray.get() == nullptr) {
+ ALOGE("Ignoring call to closeUltrasonicsArray with null ultrasonics array");
+ return Void();
+ }
+
+ // Get the ultrasonics array id so we can find it in our list.
+ std::string ultrasonicsArrayId;
+ pEvsUltrasonicsArray->getUltrasonicArrayInfo([&ultrasonicsArrayId](UltrasonicsArrayDesc desc) {
+ ultrasonicsArrayId.assign(desc.ultrasonicsArrayId);
+ });
+
+ // Find the named ultrasonics array
+ UltrasonicsArrayRecord* pRecord = findUltrasonicsArrayById(ultrasonicsArrayId);
+ if (!pRecord) {
+ ALOGE("Asked to close a ultrasonics array whose name isnt not found");
+ return Void();
+ }
+
+ sp<EvsUltrasonicsArray> pActiveUltrasonicsArray = pRecord->activeInstance.promote();
+
+ if (pActiveUltrasonicsArray.get() == nullptr) {
+ ALOGE("Somehow a ultrasonics array is being destroyed when the enumerator didn't know "
+ "one existed");
+ } else if (pActiveUltrasonicsArray != pEvsUltrasonicsArray) {
+ // This can happen if the ultrasonics array was aggressively reopened,
+ // orphaning this previous instance
+ ALOGW("Ignoring close of previously orphaned ultrasonics array - why did a client steal?");
+ } else {
+ // Drop the active ultrasonics array
+ pActiveUltrasonicsArray->forceShutdown();
+ pRecord->activeInstance = nullptr;
+ }
+
+ return Void();
+}
+
} // namespace implementation
} // namespace V1_1
} // namespace evs
diff --git a/automotive/evs/1.1/default/EvsEnumerator.h b/automotive/evs/1.1/default/EvsEnumerator.h
index 9415953..d80124b 100644
--- a/automotive/evs/1.1/default/EvsEnumerator.h
+++ b/automotive/evs/1.1/default/EvsEnumerator.h
@@ -21,6 +21,7 @@
#include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
#include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArray.h>
#include <list>
@@ -46,6 +47,7 @@
class EvsCamera; // from EvsCamera.h
class EvsDisplay; // from EvsDisplay.h
+class EvsUltrasonicsArray; // from EvsUltrasonicsArray.h
class EvsEnumerator : public IEvsEnumerator {
@@ -65,6 +67,11 @@
Return<bool> isHardware() override { return true; }
Return<void> getDisplayIdList(getDisplayIdList_cb _list_cb) override;
Return<sp<IEvsDisplay_1_1>> openDisplay_1_1(uint8_t port) override;
+ Return<void> getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb) override;
+ Return<sp<IEvsUltrasonicsArray>> openUltrasonicsArray(
+ const hidl_string& ultrasonicsArrayId) override;
+ Return<void> closeUltrasonicsArray(
+ const ::android::sp<IEvsUltrasonicsArray>& evsUltrasonicsArray) override;
// Implementation details
EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService = nullptr);
@@ -80,10 +87,21 @@
CameraRecord(const char *cameraId) : desc() { desc.v1.cameraId = cameraId; }
};
+ struct UltrasonicsArrayRecord {
+ UltrasonicsArrayDesc desc;
+ wp<EvsUltrasonicsArray> activeInstance;
+
+ UltrasonicsArrayRecord(const UltrasonicsArrayDesc& arrayDesc) : desc(arrayDesc) {};
+ };
+
static CameraRecord* findCameraById(const std::string& cameraId);
static std::list<CameraRecord> sCameraList;
+ static UltrasonicsArrayRecord* findUltrasonicsArrayById(const std::string& ultrasonicsArrayId);
+
+ static std::list<UltrasonicsArrayRecord> sUltrasonicsArrayRecordList;
+
// Weak pointer. Object destructs if client dies.
static wp<EvsDisplay> sActiveDisplay;
diff --git a/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp b/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp
new file mode 100644
index 0000000..bc69aa4
--- /dev/null
+++ b/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp
@@ -0,0 +1,551 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EvsUltrasonicsArray.h"
+
+#include <android-base/logging.h>
+#include <hidlmemory/mapping.h>
+#include <log/log.h>
+#include <time.h>
+#include <utils/SystemClock.h>
+#include <utils/Timers.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_1 {
+namespace implementation {
+
+// Arbitrary limit on number of data frames allowed to be allocated
+// Safeguards against unreasonable resource consumption and provides a testable limit
+const unsigned int kMaximumDataFramesInFlight = 100;
+
+const uint32_t kMaxReadingsPerSensor = 5;
+const uint32_t kMaxReceiversCount = 3;
+
+const unsigned int kSharedMemoryMaxSize =
+ kMaxReadingsPerSensor * kMaxReceiversCount * 2 * sizeof(float);
+
+// Target frame rate in frames per second.
+const int kTargetFrameRate = 10;
+
+namespace {
+
+void fillDummyArrayDesc(UltrasonicsArrayDesc& arrayDesc) {
+ arrayDesc.maxReadingsPerSensorCount = kMaxReadingsPerSensor;
+ arrayDesc.maxReceiversCount = kMaxReceiversCount;
+
+ const int kSensorCount = 3;
+ const float kMaxRange = 4000; // 4 metres.
+ const float kAngleOfMeasurement = 0.261799; // 15 degrees.
+
+ std::vector<UltrasonicSensor> sensors(kSensorCount);
+
+ // Sensor pointing forward on left side of front bumper.
+ sensors[0].maxRange = kMaxRange;
+ sensors[0].angleOfMeasurement = kAngleOfMeasurement;
+ sensors[0].pose = {{1, 0, 0, 0}, {-1000, 2000, 200}};
+
+ // Sensor pointing forward on center of front bumper.
+ sensors[1].maxRange = kMaxRange;
+ sensors[1].angleOfMeasurement = kAngleOfMeasurement;
+ sensors[1].pose = {{1, 0, 0, 0}, {0, 2000, 200}};
+
+ // Sensor pointing forward on right side of front bumper.
+ sensors[2].maxRange = kMaxRange;
+ sensors[2].angleOfMeasurement = kAngleOfMeasurement;
+ sensors[2].pose = {{1, 0, 0, 0}, {1000, 2000, 200}};
+
+ arrayDesc.sensors = sensors;
+}
+
+// Struct used by SerializeWaveformData().
+struct WaveformData {
+ uint8_t receiverId;
+ std::vector<std::pair<float, float>> readings;
+};
+
+// Serializes data provided in waveformDataList to a shared memory data pointer.
+// TODO(b/149950362): Add a common library for serialiazing and deserializing waveform data.
+void SerializeWaveformData(const std::vector<WaveformData>& waveformDataList, uint8_t* pData) {
+ for (auto& waveformData : waveformDataList) {
+ // Set Id
+ memcpy(pData, &waveformData.receiverId, sizeof(uint8_t));
+ pData += sizeof(uint8_t);
+
+ for (auto& reading : waveformData.readings) {
+ // Set the time of flight.
+ memcpy(pData, &reading.first, sizeof(float));
+ pData += sizeof(float);
+
+ // Set the resonance.
+ memcpy(pData, &reading.second, sizeof(float));
+ pData += sizeof(float);
+ }
+ }
+}
+
+// Fills dataFrameDesc with dummy data.
+bool fillDummyDataFrame(UltrasonicsDataFrameDesc& dataFrameDesc, sp<IMemory> pIMemory) {
+ dataFrameDesc.timestampNs = elapsedRealtimeNano();
+
+ const std::vector<uint8_t> transmittersIdList = {0};
+ dataFrameDesc.transmittersIdList = transmittersIdList;
+
+ const std::vector<uint8_t> recvIdList = {0, 1, 2};
+ dataFrameDesc.receiversIdList = recvIdList;
+
+ const std::vector<uint32_t> receiversReadingsCountList = {2, 2, 4};
+ dataFrameDesc.receiversReadingsCountList = receiversReadingsCountList;
+
+ const std::vector<WaveformData> waveformDataList = {
+ {recvIdList[0], { {1000, 0.1f}, {2000, 0.8f} }},
+ {recvIdList[1], { {1000, 0.1f}, {2000, 1.0f} }},
+ {recvIdList[2], { {1000, 0.1f}, {2000, 0.2f}, {4000, 0.2f}, {5000, 0.1f} }}
+ };
+
+ if (pIMemory.get() == nullptr) {
+ return false;
+ }
+
+ uint8_t* pData = (uint8_t*)((void*)pIMemory->getPointer());
+
+ pIMemory->update();
+ SerializeWaveformData(waveformDataList, pData);
+ pIMemory->commit();
+
+ return true;
+}
+
+} // namespace
+
+EvsUltrasonicsArray::EvsUltrasonicsArray(const char* deviceName)
+ : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED) {
+ LOG(DEBUG) << "EvsUltrasonicsArray instantiated";
+
+ // Set up dummy data for description.
+ mArrayDesc.ultrasonicsArrayId = deviceName;
+ fillDummyArrayDesc(mArrayDesc);
+
+ // Assign allocator.
+ mShmemAllocator = IAllocator::getService("ashmem");
+ if (mShmemAllocator.get() == nullptr) {
+ LOG(ERROR) << "SurroundViewHidlTest getService ashmem failed";
+ }
+}
+
+sp<EvsUltrasonicsArray> EvsUltrasonicsArray::Create(const char* deviceName) {
+ return sp<EvsUltrasonicsArray>(new EvsUltrasonicsArray(deviceName));
+}
+
+EvsUltrasonicsArray::~EvsUltrasonicsArray() {
+ LOG(DEBUG) << "EvsUltrasonicsArray being destroyed";
+ forceShutdown();
+}
+
+// This gets called if another caller "steals" ownership of the ultrasonic array.
+void EvsUltrasonicsArray::forceShutdown() {
+ LOG(DEBUG) << "EvsUltrasonicsArray forceShutdown";
+
+ // Make sure our output stream is cleaned up
+ // (It really should be already)
+ stopStream();
+
+ // Claim the lock while we work on internal state
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ // Drop all the data frames we've been using
+ for (auto&& dataFrame : mDataFrames) {
+ if (dataFrame.inUse) {
+ LOG(ERROR) << "Error - releasing data frame despite remote ownership";
+ }
+ dataFrame.sharedMemory.clear();
+ }
+ mDataFrames.clear();
+
+ // Put this object into an unrecoverable error state since somebody else
+ // is going to own the underlying ultrasonic array now
+ mStreamState = DEAD;
+}
+
+UltrasonicsArrayDesc EvsUltrasonicsArray::GetDummyArrayDesc(const char* deviceName) {
+ UltrasonicsArrayDesc ultrasonicsArrayDesc;
+ ultrasonicsArrayDesc.ultrasonicsArrayId = deviceName;
+ fillDummyArrayDesc(ultrasonicsArrayDesc);
+ return ultrasonicsArrayDesc;
+}
+
+Return<void> EvsUltrasonicsArray::getUltrasonicArrayInfo(getUltrasonicArrayInfo_cb _get_info_cb) {
+ LOG(DEBUG) << "EvsUltrasonicsArray getUltrasonicsArrayInfo";
+
+ // Return the description for the get info callback.
+ _get_info_cb(mArrayDesc);
+
+ return Void();
+}
+
+Return<EvsResult> EvsUltrasonicsArray::setMaxFramesInFlight(uint32_t bufferCount) {
+ LOG(DEBUG) << "EvsUltrasonicsArray setMaxFramesInFlight";
+
+ // Lock mutex for performing changes to available frames.
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ // We cannot function without at least one buffer to send data.
+ if (bufferCount < 1) {
+ LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested";
+ return EvsResult::INVALID_ARG;
+ }
+
+ // Update our internal state of buffer count.
+ if (setAvailableFrames_Locked(bufferCount)) {
+ return EvsResult::OK;
+ } else {
+ return EvsResult::BUFFER_NOT_AVAILABLE;
+ }
+
+ return EvsResult::OK;
+}
+
+Return<void> EvsUltrasonicsArray::doneWithDataFrame(const UltrasonicsDataFrameDesc& dataFrameDesc) {
+ LOG(DEBUG) << "EvsUltrasonicsArray doneWithFrame";
+
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (dataFrameDesc.dataFrameId >= mDataFrames.size()) {
+ LOG(ERROR) << "ignoring doneWithFrame called with invalid dataFrameId "
+ << dataFrameDesc.dataFrameId << "(max is " << mDataFrames.size() - 1 << ")";
+ return Void();
+ }
+
+ if (!mDataFrames[dataFrameDesc.dataFrameId].inUse) {
+ LOG(ERROR) << "ignoring doneWithFrame called on frame " << dataFrameDesc.dataFrameId
+ << "which is already free";
+ return Void();
+ }
+
+ // Mark the frame as available
+ mDataFrames[dataFrameDesc.dataFrameId].inUse = false;
+ mFramesInUse--;
+
+ // If this frame's index is high in the array, try to move it down
+ // to improve locality after mFramesAllowed has been reduced.
+ if (dataFrameDesc.dataFrameId >= mFramesAllowed) {
+ // Find an empty slot lower in the array (which should always exist in this case)
+ for (auto&& dataFrame : mDataFrames) {
+ if (!dataFrame.sharedMemory.IsValid()) {
+ dataFrame.sharedMemory = mDataFrames[dataFrameDesc.dataFrameId].sharedMemory;
+ mDataFrames[dataFrameDesc.dataFrameId].sharedMemory.clear();
+ return Void();
+ }
+ }
+ }
+
+ return Void();
+}
+
+Return<EvsResult> EvsUltrasonicsArray::startStream(
+ const ::android::sp<IEvsUltrasonicsArrayStream>& stream) {
+ LOG(DEBUG) << "EvsUltrasonicsArray startStream";
+
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (mStreamState != STOPPED) {
+ LOG(ERROR) << "ignoring startStream call when a stream is already running.";
+ return EvsResult::STREAM_ALREADY_RUNNING;
+ }
+
+ // If the client never indicated otherwise, configure ourselves for a single streaming buffer
+ if (mFramesAllowed < 1) {
+ if (!setAvailableFrames_Locked(1)) {
+ LOG(ERROR)
+ << "Failed to start stream because we couldn't get shared memory data buffer";
+ return EvsResult::BUFFER_NOT_AVAILABLE;
+ }
+ }
+
+ // Record the user's callback for use when we have a frame ready
+ mStream = stream;
+
+ // Start the frame generation thread
+ mStreamState = RUNNING;
+ mCaptureThread = std::thread([this]() { generateDataFrames(); });
+
+ return EvsResult::OK;
+}
+
+Return<void> EvsUltrasonicsArray::stopStream() {
+ LOG(DEBUG) << "EvsUltrasonicsArray stopStream";
+
+ bool streamStateStopping = false;
+ {
+ std::lock_guard<std::mutex> lock(mAccessLock);
+ if (mStreamState == RUNNING) {
+ // Tell the GenerateFrames loop we want it to stop
+ mStreamState = STOPPING;
+ streamStateStopping = true;
+ }
+ }
+
+ if (streamStateStopping) {
+ // Block outside the mutex until the "stop" flag has been acknowledged
+ // We won't send any more frames, but the client might still get some already in flight
+ LOG(DEBUG) << "Waiting for stream thread to end...";
+ mCaptureThread.join();
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mAccessLock);
+ mStreamState = STOPPED;
+ mStream = nullptr;
+ LOG(DEBUG) << "Stream marked STOPPED.";
+ }
+
+ return Void();
+}
+
+bool EvsUltrasonicsArray::setAvailableFrames_Locked(unsigned bufferCount) {
+ if (bufferCount < 1) {
+ LOG(ERROR) << "Ignoring request to set buffer count to zero";
+ return false;
+ }
+ if (bufferCount > kMaximumDataFramesInFlight) {
+ LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
+ return false;
+ }
+
+ // Is an increase required?
+ if (mFramesAllowed < bufferCount) {
+ // An increase is required
+ unsigned needed = bufferCount - mFramesAllowed;
+ LOG(INFO) << "Number of data frame buffers to add: " << needed;
+
+ unsigned added = increaseAvailableFrames_Locked(needed);
+ if (added != needed) {
+ // If we didn't add all the frames we needed, then roll back to the previous state
+ LOG(ERROR) << "Rolling back to previous frame queue size";
+ decreaseAvailableFrames_Locked(added);
+ return false;
+ }
+ } else if (mFramesAllowed > bufferCount) {
+ // A decrease is required
+ unsigned framesToRelease = mFramesAllowed - bufferCount;
+ LOG(INFO) << "Number of data frame buffers to reduce: " << framesToRelease;
+
+ unsigned released = decreaseAvailableFrames_Locked(framesToRelease);
+ if (released != framesToRelease) {
+ // This shouldn't happen with a properly behaving client because the client
+ // should only make this call after returning sufficient outstanding buffers
+ // to allow a clean resize.
+ LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?";
+ }
+ }
+
+ return true;
+}
+
+EvsUltrasonicsArray::SharedMemory EvsUltrasonicsArray::allocateAndMapSharedMemory() {
+ SharedMemory sharedMemory;
+
+ // Check shared memory allocator is valid.
+ if (mShmemAllocator.get() == nullptr) {
+ LOG(ERROR) << "Shared memory allocator not initialized.";
+ return SharedMemory();
+ }
+
+ // Allocate memory.
+ bool allocateSuccess = false;
+ Return<void> result = mShmemAllocator->allocate(kSharedMemoryMaxSize,
+ [&](bool success, const hidl_memory& hidlMem) {
+ if (!success) {
+ return;
+ }
+ allocateSuccess = success;
+ sharedMemory.hidlMemory = hidlMem;
+ });
+
+ // Check result of allocated memory.
+ if (!result.isOk() || !allocateSuccess) {
+ LOG(ERROR) << "Shared memory allocation failed.";
+ return SharedMemory();
+ }
+
+ // Map shared memory.
+ sharedMemory.pIMemory = mapMemory(sharedMemory.hidlMemory);
+ if (sharedMemory.pIMemory.get() == nullptr) {
+ LOG(ERROR) << "Shared memory mapping failed.";
+ return SharedMemory();
+ }
+
+ // Return success.
+ return sharedMemory;
+}
+
+unsigned EvsUltrasonicsArray::increaseAvailableFrames_Locked(unsigned numToAdd) {
+ unsigned added = 0;
+
+ while (added < numToAdd) {
+ SharedMemory sharedMemory = allocateAndMapSharedMemory();
+
+ // If allocate and map fails, break.
+ if (!sharedMemory.IsValid()) {
+ break;
+ }
+
+ // Find a place to store the new buffer
+ bool stored = false;
+ for (auto&& dataFrame : mDataFrames) {
+ if (!dataFrame.sharedMemory.IsValid()) {
+ // Use this existing entry
+ dataFrame.sharedMemory = sharedMemory;
+ dataFrame.inUse = false;
+ stored = true;
+ break;
+ }
+ }
+
+ if (!stored) {
+ // Add a BufferRecord wrapping this handle to our set of available buffers
+ mDataFrames.emplace_back(sharedMemory);
+ }
+
+ mFramesAllowed++;
+ added++;
+ }
+
+ return added;
+}
+
+unsigned EvsUltrasonicsArray::decreaseAvailableFrames_Locked(unsigned numToRemove) {
+ unsigned removed = 0;
+
+ for (auto&& dataFrame : mDataFrames) {
+ // Is this record not in use, but holding a buffer that we can free?
+ if (!dataFrame.inUse && dataFrame.sharedMemory.IsValid()) {
+ // Release buffer and update the record so we can recognize it as "empty"
+ dataFrame.sharedMemory.clear();
+
+ mFramesAllowed--;
+ removed++;
+
+ if (removed == numToRemove) {
+ break;
+ }
+ }
+ }
+
+ return removed;
+}
+
+// This is the asynchronous data frame generation thread that runs in parallel with the
+// main serving thread. There is one for each active ultrasonic array instance.
+void EvsUltrasonicsArray::generateDataFrames() {
+ LOG(DEBUG) << "Data frame generation loop started";
+
+ unsigned idx = 0;
+
+ while (true) {
+ bool timeForFrame = false;
+
+ nsecs_t startTime = elapsedRealtimeNano();
+
+ // Lock scope for updating shared state
+ {
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (mStreamState != RUNNING) {
+ // Break out of our main thread loop
+ break;
+ }
+
+ // Are we allowed to issue another buffer?
+ if (mFramesInUse >= mFramesAllowed) {
+ // Can't do anything right now -- skip this frame
+ LOG(WARNING) << "Skipped a frame because too many are in flight";
+ } else {
+ // Identify an available buffer to fill
+ for (idx = 0; idx < mDataFrames.size(); idx++) {
+ if (!mDataFrames[idx].inUse && mDataFrames[idx].sharedMemory.IsValid()) {
+ // Found an available record, so stop looking
+ break;
+ }
+ }
+ if (idx >= mDataFrames.size()) {
+ // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
+ LOG(ERROR) << "Failed to find an available buffer slot";
+ } else {
+ // We're going to make the frame busy
+ mDataFrames[idx].inUse = true;
+ mFramesInUse++;
+ timeForFrame = true;
+ }
+ }
+ }
+
+ if (timeForFrame) {
+ // Assemble the buffer description we'll transmit below
+ UltrasonicsDataFrameDesc dummyDataFrameDesc;
+ dummyDataFrameDesc.dataFrameId = idx;
+ dummyDataFrameDesc.waveformsData = mDataFrames[idx].sharedMemory.hidlMemory;
+
+ // Fill dummy waveform data.
+ fillDummyDataFrame(dummyDataFrameDesc, mDataFrames[idx].sharedMemory.pIMemory);
+
+ // Issue the (asynchronous) callback to the client -- can't be holding the lock
+ auto result = mStream->deliverDataFrame(dummyDataFrameDesc);
+ if (result.isOk()) {
+ LOG(DEBUG) << "Delivered data frame id: " << dummyDataFrameDesc.dataFrameId;
+ } else {
+ // This can happen if the client dies and is likely unrecoverable.
+ // To avoid consuming resources generating failing calls, we stop sending
+ // frames. Note, however, that the stream remains in the "STREAMING" state
+ // until cleaned up on the main thread.
+ LOG(ERROR) << "Frame delivery call failed in the transport layer.";
+
+ // Since we didn't actually deliver it, mark the frame as available
+ std::lock_guard<std::mutex> lock(mAccessLock);
+ mDataFrames[idx].inUse = false;
+ mFramesInUse--;
+
+ break;
+ }
+ }
+
+ // Sleep to generate frames at kTargetFrameRate.
+ static const nsecs_t kTargetFrameTimeUs = 1000 * 1000 / kTargetFrameRate;
+ const nsecs_t now = elapsedRealtimeNano();
+ const nsecs_t workTimeUs = (now - startTime) / 1000;
+ const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
+ if (sleepDurationUs > 0) {
+ usleep(sleepDurationUs);
+ }
+ }
+
+ // If we've been asked to stop, send an event to signal the actual end of stream
+ EvsEventDesc event;
+ event.aType = EvsEventType::STREAM_STOPPED;
+ auto result = mStream->notify(event);
+ if (!result.isOk()) {
+ LOG(ERROR) << "Error delivering end of stream marker";
+ }
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/evs/1.1/default/EvsUltrasonicsArray.h b/automotive/evs/1.1/default/EvsUltrasonicsArray.h
new file mode 100644
index 0000000..7a41012
--- /dev/null
+++ b/automotive/evs/1.1/default/EvsUltrasonicsArray.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSULTRASONICSARRAY_H
+#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSULTRASONICSARRAY_H
+
+#include <thread>
+#include <utility>
+
+#include <android-base/macros.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <utils/threads.h>
+
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArray.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArrayStream.h>
+#include <android/hardware/automotive/evs/1.1/types.h>
+
+using ::android::hardware::hidl_memory;
+using ::android::hardware::automotive::evs::V1_0::EvsResult;
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray;
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream;
+using ::android::hardware::automotive::evs::V1_1::UltrasonicsArrayDesc;
+using ::android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_1 {
+namespace implementation {
+
+class EvsUltrasonicsArray : public IEvsUltrasonicsArray {
+ public:
+ // Methods from ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray follow.
+ Return<void> getUltrasonicArrayInfo(getUltrasonicArrayInfo_cb _get_info_cb) override;
+ Return<EvsResult> setMaxFramesInFlight(uint32_t bufferCount) override;
+ Return<void> doneWithDataFrame(const UltrasonicsDataFrameDesc& dataFrameDesc) override;
+ Return<EvsResult> startStream(const ::android::sp<IEvsUltrasonicsArrayStream>& stream) override;
+ Return<void> stopStream() override;
+
+ // Factory function to create a array.
+ static sp<EvsUltrasonicsArray> Create(const char* deviceName);
+
+ // Returns a ultrasonics array descriptor filled with sample data.
+ static UltrasonicsArrayDesc GetDummyArrayDesc(const char* id);
+
+ DISALLOW_COPY_AND_ASSIGN(EvsUltrasonicsArray);
+ virtual ~EvsUltrasonicsArray() override;
+ void forceShutdown(); // This gets called if another caller "steals" ownership
+
+ private:
+ // Structure holding the hidl memory struct and the interface to a shared memory.
+ struct SharedMemory {
+ hidl_memory hidlMemory;
+ sp<IMemory> pIMemory;
+
+ SharedMemory() : hidlMemory(hidl_memory()), pIMemory(nullptr){};
+
+ SharedMemory(hidl_memory hidlMem, sp<IMemory> pIMem)
+ : hidlMemory(hidlMem), pIMemory(pIMem) {}
+
+ bool IsValid() { return (pIMemory.get() != nullptr && hidlMemory.valid()); }
+
+ void clear() {
+ hidlMemory = hidl_memory();
+ pIMemory.clear();
+ }
+ };
+
+ // Struct for a data frame record.
+ struct DataFrameRecord {
+ SharedMemory sharedMemory;
+ bool inUse;
+ explicit DataFrameRecord(SharedMemory shMem) : sharedMemory(shMem), inUse(false){};
+ };
+
+ enum StreamStateValues {
+ STOPPED,
+ RUNNING,
+ STOPPING,
+ DEAD,
+ };
+
+ EvsUltrasonicsArray(const char* deviceName);
+
+ // These three functions are expected to be called while mAccessLock is held
+ bool setAvailableFrames_Locked(unsigned bufferCount);
+ unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
+ unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
+
+ void generateDataFrames();
+
+ SharedMemory allocateAndMapSharedMemory();
+
+ UltrasonicsArrayDesc mArrayDesc = {}; // The properties of this ultrasonic array.
+
+ std::thread mCaptureThread; // The thread we'll use to synthesize frames
+
+ sp<IEvsUltrasonicsArrayStream> mStream = nullptr; // The callback used to deliver each frame
+
+ sp<IAllocator> mShmemAllocator = nullptr; // Shared memory allocator.
+
+ std::mutex mAccessLock;
+ std::vector<DataFrameRecord> mDataFrames GUARDED_BY(mAccessLock); // Shared memory buffers.
+ unsigned mFramesAllowed GUARDED_BY(mAccessLock); // How many buffers are we currently using.
+ unsigned mFramesInUse GUARDED_BY(mAccessLock); // How many buffers are currently outstanding.
+
+ StreamStateValues mStreamState GUARDED_BY(mAccessLock);
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace evs
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSULTRASONICSARRAY_H
diff --git a/automotive/evs/1.1/types.hal b/automotive/evs/1.1/types.hal
index aafdb70..b34e7e7 100644
--- a/automotive/evs/1.1/types.hal
+++ b/automotive/evs/1.1/types.hal
@@ -177,3 +177,194 @@
*/
ABSOLUTE_ZOOM,
};
+
+/**
+ * Structure identifies and describes an ultrasonics array in the car.
+ *
+ * A ultrasonics array represents a group of ultrasonic sensors within the
+ * car. These may be sensors that are physically connected to the same hardware
+ * control unit or represent a logical group of sensors like front and back.
+ * The HAL is responsible for filling out this structure for each Ultrasonics
+ * Array.
+ */
+struct UltrasonicsArrayDesc {
+ /**
+ * Unique identifier for the ultrasonic array. This may be a path or name of the
+ * physical control device or a string identifying a logical group of sensors forming an array
+ * such as "front_array" and "back_array".
+ */
+ string ultrasonicsArrayId;
+
+ /**
+ * Maximum number of readings (points on waveform) provided per sensor in
+ * each data frame. Used by client to pre-allocate required memory buffer for
+ * incoming data.
+ */
+ uint32_t maxReadingsPerSensorCount;
+
+ /**
+ * Maximum number of receiver sensors in a data frame. Must be between 1
+ * and sensorCount. Used by client to pre-allocate required memory buffer for
+ * incoming data.
+ */
+ uint32_t maxReceiversCount;
+
+ /**
+ * The order of sensors specified should preferably be in clockwise order
+ * around the car, starting from front left-most sensor.
+ */
+ vec<UltrasonicSensor> sensors;
+};
+
+/**
+ * Structure for rotation expressed as quaternions.
+ * Convention used: Unit quaternion with hamilton convention.
+ */
+struct RotationQuat {
+ float x;
+ float y;
+ float z;
+ float w;
+};
+
+/** Structure for translation with x, y and z units. */
+struct Translation {
+ float x;
+ float y;
+ float z;
+};
+
+/**
+ * Provides the orientation and location of a car sensor relative to the android automotive
+ * coordinate system:
+ * https://source.android.com/devices/sensors/sensor-types#auto_axes
+ * The sensor pose defines the transformation to be applied to the android automotive axes to
+ * obtain the sensor local axes.
+ * The pose consists of rotation, (specified as a quaternions) and translation
+ * (vector with x, y, z).
+ * This rotation and translation applied to the sensor data in the sensor's local coordinate
+ * system transform the data to the automotive coordinate system.
+ * i.e Pcar = ( Rot * Psensor ) + Trans
+ * Here Pcar is a point in automotive coordinate system and Psensor is a point in the sensor's
+ * coordinate system.
+ * Example:
+ * For a sensor on the front bumper and on the left corner of the car with its X axis pointing to
+ * the front, the sensor is located at (-2, 4, 0) meters w.r.t android automotive axes and the
+ * sensor local axes has a rotation of 90 degrees counter-clockwise w.r.t android automotive axes
+ * when viewing the car from top on the +Z axis side:
+ *
+ * ↑X sensor
+ * Y←∘______
+ * | | front
+ * | car |
+ * | ↑Y |
+ * | ∘→X | rear
+ * |______|
+ *
+ * For this example the rotation and translation will be:
+ * Rotation = + 90 degrees around Z axis = (0.7071, 0, 0, 0.7071) as a unit quaternion.
+ * Translation = (-2, 4, 0) in meters = (-2000, 4000, 0) in milli-meters.
+ * Note: Every sensor type must specify its own pose.
+ */
+struct SensorPose {
+ /**
+ * Rotation part of the sensor pose, expressed as a unit quaternion.
+ */
+ RotationQuat rotation;
+
+ /**
+ * Translation part of the sensor pose, in (x, y, z) format with milli-meter units.
+ */
+ Translation translation;
+};
+
+/**
+ * Structure that contains all information of an ultrasonic sensor.
+ */
+struct UltrasonicSensor {
+ /**
+ * Pose provides the orientation and location of the ultrasonic sensor within the car.
+ * The +Y axis points along the center of the beam spread the X axis to the right and the Z
+ * axis in the up direction.
+ */
+ SensorPose pose;
+
+ /**
+ * Maximum range of the sensor in milli-metres.
+ */
+ float maxRange;
+
+ /**
+ * Half-angle of the angle of measurement of the sensor, relative to the
+ * sensor’s x axis, in radians.
+ */
+ float angleOfMeasurement;
+};
+
+/**
+ * Structure that describes the data frame received from an ultrasonics array.
+ *
+ * Each data frame returned consists of received waveform signals from a subset
+ * of sensors in an array as indicated by the receiversIdList. The signal is
+ * transmitted at a particular time instant indicated by timestampNs from a
+ * subset of sensors in the array as provided in the transmittersIdList.
+ */
+struct UltrasonicsDataFrameDesc {
+ /**
+ * Timestamp of the start of the transmit signal for this data frame.
+ * Timestamp unit is nanoseconds and is obtained from android elapsed realtime clock which is
+ * the time since system was booted and includes deep sleep.
+ * timeOfFlight readings are future-deltas to this timestamp.
+ */
+ uint64_t timestampNs;
+
+ /**
+ * Identifier of data frame. Used by implementation for managing multiple frames in flight.
+ */
+ uint32_t dataFrameId;
+
+ /**
+ * List of indexes of sensors in range [0, sensorCount - 1] that
+ * transmitted the signal for this data frame.
+ */
+ vec<uint8_t> transmittersIdList;
+
+ /**
+ * List of indexes of sensors in range [0, sensorCount - 1] that received
+ * the signal. The order of ids must match the order of the waveforms in the
+ * waveformsData.
+ * Size of list is upper bound by maxReceiversCount.
+ */
+ vec<uint8_t> receiversIdList;
+
+ /**
+ * List of the number of readings corresponding to each ultrasonics sensor in
+ * the receiversIdList. Order of the readings count must match the order in
+ * receiversIdList.
+ * Size of list is upper bound by maxReadingsPerSensorCount.
+ */
+ vec<uint32_t> receiversReadingsCountList;
+
+ /**
+ * Shared memory object containing the waveforms data. Contains one waveform
+ * for each sensor specified in receiversIdList, in order.
+ * Each waveform is represented by a number of readings, which are sample
+ * points on the waveform. The number of readings for each waveform is as
+ * specified in the receiversReadingsCountList.
+ * Each reading is a pair of time Of flight and resonance.
+ * Time of flight (float): Time between transmit and receive signal in nanoseconds.
+ * Resonance (float): Resonance at time on waveform in range [0.0, 1.0].
+ *
+ * The structure of shared memory (example with 2 waveforms, each with 2 readings):
+ *
+ * Byte: | 0 | 1-4 | 5-8 | 9-12 | 13-16 || 17 | 18-21 | 22-25 | 26-29 | 30-33 |
+ * Data: | RecId1 | TOF1 | RES1 | TOF2 | RES2 || RecId2 | TOF1 | RES1 | TOF2 | RES2 |
+ * | Waveform1 || Waveform2 |
+ * Here:
+ * RecId : Receiver's Id. Order matches the receiversIdList, type uint8_t
+ * TOF : Time of flight, type float (4 bytes)
+ * RES : Resonance, type float (4 bytes)
+ * Note: All readings and waveforms are contigious with no padding.
+ */
+ memory waveformsData;
+};
diff --git a/automotive/evs/1.1/vts/functional/Android.bp b/automotive/evs/1.1/vts/functional/Android.bp
index 4753933..086a199 100644
--- a/automotive/evs/1.1/vts/functional/Android.bp
+++ b/automotive/evs/1.1/vts/functional/Android.bp
@@ -18,12 +18,16 @@
name: "VtsHalEvsV1_1TargetTest",
srcs: [
"FrameHandler.cpp",
+ "FrameHandlerUltrasonics.cpp",
"VtsHalEvsV1_1TargetTest.cpp",
],
defaults: ["VtsHalTargetTestDefaults"],
shared_libs: [
"libui",
"libcamera_metadata",
+ "libhidlmemory",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
],
static_libs: [
"android.hardware.automotive.evs@1.0",
@@ -34,7 +38,7 @@
"android.hardware.graphics.common@1.2",
"android.hardware.camera.device@3.2",
],
- test_suites: ["general-tests"],
+ test_suites: ["vts-core"],
cflags: [
"-O0",
"-g",
diff --git a/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.cpp b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.cpp
new file mode 100644
index 0000000..22522ce
--- /dev/null
+++ b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FrameHandlerUltrasonics.h"
+
+#include <android-base/logging.h>
+#include <hidlmemory/mapping.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::hardware::Return;
+using ::android::sp;
+
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream;
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray;
+using ::android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc;
+using ::android::hardware::automotive::evs::V1_1::EvsEventDesc;
+using ::android::hardware::automotive::evs::V1_1::EvsEventType;
+
+FrameHandlerUltrasonics::FrameHandlerUltrasonics(sp<IEvsUltrasonicsArray> pEvsUltrasonicsArray) :
+ mEvsUltrasonicsArray(pEvsUltrasonicsArray), mReceiveFramesCount(0) {
+ // Nothing but member initialization
+}
+
+Return<void> FrameHandlerUltrasonics::notify(const EvsEventDesc& evsEvent) {
+ switch (evsEvent.aType) {
+ case EvsEventType::STREAM_STARTED:
+ case EvsEventType::STREAM_STOPPED:
+ case EvsEventType::FRAME_DROPPED:
+ case EvsEventType::TIMEOUT:
+ mReceivedEvents.emplace_back(evsEvent);
+ break;
+ default:
+ LOG(ERROR) << "Received unexpected event";
+ }
+
+ return android::hardware::Void();
+}
+
+// Struct used by SerializeWaveformData().
+struct WaveformData {
+ uint8_t receiverId;
+ std::vector<std::pair<float, float>> readings;
+};
+
+// De-serializes shared memory to vector of WaveformData.
+// TODO(b/149950362): Add a common library for serialiazing and deserializing waveform data.
+std::vector<WaveformData> DeSerializeWaveformData(std::vector<uint32_t> recvReadingsCountList,
+ uint8_t* pData) {
+ std::vector<WaveformData> waveformDataList(recvReadingsCountList.size());
+
+ for (int i = 0; i < waveformDataList.size(); i++) {
+ // Set Id
+ memcpy(&waveformDataList[i].receiverId, pData, sizeof(uint8_t));
+ pData += sizeof(uint8_t);
+
+ waveformDataList[i].readings.resize(recvReadingsCountList[i]);
+
+ for (auto& reading : waveformDataList[i].readings) {
+ // Set the time of flight.
+ memcpy(&reading.first, pData, sizeof(float));
+ pData += sizeof(float);
+
+ // Set the resonance.
+ memcpy(&reading.second, pData, sizeof(float));
+ pData += sizeof(float);
+ }
+ }
+ return waveformDataList;
+}
+
+bool DataFrameValidator(const UltrasonicsDataFrameDesc& dataFrameDesc) {
+
+ if (dataFrameDesc.receiversIdList.size() != dataFrameDesc.receiversReadingsCountList.size()) {
+ LOG(ERROR) << "Size mismatch of receiversIdList and receiversReadingsCountList";
+ return false;
+ }
+
+ if(!dataFrameDesc.waveformsData.valid()) {
+ LOG(ERROR) << "Data frame does not valid hidl memory";
+ return false;
+ }
+
+ // Check total bytes from dataFrameDesc are within the shared memory size.
+ int totalWaveformDataBytesSize = 0;
+ for (int i = 0; i < dataFrameDesc.receiversIdList.size(); i++) {
+ totalWaveformDataBytesSize = 1 + (4 * 2 * dataFrameDesc.receiversReadingsCountList[i]);
+ }
+ if (totalWaveformDataBytesSize > dataFrameDesc.waveformsData.size()) {
+ LOG(ERROR) << "Total waveform data bytes in desc exceed shared memory size";
+ return false;
+ }
+
+ sp<IMemory> pIMemory = mapMemory(dataFrameDesc.waveformsData);
+ if(pIMemory.get() == nullptr) {
+ LOG(ERROR) << "Failed to map hidl memory";
+ return false;
+ }
+
+ uint8_t* pData = (uint8_t*)((void*)pIMemory->getPointer());
+ if(pData == nullptr) {
+ LOG(ERROR) << "Failed getPointer from mapped shared memory";
+ return false;
+ }
+
+ const std::vector<WaveformData> waveformDataList = DeSerializeWaveformData(
+ dataFrameDesc.receiversReadingsCountList, pData);
+
+ // Verify the waveforms data.
+ for(int i = 0; i < waveformDataList.size(); i++) {
+ if (waveformDataList[i].receiverId != dataFrameDesc.receiversIdList[i]) {
+ LOG(ERROR) << "Receiver Id mismatch";
+ return false;
+ }
+ for(auto& reading : waveformDataList[i].readings) {
+ if (reading.second < 0.0f || reading.second > 1.0f) {
+ LOG(ERROR) << "Resonance reading is not in range [0, 1]";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+Return<void> FrameHandlerUltrasonics::deliverDataFrame(
+ const UltrasonicsDataFrameDesc& dataFrameDesc) {
+ LOG(DEBUG) << "FrameHandlerUltrasonics::receiveFrames";
+
+ mReceiveFramesCount++;
+ mLastReceivedFrames = dataFrameDesc;
+
+ if(!DataFrameValidator(dataFrameDesc)) {
+ mAllFramesValid = false;
+ }
+
+ // Send done with data frame.
+ mEvsUltrasonicsArray->doneWithDataFrame(dataFrameDesc);
+
+ return android::hardware::Void();
+}
+
+bool FrameHandlerUltrasonics::checkEventReceived(EvsEventDesc evsEvent) {
+ LOG(DEBUG) << "FrameHandlerUltrasonics::checkEventReceived";
+ int size = mReceivedEvents.size(); // work around
+ LOG(DEBUG) << "Received event number: " << size;
+ auto iter = find(mReceivedEvents.begin(), mReceivedEvents.end(), evsEvent);
+ return iter != mReceivedEvents.end();
+}
+
+int FrameHandlerUltrasonics::getReceiveFramesCount() {
+ return mReceiveFramesCount;
+}
+
+bool FrameHandlerUltrasonics::areAllFramesValid() {
+ return mAllFramesValid;
+}
diff --git a/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.h b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.h
new file mode 100644
index 0000000..1fc2143
--- /dev/null
+++ b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAME_HANDLER_ULTRASONICS_H
+#define FRAME_HANDLER_ULTRASONICS_H
+
+#include <android/hardware/automotive/evs/1.1/types.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArrayStream.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArray.h>
+
+#include <vector>
+
+class FrameHandlerUltrasonics : public
+ android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream {
+public:
+ FrameHandlerUltrasonics(
+ android::sp<android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray>
+ pEvsUltrasonicsArray);
+
+ // Implementation for ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream
+ android::hardware::Return<void> notify(
+ const android::hardware::automotive::evs::V1_1::EvsEventDesc& evsEvent) override;
+ android::hardware::Return<void> deliverDataFrame(
+ const android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc&
+ dataFrameDesc) override;
+
+ bool checkEventReceived(android::hardware::automotive::evs::V1_1::EvsEventDesc evsEvent);
+ int getReceiveFramesCount();
+ bool areAllFramesValid();
+
+private:
+ android::sp<android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray>
+ mEvsUltrasonicsArray;
+ android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc mLastReceivedFrames;
+ std::vector<android::hardware::automotive::evs::V1_1::EvsEventDesc> mReceivedEvents;
+ int mReceiveFramesCount;
+ bool mAllFramesValid = true;
+};
+
+#endif //FRAME_HANDLER_ULTRASONICS_H
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index ce02973..8580500 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -37,6 +37,7 @@
#include "FrameHandler.h"
+#include "FrameHandlerUltrasonics.h"
#include <cstdio>
#include <cstring>
@@ -151,9 +152,21 @@
}
}
);
+ }
- // We insist on at least one camera for EVS to pass any camera tests
- ASSERT_GE(cameraInfo.size(), 1u);
+ void loadUltrasonicsArrayList() {
+ // SetUp() must run first!
+ assert(pEnumerator != nullptr);
+
+ // Get the ultrasonics array list
+ pEnumerator->getUltrasonicsArrayList([this](hidl_vec<UltrasonicsArrayDesc> ultraList) {
+ ALOGI("Ultrasonics array list callback received %zu arrays", ultraList.size());
+ ultrasonicsArraysInfo.reserve(ultraList.size());
+ for (auto&& ultraArray : ultraList) {
+ ALOGI("Found ultrasonics array %s", ultraArray.ultrasonicsArrayId.c_str());
+ ultrasonicsArraysInfo.push_back(ultraArray);
+ }
+ });
}
bool isLogicalCamera(const camera_metadata_t *metadata) {
@@ -240,6 +253,11 @@
// is HW module implementation.
std::deque<wp<IEvsCamera_1_1>> activeCameras; // A list of active camera handles that are
// needed to be cleaned up.
+ std::vector<UltrasonicsArrayDesc>
+ ultrasonicsArraysInfo; // Empty unless/until
+ // loadUltrasonicsArrayList() is called
+ std::deque<wp<IEvsCamera_1_1>> activeUltrasonicsArrays; // A list of active ultrasonic array
+ // handles that are to be cleaned up.
};
@@ -296,6 +314,17 @@
}
);
+ // Verify methods for extended info
+ const auto id = 0xFFFFFFFF; // meaningless id
+ hidl_vec<uint8_t> values;
+ auto err = pCam->setExtendedInfo_1_1(id, values);
+ ASSERT_EQ(EvsResult::INVALID_ARG, err);
+
+ pCam->getExtendedInfo_1_1(id, [](const auto& result, const auto& data) {
+ ASSERT_EQ(EvsResult::INVALID_ARG, result);
+ ASSERT_EQ(0, data.size());
+ });
+
// Explicitly close the camera so resources are released right away
pEnumerator->closeCamera(pCam);
}
@@ -2209,6 +2238,110 @@
}
+/*
+ * UltrasonicsArrayOpenClean:
+ * Opens each ultrasonics arrays reported by the enumerator and then explicitly closes it via a
+ * call to closeUltrasonicsArray. Then repeats the test to ensure all ultrasonics arrays
+ * can be reopened.
+ */
+TEST_F(EvsHidlTest, UltrasonicsArrayOpenClean) {
+ ALOGI("Starting UltrasonicsArrayOpenClean test");
+
+ // Get the ultrasonics array list
+ loadUltrasonicsArrayList();
+
+ // Open and close each ultrasonics array twice
+ for (auto&& ultraInfo : ultrasonicsArraysInfo) {
+ for (int pass = 0; pass < 2; pass++) {
+ sp<IEvsUltrasonicsArray> pUltrasonicsArray =
+ pEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId);
+ ASSERT_NE(pUltrasonicsArray, nullptr);
+
+ // Verify that this ultrasonics array self-identifies correctly
+ pUltrasonicsArray->getUltrasonicArrayInfo([&ultraInfo](UltrasonicsArrayDesc desc) {
+ ALOGD("Found ultrasonics array %s", ultraInfo.ultrasonicsArrayId.c_str());
+ EXPECT_EQ(ultraInfo.ultrasonicsArrayId, desc.ultrasonicsArrayId);
+ });
+
+ // Explicitly close the ultrasonics array so resources are released right away
+ pEnumerator->closeUltrasonicsArray(pUltrasonicsArray);
+ }
+ }
+}
+
+
+// Starts a stream and verifies all data received is valid.
+TEST_F(EvsHidlTest, UltrasonicsVerifyStreamData) {
+ ALOGI("Starting UltrasonicsVerifyStreamData");
+
+ // Get the ultrasonics array list
+ loadUltrasonicsArrayList();
+
+ // For each ultrasonics array.
+ for (auto&& ultraInfo : ultrasonicsArraysInfo) {
+ ALOGD("Testing ultrasonics array: %s", ultraInfo.ultrasonicsArrayId.c_str());
+
+ sp<IEvsUltrasonicsArray> pUltrasonicsArray =
+ pEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId);
+ ASSERT_NE(pUltrasonicsArray, nullptr);
+
+ sp<FrameHandlerUltrasonics> frameHandler = new FrameHandlerUltrasonics(pUltrasonicsArray);
+
+ // Start stream.
+ EvsResult result = pUltrasonicsArray->startStream(frameHandler);
+ ASSERT_EQ(result, EvsResult::OK);
+
+ // Wait 5 seconds to receive frames.
+ sleep(5);
+
+ // Stop stream.
+ pUltrasonicsArray->stopStream();
+
+ EXPECT_GT(frameHandler->getReceiveFramesCount(), 0);
+ EXPECT_TRUE(frameHandler->areAllFramesValid());
+
+ // Explicitly close the ultrasonics array so resources are released right away
+ pEnumerator->closeUltrasonicsArray(pUltrasonicsArray);
+ }
+}
+
+
+// Sets frames in flight before and after start of stream and verfies success.
+TEST_F(EvsHidlTest, UltrasonicsSetFramesInFlight) {
+ ALOGI("Starting UltrasonicsSetFramesInFlight");
+
+ // Get the ultrasonics array list
+ loadUltrasonicsArrayList();
+
+ // For each ultrasonics array.
+ for (auto&& ultraInfo : ultrasonicsArraysInfo) {
+ ALOGD("Testing ultrasonics array: %s", ultraInfo.ultrasonicsArrayId.c_str());
+
+ sp<IEvsUltrasonicsArray> pUltrasonicsArray =
+ pEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId);
+ ASSERT_NE(pUltrasonicsArray, nullptr);
+
+ EvsResult result = pUltrasonicsArray->setMaxFramesInFlight(10);
+ EXPECT_EQ(result, EvsResult::OK);
+
+ sp<FrameHandlerUltrasonics> frameHandler = new FrameHandlerUltrasonics(pUltrasonicsArray);
+
+ // Start stream.
+ result = pUltrasonicsArray->startStream(frameHandler);
+ ASSERT_EQ(result, EvsResult::OK);
+
+ result = pUltrasonicsArray->setMaxFramesInFlight(5);
+ EXPECT_EQ(result, EvsResult::OK);
+
+ // Stop stream.
+ pUltrasonicsArray->stopStream();
+
+ // Explicitly close the ultrasonics array so resources are released right away
+ pEnumerator->closeUltrasonicsArray(pUltrasonicsArray);
+ }
+}
+
+
int main(int argc, char** argv) {
::testing::AddGlobalTestEnvironment(EvsHidlEnvironment::Instance());
::testing::InitGoogleTest(&argc, argv);
diff --git a/automotive/sv/1.0/Android.bp b/automotive/sv/1.0/Android.bp
new file mode 100644
index 0000000..769bdc6
--- /dev/null
+++ b/automotive/sv/1.0/Android.bp
@@ -0,0 +1,24 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.automotive.sv@1.0",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "ISurroundViewStream.hal",
+ "ISurroundViewSession.hal",
+ "ISurroundView2dSession.hal",
+ "ISurroundView3dSession.hal",
+ "ISurroundViewService.hal",
+ ],
+ interfaces: [
+ "android.hidl.base@1.0",
+ "android.hardware.graphics.common@1.0",
+ "android.hardware.graphics.common@1.1",
+ "android.hardware.graphics.common@1.2",
+ ],
+ gen_java: true,
+}
diff --git a/automotive/sv/1.0/ISurroundView2dSession.hal b/automotive/sv/1.0/ISurroundView2dSession.hal
new file mode 100644
index 0000000..fa49674
--- /dev/null
+++ b/automotive/sv/1.0/ISurroundView2dSession.hal
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+import ISurroundViewSession;
+
+/**
+ * Interface representing a surround view 2d session.
+ *
+ * Surround view 2d provides a top/bird's eye view of the car and its surroundings.
+ */
+interface ISurroundView2dSession extends ISurroundViewSession {
+
+ /**
+ * Gets mapping information for 2d surround view.
+ *
+ * Mapping information maps the output frame of 2d surround view to actual dimensions
+ * covered on the ground. Mapping information is fixed for a car and is based upon its camera
+ * coverage. Mapping information can be used for doing overlays of objects in 3d space
+ * onto the surround view 2d output frame.
+ *
+ * @param sv2dConfig Configuration to set.
+ * @return sv2dMappingInfo mapping information of the 2d surround view.
+ */
+ get2dMappingInfo() generates (Sv2dMappingInfo sv2dMappingInfo);
+
+ /**
+ * Sets the configuration of 2d surround view.
+ *
+ * Configuration is used for supported different target use-cases of the surround view eg.
+ * fullscreen or preview. Default configuration is FULLSCREEN.
+ * A set config call can be performed at any time (before or after startStream) of the session.
+ * Once config change is complete, a CONFIG_CHANGED event is sent, after which
+ * all frames received will be of the updated config.
+ *
+ * @param sv2dConfig Configuration to set.
+ * @return svResult Returns OK if successful, appropriate error result otherwise.
+ */
+ set2dConfig(Sv2dConfig sv2dConfig) generates (SvResult svResult);
+
+ /**
+ * Gets the current configuration of the 2d surround view.
+ *
+ * Configuration is used for supported different target use-cases of the surround view eg.
+ * fullscreen view or preview. Use setConfig call to set a configuration.
+ *
+ * @return sv2dConfig the active current configuration of the 2d session.
+ */
+ get2dConfig() generates (Sv2dConfig sv2dConfig);
+
+ /**
+ * Projects points on camera image to surround view 2d image.
+ *
+ * Useful for mapping points detected on individual camera frames onto the surround view 2d
+ * output frame.
+ *
+ * @param cameraPoints List of camera pixel points to be projected in range including (0, 0)
+ * and (width - 1, height -1) of camera frame. If point is outside camera
+ frame INVALID_ARG error is returned.
+ * @param cameraId Id of the EvsCamera to use for projecting points. Id must be one of the
+ * cameras as returned by getCameraIds() else INVALID_ARG error is returned
+ * @return points2d Returns a list of 2d pixel points projecting into surround view 2d
+ * frame in the same order as cameraPoints. Point projected maybe outside
+ * surround view frame i.e. outside (0, 0) and
+ * (sv_width - 1, sv_height - 1). Points that do not project to ground
+ * plane are set with inValid true.
+ */
+ projectCameraPoints(vec<Point2dInt> cameraPoints, string cameraId) generates (
+ vec<Point2dFloat> points2d);
+};
diff --git a/automotive/sv/1.0/ISurroundView3dSession.hal b/automotive/sv/1.0/ISurroundView3dSession.hal
new file mode 100644
index 0000000..d2b0c53
--- /dev/null
+++ b/automotive/sv/1.0/ISurroundView3dSession.hal
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+import ISurroundViewSession;
+
+/**
+ * Interface representing a surround view 3d session.
+ *
+ * Surround view 3d provides a virtual view from any desired position in the 3d space around the
+ * car. Surround view 3d creates an approximate 3d surface around the car to match the surrounds
+ * and provides a virtual view as seen on this surface.
+ */
+interface ISurroundView3dSession extends ISurroundViewSession {
+
+ /**
+ * Sets the desired views of surround view 3d.
+ *
+ * Surround view 3d takes a list of desired virtual view points and provides an output frame
+ * for each view. Default view list is a single view from behind the car front facing in the
+ * front direction.
+ * A call to setViews() results in the views set by a previous call to be discarded.
+ * Each view set is identified by an Id which is returned with the corresponding output frame
+ * of that view.
+ * Clients can call setViews() at any stage of the session (before/after startStream). Client
+ * may continue to receive frames of previous views after setViews() call for a while and can
+ * identify updated set of views once effective using the view Id provided in the updated
+ * views frames.
+ *
+ * @param views List of desired views to generate output frames.
+ * @return svResult Returns OK if successful, appropriate error result otherwise.
+ */
+ setViews(vec<View3d> views) generates (SvResult svResult);
+
+ /**
+ * Sets the configuration of 3d surround view.
+ *
+ * Configuration is used for supported different target use-cases of the surround view eg.
+ * fullscreen view or preview. A set config call can be performed at anytime (before or after
+ * startStream) of the session.
+ * Once config change is complete, a CONFIG_CHANGED event is sent, after which
+ * all frames received will be of the updated config.
+ *
+ * @param sv3dConfig Configuration to set.
+ * @return svResult Returns OK if successful, appropriate error result otherwise.
+ */
+ set3dConfig(Sv3dConfig sv3dConfig) generates (SvResult svResult);
+
+ /**
+ * Gets the current configuration of the 3d surround view.
+ *
+ * Configuration is used for supported different target use-cases of the surround view eg.
+ * fullscreen view or preview. Use setConfig call to set a configuration.
+ *
+ * @return sv3dConfig The current active configuration of the 3d session.
+ */
+ get3dConfig() generates (Sv3dConfig sv3dConfig);
+
+ /**
+ * Updates 3d overlays in scene.
+ *
+ * updateOverlays() provides a way to set a 3d overlay object in the scene. An overlay is an
+ * 3d object in the scene which can be a visual indicator to provide additional information eg.
+ * parking sensor distance indicators or overlays for objects in scene.
+ *
+ * An overlay object is defined by a set of points (forming triangles) with some color and
+ * transparency values and each overlay is identified by an overlay Id.
+ * When an overlay with a new Id is passed, a new overlay is added to the scene.
+ * When an overlay with previous id is passed, its vertices/color are updated with passed data.
+ * If the overlay data is empty, the overlay is removed from the scene.
+ *
+ * @param overlaysData Object with shared memory containing overlays to add/update in the
+ * scene. Refer to OverlaysData structure for layout in shared memory.
+ * @return svResult Returns OK if successful, appropriate error result otherwise.
+ */
+ updateOverlays(OverlaysData overlaysData) generates (SvResult svResult);
+
+ /**
+ * Projects points on camera image to surround view 3D surface.
+ *
+ * Useful for mapping points detected on individual camera frames onto the surround view 3D
+ * surface, these 3d points can then be used to set overlays using the updateOverlays() for
+ * the detected objects in the scene.
+ * Note:
+ * 3d points returned are projected on an approximate 3d surface and do not provide the exact
+ * 3d location.
+ *
+ * @param cameraPoints List of camera pixel points to be projected in range including (0, 0)
+ * and (width - 1, height -1) of camera frame. If point is outside camera
+ frame INVALID_ARG error is returned.
+ * @param cameraId Id of the EvsCamera to use for projecting points. Id must be one of the
+ * cameras as returned by getCameraIds() else INVALID_ARG error is returned
+ * @return points3d Returns a list of 3d points on the approximate 3d surface in the
+ * automotive coordinate system in the same order as cameraPoints.
+ * Points that do not project to 3d surface are set with inValid true.
+ */
+ projectCameraPointsTo3dSurface(vec<Point2dInt> cameraPoints, string cameraId) generates (
+ vec<Point3dFloat> points3d);
+};
diff --git a/automotive/sv/1.0/ISurroundViewService.hal b/automotive/sv/1.0/ISurroundViewService.hal
new file mode 100644
index 0000000..7de0bd1
--- /dev/null
+++ b/automotive/sv/1.0/ISurroundViewService.hal
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+import ISurroundView2dSession;
+import ISurroundView3dSession;
+
+/**
+ * Interface representing entry point for surround view.
+ *
+ * Surround view service has two types of sessions 2d and 3d. Refer to their respective interface
+ * for more details.
+ */
+interface ISurroundViewService {
+
+ /**
+ * Gets a list of camera ids that are used for generating surround view.
+ * For 4 camera configuration, the cameras ids are ordered in clockwise direction
+ * when viewed from top of the car starting with the front camera. i.e. FRONT, RIGHT, REAR and
+ * LEFT. All other configurations must follow clockwise order.
+ *
+ * @result cameraIds List of camera ids that matching the Id of EVS Cameras used by service.
+ */
+ getCameraIds() generates (vec<string> cameraIds);
+
+ /**
+ * Starts a surround view 2d session.
+ *
+ * @result sv2dSession Returns a new 2d session that was created.
+ * result Returns OK if successful, appropriate error result otherwise.
+ */
+ start2dSession() generates (ISurroundView2dSession sv2dSession, SvResult result);
+
+ /**
+ * Stops a surround view 2d session.
+ *
+ * @param sv2dSession Valid 2d session to be stopped.
+ * @return svResult Returns OK if successful, appropriate error result otherwise.
+ */
+ stop2dSession(ISurroundView2dSession sv2dSession) generates (SvResult result);
+
+ /**
+ * Starts a surround view 3d session.
+ *
+ * @result sv3dSession Returns a new 3d session that was created.
+ * result Returns OK if successful, appropriate error result otherwise.
+ */
+ start3dSession() generates (ISurroundView3dSession sv3dSession, SvResult result);
+
+ /**
+ * Stops a surround view 2d session.
+ *
+ * @param sv2dSession Valid 2d session to be stopped.
+ * @return svResult Returns OK if successful, appropriate error result otherwise.
+ */
+ stop3dSession(ISurroundView3dSession sv3dSession) generates (SvResult result);
+};
diff --git a/automotive/sv/1.0/ISurroundViewSession.hal b/automotive/sv/1.0/ISurroundViewSession.hal
new file mode 100644
index 0000000..62cfac0
--- /dev/null
+++ b/automotive/sv/1.0/ISurroundViewSession.hal
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+import ISurroundViewStream;
+
+/**
+ * Common interface for surround view session extended by surround view 2d and 3d
+ * session.
+ */
+interface ISurroundViewSession {
+ /**
+ * Requests to start receiving surround view frames.
+ *
+ * For surround view 3d, setViews() must be set before calling startStream().
+ *
+ * @param stream Stream to receiving callbacks for the session.
+ * @return svResult Returns OK if successful, returns VIEW_NOT_SET if setViews() is not
+ * called for surround view 3d, appropriate error results otherwise.
+ */
+ startStream(ISurroundViewStream stream) generates (SvResult svResult);
+
+ /**
+ * Requests to stop stream.
+ *
+ * Frames may continue to arrive after call returns. Each must be returned until
+ * the closure of the stream is signaled by the ISurroundViewStream.
+ */
+ stopStream();
+
+ /**
+ * Signal from client that a frame, which was delivered by the stream, has been consumed.
+ *
+ * @param svFramesDesc Descriptor to signal done with frame.
+ */
+ oneway doneWithFrames(SvFramesDesc svFramesDesc);
+};
diff --git a/automotive/sv/1.0/ISurroundViewStream.hal b/automotive/sv/1.0/ISurroundViewStream.hal
new file mode 100644
index 0000000..22d610f
--- /dev/null
+++ b/automotive/sv/1.0/ISurroundViewStream.hal
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+/**
+ * Interface representing a surround view stream.
+ *
+ * This interface is to be implemented by client to receive callback for output frames and events.
+ */
+interface ISurroundViewStream {
+ /**
+ * Receives callback of surround view 2d/3d frames.
+ *
+ * @param svFramesDesc Frames descriptor containing the output frames.
+ */
+ oneway receiveFrames(SvFramesDesc svFramesDesc);
+
+ /**
+ * Receives callback for surround view events.
+ *
+ * @param svEvent Surround view event.
+ */
+ oneway notify(SvEvent svEvent);
+};
diff --git a/automotive/sv/1.0/default/Android.bp b/automotive/sv/1.0/default/Android.bp
new file mode 100644
index 0000000..8417949
--- /dev/null
+++ b/automotive/sv/1.0/default/Android.bp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+ name: "android.hardware.automotive.sv@1.0-service",
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "service.cpp",
+ "SurroundViewService.cpp",
+ "SurroundView2dSession.cpp",
+ "SurroundView3dSession.cpp",
+ ],
+ init_rc: ["android.hardware.automotive.sv@1.0-service.rc"],
+ vintf_fragments: ["android.hardware.automotive.sv@1.0-service.xml"],
+ shared_libs: [
+ "android.hardware.automotive.sv@1.0",
+ "android.hidl.memory@1.0",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libhardware",
+ "libhidlbase",
+ "liblog",
+ "libui",
+ "libutils",
+ "libhidlmemory",
+ ],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+}
diff --git a/automotive/sv/1.0/default/SurroundView2dSession.cpp b/automotive/sv/1.0/default/SurroundView2dSession.cpp
new file mode 100644
index 0000000..4f97598
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundView2dSession.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SurroundView2dSession.h"
+
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+SurroundView2dSession::SurroundView2dSession() :
+ mStreamState(STOPPED) {
+ mEvsCameraIds = {"0" , "1", "2", "3"};
+
+ mConfig.width = 640;
+ mConfig.blending = SvQuality::HIGH;
+
+ framesRecord.frames.svBuffers.resize(1);
+ framesRecord.frames.svBuffers[0].viewId = 0;
+ framesRecord.frames.svBuffers[0].hardwareBuffer.nativeHandle =
+ new native_handle_t();
+ framesRecord.frames.svBuffers[0].hardwareBuffer.description[0] =
+ mConfig.width;
+ framesRecord.frames.svBuffers[0].hardwareBuffer.description[1] =
+ mConfig.width * 3 / 4;
+}
+
+// Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession
+Return<SvResult> SurroundView2dSession::startStream(
+ const sp<ISurroundViewStream>& stream) {
+ ALOGD("SurroundView2dSession::startStream");
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (mStreamState != STOPPED) {
+ ALOGE("ignoring startVideoStream call"
+ "when a stream is already running.");
+ return SvResult::INTERNAL_ERROR;
+ }
+
+ mStream = stream;
+
+ ALOGD("Notify SvEvent::STREAM_STARTED");
+ mStream->notify(SvEvent::STREAM_STARTED);
+
+ // Start the frame generation thread
+ mStreamState = RUNNING;
+ mCaptureThread = std::thread([this](){ generateFrames(); });
+
+ return SvResult::OK;
+}
+
+Return<void> SurroundView2dSession::stopStream() {
+ ALOGD("SurroundView2dSession::stopStream");
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ if (mStreamState == RUNNING) {
+ // Tell the GenerateFrames loop we want it to stop
+ mStreamState = STOPPING;
+
+ // Block outside the mutex until the "stop" flag has been acknowledged
+ // We won't send any more frames, but the client might still get some
+ // already in flight
+ ALOGD("Waiting for stream thread to end...");
+ lock.unlock();
+ mCaptureThread.join();
+ lock.lock();
+
+ mStreamState = STOPPED;
+ mStream = nullptr;
+ ALOGD("Stream marked STOPPED.");
+ }
+
+ return android::hardware::Void();
+}
+
+Return<void> SurroundView2dSession::doneWithFrames(
+ const SvFramesDesc& svFramesDesc){
+ ALOGD("SurroundView2dSession::doneWithFrames");
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ framesRecord.inUse = false;
+
+ (void)svFramesDesc;
+ return android::hardware::Void();
+}
+
+// Methods from ISurroundView2dSession follow.
+Return<void> SurroundView2dSession::get2dMappingInfo(
+ get2dMappingInfo_cb _hidl_cb) {
+ ALOGD("SurroundView2dSession::get2dMappingInfo");
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ Sv2dMappingInfo info;
+ info.width = 8; // keeps ratio to 4:3
+ info.height = 6;
+ info.center.isValid = true;
+ info.center.x = 0;
+ info.center.y = 0;
+ _hidl_cb(info);
+ return android::hardware::Void();
+}
+
+Return<SvResult> SurroundView2dSession::set2dConfig(
+ const Sv2dConfig& sv2dConfig) {
+ ALOGD("SurroundView2dSession::setConfig");
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ mConfig.width = sv2dConfig.width;
+ mConfig.blending = sv2dConfig.blending;
+ ALOGD("Notify SvEvent::CONFIG_UPDATED");
+ mStream->notify(SvEvent::CONFIG_UPDATED);
+
+ return SvResult::OK;
+}
+
+Return<void> SurroundView2dSession::get2dConfig(get2dConfig_cb _hidl_cb) {
+ ALOGD("SurroundView2dSession::getConfig");
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ _hidl_cb(mConfig);
+ return android::hardware::Void();
+}
+
+Return<void> SurroundView2dSession::projectCameraPoints(
+ const hidl_vec<Point2dInt>& points2dCamera,
+ const hidl_string& cameraId,
+ projectCameraPoints_cb _hidl_cb) {
+ ALOGD("SurroundView2dSession::projectCameraPoints");
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ bool cameraIdFound = false;
+ for (auto evsCameraId : mEvsCameraIds) {
+ if (cameraId == evsCameraId) {
+ cameraIdFound = true;
+ ALOGI("Camera id found.");
+ break;
+ }
+ }
+
+ if (!cameraIdFound) {
+ ALOGE("Camera id not found.");
+ _hidl_cb(hidl_vec<Point2dFloat>());
+ return android::hardware::Void();
+ }
+
+ hidl_vec<Point2dFloat> outPoints;
+ outPoints.resize(points2dCamera.size());
+
+ int width = mConfig.width;
+ int height = mConfig.width * 3 / 4;
+ for (int i=0; i<points2dCamera.size(); i++) {
+ // Assuming all the points in the image frame can be projected into 2d
+ // Surround View space. Otherwise cannot.
+ if (points2dCamera[i].x < 0 || points2dCamera[i].y > width-1 ||
+ points2dCamera[i].x < 0 || points2dCamera[i].y > height-1) {
+ ALOGW("SurroundView2dSession::projectCameraPoints "
+ "gets invalid 2d camera points. Ignored");
+ outPoints[i].isValid = false;
+ outPoints[i].x = 10000;
+ outPoints[i].y = 10000;
+ } else {
+ outPoints[i].isValid = true;
+ outPoints[i].x = 0;
+ outPoints[i].y = 0;
+ }
+ }
+
+ _hidl_cb(outPoints);
+ return android::hardware::Void();
+}
+
+void SurroundView2dSession::generateFrames() {
+ ALOGD("SurroundView2dSession::generateFrames");
+
+ int sequenceId = 0;
+
+ while(true) {
+ {
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (mStreamState != RUNNING) {
+ // Break out of our main thread loop
+ break;
+ }
+
+ framesRecord.frames.svBuffers[0].hardwareBuffer.description[0] =
+ mConfig.width;
+ framesRecord.frames.svBuffers[0].hardwareBuffer.description[1] =
+ mConfig.width * 3 / 4;
+ }
+
+ usleep(100 * 1000);
+
+ framesRecord.frames.timestampNs = elapsedRealtimeNano();
+ framesRecord.frames.sequenceId = sequenceId++;
+
+ {
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (framesRecord.inUse) {
+ ALOGD("Notify SvEvent::FRAME_DROPPED");
+ mStream->notify(SvEvent::FRAME_DROPPED);
+ } else {
+ framesRecord.inUse = true;
+ mStream->receiveFrames(framesRecord.frames);
+ }
+ }
+ }
+
+ // If we've been asked to stop, send an event to signal the actual
+ // end of stream
+ ALOGD("Notify SvEvent::STREAM_STOPPED");
+ mStream->notify(SvEvent::STREAM_STOPPED);
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
diff --git a/automotive/sv/1.0/default/SurroundView2dSession.h b/automotive/sv/1.0/default/SurroundView2dSession.h
new file mode 100644
index 0000000..ee751e7
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundView2dSession.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView2dSession.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <thread>
+
+using namespace ::android::hardware::automotive::sv::V1_0;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::sp;
+using ::std::mutex;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+class SurroundView2dSession : public ISurroundView2dSession {
+public:
+ SurroundView2dSession();
+
+ // Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession.
+ Return<SvResult> startStream(
+ const sp<ISurroundViewStream>& stream) override;
+ Return<void> stopStream() override;
+ Return<void> doneWithFrames(const SvFramesDesc& svFramesDesc) override;
+
+ // Methods from ISurroundView2dSession follow.
+ Return<void> get2dMappingInfo(get2dMappingInfo_cb _hidl_cb) override;
+ Return<SvResult> set2dConfig(const Sv2dConfig& sv2dConfig) override;
+ Return<void> get2dConfig(get2dConfig_cb _hidl_cb) override;
+ Return<void> projectCameraPoints(
+ const hidl_vec<Point2dInt>& points2dCamera,
+ const hidl_string& cameraId,
+ projectCameraPoints_cb _hidl_cb) override;
+
+ // TODO(tanmayp): Make private and add set/get method.
+ // Stream subscribed for the session.
+ sp<ISurroundViewStream> mStream;
+
+private:
+ void generateFrames();
+
+ enum StreamStateValues {
+ STOPPED,
+ RUNNING,
+ STOPPING,
+ DEAD,
+ };
+ StreamStateValues mStreamState;
+
+ Sv2dConfig mConfig;
+
+ std::thread mCaptureThread; // The thread we'll use to synthesize frames
+
+ struct FramesRecord {
+ SvFramesDesc frames;
+ bool inUse = false;
+ };
+
+ FramesRecord framesRecord;
+
+ // Synchronization necessary to deconflict mCaptureThread from the main service thread
+ std::mutex mAccessLock;
+
+ std::vector<std::string> mEvsCameraIds;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
diff --git a/automotive/sv/1.0/default/SurroundView3dSession.cpp b/automotive/sv/1.0/default/SurroundView3dSession.cpp
new file mode 100644
index 0000000..da36f32
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundView3dSession.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SurroundView3dSession.h"
+
+#include <set>
+
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <hidlmemory/mapping.h>
+
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::hardware::hidl_memory;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+SurroundView3dSession::SurroundView3dSession() :
+ mStreamState(STOPPED){
+
+ mEvsCameraIds = {"0" , "1", "2", "3"};
+
+ mConfig.width = 640;
+ mConfig.height = 480;
+ mConfig.carDetails = SvQuality::HIGH;
+
+ framesRecord.frames.svBuffers.resize(1);
+ framesRecord.frames.svBuffers[0].viewId = 0;
+ framesRecord.frames.svBuffers[0].hardwareBuffer.nativeHandle = new native_handle_t();
+ framesRecord.frames.svBuffers[0].hardwareBuffer.description[0] = mConfig.width;
+ framesRecord.frames.svBuffers[0].hardwareBuffer.description[1] = mConfig.height;
+}
+
+// Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession.
+Return<SvResult> SurroundView3dSession::startStream(
+ const sp<ISurroundViewStream>& stream) {
+ ALOGD("SurroundView3dSession::startStream");
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (mStreamState != STOPPED) {
+ ALOGE("ignoring startVideoStream call when a stream is already running.");
+ return SvResult::INTERNAL_ERROR;
+ }
+
+ if (mViews.empty()) {
+ ALOGE("No views have been set for current Surround View 3d Session. "
+ "Please call setViews before starting the stream.");
+ return SvResult::VIEW_NOT_SET;
+ }
+
+ mStream = stream;
+
+ ALOGD("Notify SvEvent::STREAM_STARTED");
+ mStream->notify(SvEvent::STREAM_STARTED);
+
+ // Start the frame generation thread
+ mStreamState = RUNNING;
+ mCaptureThread = std::thread([this](){ generateFrames(); });
+
+ return SvResult::OK;
+}
+
+Return<void> SurroundView3dSession::stopStream() {
+ ALOGD("SurroundView3dSession::stopStream");
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ if (mStreamState == RUNNING) {
+ // Tell the GenerateFrames loop we want it to stop
+ mStreamState = STOPPING;
+
+ // Block outside the mutex until the "stop" flag has been acknowledged
+ // We won't send any more frames, but the client might still get some already in flight
+ ALOGD("Waiting for stream thread to end...");
+ lock.unlock();
+ mCaptureThread.join();
+ lock.lock();
+
+ mStreamState = STOPPED;
+ mStream = nullptr;
+ ALOGD("Stream marked STOPPED.");
+ }
+
+ return android::hardware::Void();
+}
+
+Return<void> SurroundView3dSession::doneWithFrames(
+ const SvFramesDesc& svFramesDesc){
+ ALOGD("SurroundView3dSession::doneWithFrames");
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ framesRecord.inUse = false;
+
+ (void)svFramesDesc;
+ return android::hardware::Void();
+}
+
+// Methods from ISurroundView3dSession follow.
+Return<SvResult> SurroundView3dSession::setViews(const hidl_vec<View3d>& views) {
+ ALOGD("SurroundView3dSession::stopStream");
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ mViews.resize(views.size());
+ for (int i=0; i<views.size(); i++) {
+ mViews[i] = views[i];
+ }
+
+ return SvResult::OK;
+}
+
+Return<SvResult> SurroundView3dSession::set3dConfig(const Sv3dConfig& sv3dConfig) {
+ ALOGD("SurroundView3dSession::set3dConfig");
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ mConfig.width = sv3dConfig.width;
+ mConfig.height = sv3dConfig.height;
+ mConfig.carDetails = sv3dConfig.carDetails;
+ ALOGD("Notify SvEvent::CONFIG_UPDATED");
+ mStream->notify(SvEvent::CONFIG_UPDATED);
+
+ return SvResult::OK;
+}
+
+Return<void> SurroundView3dSession::get3dConfig(get3dConfig_cb _hidl_cb) {
+ ALOGD("SurroundView3dSession::get3dConfig");
+ std::unique_lock <std::mutex> lock(mAccessLock);
+
+ _hidl_cb(mConfig);
+ return android::hardware::Void();
+}
+
+bool VerifyOverlayData(const OverlaysData& overlaysData) {
+ // Check size of shared memory matches overlaysMemoryDesc.
+ const int kVertexSize = 16;
+ const int kIdSize = 2;
+ int memDescSize = 0;
+ for (auto overlayMemDesc : overlaysData.overlaysMemoryDesc) {
+ memDescSize += kIdSize + kVertexSize * overlayMemDesc.verticesCount;
+ }
+ if (memDescSize != overlaysData.overlaysMemory.size()) {
+ ALOGE("shared memory and overlaysMemoryDesc size mismatch.");
+ return false;
+ }
+
+ // Map memory.
+ sp<IMemory> pSharedMemory = mapMemory(overlaysData.overlaysMemory);
+ if(pSharedMemory.get() == nullptr) {
+ ALOGE("mapMemory failed.");
+ return false;
+ }
+
+ // Get Data pointer.
+ uint8_t* pData = (uint8_t*)((void*)pSharedMemory->getPointer());
+ if (pData == nullptr) {
+ ALOGE("Shared memory getPointer() failed.");
+ return false;
+ }
+
+ int idOffset = 0;
+ std::set<uint16_t> overlayIdSet;
+ for (auto overlayMemDesc : overlaysData.overlaysMemoryDesc) {
+
+ if (overlayIdSet.find(overlayMemDesc.id) != overlayIdSet.end()) {
+ ALOGE("Duplicate id within memory descriptor.");
+ return false;
+ }
+ overlayIdSet.insert(overlayMemDesc.id);
+
+ if(overlayMemDesc.verticesCount < 3) {
+ ALOGE("Less than 3 vertices.");
+ return false;
+ }
+
+ if (overlayMemDesc.overlayPrimitive == OverlayPrimitive::TRIANGLES &&
+ overlayMemDesc.verticesCount % 3 != 0) {
+ ALOGE("Triangles primitive does not have vertices multiple of 3.");
+ return false;
+ }
+
+ uint16_t overlayId = *((uint16_t*)(pData + idOffset));
+
+ if (overlayId != overlayMemDesc.id) {
+ ALOGE("Overlay id mismatch %d , %d", overlayId, overlayMemDesc.id);
+ return false;
+ }
+
+ idOffset += kIdSize + (kVertexSize * overlayMemDesc.verticesCount);
+ }
+
+ return true;
+}
+
+Return<SvResult> SurroundView3dSession::updateOverlays(
+ const OverlaysData& overlaysData) {
+
+ if(!VerifyOverlayData(overlaysData)) {
+ ALOGE("VerifyOverlayData failed.");
+ return SvResult::INVALID_ARG;
+ }
+
+ return SvResult::OK;
+}
+
+Return<void> SurroundView3dSession::projectCameraPointsTo3dSurface(
+ const hidl_vec<Point2dInt>& cameraPoints,
+ const hidl_string& cameraId,
+ projectCameraPointsTo3dSurface_cb _hidl_cb) {
+
+ std::vector<Point3dFloat> points3d;
+ bool cameraIdFound = false;
+ for (auto evsCameraId : mEvsCameraIds) {
+ if (cameraId == evsCameraId) {
+ cameraIdFound = true;
+ ALOGI("Camera id found.");
+ break;
+ }
+ }
+
+ if (!cameraIdFound) {
+ ALOGE("Camera id not found.");
+ _hidl_cb(points3d);
+ return android::hardware::Void();
+ }
+
+ for (const auto cameraPoint : cameraPoints) {
+ Point3dFloat point3d;
+ point3d.isValid = true;
+
+ if (cameraPoint.x < 0 || cameraPoint.x >= mConfig.width-1 ||
+ cameraPoint.y < 0 || cameraPoint.y >= mConfig.height-1) {
+ ALOGE("Camera point out of bounds.");
+ point3d.isValid = false;
+ }
+ points3d.push_back(point3d);
+ }
+ _hidl_cb(points3d);
+ return android::hardware::Void();
+}
+
+void SurroundView3dSession::generateFrames() {
+ ALOGD("SurroundView3dSession::generateFrames");
+
+ int sequenceId = 0;
+
+ while(true) {
+ {
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (mStreamState != RUNNING) {
+ // Break out of our main thread loop
+ break;
+ }
+ }
+
+ usleep(100 * 1000);
+
+ framesRecord.frames.timestampNs = elapsedRealtimeNano();
+ framesRecord.frames.sequenceId = sequenceId++;
+
+ framesRecord.frames.svBuffers.resize(mViews.size());
+ for (int i=0; i<mViews.size(); i++) {
+ framesRecord.frames.svBuffers[i].viewId = mViews[i].viewId;
+ framesRecord.frames.svBuffers[i].hardwareBuffer.nativeHandle = new native_handle_t();
+ framesRecord.frames.svBuffers[i].hardwareBuffer.description[0] = mConfig.width; // width
+ framesRecord.frames.svBuffers[i].hardwareBuffer.description[1] = mConfig.height; // height
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mAccessLock);
+
+ if (framesRecord.inUse) {
+ ALOGD("Notify SvEvent::FRAME_DROPPED");
+ mStream->notify(SvEvent::FRAME_DROPPED);
+ } else {
+ framesRecord.inUse = true;
+ mStream->receiveFrames(framesRecord.frames);
+ }
+ }
+ }
+
+ // If we've been asked to stop, send an event to signal the actual end of stream
+ ALOGD("Notify SvEvent::STREAM_STOPPED");
+ mStream->notify(SvEvent::STREAM_STOPPED);
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
diff --git a/automotive/sv/1.0/default/SurroundView3dSession.h b/automotive/sv/1.0/default/SurroundView3dSession.h
new file mode 100644
index 0000000..5c638db
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundView3dSession.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView3dSession.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <thread>
+
+using namespace ::android::hardware::automotive::sv::V1_0;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::sp;
+using ::std::mutex;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+class SurroundView3dSession : public ISurroundView3dSession {
+public:
+ SurroundView3dSession();
+
+ // Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession.
+ Return<SvResult> startStream(
+ const sp<ISurroundViewStream>& stream) override;
+ Return<void> stopStream() override;
+ Return<void> doneWithFrames(const SvFramesDesc& svFramesDesc) override;
+
+ // Methods from ISurroundView3dSession follow.
+ Return<SvResult> setViews(const hidl_vec<View3d>& views) override;
+ Return<SvResult> set3dConfig(const Sv3dConfig& sv3dConfig) override;
+ Return<void> get3dConfig(get3dConfig_cb _hidl_cb) override;
+ Return<SvResult> updateOverlays(const OverlaysData& overlaysData);
+ Return<void> projectCameraPointsTo3dSurface(
+ const hidl_vec<Point2dInt>& cameraPoints,
+ const hidl_string& cameraId,
+ projectCameraPointsTo3dSurface_cb _hidl_cb);
+
+ // Stream subscribed for the session.
+ // TODO(tanmayp): Make private and add set/get method.
+ sp<ISurroundViewStream> mStream;
+
+private:
+ void generateFrames();
+
+ enum StreamStateValues {
+ STOPPED,
+ RUNNING,
+ STOPPING,
+ DEAD,
+ };
+ StreamStateValues mStreamState;
+
+ std::thread mCaptureThread; // The thread we'll use to synthesize frames
+
+ struct FramesRecord {
+ SvFramesDesc frames;
+ bool inUse = false;
+ };
+
+ FramesRecord framesRecord;
+
+ // Synchronization necessary to deconflict mCaptureThread from the main service thread
+ std::mutex mAccessLock;
+
+ std::vector<View3d> mViews;
+
+ Sv3dConfig mConfig;
+
+ std::vector<std::string> mEvsCameraIds;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/sv/1.0/default/SurroundViewService.cpp b/automotive/sv/1.0/default/SurroundViewService.cpp
new file mode 100644
index 0000000..fe89dd5
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundViewService.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SurroundViewService.h"
+
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+const std::string kCameraIds[] = {"0", "1", "2", "3"};
+
+Return<void> SurroundViewService::getCameraIds(getCameraIds_cb _hidl_cb) {
+ std::vector<hidl_string> cameraIds = {kCameraIds[0], kCameraIds[1],
+ kCameraIds[2], kCameraIds[3]};
+ _hidl_cb(cameraIds);
+ return android::hardware::Void();
+}
+
+Return<void> SurroundViewService::start2dSession(start2dSession_cb _hidl_cb) {
+ ALOGD("SurroundViewService::start2dSession");
+ if (mSurroundView2dSession != nullptr) {
+ ALOGW("Only one 2d session is supported at the same time");
+ _hidl_cb(nullptr, SvResult::INTERNAL_ERROR);
+ } else {
+ mSurroundView2dSession = new SurroundView2dSession();
+ _hidl_cb(mSurroundView2dSession, SvResult::OK);
+ }
+ return android::hardware::Void();
+}
+
+Return<SvResult> SurroundViewService::stop2dSession(
+ const sp<ISurroundView2dSession>& sv2dSession) {
+ ALOGD("SurroundViewService::stop2dSession");
+ if (sv2dSession != nullptr && sv2dSession == mSurroundView2dSession) {
+ mSurroundView2dSession = nullptr;
+ return SvResult::OK;
+ } else {
+ ALOGE("Invalid arg for stop2dSession");
+ return SvResult::INVALID_ARG;
+ }
+}
+
+Return<void> SurroundViewService::start3dSession(start3dSession_cb _hidl_cb) {
+ ALOGD("SurroundViewService::start3dSession");
+ if (mSurroundView3dSession != nullptr) {
+ ALOGW("Only one 3d session is supported at the same time");
+ _hidl_cb(nullptr, SvResult::INTERNAL_ERROR);
+ } else {
+ mSurroundView3dSession = new SurroundView3dSession();
+ _hidl_cb(mSurroundView3dSession, SvResult::OK);
+ }
+ return android::hardware::Void();
+}
+
+Return<SvResult> SurroundViewService::stop3dSession(
+ const sp<ISurroundView3dSession>& sv3dSession) {
+ ALOGD("SurroundViewService::stop3dSession");
+ if (sv3dSession != nullptr && sv3dSession == mSurroundView3dSession) {
+ mSurroundView3dSession = nullptr;
+ return SvResult::OK;
+ } else {
+ ALOGE("Invalid arg for stop3dSession");
+ return SvResult::INVALID_ARG;
+ }
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
+
diff --git a/automotive/sv/1.0/default/SurroundViewService.h b/automotive/sv/1.0/default/SurroundViewService.h
new file mode 100644
index 0000000..9e0e151
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundViewService.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SurroundView2dSession.h"
+#include "SurroundView3dSession.h"
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewService.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView2dSession.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView3dSession.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+using namespace ::android::hardware::automotive::sv::V1_0;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+class SurroundViewService : public ISurroundViewService {
+public:
+ // Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewService follow.
+ Return<void> getCameraIds(getCameraIds_cb _hidl_cb) override;
+ Return<void> start2dSession(start2dSession_cb _hidl_cb) override;
+ Return<SvResult> stop2dSession(
+ const sp<ISurroundView2dSession>& sv2dSession) override;
+
+ Return<void> start3dSession(start3dSession_cb _hidl_cb) override;
+ Return<SvResult> stop3dSession(
+ const sp<ISurroundView3dSession>& sv3dSession) override;
+
+private:
+ sp<SurroundView2dSession> mSurroundView2dSession;
+ sp<SurroundView3dSession> mSurroundView3dSession;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace sv
+} // namespace automotive
+} // namespace hardware
+} // namespace android
diff --git a/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.rc b/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.rc
new file mode 100644
index 0000000..e822017
--- /dev/null
+++ b/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.rc
@@ -0,0 +1,5 @@
+service sv_service /vendor/bin/hw/android.hardware.automotive.sv@1.0-service
+ class hal
+ user automotive_evs
+ group automotive_evs
+ disabled
diff --git a/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.xml b/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.xml
new file mode 100644
index 0000000..ba8e5ac
--- /dev/null
+++ b/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.automotive.sv</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>ISurroundViewService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/automotive/sv/1.0/default/service.cpp b/automotive/sv/1.0/default/service.cpp
new file mode 100644
index 0000000..fae7425
--- /dev/null
+++ b/automotive/sv/1.0/default/service.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.automotive.sv@1.0-service"
+
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware_buffer.h>
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+#include <thread>
+#include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/SystemClock.h>
+
+#include "SurroundViewService.h"
+
+// libhidl:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// implementation:
+using android::hardware::automotive::sv::V1_0::implementation::SurroundViewService;
+
+int main() {
+ ALOGI("ISurroundViewService default implementation is starting");
+ android::sp<ISurroundViewService> service = new SurroundViewService();
+
+ configureRpcThreadpool(1, true /* callerWillJoin */);
+
+ // Register our service -- if somebody is already registered by our name,
+ // they will be killed (their thread pool will throw an exception).
+ android::status_t status = service->registerAsService();
+
+ LOG_ALWAYS_FATAL_IF(status != android::OK,
+ "Could not register default Surround View Service (%d)",
+ status);
+
+ joinRpcThreadpool();
+
+ // In normal operation, we don't expect the thread pool to exit
+ ALOGE("Surround View Service is shutting down");
+ return 1;
+}
diff --git a/automotive/sv/1.0/types.hal b/automotive/sv/1.0/types.hal
new file mode 100644
index 0000000..573bf11
--- /dev/null
+++ b/automotive/sv/1.0/types.hal
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+import android.hardware.graphics.common@1.2::HardwareBuffer;
+
+/** Structure for translation with x, y and z units. */
+struct Translation {
+ float x;
+ float y;
+ float z;
+};
+
+/**
+ * Structure for rotation expressed as quaternions.
+ * Convention used: Unit quaternion with hamilton convention.
+ */
+struct RotationQuat {
+ float x;
+ float y;
+ float z;
+ float w;
+};
+
+/** Structure representing a 2D point with integers. Units are pixels. */
+struct Point2dInt {
+ uint32_t x;
+ uint32_t y;
+};
+
+/** Structure representing a 2D point with floats. */
+struct Point2dFloat {
+ /** Boolean flag to indicate the (x, y) data is valid. */
+ bool isValid;
+
+ /** (x, y) data is only valid if isValid is true. Units are pixels or milli-meters. */
+ float x;
+ float y;
+};
+
+/** Structure representing a 3D point with floats. */
+struct Point3dFloat {
+ /** Boolean flag to indicate the (x, y, z) data is valid. */
+ bool isValid;
+
+ /**
+ * (x, y, z) data is only valid if isValid is true. Units are milli-meters.
+ */
+ float x;
+ float y;
+ float z;
+};
+
+/**
+ * Structure defining the pose in 3D space.
+ */
+struct Pose {
+ /**
+ * Rotation part of the pose, expressed as a unit quaternion.
+ */
+ RotationQuat rotation;
+
+ /**
+ * Translation part of the pose, in (x, y, z) format with milli-meter units.
+ */
+ Translation translation;
+};
+
+/**
+ * Struct defining a virtual view in the 3d space around the car.
+ */
+struct View3d {
+ /**
+ * Id to identify each custom view, this is passed along in each result SvBuffer.
+ * Recommend client to have a unique id for each different view.
+ */
+ uint32_t viewId;
+
+ /**
+ * Pose of the view. Describes the orientation and location of a virtual view relative to the
+ * android automotive coordinate system:
+ * https://source.android.com/devices/sensors/sensor-types#auto_axes
+ * The virtual view axes are defined as +Y as look-at direction, +X as right direction and
+ * +Z as up direction.
+ * The rotation and translation of the virtual view axes w.r.t the android automotive axes is
+ * specified by the rotation and tranlation component of the pose respectively.
+ * Example: A virtual view points to the right face of the car, located on right side of
+ * the car at (4, 2, 0) and is upright w.r.t the ground :
+ * ______
+ * front | |
+ * | car | ↑X
+ * | ↑Y | Y←∘ view
+ * rear | ∘→X | (4,2)
+ * |(0,0) |
+ * |______|
+ *
+ * Here the view axes are rotated by 90 counter-clockwise w.r.t android automotive axes.
+ * For this example the rotation and translation will be:
+ * Rotation = + 90 degrees around Z axis = (0.7071, 0, 0, 0.7071) as a unit quaternion.
+ * Translation = (4, 2, 0) in meters = (2000, 4000, 0) in milli-meters.
+ */
+ Pose pose;
+
+ /**
+ * Horizontal field of view of the virtual view in degrees. Vertical fov is scaled accordingly
+ * to maintain the aspect ratio of the output frame. Must be in range (20,
+ */
+ float horizontalFov;
+};
+
+/**
+ * Memory Buffer that stores the output of a single view from 2d/3d surround view.
+ */
+struct SvBuffer {
+ /**
+ * viewId identifying the view as passed by the client in setViews() call for
+ * surround view 3d. Id value is 0 for 2d surround view frame.
+ */
+ uint32_t viewId;
+
+ /** Hardware buffer containing the surround view 2d/3d result. */
+ HardwareBuffer hardwareBuffer;
+};
+
+/**
+ * Structure describing a set of frames to be returned as output from 2d/3d surround view.
+ */
+struct SvFramesDesc {
+ /**
+ * Elapsed real-time nanoseconds of earliest camera frame from the set of camera
+ * frames used to generate the view.
+ */
+ uint64_t timestampNs;
+
+ /**
+ * Incremental counter for client to keep track of frames.
+ */
+ uint32_t sequenceId;
+
+ /**
+ * Frames generated with different views.
+ * 2d surround view has only a single svBuffer with Id 0.
+ */
+ vec<SvBuffer> svBuffers;
+};
+
+/**
+ * Enumerator for list of result returns by surround view .
+ */
+enum SvResult : uint32_t {
+ /** Operation was successful. */
+ OK = 0,
+
+ /** Invalid argument to function was provided. */
+ INVALID_ARG,
+
+ /** Error indicating the particular operation is not supported. */
+ NOT_SUPPORTED,
+
+ /** Error indicating view not set before starting stream. */
+ VIEW_NOT_SET,
+
+ /**
+ * Error indicating system does not currently have enough resources to
+ * allocate for a new requested session.
+ * Clients may retry request for session if resources become available.
+ */
+ NO_RESOURCE,
+
+ /** Internal error in surround view service. */
+ INTERNAL_ERROR,
+};
+
+/**
+ * Enumerator listing events for surround view.
+ */
+enum SvEvent : uint32_t {
+ STREAM_STARTED = 1,
+
+ STREAM_STOPPED,
+
+ /**
+ * Event sent after service switches to an updated config, all frames
+ * streamed after this event are of the updated config.
+ */
+ CONFIG_UPDATED,
+
+ /** Each frame dropped will be notified with this event. */
+ FRAME_DROPPED,
+
+ /**
+ * Timeout event occurs if any individual camera stream has a timeout.
+ * Frames will not be delivered and clients must stop the stream.
+ */
+ TIMEOUT,
+};
+
+/**
+ * Structure defining the mapping information for 2d surround view.
+ *
+ * Mapping information provides the area on ground (width and height) and
+ * position w.r.t the car that the surround view 2d covers. This can be used for
+ * mapping (linear transformation) with other sensors whose data is available in
+ * the car coordinate system (eg. Ultrasonics).
+ * Axes and origin are as per the android automotive axes:
+ * https://source.android.com/devices/sensors/sensor-types#auto_axes
+ */
+struct Sv2dMappingInfo {
+ /** Width in milli-meters of the 2d surround view along the ground plane. */
+ float width;
+
+ /** Height in milli-meters of the 2d surround view along the ground plane. */
+ float height;
+
+ /**
+ * Coordinates (x, y) of the center of the view in android automotive coordinate system on the
+ * ground plane. Units are milli-meters.
+ */
+ Point2dFloat center;
+};
+
+/**
+ * Enumerator for quality presets for 2d/3d surround view.
+ * Details of each preset are specified in the respective 2d/3d config structures.
+ */
+enum SvQuality : uint32_t {
+ HIGH = 0,
+ LOW,
+};
+
+/** Structure for surround view 2d configuration. */
+struct Sv2dConfig {
+ /**
+ * Desired output width in pixels. Must be in range (0, 4096].
+ * Height is computed keeping the aspect ratio of the mapping info,
+ * Example: If width = 1080 px and mapping_width = 5000 mm, mapping_height = 10000 mm.
+ * then, height = width * (mapping_height / mapping_width) = 2160 px.
+ * Height is set to the floor value in case of (mapping_height / mapping_width) is not integer.
+ * Mapping width, height is fixed for a car and is based on camera parameters and their ground
+ * coverage.
+ */
+ uint32_t width;
+
+ /**
+ * Blending quality preset to use.
+ * HIGH: High quality blending (eg. multiband blending) that consumes more resources.
+ * LOW: Low quality blending (eg. alpha blending) that consumes less resources.
+ */
+ SvQuality blending;
+};
+
+/** Structure for surround view 3d configuration. */
+struct Sv3dConfig {
+ /** Desired output width in pixels. Must be in range (0, 4096]. */
+ uint32_t width;
+
+ /** Desired output height in pixels. Must be in range (0, 4096]. */
+ uint32_t height;
+
+ /**
+ * Car model rendering details level.
+ * HIGH: Rendering includes shadows and reflections. Default option.
+ * LOW: Rendering with no shadows and reflections.
+ */
+ SvQuality carDetails;
+};
+
+/**
+ * Enumerator for a list of overlay primitives.
+ *
+ * Given a list of vertices for an overlay, a primitive type defines which vertices are used to form
+ * the surfaces of the overlay object.
+ */
+enum OverlayPrimitive : uint32_t {
+ /**
+ * Consecutive vertices picked in order 3 at a time form a triangle.
+ * Eg: In a list of vertices (V1, V2, V3, V4, V5, V6)
+ * (V1, V2, V3) form a triangle and (V4, V5, V6) form a triangle.
+ */
+ TRIANGLES = 0,
+
+ /**
+ * Every 3 consecutive vertices form a triangle.
+ * Example in a list of vertices V1, V2, V3, V4, V5, V6
+ * (V1, V2, V3), (V2, V3, V4), (V3, V4, V5) and (V4, V5, V6) form triangles.
+ */
+ TRIANGLES_STRIP,
+};
+
+/**
+ * Structure identifying an overlay and describing the size and arrangement of its data in
+ * shared memory.
+ */
+struct OverlayMemoryDesc {
+ /** Identifier of the overlay. */
+ uint16_t id;
+
+ /** Number of vertices in the overlay. */
+ uint32_t verticesCount;
+
+ /** Primitive for the overlay. */
+ OverlayPrimitive overlayPrimitive;
+};
+
+/**
+ * Structure containing the overlays data in shared memory.
+ */
+struct OverlaysData {
+ /** List of overlay memory descriptors, describing the data in the shared memory */
+ vec<OverlayMemoryDesc> overlaysMemoryDesc;
+
+ /**
+ * Shared memory object containing a list of vertices for each overlay as described by
+ * overlaysMemoryDesc.
+ *
+ * Each vertex comprises of:
+ * | PositionX | PositionY | PositionZ | RGBA |
+ * | float | float | float | 4 * uint8_t |
+ *
+ * Each vertex is of 3 floats and 4 bytes = 16 bytes.
+ *
+ * Layout of vertices in shared memory is in order:
+ *
+ * Bytes: | 0-1 | 2-18 | 19-34 | 35-50 | 51-66 | 67-68 | 69-84 | 85-100 | 101-116 |...
+ * Data: | id1 | V1 | V2 | V3 | V4 | id2 | V1 | V2 | V3 |...
+ * | overlay1 | overlay 2 |
+ *
+ * The order of overlays must match the order as specified in the overlaysMemoryDesc.
+ * The number of vertices each overlay has must match the verticesCount in overlaysMemoryDesc.
+ * The id must match the id specificed in the OverlayMemoryDesc. This is used for verification.
+ * For each overlay the number of vertices must be 3 or greater.
+ * For TRIANGLES primitive the number of vertices must be a multiple of 3.
+ * The overlay vertices are grouped as per the overlayPrimitive specified in overlaysMemoryDesc,
+ * eg: If primitive is TRIANGLES, (V1, V2, V3) and (V4, V5, V6) form a triangle.
+ */
+ memory overlaysMemory;
+};
diff --git a/automotive/sv/1.0/vts/functional/Android.bp b/automotive/sv/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..0e5d3df
--- /dev/null
+++ b/automotive/sv/1.0/vts/functional/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "VtsHalSurroundViewV1_0TargetTest",
+ srcs: [
+ "VtsHalSurroundViewV1_0TargetTest.cpp",
+ "SurroundViewStreamHandler.cpp",
+ ],
+ defaults: ["VtsHalTargetTestDefaults"],
+ static_libs: [
+ "libnativewindow",
+ "android.hardware.automotive.sv@1.0",
+ "android.hardware.graphics.common@1.0",
+ "android.hardware.graphics.common@1.1",
+ "android.hardware.graphics.common@1.2",
+ ],
+ shared_libs: [
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlmemory",
+ ],
+ test_suites: ["general-tests", "vts-core"],
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+}
diff --git a/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.cpp b/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.cpp
new file mode 100644
index 0000000..cb45caa
--- /dev/null
+++ b/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SurroundViewStreamHandler.h"
+
+#include <utils/Log.h>
+
+using std::lock_guard;
+
+SurroundViewServiceHandler::SurroundViewServiceHandler(sp<ISurroundViewSession> pSession) :
+ mSession(pSession),
+ mReceiveFramesCount(0),
+ mDoNotReturnFrames(false) {
+ // Nothing but member initialization
+}
+
+Return<void> SurroundViewServiceHandler::notify(SvEvent svEvent) {
+ ALOGD("SurroundViewServiceHandler::notify %d", svEvent);
+
+ lock_guard<mutex> lock(mLock);
+ switch (svEvent) {
+ case SvEvent::STREAM_STARTED:
+ case SvEvent::CONFIG_UPDATED:
+ case SvEvent::STREAM_STOPPED:
+ case SvEvent::FRAME_DROPPED:
+ case SvEvent::TIMEOUT:
+ mReceivedEvents.emplace_back(svEvent);
+ break;
+ default:
+ ALOGI("[SurroundViewLog] Received other event");
+ }
+
+ return android::hardware::Void();
+}
+
+Return<void> SurroundViewServiceHandler::receiveFrames(const SvFramesDesc& svFramesDesc) {
+ ALOGD("SurroundViewServiceHandler::receiveFrames");
+
+ lock_guard<mutex> lock(mLock);
+ unsigned long timestampNs = svFramesDesc.timestampNs;
+ unsigned sequenceId = svFramesDesc.sequenceId;
+ ALOGD("receiveFrames count: %d", mReceiveFramesCount);
+ ALOGD("timestampNs: %lu, sequenceId: %u", timestampNs, sequenceId);
+ if (mReceiveFramesCount != 0
+ && (mLastReceivedFrames.timestampNs >= svFramesDesc.timestampNs
+ || mLastReceivedFrames.sequenceId >= svFramesDesc.sequenceId)) {
+ mAllFramesValid = false;
+ ALOGD("The incoming frames are with invalid timestamp or sequenceId!");
+ }
+
+ for (int i=0; i<svFramesDesc.svBuffers.size(); i++) {
+ if (svFramesDesc.svBuffers[i].hardwareBuffer.nativeHandle == nullptr) {
+ mAllFramesValid = false;
+ ALOGD("The incoming frames are with invalid nativeHandle!");
+ break;
+ }
+ }
+
+ mReceiveFramesCount++;
+
+ // Store all the information except for the handle
+ mLastReceivedFrames.timestampNs = svFramesDesc.timestampNs;
+ mLastReceivedFrames.sequenceId = svFramesDesc.sequenceId;
+ mLastReceivedFrames.svBuffers.resize(svFramesDesc.svBuffers.size());
+ for (int i=0; i<svFramesDesc.svBuffers.size(); i++) {
+ mLastReceivedFrames.svBuffers[i].viewId = svFramesDesc.svBuffers[i].viewId;
+ mLastReceivedFrames.svBuffers[i].hardwareBuffer.description =
+ svFramesDesc.svBuffers[i].hardwareBuffer.description;
+ }
+
+ if (!mDoNotReturnFrames) {
+ mSession->doneWithFrames(svFramesDesc);
+ }
+
+ return android::hardware::Void();
+}
+
+bool SurroundViewServiceHandler::checkEventReceived(SvEvent svEvent) {
+ ALOGD("SurroundViewServiceHandler::checkEventReceived");
+ int size = mReceivedEvents.size(); // work around
+ ALOGD("Received event number: %d", size);
+ auto iter = find(mReceivedEvents.begin(), mReceivedEvents.end(), svEvent);
+ return iter != mReceivedEvents.end();
+}
+
+SvFramesDesc SurroundViewServiceHandler::getLastReceivedFrames() {
+ return mLastReceivedFrames;
+}
+
+int SurroundViewServiceHandler::getReceiveFramesCount() {
+ return mReceiveFramesCount;
+}
+
+bool SurroundViewServiceHandler::areAllFramesValid() {
+ return mAllFramesValid;
+}
+
+void SurroundViewServiceHandler::setDoNotReturnFrames(bool flag) {
+ mDoNotReturnFrames = flag;
+}
diff --git a/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.h b/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.h
new file mode 100644
index 0000000..7d3f61d
--- /dev/null
+++ b/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SURROUND_VIEW_STREAM_HANDLER_H
+#define SURROUND_VIEW_STREAM_HANDLER_H
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewSession.h>
+
+#include <thread>
+#include <vector>
+
+using std::vector;
+using std::mutex;
+using android::hardware::Return;
+using android::sp;
+using namespace ::android::hardware::automotive::sv::V1_0;
+
+class SurroundViewServiceHandler : public ISurroundViewStream {
+public:
+ SurroundViewServiceHandler(sp<ISurroundViewSession> session);
+
+ Return<void> notify(SvEvent svEvent) override;
+ Return<void> receiveFrames(const SvFramesDesc& svFramesDesc) override;
+
+ bool checkEventReceived(SvEvent svEvent);
+ SvFramesDesc getLastReceivedFrames();
+ int getReceiveFramesCount();
+ bool areAllFramesValid();
+ void setDoNotReturnFrames(bool flag);
+
+private:
+ mutex mLock;
+
+ vector<SvEvent> mReceivedEvents;
+ sp<ISurroundViewSession> mSession;
+ SvFramesDesc mLastReceivedFrames; // only use timestampNs and sequenceId
+ int mReceiveFramesCount; // TODO(haoxiangl): figure out a better name
+ bool mAllFramesValid = true;
+ bool mDoNotReturnFrames;
+};
+
+#endif //SURROUND_VIEW_STREAM_HANDLER_H
diff --git a/automotive/sv/1.0/vts/functional/VtsHalSurroundViewV1_0TargetTest.cpp b/automotive/sv/1.0/vts/functional/VtsHalSurroundViewV1_0TargetTest.cpp
new file mode 100644
index 0000000..b1b9d16
--- /dev/null
+++ b/automotive/sv/1.0/vts/functional/VtsHalSurroundViewV1_0TargetTest.cpp
@@ -0,0 +1,1137 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#define LOG_TAG "VtsHalSurroundViewTest"
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewService.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView2dSession.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView3dSession.h>
+#include <android/hardware_buffer.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <hidlmemory/mapping.h>
+#include <math.h>
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include "SurroundViewStreamHandler.h"
+
+using namespace ::android::hardware::automotive::sv::V1_0;
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::hardware::hidl_memory;
+
+const int kVertexByteSize = (3 * sizeof(float)) + 4;
+const int kIdByteSize = 2;
+
+// The main test class for Surround View Service
+class SurroundViewHidlTest : public ::testing::TestWithParam<std::string> {
+public:
+ virtual void SetUp() override {
+ mSurroundViewService = ISurroundViewService::getService(GetParam());
+ ASSERT_NE(mSurroundViewService.get(), nullptr);
+ }
+
+ virtual void TearDown() override {}
+
+ sp<ISurroundViewService> mSurroundViewService; // Every test needs access to the service
+};
+
+TEST_P(SurroundViewHidlTest, startAndStop2dSession) {
+ ALOGD("SurroundViewHidlTest::startAndStop2dSession");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ mSurroundViewService->start2dSession(
+ [&surroundView2dSession](
+ const sp<ISurroundView2dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView2dSession = session;
+ });
+
+ SvResult result = mSurroundViewService->stop2dSession(surroundView2dSession);
+ ASSERT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, stopInvalid2dSession) {
+ ALOGD("SurroundViewHidlTest::stopInvalid2dSession");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ SvResult result = mSurroundViewService->stop2dSession(surroundView2dSession);
+ ASSERT_NE(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, startAndStop2dStream) {
+ ALOGD("SurroundViewHidlTest::startAndStop2dStream");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ mSurroundViewService->start2dSession(
+ [&surroundView2dSession](
+ const sp<ISurroundView2dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView2dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView2dSession);
+
+ SvResult result = surroundView2dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(5);
+
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STARTED));
+ EXPECT_GT(handler->getReceiveFramesCount(), 0);
+
+ surroundView2dSession->stopStream();
+
+ sleep(1);
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STOPPED));
+
+ result = mSurroundViewService->stop2dSession(surroundView2dSession);
+ EXPECT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, start2dStreamWithoutReturningFrames) {
+ ALOGD("SurroundViewHidlTest::start2dStreamWithoutReturningFrames");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ mSurroundViewService->start2dSession(
+ [&surroundView2dSession](
+ const sp<ISurroundView2dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView2dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView2dSession);
+ handler->setDoNotReturnFrames(true);
+
+ SvResult result = surroundView2dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(5);
+
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STARTED));
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::FRAME_DROPPED));
+ EXPECT_GT(handler->getReceiveFramesCount(), 0);
+
+ surroundView2dSession->stopStream();
+
+ sleep(1);
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STOPPED));
+
+ result = mSurroundViewService->stop2dSession(surroundView2dSession);
+ EXPECT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, duplicateStart2dStream) {
+ ALOGD("SurroundViewHidlTest, duplicateStart2dStream");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ mSurroundViewService->start2dSession(
+ [&surroundView2dSession](
+ const sp<ISurroundView2dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView2dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView2dSession);
+
+ SvResult result = surroundView2dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ result = surroundView2dSession->startStream(handler);
+ EXPECT_NE(result, SvResult::OK);
+
+ surroundView2dSession->stopStream();
+ mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, stopInvalid2dStream) {
+ ALOGD("SurroundViewHidlTest, stopInvalid2dStream");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ mSurroundViewService->start2dSession(
+ [&surroundView2dSession](
+ const sp<ISurroundView2dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView2dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView2dSession);
+
+ surroundView2dSession->stopStream();
+ mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, validate2dSvFramesDesc) {
+ ALOGD("SurroundViewHidlTest, validate2dSvFramesDesc");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ mSurroundViewService->start2dSession(
+ [&surroundView2dSession](
+ const sp<ISurroundView2dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView2dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView2dSession);
+
+ SvResult result = surroundView2dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(5);
+
+ // Validate timestampNs and sequenceId
+ EXPECT_GT(handler->getReceiveFramesCount(), 0);
+ EXPECT_TRUE(handler->areAllFramesValid());
+
+ // Validate 2d SvFramesDesc. Do not compare nativeHandle since it is not
+ // stored and already verified on the fly.
+ SvFramesDesc frames = handler->getLastReceivedFrames();
+ EXPECT_EQ(frames.svBuffers.size(), 1);
+
+ SvBuffer svBuffer2d = frames.svBuffers[0];
+ EXPECT_EQ(svBuffer2d.viewId, 0);
+
+ const AHardwareBuffer_Desc* pDesc =
+ reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer2d.hardwareBuffer.description);
+ float mapWidth, mapHeight;
+ surroundView2dSession->get2dMappingInfo([&mapWidth, &mapHeight] (Sv2dMappingInfo info) {
+ mapWidth = info.width;
+ mapHeight = info.height;
+ });
+ EXPECT_EQ(pDesc->height, floor(pDesc->width * (mapHeight / mapWidth)));
+
+ // Clean up
+ surroundView2dSession->stopStream();
+ result = mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, get2dMappingInfo) {
+ ALOGD("SurroundViewHidlTest, get2dMappingInfo");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ mSurroundViewService->start2dSession(
+ [&surroundView2dSession](
+ const sp<ISurroundView2dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView2dSession = session;
+ });
+
+ surroundView2dSession->get2dMappingInfo([] (Sv2dMappingInfo info) {
+ EXPECT_GT(info.width, 0);
+ EXPECT_GT(info.height, 0);
+ });
+
+ mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, set2dConfigResolution) {
+ ALOGD("SurroundViewHidlTest, set2dConfigResolution");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ mSurroundViewService->start2dSession(
+ [&surroundView2dSession](
+ const sp<ISurroundView2dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView2dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView2dSession);
+
+ SvResult result = surroundView2dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(1);
+
+ // Change config
+ Sv2dConfig config;
+ config.width = 1920;
+ config.blending = SvQuality::HIGH;
+ surroundView2dSession->set2dConfig(config);
+
+ sleep(1);
+
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::CONFIG_UPDATED));
+
+ // Check width has been changed but not the ratio
+ SvFramesDesc frames = handler->getLastReceivedFrames();
+ EXPECT_EQ(frames.svBuffers.size(), 1);
+ SvBuffer svBuffer2d = frames.svBuffers[0];
+ EXPECT_EQ(svBuffer2d.viewId, 0);
+ const AHardwareBuffer_Desc* pDesc =
+ reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer2d.hardwareBuffer.description);
+ EXPECT_EQ(pDesc->width, config.width);
+
+ float mapWidth, mapHeight;
+ surroundView2dSession->get2dMappingInfo([&mapWidth, &mapHeight] (Sv2dMappingInfo info) {
+ mapWidth = info.width;
+ mapHeight = info.height;
+ });
+ EXPECT_EQ(pDesc->height, floor (pDesc->width * (mapHeight / mapWidth)));
+
+ // Clean up
+ surroundView2dSession->stopStream();
+ mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, set2dConfigBlending) {
+ ALOGD("SurroundViewHidlTest, set2dConfigBlending");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ mSurroundViewService->start2dSession(
+ [&surroundView2dSession](
+ const sp<ISurroundView2dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView2dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView2dSession);
+
+ SvResult result = surroundView2dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(1);
+
+ // Get the width before config changed
+ int oldWidth;
+ SvFramesDesc frames = handler->getLastReceivedFrames();
+ EXPECT_EQ(frames.svBuffers.size(), 1);
+ SvBuffer svBuffer2d = frames.svBuffers[0];
+ const AHardwareBuffer_Desc* pDesc =
+ reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer2d.hardwareBuffer.description);
+ oldWidth = pDesc->width;
+
+ // Change config
+ Sv2dConfig config;
+ config.width = oldWidth;
+ config.blending = SvQuality::LOW;
+ surroundView2dSession->set2dConfig(config);
+
+ sleep(1);
+
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::CONFIG_UPDATED));
+
+ Sv2dConfig retConfig;
+ surroundView2dSession->get2dConfig([&retConfig] (Sv2dConfig config) {
+ retConfig.width = config.width;
+ retConfig.blending = config.blending;
+ });
+
+ // Check config blending has been changed but not the width
+ EXPECT_EQ(retConfig.blending, config.blending);
+ EXPECT_EQ(retConfig.width, oldWidth);
+
+ // Clean up
+ surroundView2dSession->stopStream();
+ mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, projectCameraPointsWithValidCameraId) {
+ ALOGD("SurroundViewHidlTest, projectCameraPointsWithValidCameraId");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ mSurroundViewService->start2dSession(
+ [&surroundView2dSession](
+ const sp<ISurroundView2dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView2dSession = session;
+ });
+
+ hidl_vec<hidl_string> cameraIds;
+ mSurroundViewService->getCameraIds([&cameraIds](
+ const hidl_vec<hidl_string>& camIds) {
+ cameraIds = camIds;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView2dSession);
+
+ SvResult result = surroundView2dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(1);
+
+ // Get the width and height of the frame
+ int width, height;
+ SvFramesDesc frames = handler->getLastReceivedFrames();
+ EXPECT_EQ(frames.svBuffers.size(), 1);
+ SvBuffer svBuffer2d = frames.svBuffers[0];
+ const AHardwareBuffer_Desc* pDesc =
+ reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer2d.hardwareBuffer.description);
+ width = pDesc->width;
+ height = pDesc->height;
+
+ float mapWidth, mapHeight, mapCenter[2];
+ surroundView2dSession->get2dMappingInfo(
+ [&mapWidth, &mapHeight, &mapCenter] (Sv2dMappingInfo info) {
+ mapWidth = info.width;
+ mapHeight = info.height;
+ mapCenter[0] = info.center.x;
+ mapCenter[1] = info.center.y;
+ });
+
+ // Set one valid point and one invalid point
+ hidl_vec<Point2dInt> points2dCamera;
+ points2dCamera.resize(2);
+ points2dCamera[0].x = 0;
+ points2dCamera[0].y = 0;
+ points2dCamera[1].x = width * 2;
+ points2dCamera[1].y = height * 2;
+
+ surroundView2dSession->projectCameraPoints(
+ points2dCamera,
+ cameraIds[0],
+ [&mapWidth, &mapHeight, &mapCenter] (
+ const hidl_vec<Point2dFloat>& outPoints) {
+ // Make sure point[0] is valid.
+ EXPECT_TRUE(outPoints[0].isValid);
+ EXPECT_GE(outPoints[0].x, mapCenter[0] - mapWidth);
+ EXPECT_LE(outPoints[0].x, mapCenter[0] + mapWidth);
+ EXPECT_GE(outPoints[0].y, mapCenter[1] - mapHeight);
+ EXPECT_LE(outPoints[0].y, mapCenter[1] + mapHeight);
+
+ // Make sure point[1] is invalid.
+ EXPECT_FALSE(outPoints[1].isValid);
+ });
+
+ // Clean up
+ surroundView2dSession->stopStream();
+ mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, projectCameraPointsWithInvalidCameraId) {
+ ALOGD("SurroundViewHidlTest, projectCameraPointsWithInvalidCameraId");
+ sp<ISurroundView2dSession> surroundView2dSession;
+ mSurroundViewService->start2dSession(
+ [&surroundView2dSession](
+ const sp<ISurroundView2dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView2dSession = session;
+ });
+
+ hidl_vec<hidl_string> cameraIds;
+ mSurroundViewService->getCameraIds([&cameraIds](
+ const hidl_vec<hidl_string>& camIds) {
+ cameraIds = camIds;
+ });
+
+ hidl_string invalidCameraId = "INVALID_CAMERA_ID";
+
+ // In case one of the camera id happens to be identical to
+ // the invalid camera id.
+ for (auto cameraId : cameraIds) {
+ ASSERT_NE(cameraId, invalidCameraId);
+ }
+
+ // Set one valid point
+ hidl_vec<Point2dInt> points2dCamera;
+ points2dCamera.resize(1);
+ points2dCamera[0].x = 0;
+ points2dCamera[0].y = 0;
+
+ surroundView2dSession->projectCameraPoints(
+ points2dCamera,
+ invalidCameraId,
+ [] (const hidl_vec<Point2dFloat>& outPoints) {
+ // No points are return due to invalid camera id
+ EXPECT_EQ(outPoints.size(), 0);
+ });
+
+ // Clean up
+ surroundView2dSession->stopStream();
+ mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, startAndStop3dSession) {
+ ALOGD("SurroundViewHidlTest, startAndStop3dSession");
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ SvResult result = mSurroundViewService->stop3dSession(surroundView3dSession);
+ EXPECT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, stopInvalid3dSession) {
+ ALOGD("SurroundViewHidlTest, stopInvalid3dSession");
+ sp<ISurroundView3dSession> surroundView3dSession;
+ SvResult result = mSurroundViewService->stop3dSession(surroundView3dSession);
+ EXPECT_NE(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, startAndStop3dStream) {
+ ALOGD("SurroundViewHidlTest::startAndStop3dStream");
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ std::vector<View3d> views(1);
+ SvResult setViewResult = surroundView3dSession->setViews(views);
+ EXPECT_EQ(setViewResult, SvResult::OK);
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView3dSession);
+
+ SvResult result = surroundView3dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(5);
+
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STARTED));
+ EXPECT_GT(handler->getReceiveFramesCount(), 0);
+
+ surroundView3dSession->stopStream();
+
+ sleep(1);
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STOPPED));
+
+ result = mSurroundViewService->stop3dSession(surroundView3dSession);
+ EXPECT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, start3dStreamWithoutReturningFrames) {
+ ALOGD("SurroundViewHidlTest::start3dStreamWithoutReturningFrames");
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ std::vector<View3d> views(1);
+ SvResult setViewResult = surroundView3dSession->setViews(views);
+ EXPECT_EQ(setViewResult, SvResult::OK);
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView3dSession);
+ handler->setDoNotReturnFrames(true);
+
+ SvResult result = surroundView3dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(5);
+
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STARTED));
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::FRAME_DROPPED));
+ EXPECT_GT(handler->getReceiveFramesCount(), 0);
+
+ surroundView3dSession->stopStream();
+
+ sleep(1);
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STOPPED));
+
+ result = mSurroundViewService->stop3dSession(surroundView3dSession);
+ EXPECT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, duplicateStart3dStream) {
+ ALOGD("SurroundViewHidlTest, duplicateStart3dStream");
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ std::vector<View3d> views(1);
+ SvResult setViewResult = surroundView3dSession->setViews(views);
+ EXPECT_EQ(setViewResult, SvResult::OK);
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView3dSession);
+
+ SvResult result = surroundView3dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ result = surroundView3dSession->startStream(handler);
+ EXPECT_NE(result, SvResult::OK);
+
+ surroundView3dSession->stopStream();
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, start3dStreamNoViewSetFail) {
+ ALOGD("SurroundViewHidlTest, start3dStreamNoViewSetFail");
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView3dSession);
+
+ SvResult result = surroundView3dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::VIEW_NOT_SET);
+
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, validate3dSvFramesDesc) {
+ ALOGD("SurroundViewHidlTest, validate3dSvFramesDesc");
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView3dSession);
+
+ std::vector<View3d> views(1);
+ views[0].viewId = 0;
+ SvResult setViewResult = surroundView3dSession->setViews(views);
+ EXPECT_EQ(setViewResult, SvResult::OK);
+
+ SvResult result = surroundView3dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(5);
+
+ EXPECT_GT(handler->getReceiveFramesCount(), 0);
+ EXPECT_TRUE(handler->areAllFramesValid());
+
+ // Validate 3d SvFramesDesc when only one view is set.
+ SvFramesDesc frames = handler->getLastReceivedFrames();
+ EXPECT_EQ(frames.svBuffers.size(), 1);
+ EXPECT_EQ(frames.svBuffers[0].viewId, 0);
+
+ views.resize(3);
+ views[0].viewId = 0;
+ views[1].viewId = 1;
+ views[2].viewId = 2;
+ setViewResult = surroundView3dSession->setViews(views);
+ EXPECT_EQ(setViewResult, SvResult::OK);
+
+ sleep(1);
+
+ // Validate 3d SvFramesDesc when multiple views are set.
+ EXPECT_TRUE(handler->areAllFramesValid());
+ frames = handler->getLastReceivedFrames();
+ EXPECT_EQ(frames.svBuffers.size(), 3);
+ EXPECT_EQ(frames.svBuffers[0].viewId, 0);
+ EXPECT_EQ(frames.svBuffers[1].viewId, 1);
+ EXPECT_EQ(frames.svBuffers[2].viewId, 2);
+ EXPECT_EQ(frames.svBuffers[0].hardwareBuffer.description[0],
+ frames.svBuffers[1].hardwareBuffer.description[0]);
+ EXPECT_EQ(frames.svBuffers[0].hardwareBuffer.description[1],
+ frames.svBuffers[1].hardwareBuffer.description[1]);
+ EXPECT_EQ(frames.svBuffers[1].hardwareBuffer.description[0],
+ frames.svBuffers[2].hardwareBuffer.description[0]);
+ EXPECT_EQ(frames.svBuffers[1].hardwareBuffer.description[1],
+ frames.svBuffers[2].hardwareBuffer.description[1]);
+
+ // Clean up
+ surroundView3dSession->stopStream();
+ result = mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, set3dConfigResolution) {
+ ALOGD("SurroundViewHidlTest, set3dConfigResolution");
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView3dSession);
+
+ std::vector<View3d> views(1);
+ views[0].viewId = 0;
+ SvResult setViewResult = surroundView3dSession->setViews(views);
+ EXPECT_EQ(setViewResult, SvResult::OK);
+ SvResult result = surroundView3dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(1);
+
+ // Change config
+ Sv3dConfig config;
+ config.width = 1920;
+ config.height = 1080;
+ config.carDetails = SvQuality::HIGH;
+ surroundView3dSession->set3dConfig(config);
+
+ sleep(1);
+
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::CONFIG_UPDATED));
+
+ // Check width has been changed but not the ratio
+ SvFramesDesc frames = handler->getLastReceivedFrames();
+ EXPECT_EQ(frames.svBuffers.size(), 1);
+ SvBuffer svBuffer3d = frames.svBuffers[0];
+ EXPECT_EQ(svBuffer3d.viewId, 0);
+ const AHardwareBuffer_Desc* pDesc =
+ reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer3d.hardwareBuffer.description);
+ EXPECT_EQ(pDesc->width, config.width);
+ EXPECT_EQ(pDesc->height, config.height);
+
+ // Clean up
+ surroundView3dSession->stopStream();
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, set3dConfigCarDetails) {
+ ALOGD("SurroundViewHidlTest, set3dConfigCarDetails");
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView3dSession);
+
+ std::vector<View3d> views(1);
+ views[0].viewId = 0;
+ SvResult setViewResult = surroundView3dSession->setViews(views);
+ EXPECT_EQ(setViewResult, SvResult::OK);
+ SvResult result = surroundView3dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(1);
+
+ // Get the width before config changed
+ int oldWidth, oldHeight;
+ SvFramesDesc frames = handler->getLastReceivedFrames();
+ EXPECT_EQ(frames.svBuffers.size(), 1);
+ SvBuffer svBuffer3d = frames.svBuffers[0];
+ const AHardwareBuffer_Desc* pDesc =
+ reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer3d.hardwareBuffer.description);
+ oldWidth = pDesc->width;
+ oldHeight = pDesc->height;
+
+ // Change config
+ Sv3dConfig config;
+ config.width = oldWidth;
+ config.height = oldHeight;
+ config.carDetails = SvQuality::LOW;
+ surroundView3dSession->set3dConfig(config);
+
+ sleep(1);
+
+ EXPECT_TRUE(handler->checkEventReceived(SvEvent::CONFIG_UPDATED));
+
+ Sv3dConfig retConfig;
+ surroundView3dSession->get3dConfig([&retConfig] (Sv3dConfig config) {
+ retConfig.width = config.width;
+ retConfig.height = config.height;
+ retConfig.carDetails = config.carDetails;
+ });
+
+ // Check config blending has been changed but not the width
+ EXPECT_EQ(retConfig.carDetails, config.carDetails);
+ EXPECT_EQ(retConfig.width, oldWidth);
+ EXPECT_EQ(retConfig.height, oldHeight);
+
+ // Clean up
+ surroundView3dSession->stopStream();
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+std::pair<hidl_memory, sp<IMemory>> GetMappedSharedMemory(int bytesSize) {
+
+ const auto nullResult = std::make_pair(hidl_memory(), nullptr);
+
+ sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
+ if (ashmemAllocator.get() == nullptr) {
+ ALOGE("SurroundViewHidlTest getService ashmem failed");
+ return nullResult;
+ }
+
+ // Allocate shared memory.
+ hidl_memory hidlMemory;
+ bool allocateSuccess = false;
+ Return<void> result = ashmemAllocator->allocate(bytesSize,
+ [&](bool success, const hidl_memory& hidlMem) {
+ if (!success) {
+ return;
+ }
+ allocateSuccess = success;
+ hidlMemory = hidlMem;
+ });
+
+ // Check result of allocated memory.
+ if (!result.isOk() || !allocateSuccess) {
+ ALOGE("SurroundViewHidlTest allocate shared memory failed");
+ return nullResult;
+ }
+
+ // Map shared memory.
+ sp<IMemory> pIMemory = mapMemory(hidlMemory);
+ if (pIMemory.get() == nullptr) {
+ ALOGE("SurroundViewHidlTest map shared memory failed");
+ return nullResult;
+ }
+
+ return std::make_pair(hidlMemory, pIMemory);
+}
+
+void SetIndexOfOverlaysMemory(
+ const std::vector<OverlayMemoryDesc>& overlaysMemDesc,
+ sp<IMemory> pIMemory, int indexPosition, uint16_t indexValue) {
+
+ // Count the number of vertices until the index.
+ int totalVerticesCount = 0;
+ for (int i = 0; i < indexPosition; i++) {
+ totalVerticesCount += overlaysMemDesc[i].verticesCount;
+ }
+
+ const int indexBytePosition = (indexPosition * kIdByteSize) +
+ (kVertexByteSize * totalVerticesCount);
+
+ uint8_t* pSharedMemoryData = (uint8_t*)((void*)pIMemory->getPointer());
+ pSharedMemoryData += indexBytePosition;
+ uint16_t* pIndex16bit = (uint16_t*)pSharedMemoryData;
+
+ ALOGD("Setting index at pos %d", indexBytePosition);
+
+ // Modify shared memory.
+ pIMemory->update();
+ *pIndex16bit = indexValue;
+ pIMemory->commit();
+}
+
+std::pair<OverlaysData, sp<IMemory>> GetSampleOverlaysData() {
+ OverlaysData overlaysData;
+ overlaysData.overlaysMemoryDesc.resize(2);
+
+ int sharedMemBytesSize = 0;
+ OverlayMemoryDesc overlayMemDesc1, overlayMemDesc2;
+ overlayMemDesc1.id = 0;
+ overlayMemDesc1.verticesCount = 6;
+ overlayMemDesc1.overlayPrimitive = OverlayPrimitive::TRIANGLES;
+ overlaysData.overlaysMemoryDesc[0] = overlayMemDesc1;
+ sharedMemBytesSize += kIdByteSize +
+ kVertexByteSize * overlayMemDesc1.verticesCount;
+
+ overlayMemDesc2.id = 1;
+ overlayMemDesc2.verticesCount = 4;
+ overlayMemDesc2.overlayPrimitive = OverlayPrimitive::TRIANGLES_STRIP;
+ overlaysData.overlaysMemoryDesc[1] = overlayMemDesc2;
+ sharedMemBytesSize += kIdByteSize +
+ kVertexByteSize * overlayMemDesc2.verticesCount;
+
+ std::pair<hidl_memory, sp<IMemory>> sharedMem =
+ GetMappedSharedMemory(sharedMemBytesSize);
+ sp<IMemory> pIMemory = sharedMem.second;
+ if (pIMemory.get() == nullptr) {
+ return std::make_pair(OverlaysData(), nullptr);
+ }
+
+ // Get pointer to shared memory data and set all bytes to 0.
+ uint8_t* pSharedMemoryData = (uint8_t*)((void*)pIMemory->getPointer());
+ pIMemory->update();
+ memset(pSharedMemoryData, 0, sharedMemBytesSize);
+ pIMemory->commit();
+
+ std::vector<OverlayMemoryDesc> overlaysDesc = {overlayMemDesc1,
+ overlayMemDesc2};
+
+ // Set indexes in shared memory.
+ SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 0, overlayMemDesc1.id);
+ SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 1, overlayMemDesc2.id);
+
+ overlaysData.overlaysMemoryDesc = overlaysDesc;
+ overlaysData.overlaysMemory = sharedMem.first;
+
+ return std::make_pair(overlaysData, pIMemory);
+}
+
+TEST_P(SurroundViewHidlTest, updateOverlaysSuccess) {
+ ALOGD("SurroundViewHidlTest, updateOverlaysSuccess");
+
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ std::pair<OverlaysData, sp<IMemory>> overlaysData = GetSampleOverlaysData();
+ ASSERT_NE(overlaysData.second, nullptr);
+
+ SvResult result = surroundView3dSession->updateOverlays(overlaysData.first);
+ EXPECT_EQ(result, SvResult::OK);
+
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, overlaysDataMismatchIdFail) {
+ ALOGD("SurroundViewHidlTest, overlaysDataMismatchIdFail");
+
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ std::pair<OverlaysData, sp<IMemory>> overlaysDataMismatchId
+ = GetSampleOverlaysData();
+ ASSERT_NE(overlaysDataMismatchId.second, nullptr);
+
+ // Set id of second overlay in shared memory to 2 (expected is 1).
+ auto& overlaysDesc = overlaysDataMismatchId.first.overlaysMemoryDesc;
+ auto& pIMemory = overlaysDataMismatchId.second;
+ SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 1, 2);
+
+ SvResult result = surroundView3dSession->updateOverlays(
+ overlaysDataMismatchId.first);
+ EXPECT_EQ(result, SvResult::INVALID_ARG);
+
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, overlaysDataNullMemoryFail) {
+ ALOGD("SurroundViewHidlTest, overlaysDataNullMemoryFail");
+
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ std::pair<OverlaysData, sp<IMemory>> overlaysDataNullMemory
+ = GetSampleOverlaysData();
+
+ // Set shared memory to null.
+ overlaysDataNullMemory.first.overlaysMemory = hidl_memory();
+
+ SvResult result = surroundView3dSession->updateOverlays(
+ overlaysDataNullMemory.first);
+ EXPECT_EQ(result, SvResult::INVALID_ARG);
+
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, overlaysDataLessThan3VerticesFail) {
+ ALOGD("SurroundViewHidlTest, overlaysDataLessThan3VerticesFail");
+
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ std::pair<OverlaysData, sp<IMemory>> overlaysData2Vertices
+ = GetSampleOverlaysData();
+
+ // Set vertices count of second overlay to 2.
+ overlaysData2Vertices.first.overlaysMemoryDesc[1].verticesCount = 2;
+
+ SvResult result = surroundView3dSession->updateOverlays(
+ overlaysData2Vertices.first);
+ EXPECT_EQ(result, SvResult::INVALID_ARG);
+
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, overlaysDataVerticesCountFail) {
+ ALOGD("SurroundViewHidlTest, overlaysDataVerticesNotMultipleOf3Fail");
+
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ OverlaysData overlaysData;
+ overlaysData.overlaysMemoryDesc.resize(1);
+
+ OverlayMemoryDesc overlayMemDesc1;
+ overlayMemDesc1.id = 0;
+ overlayMemDesc1.verticesCount = 4; // Invalid count for TRIANGLES primitive.
+ overlayMemDesc1.overlayPrimitive = OverlayPrimitive::TRIANGLES;
+ overlaysData.overlaysMemoryDesc[0] = overlayMemDesc1;
+
+ const int sharedMemBytesSize =
+ 2 + sizeof(float) * overlayMemDesc1.verticesCount;
+ auto sharedMem = GetMappedSharedMemory(sharedMemBytesSize);
+
+ // Set index in shared memory.
+ auto& overlaysDesc = overlaysData.overlaysMemoryDesc;
+ auto& pIMemory = sharedMem.second;
+ SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 0, overlayMemDesc1.id);
+
+ SvResult result = surroundView3dSession->updateOverlays(overlaysData);
+ EXPECT_EQ(result, SvResult::INVALID_ARG);
+
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, overlaysDataSameIdFail) {
+ ALOGD("SurroundViewHidlTest, overlaysDataSameIdFail");
+
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ std::pair<OverlaysData, sp<IMemory>> overlaysDataSameId
+ = GetSampleOverlaysData();
+ ASSERT_NE(overlaysDataSameId.second, nullptr);
+
+ // Set id of second overlay as id of first.
+ auto& overlaysDesc = overlaysDataSameId.first.overlaysMemoryDesc;
+ auto& pIMemory = overlaysDataSameId.second;
+ SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 1, overlaysDesc[0].id);
+
+ SvResult result = surroundView3dSession->updateOverlays(
+ overlaysDataSameId.first);
+ EXPECT_EQ(result, SvResult::INVALID_ARG);
+
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, projectPointsIncorrectCameraIdFail) {
+ ALOGD("SurroundViewHidlTest, projectPointsIncorrectCameraIdFail");
+
+ hidl_vec<hidl_string> cameraIds;
+ mSurroundViewService->getCameraIds([&cameraIds](
+ const hidl_vec<hidl_string>& camIds) {
+ cameraIds = camIds;
+ });
+ EXPECT_GT(cameraIds.size(), 0);
+
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ Point2dInt cameraPoint;
+ cameraPoint.x = 0;
+ cameraPoint.y = 0;
+ std::vector<Point2dInt> cameraPoints = {cameraPoint};
+
+ hidl_string invalidCameraId = "INVALID_CAMERA_ID";
+
+ std::vector<Point3dFloat> points3d;
+ surroundView3dSession->projectCameraPointsTo3dSurface(
+ cameraPoints, invalidCameraId,
+ [&points3d](const hidl_vec<Point3dFloat>& points3dproj) {
+ points3d = points3dproj;
+ });
+
+ EXPECT_TRUE(points3d.empty());
+
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, projectPointsInvalidPointsFail) {
+ ALOGD("SurroundViewHidlTest, projectPointsInvalidPointsFail");
+
+ hidl_vec<hidl_string> cameraIds;
+ mSurroundViewService->getCameraIds([&cameraIds](
+ const hidl_vec<hidl_string>& camIds) {
+ cameraIds = camIds;
+ });
+
+ EXPECT_GT(cameraIds.size(), 0);
+
+ sp<ISurroundView3dSession> surroundView3dSession;
+ mSurroundViewService->start3dSession(
+ [&surroundView3dSession](
+ const sp<ISurroundView3dSession>& session, SvResult result) {
+ ASSERT_EQ(result, SvResult::OK);
+ surroundView3dSession = session;
+ });
+
+ sp<SurroundViewServiceHandler> handler =
+ new SurroundViewServiceHandler(surroundView3dSession);
+
+ std::vector<View3d> views(1);
+ views[0].viewId = 0;
+ SvResult setViewResult = surroundView3dSession->setViews(views);
+ EXPECT_EQ(setViewResult, SvResult::OK);
+ SvResult result = surroundView3dSession->startStream(handler);
+ EXPECT_EQ(result, SvResult::OK);
+
+ sleep(1);
+
+ // Get the width and height of the frame
+ int width, height;
+ SvFramesDesc frames = handler->getLastReceivedFrames();
+ EXPECT_EQ(frames.svBuffers.size(), 1);
+ SvBuffer svBuffer2d = frames.svBuffers[0];
+ const AHardwareBuffer_Desc* pDesc =
+ reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer2d.hardwareBuffer.description);
+ width = pDesc->width;
+ height = pDesc->height;
+
+ Point2dInt cameraPoint;
+ cameraPoint.x = width * 2;
+ cameraPoint.y = height * 2;
+ std::vector<Point2dInt> cameraPoints = {cameraPoint};
+
+ std::vector<Point3dFloat> points3d;
+ surroundView3dSession->projectCameraPointsTo3dSurface(
+ cameraPoints, cameraIds[0],
+ [&points3d](const hidl_vec<Point3dFloat>& points3dproj) {
+ points3d = points3dproj;
+ });
+
+ EXPECT_FALSE(points3d[0].isValid);
+
+ surroundView3dSession->stopStream();
+ mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance,
+ SurroundViewHidlTest,
+ testing::ValuesIn(
+ android::hardware::getAllHalInstanceNames(ISurroundViewService::descriptor)
+ ),
+ android::hardware::PrintInstanceNameToString
+);
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 785f0e0..4b94800 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -70,6 +70,7 @@
(int)(0x104 | VehiclePropertyGroup::VENDOR | VehiclePropertyType::STRING | VehicleArea::GLOBAL);
constexpr int FUEL_DOOR_REAR_LEFT = (int)PortLocationType::REAR_LEFT;
constexpr int CHARGE_PORT_FRONT_LEFT = (int)PortLocationType::FRONT_LEFT;
+constexpr int CHARGE_PORT_REAR_LEFT = (int)PortLocationType::REAR_LEFT;
constexpr int LIGHT_STATE_ON = (int)VehicleLightState::ON;
constexpr int LIGHT_SWITCH_AUTO = (int)VehicleLightSwitch::AUTOMATIC;
constexpr int WHEEL_FRONT_LEFT = (int)VehicleAreaWheel::LEFT_FRONT;
@@ -249,6 +250,14 @@
{.config =
{
+ .prop = toInt(VehicleProperty::INFO_MULTI_EV_PORT_LOCATIONS),
+ .access = VehiclePropertyAccess::READ,
+ .changeMode = VehiclePropertyChangeMode::STATIC,
+ },
+ .initialValue = {.int32Values = {CHARGE_PORT_FRONT_LEFT, CHARGE_PORT_REAR_LEFT}}},
+
+ {.config =
+ {
.prop = toInt(VehicleProperty::INFO_MAKE),
.access = VehiclePropertyAccess::READ,
.changeMode = VehiclePropertyChangeMode::STATIC,
@@ -457,6 +466,14 @@
},
.initialValue = {.int32Values = {0, 0, 0}}},
+ {.config =
+ {
+ .prop = toInt(VehicleProperty::HW_ROTARY_INPUT),
+ .access = VehiclePropertyAccess::READ,
+ .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+ },
+ .initialValue = {.int32Values = {0, 0, 0}}},
+
{.config = {.prop = toInt(VehicleProperty::HVAC_POWER_ON),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index cbd9e28..23f9135 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -320,6 +320,25 @@
| VehicleArea:GLOBAL),
/**
+ * Multiple EV port locations
+ *
+ * Implement this property if the vehicle has multiple EV ports.
+ * Port locations are defined in PortLocationType.
+ * For example, a car has one port in front left and one port in rear left:
+ * int32Values[0] = PortLocationType::FRONT_LEFT
+ * int32Values[0] = PortLocationType::REAR_LEFT
+ *
+ * @change_mode VehiclePropertyChangeMode:STATIC
+ * @access VehiclePropertyAccess:READ
+ * @data_enum PortLocationType
+ */
+ INFO_MULTI_EV_PORT_LOCATIONS = (
+ 0x010C
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:INT32_VEC
+ | VehicleArea:GLOBAL),
+
+ /**
* Current odometer value of the vehicle
*
* @change_mode VehiclePropertyChangeMode:CONTINUOUS
@@ -1413,6 +1432,33 @@
| VehiclePropertyType:INT32_VEC
| VehicleArea:GLOBAL),
+ /**
+ * Property to feed H/W rotary events to android
+ *
+ * int32Values[0] : RotaryInputType identifying which rotary knob rotated
+ * int32Values[1] : number of detents (clicks), positive for clockwise,
+ * negative for counterclockwise
+ * int32Values[2] : target display defined in VehicleDisplay. Events not
+ * tied to specific display must be sent to
+ * VehicleDisplay#MAIN.
+ * int32values[3 .. 3 + abs(number of detents) - 2]:
+ * nanosecond deltas between pairs of consecutive detents,
+ * if the number of detents is > 1 or < -1
+ *
+ * VehiclePropValue.timestamp: when the rotation occurred. If the number of
+ * detents is > 1 or < -1, this is when the
+ * first detent of rotation occurred.
+ *
+ * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @data_enum RotaryInputType
+ * @access VehiclePropertyAccess:READ
+ */
+ HW_ROTARY_INPUT = (
+ 0x0A20
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:INT32_VEC
+ | VehicleArea:GLOBAL),
+
/***************************************************************************
* Most Car Cabin properties have both a POSition and MOVE parameter. These
* are used to control the various movements for seats, doors, and windows
@@ -4651,3 +4697,18 @@
UserIdentificationAssociationSetValue value;
};
+
+/**
+ * A rotary control which can rotate without limits. These controls use HW_ROTARY_INPUT to report
+ * relative clockwise or counterclockwise motion. They have no absolute position.
+ */
+enum RotaryInputType : int32_t {
+ /**
+ * Main rotary control, typically in the center console, used to navigate the user interface.
+ */
+ ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION = 0,
+
+ /** Volume control for adjusting audio volume. */
+ ROTARY_INPUT_TYPE_AUDIO_VOLUME = 1,
+};
+
diff --git a/camera/device/3.6/types.hal b/camera/device/3.6/types.hal
index 743b139..f4c50ed 100644
--- a/camera/device/3.6/types.hal
+++ b/camera/device/3.6/types.hal
@@ -119,14 +119,20 @@
* For devices that does not support the OFFLINE_PROCESSING capability, this
* fields will always be false.
*
- * For devices support the OFFLINE_PROCESSING capability: any input stream
- * and any output stream that can be output of the input stream must set
- * this field to true. Also any stream of YUV420_888 format or JPEG format,
- * with CPU_READ usage flag, must set this field to true. All other streams
- * are up to camera HAL to advertise support or not, though it is not
- * recommended to list support for streams with hardware composer or video
- * encoder usage flags as these streams tend to be targeted continuously and
- * can lead to long latency when trying to switch to offline.
+ * For backward compatible camera devices that support the
+ * OFFLINE_PROCESSING capability: any input stream and any output stream
+ * that can be output of the input stream must set this field to true. Also
+ * any stream of YUV420_888 format or JPEG format, with CPU_READ usage flag,
+ * must set this field to true.
+ *
+ * For depth only camera devices that support the OFFLINE_PROCESSING
+ * capability: any DEPTH16 output stream must set this field to true.
+ *
+ * All other streams are up to camera HAL to advertise support or not,
+ * though it is not recommended to list support for streams with
+ * hardware composer or video encoder usage flags as these streams tend
+ * to be targeted continuously and can lead to long latency when trying to
+ * switch to offline.
*
*/
bool supportOffline;
diff --git a/camera/provider/2.6/ICameraProvider.hal b/camera/provider/2.6/ICameraProvider.hal
index 0948db6..9c46ba0 100644
--- a/camera/provider/2.6/ICameraProvider.hal
+++ b/camera/provider/2.6/ICameraProvider.hal
@@ -38,20 +38,29 @@
* streaming concurrently with the other camera ids in the combination.
*
* Target 1 Target 2
- * ---------------------------------------------
- * | Type | Size | Type | Size |
- * ---------------------------------------------
- * | YUV | 1280 X 720 | |
- * ---------------------------------------------
- * | PRIV | 1280 X 720 | |
- * ---------------------------------------------
- * | YUV | 1280 X 720 | YUV |1280 X 720|
- * ---------------------------------------------
- * | PRIV | 1280 X 720 | PRIV |1280 X 720|
- * ---------------------------------------------
- * | PRIV | 1280 X 720 | YUV |1280 X 720|
- * ---------------------------------------------
-
+ * -----------------------------------------------------
+ * | Type | Size | Type | Size |
+ * -----------------------------------------------------
+ * | YUV | s1440p | |
+ * -----------------------------------------------------
+ * | JPEG | s1440p | |
+ * -----------------------------------------------------
+ * | PRIV | s1440p | |
+ * -----------------------------------------------------
+ * | YUV / PRIV | s720p | YUV / PRIV | s1440p |
+ * -----------------------------------------------------
+ * | YUV / PRIV | s720p | JPEG | s1440p |
+ * -----------------------------------------------------
+ *
+ * where:
+ * s720p - min (max output resolution for the given format, 1280 X 720)
+ * s1440p - min (max output resolution for the given format, 1920 X 1440)
+ *
+ * The camera framework must call this method whenever it gets a
+ * cameraDeviceStatusChange callback adding a new camera device or removing
+ * a camera device known to it. This is so that the camera framework can get new combinations
+ * of camera ids that can stream concurrently, that might have potentially appeared.
+ *
* @return status Status code for the operation
* @return cameraIds a list of camera id combinations that support
* concurrent stream configurations with the minimum guarantees
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index f8cec64..de1ee84 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -56,6 +56,14 @@
</interface>
</hal>
<hal format="hidl" optional="true">
+ <name>android.hardware.automotive.sv</name>
+ <version>1.0</version>
+ <interface>
+ <name>ISurroundView</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
<name>android.hardware.automotive.vehicle</name>
<version>2.0</version>
<interface>
@@ -153,7 +161,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.contexthub</name>
- <version>1.0</version>
+ <version>1.0-1</version>
<interface>
<name>IContexthub</name>
<instance>default</instance>
@@ -280,14 +288,6 @@
<instance>strongbox</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.light</name>
- <version>2.0</version>
- <interface>
- <name>ILight</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.light</name>
<interface>
@@ -383,7 +383,10 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.radio.config</name>
- <version>1.3</version>
+ <!--
+ See compatibility_matrix.4.xml on versioning of radio config HAL.
+ -->
+ <version>1.1</version>
<interface>
<name>IRadioConfig</name>
<instance>default</instance>
@@ -416,7 +419,7 @@
<hal format="hidl" optional="true">
<name>android.hardware.sensors</name>
<version>1.0</version>
- <version>2.0</version>
+ <version>2.0-1</version>
<interface>
<name>ISensors</name>
<instance>default</instance>
diff --git a/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp b/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp
index 278d1f4..fb01ad0 100644
--- a/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp
+++ b/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp
@@ -336,7 +336,7 @@
ASSERT_EQ(0U, result.args->formattedMessage_.size());
}
-// Simulates the framework candelling an ongoing prompt
+// Simulates the framework cancelling an ongoing prompt
TEST_F(ConfirmationUIHidlTest, AbortTest) {
static constexpr char test_prompt[] = "Me first, gimme gimme!";
static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
@@ -354,6 +354,92 @@
ASSERT_EQ(0U, result.args->formattedMessage_.size());
}
+// Tests if the confirmation dialog can successfully render 100 'W' characters as required by
+// the design guidelines.
+TEST_F(ConfirmationUIHidlTest, PortableMessageTest1) {
+ static constexpr char test_prompt[] =
+ "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
+ "WWWWWWWWWWWWWW";
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
+ hidl_string prompt_text(test_prompt);
+ hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
+ ASSERT_HAL_CALL(ResponseCode::OK,
+ confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
+
+ confirmator().abort();
+
+ auto result = conf_cb->WaitForCallback();
+ ASSERT_EQ(ResponseCode::Aborted, result.args->error_);
+ ASSERT_EQ(0U, result.args->confirmationToken_.size());
+ ASSERT_EQ(0U, result.args->formattedMessage_.size());
+}
+
+// Tests if the confirmation dialog can successfully render 100 'W' characters as required by
+// the design guidelines in magnified mode.
+TEST_F(ConfirmationUIHidlTest, PortableMessageTest1Magnified) {
+ static constexpr char test_prompt[] =
+ "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
+ "WWWWWWWWWWWWWW";
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
+ hidl_string prompt_text(test_prompt);
+ hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
+ ASSERT_HAL_CALL(ResponseCode::OK,
+ confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en",
+ {UIOption::AccessibilityMagnified}));
+
+ confirmator().abort();
+
+ auto result = conf_cb->WaitForCallback();
+ ASSERT_EQ(ResponseCode::Aborted, result.args->error_);
+ ASSERT_EQ(0U, result.args->confirmationToken_.size());
+ ASSERT_EQ(0U, result.args->formattedMessage_.size());
+}
+
+// Tests if the confirmation dialog can successfully render 8 groups of 12 'W' characters as
+// required by the design guidelines.
+TEST_F(ConfirmationUIHidlTest, PortableMessageTest2) {
+ static constexpr char test_prompt[] =
+ "WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW "
+ "WWWWWWWWWWWW WWWWWWWWWWWW";
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
+ hidl_string prompt_text(test_prompt);
+ hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
+ ASSERT_HAL_CALL(ResponseCode::OK,
+ confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
+
+ confirmator().abort();
+
+ auto result = conf_cb->WaitForCallback();
+ ASSERT_EQ(ResponseCode::Aborted, result.args->error_);
+ ASSERT_EQ(0U, result.args->confirmationToken_.size());
+ ASSERT_EQ(0U, result.args->formattedMessage_.size());
+}
+
+// Tests if the confirmation dialog can successfully render 8 groups of 12 'W' characters as
+// required by the design guidelines in magnified mode.
+TEST_F(ConfirmationUIHidlTest, PortableMessageTest2Magnified) {
+ static constexpr char test_prompt[] =
+ "WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW "
+ "WWWWWWWWWWWW WWWWWWWWWWWW";
+ static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+ sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
+ hidl_string prompt_text(test_prompt);
+ hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
+ ASSERT_HAL_CALL(ResponseCode::OK,
+ confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en",
+ {UIOption::AccessibilityMagnified}));
+
+ confirmator().abort();
+
+ auto result = conf_cb->WaitForCallback();
+ ASSERT_EQ(ResponseCode::Aborted, result.args->error_);
+ ASSERT_EQ(0U, result.args->confirmationToken_.size());
+ ASSERT_EQ(0U, result.args->formattedMessage_.size());
+}
+
// Passing malformed UTF-8 to the confirmation UI
// This test passes a string that ends in the middle of a multibyte character
TEST_F(ConfirmationUIHidlTest, MalformedUTF8Test1) {
diff --git a/contexthub/1.0/vts/functional/Android.bp b/contexthub/1.0/vts/functional/Android.bp
index 9e99c33..d51c966 100644
--- a/contexthub/1.0/vts/functional/Android.bp
+++ b/contexthub/1.0/vts/functional/Android.bp
@@ -18,7 +18,10 @@
name: "VtsHalContexthubV1_0TargetTest",
defaults: ["VtsHalTargetTestDefaults"],
srcs: ["VtsHalContexthubV1_0TargetTest.cpp"],
- static_libs: ["android.hardware.contexthub@1.0"],
+ static_libs: [
+ "android.hardware.contexthub@1.0",
+ "VtsHalContexthubUtils",
+ ],
test_suites: [
"general-tests",
"vts-core",
diff --git a/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp b/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
index a1d173b..ada232b 100644
--- a/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
+++ b/contexthub/1.0/vts/functional/VtsHalContexthubV1_0TargetTest.cpp
@@ -16,6 +16,10 @@
#define LOG_TAG "contexthub_hidl_hal_test"
+#include "ContexthubCallbackBase.h"
+#include "ContexthubHidlTestBase.h"
+#include "VtsHalContexthubUtils.h"
+
#include <android-base/logging.h>
#include <android/hardware/contexthub/1.0/IContexthub.h>
#include <android/hardware/contexthub/1.0/IContexthubCallback.h>
@@ -23,17 +27,17 @@
#include <android/log.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
#include <log/log.h>
#include <cinttypes>
#include <future>
#include <utility>
-using ::android::hardware::Return;
-using ::android::hardware::Void;
+using ::android::sp;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
using ::android::hardware::contexthub::V1_0::AsyncEventType;
using ::android::hardware::contexthub::V1_0::ContextHub;
using ::android::hardware::contexthub::V1_0::ContextHubMsg;
@@ -43,10 +47,11 @@
using ::android::hardware::contexthub::V1_0::NanoAppBinary;
using ::android::hardware::contexthub::V1_0::Result;
using ::android::hardware::contexthub::V1_0::TransactionResult;
-using ::android::sp;
-
-#define ASSERT_OK(result) ASSERT_EQ(result, Result::OK)
-#define EXPECT_OK(result) EXPECT_EQ(result, Result::OK)
+using ::android::hardware::contexthub::vts_utils::asBaseType;
+using ::android::hardware::contexthub::vts_utils::ContexthubCallbackBase;
+using ::android::hardware::contexthub::vts_utils::ContexthubHidlTestBase;
+using ::android::hardware::contexthub::vts_utils::getHalAndHubIdList;
+using ::android::hardware::contexthub::vts_utils::getHubsSync;
namespace {
@@ -54,132 +59,37 @@
// app ID is reserved and must never appear in the list of loaded apps.
constexpr uint64_t kNonExistentAppId = 0x476f6f6754555555;
-// Helper that does explicit conversion of an enum class to its underlying/base
-// type. Useful for stream output of enum values.
-template<typename EnumType>
-constexpr typename std::underlying_type<EnumType>::type asBaseType(
- EnumType value) {
- return static_cast<typename std::underlying_type<EnumType>::type>(value);
-}
+const std::vector<std::tuple<std::string, std::string>> kTestParameters =
+ getHalAndHubIdList<IContexthub>();
-// Synchronously queries IContexthub::getHubs() and returns the result
-hidl_vec<ContextHub> getHubsSync(sp<IContexthub> hubApi) {
- hidl_vec<ContextHub> hubList;
- std::promise<void> barrier;
-
- hubApi->getHubs([&hubList, &barrier](const hidl_vec<ContextHub>& hubs) {
- hubList = hubs;
- barrier.set_value();
- });
- barrier.get_future().wait_for(std::chrono::seconds(1));
-
- return hubList;
-}
-
-// Gets a list of valid hub IDs in the system
-std::vector<std::string> getHubIds(const std::string& service_name) {
- std::vector<std::string> hubIds;
-
- sp<IContexthub> hubApi = IContexthub::getService(service_name);
-
- if (hubApi != nullptr) {
- for (const ContextHub& hub : getHubsSync(hubApi)) {
- hubIds.push_back(std::to_string(hub.hubId));
- }
- }
-
- ALOGD("Running tests against all %zu reported hubs for service %s", hubIds.size(),
- service_name.c_str());
- return hubIds;
-}
-
-// Test fixture parameterized by hub ID, initializes the HAL and makes the context hub API handle
-// available.
-class ContexthubHidlTest : public ::testing::TestWithParam<std::tuple<std::string, std::string>> {
- public:
- virtual void SetUp() override {
- hubApi = IContexthub::getService(std::get<0>(GetParam()));
- ASSERT_NE(hubApi, nullptr);
-
- // getHubs() must be called at least once for proper initialization of the
- // HAL implementation
- getHubsSync(hubApi);
- }
-
- uint32_t getHubId() { return std::stoi(std::get<1>(GetParam())); }
-
- Result registerCallback(sp<IContexthubCallback> cb) {
- Result result = hubApi->registerCallback(getHubId(), cb);
- ALOGD("Registered callback, result %" PRIu32, result);
- return result;
- }
-
- sp<IContexthub> hubApi;
-};
-
-// Base callback implementation that just logs all callbacks by default
-class ContexthubCallbackBase : public IContexthubCallback {
- public:
- virtual Return<void> handleClientMsg(const ContextHubMsg& /*msg*/) override {
- ALOGD("Got client message callback");
- return Void();
- }
-
- virtual Return<void> handleTxnResult(
- uint32_t txnId, TransactionResult result) override {
- ALOGD("Got transaction result callback for txnId %" PRIu32 " with result %"
- PRId32, txnId, result);
- return Void();
- }
-
- virtual Return<void> handleHubEvent(AsyncEventType evt) override {
- ALOGD("Got hub event callback for event type %" PRIu32, evt);
- return Void();
- }
-
- virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode)
- override {
- ALOGD("Got app abort notification for appId 0x%" PRIx64 " with abort code "
- "0x%" PRIx32, appId, abortCode);
- return Void();
- }
-
- virtual Return<void> handleAppsInfo(const hidl_vec<HubAppInfo>& /*appInfo*/)
- override {
- ALOGD("Got app info callback");
- return Void();
- }
-};
+class ContexthubHidlTest : public ContexthubHidlTestBase<IContexthub> {};
// Wait for a callback to occur (signaled by the given future) up to the
// provided timeout. If the future is invalid or the callback does not come
// within the given time, returns false.
-template<class ReturnType>
-bool waitForCallback(
- std::future<ReturnType> future,
- ReturnType *result,
- std::chrono::milliseconds timeout = std::chrono::seconds(5)) {
- auto expiration = std::chrono::system_clock::now() + timeout;
+template <class ReturnType>
+bool waitForCallback(std::future<ReturnType> future, ReturnType* result,
+ std::chrono::milliseconds timeout = std::chrono::seconds(5)) {
+ auto expiration = std::chrono::system_clock::now() + timeout;
- EXPECT_NE(result, nullptr);
- EXPECT_TRUE(future.valid());
- if (result != nullptr && future.valid()) {
- std::future_status status = future.wait_until(expiration);
- EXPECT_NE(status, std::future_status::timeout)
- << "Timed out waiting for callback";
+ EXPECT_NE(result, nullptr);
+ EXPECT_TRUE(future.valid());
+ if (result != nullptr && future.valid()) {
+ std::future_status status = future.wait_until(expiration);
+ EXPECT_NE(status, std::future_status::timeout) << "Timed out waiting for callback";
- if (status == std::future_status::ready) {
- *result = future.get();
- return true;
+ if (status == std::future_status::ready) {
+ *result = future.get();
+ return true;
+ }
}
- }
- return false;
+ return false;
}
// Ensures that the metadata reported in getHubs() is sane
TEST_P(ContexthubHidlTest, TestGetHubs) {
- hidl_vec<ContextHub> hubs = getHubsSync(hubApi);
+ hidl_vec<ContextHub> hubs = getHubsSync(hubApi.get());
ALOGD("System reports %zu hubs", hubs.size());
for (const ContextHub& hub : hubs) {
@@ -199,189 +109,158 @@
}
TEST_P(ContexthubHidlTest, TestRegisterCallback) {
- ALOGD("TestRegisterCallback called, hubId %" PRIu32, getHubId());
- ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
+ ALOGD("TestRegisterCallback called, hubId %" PRIu32, getHubId());
+ ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
}
TEST_P(ContexthubHidlTest, TestRegisterNullCallback) {
- ALOGD("TestRegisterNullCallback called, hubId %" PRIu32, getHubId());
- ASSERT_OK(registerCallback(nullptr));
+ ALOGD("TestRegisterNullCallback called, hubId %" PRIu32, getHubId());
+ ASSERT_OK(registerCallback(nullptr));
}
// Helper callback that puts the async appInfo callback data into a promise
class QueryAppsCallback : public ContexthubCallbackBase {
- public:
- virtual Return<void> handleAppsInfo(const hidl_vec<HubAppInfo>& appInfo)
- override {
- ALOGD("Got app info callback with %zu apps", appInfo.size());
- promise.set_value(appInfo);
- return Void();
- }
+ public:
+ virtual Return<void> handleAppsInfo(const hidl_vec<HubAppInfo>& appInfo) override {
+ ALOGD("Got app info callback with %zu apps", appInfo.size());
+ promise.set_value(appInfo);
+ return Void();
+ }
- std::promise<hidl_vec<HubAppInfo>> promise;
+ std::promise<hidl_vec<HubAppInfo>> promise;
};
// Calls queryApps() and checks the returned metadata
TEST_P(ContexthubHidlTest, TestQueryApps) {
- ALOGD("TestQueryApps called, hubId %u", getHubId());
- sp<QueryAppsCallback> cb = new QueryAppsCallback();
- ASSERT_OK(registerCallback(cb));
+ ALOGD("TestQueryApps called, hubId %u", getHubId());
+ sp<QueryAppsCallback> cb = new QueryAppsCallback();
+ ASSERT_OK(registerCallback(cb));
- Result result = hubApi->queryApps(getHubId());
- ASSERT_OK(result);
+ Result result = hubApi->queryApps(getHubId());
+ ASSERT_OK(result);
- ALOGD("Waiting for app info callback");
- hidl_vec<HubAppInfo> appList;
- ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &appList));
- for (const HubAppInfo &appInfo : appList) {
- EXPECT_NE(appInfo.appId, UINT64_C(0));
- EXPECT_NE(appInfo.appId, kNonExistentAppId);
- }
+ ALOGD("Waiting for app info callback");
+ hidl_vec<HubAppInfo> appList;
+ ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &appList));
+ for (const HubAppInfo& appInfo : appList) {
+ EXPECT_NE(appInfo.appId, UINT64_C(0));
+ EXPECT_NE(appInfo.appId, kNonExistentAppId);
+ }
}
// Helper callback that puts the TransactionResult for the expectedTxnId into a
// promise
class TxnResultCallback : public ContexthubCallbackBase {
- public:
- virtual Return<void> handleTxnResult(
- uint32_t txnId, TransactionResult result) override {
- ALOGD("Got transaction result callback for txnId %" PRIu32 " (expecting %"
- PRIu32 ") with result %" PRId32, txnId, expectedTxnId, result);
- if (txnId == expectedTxnId) {
- promise.set_value(result);
+ public:
+ virtual Return<void> handleTxnResult(uint32_t txnId, TransactionResult result) override {
+ ALOGD("Got transaction result callback for txnId %" PRIu32 " (expecting %" PRIu32
+ ") with result %" PRId32,
+ txnId, expectedTxnId, result);
+ if (txnId == expectedTxnId) {
+ promise.set_value(result);
+ }
+ return Void();
}
- return Void();
- }
- uint32_t expectedTxnId = 0;
- std::promise<TransactionResult> promise;
+ uint32_t expectedTxnId = 0;
+ std::promise<TransactionResult> promise;
};
// Parameterized fixture that sets the callback to TxnResultCallback
class ContexthubTxnTest : public ContexthubHidlTest {
- public:
- virtual void SetUp() override {
- ContexthubHidlTest::SetUp();
- ASSERT_OK(registerCallback(cb));
- }
+ public:
+ virtual void SetUp() override {
+ ContexthubHidlTest::SetUp();
+ ASSERT_OK(registerCallback(cb));
+ }
- sp<TxnResultCallback> cb = new TxnResultCallback();
+ sp<TxnResultCallback> cb = new TxnResultCallback();
};
-
// Checks cases where the hub implementation is expected to return an error, but
// that error can be returned either synchronously or in the asynchronous
// transaction callback. Returns an AssertionResult that can be used in
// ASSERT/EXPECT_TRUE. Allows checking the sync result against 1 additional
// allowed error code apart from OK and TRANSACTION_FAILED, which are always
// allowed.
-::testing::AssertionResult checkFailureSyncOrAsync(
- Result result, Result allowedSyncResult,
- std::future<TransactionResult>&& future) {
- if (result == Result::OK) {
- // No error reported synchronously - this is OK, but then we should get an
- // async callback with a failure status
- TransactionResult asyncResult;
- if (!waitForCallback(std::forward<std::future<TransactionResult>>(future),
- &asyncResult)) {
- return ::testing::AssertionFailure()
- << "Got successful sync result, then failed to receive async cb";
- } else if (asyncResult == TransactionResult::SUCCESS) {
- return ::testing::AssertionFailure()
- << "Got successful sync result, then unexpected successful async "
- "result";
+::testing::AssertionResult checkFailureSyncOrAsync(Result result, Result allowedSyncResult,
+ std::future<TransactionResult>&& future) {
+ if (result == Result::OK) {
+ // No error reported synchronously - this is OK, but then we should get an
+ // async callback with a failure status
+ TransactionResult asyncResult;
+ if (!waitForCallback(std::forward<std::future<TransactionResult>>(future), &asyncResult)) {
+ return ::testing::AssertionFailure()
+ << "Got successful sync result, then failed to receive async cb";
+ } else if (asyncResult == TransactionResult::SUCCESS) {
+ return ::testing::AssertionFailure()
+ << "Got successful sync result, then unexpected successful async "
+ "result";
+ }
+ } else if (result != allowedSyncResult && result != Result::TRANSACTION_FAILED) {
+ return ::testing::AssertionFailure()
+ << "Got sync result " << asBaseType(result) << ", expected TRANSACTION_FAILED or "
+ << asBaseType(allowedSyncResult);
}
- } else if (result != allowedSyncResult &&
- result != Result::TRANSACTION_FAILED) {
- return ::testing::AssertionFailure() << "Got sync result "
- << asBaseType(result) << ", expected TRANSACTION_FAILED or "
- << asBaseType(allowedSyncResult);
- }
- return ::testing::AssertionSuccess();
+ return ::testing::AssertionSuccess();
}
TEST_P(ContexthubTxnTest, TestSendMessageToNonExistentNanoApp) {
- ContextHubMsg msg;
- msg.appName = kNonExistentAppId;
- msg.msgType = 1;
- msg.msg.resize(4);
- std::fill(msg.msg.begin(), msg.msg.end(), 0);
+ ContextHubMsg msg;
+ msg.appName = kNonExistentAppId;
+ msg.msgType = 1;
+ msg.msg.resize(4);
+ std::fill(msg.msg.begin(), msg.msg.end(), 0);
- ALOGD("Sending message to non-existent nanoapp");
- Result result = hubApi->sendMessageToHub(getHubId(), msg);
- if (result != Result::OK &&
- result != Result::BAD_PARAMS &&
- result != Result::TRANSACTION_FAILED) {
- FAIL() << "Got result " << asBaseType(result) << ", expected OK, BAD_PARAMS"
- << ", or TRANSACTION_FAILED";
- }
+ ALOGD("Sending message to non-existent nanoapp");
+ Result result = hubApi->sendMessageToHub(getHubId(), msg);
+ if (result != Result::OK && result != Result::BAD_PARAMS &&
+ result != Result::TRANSACTION_FAILED) {
+ FAIL() << "Got result " << asBaseType(result) << ", expected OK, BAD_PARAMS"
+ << ", or TRANSACTION_FAILED";
+ }
}
TEST_P(ContexthubTxnTest, TestLoadEmptyNanoApp) {
- cb->expectedTxnId = 0123;
- NanoAppBinary emptyApp;
+ cb->expectedTxnId = 0123;
+ NanoAppBinary emptyApp;
- emptyApp.appId = kNonExistentAppId;
- emptyApp.appVersion = 1;
- emptyApp.flags = 0;
- emptyApp.targetChreApiMajorVersion = 1;
- emptyApp.targetChreApiMinorVersion = 0;
+ emptyApp.appId = kNonExistentAppId;
+ emptyApp.appVersion = 1;
+ emptyApp.flags = 0;
+ emptyApp.targetChreApiMajorVersion = 1;
+ emptyApp.targetChreApiMinorVersion = 0;
- ALOGD("Loading empty nanoapp");
- Result result = hubApi->loadNanoApp(getHubId(), emptyApp, cb->expectedTxnId);
- EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
- cb->promise.get_future()));
+ ALOGD("Loading empty nanoapp");
+ Result result = hubApi->loadNanoApp(getHubId(), emptyApp, cb->expectedTxnId);
+ EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, cb->promise.get_future()));
}
TEST_P(ContexthubTxnTest, TestUnloadNonexistentNanoApp) {
- cb->expectedTxnId = 1234;
+ cb->expectedTxnId = 1234;
- ALOGD("Unloading nonexistent nanoapp");
- Result result = hubApi->unloadNanoApp(getHubId(), kNonExistentAppId,
- cb->expectedTxnId);
- EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
- cb->promise.get_future()));
+ ALOGD("Unloading nonexistent nanoapp");
+ Result result = hubApi->unloadNanoApp(getHubId(), kNonExistentAppId, cb->expectedTxnId);
+ EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, cb->promise.get_future()));
}
TEST_P(ContexthubTxnTest, TestEnableNonexistentNanoApp) {
- cb->expectedTxnId = 2345;
+ cb->expectedTxnId = 2345;
- ALOGD("Enabling nonexistent nanoapp");
- Result result = hubApi->enableNanoApp(getHubId(), kNonExistentAppId,
- cb->expectedTxnId);
- EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
- cb->promise.get_future()));
+ ALOGD("Enabling nonexistent nanoapp");
+ Result result = hubApi->enableNanoApp(getHubId(), kNonExistentAppId, cb->expectedTxnId);
+ EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, cb->promise.get_future()));
}
TEST_P(ContexthubTxnTest, TestDisableNonexistentNanoApp) {
- cb->expectedTxnId = 3456;
+ cb->expectedTxnId = 3456;
- ALOGD("Disabling nonexistent nanoapp");
- Result result = hubApi->disableNanoApp(getHubId(), kNonExistentAppId,
- cb->expectedTxnId);
- EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
- cb->promise.get_future()));
+ ALOGD("Disabling nonexistent nanoapp");
+ Result result = hubApi->disableNanoApp(getHubId(), kNonExistentAppId, cb->expectedTxnId);
+ EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS, cb->promise.get_future()));
}
-// Return the test parameters of a vecter of tuples for all IContexthub services and each of its hub
-// id: <service name of IContexthub, hub id of the IContexthub service>
-static std::vector<std::tuple<std::string, std::string>> get_parameters() {
- std::vector<std::tuple<std::string, std::string>> parameters;
- std::vector<std::string> service_names =
- android::hardware::getAllHalInstanceNames(IContexthub::descriptor);
- for (const std::string& service_name : service_names) {
- std::vector<std::string> ids = getHubIds(service_name);
- for (const std::string& id : ids) {
- parameters.push_back(std::make_tuple(service_name, id));
- }
- }
-
- return parameters;
-}
-
-static std::vector<std::tuple<std::string, std::string>> kTestParameters = get_parameters();
-
INSTANTIATE_TEST_SUITE_P(HubIdSpecificTests, ContexthubHidlTest, testing::ValuesIn(kTestParameters),
android::hardware::PrintInstanceTupleNameToString<>);
diff --git a/contexthub/1.1/Android.bp b/contexthub/1.1/Android.bp
new file mode 100644
index 0000000..649f1db
--- /dev/null
+++ b/contexthub/1.1/Android.bp
@@ -0,0 +1,18 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.contexthub@1.1",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "IContexthub.hal",
+ ],
+ interfaces: [
+ "android.hardware.contexthub@1.0",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/contexthub/1.1/IContexthub.hal b/contexthub/1.1/IContexthub.hal
new file mode 100644
index 0000000..a3b4bd4
--- /dev/null
+++ b/contexthub/1.1/IContexthub.hal
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.contexthub@1.1;
+
+import @1.0::IContexthub;
+import @1.0::Result;
+
+interface IContexthub extends @1.0::IContexthub {
+ /**
+ * Notification sent by the framework to indicate that the user
+ * has changed a setting.
+ *
+ * @param setting User setting that has been modified.
+ * @param newValue The update value of the user setting.
+ */
+ onSettingChanged(Setting setting, SettingValue newValue);
+};
\ No newline at end of file
diff --git a/contexthub/1.1/default/Android.bp b/contexthub/1.1/default/Android.bp
new file mode 100644
index 0000000..86858c0
--- /dev/null
+++ b/contexthub/1.1/default/Android.bp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_binary {
+ name: "android.hardware.contexthub@1.1-service.mock",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.contexthub@1.1-service.rc"],
+ srcs: [
+ "Contexthub.cpp",
+ "service.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "android.hardware.contexthub@1.0",
+ "android.hardware.contexthub@1.1",
+ "libbase",
+ "libcutils",
+ "libhardware",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+ vintf_fragments: ["android.hardware.contexthub@1.1.xml"],
+}
diff --git a/contexthub/1.1/default/Contexthub.cpp b/contexthub/1.1/default/Contexthub.cpp
new file mode 100644
index 0000000..19cc262
--- /dev/null
+++ b/contexthub/1.1/default/Contexthub.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "Contexthub.h"
+
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::contexthub::V1_0::ContextHub;
+using ::android::hardware::contexthub::V1_0::HubAppInfo;
+using ::android::hardware::contexthub::V1_0::Result;
+
+namespace {
+
+constexpr uint32_t kMockHubId = 0;
+
+} // anonymous namespace
+
+Return<void> Contexthub::getHubs(getHubs_cb _hidl_cb) {
+ ContextHub hub = {};
+ hub.name = "Mock Context Hub";
+ hub.vendor = "AOSP";
+ hub.toolchain = "n/a";
+ hub.platformVersion = 1;
+ hub.toolchainVersion = 1;
+ hub.hubId = kMockHubId;
+ hub.peakMips = 1;
+ hub.peakPowerDrawMw = 1;
+ hub.maxSupportedMsgLen = 4096;
+ hub.chrePlatformId = UINT64_C(0x476f6f6754000000);
+ hub.chreApiMajorVersion = 1;
+ hub.chreApiMinorVersion = 4;
+
+ // Report a single mock hub
+ std::vector<ContextHub> hubs;
+ hubs.push_back(hub);
+
+ _hidl_cb(hubs);
+ return Void();
+}
+
+Return<Result> Contexthub::registerCallback(uint32_t hubId, const sp<IContexthubCallback>& cb) {
+ if (hubId == kMockHubId) {
+ mCallback = cb;
+ return Result::OK;
+ }
+ return Result::BAD_PARAMS;
+}
+
+// We don't expose any nanoapps, therefore all nanoapp-related API calls return with BAD_PARAMS
+Return<Result> Contexthub::sendMessageToHub(uint32_t /*hubId*/, const ContextHubMsg& /*msg*/) {
+ return Result::BAD_PARAMS;
+}
+
+Return<Result> Contexthub::loadNanoApp(uint32_t /*hubId*/, const NanoAppBinary& /*appBinary*/,
+ uint32_t /*transactionId*/) {
+ return Result::BAD_PARAMS;
+}
+
+Return<Result> Contexthub::unloadNanoApp(uint32_t /*hubId*/, uint64_t /*appId*/,
+ uint32_t /*transactionId*/) {
+ return Result::BAD_PARAMS;
+}
+
+Return<Result> Contexthub::enableNanoApp(uint32_t /*hubId*/, uint64_t /*appId*/,
+ uint32_t /*transactionId*/) {
+ return Result::BAD_PARAMS;
+}
+
+Return<Result> Contexthub::disableNanoApp(uint32_t /*hubId*/, uint64_t /*appId*/,
+ uint32_t /*transactionId*/) {
+ return Result::BAD_PARAMS;
+}
+
+Return<Result> Contexthub::queryApps(uint32_t hubId) {
+ if (hubId == kMockHubId && mCallback != nullptr) {
+ std::vector<HubAppInfo> nanoapps;
+ mCallback->handleAppsInfo(nanoapps);
+ return Result::OK;
+ }
+ return Result::BAD_PARAMS;
+}
+
+Return<void> Contexthub::onSettingChanged(Setting /*setting*/, SettingValue /*newValue*/) {
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/contexthub/1.1/default/Contexthub.h b/contexthub/1.1/default/Contexthub.h
new file mode 100644
index 0000000..0da61d1
--- /dev/null
+++ b/contexthub/1.1/default/Contexthub.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android/hardware/contexthub/1.1/IContexthub.h>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace V1_1 {
+namespace implementation {
+
+class Contexthub : public V1_1::IContexthub {
+ using ContextHubMsg = ::android::hardware::contexthub::V1_0::ContextHubMsg;
+ using IContexthubCallback = ::android::hardware::contexthub::V1_0::IContexthubCallback;
+ using NanoAppBinary = ::android::hardware::contexthub::V1_0::NanoAppBinary;
+ using Result = ::android::hardware::contexthub::V1_0::Result;
+
+ public:
+ // Methods from V1_0::IContexthub
+ Return<void> getHubs(getHubs_cb _hidl_cb) override;
+ Return<Result> registerCallback(uint32_t hubId,
+ const ::android::sp<IContexthubCallback>& cb) override;
+ Return<Result> sendMessageToHub(uint32_t hubId, const ContextHubMsg& msg) override;
+ Return<Result> loadNanoApp(uint32_t hubId, const NanoAppBinary& appBinary,
+ uint32_t transactionId) override;
+ Return<Result> unloadNanoApp(uint32_t hubId, uint64_t appId, uint32_t transactionId) override;
+ Return<Result> enableNanoApp(uint32_t hubId, uint64_t appId, uint32_t transactionId) override;
+ Return<Result> disableNanoApp(uint32_t hubId, uint64_t appId, uint32_t transactionId) override;
+ Return<Result> queryApps(uint32_t hubId) override;
+
+ // Methods from V1_1::IContexthub
+ Return<void> onSettingChanged(Setting setting, SettingValue newValue) override;
+
+ private:
+ sp<IContexthubCallback> mCallback;
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/contexthub/1.1/default/OWNERS b/contexthub/1.1/default/OWNERS
new file mode 100644
index 0000000..90c2330
--- /dev/null
+++ b/contexthub/1.1/default/OWNERS
@@ -0,0 +1,3 @@
+arthuri@google.com
+bduddie@google.com
+stange@google.com
diff --git a/contexthub/1.1/default/android.hardware.contexthub@1.1-service.rc b/contexthub/1.1/default/android.hardware.contexthub@1.1-service.rc
new file mode 100644
index 0000000..9db00f9
--- /dev/null
+++ b/contexthub/1.1/default/android.hardware.contexthub@1.1-service.rc
@@ -0,0 +1,6 @@
+service vendor.contexthub-hal-1-1-mock /vendor/bin/hw/android.hardware.contexthub@1.1-service.mock
+ interface android.hardware.contexthub@1.0::IContexthub default
+ interface android.hardware.contexthub@1.1::IContexthub default
+ class hal
+ user system
+ group system
diff --git a/contexthub/1.1/default/android.hardware.contexthub@1.1.xml b/contexthub/1.1/default/android.hardware.contexthub@1.1.xml
new file mode 100644
index 0000000..388f781
--- /dev/null
+++ b/contexthub/1.1/default/android.hardware.contexthub@1.1.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.contexthub</name>
+ <transport>hwbinder</transport>
+ <version>1.1</version>
+ <interface>
+ <name>IContexthub</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/contexthub/1.1/default/service.cpp b/contexthub/1.1/default/service.cpp
new file mode 100644
index 0000000..c5643f1
--- /dev/null
+++ b/contexthub/1.1/default/service.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.contexthub@1.1-service"
+
+#include <android/hardware/contexthub/1.1/IContexthub.h>
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+#include "Contexthub.h"
+
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::joinRpcThreadpool;
+using ::android::hardware::contexthub::V1_1::IContexthub;
+using ::android::hardware::contexthub::V1_1::implementation::Contexthub;
+
+int main() {
+ configureRpcThreadpool(1, true /* callerWillJoin */);
+
+ ::android::sp<IContexthub> contexthub = new Contexthub();
+ if (contexthub->registerAsService() != ::android::OK) {
+ ALOGE("Failed to register Contexthub HAL instance");
+ return 1;
+ }
+
+ joinRpcThreadpool();
+ ALOGE("Service exited");
+ return 1;
+}
diff --git a/radio/config/1.3/IRadioConfigIndication.hal b/contexthub/1.1/types.hal
similarity index 64%
copy from radio/config/1.3/IRadioConfigIndication.hal
copy to contexthub/1.1/types.hal
index 9ef496c..885cf32 100644
--- a/radio/config/1.3/IRadioConfigIndication.hal
+++ b/contexthub/1.1/types.hal
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,19 @@
* limitations under the License.
*/
-package android.hardware.radio.config@1.3;
-
-import @1.2::IRadioConfigIndication;
+package android.hardware.contexthub@1.1;
/**
- * Interface declaring unsolicited radio config indications.
+ * Used to indicate the type of user setting that has changed.
*/
-interface IRadioConfigIndication extends @1.2::IRadioConfigIndication {
-
+enum Setting : uint8_t {
+ LOCATION,
};
+
+/**
+ * Used to indicate the value of a user setting.
+ */
+enum SettingValue : uint8_t {
+ DISABLED,
+ ENABLED,
+};
\ No newline at end of file
diff --git a/identity/1.0/vts/functional/Android.bp b/contexthub/1.1/vts/functional/Android.bp
similarity index 65%
rename from identity/1.0/vts/functional/Android.bp
rename to contexthub/1.1/vts/functional/Android.bp
index 03b49de..f1625a6 100644
--- a/identity/1.0/vts/functional/Android.bp
+++ b/contexthub/1.1/vts/functional/Android.bp
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2019 The Android Open Source Project
+// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,19 +15,13 @@
//
cc_test {
- name: "VtsHalIdentityCredentialTargetTest",
+ name: "VtsHalContexthubV1_1TargetTest",
defaults: ["VtsHalTargetTestDefaults"],
- srcs: [
- "VtsHalIdentityCredentialTargetTest.cpp",
- ],
+ srcs: ["VtsHalContexthubV1_1TargetTest.cpp"],
static_libs: [
- "android.hardware.identity@1.0",
- "android.hardware.identity-support-lib",
- "android.hardware.keymaster@4.0",
- "libcppbor",
- ],
- shared_libs: [
- "libcrypto",
+ "android.hardware.contexthub@1.0",
+ "android.hardware.contexthub@1.1",
+ "VtsHalContexthubUtils",
],
test_suites: [
"general-tests",
diff --git a/contexthub/1.1/vts/functional/OWNERS b/contexthub/1.1/vts/functional/OWNERS
new file mode 100644
index 0000000..161b2f0
--- /dev/null
+++ b/contexthub/1.1/vts/functional/OWNERS
@@ -0,0 +1,8 @@
+#Context Hub team
+arthuri@google.com
+bduddie@google.com
+stange@google.com
+
+#VTS team
+dshi@google.com
+trong@google.com
diff --git a/contexthub/1.1/vts/functional/VtsHalContexthubV1_1TargetTest.cpp b/contexthub/1.1/vts/functional/VtsHalContexthubV1_1TargetTest.cpp
new file mode 100644
index 0000000..f2fcdfc
--- /dev/null
+++ b/contexthub/1.1/vts/functional/VtsHalContexthubV1_1TargetTest.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "contexthub_hidl_hal_test"
+
+#include "ContexthubCallbackBase.h"
+#include "ContexthubHidlTestBase.h"
+#include "VtsHalContexthubUtils.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/contexthub/1.0/IContexthub.h>
+#include <android/hardware/contexthub/1.1/IContexthub.h>
+#include <android/log.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include <cinttypes>
+
+using ::android::hardware::contexthub::V1_1::IContexthub;
+using ::android::hardware::contexthub::V1_1::Setting;
+using ::android::hardware::contexthub::V1_1::SettingValue;
+using ::android::hardware::contexthub::vts_utils::ContexthubCallbackBase;
+using ::android::hardware::contexthub::vts_utils::ContexthubHidlTestBase;
+using ::android::hardware::contexthub::vts_utils::getHalAndHubIdList;
+
+namespace {
+
+const std::vector<std::tuple<std::string, std::string>> kTestParameters =
+ getHalAndHubIdList<IContexthub>();
+
+class ContexthubHidlTest : public ContexthubHidlTestBase<IContexthub> {};
+
+TEST_P(ContexthubHidlTest, TestOnSettingChanged) {
+ // In VTS, we only test that sending the values doesn't cause things to blow up - other test
+ // suites verify the expected E2E behavior in CHRE
+ ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
+ hubApi->onSettingChanged(Setting::LOCATION, SettingValue::DISABLED);
+ hubApi->onSettingChanged(Setting::LOCATION, SettingValue::ENABLED);
+ ASSERT_OK(registerCallback(nullptr));
+}
+
+INSTANTIATE_TEST_SUITE_P(HubIdSpecificTests, ContexthubHidlTest, testing::ValuesIn(kTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
+
+} // anonymous namespace
diff --git a/radio/config/1.3/IRadioConfigIndication.hal b/contexthub/common/vts/Android.bp
similarity index 61%
copy from radio/config/1.3/IRadioConfigIndication.hal
copy to contexthub/common/vts/Android.bp
index 9ef496c..3d5196a 100644
--- a/radio/config/1.3/IRadioConfigIndication.hal
+++ b/contexthub/common/vts/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,17 @@
* limitations under the License.
*/
-package android.hardware.radio.config@1.3;
-
-import @1.2::IRadioConfigIndication;
-
-/**
- * Interface declaring unsolicited radio config indications.
- */
-interface IRadioConfigIndication extends @1.2::IRadioConfigIndication {
-
-};
+cc_test_library {
+ name: "VtsHalContexthubUtils",
+ srcs: [
+ "VtsHalContexthubUtils.cpp",
+ ],
+ export_include_dirs: ["."],
+ shared_libs: [
+ "android.hardware.contexthub@1.0",
+ "libhardware",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/contexthub/common/vts/ContexthubCallbackBase.h b/contexthub/common/vts/ContexthubCallbackBase.h
new file mode 100644
index 0000000..124a116
--- /dev/null
+++ b/contexthub/common/vts/ContexthubCallbackBase.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android/hardware/contexthub/1.0/IContexthubCallback.h>
+#include <log/log.h>
+
+#include <cinttypes>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace vts_utils {
+
+// Base callback implementation that just logs all callbacks by default, but
+// records a failure if
+class ContexthubCallbackBase : public V1_0::IContexthubCallback {
+ public:
+ virtual Return<void> handleClientMsg(const V1_0::ContextHubMsg& /*msg*/) override {
+ ALOGD("Got client message callback");
+ return Void();
+ }
+
+ virtual Return<void> handleTxnResult(uint32_t txnId, V1_0::TransactionResult result) override {
+ ALOGD("Got transaction result callback for txnId %" PRIu32 " with result %" PRId32, txnId,
+ result);
+ return Void();
+ }
+
+ virtual Return<void> handleHubEvent(V1_0::AsyncEventType evt) override {
+ ALOGD("Got hub event callback for event type %" PRIu32, evt);
+ return Void();
+ }
+
+ virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode) override {
+ ALOGD("Got app abort notification for appId 0x%" PRIx64 " with abort code 0x%" PRIx32,
+ appId, abortCode);
+ return Void();
+ }
+
+ virtual Return<void> handleAppsInfo(const hidl_vec<V1_0::HubAppInfo>& /*appInfo*/) override {
+ ALOGD("Got app info callback");
+ return Void();
+ }
+};
+
+} // namespace vts_utils
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/contexthub/common/vts/ContexthubHidlTestBase.h b/contexthub/common/vts/ContexthubHidlTestBase.h
new file mode 100644
index 0000000..ee5b7d3
--- /dev/null
+++ b/contexthub/common/vts/ContexthubHidlTestBase.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android/hardware/contexthub/1.0/IContexthubCallback.h>
+#include <gtest/gtest.h>
+
+#include <string>
+#include <tuple>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace vts_utils {
+
+// Base fixture for Context Hub HAL tests. Parameterized by service name and hub ID.
+template <class IContexthubVersion>
+class ContexthubHidlTestBase
+ : public ::testing::TestWithParam<std::tuple<std::string, std::string>> {
+ public:
+ virtual void SetUp() override { fetchHubApi(); }
+
+ void fetchHubApi() {
+ hubApi = IContexthubVersion::getService(std::get<0>(GetParam()));
+ ASSERT_NE(hubApi, nullptr);
+ }
+
+ uint32_t getHubId() { return std::stoi(std::get<1>(GetParam())); }
+
+ V1_0::Result registerCallback(sp<V1_0::IContexthubCallback> cb) {
+ return hubApi->registerCallback(getHubId(), cb);
+ }
+
+ sp<IContexthubVersion> hubApi;
+};
+
+} // namespace vts_utils
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/contexthub/common/vts/OWNERS b/contexthub/common/vts/OWNERS
new file mode 100644
index 0000000..161b2f0
--- /dev/null
+++ b/contexthub/common/vts/OWNERS
@@ -0,0 +1,8 @@
+#Context Hub team
+arthuri@google.com
+bduddie@google.com
+stange@google.com
+
+#VTS team
+dshi@google.com
+trong@google.com
diff --git a/contexthub/common/vts/VtsHalContexthubUtils.cpp b/contexthub/common/vts/VtsHalContexthubUtils.cpp
new file mode 100644
index 0000000..5033b41
--- /dev/null
+++ b/contexthub/common/vts/VtsHalContexthubUtils.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VtsHalContexthubUtils.h"
+
+#include <chrono>
+#include <future>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace vts_utils {
+
+using ::android::hardware::contexthub::V1_0::ContextHub;
+using ::android::hardware::contexthub::V1_0::IContexthub;
+
+// Synchronously queries IContexthub::getHubs() and returns the result
+hidl_vec<ContextHub> getHubsSync(IContexthub* hubApi) {
+ hidl_vec<ContextHub> hubList;
+ std::promise<void> barrier;
+
+ hubApi->getHubs([&hubList, &barrier](const hidl_vec<ContextHub>& hubs) {
+ hubList = hubs;
+ barrier.set_value();
+ });
+ barrier.get_future().wait_for(std::chrono::seconds(1));
+
+ return hubList;
+}
+
+} // namespace vts_utils
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/contexthub/common/vts/VtsHalContexthubUtils.h b/contexthub/common/vts/VtsHalContexthubUtils.h
new file mode 100644
index 0000000..8f9b694
--- /dev/null
+++ b/contexthub/common/vts/VtsHalContexthubUtils.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android/hardware/contexthub/1.0/IContexthub.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/ServiceManagement.h>
+#include <utils/StrongPointer.h>
+
+#include <chrono>
+#include <future>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace contexthub {
+namespace vts_utils {
+
+#define ASSERT_OK(result) ASSERT_EQ(result, ::android::hardware::contexthub::V1_0::Result::OK)
+#define EXPECT_OK(result) EXPECT_EQ(result, ::android::hardware::contexthub::V1_0::Result::OK)
+
+// Helper that does explicit conversion of an enum class to its underlying/base
+// type. Useful for stream output of enum values.
+template <typename EnumType>
+inline constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
+ return static_cast<typename std::underlying_type<EnumType>::type>(value);
+}
+
+// Synchronously queries IContexthub::getHubs() and returns the result
+hidl_vec<V1_0::ContextHub> getHubsSync(V1_0::IContexthub* hubApi);
+
+// Create a vector of tuples that include each IContexthub service paired with each hub ID it
+// exposes via getHubs(). Each tuple represents a test target that we should run the VTS suite
+// against.
+template <class IContexthubVersion>
+static std::vector<std::tuple<std::string, std::string>> getHalAndHubIdList() {
+ std::vector<std::tuple<std::string, std::string>> parameters;
+ std::vector<std::string> serviceNames =
+ ::android::hardware::getAllHalInstanceNames(IContexthubVersion::descriptor);
+ for (const std::string& serviceName : serviceNames) {
+ sp<IContexthubVersion> hubApi = IContexthubVersion::getService(serviceName);
+ if (hubApi != nullptr) {
+ hidl_vec<V1_0::ContextHub> hubs = getHubsSync(hubApi.get());
+ for (const V1_0::ContextHub& hub : hubs) {
+ parameters.push_back(std::make_tuple(serviceName, std::to_string(hub.hubId)));
+ }
+ }
+ }
+
+ return parameters;
+}
+
+} // namespace vts_utils
+} // namespace contexthub
+} // namespace hardware
+} // namespace android
diff --git a/current.txt b/current.txt
index 0a7bf74..e7face5 100644
--- a/current.txt
+++ b/current.txt
@@ -597,7 +597,7 @@
5f6d3097ba84cb63c430787123f4de1b31c11f90b531b98eae9a8623a5ae962a android.hardware.neuralnetworks@1.1::types
fb382e986c10b8fbb797a8546e8f9ea6d1107bfe6f3fb7e57f6bbbf1f807a906 android.hardware.neuralnetworks@1.2::IDevice
40e71cd693de5b832325c5d8f081f2ff20a7ba2b89d401cee5b4b3eb0e241681 android.hardware.neuralnetworks@1.2::IPreparedModel
-6c29d6fdd5445911df5456b3b84b949cdd59fca0c0b5507662f26a5cac0cf5e5 android.hardware.neuralnetworks@1.2::types
+00649d29680f2c47edf60000c3ae7ae906ba638f0616947147e3676a83cf36fa android.hardware.neuralnetworks@1.2::types
a785a57447a81e9c130eef6904c3a5c256076c6a04588c40620ebd6fa2660d77 android.hardware.radio@1.2::types
1a6e2bd289f22931c526b21916910f1d4c436b7acb9556e4243de4ce8e6cc2e4 android.hardware.soundtrigger@2.0::ISoundTriggerHwCallback
fd65298e1e09e0e3c781ab18305920d757dbe55a3b459ce17814ec5cf6dfee99 android.hardware.wifi@1.0::IWifiP2pIface
@@ -638,18 +638,21 @@
07d0a252b2d8fa35887908a996ba395cf392968395fc30afab791f46e0c22a52 android.hardware.boot@1.1::IBootControl
74049a402be913963edfdd80828a53736570e9d8124a1bf18166b6ed46a6b0ab android.hardware.boot@1.1::types
b8c63679e1a3874b356f3e691aecce1191d38f59063cf2ed2dce8a9d4cabf00e android.hardware.camera.device@3.6::ICameraDevice
-e88840e0558439cb54837514ddccd43877094951758f367e9c638084eb7455a6 android.hardware.camera.provider@2.6::ICameraProvider
+daad72a2f482d8a1f660d0e99ac1b78fedb414a4cfbe3fa56b2cd480e5d6f0cb android.hardware.camera.provider@2.6::ICameraProvider
8f8d9463508ff9cae88eb35c429fd0e2dbca0ca8f5de7fdf836cc0c4370becb6 android.hardware.camera.provider@2.6::ICameraProviderCallback
+a35d5151b48505f06a775b38c0e2e265f80a845d92802324c643565807f81c53 android.hardware.camera.device@3.6::types
c1aa508d00b66ed5feefea398fd5edf28fa651ac89773adad7dfda4e0a73a952 android.hardware.cas@1.2::ICas
9811f867def49b420d8c707f7e38d3bdd64f835244e1d2a5e9762ab9835672dc android.hardware.cas@1.2::ICasListener
f18695dd36ee205640b8326a17453858a7b4596653aaa6ef0016b0aef1bd4dac android.hardware.cas@1.2::IMediaCasService
4d85e814f94949dae4dc6cb82bbd7d6bb24ffafda6ddb2eac928d2a4fc2e21ce android.hardware.cas@1.2::types
+8351cc01eed4c0b4482d9572b5c7ddfd17874d8edb51d6761d348116fc91dd18 android.hardware.contexthub@1.1::IContexthub
+3581d0ba61663cdd45807494dcd697d01c074f27587df9140655f94346969cfe android.hardware.contexthub@1.1::types
66931c2506fbb5af61f20138cb05e0a09e7bf67d6964c231d27c648933bb33ec android.hardware.drm@1.3::ICryptoFactory
994d08ab27d613022c258a9ec48cece7adf2a305e92df5d76ef923e2c6665f64 android.hardware.drm@1.3::IDrmFactory
-446287268831f4ddfac4a51bb1c32ae1e48e47bccd535fccc2c4546d0e7c4013 android.hardware.dumpstate@1.1::types
+d9df99be0f59d8f33a9699fe316c67bfd11818aa69440bb1123ba43e717cea85 android.hardware.dumpstate@1.1::types
186bc152ae189ab48f3a761a44ddf5edd0d483073c5b6ca1f802f8b50488b754 android.hardware.dumpstate@1.1::IDumpstateDevice
769d346927a94fd40ee80a5a976d8d15cf022ef99c5900738f4a82f26c0ed229 android.hardware.gnss@2.1::types
-626db710bf917ecf551a0b0b1f25be10bf52758f43e0fc808b148b6aae2ef73e android.hardware.gnss@2.1::IGnss
+c319e68b03829958404402c2d9c682019678087d60495807c0a7444e0a6af981 android.hardware.gnss@2.1::IGnss
ba5ac712b2a656dc07c83ab4a7a2c2f3bee1bbcb752e8b8ffa9b672f3b5b0728 android.hardware.gnss@2.1::IGnssAntennaInfo
0bc3ed97cbc3f6abc89c68f4e9f4d124f9f723431997dc88c2186cf4d2ad47ee android.hardware.gnss@2.1::IGnssAntennaInfoCallback
3541d83adfeac16ee3e45d183a58dffe06012ccb5aa5bcd2e4f6eeae269f69cd android.hardware.gnss@2.1::IGnssCallback
@@ -657,10 +660,10 @@
7913a11206a577b12ade86a7cf3f95c2639cb514d086673f279bf99238c9917e android.hardware.gnss@2.1::IGnssMeasurement
0a16e5913e94d995cfcf959a1c6f10b0b8e9dfdb5f45ac6e7244711ddd740272 android.hardware.gnss@2.1::IGnssMeasurementCallback
6670e7780803a8c696c6391fda5589a334b1b37dc7be9393792ed35035413633 android.hardware.gnss.measurement_corrections@1.1::IMeasurementCorrections
-a28d6c29a7e36976acffb018208e65b3496d9152d57d864038556cdd83b35744 android.hardware.gnss.measurement_corrections@1.1::types
+956c1576ca0d6f11b42980ef59052062836b6763fe973af6cb709da50787f710 android.hardware.gnss.measurement_corrections@1.1::types
ce8dbe76eb9ee94b46ef98f725be992e760a5751073d4f4912484026541371f3 android.hardware.health@2.1::IHealth
26f04510a0b57aba5167c5c0a7c2f077c2acbb98b81902a072517829fd9fd67f android.hardware.health@2.1::IHealthInfoCallback
-3a4e7462a12589bd219599de59663d0ba9915313f45150774780d09f4e114f74 android.hardware.health@2.1::types
+e2f8bc1868fd4a3fd587c172773ea5a8c2f5a3deaf7958394102ca455252b255 android.hardware.health@2.1::types
0589e410f519e36514e7ece18f283f022df0f70efd2c12821d822f67f74aba98 android.hardware.identity@1.0::types
bbeee9604128ede83ee755b67e73b5ad29e6e1dbac9ec41fea6ffe2745b0c50a android.hardware.identity@1.0::IIdentityCredential
96ce8aad80f4c476f25261f790d357c117e79e18474c7dadd850dac704bbe65e android.hardware.identity@1.0::IIdentityCredentialStore
@@ -671,12 +674,25 @@
df9c79c4fdde2821550c6d5c3d07f5ec0adfb1b702561ce543c906ddef698703 android.hardware.media.c2@1.1::IComponent
a3eddd9bbdc87e8c22764070037dd1154f1cf006e6fba93364c4f85d4c134a19 android.hardware.media.c2@1.1::IComponentStore
65c16331e57f6dd68b3971f06f78fe9e3209afb60630c31705aa355f9a52bf0d android.hardware.neuralnetworks@1.3::IBuffer
-9db064ee44268a876be0367ff771e618362d39ec603b6ecab17e1575725fcd87 android.hardware.neuralnetworks@1.3::IDevice
-4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback
-2fa3679ad7c94b5e88724adcd560c561041068a4ca565c63830e68101988746a android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
-237b23b126a66f3432658020fed78cdd06ba6297459436fe6bae0ba753370833 android.hardware.neuralnetworks@1.3::IPreparedModel
-0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback
-5e2a14b40dc11da9d478185838f4401b652739922d14cecea0a0ce4c1359fe21 android.hardware.neuralnetworks@1.3::types
+278817920bfd5292a7713f97f1832cca53de3de640f7670e413d97c6e7fd581c android.hardware.neuralnetworks@1.3::IDevice
+127ba11efb8220dc3aec9a8f441b59eaf1c68d7f03f577833e1824de75a36b17 android.hardware.neuralnetworks@1.3::IExecutionCallback
+6e904be0ddca5ae1de8eba020e6c38ed935ea7d80cd08f47787f137a0ca58555 android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
+2b0b10d2ea7a18a4048cd0eb83d35c19a817aeee95f65807fc31f4ef21381397 android.hardware.neuralnetworks@1.3::IPreparedModel
+eee3430cc86c97c7b407495863d8fb61da6f1a64b7721e77b9b4909b11b174e9 android.hardware.neuralnetworks@1.3::IPreparedModelCallback
+c9320b04ec302624985180a02d591bea5e435601fc411a6cabb58878e4e1ad68 android.hardware.neuralnetworks@1.3::types
+b335c3c732c799b299fa61c6de6260ab4d20cbd0ec21acd6db14d8156c745d0b android.hardware.tv.tuner@1.0::types
+adab52e647d1a1ccfbdabdfc9c73352f8e834b61322e505bc6e3d3a0d3acc259 android.hardware.tv.tuner@1.0::IDemux
+548e1a16fc4f779346e14968a63cd6f160e1e2c8b8c99256b2bac24a24b52a9a android.hardware.tv.tuner@1.0::IDescrambler
+b84597d59f0f1d03c9997d60eb15280f3950c287d46016240d89859789db4d47 android.hardware.tv.tuner@1.0::IDvr
+e512a8b4ddef0d0c29d9f68101363dca7616033a24bab86bfca0829661e7484c android.hardware.tv.tuner@1.0::IDvrCallback
+c113b5bed45b3a67ee3f15de1482742a8fd8d96e45ec72e628ee9be6301d51d7 android.hardware.tv.tuner@1.0::IFilter
+8bbc6bde44573edf800cda2b457852175786a3c73f36666bfb2d3afdce3d0dfa android.hardware.tv.tuner@1.0::IFilterCallback
+ccd985e820ed92a5cb55f524b3549462483d21824ca2df0276f5bc2f42878ea3 android.hardware.tv.tuner@1.0::IFrontend
+818587bab10f2534b5a1ef70ed9bae99019f7d453b2fcb2dda01c6db91c3a805 android.hardware.tv.tuner@1.0::IFrontendCallback
+83de3964a800a0e707731adcbcbfbaa56868549233e4c8dcccc8969fab727d38 android.hardware.tv.tuner@1.0::ILnb
+b2310785bdb55f97bbbb2176e2ee73ed8d2a7ce5895bd20c997b90c5f2868ad8 android.hardware.tv.tuner@1.0::ILnbCallback
+4788787e662293d526ff7789fc24e82533e7f6ff99a967ebc3e3ec6b17628796 android.hardware.tv.tuner@1.0::ITimeFilter
+c350c7783843e0c7cf30f90c918770b0d3c09fc0fe5e532e2f2e7210fcfe71c9 android.hardware.tv.tuner@1.0::ITuner
3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
c67aaf26a7a40d14ea61e70e20afacbd0bb906df1704d585ac8599fbb69dd44b android.hardware.wifi.hostapd@1.2::IHostapd
2b5a7ea572b736030c64a3b4043af244425477c4672301780fe15aba5ed393d9 android.hardware.wifi.hostapd@1.2::types
@@ -689,10 +705,13 @@
7fefa2cc5b3b3be10b5cff5c5dc195385f491d4bf23ca65f9c6b3c30c8753a33 android.hardware.radio@1.5::IRadio
e96ae1c3a9c0689002ec2318e9c587f4f607c16a75a3cd38788b77eb91072021 android.hardware.radio@1.5::IRadioIndication
829d3827eeb5a8f563e80fe627419b3231012fc02bc2e79782ec5e9ad9f799a4 android.hardware.radio@1.5::IRadioResponse
-dcc8872337f0135e81970e1d8d5fd7139160dc80e9be76f0ae05290fa7e472b8 android.hardware.radio.config@1.3::types
+4c4ce691df02faa28c0729e2a033ec464e1d72699be8bcde4dfb141313dbeba8 android.hardware.radio.config@1.3::types
a2977755bc5f1ef47f04b7f2400632efda6218e1515dba847da487145cfabc4f android.hardware.radio.config@1.3::IRadioConfig
742360c775313438b0f82256eac62fb5bbc76a6ae6f388573f3aa142fb2c1eea android.hardware.radio.config@1.3::IRadioConfigIndication
0006ab8e8b0910cbd3bbb08d5f17d5fac7d65a2bdad5f2334e4851db9d1e6fa8 android.hardware.radio.config@1.3::IRadioConfigResponse
+3ca6616381080bdd6c08141ad12775a94ae868c58b02b1274ae3326f7de724ab android.hardware.sensors@2.1::ISensors
+3d4141c6373cd9ca02fe221a7d12343840de2255d032c38248fe8e35816b58b2 android.hardware.sensors@2.1::ISensorsCallback
+8051cc50fc90ed447f058a8b15d81f35a65f1bd9004b1de4f127edeb89b47978 android.hardware.sensors@2.1::types
4a6517ea4ad807855428b0101d8e1a486497bd88ab4300ba3b2be43d46d32580 android.hardware.soundtrigger@2.3::types
-12d7533ff0754f45bf59ab300799074570a99a676545652c2c23abc73cb4515d android.hardware.soundtrigger@2.3::ISoundTriggerHw
+b37f78e3fdc79af8b32a545b2b426f1fd1355b359d9e7835f3bf1ed0aa4518d8 android.hardware.soundtrigger@2.3::ISoundTriggerHw
7746fda1fbf9c7c132bae701cc5a161309e4f5e7f3e8065811045975ee86196d android.hardware.usb.gadget@1.1::IUsbGadget
diff --git a/dumpstate/1.1/types.hal b/dumpstate/1.1/types.hal
index f5cbade..c522f7c 100644
--- a/dumpstate/1.1/types.hal
+++ b/dumpstate/1.1/types.hal
@@ -55,6 +55,13 @@
* This mode MUST be supported if the dumpstate HAL is implemented.
*/
DEFAULT = 6,
+ /**
+ * Takes a report in protobuf.
+ *
+ * The content, if implemented, must be a binary protobuf message written to the first file
+ * descriptor of the native handle. The protobuf schema shall be defined by the vendor.
+ */
+ PROTO = 7,
};
/**
diff --git a/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
index 51dce5e..cbdd87b 100644
--- a/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
+++ b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
@@ -92,7 +92,8 @@
TEST_FOR_DUMPSTATE_MODE(name, body, WEAR); \
TEST_FOR_DUMPSTATE_MODE(name, body, CONNECTIVITY); \
TEST_FOR_DUMPSTATE_MODE(name, body, WIFI); \
- TEST_FOR_DUMPSTATE_MODE(name, body, DEFAULT);
+ TEST_FOR_DUMPSTATE_MODE(name, body, DEFAULT); \
+ TEST_FOR_DUMPSTATE_MODE(name, body, PROTO);
constexpr uint64_t kDefaultTimeoutMillis = 30 * 1000; // 30 seconds
diff --git a/gnss/2.1/IGnss.hal b/gnss/2.1/IGnss.hal
index e4da507..a880b3f 100644
--- a/gnss/2.1/IGnss.hal
+++ b/gnss/2.1/IGnss.hal
@@ -45,9 +45,9 @@
/**
* This method returns the IGnssMeasurement interface.
*
- * At least one of getExtensionGnssMeasurement(), getExtensionGnssMeasurement_1_1(),
+ * getExtensionGnssMeasurement(), getExtensionGnssMeasurement_1_1(),
* getExtensionGnssMeasurement_2_0(), and getExtensionGnssMeasurement_2_1() methods must return
- * a non-null handle, and the other methods must return nullptr.
+ * non-null. They can all return the same, latest version of IGnssMeasurement.
*
* @return gnssMeasurementIface Handle to the IGnssMeasurement interface.
*/
@@ -56,9 +56,9 @@
/**
* This method returns the IGnssConfiguration interface.
*
- * At least one of getExtensionGnssConfiguration(), getExtensionGnssConfiguration_1_1(),
+ * getExtensionGnssConfiguration(), getExtensionGnssConfiguration_1_1(),
* getExtensionGnssConfiguration_2_0(), and getExtensionGnssConfiguration_2_1() methods must
- * return a non-null handle, and the other methods must return nullptr.
+ * return non-null. They can all return the same, latest version of IGnssConfiguration.
*
* @return gnssConfigurationIface Handle to the IGnssConfiguration interface.
*/
diff --git a/gnss/2.1/default/GnssMeasurementCorrections.cpp b/gnss/2.1/default/GnssMeasurementCorrections.cpp
index 9dedbf6..accf62b 100644
--- a/gnss/2.1/default/GnssMeasurementCorrections.cpp
+++ b/gnss/2.1/default/GnssMeasurementCorrections.cpp
@@ -82,19 +82,20 @@
static_cast<int>(corrections.v1_0.satCorrections.size()),
corrections.hasEnvironmentBearing, corrections.environmentBearingDegrees,
corrections.environmentBearingUncertaintyDegrees);
- for (auto singleSatCorrection : corrections.v1_0.satCorrections) {
+ for (auto singleSatCorrection : corrections.satCorrections) {
ALOGD("singleSatCorrection = flags: %d, constellation: %d, svid: %d, cfHz: %f, probLos: %f,"
" epl: %f, eplUnc: %f",
- static_cast<int>(singleSatCorrection.singleSatCorrectionFlags),
+ static_cast<int>(singleSatCorrection.v1_0.singleSatCorrectionFlags),
static_cast<int>(singleSatCorrection.constellation),
- static_cast<int>(singleSatCorrection.svid), singleSatCorrection.carrierFrequencyHz,
- singleSatCorrection.probSatIsLos, singleSatCorrection.excessPathLengthMeters,
- singleSatCorrection.excessPathLengthUncertaintyMeters);
+ static_cast<int>(singleSatCorrection.v1_0.svid),
+ singleSatCorrection.v1_0.carrierFrequencyHz, singleSatCorrection.v1_0.probSatIsLos,
+ singleSatCorrection.v1_0.excessPathLengthMeters,
+ singleSatCorrection.v1_0.excessPathLengthUncertaintyMeters);
ALOGD("reflecting plane = lat: %f, lng: %f, alt: %f, azm: %f",
- singleSatCorrection.reflectingPlane.latitudeDegrees,
- singleSatCorrection.reflectingPlane.longitudeDegrees,
- singleSatCorrection.reflectingPlane.altitudeMeters,
- singleSatCorrection.reflectingPlane.azimuthDegrees);
+ singleSatCorrection.v1_0.reflectingPlane.latitudeDegrees,
+ singleSatCorrection.v1_0.reflectingPlane.longitudeDegrees,
+ singleSatCorrection.v1_0.reflectingPlane.altitudeMeters,
+ singleSatCorrection.v1_0.reflectingPlane.azimuthDegrees);
}
return true;
diff --git a/gnss/common/utils/vts/Utils.cpp b/gnss/common/utils/vts/Utils.cpp
index b6c3f5e..4b5a50f 100644
--- a/gnss/common/utils/vts/Utils.cpp
+++ b/gnss/common/utils/vts/Utils.cpp
@@ -22,7 +22,9 @@
namespace gnss {
namespace common {
-using V1_0::GnssConstellationType;
+using GnssConstellationType_V1_0 = V1_0::GnssConstellationType;
+using GnssConstellationType_V2_0 = V2_0::GnssConstellationType;
+
using V1_0::GnssLocationFlags;
void Utils::checkLocation(const GnssLocation& location, bool check_speed,
@@ -100,12 +102,12 @@
.azimuthDegrees = 203.0,
};
- SingleSatCorrection singleSatCorrection1 = {
+ SingleSatCorrection_V1_0 singleSatCorrection1 = {
.singleSatCorrectionFlags = GnssSingleSatCorrectionFlags::HAS_SAT_IS_LOS_PROBABILITY |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH_UNC |
GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE,
- .constellation = GnssConstellationType::GPS,
+ .constellation = GnssConstellationType_V1_0::GPS,
.svid = 12,
.carrierFrequencyHz = 1.59975e+09,
.probSatIsLos = 0.50001,
@@ -113,11 +115,11 @@
.excessPathLengthUncertaintyMeters = 25.5,
.reflectingPlane = reflectingPlane,
};
- SingleSatCorrection singleSatCorrection2 = {
+ SingleSatCorrection_V1_0 singleSatCorrection2 = {
.singleSatCorrectionFlags = GnssSingleSatCorrectionFlags::HAS_SAT_IS_LOS_PROBABILITY |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH_UNC,
- .constellation = GnssConstellationType::GPS,
+ .constellation = GnssConstellationType_V1_0::GPS,
.svid = 9,
.carrierFrequencyHz = 1.59975e+09,
.probSatIsLos = 0.873,
@@ -125,8 +127,8 @@
.excessPathLengthUncertaintyMeters = 10.0,
};
- hidl_vec<SingleSatCorrection> singleSatCorrections = {singleSatCorrection1,
- singleSatCorrection2};
+ hidl_vec<SingleSatCorrection_V1_0> singleSatCorrections = {singleSatCorrection1,
+ singleSatCorrection2};
MeasurementCorrections_1_0 mockCorrections = {
.latitudeDegrees = 37.4219999,
.longitudeDegrees = -122.0840575,
@@ -142,11 +144,27 @@
const MeasurementCorrections_1_1 Utils::getMockMeasurementCorrections_1_1() {
MeasurementCorrections_1_0 mockCorrections_1_0 = getMockMeasurementCorrections();
+ SingleSatCorrection_V1_1 singleSatCorrection1 = {
+ .v1_0 = mockCorrections_1_0.satCorrections[0],
+ .constellation = GnssConstellationType_V2_0::IRNSS,
+ };
+ SingleSatCorrection_V1_1 singleSatCorrection2 = {
+ .v1_0 = mockCorrections_1_0.satCorrections[1],
+ .constellation = GnssConstellationType_V2_0::IRNSS,
+ };
+
+ mockCorrections_1_0.satCorrections[0].constellation = GnssConstellationType_V1_0::UNKNOWN;
+ mockCorrections_1_0.satCorrections[1].constellation = GnssConstellationType_V1_0::UNKNOWN;
+
+ hidl_vec<SingleSatCorrection_V1_1> singleSatCorrections = {singleSatCorrection1,
+ singleSatCorrection2};
+
MeasurementCorrections_1_1 mockCorrections_1_1 = {
.v1_0 = mockCorrections_1_0,
.hasEnvironmentBearing = true,
.environmentBearingDegrees = 45.0,
.environmentBearingUncertaintyDegrees = 4.0,
+ .satCorrections = singleSatCorrections,
};
return mockCorrections_1_1;
}
diff --git a/gnss/common/utils/vts/include/Utils.h b/gnss/common/utils/vts/include/Utils.h
index 781ad42..c3cdd18 100644
--- a/gnss/common/utils/vts/include/Utils.h
+++ b/gnss/common/utils/vts/include/Utils.h
@@ -29,6 +29,11 @@
using MeasurementCorrections_1_1 =
android::hardware::gnss::measurement_corrections::V1_1::MeasurementCorrections;
+using SingleSatCorrection_V1_0 =
+ android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection;
+using SingleSatCorrection_V1_1 =
+ android::hardware::gnss::measurement_corrections::V1_1::SingleSatCorrection;
+
namespace android {
namespace hardware {
namespace gnss {
diff --git a/gnss/measurement_corrections/1.1/Android.bp b/gnss/measurement_corrections/1.1/Android.bp
index 1d69f20..d279af6 100644
--- a/gnss/measurement_corrections/1.1/Android.bp
+++ b/gnss/measurement_corrections/1.1/Android.bp
@@ -12,6 +12,7 @@
],
interfaces: [
"android.hardware.gnss.measurement_corrections@1.0",
+ "android.hardware.gnss@2.0",
"android.hardware.gnss@1.0",
"android.hidl.base@1.0",
],
diff --git a/gnss/measurement_corrections/1.1/types.hal b/gnss/measurement_corrections/1.1/types.hal
index f945c57..e98be13 100644
--- a/gnss/measurement_corrections/1.1/types.hal
+++ b/gnss/measurement_corrections/1.1/types.hal
@@ -17,11 +17,14 @@
package android.hardware.gnss.measurement_corrections@1.1;
import @1.0::MeasurementCorrections;
+import @1.0::SingleSatCorrection;
+import android.hardware.gnss@2.0::GnssConstellationType;
/**
* A struct containing a set of measurement corrections for all used GNSS satellites at the location
* specified by latitudeDegrees, longitudeDegrees, altitudeMeters and at the time of week specified
- * toaGpsNanosecondsOfWeek
+ * toaGpsNanosecondsOfWeek. The v1_0.satCorrections field is deprecated and is no longer used by
+ * framework.
*/
struct MeasurementCorrections {
@1.0::MeasurementCorrections v1_0;
@@ -64,4 +67,25 @@
* before calling this method. The value is undefined if hasEnvironmentBearing is false.
*/
float environmentBearingUncertaintyDegrees;
+
+ /**
+ * A set of SingleSatCorrection each containing measurement corrections for a satellite in view
+ */
+ vec<SingleSatCorrection> satCorrections;
+};
+
+/**
+ * A struct with measurement corrections for a single visible satellites, updating the
+ * GnssConstellationType to 2.0, which supports IRNSS. The v1_0.constellation is deprecated and is
+ * no longer used by framework.
+ *
+ * The bit mask singleSatCorrectionFlags indicates which correction values are valid in the struct
+ */
+struct SingleSatCorrection {
+ @1.0::SingleSatCorrection v1_0;
+
+ /**
+ * Defines the constellation of the given satellite.
+ */
+ GnssConstellationType constellation;
};
diff --git a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
index a23d72c..c78c358 100644
--- a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
+++ b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
@@ -33,6 +33,10 @@
mRenderEngine = renderengine::RenderEngine::create(args);
}
+TestRenderEngine::~TestRenderEngine() {
+ mRenderEngine.release();
+}
+
void TestRenderEngine::setRenderLayers(std::vector<std::shared_ptr<TestLayer>> layers) {
sort(layers.begin(), layers.end(),
[](const std::shared_ptr<TestLayer>& lhs, const std::shared_ptr<TestLayer>& rhs) -> bool {
diff --git a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h
index 4eb4bb7..f2d5f19 100644
--- a/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h
+++ b/graphics/composer/2.2/utils/vts/include/composer-vts/2.2/RenderEngineVts.h
@@ -41,7 +41,7 @@
static constexpr uint32_t sMaxFrameBufferAcquireBuffers = 2;
TestRenderEngine(const RenderEngineCreationArgs& args);
- ~TestRenderEngine() = default;
+ ~TestRenderEngine();
void setRenderLayers(std::vector<std::shared_ptr<TestLayer>> layers);
void initGraphicBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint64_t usage);
diff --git a/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h b/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h
index 8540068..13df3bc 100644
--- a/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h
+++ b/graphics/mapper/2.1/utils/passthrough/include/mapper-passthrough/2.1/Gralloc0Hal.h
@@ -49,7 +49,12 @@
mModule, bufferHandle, descriptorInfo.width, descriptorInfo.height,
static_cast<int32_t>(descriptorInfo.format),
static_cast<uint64_t>(descriptorInfo.usage), stride);
- return static_cast<Error>(ret);
+ if (ret == -EINVAL) {
+ return Error::BAD_BUFFER;
+ } else if (ret < 0) {
+ return Error::BAD_VALUE;
+ }
+ return Error::NONE;
}
Error getTransportSize(const native_handle_t* bufferHandle, uint32_t* outNumFds,
uint32_t* outNumInts) override {
diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
index 58b7ed3..1416fcc 100644
--- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
+++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
@@ -1198,65 +1198,6 @@
}
/**
- * Test IMapper::set(Usage) remove flag
- */
-TEST_P(GraphicsMapperHidlTest, SetUsageRemoveBit) {
- uint64_t usage = static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN);
- hidl_vec<uint8_t> vec;
- ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec));
-
- testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Usage, vec,
- [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
- uint64_t realUsage = 0;
- ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage));
- EXPECT_EQ(usage, realUsage);
- });
-}
-/**
- * Test IMapper::set(Usage) add flag
- */
-TEST_P(GraphicsMapperHidlTest, SetUsageAddBit) {
- uint64_t usage = mDummyDescriptorInfo.usage | static_cast<uint64_t>(BufferUsage::GPU_TEXTURE);
- hidl_vec<uint8_t> vec;
- ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec));
-
- testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Usage, vec,
- [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
- uint64_t realUsage = 0;
- ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage));
- EXPECT_EQ(usage, realUsage);
- });
-}
-
-/**
- * Test IMapper::set(Usage) to test protected content
- */
-TEST_P(GraphicsMapperHidlTest, SetUsageProtected) {
- const native_handle_t* bufferHandle = nullptr;
- auto info = mDummyDescriptorInfo;
- info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY;
-
- bufferHandle = mGralloc->allocate(info, true, true);
- if (bufferHandle) {
- GTEST_SUCCEED() << "unable to allocate protected content";
- return;
- }
-
- uint64_t usage = static_cast<uint64_t>(BufferUsage::COMPOSER_OVERLAY);
- hidl_vec<uint8_t> vec;
- ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec));
-
- Error err = mGralloc->set(bufferHandle, gralloc4::MetadataType_Usage, vec);
- ASSERT_EQ(err, Error::UNSUPPORTED);
- vec.resize(0);
-
- uint64_t realUsage = 0;
- ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, gralloc4::MetadataType_Usage, &vec));
- ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage));
- EXPECT_EQ(info.usage, realUsage);
-}
-
-/**
* Test IMapper::set(AllocationSize)
*/
TEST_P(GraphicsMapperHidlTest, SetAllocationSize) {
diff --git a/health/2.1/types.hal b/health/2.1/types.hal
index be1eaf8..d775491 100644
--- a/health/2.1/types.hal
+++ b/health/2.1/types.hal
@@ -19,6 +19,10 @@
import @1.0::HealthConfig;
import @2.0::HealthInfo;
+enum Constants : int64_t {
+ BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED = -1,
+};
+
/**
* Battery capacity level. This enum provides additional information along side
* with the battery capacity.
@@ -27,11 +31,17 @@
*/
enum BatteryCapacityLevel : int32_t {
/**
+ * Battery capacity level is unsupported.
+ * Battery capacity level must be set to this value if and only if the
+ * implementation is unsupported.
+ */
+ UNSUPPORTED = -1,
+ /**
* Battery capacity level is unknown.
* Battery capacity level must be set to this value if and only if battery
- * is not present.
+ * is not present or the battery capacity level is unknown/uninitialized.
*/
- UNKNOWN = 0,
+ UNKNOWN,
/**
* Battery is at critical level. The Android framework must schedule a
* shutdown when it sees this value from the HAL.
@@ -78,6 +88,8 @@
/**
* Estimated time to fully charge the device (in seconds).
+ * Value must be BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED if and
+ * only if the implementation is unsupported.
* Value must be 0 if and only if batteryCapacityLevel is FULL or UNKNOWN.
* Otherwise, value must be positive.
*/
diff --git a/identity/1.0/Android.bp b/identity/1.0/Android.bp
deleted file mode 100644
index e0a6332..0000000
--- a/identity/1.0/Android.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-// This file is autogenerated by hidl-gen -Landroidbp.
-
-hidl_interface {
- name: "android.hardware.identity@1.0",
- root: "android.hardware",
- vndk: {
- enabled: true,
- },
- srcs: [
- "types.hal",
- "IIdentityCredential.hal",
- "IIdentityCredentialStore.hal",
- "IWritableIdentityCredential.hal",
- ],
- interfaces: [
- "android.hardware.keymaster@4.0",
- "android.hidl.base@1.0",
- ],
- gen_java: false,
-}
diff --git a/identity/1.0/default/Android.bp b/identity/1.0/default/Android.bp
deleted file mode 100644
index d2b2966..0000000
--- a/identity/1.0/default/Android.bp
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_binary {
- name: "android.hardware.identity@1.0-service.example",
- init_rc: ["android.hardware.identity@1.0-service.example.rc"],
- vendor: true,
- relative_install_path: "hw",
- cflags: [
- "-Wall",
- "-Wextra",
- ],
- srcs: [
- "service.cpp",
- "IdentityCredential.cpp",
- "IdentityCredentialStore.cpp",
- "WritableIdentityCredential.cpp",
- ],
- shared_libs: [
- "android.hardware.identity@1.0",
- "android.hardware.identity-support-lib",
- "android.hardware.keymaster@4.0",
- "libcppbor",
- "libcrypto",
- "libbase",
- "libhidlbase",
- "liblog",
- "libutils",
- ],
-}
diff --git a/identity/1.0/default/IdentityCredential.cpp b/identity/1.0/default/IdentityCredential.cpp
deleted file mode 100644
index b0a5e56..0000000
--- a/identity/1.0/default/IdentityCredential.cpp
+++ /dev/null
@@ -1,773 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IdentityCredential"
-
-#include "IdentityCredential.h"
-#include "IdentityCredentialStore.h"
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <string.h>
-
-#include <android-base/logging.h>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::android::hardware::keymaster::V4_0::Timestamp;
-using ::std::optional;
-
-Return<void> IdentityCredential::deleteCredential(deleteCredential_cb _hidl_cb) {
- cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
- vector<uint8_t> proofOfDeletion = array.encode();
-
- optional<vector<uint8_t>> proofOfDeletionSignature =
- support::coseSignEcDsa(credentialPrivKey_,
- proofOfDeletion, // payload
- {}, // additionalData
- {}); // certificateChain
- if (!proofOfDeletionSignature) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {});
- return Void();
- }
-
- _hidl_cb(support::resultOK(), proofOfDeletionSignature.value());
- return Void();
-}
-
-Return<void> IdentityCredential::createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) {
- optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
- if (!keyPair) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating ephemeral key pair"), {});
- return Void();
- }
-
- // Stash public key of this key-pair for later check in startRetrieval().
- optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPair.value());
- if (!publicKey) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error getting public part of ephemeral key pair"),
- {});
- return Void();
- }
- ephemeralPublicKey_ = publicKey.value();
-
- _hidl_cb(support::resultOK(), keyPair.value());
- return Void();
-}
-
-Return<void> IdentityCredential::setReaderEphemeralPublicKey(
- const hidl_vec<uint8_t>& publicKey, setReaderEphemeralPublicKey_cb _hidl_cb) {
- readerPublicKey_ = publicKey;
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-ResultCode IdentityCredential::initialize() {
- auto [item, _, message] = cppbor::parse(credentialData_);
- if (item == nullptr) {
- LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
- return ResultCode::INVALID_DATA;
- }
-
- const cppbor::Array* arrayItem = item->asArray();
- if (arrayItem == nullptr || arrayItem->size() != 3) {
- LOG(ERROR) << "CredentialData is not an array with three elements";
- return ResultCode::INVALID_DATA;
- }
-
- const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
- const cppbor::Bool* testCredentialItem =
- ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
- : nullptr);
- const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
- if (docTypeItem == nullptr || testCredentialItem == nullptr ||
- encryptedCredentialKeysItem == nullptr) {
- LOG(ERROR) << "CredentialData unexpected item types";
- return ResultCode::INVALID_DATA;
- }
-
- docType_ = docTypeItem->value();
- testCredential_ = testCredentialItem->value();
-
- vector<uint8_t> hardwareBoundKey;
- if (testCredential_) {
- hardwareBoundKey = support::getTestHardwareBoundKey();
- } else {
- hardwareBoundKey = support::getHardwareBoundKey();
- }
-
- const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
- const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> decryptedCredentialKeys =
- support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
- if (!decryptedCredentialKeys) {
- LOG(ERROR) << "Error decrypting CredentialKeys";
- return ResultCode::INVALID_DATA;
- }
-
- auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
- if (dckItem == nullptr) {
- LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
- return ResultCode::INVALID_DATA;
- }
- const cppbor::Array* dckArrayItem = dckItem->asArray();
- if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
- LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
- return ResultCode::INVALID_DATA;
- }
- const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
- const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
- if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
- LOG(ERROR) << "CredentialKeys unexpected item types";
- return ResultCode::INVALID_DATA;
- }
- storageKey_ = storageKeyItem->value();
- credentialPrivKey_ = credentialPrivKeyItem->value();
-
- return ResultCode::OK;
-}
-
-Return<void> IdentityCredential::createAuthChallenge(createAuthChallenge_cb _hidl_cb) {
- uint64_t challenge = 0;
- while (challenge == 0) {
- optional<vector<uint8_t>> bytes = support::getRandom(8);
- if (!bytes) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting random data for challenge"),
- 0);
- return Void();
- }
-
- challenge = 0;
- for (size_t n = 0; n < bytes.value().size(); n++) {
- challenge |= ((bytes.value())[n] << (n * 8));
- }
- }
-
- authChallenge_ = challenge;
- _hidl_cb(support::resultOK(), challenge);
- return Void();
-}
-
-// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
-// ahead of time.
-bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& readerCertificateChain) {
- optional<vector<uint8_t>> acpPubKey =
- support::certificateChainGetTopMostKey(profile.readerCertificate);
- if (!acpPubKey) {
- LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
- return false;
- }
-
- optional<vector<vector<uint8_t>>> certificatesInChain =
- support::certificateChainSplit(readerCertificateChain);
- if (!certificatesInChain) {
- LOG(ERROR) << "Error splitting readerCertificateChain";
- return false;
- }
- for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
- optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
- if (!certPubKey) {
- LOG(ERROR)
- << "Error extracting public key from certificate in chain presented by reader";
- return false;
- }
- if (acpPubKey.value() == certPubKey.value()) {
- return true;
- }
- }
- return false;
-}
-
-Timestamp clockGetTime() {
- struct timespec time;
- clock_gettime(CLOCK_MONOTONIC, &time);
- return time.tv_sec * 1000 + time.tv_nsec / 1000000;
-}
-
-bool checkUserAuthentication(const SecureAccessControlProfile& profile,
- const HardwareAuthToken& authToken, uint64_t authChallenge) {
- if (profile.secureUserId != authToken.userId) {
- LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
- << ") differs from userId in authToken (" << authToken.userId << ")";
- return false;
- }
-
- if (profile.timeoutMillis == 0) {
- if (authToken.challenge == 0) {
- LOG(ERROR) << "No challenge in authToken";
- return false;
- }
-
- if (authToken.challenge != authChallenge) {
- LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
- return false;
- }
- return true;
- }
-
- // Note that the Epoch for timestamps in HardwareAuthToken is at the
- // discretion of the vendor:
- //
- // "[...] since some starting point (generally the most recent device
- // boot) which all of the applications within one secure environment
- // must agree upon."
- //
- // Therefore, if this software implementation is used on a device which isn't
- // the emulator then the assumption that the epoch is the same as used in
- // clockGetTime above will not hold. This is OK as this software
- // implementation should never be used on a real device.
- //
- Timestamp now = clockGetTime();
- if (authToken.timestamp > now) {
- LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp
- << ") is in the future (now: " << now << ")";
- return false;
- }
- if (now > authToken.timestamp + profile.timeoutMillis) {
- LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp << " + "
- << profile.timeoutMillis << " = "
- << (authToken.timestamp + profile.timeoutMillis)
- << ") is in the past (now: " << now << ")";
- return false;
- }
-
- return true;
-}
-
-Return<void> IdentityCredential::startRetrieval(
- const hidl_vec<SecureAccessControlProfile>& accessControlProfiles,
- const HardwareAuthToken& authToken, const hidl_vec<uint8_t>& itemsRequest,
- const hidl_vec<uint8_t>& sessionTranscript, const hidl_vec<uint8_t>& readerSignature,
- const hidl_vec<uint16_t>& requestCounts, startRetrieval_cb _hidl_cb) {
- if (sessionTranscript.size() > 0) {
- auto [item, _, message] = cppbor::parse(sessionTranscript);
- if (item == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "SessionTranscript contains invalid CBOR"));
- return Void();
- }
- sessionTranscriptItem_ = std::move(item);
- }
- if (numStartRetrievalCalls_ > 0) {
- if (sessionTranscript_ != vector<uint8_t>(sessionTranscript)) {
- _hidl_cb(support::result(
- ResultCode::SESSION_TRANSCRIPT_MISMATCH,
- "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
- return Void();
- }
- }
- sessionTranscript_ = sessionTranscript;
-
- // If there is a signature, validate that it was made with the top-most key in the
- // certificate chain embedded in the COSE_Sign1 structure.
- optional<vector<uint8_t>> readerCertificateChain;
- if (readerSignature.size() > 0) {
- readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
- if (!readerCertificateChain) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "Unable to get reader certificate chain from COSE_Sign1"));
- return Void();
- }
-
- if (!support::certificateChainValidate(readerCertificateChain.value())) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "Error validating reader certificate chain"));
- return Void();
- }
-
- optional<vector<uint8_t>> readerPublicKey =
- support::certificateChainGetTopMostKey(readerCertificateChain.value());
- if (!readerPublicKey) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "Unable to get public key from reader certificate chain"));
- return Void();
- }
-
- const vector<uint8_t>& itemsRequestBytes = itemsRequest;
- vector<uint8_t> dataThatWasSigned = cppbor::Array()
- .add("ReaderAuthentication")
- .add(sessionTranscriptItem_->clone())
- .add(cppbor::Semantic(24, itemsRequestBytes))
- .encode();
- if (!support::coseCheckEcDsaSignature(readerSignature,
- dataThatWasSigned, // detached content
- readerPublicKey.value())) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "readerSignature check failed"));
- return Void();
- }
- }
-
- // Here's where we would validate the passed-in |authToken| to assure ourselves
- // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
- //
- // However this involves calculating the MAC. However this requires access
- // to the key needed to a pre-shared key which we don't have...
- //
-
- // To prevent replay-attacks, we check that the public part of the ephemeral
- // key we previously created, is present in the DeviceEngagement part of
- // SessionTranscript as a COSE_Key, in uncompressed form.
- //
- // We do this by just searching for the X and Y coordinates.
- if (sessionTranscript.size() > 0) {
- const cppbor::Array* array = sessionTranscriptItem_->asArray();
- if (array == nullptr || array->size() != 2) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "SessionTranscript is not an array with two items"));
- return Void();
- }
- const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic();
- if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "First item in SessionTranscript array is not a "
- "semantic with value 24"));
- return Void();
- }
- const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr();
- if (encodedDE == nullptr) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "Child of semantic in first item in SessionTranscript "
- "array is not a bstr"));
- return Void();
- }
- const vector<uint8_t>& bytesDE = encodedDE->value();
-
- auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
- if (!getXYSuccess) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "Error extracting X and Y from ePub"));
- return Void();
- }
- if (sessionTranscript.size() > 0 &&
- !(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr &&
- memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "Did not find ephemeral public key's X and Y coordinates in "
- "SessionTranscript (make sure leading zeroes are not used)"));
- return Void();
- }
- }
-
- // itemsRequest: If non-empty, contains request data that may be signed by the
- // reader. The content can be defined in the way appropriate for the
- // credential, but there are three requirements that must be met to work with
- // this HAL:
- if (itemsRequest.size() > 0) {
- // 1. The content must be a CBOR-encoded structure.
- auto [item, _, message] = cppbor::parse(itemsRequest);
- if (item == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "Error decoding CBOR in itemsRequest: %s", message.c_str()));
- return Void();
- }
-
- // 2. The CBOR structure must be a map.
- const cppbor::Map* map = item->asMap();
- if (map == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "itemsRequest is not a CBOR map"));
- return Void();
- }
-
- // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
- // the example below.
- //
- // NameSpaces = {
- // + NameSpace => DataElements ; Requested data elements for each NameSpace
- // }
- //
- // NameSpace = tstr
- //
- // DataElements = {
- // + DataElement => IntentToRetain
- // }
- //
- // DataElement = tstr
- // IntentToRetain = bool
- //
- // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
- // through 3.:
- //
- // {
- // 'docType' : 'org.iso.18013-5.2019',
- // 'nameSpaces' : {
- // 'org.iso.18013-5.2019' : {
- // 'Last name' : false,
- // 'Birth date' : false,
- // 'First name' : false,
- // 'Home address' : true
- // },
- // 'org.aamva.iso.18013-5.2019' : {
- // 'Real Id' : false
- // }
- // }
- // }
- //
- const cppbor::Map* nsMap = nullptr;
- for (size_t n = 0; n < map->size(); n++) {
- const auto& [keyItem, valueItem] = (*map)[n];
- if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
- valueItem->type() == cppbor::MAP) {
- nsMap = valueItem->asMap();
- break;
- }
- }
- if (nsMap == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "No nameSpaces map in top-most map"));
- return Void();
- }
-
- for (size_t n = 0; n < nsMap->size(); n++) {
- auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
- const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
- const cppbor::Map* nsInnerMap = nsValueItem->asMap();
- if (nsKey == nullptr || nsInnerMap == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "Type mismatch in nameSpaces map"));
- return Void();
- }
- string requestedNamespace = nsKey->value();
- vector<string> requestedKeys;
- for (size_t m = 0; m < nsInnerMap->size(); m++) {
- const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
- const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
- const cppbor::Simple* simple = innerMapValueItem->asSimple();
- const cppbor::Bool* intentToRetainItem =
- (simple != nullptr) ? simple->asBool() : nullptr;
- if (nameItem == nullptr || intentToRetainItem == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "Type mismatch in value in nameSpaces map"));
- return Void();
- }
- requestedKeys.push_back(nameItem->value());
- }
- requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
- }
- }
-
- // Finally, validate all the access control profiles in the requestData.
- bool haveAuthToken = (authToken.mac.size() > 0);
- for (const auto& profile : accessControlProfiles) {
- if (!support::secureAccessControlProfileCheckMac(profile, storageKey_)) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Error checking MAC for profile with id %d", int(profile.id)));
- return Void();
- }
- ResultCode accessControlCheck = ResultCode::OK;
- if (profile.userAuthenticationRequired) {
- if (!haveAuthToken || !checkUserAuthentication(profile, authToken, authChallenge_)) {
- accessControlCheck = ResultCode::USER_AUTHENTICATION_FAILED;
- }
- } else if (profile.readerCertificate.size() > 0) {
- if (!readerCertificateChain ||
- !checkReaderAuthentication(profile, readerCertificateChain.value())) {
- accessControlCheck = ResultCode::READER_AUTHENTICATION_FAILED;
- }
- }
- profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
- }
-
- deviceNameSpacesMap_ = cppbor::Map();
- currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
-
- requestCountsRemaining_ = requestCounts;
- currentNameSpace_ = "";
-
- itemsRequest_ = itemsRequest;
-
- numStartRetrievalCalls_ += 1;
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> IdentityCredential::startRetrieveEntryValue(
- const hidl_string& nameSpace, const hidl_string& name, uint32_t entrySize,
- const hidl_vec<uint16_t>& accessControlProfileIds, startRetrieveEntryValue_cb _hidl_cb) {
- if (name.empty()) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name cannot be empty"));
- return Void();
- }
- if (nameSpace.empty()) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name space cannot be empty"));
- return Void();
- }
-
- if (requestCountsRemaining_.size() == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "No more name spaces left to go through"));
- return Void();
- }
-
- if (currentNameSpace_ == "") {
- // First call.
- currentNameSpace_ = nameSpace;
- }
-
- if (nameSpace == currentNameSpace_) {
- // Same namespace.
- if (requestCountsRemaining_[0] == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "No more entries to be retrieved in current name space"));
- return Void();
- }
- requestCountsRemaining_[0] -= 1;
- } else {
- // New namespace.
- if (requestCountsRemaining_[0] != 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Moved to new name space but %d entries need to be retrieved "
- "in current name space",
- int(requestCountsRemaining_[0])));
- return Void();
- }
- if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
- deviceNameSpacesMap_.add(currentNameSpace_,
- std::move(currentNameSpaceDeviceNameSpacesMap_));
- }
- currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
-
- requestCountsRemaining_.erase(requestCountsRemaining_.begin());
- currentNameSpace_ = nameSpace;
- }
-
- // It's permissible to have an empty itemsRequest... but if non-empty you can
- // only request what was specified in said itemsRequest. Enforce that.
- if (itemsRequest_.size() > 0) {
- const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
- if (it == requestedNameSpacesAndNames_.end()) {
- _hidl_cb(support::result(ResultCode::NOT_IN_REQUEST_MESSAGE,
- "Name space '%s' was not requested in startRetrieval",
- nameSpace.c_str()));
- return Void();
- }
- const auto& dataItemNames = it->second;
- if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) {
- _hidl_cb(support::result(
- ResultCode::NOT_IN_REQUEST_MESSAGE,
- "Data item name '%s' in name space '%s' was not requested in startRetrieval",
- name.c_str(), nameSpace.c_str()));
- return Void();
- }
- }
-
- // Enforce access control.
- //
- // Access is granted if at least one of the profiles grants access.
- //
- // If an item is configured without any profiles, access is denied.
- //
- ResultCode accessControl = ResultCode::NO_ACCESS_CONTROL_PROFILES;
- for (auto id : accessControlProfileIds) {
- auto search = profileIdToAccessCheckResult_.find(id);
- if (search == profileIdToAccessCheckResult_.end()) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Requested entry with unvalidated profile id %d", (int(id))));
- return Void();
- }
- ResultCode accessControlForProfile = search->second;
- if (accessControlForProfile == ResultCode::OK) {
- accessControl = ResultCode::OK;
- break;
- }
- accessControl = accessControlForProfile;
- }
- if (accessControl != ResultCode::OK) {
- _hidl_cb(support::result(accessControl, "Access control check failed"));
- return Void();
- }
-
- entryAdditionalData_ =
- support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
-
- currentName_ = name;
- entryRemainingBytes_ = entrySize;
- entryValue_.resize(0);
-
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> IdentityCredential::retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent,
- retrieveEntryValue_cb _hidl_cb) {
- optional<vector<uint8_t>> content =
- support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
- if (!content) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting data"), {});
- return Void();
- }
-
- size_t chunkSize = content.value().size();
-
- if (chunkSize > entryRemainingBytes_) {
- LOG(ERROR) << "Retrieved chunk of size " << chunkSize
- << " is bigger than remaining space of size " << entryRemainingBytes_;
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Retrieved chunk of size %zd is bigger than remaining space "
- "of size %zd",
- chunkSize, entryRemainingBytes_),
- {});
- return Void();
- }
-
- entryRemainingBytes_ -= chunkSize;
- if (entryRemainingBytes_ > 0) {
- if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Retrieved non-final chunk of size %zd but expected "
- "kGcmChunkSize which is %zd",
- chunkSize, IdentityCredentialStore::kGcmChunkSize),
- {});
- return Void();
- }
- }
-
- entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
-
- if (entryRemainingBytes_ == 0) {
- auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
- if (entryValueItem == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Retrieved data invalid CBOR"), {});
- return Void();
- }
- currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
- }
-
- _hidl_cb(support::resultOK(), content.value());
- return Void();
-}
-
-Return<void> IdentityCredential::finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob,
- finishRetrieval_cb _hidl_cb) {
- if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
- deviceNameSpacesMap_.add(currentNameSpace_,
- std::move(currentNameSpaceDeviceNameSpacesMap_));
- }
- vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
-
- // If there's no signing key or no sessionTranscript or no reader ephemeral
- // public key, we return the empty MAC.
- optional<vector<uint8_t>> mac;
- if (signingKeyBlob.size() > 0 && sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0) {
- cppbor::Array array;
- array.add("DeviceAuthentication");
- array.add(sessionTranscriptItem_->clone());
- array.add(docType_);
- array.add(cppbor::Semantic(24, encodedDeviceNameSpaces));
- vector<uint8_t> encodedDeviceAuthentication = array.encode();
-
- vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> signingKey =
- support::decryptAes128Gcm(storageKey_, signingKeyBlob, docTypeAsBlob);
- if (!signingKey) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting signingKeyBlob"),
- {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> sharedSecret =
- support::ecdh(readerPublicKey_, signingKey.value());
- if (!sharedSecret) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error doing ECDH"), {}, {});
- return Void();
- }
-
- vector<uint8_t> salt = {0x00};
- vector<uint8_t> info = {};
- optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
- if (!derivedKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error deriving key from shared secret"),
- {}, {});
- return Void();
- }
-
- mac = support::coseMac0(derivedKey.value(), {}, // payload
- encodedDeviceAuthentication); // additionalData
- if (!mac) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error MACing data"), {}, {});
- return Void();
- }
- }
-
- _hidl_cb(support::resultOK(), mac.value_or(vector<uint8_t>({})), encodedDeviceNameSpaces);
- return Void();
-}
-
-Return<void> IdentityCredential::generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) {
- string serialDecimal = "0"; // TODO: set serial to something unique
- string issuer = "Android Open Source Project";
- string subject = "Android IdentityCredential Reference Implementation";
- time_t validityNotBefore = time(nullptr);
- time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
-
- optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
- if (!signingKeyPKCS8) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> signingPublicKey =
- support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
- if (!signingPublicKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of signingKey"), {},
- {});
- return Void();
- }
-
- optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
- if (!signingKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting private part of signingKey"),
- {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
- signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- if (!certificate) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> nonce = support::getRandom(12);
- if (!nonce) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting random"), {}, {});
- return Void();
- }
- vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
- storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
- if (!encryptedSigningKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting signingKey"), {}, {});
- return Void();
- }
- _hidl_cb(support::resultOK(), encryptedSigningKey.value(), certificate.value());
- return Void();
-}
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
diff --git a/identity/1.0/default/IdentityCredential.h b/identity/1.0/default/IdentityCredential.h
deleted file mode 100644
index eb8787b..0000000
--- a/identity/1.0/default/IdentityCredential.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
-#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
-
-#include <android/hardware/identity/1.0/IIdentityCredential.h>
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <cppbor/cppbor.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::std::map;
-using ::std::string;
-using ::std::vector;
-
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::identity::V1_0::IIdentityCredential;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
-
-using MapStringToVectorOfStrings = map<string, vector<string>>;
-
-class IdentityCredential : public IIdentityCredential {
- public:
- IdentityCredential(const hidl_vec<uint8_t>& credentialData)
- : credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {}
-
- // Parses and decrypts credentialData_, return false on failure. Must be
- // called right after construction.
- ResultCode initialize();
-
- // Methods from ::android::hardware::identity::IIdentityCredential follow.
-
- Return<void> deleteCredential(deleteCredential_cb _hidl_cb) override;
- Return<void> createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) override;
-
- Return<void> setReaderEphemeralPublicKey(const hidl_vec<uint8_t>& publicKey,
- setReaderEphemeralPublicKey_cb _hidl_cb) override;
-
- Return<void> createAuthChallenge(createAuthChallenge_cb _hidl_cb) override;
-
- Return<void> startRetrieval(const hidl_vec<SecureAccessControlProfile>& accessControlProfiles,
- const HardwareAuthToken& authToken,
- const hidl_vec<uint8_t>& itemsRequest,
- const hidl_vec<uint8_t>& sessionTranscript,
- const hidl_vec<uint8_t>& readerSignature,
- const hidl_vec<uint16_t>& requestCounts,
- startRetrieval_cb _hidl_cb) override;
- Return<void> startRetrieveEntryValue(const hidl_string& nameSpace, const hidl_string& name,
- uint32_t entrySize,
- const hidl_vec<uint16_t>& accessControlProfileIds,
- startRetrieveEntryValue_cb _hidl_cb) override;
- Return<void> retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent,
- retrieveEntryValue_cb _hidl_cb) override;
- Return<void> finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob,
- finishRetrieval_cb _hidl_cb) override;
-
- Return<void> generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) override;
-
- private:
- // Set by constructor
- vector<uint8_t> credentialData_;
- int numStartRetrievalCalls_;
-
- // Set by initialize()
- string docType_;
- bool testCredential_;
- vector<uint8_t> storageKey_;
- vector<uint8_t> credentialPrivKey_;
-
- // Set by createEphemeralKeyPair()
- vector<uint8_t> ephemeralPublicKey_;
-
- // Set by setReaderEphemeralPublicKey()
- vector<uint8_t> readerPublicKey_;
-
- // Set by createAuthChallenge()
- uint64_t authChallenge_;
-
- // Set at startRetrieval() time.
- map<uint16_t, ResultCode> profileIdToAccessCheckResult_;
- vector<uint8_t> sessionTranscript_;
- std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
- vector<uint8_t> itemsRequest_;
- vector<uint16_t> requestCountsRemaining_;
- MapStringToVectorOfStrings requestedNameSpacesAndNames_;
- cppbor::Map deviceNameSpacesMap_;
- cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
-
- // Set at startRetrieveEntryValue() time.
- string currentNameSpace_;
- string currentName_;
- size_t entryRemainingBytes_;
- vector<uint8_t> entryValue_;
- vector<uint8_t> entryAdditionalData_;
-};
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
diff --git a/identity/1.0/default/IdentityCredentialStore.cpp b/identity/1.0/default/IdentityCredentialStore.cpp
deleted file mode 100644
index 9eb1e70..0000000
--- a/identity/1.0/default/IdentityCredentialStore.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IdentityCredentialStore"
-
-#include "IdentityCredentialStore.h"
-#include "IdentityCredential.h"
-#include "WritableIdentityCredential.h"
-
-#include <android-base/logging.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-// Methods from ::android::hardware::identity::IIdentityCredentialStore follow.
-
-Return<void> IdentityCredentialStore::getHardwareInformation(getHardwareInformation_cb _hidl_cb) {
- _hidl_cb(support::resultOK(), "IdentityCredential Reference Implementation", "Google",
- kGcmChunkSize, false /* isDirectAccess */, {} /* supportedDocTypes */);
- return Void();
-}
-
-Return<void> IdentityCredentialStore::createCredential(const hidl_string& docType,
- bool testCredential,
- createCredential_cb _hidl_cb) {
- auto writable_credential = new WritableIdentityCredential(docType, testCredential);
- if (!writable_credential->initialize()) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error initializing WritableIdentityCredential"),
- writable_credential);
- return Void();
- }
- _hidl_cb(support::resultOK(), writable_credential);
- return Void();
-}
-
-Return<void> IdentityCredentialStore::getCredential(const hidl_vec<uint8_t>& credentialData,
- getCredential_cb _hidl_cb) {
- auto credential = new IdentityCredential(credentialData);
- // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
- auto ret = credential->initialize();
- if (ret != ResultCode::OK) {
- _hidl_cb(support::result(ret, "Error initializing IdentityCredential"), credential);
- return Void();
- }
- _hidl_cb(support::resultOK(), credential);
- return Void();
-}
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
diff --git a/identity/1.0/default/IdentityCredentialStore.h b/identity/1.0/default/IdentityCredentialStore.h
deleted file mode 100644
index ad75360..0000000
--- a/identity/1.0/default/IdentityCredentialStore.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
-#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
-
-#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-
-class IdentityCredentialStore : public IIdentityCredentialStore {
- public:
- IdentityCredentialStore() {}
-
- // The GCM chunk size used by this implementation is 64 KiB.
- static constexpr size_t kGcmChunkSize = 64 * 1024;
-
- // Methods from ::android::hardware::identity::IIdentityCredentialStore follow.
- Return<void> getHardwareInformation(getHardwareInformation_cb _hidl_cb) override;
- Return<void> createCredential(const hidl_string& docType, bool testCredential,
- createCredential_cb _hidl_cb) override;
- Return<void> getCredential(const hidl_vec<uint8_t>& credentialData,
- getCredential_cb _hidl_cb) override;
-};
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
diff --git a/identity/1.0/default/OWNERS b/identity/1.0/default/OWNERS
deleted file mode 100644
index 6969910..0000000
--- a/identity/1.0/default/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-swillden@google.com
-zeuthen@google.com
diff --git a/identity/1.0/default/WritableIdentityCredential.h b/identity/1.0/default/WritableIdentityCredential.h
deleted file mode 100644
index b1deb16..0000000
--- a/identity/1.0/default/WritableIdentityCredential.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
-#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
-
-#include <android/hardware/identity/1.0/IWritableIdentityCredential.h>
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <cppbor.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::std::string;
-using ::std::vector;
-
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-
-class WritableIdentityCredential : public IWritableIdentityCredential {
- public:
- WritableIdentityCredential(const hidl_string& docType, bool testCredential)
- : docType_(docType), testCredential_(testCredential) {}
-
- // Creates the Credential Key. Returns false on failure. Must be called
- // right after construction.
- bool initialize();
-
- // Methods from ::android::hardware::identity::IWritableIdentityCredential
- // follow.
- Return<void> getAttestationCertificate(const hidl_vec<uint8_t>& attestationApplicationId,
- const hidl_vec<uint8_t>& attestationChallenge,
- getAttestationCertificate_cb _hidl_cb) override;
-
- Return<void> startPersonalization(uint16_t accessControlProfileCount,
- const hidl_vec<uint16_t>& entryCounts,
- startPersonalization_cb _hidl_cb) override;
-
- Return<void> addAccessControlProfile(uint16_t id, const hidl_vec<uint8_t>& readerCertificate,
- bool userAuthenticationRequired, uint64_t timeoutMillis,
- uint64_t secureUserId,
- addAccessControlProfile_cb _hidl_cb) override;
-
- Return<void> beginAddEntry(const hidl_vec<uint16_t>& accessControlProfileIds,
- const hidl_string& nameSpace, const hidl_string& name,
- uint32_t entrySize, beginAddEntry_cb _hidl_cb) override;
-
- Return<void> addEntryValue(const hidl_vec<uint8_t>& content,
- addEntryValue_cb _hidl_cb) override;
-
- Return<void> finishAddingEntries(finishAddingEntries_cb _hidl_cb) override;
-
- private:
- string docType_;
- bool testCredential_;
-
- // These are set in initialize().
- vector<uint8_t> storageKey_;
- vector<uint8_t> credentialPrivKey_;
- vector<uint8_t> credentialPubKey_;
-
- // These fields are initialized during startPersonalization()
- size_t numAccessControlProfileRemaining_;
- vector<uint16_t> remainingEntryCounts_;
- cppbor::Array signedDataAccessControlProfiles_;
- cppbor::Map signedDataNamespaces_;
- cppbor::Array signedDataCurrentNamespace_;
-
- // These fields are initialized during beginAddEntry()
- size_t entryRemainingBytes_;
- vector<uint8_t> entryAdditionalData_;
- string entryNameSpace_;
- string entryName_;
- vector<uint16_t> entryAccessControlProfileIds_;
- vector<uint8_t> entryBytes_;
-};
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
diff --git a/identity/1.0/default/android.hardware.identity@1.0-service.example.rc b/identity/1.0/default/android.hardware.identity@1.0-service.example.rc
deleted file mode 100644
index 1eb7319..0000000
--- a/identity/1.0/default/android.hardware.identity@1.0-service.example.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service vendor.identity-1-0 /vendor/bin/hw/android.hardware.identity@1.0-service.example
- class hal
- user nobody
diff --git a/identity/1.0/default/service.cpp b/identity/1.0/default/service.cpp
deleted file mode 100644
index 839e803..0000000
--- a/identity/1.0/default/service.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "android.hardware.identity@1.0-service"
-
-#include <android-base/logging.h>
-#include <hidl/HidlTransportSupport.h>
-
-#include "IdentityCredentialStore.h"
-
-using android::hardware::joinRpcThreadpool;
-using android::hardware::identity::implementation::IdentityCredentialStore;
-
-int main(int /* argc */, char* argv[]) {
- ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
-
- ::android::base::InitLogging(argv, &android::base::StderrLogger);
-
- auto identity_store = new IdentityCredentialStore();
- auto status = identity_store->registerAsService();
- if (status != android::OK) {
- LOG(FATAL) << "Could not register service for IdentityCredentialStore 1.0 (" << status
- << ")";
- }
- joinRpcThreadpool();
- return -1; // Should never get here.
-}
diff --git a/identity/1.0/types.hal b/identity/1.0/types.hal
deleted file mode 100644
index 5aedfea..0000000
--- a/identity/1.0/types.hal
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.identity@1.0;
-
-/**
- * The ResultCode enumeration is used to convey the status of an operation.
- */
-enum ResultCode : int32_t {
- /**
- * Success.
- */
- OK = 0,
-
- /**
- * The operation failed. This is used as a generic catch-all for errors that don't belong
- * in other categories, including memory/resource allocation failures and I/O errors.
- */
- FAILED = 1,
-
- /**
- * The passed data was invalid. This is a generic catch all for errors that don't belong
- * in other categories related to parameter validation.
- */
- INVALID_DATA = 2,
-
- /**
- * The authToken parameter passed to IIdentityCredential.startRetrieval() is not valid.
- */
- INVALID_AUTH_TOKEN = 3,
-
- /**
- * The itemsRequest parameter passed to IIdentityCredential.startRetrieval() does not meet
- * the requirements described in the documentation for that method.
- */
- INVALID_ITEMS_REQUEST_MESSAGE = 4,
-
- /**
- * The readerSignature parameter in IIdentityCredential.startRetrieval() is invalid,
- * doesn't contain an embedded certificate chain, or the signature failed to
- * validate.
- */
- READER_SIGNATURE_CHECK_FAILED = 5,
-
- /**
- * The sessionTranscript passed to startRetrieval() did not contain the ephmeral public
- * key returned by createEphemeralPublicKey().
- */
- EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 6,
-
- /**
- * An access condition related to user authentication was not satisfied.
- */
- USER_AUTHENTICATION_FAILED = 7,
-
- /**
- * An access condition related to reader authentication was not satisfied.
- */
- READER_AUTHENTICATION_FAILED = 8,
-
- /**
- * The request data element has no access control profiles associated so it cannot be accessed.
- */
- NO_ACCESS_CONTROL_PROFILES = 9,
-
- /**
- * The requested data element is not in the provided non-empty itemsRequest message.
- */
- NOT_IN_REQUEST_MESSAGE = 10,
-
- /**
- * The passed-in sessionTranscript doesn't match the previously passed-in sessionTranscript.
- */
- SESSION_TRANSCRIPT_MISMATCH = 11,
-};
-
-/**
- * A result has a ResultCode and corresponding textual message.
- */
-struct Result {
- /**
- * The result code.
- *
- * Implementations must not use values not defined in the ResultCode enumeration.
- */
- ResultCode code;
-
- /**
- * A human-readable message in English conveying more detail about a failure.
- *
- * If code is ResultCode::OK this field must be set to the empty string.
- */
- string message;
-};
-
-struct SecureAccessControlProfile {
- /**
- * id is a numeric identifier that must be unique within the context of a Credential and may be
- * used to reference the profile.
- */
- uint16_t id;
-
- /**
- * readerCertificate, if non-empty, specifies a single X.509 certificate (not a chain
- * of certificates) that must be used to authenticate requests. For details about how
- * this is done, see the readerSignature paremter of IIdentityCredential.startRetrieval.
- */
- vec<uint8_t> readerCertificate;
-
- /**
- * if true, the user is required to authenticate to allow requests. Required authentication
- * fressness is specified by timeout below.
- *
- */
- bool userAuthenticationRequired;
-
- /**
- * Timeout specifies the amount of time, in milliseconds, for which a user authentication (see
- * above) is valid, if userAuthenticationRequired is set to true. If userAuthenticationRequired
- * is true and timout is zero then authentication is required for each reader session.
- *
- * If userAuthenticationRequired is false, timeout must be zero.
- */
- uint64_t timeoutMillis;
-
- /**
- * secureUserId must be non-zero if userAuthenticationRequired is true.
- * It is not related to any Android user ID or UID, but is created in the
- * Gatekeeper application in the secure environment.
- */
- uint64_t secureUserId;
-
- /**
- * The mac is used to authenticate the access control profile. It contains:
- *
- * AES-GCM-ENC(storageKey, R, {}, AccessControlProfile)
- *
- * where AccessControlProfile is the CBOR map:
- *
- * AccessControlProfile = {
- * "id": uint,
- * ? "readerCertificate" : bstr,
- * ? (
- * "userAuthenticationRequired" : bool,
- * "timeoutMillis" : uint,
- * "secureUserId" : uint
- * )
- * }
- */
- vec<uint8_t> mac;
-};
diff --git a/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp b/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp
deleted file mode 100644
index 88b06df..0000000
--- a/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * 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 "IdentityCredentialHidlHalTest"
-
-#include <map>
-
-#include <android-base/logging.h>
-#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
-#include <android/hardware/identity/1.0/types.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-
-using std::map;
-using std::optional;
-using std::string;
-using std::vector;
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace test {
-
-using ::android::hardware::identity::V1_0::IIdentityCredential;
-using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
-using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
-
-// ---------------------------------------------------------------------------
-// Test Data.
-// ---------------------------------------------------------------------------
-
-struct TestEntryData {
- TestEntryData(string nameSpace, string name, vector<uint16_t> profileIds)
- : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
-
- TestEntryData(string nameSpace, string name, const string& value, vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
- }
- TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
- vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- valueCbor = cppbor::Bstr(value).encode();
- }
- TestEntryData(string nameSpace, string name, bool value, vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- valueCbor = cppbor::Bool(value).encode();
- }
- TestEntryData(string nameSpace, string name, int64_t value, vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- if (value >= 0) {
- valueCbor = cppbor::Uint(value).encode();
- } else {
- valueCbor = cppbor::Nint(-value).encode();
- }
- }
-
- string nameSpace;
- string name;
- vector<uint8_t> valueCbor;
- vector<uint16_t> profileIds;
-};
-
-struct TestProfile {
- uint16_t id;
- hidl_vec<uint8_t> readerCertificate;
- bool userAuthenticationRequired;
- uint64_t timeoutMillis;
-};
-
-/************************************
- * TEST DATA FOR AUTHENTICATION
- ************************************/
-// Test authentication token for user authentication
-
-class IdentityCredentialStoreHidlTest : public ::testing::TestWithParam<std::string> {
- public:
- virtual void SetUp() override {
- string serviceName = GetParam();
- ASSERT_FALSE(serviceName.empty());
- credentialStore_ = IIdentityCredentialStore::getService(serviceName);
- ASSERT_NE(credentialStore_, nullptr);
-
- credentialStore_->getHardwareInformation(
- [&](const Result& result, const hidl_string& credentialStoreName,
- const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
- bool /* isDirectAccess */,
- const hidl_vec<hidl_string> /* supportedDocTypes */) {
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_GT(credentialStoreName.size(), 0u);
- ASSERT_GT(credentialStoreAuthorName.size(), 0u);
- ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported
- dataChunkSize_ = chunkSize;
- });
- }
- virtual void TearDown() override {}
-
- uint32_t dataChunkSize_ = 0;
-
- sp<IIdentityCredentialStore> credentialStore_;
-};
-
-TEST_P(IdentityCredentialStoreHidlTest, HardwareConfiguration) {
- credentialStore_->getHardwareInformation(
- [&](const Result& result, const hidl_string& credentialStoreName,
- const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
- bool /* isDirectAccess */, const hidl_vec<hidl_string> /* supportedDocTypes */) {
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_GT(credentialStoreName.size(), 0u);
- ASSERT_GT(credentialStoreAuthorName.size(), 0u);
- ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported
- });
-}
-
-TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
- // First, generate a key-pair for the reader since its public key will be
- // part of the request data.
- optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
- ASSERT_TRUE(readerKeyPKCS8);
- optional<vector<uint8_t>> readerPublicKey =
- support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
- optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
- string serialDecimal = "1234";
- string issuer = "Android Open Source Project";
- string subject = "Android IdentityCredential VTS Test";
- time_t validityNotBefore = time(nullptr);
- time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
- optional<vector<uint8_t>> readerCertificate = support::ecPublicKeyGenerateCertificate(
- readerPublicKey.value(), readerKey.value(), serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- ASSERT_TRUE(readerCertificate);
-
- // Make the portrait image really big (just shy of 256 KiB) to ensure that
- // the chunking code gets exercised.
- vector<uint8_t> portraitImage;
- portraitImage.resize(256 * 1024 - 10);
- for (size_t n = 0; n < portraitImage.size(); n++) {
- portraitImage[n] = (uint8_t)n;
- }
-
- // Access control profiles:
- const vector<TestProfile> testProfiles = {// Profile 0 (reader authentication)
- {0, readerCertificate.value(), false, 0},
- // Profile 1 (no authentication)
- {1, {}, false, 0}};
-
- HardwareAuthToken authToken = {};
-
- // Here's the actual test data:
- const vector<TestEntryData> testEntries = {
- {"PersonalData", "Last name", string("Turing"), vector<uint16_t>{0, 1}},
- {"PersonalData", "Birth date", string("19120623"), vector<uint16_t>{0, 1}},
- {"PersonalData", "First name", string("Alan"), vector<uint16_t>{0, 1}},
- {"PersonalData", "Home address", string("Maida Vale, London, England"),
- vector<uint16_t>{0}},
- {"Image", "Portrait image", portraitImage, vector<uint16_t>{0, 1}},
- };
- const vector<uint16_t> testEntriesEntryCounts = {static_cast<uint16_t>(testEntries.size() - 1),
- 1u};
-
- string cborPretty;
- sp<IWritableIdentityCredential> writableCredential;
-
- hidl_vec<uint8_t> empty{0};
-
- string docType = "org.iso.18013-5.2019.mdl";
- bool testCredential = true;
- Result result;
- credentialStore_->createCredential(
- docType, testCredential,
- [&](const Result& _result, const sp<IWritableIdentityCredential>& _writableCredential) {
- result = _result;
- writableCredential = _writableCredential;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_NE(writableCredential, nullptr);
-
- string challenge = "attestationChallenge";
- // TODO: set it to something random and check it's in the cert chain
- vector<uint8_t> attestationApplicationId = {};
- vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
- vector<uint8_t> attestationCertificate;
- writableCredential->getAttestationCertificate(
- attestationApplicationId, attestationChallenge,
- [&](const Result& _result, const hidl_vec<hidl_vec<uint8_t>>& _splitCertChain) {
- result = _result;
- vector<vector<uint8_t>> splitCerts;
- std::copy(_splitCertChain.begin(), _splitCertChain.end(),
- std::back_inserter(splitCerts));
- attestationCertificate = support::certificateChainJoin(splitCerts);
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts,
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<SecureAccessControlProfile> returnedSecureProfiles;
- for (const auto& testProfile : testProfiles) {
- SecureAccessControlProfile profile;
- writableCredential->addAccessControlProfile(
- testProfile.id, testProfile.readerCertificate,
- testProfile.userAuthenticationRequired, testProfile.timeoutMillis,
- 0, // secureUserId
- [&](const Result& _result, const SecureAccessControlProfile& _profile) {
- result = _result;
- profile = _profile;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_EQ(testProfile.id, profile.id);
- ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate);
- ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
- ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
- ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
- returnedSecureProfiles.push_back(profile);
- }
-
- // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
- // is a little hacky but it works well enough.
- map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
-
- for (const auto& entry : testEntries) {
- vector<vector<uint8_t>> chunks = support::chunkVector(entry.valueCbor, dataChunkSize_);
-
- writableCredential->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
- entry.valueCbor.size(),
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<vector<uint8_t>> encryptedChunks;
- for (const auto& chunk : chunks) {
- writableCredential->addEntryValue(
- chunk, [&](const Result& result, hidl_vec<uint8_t> encryptedContent) {
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_GT(encryptedContent.size(), 0u);
- encryptedChunks.push_back(encryptedContent);
- });
- }
- encryptedBlobs[&entry] = encryptedChunks;
- }
-
- vector<uint8_t> credentialData;
- vector<uint8_t> proofOfProvisioningSignature;
- writableCredential->finishAddingEntries(
- [&](const Result& _result, const hidl_vec<uint8_t>& _credentialData,
- const hidl_vec<uint8_t>& _proofOfProvisioningSignature) {
- result = _result;
- credentialData = _credentialData;
- proofOfProvisioningSignature = _proofOfProvisioningSignature;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- optional<vector<uint8_t>> proofOfProvisioning =
- support::coseSignGetPayload(proofOfProvisioningSignature);
- ASSERT_TRUE(proofOfProvisioning);
- cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
- EXPECT_EQ(
- "[\n"
- " 'ProofOfProvisioning',\n"
- " 'org.iso.18013-5.2019.mdl',\n"
- " [\n"
- " {\n"
- " 'id' : 0,\n"
- " 'readerCertificate' : <not printed>,\n"
- " },\n"
- " {\n"
- " 'id' : 1,\n"
- " },\n"
- " ],\n"
- " {\n"
- " 'PersonalData' : [\n"
- " {\n"
- " 'name' : 'Last name',\n"
- " 'value' : 'Turing',\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " {\n"
- " 'name' : 'Birth date',\n"
- " 'value' : '19120623',\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " {\n"
- " 'name' : 'First name',\n"
- " 'value' : 'Alan',\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " {\n"
- " 'name' : 'Home address',\n"
- " 'value' : 'Maida Vale, London, England',\n"
- " 'accessControlProfiles' : [0, ],\n"
- " },\n"
- " ],\n"
- " 'Image' : [\n"
- " {\n"
- " 'name' : 'Portrait image',\n"
- " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " ],\n"
- " },\n"
- " true,\n"
- "]",
- cborPretty);
-
- optional<vector<uint8_t>> credentialPubKey =
- support::certificateChainGetTopMostKey(attestationCertificate);
- ASSERT_TRUE(credentialPubKey);
- EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
- {}, // Additional data
- credentialPubKey.value()));
- writableCredential = nullptr;
-
- // Now that the credential has been provisioned, read it back and check the
- // correct data is returned.
- sp<IIdentityCredential> credential;
- credentialStore_->getCredential(
- credentialData, [&](const Result& _result, const sp<IIdentityCredential>& _credential) {
- result = _result;
- credential = _credential;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_NE(credential, nullptr);
-
- optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
- ASSERT_TRUE(readerEphemeralKeyPair);
- optional<vector<uint8_t>> readerEphemeralPublicKey =
- support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
- credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value(),
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<uint8_t> ephemeralKeyPair;
- credential->createEphemeralKeyPair(
- [&](const Result& _result, const hidl_vec<uint8_t>& _ephemeralKeyPair) {
- result = _result;
- ephemeralKeyPair = _ephemeralKeyPair;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
-
- // Calculate requestData field and sign it with the reader key.
- auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
- ASSERT_TRUE(getXYSuccess);
- cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
- vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
- vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
- cppbor::Array sessionTranscript = cppbor::Array()
- .add(cppbor::Semantic(24, deviceEngagementBytes))
- .add(cppbor::Semantic(24, eReaderPubBytes));
- vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
-
- vector<uint8_t> itemsRequestBytes =
- cppbor::Map("nameSpaces",
- cppbor::Map()
- .add("PersonalData", cppbor::Map()
- .add("Last name", false)
- .add("Birth date", false)
- .add("First name", false)
- .add("Home address", true))
- .add("Image", cppbor::Map().add("Portrait image", false)))
- .encode();
- cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
- EXPECT_EQ(
- "{\n"
- " 'nameSpaces' : {\n"
- " 'PersonalData' : {\n"
- " 'Last name' : false,\n"
- " 'Birth date' : false,\n"
- " 'First name' : false,\n"
- " 'Home address' : true,\n"
- " },\n"
- " 'Image' : {\n"
- " 'Portrait image' : false,\n"
- " },\n"
- " },\n"
- "}",
- cborPretty);
- vector<uint8_t> dataToSign = cppbor::Array()
- .add("ReaderAuthentication")
- .add(sessionTranscript.clone())
- .add(cppbor::Semantic(24, itemsRequestBytes))
- .encode();
- optional<vector<uint8_t>> readerSignature =
- support::coseSignEcDsa(readerKey.value(), {}, // content
- dataToSign, // detached content
- readerCertificate.value());
- ASSERT_TRUE(readerSignature);
-
- credential->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
- sessionTranscriptBytes, readerSignature.value(),
- testEntriesEntryCounts,
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- for (const auto& entry : testEntries) {
- credential->startRetrieveEntryValue(entry.nameSpace, entry.name, entry.valueCbor.size(),
- entry.profileIds,
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- auto it = encryptedBlobs.find(&entry);
- ASSERT_NE(it, encryptedBlobs.end());
- const vector<vector<uint8_t>>& encryptedChunks = it->second;
-
- vector<uint8_t> content;
- for (const auto& encryptedChunk : encryptedChunks) {
- vector<uint8_t> chunk;
- credential->retrieveEntryValue(
- encryptedChunk, [&](const Result& _result, const hidl_vec<uint8_t>& _chunk) {
- result = _result;
- chunk = _chunk;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- content.insert(content.end(), chunk.begin(), chunk.end());
- }
- EXPECT_EQ(content, entry.valueCbor);
- }
-
- // Generate the key that will be used to sign AuthenticatedData.
- vector<uint8_t> signingKeyBlob;
- vector<uint8_t> signingKeyCertificate;
- credential->generateSigningKeyPair([&](const Result& _result,
- const hidl_vec<uint8_t> _signingKeyBlob,
- const hidl_vec<uint8_t> _signingKeyCertificate) {
- result = _result;
- signingKeyBlob = _signingKeyBlob;
- signingKeyCertificate = _signingKeyCertificate;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<uint8_t> mac;
- vector<uint8_t> deviceNameSpacesBytes;
- credential->finishRetrieval(signingKeyBlob,
- [&](const Result& _result, const hidl_vec<uint8_t> _mac,
- const hidl_vec<uint8_t> _deviceNameSpacesBytes) {
- result = _result;
- mac = _mac;
- deviceNameSpacesBytes = _deviceNameSpacesBytes;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
- ASSERT_EQ(
- "{\n"
- " 'PersonalData' : {\n"
- " 'Last name' : 'Turing',\n"
- " 'Birth date' : '19120623',\n"
- " 'First name' : 'Alan',\n"
- " 'Home address' : 'Maida Vale, London, England',\n"
- " },\n"
- " 'Image' : {\n"
- " 'Portrait image' : <bstr size=262134 "
- "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
- " },\n"
- "}",
- cborPretty);
- // The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType,
- // deviceNameSpacesBytes] so build up that structure
- cppbor::Array deviceAuthentication;
- deviceAuthentication.add("DeviceAuthentication");
- deviceAuthentication.add(sessionTranscript.clone());
- deviceAuthentication.add(docType);
- deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
- vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
- optional<vector<uint8_t>> signingPublicKey =
- support::certificateChainGetTopMostKey(signingKeyCertificate);
- EXPECT_TRUE(signingPublicKey);
-
- // Derive the key used for MACing.
- optional<vector<uint8_t>> readerEphemeralPrivateKey =
- support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
- optional<vector<uint8_t>> sharedSecret =
- support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
- ASSERT_TRUE(sharedSecret);
- vector<uint8_t> salt = {0x00};
- vector<uint8_t> info = {};
- optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
- ASSERT_TRUE(derivedKey);
- optional<vector<uint8_t>> calculatedMac =
- support::coseMac0(derivedKey.value(), {}, // payload
- encodedDeviceAuthentication); // detached content
- ASSERT_TRUE(calculatedMac);
- EXPECT_EQ(mac, calculatedMac);
-}
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, IdentityCredentialStoreHidlTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(
- IIdentityCredentialStore::descriptor)),
- android::hardware::PrintInstanceNameToString);
-
-} // namespace test
-} // namespace identity
-} // namespace hardware
-} // namespace android
diff --git a/identity/1.0/vts/OWNERS b/identity/OWNERS
similarity index 100%
rename from identity/1.0/vts/OWNERS
rename to identity/OWNERS
diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp
new file mode 100644
index 0000000..72b19a1
--- /dev/null
+++ b/identity/aidl/Android.bp
@@ -0,0 +1,21 @@
+aidl_interface {
+ name: "android.hardware.identity",
+ vendor_available: true,
+ srcs: [
+ "android/hardware/identity/*.aidl",
+ ],
+ imports: [
+ "android.hardware.keymaster",
+ ],
+ stability: "vintf",
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/radio/config/1.3/IRadioConfigIndication.hal b/identity/aidl/android/hardware/identity/Certificate.aidl
similarity index 63%
copy from radio/config/1.3/IRadioConfigIndication.hal
copy to identity/aidl/android/hardware/identity/Certificate.aidl
index 9ef496c..5bbc17c 100644
--- a/radio/config/1.3/IRadioConfigIndication.hal
+++ b/identity/aidl/android/hardware/identity/Certificate.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package android.hardware.radio.config@1.3;
+package android.hardware.identity;
-import @1.2::IRadioConfigIndication;
-
-/**
- * Interface declaring unsolicited radio config indications.
- */
-interface IRadioConfigIndication extends @1.2::IRadioConfigIndication {
-
-};
+@VintfStability
+parcelable Certificate {
+ /**
+ * encodedCertificate contains the bytes of a DER-encoded X.509 certificate.
+ *
+ * If there is no certificate, this array is empty.
+ */
+ byte[] encodedCertificate;
+}
diff --git a/identity/aidl/android/hardware/identity/CipherSuite.aidl b/identity/aidl/android/hardware/identity/CipherSuite.aidl
new file mode 100644
index 0000000..20b02a8
--- /dev/null
+++ b/identity/aidl/android/hardware/identity/CipherSuite.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.identity;
+
+/**
+ * Cipher suites that can be used for communication between holder and reader devices.
+ */
+@VintfStability
+@Backing(type="int")
+enum CipherSuite {
+ /**
+ * Specifies that the cipher suite that will be used to secure communications between the reader
+ * is:
+ *
+ * - ECDHE with HKDF-SHA-256 for key agreement.
+ * - AES-256 with GCM block mode for authenticated encryption (nonces are incremented by
+ * one for every message).
+ * - ECDSA with SHA-256 for signing (used for signing session transcripts to defeat
+ * man-in-the-middle attacks), signing keys are not ephemeral.
+ *
+ * At present this is the only supported cipher suite and it is mandatory for all
+ * implementations to support it.
+ */
+ CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1,
+}
diff --git a/identity/aidl/android/hardware/identity/HardwareInformation.aidl b/identity/aidl/android/hardware/identity/HardwareInformation.aidl
new file mode 100644
index 0000000..d67739d
--- /dev/null
+++ b/identity/aidl/android/hardware/identity/HardwareInformation.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.identity;
+
+@VintfStability
+parcelable HardwareInformation {
+ /**
+ * credentialStoreName is the name of the credential store implementation.
+ */
+ @utf8InCpp String credentialStoreName;
+
+ /**
+ * credentialStoreAuthorName is the name of the credential store author.
+ */
+ @utf8InCpp String credentialStoreAuthorName;
+
+ /**
+ * dataChunkSize is the size of data chunks to be used when sending and recieving data
+ * entries. All data chunks for a data item must be this size except for the last.
+ */
+ int dataChunkSize;
+
+ /**
+ * isDirectAccess specifies whether the provisioned credential is available through
+ * direct access. Credentials provisioned in credential stores with this set
+ * to true, should use reader authentication on all data elements.
+ */
+ boolean isDirectAccess;
+
+ /**
+ * supportedDocTypes if empty, then any document type is supported, otherwise
+ * only the document types returned are supported.
+ *
+ * Document types are defined in the relevant standard for the document, for example for the
+ * for Mobile Driving License as defined by ISO 18013-5 the document type is defined to
+ * be "org.iso.18013.5.1.mDL".
+ *
+ */
+ @utf8InCpp String[] supportedDocTypes;
+}
diff --git a/identity/1.0/IIdentityCredential.hal b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
similarity index 75%
rename from identity/1.0/IIdentityCredential.hal
rename to identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index 75f6e18..10ce4c2 100644
--- a/identity/1.0/IIdentityCredential.hal
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package android.hardware.identity@1.0;
+package android.hardware.identity;
-import android.hardware.keymaster@4.0::HardwareAuthToken;
+import android.hardware.identity.Certificate;
+import android.hardware.identity.SecureAccessControlProfile;
+import android.hardware.keymaster.HardwareAuthToken;
+@VintfStability
interface IIdentityCredential {
/**
* Delete a credential.
@@ -35,10 +38,9 @@
* After this method has been called, the persistent storage used for credentialData should
* be deleted.
*
- * @return proofOfDeletionSignature is a COSE_Sign1 signature described above.
+ * @return a COSE_Sign1 signature described above.
*/
- deleteCredential()
- generates(Result result, vec<uint8_t> proofOfDeletionSignature);
+ byte[] deleteCredential();
/**
* Creates an ephemeral EC key pair, for use in establishing a seceure session with a reader.
@@ -46,39 +48,33 @@
* with the reader. The reason for generating the key pair in the secure environment is so that
* the secure environment knows what public key to expect to find in the session transcript.
*
- * This method may only be called once per instance. If called more than once, FAILED
+ * This method may only be called once per instance. If called more than once, STATUS_FAILED
* will be returned.
*
- * @return result is OK on success or FAILED if an error occurred.
- *
- * @return keyPair contains the unencrypted key-pair in PKCS#8 format.
+ * @return the unencrypted key-pair in PKCS#8 format.
*/
- createEphemeralKeyPair() generates (Result result, vec<uint8_t> keyPair);
+ byte[] createEphemeralKeyPair();
/**
* Sets the public part of the reader's ephemeral key pair.
*
- * This method may only be called once per instance. If called more than once, FAILED
+ * This method may only be called once per instance. If called more than once, STATUS_FAILED
* will be returned.
*
* @param publicKey contains the reader's ephemeral public key, in uncompressed form.
- *
- * @return result is OK on success or FAILED if an error occurred.
*/
- setReaderEphemeralPublicKey(vec<uint8_t> publicKey) generates (Result result);
+ void setReaderEphemeralPublicKey(in byte[] publicKey);
/**
* Creates a challenge value to be used for proving successful user authentication. This
* is included in the authToken passed to the startRetrieval() method.
*
- * This method may only be called once per instance. If called more than once, FAILED
+ * This method may only be called once per instance. If called more than once, STATUS_FAILED
* will be returned.
*
- * @return result is OK on success or FAILED if an error occurred.
- *
- * @return challenge on success, is a non-zero number.
+ * @return challenge, a non-zero number.
*/
- createAuthChallenge() generates (Result result, uint64_t challenge);
+ long createAuthChallenge();
/**
* Start an entry retrieval process.
@@ -92,12 +88,12 @@
* startRetrieval(), then multiple calls of startRetrieveEntryValue(), retrieveEntryValue(),
* then finally finishRetrieval()) but if this is done, the sessionTranscript parameter
* must be identical for each startRetrieval() invocation. If this is not the case, this call
- * fails with the SESSION_TRANSCRIPT_MISMATCH error.
+ * fails with the STATUS_SESSION_TRANSCRIPT_MISMATCH error.
*
- * If the provided authToken is not valid this method fails with INVALID_AUTH_TOKEN.
+ * If the provided authToken is not valid this method fails with STATUS_INVALID_AUTH_TOKEN.
*
* Each of the provided accessControlProfiles is checked in this call. If they are not
- * all valid, the call fails with INVALID_DATA.
+ * all valid, the call fails with STATUS_INVALID_DATA.
*
* For the itemsRequest parameter, the content can be defined in the way appropriate for
* the credential, but there are three requirements that must be met to work with this HAL:
@@ -108,7 +104,7 @@
* the example below.
*
* If these requirements are not met the startRetrieval() call fails with
- * INVALID_ITEMS_REQUEST_MESSAGE.
+ * STATUS_INVALID_ITEMS_REQUEST_MESSAGE.
*
* Here's an example of ItemsRequest CBOR which conforms to this requirement:
*
@@ -156,17 +152,18 @@
* 'x5chain' unprotected header element of the COSE_Sign1 structure (as as described
* in 'draft-ietf-cose-x509-04'). There will be at least one certificate in said element
* and there may be more (and if so, each certificate must be signed by its successor).
- * This is checked and if the check fails the call fails with READER_SIGNATURE_CHECK_FAILED.
+ * This is checked and if the check fails the call fails with
+ * STATUS_READER_SIGNATURE_CHECK_FAILED.
*
* The SessionTranscript CBOR is conveyed in the sessionTranscript parameter. It
* is permissible for this to be empty in which case the readerSignature parameter
- * must also be empty. If this is not the case, the call fails with FAILED.
+ * must also be empty. If this is not the case, the call fails with STATUS_FAILED.
*
* If the SessionTranscript CBOR is not empty, the X and Y coordinates of the public
* part of the key-pair previously generated by createEphemeralKeyPair() must appear
* somewhere in the bytes of DeviceEngagement structure. Both X and Y should be in
* uncompressed form. If this is not satisfied, the call fails with
- * EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
+ * STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
*
* @param accessControlProfiles
* Access control profiles that are required to retrieve the entries that are going to be
@@ -196,16 +193,11 @@
* will succeed (i.e. that the access control profile checks will succeed). This means that
* it's the responsibility of the caller to determine which access control checks will fail
* and remove the corresponding requests from the counts.
- *
- * @return result is OK on success. If an error occurs one of the values described above
- * will be returned.
*/
- startRetrieval(vec<SecureAccessControlProfile> accessControlProfiles,
- HardwareAuthToken authToken,
- vec<uint8_t> itemsRequest,
- vec<uint8_t> sessionTranscript,
- vec<uint8_t> readerSignature,
- vec<uint16_t> requestCounts) generates(Result result);
+ void startRetrieval(in SecureAccessControlProfile[] accessControlProfiles,
+ in HardwareAuthToken authToken,
+ in byte[] itemsRequest,
+ in byte[] sessionTranscript, in byte[] readerSignature, in int[] requestCounts);
/**
* Starts retrieving an entry, subject to access control requirements. Entries must be
@@ -213,15 +205,15 @@
*
* If the requestData parameter as passed to startRetrieval() was non-empty
* this method must only be called with entries specified in that field. If this
- * requirement is not met, the call fails with NOT_IN_REQUEST_MESSAGE.
+ * requirement is not met, the call fails with STATUS_NOT_IN_REQUEST_MESSAGE.
*
- * If nameSpace or name is empty this call fails with INVALID_DATA.
+ * If nameSpace or name is empty this call fails with STATUS_INVALID_DATA.
*
* Each access control profile for the entry is checked. If user authentication
* is required and the supplied auth token doesn't provide it the call fails
- * with USER_AUTHENTICATION_FAILED. If reader authentication is required and
+ * with STATUS_USER_AUTHENTICATION_FAILED. If reader authentication is required and
* a suitable reader certificate chain isn't presented, the call fails with
- * READER_AUTHENTICATION_FAILED.
+ * STATUS_READER_AUTHENTICATION_FAILED.
*
* It is permissible to keep retrieving values if an access control check fails.
*
@@ -231,38 +223,29 @@
*
* @param entrySize is the size of the entry value, if it's a text string or a byte string.
* It must be zero if the entry value is an integer or boolean. If this requirement
- * is not met the call fails with INVALID_DATA.
+ * is not met the call fails with STATUS_INVALID_DATA.
*
* @param accessControlProfileIds specifies the set of access control profiles that can
* authorize access to the provisioned element. If an identifier of a profile
* is given and this profile wasn't passed to startRetrieval() this call fails
- * with INVALID_DATA.
- *
- * @return result is OK on success. Otherwise one of INVALID_DATA, FAILED,
- * USER_AUTHENTICATION_FAILED, READER_AUTHENTICATION_FAILED.
+ * with STATUS_INVALID_DATA.
*/
- startRetrieveEntryValue(string nameSpace, string name, uint32_t entrySize,
- vec<uint16_t> accessControlProfileIds)
- generates (Result result);
-
+ void startRetrieveEntryValue(in @utf8InCpp String nameSpace, in @utf8InCpp String name,
+ in int entrySize, in int[] accessControlProfileIds);
/**
* Retrieves an entry value, or part of one, if the entry value is larger than gcmChunkSize.
* May only be called after startRetrieveEntry().
*
* If the passed in data is not authentic, can't be decrypted, is of the wrong size, or can't
- * be decoded, this call fails with INVALID_DATA.
+ * be decoded, this call fails with STATUS_INVALID_DATA.
*
* @param encryptedContent contains the encrypted and MACed content.
*
- * @return result is OK on success, INVALID_DATA, or FAILED if an error occurred.
- *
- * @return content is the entry value as CBOR, or part of the entry value in the case the
+ * @return the entry value as CBOR, or part of the entry value in the case the
* content exceeds gcmChunkSize in length.
*/
- retrieveEntryValue(vec<uint8_t> encryptedContent)
- generates (Result result, vec<uint8_t> content);
-
+ byte[] retrieveEntryValue(in byte[] encryptedContent);
/**
* End retrieval of data, optionally returning a message authentication code over the
@@ -273,11 +256,9 @@
*
* @param signingKeyBlob is either empty or a signingKeyBlob (see generateSigningKeyPair(),
* below) containing the signing key to use to sign the data retrieved. If this
- * is not in the right format the call fails with INVALID_DATA.
+ * is not in the right format the call fails with STATUS_INVALID_DATA.
*
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
- *
- * @return mac is empty if signingKeyBlob or the sessionTranscript passed to
+ * @param out mac is empty if signingKeyBlob or the sessionTranscript passed to
* startRetrieval() is empty. Otherwise it is a COSE_Mac0 with empty payload
* and the detached content is set to DeviceAuthentication as defined below.
* The key used for the MAC operation is EMacKey and is derived as follows:
@@ -321,23 +302,17 @@
* DataItemValue = any
*
*
- * @return deviceNameSpaces the bytes of DeviceNameSpaces.
+ * @param out deviceNameSpaces the bytes of DeviceNameSpaces.
*/
- finishRetrieval(vec<uint8_t> signingKeyBlob)
- generates(Result result, vec<uint8_t> mac, vec<uint8_t> deviceNameSpaces);
-
+ void finishRetrieval(in byte[] signingKeyBlob, out byte[] mac, out byte[] deviceNameSpaces);
/**
* Generate a key pair to be used for signing session data and retrieved data items.
*
- * @return result is OK on success or FAILED if an error occurred.
+ * @param out signingKeyBlob contains an encrypted copy of the newly-generated private
+ * signing key.
*
- * @return signingKeyBlob contains an encrypted copy of the newly-generated private signing key.
- *
- * @return signingKeyCertificate contains an X.509 certificate for the new signing key, signed
- * by the credential key.
+ * @return an X.509 certificate for the new signing key, signed by the credential key.
*/
- generateSigningKeyPair()
- generates(Result result, vec<uint8_t> signingKeyBlob,
- vec<uint8_t> signingKeyCertificate);
-};
+ Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
+}
diff --git a/identity/1.0/IIdentityCredentialStore.hal b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
similarity index 67%
rename from identity/1.0/IIdentityCredentialStore.hal
rename to identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
index 118ca6f..23cb1b7 100644
--- a/identity/1.0/IIdentityCredentialStore.hal
+++ b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package android.hardware.identity@1.0;
+package android.hardware.identity;
-import IWritableIdentityCredential;
-import IIdentityCredential;
+import android.hardware.identity.IIdentityCredential;
+import android.hardware.identity.IWritableIdentityCredential;
+import android.hardware.identity.HardwareInformation;
+import android.hardware.identity.CipherSuite;
/**
* IIdentityCredentialStore provides an interface to a secure store for user identity documents.
@@ -98,37 +100,90 @@
* define appropriate encodings, those are used. For example, X.509 certificates. Where new
* encodings are needed, CBOR is used. CBOR maps are described in CDDL notation
* (https://tools.ietf.org/html/draft-ietf-cbor-cddl-06).
+ *
+ * All binder calls in the HAL may return a ServiceSpecificException with statuses from the
+ * STATUS_* integers defined in this interface. Each method states which status can be returned
+ * and under which circumstances.
*/
+@VintfStability
interface IIdentityCredentialStore {
+ /**
+ * Success.
+ */
+ const int STATUS_OK = 0;
+
+ /**
+ * The operation failed. This is used as a generic catch-all for errors that don't belong
+ * in other categories, including memory/resource allocation failures and I/O errors.
+ */
+ const int STATUS_FAILED = 1;
+
+ /**
+ * Unsupported cipher suite.
+ */
+ const int STATUS_CIPHER_SUITE_NOT_SUPPORTED = 2;
+
+ /**
+ * The passed data was invalid. This is a generic catch all for errors that don't belong
+ * in other categories related to parameter validation.
+ */
+ const int STATUS_INVALID_DATA = 3;
+
+ /**
+ * The authToken parameter passed to IIdentityCredential.startRetrieval() is not valid.
+ */
+ const int STATUS_INVALID_AUTH_TOKEN = 4;
+
+ /**
+ * The itemsRequest parameter passed to IIdentityCredential.startRetrieval() does not meet
+ * the requirements described in the documentation for that method.
+ */
+ const int STATUS_INVALID_ITEMS_REQUEST_MESSAGE = 5;
+
+ /**
+ * The readerSignature parameter in IIdentityCredential.startRetrieval() is invalid,
+ * doesn't contain an embedded certificate chain, or the signature failed to
+ * validate.
+ */
+ const int STATUS_READER_SIGNATURE_CHECK_FAILED = 6;
+
+ /**
+ * The sessionTranscript passed to startRetrieval() did not contain the ephmeral public
+ * key returned by createEphemeralPublicKey().
+ */
+ const int STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 7;
+
+ /**
+ * An access condition related to user authentication was not satisfied.
+ */
+ const int STATUS_USER_AUTHENTICATION_FAILED = 8;
+
+ /**
+ * An access condition related to reader authentication was not satisfied.
+ */
+ const int STATUS_READER_AUTHENTICATION_FAILED = 9;
+
+ /**
+ * The request data element has no access control profiles associated so it cannot be accessed.
+ */
+ const int STATUS_NO_ACCESS_CONTROL_PROFILES = 10;
+
+ /**
+ * The requested data element is not in the provided non-empty itemsRequest message.
+ */
+ const int STATUS_NOT_IN_REQUEST_MESSAGE = 11;
+
+ /**
+ * The passed-in sessionTranscript doesn't match the previously passed-in sessionTranscript.
+ */
+ const int STATUS_SESSION_TRANSCRIPT_MISMATCH = 12;
/**
* Returns information about hardware.
*
- * The isDirectAccess output parameter indicates whether this credential store
- * implementation is for direct access. Credentials provisioned in credential
- * stores with this set to true, should use reader authentication on all data elements.
- *
- * @return result is OK on success, FAILED if an error occurred.
- *
- * @return credentialStoreName the name of the credential store implementation.
- *
- * @return credentialStoreAuthorName the name of the credential store author.
- *
- * @return dataChunkSize the maximum size of data chunks.
- *
- * @return isDirectAccess whether the provisioned credential is available through
- * direct access.
- *
- * @return supportedDocTypes if empty, then any document type is supported, otherwise
- * only the document types returned are supported.
+ * @return a HardwareInformation with information about the hardware.
*/
- getHardwareInformation()
- generates(Result result,
- string credentialStoreName,
- string credentialStoreAuthorName,
- uint32_t dataChunkSize,
- bool isDirectAccess,
- vec<string> supportedDocTypes);
+ HardwareInformation getHardwareInformation();
/**
* createCredential creates a new Credential. When a Credential is created, two cryptographic
@@ -147,16 +202,14 @@
* all-zeros hardware-bound key (HBK) and must set the test bit in the
* personalizationReceipt (see finishAddingEntries(), in IWritableIdentityCredential).
*
- * @return result is OK on success, FAILED if an error occurred.
- *
- * @return writableCredential is an IWritableIdentityCredential HIDL interface that provides
- * operations to provision a credential.
+ * @return an IWritableIdentityCredential interface that provides operations to
+ * provision a credential.
*/
- createCredential(string docType, bool testCredential)
- generates(Result result, IWritableIdentityCredential writableCredential);
+ IWritableIdentityCredential createCredential(in @utf8InCpp String docType,
+ in boolean testCredential);
/**
- * getCredential retrieves an IIdentityCredential HIDL interface which allows use of a stored
+ * getCredential retrieves an IIdentityCredential interface which allows use of a stored
* Credential.
*
* The cipher suite used to communicate with the remote verifier must also be specified. Currently
@@ -170,16 +223,17 @@
*
* Support for other cipher suites may be added in a future version of this HAL.
*
+ * This method fails with STATUS_INVALID_DATA if the passed in credentialData cannot be
+ * decoded or decrypted.
+ *
+ * @param cipherSuite is the cipher suite to use.
+ *
* @param credentialData is a CBOR-encoded structure containing metadata about the credential
* and an encrypted byte array that contains data used to secure the credential. See the
- * return argument of the same name in finishAddingEntries(), in IWritableIdentityCredential.
+ * return argument of the same name in finishAddingEntries(), in
+ * IWritableIdentityCredential.
*
- * @return result is OK on success or INVALID_DATA if the passed in credentialData
- * cannot be decoded or decrypted.
- *
- * @return credential is an IIdentityCredential HIDL interface that provides operations on the
- * Credential.
+ * @return an IIdentityCredential HIDL interface that provides operations on the Credential.
*/
- getCredential(vec<uint8_t> credentialData)
- generates (Result result, IIdentityCredential credential);
-};
+ IIdentityCredential getCredential(in CipherSuite cipherSuite, in byte[] credentialData);
+}
diff --git a/identity/1.0/IWritableIdentityCredential.hal b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
similarity index 75%
rename from identity/1.0/IWritableIdentityCredential.hal
rename to identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index f26f763..483b0c7 100644
--- a/identity/1.0/IWritableIdentityCredential.hal
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -14,12 +14,16 @@
* limitations under the License.
*/
-package android.hardware.identity@1.0;
+package android.hardware.identity;
+
+import android.hardware.identity.Certificate;
+import android.hardware.identity.SecureAccessControlProfile;
/**
* IWritableIdentityCredential is used to personalize a new identity credential. Credentials cannot
* be updated or modified after creation; any changes require deletion and re-creation.
*/
+@VintfStability
interface IWritableIdentityCredential {
/**
* Gets the certificate chain for credentialKey which can be used to prove the hardware
@@ -69,31 +73,23 @@
*
* @param attestationChallenge a challenge set by the issuer to ensure freshness.
*
- * @return result is OK on success, FAILED if an error occurred.
- *
- * @return certificateChain is the X.509 certificate chain for the credentialKey
+ * @return the X.509 certificate chain for the credentialKey
*/
- getAttestationCertificate(vec<uint8_t> attestationApplicationId,
- vec<uint8_t> attestationChallenge)
- generates(Result result, vec<vec<uint8_t>> certificateChain);
+ Certificate[] getAttestationCertificate(in byte[] attestationApplicationId, in byte[] attestationChallenge);
/**
* Start the personalization process.
*
* startPersonalization must not be called more than once.
*
- * @param accessControlProfileCount specifies the number of access control profiles that will be
- * provisioned with addAccessControlProfile().
+ * @param accessControlProfileCount specifies the number of access control profiles that will
+ * be provisioned with addAccessControlProfile().
*
* @param entryCounts specifies the number of data entries that will be provisioned with
* beginAddEntry() and addEntry(). Each item in the array specifies how many entries
* will be added for each name space.
- *
- * @return result is OK on success, FAILED if an error occurred.
- *
*/
- startPersonalization(uint16_t accessControlProfileCount, vec<uint16_t> entryCounts)
- generates(Result result);
+ void startPersonalization(in int accessControlProfileCount, in int[] entryCounts);
/**
* Add an access control profile, which defines the requirements or retrieval of one or more
@@ -103,15 +99,15 @@
*
* This method must be called exactly as many times as specified in the startPersonalization()
* accessControlProfileCount parameter. If this is requirement is not met, the method fails
- * with INVALID_DATA.
+ * with STATUS_INVALID_DATA.
*
* @param id a numeric identifier that must be unique within the context of a Credential and may
* be used to reference the profile. If this is not satisfied the call fails with
- * INVALID_DATA.
+ * STATUS_INVALID_DATA.
*
- * @param readerCertificate if non-empty, specifies a X.509 certificate (or chain of certificates)
- * that must be used to authenticate requests (see the readerSignature parameter in
- * IIdentityCredential.startRetrieval).
+ * @param readerCertificate if non-empty, specifies a X.509 certificate (or chain of
+ * certificates) that must be used to authenticate requests (see the readerSignature
+ * parameter in IIdentityCredential.startRetrieval).
*
* @param userAuthenticationRequired if true, specifies that the user is required to
* authenticate to allow requests. Required authentication freshness is specified by
@@ -121,22 +117,18 @@
* authentication (see userAuthenticationRequired above) is valid, if
* userAuthenticationRequired is true. If the timout is zero then authentication is
* required for each reader session. If userAuthenticationRequired is false, the timeout
- * must be zero. If this requirement is not met the call fails with INVALID_DATA.
+ * must be zero. If this requirement is not met the call fails with STATUS_INVALID_DATA.
*
* @param secureUserId must be non-zero if userAuthenticationRequired is true. It is not
* related to any Android user ID or UID, but is created in the Gatekeeper application
* in the secure environment. If this requirement is not met the call fails with
- * INVALID_DATA.
+ * STATUS_INVALID_DATA.
*
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
- *
- * @return secureAccessControlProfile is a structure with the passed-in data and MAC created
- * with storageKey for authenticating the data at a later point in time.
+ * @return a structure with the passed-in data and MAC created with storageKey for authenticating
+ * the data at a later point in time.
*/
- addAccessControlProfile(uint16_t id, vec<uint8_t> readerCertificate,
- bool userAuthenticationRequired, uint64_t timeoutMillis,
- uint64_t secureUserId)
- generates(Result result, SecureAccessControlProfile secureAccessControlProfile);
+ SecureAccessControlProfile addAccessControlProfile(in int id, in Certificate readerCertificate,
+ in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId);
/**
* Begins the process of adding an entry to the credential. All access control profiles must be
@@ -145,7 +137,7 @@
*
* This method must be called exactly as many times as the sum of the items in the entryCounts
* parameter specified in the startPersonalization(), and must be followed by one or more calls
- * to addEntryValue(). If this requirement is not met the method fails with INVALID_DATA.
+ * to addEntryValue(). If this requirement is not met the method fails with STATUS_INVALID_DATA.
*
* @param accessControlProfileIds specifies the set of access control profiles that can
* authorize access to the provisioned element.
@@ -155,13 +147,10 @@
* @param name is the name of the element.
*
* @param entrySize is the size of the entry value. If this requirement
- * is not met this method fails with INVALID_DATA.
- *
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
+ * is not met this method fails with STATUS_INVALID_DATA.
*/
- beginAddEntry(vec<uint16_t> accessControlProfileIds, string nameSpace,
- string name, uint32_t entrySize)
- generates(Result result);
+ void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace,
+ in @utf8InCpp String name, in int entrySize);
/**
* Continues the process of adding an entry, providing a value or part of a value.
@@ -171,18 +160,13 @@
* (see IIdentityCredentialStore.getHardwareInformation()), the caller must provide the
* value in chunks. All chunks must be exactly gcmChunkSize except the last and the sum of all
* chunk sizes must equal the value of the beginAddEntry() entrySize argument. If this
- * requirement is not met the call fails with INVALID_DATA.
+ * requirement is not met the call fails with STATUS_INVALID_DATA.
*
* @param content is the entry value, encoded as CBOR. In the case the content exceeds gcmChunkSize,
* this may be partial content up to gcmChunkSize bytes long.
*
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
- *
- * @return encryptedContent contains the encrypted and MACed content. For directly-available
- * credentials the contents are implementation-defined but must not exceed 32 bytes in
- * length.
- *
- * For other credentials, encryptedContent contains:
+ * @return the encrypted and MACed content. For directly-available credentials the contents are
+ * implementation-defined. For other credentials, the result contains
*
* AES-GCM-ENC(storageKey, R, Data, AdditionalData)
*
@@ -196,18 +180,15 @@
* "AccessControlProfileIds" : [ + uint ],
* }
*/
- addEntryValue(vec<uint8_t> content)
- generates(Result result, vec<uint8_t> encryptedContent);
+ byte[] addEntryValue(in byte[] content);
/**
- * Finishes adding entries and returns a signature that an issuing authority may use to validate
- * that all data was provisioned correctly.
+ * Finishes adding entries and returns a signature that an issuing authority may use to
+ * validate that all data was provisioned correctly.
*
* After this method is called, the IWritableIdentityCredential is no longer usable.
*
- * @return result is OK on success or FAILED if an error occurred.
- *
- * @return credentialData is a CBOR-encoded structure (in CDDL notation):
+ * @param out credentialData is a CBOR-encoded structure (in CDDL notation):
*
* CredentialData = [
* tstr, ; docType, an optional name that identifies the type of credential
@@ -230,10 +211,10 @@
* bstr ; credentialPrivKey, the private key for credentialKey
* ]
*
- * @return proofOfProvisioningSignature proves to the IA that the credential was imported into the
- * secure hardware without alteration or error. When the final addEntry() call is made
- * (when the number of provisioned entries equals the sum of the items in
- * startPersonalization() entryCounts parameter), it a COSE_Sign1 structure
+ * @param out proofOfProvisioningSignature proves to the IA that the credential was imported
+ * into the secure hardware without alteration or error. When the final addEntry() call is
+ * made (when the number of provisioned entries equals the sum of the items in
+ * startPersonalization() entryCounts parameter), a COSE_Sign1 structure
* signed by CredentialKey with payload set to the ProofOfProvisioning CBOR below:
*
* ProofOfProvisioning = [
@@ -266,7 +247,6 @@
* "accessControlProfiles" : [ * uint ],
* }
*/
- finishAddingEntries()
- generates(Result result, vec<uint8_t> credentialData,
- vec<uint8_t> proofOfProvisioningSignature);
-};
+ void finishAddingEntries(out byte[] credentialData,
+ out byte[] proofOfProvisioningSignature);
+}
diff --git a/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl b/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl
new file mode 100644
index 0000000..01d312d
--- /dev/null
+++ b/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.identity;
+
+import android.hardware.identity.Certificate;
+
+@VintfStability
+parcelable SecureAccessControlProfile {
+ /**
+ * id is a numeric identifier that must be unique within the context of a Credential and may be
+ * used to reference the profile.
+ */
+ int id;
+
+ /**
+ * readerCertificate, if non-empty, specifies a single X.509 certificate (not a chain
+ * of certificates) that must be used to authenticate requests. For details about how
+ * this is done, see the readerSignature paremter of IIdentityCredential.startRetrieval.
+ */
+ Certificate readerCertificate;
+
+ /**
+ * if true, the user is required to authenticate to allow requests. Required authentication
+ * fressness is specified by timeout below.
+ *
+ */
+ boolean userAuthenticationRequired;
+
+ /**
+ * Timeout specifies the amount of time, in milliseconds, for which a user authentication (see
+ * above) is valid, if userAuthenticationRequired is set to true. If userAuthenticationRequired
+ * is true and timout is zero then authentication is required for each reader session.
+ *
+ * If userAuthenticationRequired is false, timeout must be zero.
+ */
+ long timeoutMillis;
+
+ /**
+ * secureUserId must be non-zero if userAuthenticationRequired is true.
+ * It is not related to any Android user ID or UID, but is created in the
+ * Gatekeeper application in the secure environment.
+ */
+ long secureUserId;
+
+ /**
+ * The mac is used to authenticate the access control profile. It contains:
+ *
+ * AES-GCM-ENC(storageKey, R, {}, AccessControlProfile)
+ *
+ * where AccessControlProfile is the CBOR map:
+ *
+ * AccessControlProfile = {
+ * "id": uint,
+ * ? "readerCertificate" : bstr,
+ * ? (
+ * "userAuthenticationRequired" : bool,
+ * "timeoutMillis" : uint,
+ * "secureUserId" : uint
+ * )
+ * }
+ */
+ byte[] mac;
+}
+
diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp
new file mode 100644
index 0000000..2eb0faa
--- /dev/null
+++ b/identity/aidl/default/Android.bp
@@ -0,0 +1,29 @@
+cc_binary {
+ name: "android.hardware.identity-service.example",
+ relative_install_path: "hw",
+ init_rc: ["identity-default.rc"],
+ vintf_fragments: ["identity-default.xml"],
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcppbor",
+ "libcrypto",
+ "liblog",
+ "libutils",
+ "android.hardware.identity-support-lib",
+ "android.hardware.identity-ndk_platform",
+ "android.hardware.keymaster-ndk_platform",
+ ],
+ srcs: [
+ "IdentityCredential.cpp",
+ "IdentityCredentialStore.cpp",
+ "WritableIdentityCredential.cpp",
+ "Util.cpp",
+ "service.cpp",
+ ],
+}
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp
new file mode 100644
index 0000000..d5b3a0f
--- /dev/null
+++ b/identity/aidl/default/IdentityCredential.cpp
@@ -0,0 +1,768 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IdentityCredential"
+
+#include "IdentityCredential.h"
+#include "IdentityCredentialStore.h"
+#include "Util.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <string.h>
+
+#include <android-base/logging.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::aidl::android::hardware::keymaster::Timestamp;
+using ::std::optional;
+
+using namespace ::android::hardware::identity;
+
+int IdentityCredential::initialize() {
+ auto [item, _, message] = cppbor::parse(credentialData_);
+ if (item == nullptr) {
+ LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ const cppbor::Array* arrayItem = item->asArray();
+ if (arrayItem == nullptr || arrayItem->size() != 3) {
+ LOG(ERROR) << "CredentialData is not an array with three elements";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+ const cppbor::Bool* testCredentialItem =
+ ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
+ : nullptr);
+ const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+ if (docTypeItem == nullptr || testCredentialItem == nullptr ||
+ encryptedCredentialKeysItem == nullptr) {
+ LOG(ERROR) << "CredentialData unexpected item types";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ docType_ = docTypeItem->value();
+ testCredential_ = testCredentialItem->value();
+
+ vector<uint8_t> hardwareBoundKey;
+ if (testCredential_) {
+ hardwareBoundKey = support::getTestHardwareBoundKey();
+ } else {
+ hardwareBoundKey = getHardwareBoundKey();
+ }
+
+ const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
+ const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
+ optional<vector<uint8_t>> decryptedCredentialKeys =
+ support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
+ if (!decryptedCredentialKeys) {
+ LOG(ERROR) << "Error decrypting CredentialKeys";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
+ if (dckItem == nullptr) {
+ LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+ const cppbor::Array* dckArrayItem = dckItem->asArray();
+ if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
+ LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+ const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
+ const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
+ if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
+ LOG(ERROR) << "CredentialKeys unexpected item types";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+ storageKey_ = storageKeyItem->value();
+ credentialPrivKey_ = credentialPrivKeyItem->value();
+
+ return IIdentityCredentialStore::STATUS_OK;
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredential(
+ vector<int8_t>* outProofOfDeletionSignature) {
+ cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
+ vector<uint8_t> proofOfDeletion = array.encode();
+
+ optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
+ proofOfDeletion, // payload
+ {}, // additionalData
+ {}); // certificateChain
+ if (!signature) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+ }
+
+ *outProofOfDeletionSignature = byteStringToSigned(signature.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<int8_t>* outKeyPair) {
+ optional<vector<uint8_t>> kp = support::createEcKeyPair();
+ if (!kp) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key pair"));
+ }
+
+ // Stash public key of this key-pair for later check in startRetrieval().
+ optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(kp.value());
+ if (!publicKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public part of ephemeral key pair"));
+ }
+ ephemeralPublicKey_ = publicKey.value();
+
+ *outKeyPair = byteStringToSigned(kp.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
+ const vector<int8_t>& publicKey) {
+ readerPublicKey_ = byteStringToUnsigned(publicKey);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
+ uint64_t challenge = 0;
+ while (challenge == 0) {
+ optional<vector<uint8_t>> bytes = support::getRandom(8);
+ if (!bytes) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting random data for challenge"));
+ }
+
+ challenge = 0;
+ for (size_t n = 0; n < bytes.value().size(); n++) {
+ challenge |= ((bytes.value())[n] << (n * 8));
+ }
+ }
+
+ *outChallenge = challenge;
+ return ndk::ScopedAStatus::ok();
+}
+
+// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
+// ahead of time.
+bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
+ const vector<uint8_t>& readerCertificateChain) {
+ optional<vector<uint8_t>> acpPubKey = support::certificateChainGetTopMostKey(
+ byteStringToUnsigned(profile.readerCertificate.encodedCertificate));
+ if (!acpPubKey) {
+ LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
+ return false;
+ }
+
+ optional<vector<vector<uint8_t>>> certificatesInChain =
+ support::certificateChainSplit(readerCertificateChain);
+ if (!certificatesInChain) {
+ LOG(ERROR) << "Error splitting readerCertificateChain";
+ return false;
+ }
+ for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
+ optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
+ if (!certPubKey) {
+ LOG(ERROR)
+ << "Error extracting public key from certificate in chain presented by reader";
+ return false;
+ }
+ if (acpPubKey.value() == certPubKey.value()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+Timestamp clockGetTime() {
+ struct timespec time;
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ Timestamp ts;
+ ts.milliSeconds = time.tv_sec * 1000 + time.tv_nsec / 1000000;
+ return ts;
+}
+
+bool checkUserAuthentication(const SecureAccessControlProfile& profile,
+ const HardwareAuthToken& authToken, uint64_t authChallenge) {
+ if (profile.secureUserId != authToken.userId) {
+ LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
+ << ") differs from userId in authToken (" << authToken.userId << ")";
+ return false;
+ }
+
+ if (profile.timeoutMillis == 0) {
+ if (authToken.challenge == 0) {
+ LOG(ERROR) << "No challenge in authToken";
+ return false;
+ }
+
+ if (authToken.challenge != int64_t(authChallenge)) {
+ LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
+ return false;
+ }
+ return true;
+ }
+
+ // Note that the Epoch for timestamps in HardwareAuthToken is at the
+ // discretion of the vendor:
+ //
+ // "[...] since some starting point (generally the most recent device
+ // boot) which all of the applications within one secure environment
+ // must agree upon."
+ //
+ // Therefore, if this software implementation is used on a device which isn't
+ // the emulator then the assumption that the epoch is the same as used in
+ // clockGetTime above will not hold. This is OK as this software
+ // implementation should never be used on a real device.
+ //
+ Timestamp now = clockGetTime();
+ if (authToken.timestamp.milliSeconds > now.milliSeconds) {
+ LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp.milliSeconds
+ << ") is in the future (now: " << now.milliSeconds << ")";
+ return false;
+ }
+ if (now.milliSeconds > authToken.timestamp.milliSeconds + profile.timeoutMillis) {
+ LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp.milliSeconds << " + "
+ << profile.timeoutMillis << " = "
+ << (authToken.timestamp.milliSeconds + profile.timeoutMillis)
+ << ") is in the past (now: " << now.milliSeconds << ")";
+ return false;
+ }
+ return true;
+}
+
+ndk::ScopedAStatus IdentityCredential::startRetrieval(
+ const vector<SecureAccessControlProfile>& accessControlProfiles,
+ const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequestS,
+ const vector<int8_t>& sessionTranscriptS, const vector<int8_t>& readerSignatureS,
+ const vector<int32_t>& requestCounts) {
+ auto sessionTranscript = byteStringToUnsigned(sessionTranscriptS);
+ auto itemsRequest = byteStringToUnsigned(itemsRequestS);
+ auto readerSignature = byteStringToUnsigned(readerSignatureS);
+
+ if (sessionTranscript.size() > 0) {
+ auto [item, _, message] = cppbor::parse(sessionTranscript);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "SessionTranscript contains invalid CBOR"));
+ }
+ sessionTranscriptItem_ = std::move(item);
+ }
+ if (numStartRetrievalCalls_ > 0) {
+ if (sessionTranscript_ != sessionTranscript) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
+ "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
+ }
+ }
+ sessionTranscript_ = sessionTranscript;
+
+ // If there is a signature, validate that it was made with the top-most key in the
+ // certificate chain embedded in the COSE_Sign1 structure.
+ optional<vector<uint8_t>> readerCertificateChain;
+ if (readerSignature.size() > 0) {
+ readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
+ if (!readerCertificateChain) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Unable to get reader certificate chain from COSE_Sign1"));
+ }
+
+ if (!support::certificateChainValidate(readerCertificateChain.value())) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Error validating reader certificate chain"));
+ }
+
+ optional<vector<uint8_t>> readerPublicKey =
+ support::certificateChainGetTopMostKey(readerCertificateChain.value());
+ if (!readerPublicKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Unable to get public key from reader certificate chain"));
+ }
+
+ const vector<uint8_t>& itemsRequestBytes = itemsRequest;
+ vector<uint8_t> dataThatWasSigned = cppbor::Array()
+ .add("ReaderAuthentication")
+ .add(sessionTranscriptItem_->clone())
+ .add(cppbor::Semantic(24, itemsRequestBytes))
+ .encode();
+ if (!support::coseCheckEcDsaSignature(readerSignature,
+ dataThatWasSigned, // detached content
+ readerPublicKey.value())) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "readerSignature check failed"));
+ }
+ }
+
+ // Here's where we would validate the passed-in |authToken| to assure ourselves
+ // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
+ //
+ // However this involves calculating the MAC. However this requires access
+ // to the key needed to a pre-shared key which we don't have...
+ //
+
+ // To prevent replay-attacks, we check that the public part of the ephemeral
+ // key we previously created, is present in the DeviceEngagement part of
+ // SessionTranscript as a COSE_Key, in uncompressed form.
+ //
+ // We do this by just searching for the X and Y coordinates.
+ if (sessionTranscript.size() > 0) {
+ const cppbor::Array* array = sessionTranscriptItem_->asArray();
+ if (array == nullptr || array->size() != 2) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "SessionTranscript is not an array with two items"));
+ }
+ const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic();
+ if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "First item in SessionTranscript array is not a "
+ "semantic with value 24"));
+ }
+ const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr();
+ if (encodedDE == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "Child of semantic in first item in SessionTranscript "
+ "array is not a bstr"));
+ }
+ const vector<uint8_t>& bytesDE = encodedDE->value();
+
+ auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
+ if (!getXYSuccess) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "Error extracting X and Y from ePub"));
+ }
+ if (sessionTranscript.size() > 0 &&
+ !(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr &&
+ memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "Did not find ephemeral public key's X and Y coordinates in "
+ "SessionTranscript (make sure leading zeroes are not used)"));
+ }
+ }
+
+ // itemsRequest: If non-empty, contains request data that may be signed by the
+ // reader. The content can be defined in the way appropriate for the
+ // credential, but there are three requirements that must be met to work with
+ // this HAL:
+ if (itemsRequest.size() > 0) {
+ // 1. The content must be a CBOR-encoded structure.
+ auto [item, _, message] = cppbor::parse(itemsRequest);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Error decoding CBOR in itemsRequest"));
+ }
+
+ // 2. The CBOR structure must be a map.
+ const cppbor::Map* map = item->asMap();
+ if (map == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "itemsRequest is not a CBOR map"));
+ }
+
+ // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
+ // the example below.
+ //
+ // NameSpaces = {
+ // + NameSpace => DataElements ; Requested data elements for each NameSpace
+ // }
+ //
+ // NameSpace = tstr
+ //
+ // DataElements = {
+ // + DataElement => IntentToRetain
+ // }
+ //
+ // DataElement = tstr
+ // IntentToRetain = bool
+ //
+ // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
+ // through 3.:
+ //
+ // {
+ // 'docType' : 'org.iso.18013-5.2019',
+ // 'nameSpaces' : {
+ // 'org.iso.18013-5.2019' : {
+ // 'Last name' : false,
+ // 'Birth date' : false,
+ // 'First name' : false,
+ // 'Home address' : true
+ // },
+ // 'org.aamva.iso.18013-5.2019' : {
+ // 'Real Id' : false
+ // }
+ // }
+ // }
+ //
+ const cppbor::Map* nsMap = nullptr;
+ for (size_t n = 0; n < map->size(); n++) {
+ const auto& [keyItem, valueItem] = (*map)[n];
+ if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
+ valueItem->type() == cppbor::MAP) {
+ nsMap = valueItem->asMap();
+ break;
+ }
+ }
+ if (nsMap == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "No nameSpaces map in top-most map"));
+ }
+
+ for (size_t n = 0; n < nsMap->size(); n++) {
+ auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
+ const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
+ const cppbor::Map* nsInnerMap = nsValueItem->asMap();
+ if (nsKey == nullptr || nsInnerMap == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Type mismatch in nameSpaces map"));
+ }
+ string requestedNamespace = nsKey->value();
+ vector<string> requestedKeys;
+ for (size_t m = 0; m < nsInnerMap->size(); m++) {
+ const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
+ const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
+ const cppbor::Simple* simple = innerMapValueItem->asSimple();
+ const cppbor::Bool* intentToRetainItem =
+ (simple != nullptr) ? simple->asBool() : nullptr;
+ if (nameItem == nullptr || intentToRetainItem == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Type mismatch in value in nameSpaces map"));
+ }
+ requestedKeys.push_back(nameItem->value());
+ }
+ requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
+ }
+ }
+
+ // Finally, validate all the access control profiles in the requestData.
+ bool haveAuthToken = (authToken.mac.size() > 0);
+ for (const auto& profile : accessControlProfiles) {
+ if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Error checking MAC for profile"));
+ }
+ int accessControlCheck = IIdentityCredentialStore::STATUS_OK;
+ if (profile.userAuthenticationRequired) {
+ if (!haveAuthToken || !checkUserAuthentication(profile, authToken, authChallenge_)) {
+ accessControlCheck = IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED;
+ }
+ } else if (profile.readerCertificate.encodedCertificate.size() > 0) {
+ if (!readerCertificateChain ||
+ !checkReaderAuthentication(profile, readerCertificateChain.value())) {
+ accessControlCheck = IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED;
+ }
+ }
+ profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
+ }
+
+ deviceNameSpacesMap_ = cppbor::Map();
+ currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+ requestCountsRemaining_ = requestCounts;
+ currentNameSpace_ = "";
+
+ itemsRequest_ = itemsRequest;
+
+ numStartRetrievalCalls_ += 1;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
+ const string& nameSpace, const string& name, int32_t entrySize,
+ const vector<int32_t>& accessControlProfileIds) {
+ if (name.empty()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
+ }
+ if (nameSpace.empty()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Name space cannot be empty"));
+ }
+
+ if (requestCountsRemaining_.size() == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "No more name spaces left to go through"));
+ }
+
+ if (currentNameSpace_ == "") {
+ // First call.
+ currentNameSpace_ = nameSpace;
+ }
+
+ if (nameSpace == currentNameSpace_) {
+ // Same namespace.
+ if (requestCountsRemaining_[0] == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "No more entries to be retrieved in current name space"));
+ }
+ requestCountsRemaining_[0] -= 1;
+ } else {
+ // New namespace.
+ if (requestCountsRemaining_[0] != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Moved to new name space but one or more entries need to be retrieved "
+ "in current name space"));
+ }
+ if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+ deviceNameSpacesMap_.add(currentNameSpace_,
+ std::move(currentNameSpaceDeviceNameSpacesMap_));
+ }
+ currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+ requestCountsRemaining_.erase(requestCountsRemaining_.begin());
+ currentNameSpace_ = nameSpace;
+ }
+
+ // It's permissible to have an empty itemsRequest... but if non-empty you can
+ // only request what was specified in said itemsRequest. Enforce that.
+ if (itemsRequest_.size() > 0) {
+ const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
+ if (it == requestedNameSpacesAndNames_.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
+ "Name space was not requested in startRetrieval"));
+ }
+ const auto& dataItemNames = it->second;
+ if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
+ "Data item name in name space was not requested in startRetrieval"));
+ }
+ }
+
+ // Enforce access control.
+ //
+ // Access is granted if at least one of the profiles grants access.
+ //
+ // If an item is configured without any profiles, access is denied.
+ //
+ int accessControl = IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES;
+ for (auto id : accessControlProfileIds) {
+ auto search = profileIdToAccessCheckResult_.find(id);
+ if (search == profileIdToAccessCheckResult_.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Requested entry with unvalidated profile id"));
+ }
+ int accessControlForProfile = search->second;
+ if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
+ accessControl = IIdentityCredentialStore::STATUS_OK;
+ break;
+ }
+ accessControl = accessControlForProfile;
+ }
+ if (accessControl != IIdentityCredentialStore::STATUS_OK) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ int(accessControl), "Access control check failed"));
+ }
+
+ entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
+
+ currentName_ = name;
+ entryRemainingBytes_ = entrySize;
+ entryValue_.resize(0);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<int8_t>& encryptedContentS,
+ vector<int8_t>* outContent) {
+ auto encryptedContent = byteStringToUnsigned(encryptedContentS);
+
+ optional<vector<uint8_t>> content =
+ support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
+ if (!content) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data"));
+ }
+
+ size_t chunkSize = content.value().size();
+
+ if (chunkSize > entryRemainingBytes_) {
+ LOG(ERROR) << "Retrieved chunk of size " << chunkSize
+ << " is bigger than remaining space of size " << entryRemainingBytes_;
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved chunk is bigger than remaining space"));
+ }
+
+ entryRemainingBytes_ -= chunkSize;
+ if (entryRemainingBytes_ > 0) {
+ if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved non-final chunk of size which isn't kGcmChunkSize"));
+ }
+ }
+
+ entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
+
+ if (entryRemainingBytes_ == 0) {
+ auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
+ if (entryValueItem == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved data which is invalid CBOR"));
+ }
+ currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
+ }
+
+ *outContent = byteStringToSigned(content.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::finishRetrieval(const vector<int8_t>& signingKeyBlobS,
+ vector<int8_t>* outMac,
+ vector<int8_t>* outDeviceNameSpaces) {
+ auto signingKeyBlob = byteStringToUnsigned(signingKeyBlobS);
+
+ if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+ deviceNameSpacesMap_.add(currentNameSpace_,
+ std::move(currentNameSpaceDeviceNameSpacesMap_));
+ }
+ vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
+
+ // If there's no signing key or no sessionTranscript or no reader ephemeral
+ // public key, we return the empty MAC.
+ optional<vector<uint8_t>> mac;
+ if (signingKeyBlob.size() > 0 && sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0) {
+ cppbor::Array array;
+ array.add("DeviceAuthentication");
+ array.add(sessionTranscriptItem_->clone());
+ array.add(docType_);
+ array.add(cppbor::Semantic(24, encodedDeviceNameSpaces));
+ vector<uint8_t> encodedDeviceAuthentication = array.encode();
+
+ vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
+ optional<vector<uint8_t>> signingKey =
+ support::decryptAes128Gcm(storageKey_, signingKeyBlob, docTypeAsBlob);
+ if (!signingKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Error decrypting signingKeyBlob"));
+ }
+
+ optional<vector<uint8_t>> sharedSecret =
+ support::ecdh(readerPublicKey_, signingKey.value());
+ if (!sharedSecret) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error doing ECDH"));
+ }
+
+ vector<uint8_t> salt = {0x00};
+ vector<uint8_t> info = {};
+ optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
+ if (!derivedKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error deriving key from shared secret"));
+ }
+
+ mac = support::coseMac0(derivedKey.value(), {}, // payload
+ encodedDeviceAuthentication); // additionalData
+ if (!mac) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
+ }
+ }
+
+ *outMac = byteStringToSigned(mac.value_or(vector<uint8_t>({})));
+ *outDeviceNameSpaces = byteStringToSigned(encodedDeviceNameSpaces);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
+ vector<int8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
+ string serialDecimal = "0"; // TODO: set serial to something unique
+ string issuer = "Android Open Source Project";
+ string subject = "Android IdentityCredential Reference Implementation";
+ time_t validityNotBefore = time(nullptr);
+ time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+
+ optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
+ if (!signingKeyPKCS8) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
+ }
+
+ optional<vector<uint8_t>> signingPublicKey =
+ support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
+ if (!signingPublicKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public part of signingKey"));
+ }
+
+ optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
+ if (!signingKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting private part of signingKey"));
+ }
+
+ optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
+ signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ if (!certificate) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
+ }
+
+ optional<vector<uint8_t>> nonce = support::getRandom(12);
+ if (!nonce) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error getting random"));
+ }
+ vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
+ optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
+ storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
+ if (!encryptedSigningKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error encrypting signingKey"));
+ }
+ *outSigningKeyBlob = byteStringToSigned(encryptedSigningKey.value());
+ *outSigningKeyCertificate = Certificate();
+ outSigningKeyCertificate->encodedCertificate = byteStringToSigned(certificate.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/IdentityCredential.h b/identity/aidl/default/IdentityCredential.h
new file mode 100644
index 0000000..49ed0d4
--- /dev/null
+++ b/identity/aidl/default/IdentityCredential.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredential.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <cppbor/cppbor.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::aidl::android::hardware::keymaster::HardwareAuthToken;
+using ::std::map;
+using ::std::string;
+using ::std::vector;
+
+using MapStringToVectorOfStrings = map<string, vector<string>>;
+
+class IdentityCredential : public BnIdentityCredential {
+ public:
+ IdentityCredential(const vector<uint8_t>& credentialData)
+ : credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {}
+
+ // Parses and decrypts credentialData_, return a status code from
+ // IIdentityCredentialStore. Must be called right after construction.
+ int initialize();
+
+ // Methods from IIdentityCredential follow.
+ ndk::ScopedAStatus deleteCredential(vector<int8_t>* outProofOfDeletionSignature) override;
+ ndk::ScopedAStatus createEphemeralKeyPair(vector<int8_t>* outKeyPair) override;
+ ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<int8_t>& publicKey) override;
+ ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
+ ndk::ScopedAStatus startRetrieval(
+ const vector<SecureAccessControlProfile>& accessControlProfiles,
+ const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequest,
+ const vector<int8_t>& sessionTranscript, const vector<int8_t>& readerSignature,
+ const vector<int32_t>& requestCounts) override;
+ ndk::ScopedAStatus startRetrieveEntryValue(
+ const string& nameSpace, const string& name, int32_t entrySize,
+ const vector<int32_t>& accessControlProfileIds) override;
+ ndk::ScopedAStatus retrieveEntryValue(const vector<int8_t>& encryptedContent,
+ vector<int8_t>* outContent) override;
+ ndk::ScopedAStatus finishRetrieval(const vector<int8_t>& signingKeyBlob, vector<int8_t>* outMac,
+ vector<int8_t>* outDeviceNameSpaces) override;
+ ndk::ScopedAStatus generateSigningKeyPair(vector<int8_t>* outSigningKeyBlob,
+ Certificate* outSigningKeyCertificate) override;
+
+ private:
+ // Set by constructor
+ vector<uint8_t> credentialData_;
+ int numStartRetrievalCalls_;
+
+ // Set by initialize()
+ string docType_;
+ bool testCredential_;
+ vector<uint8_t> storageKey_;
+ vector<uint8_t> credentialPrivKey_;
+
+ // Set by createEphemeralKeyPair()
+ vector<uint8_t> ephemeralPublicKey_;
+
+ // Set by setReaderEphemeralPublicKey()
+ vector<uint8_t> readerPublicKey_;
+
+ // Set by createAuthChallenge()
+ uint64_t authChallenge_;
+
+ // Set at startRetrieval() time.
+ map<int32_t, int> profileIdToAccessCheckResult_;
+ vector<uint8_t> sessionTranscript_;
+ std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
+ vector<uint8_t> itemsRequest_;
+ vector<int32_t> requestCountsRemaining_;
+ MapStringToVectorOfStrings requestedNameSpacesAndNames_;
+ cppbor::Map deviceNameSpacesMap_;
+ cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
+
+ // Set at startRetrieveEntryValue() time.
+ string currentNameSpace_;
+ string currentName_;
+ size_t entryRemainingBytes_;
+ vector<uint8_t> entryValue_;
+ vector<uint8_t> entryAdditionalData_;
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
diff --git a/identity/aidl/default/IdentityCredentialStore.cpp b/identity/aidl/default/IdentityCredentialStore.cpp
new file mode 100644
index 0000000..1efb4b4
--- /dev/null
+++ b/identity/aidl/default/IdentityCredentialStore.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IdentityCredentialStore"
+
+#include <android-base/logging.h>
+
+#include "IdentityCredential.h"
+#include "IdentityCredentialStore.h"
+#include "WritableIdentityCredential.h"
+
+namespace aidl::android::hardware::identity {
+
+ndk::ScopedAStatus IdentityCredentialStore::getHardwareInformation(
+ HardwareInformation* hardwareInformation) {
+ HardwareInformation hw;
+ hw.credentialStoreName = "Identity Credential Reference Implementation";
+ hw.credentialStoreAuthorName = "Google";
+ hw.dataChunkSize = kGcmChunkSize;
+ hw.isDirectAccess = false;
+ hw.supportedDocTypes = {};
+ *hardwareInformation = hw;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredentialStore::createCredential(
+ const string& docType, bool testCredential,
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
+ shared_ptr<WritableIdentityCredential> wc =
+ ndk::SharedRefBase::make<WritableIdentityCredential>(docType, testCredential);
+ if (!wc->initialize()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error initializing WritableIdentityCredential"));
+ }
+ *outWritableCredential = wc;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredentialStore::getCredential(
+ CipherSuite cipherSuite, const vector<int8_t>& credentialData,
+ shared_ptr<IIdentityCredential>* outCredential) {
+ // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
+ if (cipherSuite != CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED,
+ "Unsupported cipher suite"));
+ }
+
+ vector<uint8_t> data = vector<uint8_t>(credentialData.begin(), credentialData.end());
+ shared_ptr<IdentityCredential> credential = ndk::SharedRefBase::make<IdentityCredential>(data);
+ auto ret = credential->initialize();
+ if (ret != IIdentityCredentialStore::STATUS_OK) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ int(ret), "Error initializing IdentityCredential"));
+ }
+ *outCredential = credential;
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/IdentityCredentialStore.h b/identity/aidl/default/IdentityCredentialStore.h
new file mode 100644
index 0000000..a205113
--- /dev/null
+++ b/identity/aidl/default/IdentityCredentialStore.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredentialStore.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::std::shared_ptr;
+using ::std::string;
+using ::std::vector;
+
+class IdentityCredentialStore : public BnIdentityCredentialStore {
+ public:
+ IdentityCredentialStore() {}
+
+ // The GCM chunk size used by this implementation is 64 KiB.
+ static constexpr size_t kGcmChunkSize = 64 * 1024;
+
+ // Methods from IIdentityCredentialStore follow.
+ ndk::ScopedAStatus getHardwareInformation(HardwareInformation* hardwareInformation) override;
+
+ ndk::ScopedAStatus createCredential(
+ const string& docType, bool testCredential,
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
+
+ ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector<int8_t>& credentialData,
+ shared_ptr<IIdentityCredential>* outCredential) override;
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
diff --git a/identity/aidl/default/Util.cpp b/identity/aidl/default/Util.cpp
new file mode 100644
index 0000000..a0f86be
--- /dev/null
+++ b/identity/aidl/default/Util.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Util"
+
+#include "Util.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <string.h>
+
+#include <android-base/logging.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+namespace aidl::android::hardware::identity {
+
+using namespace ::android::hardware::identity;
+
+// This is not a very random HBK but that's OK because this is the SW
+// implementation where it can't be kept secret.
+vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+const vector<uint8_t>& getHardwareBoundKey() {
+ return hardwareBoundKey;
+}
+
+vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value) {
+ return vector<uint8_t>(value.begin(), value.end());
+}
+
+vector<int8_t> byteStringToSigned(const vector<uint8_t>& value) {
+ return vector<int8_t>(value.begin(), value.end());
+}
+
+vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
+ cppbor::Map map;
+ map.add("id", profile.id);
+
+ if (profile.readerCertificate.encodedCertificate.size() > 0) {
+ map.add("readerCertificate",
+ cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
+ }
+
+ if (profile.userAuthenticationRequired) {
+ map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
+ map.add("timeoutMillis", profile.timeoutMillis);
+ map.add("secureUserId", profile.secureUserId);
+ }
+
+ return map.encode();
+}
+
+optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
+ const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
+ vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
+
+ optional<vector<uint8_t>> nonce = support::getRandom(12);
+ if (!nonce) {
+ return {};
+ }
+ optional<vector<uint8_t>> macO =
+ support::encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
+ if (!macO) {
+ return {};
+ }
+ return macO.value();
+}
+
+bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
+ const vector<uint8_t>& storageKey) {
+ vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
+
+ if (profile.mac.size() < support::kAesGcmIvSize) {
+ return false;
+ }
+ vector<uint8_t> nonce =
+ vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + support::kAesGcmIvSize);
+ optional<vector<uint8_t>> mac = support::encryptAes128Gcm(storageKey, nonce, {}, cborData);
+ if (!mac) {
+ return false;
+ }
+ if (mac.value() != byteStringToUnsigned(profile.mac)) {
+ return false;
+ }
+ return true;
+}
+
+vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
+ const vector<int32_t> accessControlProfileIds) {
+ cppbor::Map map;
+ map.add("Namespace", nameSpace);
+ map.add("Name", name);
+
+ cppbor::Array acpIds;
+ for (auto id : accessControlProfileIds) {
+ acpIds.add(id);
+ }
+ map.add("AccessControlProfileIds", std::move(acpIds));
+ return map.encode();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/Util.h b/identity/aidl/default/Util.h
new file mode 100644
index 0000000..ee41ad1
--- /dev/null
+++ b/identity/aidl/default/Util.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_UTIL_H
+#define ANDROID_HARDWARE_IDENTITY_UTIL_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredential.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <map>
+#include <optional>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <cppbor/cppbor.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::std::optional;
+using ::std::string;
+using ::std::vector;
+
+// Returns the hardware-bound AES-128 key.
+const vector<uint8_t>& getHardwareBoundKey();
+
+// Calculates the MAC for |profile| using |storageKey|.
+optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
+ const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
+
+// Checks authenticity of the MAC in |profile| using |storageKey|.
+bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
+ const vector<uint8_t>& storageKey);
+
+// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
+vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
+ const vector<int32_t> accessControlProfileIds);
+
+vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value);
+
+vector<int8_t> byteStringToSigned(const vector<uint8_t>& value);
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_UTIL_H
diff --git a/identity/1.0/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
similarity index 61%
rename from identity/1.0/default/WritableIdentityCredential.cpp
rename to identity/aidl/default/WritableIdentityCredential.cpp
index 4c39f85..ba2062d 100644
--- a/identity/1.0/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/WritableIdentityCredential.cpp
@@ -16,9 +16,6 @@
#define LOG_TAG "WritableIdentityCredential"
-#include "WritableIdentityCredential.h"
-#include "IdentityCredentialStore.h"
-
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
#include <android-base/logging.h>
@@ -26,12 +23,315 @@
#include <cppbor/cppbor.h>
#include <cppbor/cppbor_parse.h>
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
+#include "IdentityCredentialStore.h"
+#include "Util.h"
+#include "WritableIdentityCredential.h"
+
+namespace aidl::android::hardware::identity {
using ::std::optional;
+using namespace ::android::hardware::identity;
+
+bool WritableIdentityCredential::initialize() {
+ optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+ if (!keyPair) {
+ LOG(ERROR) << "Error creating credentialKey";
+ return false;
+ }
+
+ optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
+ if (!pubKey) {
+ LOG(ERROR) << "Error getting public part of credentialKey";
+ return false;
+ }
+ credentialPubKey_ = pubKey.value();
+
+ optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+ if (!privKey) {
+ LOG(ERROR) << "Error getting private part of credentialKey";
+ return false;
+ }
+ credentialPrivKey_ = privKey.value();
+
+ optional<vector<uint8_t>> random = support::getRandom(16);
+ if (!random) {
+ LOG(ERROR) << "Error creating storageKey";
+ return false;
+ }
+ storageKey_ = random.value();
+
+ return true;
+}
+
+// TODO: use |attestationApplicationId| and |attestationChallenge| and also
+// ensure the returned certificate chain satisfy the requirements listed in
+// the docs for IWritableIdentityCredential::getAttestationCertificate()
+//
+ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
+ const vector<int8_t>& /*attestationApplicationId*/,
+ const vector<int8_t>& /*attestationChallenge*/, vector<Certificate>* outCertificateChain) {
+ // For now, we dynamically generate an attestion key on each and every
+ // request and use that to sign CredentialKey. In a real implementation this
+ // would look very differently.
+ optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
+ if (!attestationKeyPair) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating attestationKey"));
+ }
+
+ optional<vector<uint8_t>> attestationPubKey =
+ support::ecKeyPairGetPublicKey(attestationKeyPair.value());
+ if (!attestationPubKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public part of attestationKey"));
+ }
+
+ optional<vector<uint8_t>> attestationPrivKey =
+ support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
+ if (!attestationPrivKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting private part of attestationKey"));
+ }
+
+ string serialDecimal;
+ string issuer;
+ string subject;
+ time_t validityNotBefore = time(nullptr);
+ time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+
+ // First create a certificate for |credentialPubKey| which is signed by
+ // |attestationPrivKey|.
+ //
+ serialDecimal = "0"; // TODO: set serial to |attestationChallenge|
+ issuer = "Android Open Source Project";
+ subject = "Android IdentityCredential CredentialKey";
+ optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
+ credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ if (!credentialPubKeyCertificate) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error creating certificate for credentialPubKey"));
+ }
+
+ // This is followed by a certificate for |attestationPubKey| self-signed by
+ // |attestationPrivKey|.
+ serialDecimal = "0"; // TODO: set serial
+ issuer = "Android Open Source Project";
+ subject = "Android IdentityCredential AttestationKey";
+ optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
+ attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ if (!attestationKeyCertificate) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error creating certificate for attestationPubKey"));
+ }
+
+ // Concatenate the certificates to form the chain.
+ vector<uint8_t> certificateChain;
+ certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
+ credentialPubKeyCertificate.value().end());
+ certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
+ attestationKeyCertificate.value().end());
+
+ optional<vector<vector<uint8_t>>> splitCertChain =
+ support::certificateChainSplit(certificateChain);
+ if (!splitCertChain) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error splitting certificate chain"));
+ }
+ *outCertificateChain = vector<Certificate>();
+ for (const vector<uint8_t>& cert : splitCertChain.value()) {
+ Certificate c = Certificate();
+ c.encodedCertificate = byteStringToSigned(cert);
+ outCertificateChain->push_back(std::move(c));
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
+ int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
+ numAccessControlProfileRemaining_ = accessControlProfileCount;
+ remainingEntryCounts_ = entryCounts;
+ entryNameSpace_ = "";
+
+ signedDataAccessControlProfiles_ = cppbor::Array();
+ signedDataNamespaces_ = cppbor::Map();
+ signedDataCurrentNamespace_ = cppbor::Array();
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
+ int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
+ int64_t timeoutMillis, int64_t secureUserId,
+ SecureAccessControlProfile* outSecureAccessControlProfile) {
+ SecureAccessControlProfile profile;
+
+ if (numAccessControlProfileRemaining_ == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
+ }
+
+ // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
+ // be zero.
+ if (!userAuthenticationRequired && timeoutMillis != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "userAuthenticationRequired is false but timeout is non-zero"));
+ }
+
+ profile.id = id;
+ profile.readerCertificate = readerCertificate;
+ profile.userAuthenticationRequired = userAuthenticationRequired;
+ profile.timeoutMillis = timeoutMillis;
+ profile.secureUserId = secureUserId;
+ optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_);
+ if (!mac) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile"));
+ }
+ profile.mac = byteStringToSigned(mac.value());
+
+ cppbor::Map profileMap;
+ profileMap.add("id", profile.id);
+ if (profile.readerCertificate.encodedCertificate.size() > 0) {
+ profileMap.add(
+ "readerCertificate",
+ cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
+ }
+ if (profile.userAuthenticationRequired) {
+ profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
+ profileMap.add("timeoutMillis", profile.timeoutMillis);
+ }
+ signedDataAccessControlProfiles_.add(std::move(profileMap));
+
+ numAccessControlProfileRemaining_--;
+
+ *outSecureAccessControlProfile = profile;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
+ const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
+ int32_t entrySize) {
+ if (numAccessControlProfileRemaining_ != 0) {
+ LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
+ << " and expected zero";
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "numAccessControlProfileRemaining_ is not zero"));
+ }
+
+ if (remainingEntryCounts_.size() == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
+ }
+
+ // Handle initial beginEntry() call.
+ if (entryNameSpace_ == "") {
+ entryNameSpace_ = nameSpace;
+ }
+
+ // If the namespace changed...
+ if (nameSpace != entryNameSpace_) {
+ // Then check that all entries in the previous namespace have been added..
+ if (remainingEntryCounts_[0] != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "New namespace but a non-zero number of entries remain to be added"));
+ }
+ remainingEntryCounts_.erase(remainingEntryCounts_.begin());
+
+ if (signedDataCurrentNamespace_.size() > 0) {
+ signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
+ signedDataCurrentNamespace_ = cppbor::Array();
+ }
+ } else {
+ // Same namespace...
+ if (remainingEntryCounts_[0] == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Same namespace but no entries remain to be added"));
+ }
+ remainingEntryCounts_[0] -= 1;
+ }
+
+ entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
+
+ entryRemainingBytes_ = entrySize;
+ entryNameSpace_ = nameSpace;
+ entryName_ = name;
+ entryAccessControlProfileIds_ = accessControlProfileIds;
+ entryBytes_.resize(0);
+ // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<int8_t>& contentS,
+ vector<int8_t>* outEncryptedContent) {
+ auto content = byteStringToUnsigned(contentS);
+ size_t contentSize = content.size();
+
+ if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Passed in chunk of is bigger than kGcmChunkSize"));
+ }
+ if (contentSize > entryRemainingBytes_) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Passed in chunk is bigger than remaining space"));
+ }
+
+ entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
+ entryRemainingBytes_ -= contentSize;
+ if (entryRemainingBytes_ > 0) {
+ if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved non-final chunk which isn't kGcmChunkSize"));
+ }
+ }
+
+ optional<vector<uint8_t>> nonce = support::getRandom(12);
+ if (!nonce) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
+ }
+ optional<vector<uint8_t>> encryptedContent =
+ support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
+ if (!encryptedContent) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
+ }
+
+ if (entryRemainingBytes_ == 0) {
+ // TODO: ideally do do this without parsing the data (but still validate data is valid
+ // CBOR).
+ auto [item, _, message] = cppbor::parse(entryBytes_);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
+ }
+ cppbor::Map entryMap;
+ entryMap.add("name", entryName_);
+ entryMap.add("value", std::move(item));
+ cppbor::Array profileIdArray;
+ for (auto id : entryAccessControlProfileIds_) {
+ profileIdArray.add(id);
+ }
+ entryMap.add("accessControlProfiles", std::move(profileIdArray));
+ signedDataCurrentNamespace_.add(std::move(entryMap));
+ }
+
+ *outEncryptedContent = byteStringToSigned(encryptedContent.value());
+ return ndk::ScopedAStatus::ok();
+}
// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
// |credentialPrivKey|.
@@ -77,325 +377,8 @@
return true;
}
-bool WritableIdentityCredential::initialize() {
- optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
- if (!keyPair) {
- LOG(ERROR) << "Error creating credentialKey";
- return false;
- }
-
- optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
- if (!pubKey) {
- LOG(ERROR) << "Error getting public part of credentialKey";
- return false;
- }
- credentialPubKey_ = pubKey.value();
-
- optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
- if (!privKey) {
- LOG(ERROR) << "Error getting private part of credentialKey";
- return false;
- }
- credentialPrivKey_ = privKey.value();
-
- optional<vector<uint8_t>> random = support::getRandom(16);
- if (!random) {
- LOG(ERROR) << "Error creating storageKey";
- return false;
- }
- storageKey_ = random.value();
-
- return true;
-}
-
-// TODO: use |attestationApplicationId| and |attestationChallenge| and also
-// ensure the returned certificate chain satisfy the requirements listed in
-// the docs for IWritableIdentityCredential::getAttestationCertificate()
-//
-Return<void> WritableIdentityCredential::getAttestationCertificate(
- const hidl_vec<uint8_t>& /* attestationApplicationId */,
- const hidl_vec<uint8_t>& /* attestationChallenge */,
- getAttestationCertificate_cb _hidl_cb) {
- // For now, we dynamically generate an attestion key on each and every
- // request and use that to sign CredentialKey. In a real implementation this
- // would look very differently.
- optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
- if (!attestationKeyPair) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating attestationKey"), {});
- return Void();
- }
-
- optional<vector<uint8_t>> attestationPubKey =
- support::ecKeyPairGetPublicKey(attestationKeyPair.value());
- if (!attestationPubKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of attestationKey"),
- {});
- return Void();
- }
-
- optional<vector<uint8_t>> attestationPrivKey =
- support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
- if (!attestationPrivKey) {
- _hidl_cb(
- support::result(ResultCode::FAILED, "Error getting private part of attestationKey"),
- {});
- return Void();
- }
-
- string serialDecimal;
- string issuer;
- string subject;
- time_t validityNotBefore = time(nullptr);
- time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
-
- // First create a certificate for |credentialPubKey| which is signed by
- // |attestationPrivKey|.
- //
- serialDecimal = "0"; // TODO: set serial to |attestationChallenge|
- issuer = "Android Open Source Project";
- subject = "Android IdentityCredential CredentialKey";
- optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
- credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- if (!credentialPubKeyCertificate) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error creating certificate for credentialPubKey"),
- {});
- return Void();
- }
-
- // This is followed by a certificate for |attestationPubKey| self-signed by
- // |attestationPrivKey|.
- serialDecimal = "0"; // TODO: set serial
- issuer = "Android Open Source Project";
- subject = "Android IdentityCredential AttestationKey";
- optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
- attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- if (!attestationKeyCertificate) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error creating certificate for attestationPubKey"),
- {});
- return Void();
- }
-
- // Concatenate the certificates to form the chain.
- vector<uint8_t> certificateChain;
- certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
- credentialPubKeyCertificate.value().end());
- certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
- attestationKeyCertificate.value().end());
-
- optional<vector<vector<uint8_t>>> splitCertChain =
- support::certificateChainSplit(certificateChain);
- if (!splitCertChain) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error splitting certificate chain"), {});
- return Void();
- }
- hidl_vec<hidl_vec<uint8_t>> ret;
- ret.resize(splitCertChain.value().size());
- std::copy(splitCertChain.value().begin(), splitCertChain.value().end(), ret.begin());
- _hidl_cb(support::resultOK(), ret);
- return Void();
-}
-
-Return<void> WritableIdentityCredential::startPersonalization(uint16_t accessControlProfileCount,
- const hidl_vec<uint16_t>& entryCounts,
- startPersonalization_cb _hidl_cb) {
- numAccessControlProfileRemaining_ = accessControlProfileCount;
- remainingEntryCounts_ = entryCounts;
- entryNameSpace_ = "";
-
- signedDataAccessControlProfiles_ = cppbor::Array();
- signedDataNamespaces_ = cppbor::Map();
- signedDataCurrentNamespace_ = cppbor::Array();
-
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> WritableIdentityCredential::addAccessControlProfile(
- uint16_t id, const hidl_vec<uint8_t>& readerCertificate, bool userAuthenticationRequired,
- uint64_t timeoutMillis, uint64_t secureUserId, addAccessControlProfile_cb _hidl_cb) {
- SecureAccessControlProfile profile;
-
- if (numAccessControlProfileRemaining_ == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "numAccessControlProfileRemaining_ is 0 and expected non-zero"),
- profile);
- return Void();
- }
-
- // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
- // be zero.
- if (!userAuthenticationRequired && timeoutMillis != 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "userAuthenticationRequired is false but timeout is non-zero"),
- profile);
- return Void();
- }
-
- profile.id = id;
- profile.readerCertificate = readerCertificate;
- profile.userAuthenticationRequired = userAuthenticationRequired;
- profile.timeoutMillis = timeoutMillis;
- profile.secureUserId = secureUserId;
- optional<vector<uint8_t>> mac =
- support::secureAccessControlProfileCalcMac(profile, storageKey_);
- if (!mac) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error calculating MAC for profile"), profile);
- return Void();
- }
- profile.mac = mac.value();
-
- cppbor::Map profileMap;
- profileMap.add("id", profile.id);
- if (profile.readerCertificate.size() > 0) {
- profileMap.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
- }
- if (profile.userAuthenticationRequired) {
- profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
- profileMap.add("timeoutMillis", profile.timeoutMillis);
- }
- signedDataAccessControlProfiles_.add(std::move(profileMap));
-
- numAccessControlProfileRemaining_--;
-
- _hidl_cb(support::resultOK(), profile);
- return Void();
-}
-
-Return<void> WritableIdentityCredential::beginAddEntry(
- const hidl_vec<uint16_t>& accessControlProfileIds, const hidl_string& nameSpace,
- const hidl_string& name, uint32_t entrySize, beginAddEntry_cb _hidl_cb) {
- if (numAccessControlProfileRemaining_ != 0) {
- LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
- << " and expected zero";
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "numAccessControlProfileRemaining_ is %zd and expected zero",
- numAccessControlProfileRemaining_));
- return Void();
- }
-
- if (remainingEntryCounts_.size() == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "No more namespaces to add to"));
- return Void();
- }
-
- // Handle initial beginEntry() call.
- if (entryNameSpace_ == "") {
- entryNameSpace_ = nameSpace;
- }
-
- // If the namespace changed...
- if (nameSpace != entryNameSpace_) {
- // Then check that all entries in the previous namespace have been added..
- if (remainingEntryCounts_[0] != 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "New namespace but %d entries remain to be added",
- int(remainingEntryCounts_[0])));
- return Void();
- }
- remainingEntryCounts_.erase(remainingEntryCounts_.begin());
-
- if (signedDataCurrentNamespace_.size() > 0) {
- signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
- signedDataCurrentNamespace_ = cppbor::Array();
- }
- } else {
- // Same namespace...
- if (remainingEntryCounts_[0] == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Same namespace but no entries remain to be added"));
- return Void();
- }
- remainingEntryCounts_[0] -= 1;
- }
-
- entryAdditionalData_ =
- support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
-
- entryRemainingBytes_ = entrySize;
- entryNameSpace_ = nameSpace;
- entryName_ = name;
- entryAccessControlProfileIds_ = accessControlProfileIds;
- entryBytes_.resize(0);
- // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
-
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> WritableIdentityCredential::addEntryValue(const hidl_vec<uint8_t>& content,
- addEntryValue_cb _hidl_cb) {
- size_t contentSize = content.size();
-
- if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
- _hidl_cb(support::result(
- ResultCode::INVALID_DATA,
- "Passed in chunk of size %zd is bigger than kGcmChunkSize which is %zd",
- contentSize, IdentityCredentialStore::kGcmChunkSize),
- {});
- return Void();
- }
- if (contentSize > entryRemainingBytes_) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Passed in chunk of size %zd is bigger than remaining space "
- "of size %zd",
- contentSize, entryRemainingBytes_),
- {});
- return Void();
- }
-
- entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
- entryRemainingBytes_ -= contentSize;
- if (entryRemainingBytes_ > 0) {
- if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Retrieved non-final chunk of size %zd but expected "
- "kGcmChunkSize which is %zd",
- contentSize, IdentityCredentialStore::kGcmChunkSize),
- {});
- return Void();
- }
- }
-
- optional<vector<uint8_t>> nonce = support::getRandom(12);
- if (!nonce) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting nonce"), {});
- return Void();
- }
- optional<vector<uint8_t>> encryptedContent =
- support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
- if (!encryptedContent) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting content"), {});
- return Void();
- }
-
- if (entryRemainingBytes_ == 0) {
- // TODO: ideally do do this without parsing the data (but still validate data is valid
- // CBOR).
- auto [item, _, message] = cppbor::parse(entryBytes_);
- if (item == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Data is not valid CBOR"), {});
- return Void();
- }
- cppbor::Map entryMap;
- entryMap.add("name", entryName_);
- entryMap.add("value", std::move(item));
- cppbor::Array profileIdArray;
- for (auto id : entryAccessControlProfileIds_) {
- profileIdArray.add(id);
- }
- entryMap.add("accessControlProfiles", std::move(profileIdArray));
- signedDataCurrentNamespace_.add(std::move(entryMap));
- }
-
- _hidl_cb(support::resultOK(), encryptedContent.value());
- return Void();
-}
-
-Return<void> WritableIdentityCredential::finishAddingEntries(finishAddingEntries_cb _hidl_cb) {
+ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
+ vector<int8_t>* outCredentialData, vector<int8_t>* outProofOfProvisioningSignature) {
if (signedDataCurrentNamespace_.size() > 0) {
signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
}
@@ -412,29 +395,27 @@
{}, // additionalData
{}); // certificateChain
if (!signature) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {}, {});
- return Void();
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
}
vector<uint8_t> credentialKeys;
if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialKeys"), {}, {});
- return Void();
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
}
vector<uint8_t> credentialData;
- if (!generateCredentialData(testCredential_ ? support::getTestHardwareBoundKey()
- : support::getHardwareBoundKey(),
- docType_, testCredential_, credentialKeys, credentialData)) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialData"), {}, {});
- return Void();
+ if (!generateCredentialData(
+ testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
+ docType_, testCredential_, credentialKeys, credentialData)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
}
- _hidl_cb(support::resultOK(), credentialData, signature.value());
- return Void();
+ *outCredentialData = byteStringToSigned(credentialData);
+ *outProofOfProvisioningSignature = byteStringToSigned(signature.value());
+ return ndk::ScopedAStatus::ok();
}
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/WritableIdentityCredential.h b/identity/aidl/default/WritableIdentityCredential.h
new file mode 100644
index 0000000..b380f89
--- /dev/null
+++ b/identity/aidl/default/WritableIdentityCredential.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+
+#include <aidl/android/hardware/identity/BnWritableIdentityCredential.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <cppbor.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::std::string;
+using ::std::vector;
+
+class WritableIdentityCredential : public BnWritableIdentityCredential {
+ public:
+ WritableIdentityCredential(const string& docType, bool testCredential)
+ : docType_(docType), testCredential_(testCredential) {}
+
+ // Creates the Credential Key. Returns false on failure. Must be called
+ // right after construction.
+ bool initialize();
+
+ // Methods from IWritableIdentityCredential follow.
+ ndk::ScopedAStatus getAttestationCertificate(const vector<int8_t>& attestationApplicationId,
+ const vector<int8_t>& attestationChallenge,
+ vector<Certificate>* outCertificateChain) override;
+
+ ndk::ScopedAStatus startPersonalization(int32_t accessControlProfileCount,
+ const vector<int32_t>& entryCounts) override;
+
+ ndk::ScopedAStatus addAccessControlProfile(
+ int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
+ int64_t timeoutMillis, int64_t secureUserId,
+ SecureAccessControlProfile* outSecureAccessControlProfile) override;
+
+ ndk::ScopedAStatus beginAddEntry(const vector<int32_t>& accessControlProfileIds,
+ const string& nameSpace, const string& name,
+ int32_t entrySize) override;
+
+ ndk::ScopedAStatus addEntryValue(const vector<int8_t>& content,
+ vector<int8_t>* outEncryptedContent) override;
+
+ ndk::ScopedAStatus finishAddingEntries(
+ vector<int8_t>* outCredentialData,
+ vector<int8_t>* outProofOfProvisioningSignature) override;
+
+ // private:
+ string docType_;
+ bool testCredential_;
+
+ // These are set in initialize().
+ vector<uint8_t> storageKey_;
+ vector<uint8_t> credentialPrivKey_;
+ vector<uint8_t> credentialPubKey_;
+
+ // These fields are initialized during startPersonalization()
+ size_t numAccessControlProfileRemaining_;
+ vector<int32_t> remainingEntryCounts_;
+ cppbor::Array signedDataAccessControlProfiles_;
+ cppbor::Map signedDataNamespaces_;
+ cppbor::Array signedDataCurrentNamespace_;
+
+ // These fields are initialized during beginAddEntry()
+ size_t entryRemainingBytes_;
+ vector<uint8_t> entryAdditionalData_;
+ string entryNameSpace_;
+ string entryName_;
+ vector<int32_t> entryAccessControlProfileIds_;
+ vector<uint8_t> entryBytes_;
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
diff --git a/identity/aidl/default/identity-default.rc b/identity/aidl/default/identity-default.rc
new file mode 100644
index 0000000..d3b62c1
--- /dev/null
+++ b/identity/aidl/default/identity-default.rc
@@ -0,0 +1,3 @@
+service vendor.identity-default /vendor/bin/hw/android.hardware.identity-service.example
+ class hal
+ user nobody
diff --git a/identity/aidl/default/identity-default.xml b/identity/aidl/default/identity-default.xml
new file mode 100644
index 0000000..a47d354
--- /dev/null
+++ b/identity/aidl/default/identity-default.xml
@@ -0,0 +1,9 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.identity</name>
+ <interface>
+ <name>IIdentityCredentialStore</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/identity/aidl/default/service.cpp b/identity/aidl/default/service.cpp
new file mode 100644
index 0000000..f05c615
--- /dev/null
+++ b/identity/aidl/default/service.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.identity-service"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "IdentityCredentialStore.h"
+
+using aidl::android::hardware::identity::IdentityCredentialStore;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<IdentityCredentialStore> store =
+ ndk::SharedRefBase::make<IdentityCredentialStore>();
+
+ const std::string instance = std::string() + IdentityCredentialStore::descriptor + "/default";
+ LOG(INFO) << "instance: " << instance;
+ binder_status_t status = AServiceManager_addService(store->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
new file mode 100644
index 0000000..21ff440
--- /dev/null
+++ b/identity/aidl/vts/Android.bp
@@ -0,0 +1,21 @@
+cc_test {
+ name: "VtsHalIdentityTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["VtsHalIdentityTargetTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libcppbor",
+ "android.hardware.identity-support-lib",
+ ],
+ static_libs: [
+ "android.hardware.identity-cpp",
+ "android.hardware.keymaster-cpp",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
+}
diff --git a/identity/aidl/vts/VtsHalIdentityTargetTest.cpp b/identity/aidl/vts/VtsHalIdentityTargetTest.cpp
new file mode 100644
index 0000000..5abe5a2
--- /dev/null
+++ b/identity/aidl/vts/VtsHalIdentityTargetTest.cpp
@@ -0,0 +1,446 @@
+/*
+ * 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 "VtsHalIdentityTargetTest"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+
+namespace android::hardware::identity {
+
+using std::map;
+using std::optional;
+using std::string;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+
+// ---------------------------------------------------------------------------
+// Test Data.
+// ---------------------------------------------------------------------------
+
+struct TestEntryData {
+ TestEntryData(string nameSpace, string name, vector<int32_t> profileIds)
+ : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
+
+ TestEntryData(string nameSpace, string name, const string& value, vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
+ }
+ TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
+ vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ valueCbor = cppbor::Bstr(value).encode();
+ }
+ TestEntryData(string nameSpace, string name, bool value, vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ valueCbor = cppbor::Bool(value).encode();
+ }
+ TestEntryData(string nameSpace, string name, int64_t value, vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ if (value >= 0) {
+ valueCbor = cppbor::Uint(value).encode();
+ } else {
+ valueCbor = cppbor::Nint(-value).encode();
+ }
+ }
+
+ string nameSpace;
+ string name;
+ vector<uint8_t> valueCbor;
+ vector<int32_t> profileIds;
+};
+
+struct TestProfile {
+ uint16_t id;
+ vector<uint8_t> readerCertificate;
+ bool userAuthenticationRequired;
+ uint64_t timeoutMillis;
+};
+
+// ----------------------------------------------------------------
+
+class IdentityAidl : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(GetParam().c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ }
+
+ sp<IIdentityCredentialStore> credentialStore_;
+};
+
+TEST_P(IdentityAidl, hardwareInformation) {
+ HardwareInformation info;
+ ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
+ ASSERT_GT(info.credentialStoreName.size(), 0);
+ ASSERT_GT(info.credentialStoreAuthorName.size(), 0);
+ ASSERT_GE(info.dataChunkSize, 256);
+}
+
+TEST_P(IdentityAidl, createAndRetrieveCredential) {
+ // First, generate a key-pair for the reader since its public key will be
+ // part of the request data.
+ optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
+ ASSERT_TRUE(readerKeyPKCS8);
+ optional<vector<uint8_t>> readerPublicKey =
+ support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
+ optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
+ string serialDecimal = "1234";
+ string issuer = "Android Open Source Project";
+ string subject = "Android IdentityCredential VTS Test";
+ time_t validityNotBefore = time(nullptr);
+ time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+ optional<vector<uint8_t>> readerCertificate = support::ecPublicKeyGenerateCertificate(
+ readerPublicKey.value(), readerKey.value(), serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ ASSERT_TRUE(readerCertificate);
+
+ // Make the portrait image really big (just shy of 256 KiB) to ensure that
+ // the chunking code gets exercised.
+ vector<uint8_t> portraitImage;
+ portraitImage.resize(256 * 1024 - 10);
+ for (size_t n = 0; n < portraitImage.size(); n++) {
+ portraitImage[n] = (uint8_t)n;
+ }
+
+ // Access control profiles:
+ const vector<TestProfile> testProfiles = {// Profile 0 (reader authentication)
+ {0, readerCertificate.value(), false, 0},
+ // Profile 1 (no authentication)
+ {1, {}, false, 0}};
+
+ HardwareAuthToken authToken;
+
+ // Here's the actual test data:
+ const vector<TestEntryData> testEntries = {
+ {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
+ {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
+ {"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
+ {"PersonalData", "Home address", string("Maida Vale, London, England"),
+ vector<int32_t>{0}},
+ {"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}},
+ };
+ const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1),
+ 1u};
+ HardwareInformation hwInfo;
+ ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
+
+ string cborPretty;
+ sp<IWritableIdentityCredential> writableCredential;
+ string docType = "org.iso.18013-5.2019.mdl";
+ bool testCredential = true;
+ ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &writableCredential)
+ .isOk());
+ ASSERT_NE(writableCredential, nullptr);
+
+ string challenge = "attestationChallenge";
+ // TODO: set it to something random and check it's in the cert chain
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
+ vector<Certificate> attestationCertificates;
+ ASSERT_TRUE(writableCredential
+ ->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &attestationCertificates)
+ .isOk());
+ ASSERT_GE(attestationCertificates.size(), 2);
+
+ ASSERT_TRUE(
+ writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
+ .isOk());
+
+ vector<SecureAccessControlProfile> returnedSecureProfiles;
+ for (const auto& testProfile : testProfiles) {
+ SecureAccessControlProfile profile;
+ Certificate cert;
+ cert.encodedCertificate = testProfile.readerCertificate;
+ ASSERT_TRUE(writableCredential
+ ->addAccessControlProfile(testProfile.id, cert,
+ testProfile.userAuthenticationRequired,
+ testProfile.timeoutMillis,
+ 0, // secureUserId
+ &profile)
+ .isOk());
+ ASSERT_EQ(testProfile.id, profile.id);
+ ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate);
+ ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
+ ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
+ ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
+ returnedSecureProfiles.push_back(profile);
+ }
+
+ // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
+ // is a little hacky but it works well enough.
+ map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
+
+ for (const auto& entry : testEntries) {
+ vector<vector<uint8_t>> chunks =
+ support::chunkVector(entry.valueCbor, hwInfo.dataChunkSize);
+
+ ASSERT_TRUE(writableCredential
+ ->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
+ entry.valueCbor.size())
+ .isOk());
+
+ vector<vector<uint8_t>> encryptedChunks;
+ for (const auto& chunk : chunks) {
+ vector<uint8_t> encryptedChunk;
+ ASSERT_TRUE(writableCredential->addEntryValue(chunk, &encryptedChunk).isOk());
+ encryptedChunks.push_back(encryptedChunk);
+ }
+ encryptedBlobs[&entry] = encryptedChunks;
+ }
+
+ vector<uint8_t> credentialData;
+ vector<uint8_t> proofOfProvisioningSignature;
+ ASSERT_TRUE(
+ writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
+ .isOk());
+
+ optional<vector<uint8_t>> proofOfProvisioning =
+ support::coseSignGetPayload(proofOfProvisioningSignature);
+ ASSERT_TRUE(proofOfProvisioning);
+ cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
+ EXPECT_EQ(
+ "[\n"
+ " 'ProofOfProvisioning',\n"
+ " 'org.iso.18013-5.2019.mdl',\n"
+ " [\n"
+ " {\n"
+ " 'id' : 0,\n"
+ " 'readerCertificate' : <not printed>,\n"
+ " },\n"
+ " {\n"
+ " 'id' : 1,\n"
+ " },\n"
+ " ],\n"
+ " {\n"
+ " 'PersonalData' : [\n"
+ " {\n"
+ " 'name' : 'Last name',\n"
+ " 'value' : 'Turing',\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " {\n"
+ " 'name' : 'Birth date',\n"
+ " 'value' : '19120623',\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " {\n"
+ " 'name' : 'First name',\n"
+ " 'value' : 'Alan',\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " {\n"
+ " 'name' : 'Home address',\n"
+ " 'value' : 'Maida Vale, London, England',\n"
+ " 'accessControlProfiles' : [0, ],\n"
+ " },\n"
+ " ],\n"
+ " 'Image' : [\n"
+ " {\n"
+ " 'name' : 'Portrait image',\n"
+ " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " ],\n"
+ " },\n"
+ " true,\n"
+ "]",
+ cborPretty);
+
+ optional<vector<uint8_t>> credentialPubKey =
+ support::certificateChainGetTopMostKey(attestationCertificates[0].encodedCertificate);
+ ASSERT_TRUE(credentialPubKey);
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+ {}, // Additional data
+ credentialPubKey.value()));
+ writableCredential = nullptr;
+
+ // Now that the credential has been provisioned, read it back and check the
+ // correct data is returned.
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData, &credential)
+ .isOk());
+ ASSERT_NE(credential, nullptr);
+
+ optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
+ ASSERT_TRUE(readerEphemeralKeyPair);
+ optional<vector<uint8_t>> readerEphemeralPublicKey =
+ support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
+ ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk());
+
+ vector<uint8_t> ephemeralKeyPair;
+ ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk());
+ optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
+
+ // Calculate requestData field and sign it with the reader key.
+ auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
+ ASSERT_TRUE(getXYSuccess);
+ cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
+ vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
+ vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
+ cppbor::Array sessionTranscript = cppbor::Array()
+ .add(cppbor::Semantic(24, deviceEngagementBytes))
+ .add(cppbor::Semantic(24, eReaderPubBytes));
+ vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
+
+ vector<uint8_t> itemsRequestBytes =
+ cppbor::Map("nameSpaces",
+ cppbor::Map()
+ .add("PersonalData", cppbor::Map()
+ .add("Last name", false)
+ .add("Birth date", false)
+ .add("First name", false)
+ .add("Home address", true))
+ .add("Image", cppbor::Map().add("Portrait image", false)))
+ .encode();
+ cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
+ EXPECT_EQ(
+ "{\n"
+ " 'nameSpaces' : {\n"
+ " 'PersonalData' : {\n"
+ " 'Last name' : false,\n"
+ " 'Birth date' : false,\n"
+ " 'First name' : false,\n"
+ " 'Home address' : true,\n"
+ " },\n"
+ " 'Image' : {\n"
+ " 'Portrait image' : false,\n"
+ " },\n"
+ " },\n"
+ "}",
+ cborPretty);
+ vector<uint8_t> dataToSign = cppbor::Array()
+ .add("ReaderAuthentication")
+ .add(sessionTranscript.clone())
+ .add(cppbor::Semantic(24, itemsRequestBytes))
+ .encode();
+ optional<vector<uint8_t>> readerSignature =
+ support::coseSignEcDsa(readerKey.value(), {}, // content
+ dataToSign, // detached content
+ readerCertificate.value());
+ ASSERT_TRUE(readerSignature);
+
+ ASSERT_TRUE(credential
+ ->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
+ sessionTranscriptBytes, readerSignature.value(),
+ testEntriesEntryCounts)
+ .isOk());
+
+ for (const auto& entry : testEntries) {
+ ASSERT_TRUE(credential
+ ->startRetrieveEntryValue(entry.nameSpace, entry.name,
+ entry.valueCbor.size(), entry.profileIds)
+ .isOk());
+
+ auto it = encryptedBlobs.find(&entry);
+ ASSERT_NE(it, encryptedBlobs.end());
+ const vector<vector<uint8_t>>& encryptedChunks = it->second;
+
+ vector<uint8_t> content;
+ for (const auto& encryptedChunk : encryptedChunks) {
+ vector<uint8_t> chunk;
+ ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
+ content.insert(content.end(), chunk.begin(), chunk.end());
+ }
+ EXPECT_EQ(content, entry.valueCbor);
+ }
+
+ // Generate the key that will be used to sign AuthenticatedData.
+ vector<uint8_t> signingKeyBlob;
+ Certificate signingKeyCertificate;
+ ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+
+ vector<uint8_t> mac;
+ vector<uint8_t> deviceNameSpacesBytes;
+ ASSERT_TRUE(credential->finishRetrieval(signingKeyBlob, &mac, &deviceNameSpacesBytes).isOk());
+ cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+ ASSERT_EQ(
+ "{\n"
+ " 'PersonalData' : {\n"
+ " 'Last name' : 'Turing',\n"
+ " 'Birth date' : '19120623',\n"
+ " 'First name' : 'Alan',\n"
+ " 'Home address' : 'Maida Vale, London, England',\n"
+ " },\n"
+ " 'Image' : {\n"
+ " 'Portrait image' : <bstr size=262134 "
+ "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+ " },\n"
+ "}",
+ cborPretty);
+ // The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType,
+ // deviceNameSpacesBytes] so build up that structure
+ cppbor::Array deviceAuthentication;
+ deviceAuthentication.add("DeviceAuthentication");
+ deviceAuthentication.add(sessionTranscript.clone());
+ deviceAuthentication.add(docType);
+ deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
+ vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
+ optional<vector<uint8_t>> signingPublicKey =
+ support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
+ EXPECT_TRUE(signingPublicKey);
+
+ // Derive the key used for MACing.
+ optional<vector<uint8_t>> readerEphemeralPrivateKey =
+ support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
+ optional<vector<uint8_t>> sharedSecret =
+ support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
+ ASSERT_TRUE(sharedSecret);
+ vector<uint8_t> salt = {0x00};
+ vector<uint8_t> info = {};
+ optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
+ ASSERT_TRUE(derivedKey);
+ optional<vector<uint8_t>> calculatedMac =
+ support::coseMac0(derivedKey.value(), {}, // payload
+ encodedDeviceAuthentication); // detached content
+ ASSERT_TRUE(calculatedMac);
+ EXPECT_EQ(mac, calculatedMac);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Identity, IdentityAidl,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl,
+// testing::Values("android.hardware.identity.IIdentityCredentialStore/default"));
+
+} // namespace android::hardware::identity
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ ::android::ProcessState::self()->startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/identity/support/Android.bp b/identity/support/Android.bp
index 38dc10b..7b4546b 100644
--- a/identity/support/Android.bp
+++ b/identity/support/Android.bp
@@ -23,7 +23,6 @@
"include",
],
shared_libs: [
- "android.hardware.identity@1.0",
"libcrypto",
"libbase",
"libhidlbase",
@@ -41,7 +40,6 @@
],
shared_libs: [
"android.hardware.identity-support-lib",
- "android.hardware.identity@1.0",
"libcrypto",
"libbase",
"libhidlbase",
diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
index 485571a..4533ad9 100644
--- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
+++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
@@ -18,12 +18,11 @@
#define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
#include <cstdint>
+#include <optional>
#include <string>
#include <tuple>
#include <vector>
-#include <android/hardware/identity/1.0/types.h>
-
namespace android {
namespace hardware {
namespace identity {
@@ -34,10 +33,6 @@
using ::std::tuple;
using ::std::vector;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-
// ---------------------------------------------------------------------------
// Miscellaneous utilities.
// ---------------------------------------------------------------------------
@@ -258,21 +253,11 @@
const vector<uint8_t>& detachedContent);
// ---------------------------------------------------------------------------
-// Platform abstraction.
-// ---------------------------------------------------------------------------
-
-// Returns the hardware-bound AES-128 key.
-const vector<uint8_t>& getHardwareBoundKey();
-
-// ---------------------------------------------------------------------------
// Utility functions specific to IdentityCredential.
// ---------------------------------------------------------------------------
-// Returns a reference to a Result with code OK and empty message.
-const Result& resultOK();
-
-// Returns a new Result with the given code and message.
-Result result(ResultCode code, const char* format, ...) __attribute__((format(printf, 2, 3)));
+// Returns the testing AES-128 key where all bits are set to 0.
+const vector<uint8_t>& getTestHardwareBoundKey();
// Splits the given bytestring into chunks. If the given vector is smaller or equal to
// |maxChunkSize| a vector with |content| as the only element is returned. Otherwise
@@ -280,21 +265,6 @@
// may be smaller than |maxChunkSize|.
vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize);
-// Calculates the MAC for |profile| using |storageKey|.
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
- const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
-
-// Checks authenticity of the MAC in |profile| using |storageKey|.
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& storageKey);
-
-// Returns the testing AES-128 key where all bits are set to 0.
-const vector<uint8_t>& getTestHardwareBoundKey();
-
-// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
- const vector<uint16_t> accessControlProfileIds);
-
} // namespace support
} // namespace identity
} // namespace hardware
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index 7d93a4b..e2828bf 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -1682,36 +1682,9 @@
}
// ---------------------------------------------------------------------------
-// Platform abstraction.
-// ---------------------------------------------------------------------------
-
-// This is not a very random HBK but that's OK because this is the SW
-// implementation where it can't be kept secret.
-vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
-
-const vector<uint8_t>& getHardwareBoundKey() {
- return hardwareBoundKey;
-}
-
-// ---------------------------------------------------------------------------
// Utility functions specific to IdentityCredential.
// ---------------------------------------------------------------------------
-Result okResult{ResultCode::OK, ""};
-
-const Result& resultOK() {
- return okResult;
-}
-
-Result result(ResultCode code, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- string str;
- android::base::StringAppendV(&str, format, ap);
- va_end(ap);
- return Result{code, str};
-}
-
vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize) {
vector<vector<uint8_t>> ret;
@@ -1738,56 +1711,6 @@
return ret;
}
-vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
- cppbor::Map map;
- map.add("id", profile.id);
-
- if (profile.readerCertificate.size() > 0) {
- map.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
- }
-
- if (profile.userAuthenticationRequired) {
- map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
- map.add("timeoutMillis", profile.timeoutMillis);
- map.add("secureUserId", profile.secureUserId);
- }
-
- return map.encode();
-}
-
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
- const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
- vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
- optional<vector<uint8_t>> nonce = getRandom(12);
- if (!nonce) {
- return {};
- }
- optional<vector<uint8_t>> macO = encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
- if (!macO) {
- return {};
- }
- return macO.value();
-}
-
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& storageKey) {
- vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
- if (profile.mac.size() < kAesGcmIvSize) {
- return false;
- }
- vector<uint8_t> nonce =
- vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + kAesGcmIvSize);
- optional<vector<uint8_t>> mac = encryptAes128Gcm(storageKey, nonce, {}, cborData);
- if (!mac) {
- return false;
- }
- if (mac.value() != vector<uint8_t>(profile.mac)) {
- return false;
- }
- return true;
-}
vector<uint8_t> testHardwareBoundKey = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -1795,20 +1718,6 @@
return testHardwareBoundKey;
}
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
- const vector<uint16_t> accessControlProfileIds) {
- cppbor::Map map;
- map.add("Namespace", nameSpace);
- map.add("Name", name);
-
- cppbor::Array acpIds;
- for (auto id : accessControlProfileIds) {
- acpIds.add(id);
- }
- map.add("AccessControlProfileIds", std::move(acpIds));
- return map.encode();
-}
-
} // namespace support
} // namespace identity
} // namespace hardware
diff --git a/keymaster/aidl/Android.bp b/keymaster/aidl/Android.bp
new file mode 100644
index 0000000..a2d73ead
--- /dev/null
+++ b/keymaster/aidl/Android.bp
@@ -0,0 +1,18 @@
+aidl_interface {
+ name: "android.hardware.keymaster",
+ vendor_available: true,
+ srcs: [
+ "android/hardware/keymaster/*.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
new file mode 100644
index 0000000..58602aa
--- /dev/null
+++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.keymaster;
+
+import android.hardware.keymaster.Timestamp;
+import android.hardware.keymaster.HardwareAuthenticatorType;
+
+/**
+ * HardwareAuthToken is used to prove successful user authentication, to unlock the use of a key.
+ *
+ * HardwareAuthTokens are produced by other secure environment applications, notably GateKeeper and
+ * Fingerprint, in response to successful user authentication events. These tokens are passed to
+ * begin(), update(), and finish() to prove that authentication occurred. See those methods for
+ * more details. It is up to the caller to determine which of the generated auth tokens is
+ * appropriate for a given key operation.
+ */
+@VintfStability
+parcelable HardwareAuthToken {
+
+ /**
+ * challenge is a value that's used to enable authentication tokens to authorize specific
+ * events. The primary use case for challenge is to authorize an IKeymasterDevice cryptographic
+ * operation, for keys that require authentication per operation. See begin() for details.
+ */
+ long challenge;
+
+ /**
+ * userId is the a "secure" user ID. It is not related to any Android user ID or UID, but is
+ * created in the Gatekeeper application in the secure environment.
+ */
+ long userId;
+
+ /**
+ * authenticatorId is the a "secure" user ID. It is not related to any Android user ID or UID,
+ * but is created in an authentication application in the secure environment, such as the
+ * Fingerprint application.
+ */
+ long authenticatorId; // Secure authenticator ID.
+
+ /**
+ * authenticatorType describes the type of authentication that took place, e.g. password or
+ * fingerprint.
+ */
+ HardwareAuthenticatorType authenticatorType;
+
+ /**
+ * timestamp indicates when the user authentication took place, in milliseconds since some
+ * starting point (generally the most recent device boot) which all of the applications within
+ * one secure environment must agree upon. This timestamp is used to determine whether or not
+ * the authentication occurred recently enough to unlock a key (see Tag::AUTH_TIMEOUT).
+ */
+ Timestamp timestamp;
+
+ /**
+ * MACs are computed with a backward-compatible method, used by Keymaster 3.0, Gatekeeper 1.0
+ * and Fingerprint 1.0, as well as pre-treble HALs.
+ *
+ * The MAC is Constants::AUTH_TOKEN_MAC_LENGTH bytes in length and is computed as follows:
+ *
+ * HMAC_SHA256(
+ * H, 0 || challenge || user_id || authenticator_id || authenticator_type || timestamp)
+ *
+ * where ``||'' represents concatenation, the leading zero is a single byte, and all integers
+ * are represented as unsigned values, the full width of the type. The challenge, userId and
+ * authenticatorId values are in machine order, but authenticatorType and timestamp are in
+ * network order (big-endian). This odd construction is compatible with the hw_auth_token_t
+ * structure,
+ *
+ * Note that mac is a vec rather than an array, not because it's actually variable-length but
+ * because it could be empty. As documented in the IKeymasterDevice::begin,
+ * IKeymasterDevice::update and IKeymasterDevice::finish doc comments, an empty mac indicates
+ * that this auth token is empty.
+ */
+ byte[] mac;
+}
diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl
new file mode 100644
index 0000000..3141858
--- /dev/null
+++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.keymaster;
+
+/**
+ * Hardware authentication type, used by HardwareAuthTokens to specify the mechanism used to
+ * authentiate the user, and in KeyCharacteristics to specify the allowable mechanisms for
+ * authenticating to activate a key.
+ */
+@VintfStability
+@Backing(type="int")
+enum HardwareAuthenticatorType {
+ NONE = 0,
+ PASSWORD = 1 << 0,
+ FINGERPRINT = 1 << 1,
+ // Additional entries must be powers of 2.
+ ANY = 0xFFFFFFFF,
+}
diff --git a/radio/config/1.3/IRadioConfigIndication.hal b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
similarity index 66%
rename from radio/config/1.3/IRadioConfigIndication.hal
rename to keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
index 9ef496c..4b2f108 100644
--- a/radio/config/1.3/IRadioConfigIndication.hal
+++ b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,9 @@
* limitations under the License.
*/
-package android.hardware.radio.config@1.3;
+package android.hardware.keymaster;
-import @1.2::IRadioConfigIndication;
-
-/**
- * Interface declaring unsolicited radio config indications.
- */
-interface IRadioConfigIndication extends @1.2::IRadioConfigIndication {
-
-};
+@VintfStability
+parcelable Timestamp {
+ long milliSeconds;
+}
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 993a105..f0fd769 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -2314,7 +2314,38 @@
AXIS_ALIGNED_BBOX_TRANSFORM = 41,
/**
- * Performs a forward LSTM on the input followed by a backward LSTM.
+ * A recurrent neural network layer that applies an LSTM cell to a
+ * sequence of inputs in forward and backward directions.
+ *
+ * The op supports cross-linking via an auxiliary input. Regular cell feeds
+ * one input into the two RNN cells in the following way:
+ *
+ * INPUT (INPUT_REVERSED)
+ * | |
+ * ---------------------
+ * | FW_LSTM BW_LSTM |
+ * ---------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * An op with cross-linking takes two inputs and feeds them into the RNN
+ * cells in the following way:
+ *
+ * AUX_INPUT (AUX_INPUT_REVERSED)
+ * | |
+ * INPUT | (INPUT_R'D.)|
+ * | | | |
+ * -----------------------
+ * | \ / \ / |
+ * | FW_LSTM BW_LSTM |
+ * -----------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * The cross-linking mode is enabled iff auxiliary input and auxiliary
+ * weights are present. While stacking this op on top of itself, this
+ * allows to connect both forward and backward outputs from previous cell
+ * to the next cell's input.
*
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16}
@@ -2324,7 +2355,6 @@
*
* All input and output tensors must be of the same type.
*
- *
* Inputs:
* * 0: The input.
* A 3-D tensor of shape:
@@ -2533,8 +2563,8 @@
* * “activation” is the function passed as the “fused_activation_function”
* argument (if not “NONE”).
*
- * The op also supports an auxiliary input. Regular cell feeds one input
- * into the two RNN cells in the following way:
+ * The op supports cross-linking via an auxiliary input. Regular cell feeds
+ * one input into the two RNN cells in the following way:
*
* INPUT (INPUT_REVERSED)
* | |
@@ -2544,8 +2574,8 @@
* | |
* FW_OUT BW_OUT
*
- * An op with an auxiliary input takes two inputs and feeds them into the
- * RNN cells in the following way:
+ * An op with cross-linking takes two inputs and feeds them into the RNN
+ * cells in the following way:
*
* AUX_INPUT (AUX_INPUT_REVERSED)
* | |
@@ -2558,9 +2588,10 @@
* | |
* FW_OUT BW_OUT
*
- * While stacking this op on top of itself, this allows to connect both
- * forward and backward outputs from previous cell to the next cell's
- * inputs.
+ * The cross-linking mode is enabled iff auxiliary input and auxiliary
+ * weights are present. While stacking this op on top of itself, this
+ * allows to connect both forward and backward outputs from previous cell
+ * to the next cell's input.
*
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16}
diff --git a/neuralnetworks/1.3/IDevice.hal b/neuralnetworks/1.3/IDevice.hal
index 79f9c32..e0b04a8 100644
--- a/neuralnetworks/1.3/IDevice.hal
+++ b/neuralnetworks/1.3/IDevice.hal
@@ -48,19 +48,6 @@
getCapabilities_1_3() generates (ErrorStatus status, Capabilities capabilities);
/**
- * Returns whether the device is able to complete or abort a task within a
- * specified duration.
- *
- * @return prepareModelDeadline 'true' if the device supports completing or
- * aborting model preparation by the deadline when the deadline is supplied,
- * 'false' otherwise.
- * @return executionDeadline 'true' if the device supports completing or
- * aborting an execution by the deadline when the deadline is supplied,
- * 'false' otherwise.
- */
- supportsDeadlines() generates (bool prepareModelDeadline, bool executionDeadline);
-
- /**
* Gets the supported operations in a model.
*
* getSupportedOperations indicates which operations of the top-level
@@ -140,14 +127,10 @@
*
* prepareModel_1_3 can be called with an optional deadline. If the model
* is not able to be prepared before the provided deadline, the model
- * preparation must be aborted, and either {@link
+ * preparation may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
- * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
- * If the service reports that it does not support preparation deadlines via
- * IDevice::supportsDeadlines, and prepareModel_1_3 is called with a
- * deadline, then the argument is invalid, and {@link
- * ErrorStatus::INVALID_ARGUMENT} must be returned.
*
* Optionally, the driver may save the prepared model to cache during the
* asynchronous preparation. Any error that occurs when saving to cache must
@@ -172,9 +155,9 @@
* model.
* @param priority The priority of the prepared model relative to other
* prepared models owned by the client.
- * @param deadline The time by which the model must be prepared. If the
- * model cannot be prepared by the deadline, the preparation must be
- * aborted.
+ * @param deadline The time by which the model is expected to be prepared.
+ * If the model cannot be prepared by the deadline, the preparation may
+ * be aborted.
* @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
@@ -209,8 +192,8 @@
* - GENERAL_FAILURE if there is an unspecified error
* - INVALID_ARGUMENT if one of the input arguments related to preparing
* the model is invalid
- * - MISSED_DEADLINE_* if the deadline for preparing a model cannot be
- * met
+ * - MISSED_DEADLINE_* if the preparation is aborted because the model
+ * cannot be prepared by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
prepareModel_1_3(Model model, ExecutionPreference preference,
@@ -262,14 +245,11 @@
*
* prepareModelFromCache_1_3 can be called with an optional deadline. If the
* model is not able to prepared before the provided deadline, the model
- * preparation must be aborted, and either {@link
+ * preparation may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT}
- * or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The
+ * or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The
* error due to an abort must be sent the same way as other errors,
- * described above. If the service reports that it does not support
- * preparation deadlines via IDevice::supportsDeadlines, and
- * prepareModelFromCache_1_3 is called with a deadline, then the argument is
- * invalid, and {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
+ * described above.
*
* 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
@@ -279,9 +259,9 @@
* used with different shapes of inputs on different (possibly concurrent)
* executions.
*
- * @param deadline The time by which the model must be prepared. If the
- * model cannot be prepared by the deadline, the preparation must be
- * aborted.
+ * @param deadline The time by which the model is expected to be prepared.
+ * If the model cannot be prepared by the deadline, the preparation may
+ * be aborted.
* @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.
@@ -307,8 +287,8 @@
* - GENERAL_FAILURE if caching is not supported or if there is an
* unspecified error
* - INVALID_ARGUMENT if one of the input arguments is invalid
- * - MISSED_DEADLINE_* if the deadline for preparing a model cannot be
- * met
+ * - MISSED_DEADLINE_* if the preparation is aborted because the model
+ * cannot be prepared by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
prepareModelFromCache_1_3(OptionalTimePoint deadline,
diff --git a/neuralnetworks/1.3/IExecutionCallback.hal b/neuralnetworks/1.3/IExecutionCallback.hal
index 439428a..ea11b17 100644
--- a/neuralnetworks/1.3/IExecutionCallback.hal
+++ b/neuralnetworks/1.3/IExecutionCallback.hal
@@ -47,7 +47,8 @@
* corresponding output
* - INVALID_ARGUMENT if one of the input arguments to
* prepareModel is invalid
- * - MISSED_DEADLINE_* if the deadline could not be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
* @param outputShapes A list of shape information of model output operands.
* The index into "outputShapes" corresponds with to index
diff --git a/neuralnetworks/1.3/IFencedExecutionCallback.hal b/neuralnetworks/1.3/IFencedExecutionCallback.hal
index 6030809..949438e 100644
--- a/neuralnetworks/1.3/IFencedExecutionCallback.hal
+++ b/neuralnetworks/1.3/IFencedExecutionCallback.hal
@@ -38,8 +38,8 @@
* - DEVICE_UNAVAILABLE if driver is offline or busy
* - GENERAL_FAILURE if the asynchronous task resulted in an
* unspecified error
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
* @return timingLaunched The duration starts when executeFenced is called and ends when
diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal
index d645de7..a1814b5 100644
--- a/neuralnetworks/1.3/IPreparedModel.hal
+++ b/neuralnetworks/1.3/IPreparedModel.hal
@@ -70,14 +70,10 @@
*
* execute_1_3 can be called with an optional deadline. If the execution
* is not able to be completed before the provided deadline, the execution
- * must be aborted, and either {@link
+ * may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
- * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
- * If the service reports that it does not support execution deadlines via
- * IDevice::supportsDeadlines, and execute_1_3 is called with a deadline,
- * then the argument is invalid, and {@link ErrorStatus::INVALID_ARGUMENT}
- * must be returned.
*
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
@@ -89,9 +85,20 @@
* The duration runs from the time the driver sees the call
* to the execute_1_3 function to the time the driver invokes
* the callback.
- * @param deadline The time by which the execution must complete. If the
- * execution cannot be finished by the deadline, the
- * execution must be aborted.
+ * @param deadline The time by which the execution is expected to complete.
+ * If the execution cannot be completed by the deadline, the
+ * execution may be aborted.
+ * @param loopTimeoutDuration The maximum amount of time that should be spent
+ * executing a {@link OperationType::WHILE}
+ * operation. If a loop condition model does not
+ * output false within this duration, the
+ * execution must be aborted. If the model
+ * contains a {@link OperationType::WHILE}
+ * operation and no loop timeout duration is
+ * provided, the maximum amount of time is {@link
+ * LoopTimeoutDurationNs::DEFAULT}. When
+ * provided, the duration must not exceed {@link
+ * LoopTimeoutDurationNs::MAXIMUM}.
* @param callback A callback object used to return the error status of
* the execution, shape information of model output operands, and
* duration of execution. The callback object's notify function must
@@ -105,13 +112,13 @@
* not large enough to store the resultant values
* - INVALID_ARGUMENT if one of the input arguments is
* invalid
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
*/
execute_1_3(Request request, MeasureTiming measure, OptionalTimePoint deadline,
- IExecutionCallback callback)
+ OptionalTimeoutDuration loopTimeoutDuration, IExecutionCallback callback)
generates (ErrorStatus status);
/**
@@ -139,16 +146,12 @@
* (ErrorStatus::NONE): There must be no failure unless the device itself is
* in a bad state.
*
- * executeSynchronously_1_3 can be called with an optional deadline. If the
+ * executeSynchronously_1_3 may be called with an optional deadline. If the
* execution is not able to be completed before the provided deadline, the
- * execution must be aborted, and either {@link
+ * execution may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
- * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
- * If the service reports that it does not support execution deadlines via
- * IDevice::supportsDeadlines, and executeSynchronously_1_3 is called with a
- * deadline, then the argument is invalid, and
- * {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
*
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
@@ -160,9 +163,20 @@
* The duration runs from the time the driver sees the call
* to the executeSynchronously_1_3 function to the time the driver
* returns from the function.
- * @param deadline The time by which the execution must complete. If the
- * execution cannot be finished by the deadline, the
- * execution must be aborted.
+ * @param deadline The time by which the execution is expected to complete.
+ * If the execution cannot be finished by the deadline, the
+ * execution may be aborted.
+ * @param loopTimeoutDuration The maximum amount of time that should be spent
+ * executing a {@link OperationType::WHILE}
+ * operation. If a loop condition model does not
+ * output false within this duration, the
+ * execution must be aborted. If the model
+ * contains a {@link OperationType::WHILE}
+ * operation and no loop timeout duration is
+ * provided, the maximum amount of time is {@link
+ * LoopTimeoutDurationNs::DEFAULT}. When
+ * provided, the duration must not exceed {@link
+ * LoopTimeoutDurationNs::MAXIMUM}.
* @return status Error status of the execution, must be:
* - NONE if execution is performed successfully
* - DEVICE_UNAVAILABLE if driver is offline or busy
@@ -172,8 +186,8 @@
* corresponding output
* - INVALID_ARGUMENT if one of the input arguments is
* invalid
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
* @return outputShapes A list of shape information of model output operands.
@@ -187,7 +201,8 @@
* measurement is not available.
*/
executeSynchronously_1_3(Request request, MeasureTiming measure,
- OptionalTimePoint deadline)
+ OptionalTimePoint deadline,
+ OptionalTimeoutDuration loopTimeoutDuration)
generates (ErrorStatus status, vec<OutputShape> outputShapes,
Timing timing);
@@ -213,17 +228,13 @@
* any data object referenced by 'request' (described by the
* {@link @1.0::DataLocation} of a {@link @1.0::RequestArgument}).
*
- * executeFenced can be called with an optional deadline and an optional duration.
+ * executeFenced may be called with an optional deadline and an optional duration.
* If the execution is not able to be completed before the provided deadline or
* within the timeout duration (measured from when all sync fences in waitFor are
- * signaled), whichever comes earlier, the execution must be aborted, and either
+ * signaled), whichever comes earlier, the execution may be aborted, and either
* {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
- * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
- * If the service reports that it does not support execution deadlines via
- * IDevice::supportsDeadlines, and executeFenced is called with a
- * deadline or duration, then the argument is invalid, and
- * {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
*
* If any of the sync fences in waitFor changes to error status after the executeFenced
* call succeeds, or the execution is aborted because it cannot finish before the deadline
@@ -240,21 +251,32 @@
* @param waitFor A vector of sync fence file descriptors.
* Execution must not start until all sync fences have been signaled.
* @param measure Specifies whether or not to measure duration of the execution.
- * @param deadline The time by which the execution must complete. If the
- * execution cannot be finished by the deadline, the
- * execution must be aborted.
- * @param duration The length of time within which the execution must
- * complete after all sync fences in waitFor are signaled. If the
- * execution cannot be finished within the duration, the execution
- * must be aborted.
+ * @param deadline The time by which the execution is expected to complete.
+ * If the execution cannot be finished by the deadline, the
+ * execution may be aborted.
+ * @param loopTimeoutDuration The maximum amount of time that should be spent
+ * executing a {@link OperationType::WHILE}
+ * operation. If a loop condition model does not
+ * output false within this duration, the
+ * execution must be aborted. If the model
+ * contains a {@link OperationType::WHILE}
+ * operation and no loop timeout duration is
+ * provided, the maximum amount of time is {@link
+ * LoopTimeoutDurationNs::DEFAULT}. When
+ * provided, the duration must not exceed {@link
+ * LoopTimeoutDurationNs::MAXIMUM}.
+ * @param duration The length of time within which the execution is expected
+ * to complete after all sync fences in waitFor are signaled.
+ * If the execution cannot be finished within the duration,
+ * the execution may be aborted.
* @return status Error status of the call, must be:
* - NONE if 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, including
* fences in error states.
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
* @return syncFence The sync fence that will be signaled when the task is completed.
@@ -264,6 +286,7 @@
* and error status when the execution is completed.
*/
executeFenced(Request request, vec<handle> waitFor, MeasureTiming measure,
- OptionalTimePoint deadline, OptionalTimeoutDuration duration)
+ OptionalTimePoint deadline, OptionalTimeoutDuration loopTimeoutDuration,
+ OptionalTimeoutDuration duration)
generates (ErrorStatus status, handle syncFence, IFencedExecutionCallback callback);
};
diff --git a/neuralnetworks/1.3/IPreparedModelCallback.hal b/neuralnetworks/1.3/IPreparedModelCallback.hal
index 11ebbf4..c0d3416 100644
--- a/neuralnetworks/1.3/IPreparedModelCallback.hal
+++ b/neuralnetworks/1.3/IPreparedModelCallback.hal
@@ -47,8 +47,8 @@
* unspecified error
* - INVALID_ARGUMENT if one of the input arguments to
* prepareModel is invalid
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the preparation is aborted because
+ * the model cannot be prepared by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
* @param preparedModel A model that has been asynchronously prepared for
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index 8ee867c..daaf22e 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -1584,6 +1584,17 @@
* * 3: An optional {@link OperandType::BOOL} scalar, default to false.
* Set to true to specify NCHW data layout for input0 and output0.
* Available since HAL version 1.2.
+ * * 4: Align corners. An optional {@link OperandType::BOOL}
+ * scalar, default to false. If True, the centers of the 4 corner
+ * pixels of the input and output tensors are aligned, preserving the
+ * values at the corner pixels.
+ * Available since HAL version 1.3.
+ * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+ * scalar, default to false. If True, the pixel centers are assumed to
+ * be at (0.5, 0.5). This is the default behavior of image.resize in
+ * TF 2.0. If this parameter is True, then align_corners parameter
+ * must be False.
+ * Available since HAL version 1.3.
*
* Inputs (resizing by scale, since HAL version 1.2):
* * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
@@ -1602,6 +1613,17 @@
* {@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.
+ * * 4: Align corners. An optional {@link OperandType::BOOL}
+ * scalar, default to false. If True, the centers of the 4 corner
+ * pixels of the input and output tensors are aligned, preserving the
+ * values at the corner pixels.
+ * Available since HAL version 1.3.
+ * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+ * scalar, default to false. If True, the pixel centers are assumed to
+ * be at (0.5, 0.5). This is the default behavior of image.resize in
+ * TF 2.0. If this parameter is True, then align_corners parameter
+ * must be False.
+ * Available since HAL version 1.3.
*
* Outputs:
* * 0: The output 4-D tensor, of shape
@@ -2364,7 +2386,54 @@
AXIS_ALIGNED_BBOX_TRANSFORM = @1.2::OperationType:AXIS_ALIGNED_BBOX_TRANSFORM,
/**
- * Performs a forward LSTM on the input followed by a backward LSTM.
+ * A recurrent neural network layer that applies an LSTM cell to a
+ * sequence of inputs in forward and backward directions.
+ *
+ * The op supports cross-linking via an auxiliary input. Regular cell feeds
+ * one input into the two RNN cells in the following way:
+ *
+ * INPUT (INPUT_REVERSED)
+ * | |
+ * ---------------------
+ * | FW_LSTM BW_LSTM |
+ * ---------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * An op with cross-linking takes two inputs and feeds them into the RNN
+ * cells in the following way:
+ *
+ * AUX_INPUT (AUX_INPUT_REVERSED)
+ * | |
+ * INPUT | (INPUT_R'D.)|
+ * | | | |
+ * -----------------------
+ * | \ / \ / |
+ * | FW_LSTM BW_LSTM |
+ * -----------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * The cross-linking mode is enabled iff auxiliary input and auxiliary
+ * weights are present. While stacking this op on top of itself, this
+ * allows to connect both forward and backward outputs from previous cell
+ * to the next cell's input.
+ *
+ * Since HAL version 1.3 parallel linking mode is supported. The mode is
+ * enabled if auxiliary input is present but auxiliary weights are omitted.
+ * In this case, the cell feeds inputs into the RNN in the following way:
+ *
+ * INPUT (AUX_INPUT_REVERSED)
+ * | |
+ * ---------------------
+ * | FW_LSTM BW_LSTM |
+ * ---------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
+ * While stacking this op on top of itself, this allows to connect both
+ * forward and backward outputs from previous cell to the next cell's
+ * corresponding inputs.
*
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16}
@@ -2374,7 +2443,6 @@
*
* All input and output tensors must be of the same type.
*
- *
* Inputs:
* * 0: The input.
* A 3-D tensor of shape:
@@ -2466,25 +2534,34 @@
* * 38: The backward input cell state.
* 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 [fw_num_units, input_size].
- * * 41: The forward auxiliary input-to-forget weights. Optional.
- * 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 [fw_num_units, input_size].
- * * 43: The forward auxiliary input-to-output weights. Optional.
- * 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 [bw_num_units, input_size].
- * * 45: The backward auxiliary input-to-forget weights. Optional.
- * 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 [bw_num_units, input_size].
- * * 47: The backward auxiliary input-to-output weights. Optional.
- * A 2-D tensor of shape [bw_num_units, input_size].
+ * A 3-D tensor of shape [max_time, batch_size, aux_input_size],
+ * where “batch_size” corresponds to the batching dimension, and
+ * “aux_input_size” is the size of the auxiliary input. Optional. See
+ * the docs above for the usage modes explanation.
+ * * 40: The forward auxiliary input-to-input weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [fw_num_units, aux_input_size].
+ * * 41: The forward auxiliary input-to-forget weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [fw_num_units, aux_input_size].
+ * * 42: The forward auxiliary input-to-cell weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [fw_num_units, aux_input_size].
+ * * 43: The forward auxiliary input-to-output weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [fw_num_units, aux_input_size].
+ * * 44: The backward auxiliary input-to-input weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [bw_num_units, aux_input_size].
+ * * 45: The backward auxiliary input-to-forget weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [bw_num_units, aux_input_size].
+ * * 46: The backward auxiliary input-to-cell weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [bw_num_units, aux_input_size].
+ * * 47: The backward auxiliary input-to-output weights.
+ * Optional. See the docs above for the usage modes explanation.
+ * A 2-D tensor of shape [bw_num_units, aux_input_size].
* * 48: The activation function.
* A value indicating the activation function:
* <ul>
@@ -2607,8 +2684,8 @@
* * “activation” is the function passed as the “fused_activation_function”
* argument (if not “NONE”).
*
- * The op also supports an auxiliary input. Regular cell feeds one input
- * into the two RNN cells in the following way:
+ * The op supports cross-linking via an auxiliary input. Regular cell feeds
+ * one input into the two RNN cells in the following way:
*
* INPUT (INPUT_REVERSED)
* | |
@@ -2618,8 +2695,8 @@
* | |
* FW_OUT BW_OUT
*
- * An op with an auxiliary input takes two inputs and feeds them into the
- * RNN cells in the following way:
+ * An op with cross-linking takes two inputs and feeds them into the RNN
+ * cells in the following way:
*
* AUX_INPUT (AUX_INPUT_REVERSED)
* | |
@@ -2632,9 +2709,26 @@
* | |
* FW_OUT BW_OUT
*
+ * The cross-linking mode is enabled iff auxiliary input and auxiliary
+ * weights are present. While stacking this op on top of itself, this
+ * allows to connect both forward and backward outputs from previous cell
+ * to the next cell's input.
+ *
+ * Since HAL version 1.3 parallel linking mode is supported. The mode is
+ * enabled if auxiliary input is present but auxiliary weights are omitted.
+ * In this case, the cell feeds inputs into the RNN in the following way:
+ *
+ * INPUT (AUX_INPUT_REVERSED)
+ * | |
+ * ---------------------
+ * | FW_RNN BW_RNN |
+ * ---------------------
+ * | |
+ * FW_OUT BW_OUT
+ *
* While stacking this op on top of itself, this allows to connect both
* forward and backward outputs from previous cell to the next cell's
- * inputs.
+ * corresponding inputs.
*
* Supported tensor {@link OperandType}:
* * {@link OperandType::TENSOR_FLOAT16}
@@ -2667,11 +2761,17 @@
* A 2-D tensor of shape [batchSize, bwNumUnits]. Specifies a hidden
* state input for the first time step of the computation.
* * 9: auxInput.
- * A 3-D tensor. The shape is the same as of the input 0.
+ * A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+ * it is set to true, then the input has a shape [maxTime, batchSize,
+ * auxInputSize], otherwise the input has a shape [batchSize, maxTime,
+ * auxInputSize]. Can be omitted. See the docs above for the usage
+ * modes explanation.
* * 10:fwAuxWeights.
- * A 2-D tensor of shape [fwNumUnits, inputSize].
+ * A 2-D tensor of shape [fwNumUnits, auxInputSize]. Can be omitted.
+ * See the docs above for the usage modes explanation.
* * 11:bwAuxWeights.
- * A 2-D tensor of shape [bwNumUnits, inputSize].
+ * A 2-D tensor of shape [bwNumUnits, auxInputSize]. Can be omitted.
+ * See the docs above for the usage modes explanation.
* * 12:fusedActivationFunction.
* A {@link FusedActivationFunc} value indicating the activation function. If
* “NONE” is specified then it results in a linear activation.
@@ -4792,6 +4892,17 @@
* height of the output tensor.
* * 3: An {@link OperandType::BOOL} scalar, default to false.
* Set to true to specify NCHW data layout for input0 and output0.
+ * * 4: Align corners. An optional {@link OperandType::BOOL}
+ * scalar, default to false. If True, the centers of the 4 corner
+ * pixels of the input and output tensors are aligned, preserving the
+ * values at the corner pixels.
+ * Available since HAL version 1.3.
+ * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+ * scalar, default to false. If True, the pixel centers are assumed to
+ * be at (0.5, 0.5). This is the default behavior of image.resize in
+ * TF 2.0. If this parameter is True, then align_corners parameter
+ * must be False.
+ * Available since HAL version 1.3.
*
* Inputs (resizing by scale):
* * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
@@ -4810,6 +4921,17 @@
* {@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.
+ * * 4: Align corners. An optional {@link OperandType::BOOL}
+ * scalar, default to false. If True, the centers of the 4 corner
+ * pixels of the input and output tensors are aligned, preserving the
+ * values at the corner pixels.
+ * Available since HAL version 1.3.
+ * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+ * scalar, default to false. If True, the pixel centers are assumed to
+ * be at (0.5, 0.5). This is the default behavior of image.resize in
+ * TF 2.0. If this parameter is True, then align_corners parameter
+ * must be False.
+ * Available since HAL version 1.3.
*
* Outputs:
* * 0: The output 4-D tensor, of shape
@@ -5671,3 +5793,14 @@
*/
RESOURCE_EXHAUSTED_PERSISTENT,
};
+
+/**
+ * Each {@link OperationType::WHILE} operation in the model has an implicit
+ * execution timeout duration associated with it ("loop timeout duration").
+ * This duration is configurable on a per-execution basis and must not exceed
+ * 15 seconds. The default value is 2 seconds.
+ */
+enum LoopTimeoutDurationNs : uint64_t {
+ DEFAULT = 2000000000,
+ MAXIMUM = 15000000000,
+};
diff --git a/neuralnetworks/1.3/types.t b/neuralnetworks/1.3/types.t
index c663a60..0a6e45e 100644
--- a/neuralnetworks/1.3/types.t
+++ b/neuralnetworks/1.3/types.t
@@ -598,3 +598,14 @@
*/
RESOURCE_EXHAUSTED_PERSISTENT,
};
+
+/**
+ * Each {@link OperationType::WHILE} operation in the model has an implicit
+ * execution timeout duration associated with it ("loop timeout duration").
+ * This duration is configurable on a per-execution basis and must not exceed
+ * 15 seconds. The default value is 2 seconds.
+ */
+enum LoopTimeoutDurationNs : uint64_t {
+ DEFAULT = 2000000000,
+ MAXIMUM = 15000000000,
+};
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index 404c2a1..8c9393b 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
@@ -74,7 +74,7 @@
enum class Executor { ASYNC, SYNC, BURST, FENCED };
-enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT };
+enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT, MISSED_DEADLINE };
enum class MemoryType { SHARED, DEVICE };
@@ -495,16 +495,18 @@
static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
const Request& request, MeasureTiming measure,
+ const OptionalTimeoutDuration& loopTimeoutDuration,
sp<ExecutionCallback>& callback) {
- return preparedModel->execute_1_3(request, measure, {}, callback);
+ return preparedModel->execute_1_3(request, measure, {}, loopTimeoutDuration, callback);
}
static Return<ErrorStatus> ExecutePreparedModel(const sp<IPreparedModel>& preparedModel,
const Request& request, MeasureTiming measure,
+ const OptionalTimeoutDuration& loopTimeoutDuration,
hidl_vec<OutputShape>* outputShapes,
Timing* timing) {
ErrorStatus result;
Return<void> ret = preparedModel->executeSynchronously_1_3(
- request, measure, {},
+ request, measure, {}, loopTimeoutDuration,
[&result, outputShapes, timing](ErrorStatus error, const hidl_vec<OutputShape>& shapes,
const Timing& time) {
result = error;
@@ -545,6 +547,17 @@
makeOutputInsufficientSize(/*outputIndex=*/0, &request);
}
+ OptionalTimeoutDuration loopTimeoutDuration;
+ // OutputType::MISSED_DEADLINE is only used by
+ // TestKind::INTINITE_LOOP_TIMEOUT tests to verify that an infinite loop is
+ // aborted after a timeout.
+ if (testConfig.outputType == OutputType::MISSED_DEADLINE) {
+ // Override the default loop timeout duration with a small value to
+ // speed up test execution.
+ constexpr uint64_t kMillisecond = 1'000'000;
+ loopTimeoutDuration.nanoseconds(1 * kMillisecond);
+ }
+
ErrorStatus executionStatus;
hidl_vec<OutputShape> outputShapes;
Timing timing;
@@ -554,8 +567,9 @@
// launch execution
sp<ExecutionCallback> executionCallback = new ExecutionCallback();
- Return<ErrorStatus> executionLaunchStatus = ExecutePreparedModel(
- preparedModel, request, testConfig.measureTiming, executionCallback);
+ Return<ErrorStatus> executionLaunchStatus =
+ ExecutePreparedModel(preparedModel, request, testConfig.measureTiming,
+ loopTimeoutDuration, executionCallback);
ASSERT_TRUE(executionLaunchStatus.isOk());
EXPECT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(executionLaunchStatus));
@@ -571,8 +585,9 @@
SCOPED_TRACE("synchronous");
// execute
- Return<ErrorStatus> executionReturnStatus = ExecutePreparedModel(
- preparedModel, request, testConfig.measureTiming, &outputShapes, &timing);
+ Return<ErrorStatus> executionReturnStatus =
+ ExecutePreparedModel(preparedModel, request, testConfig.measureTiming,
+ loopTimeoutDuration, &outputShapes, &timing);
ASSERT_TRUE(executionReturnStatus.isOk());
executionStatus = static_cast<ErrorStatus>(executionReturnStatus);
@@ -612,7 +627,7 @@
hidl_handle syncFenceHandle;
sp<IFencedExecutionCallback> fencedCallback;
Return<void> ret = preparedModel->executeFenced(
- request, {}, testConfig.measureTiming, {}, {},
+ request, {}, testConfig.measureTiming, {}, loopTimeoutDuration, {},
[&result, &syncFenceHandle, &fencedCallback](
ErrorStatus error, const hidl_handle& handle,
const sp<IFencedExecutionCallback>& callback) {
@@ -686,6 +701,11 @@
ASSERT_EQ(outputShapes.size(), testModel.main.outputIndexes.size());
ASSERT_FALSE(outputShapes[0].isSufficient);
return;
+ case OutputType::MISSED_DEADLINE:
+ ASSERT_TRUE(executionStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ executionStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT)
+ << "executionStatus = " << executionStatus;
+ return;
}
// Go through all outputs, check returned output shapes.
@@ -736,6 +756,12 @@
LOG(FATAL) << "Wrong TestKind for EvaluatePreparedModel";
return;
} break;
+ case TestKind::INTINITE_LOOP_TIMEOUT: {
+ outputTypesList = {OutputType::MISSED_DEADLINE};
+ measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
+ // Burst does not support V1_3 loop timeout.
+ executorList = {Executor::ASYNC, Executor::SYNC, Executor::FENCED};
+ } break;
}
for (const OutputType outputType : outputTypesList) {
@@ -794,7 +820,8 @@
case TestKind::GENERAL:
case TestKind::DYNAMIC_SHAPE:
case TestKind::MEMORY_DOMAIN:
- case TestKind::FENCED_COMPUTE: {
+ case TestKind::FENCED_COMPUTE:
+ case TestKind::INTINITE_LOOP_TIMEOUT: {
createPreparedModel(device, model, &preparedModel);
if (preparedModel == nullptr) return;
EvaluatePreparedModel(device, preparedModel, testModel, testKind);
@@ -831,12 +858,6 @@
void GeneratedTestBase::SetUp() {
testing::TestWithParam<GeneratedTestParam>::SetUp();
ASSERT_NE(kDevice, nullptr);
-
- const Return<void> ret =
- kDevice->supportsDeadlines([this](bool prepareModelDeadline, bool executionDeadline) {
- mSupportsDeadlines = {prepareModelDeadline, executionDeadline};
- });
- ASSERT_TRUE(ret.isOk());
}
std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
@@ -863,24 +884,31 @@
// Tag for the dynamic output shape tests
class QuantizationCouplingTest : public GeneratedTest {};
+// Tag for the loop timeout tests
+class InfiniteLoopTimeoutTest : public GeneratedTest {};
+
TEST_P(GeneratedTest, Test) {
- Execute(kDevice, kTestModel, /*testKind=*/TestKind::GENERAL);
+ Execute(kDevice, kTestModel, TestKind::GENERAL);
}
TEST_P(DynamicOutputShapeTest, Test) {
- Execute(kDevice, kTestModel, /*testKind=*/TestKind::DYNAMIC_SHAPE);
+ Execute(kDevice, kTestModel, TestKind::DYNAMIC_SHAPE);
}
TEST_P(MemoryDomainTest, Test) {
- Execute(kDevice, kTestModel, /*testKind=*/TestKind::MEMORY_DOMAIN);
+ Execute(kDevice, kTestModel, TestKind::MEMORY_DOMAIN);
}
TEST_P(FencedComputeTest, Test) {
- Execute(kDevice, kTestModel, /*testKind=*/TestKind::FENCED_COMPUTE);
+ Execute(kDevice, kTestModel, TestKind::FENCED_COMPUTE);
}
TEST_P(QuantizationCouplingTest, Test) {
- Execute(kDevice, kTestModel, /*testKind=*/TestKind::QUANTIZATION_COUPLING);
+ Execute(kDevice, kTestModel, TestKind::QUANTIZATION_COUPLING);
+}
+
+TEST_P(InfiniteLoopTimeoutTest, Test) {
+ Execute(kDevice, kTestModel, TestKind::INTINITE_LOOP_TIMEOUT);
}
INSTANTIATE_GENERATED_TEST(GeneratedTest,
@@ -900,4 +928,8 @@
return testModel.hasQuant8CoupledOperands() && testModel.main.operations.size() == 1;
});
+INSTANTIATE_GENERATED_TEST(InfiniteLoopTimeoutTest, [](const TestModel& testModel) {
+ return testModel.isInfiniteLoopTimeoutTest();
+});
+
} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
index e597fac..834d335 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
@@ -36,7 +36,6 @@
void SetUp() override;
const sp<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
- std::pair<bool, bool> mSupportsDeadlines;
};
using FilterFn = std::function<bool(const test_helper::TestModel&)>;
@@ -70,7 +69,9 @@
// Tests if quantized model with TENSOR_QUANT8_ASYMM produces the same result
// (OK/SKIPPED/FAILED) as the model with all such tensors converted to
// TENSOR_QUANT8_ASYMM_SIGNED.
- QUANTIZATION_COUPLING
+ QUANTIZATION_COUPLING,
+ // Runs a test model and verifies that MISSED_DEADLINE_* is returned.
+ INTINITE_LOOP_TIMEOUT
};
void EvaluatePreparedModel(const sp<IDevice>& device, const sp<IPreparedModel>& preparedModel,
diff --git a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
index 8271135..879989e 100644
--- a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
@@ -34,45 +34,52 @@
using HidlToken =
hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
-enum class DeadlineBoundType { NOW, UNLIMITED };
-constexpr std::array<DeadlineBoundType, 2> deadlineBounds = {DeadlineBoundType::NOW,
- DeadlineBoundType::UNLIMITED};
+enum class DeadlineBoundType { NOW, UNLIMITED, SHORT };
+constexpr std::array<DeadlineBoundType, 3> deadlineBounds = {
+ DeadlineBoundType::NOW, DeadlineBoundType::UNLIMITED, DeadlineBoundType::SHORT};
std::string toString(DeadlineBoundType type) {
switch (type) {
case DeadlineBoundType::NOW:
return "NOW";
case DeadlineBoundType::UNLIMITED:
return "UNLIMITED";
+ case DeadlineBoundType::SHORT:
+ return "SHORT";
}
LOG(FATAL) << "Unrecognized DeadlineBoundType: " << static_cast<int>(type);
return {};
}
+constexpr auto kShortDuration = std::chrono::milliseconds{5};
+
using Results = std::tuple<ErrorStatus, hidl_vec<OutputShape>, Timing>;
using MaybeResults = std::optional<Results>;
using ExecutionFunction =
std::function<MaybeResults(const sp<IPreparedModel>& preparedModel, const Request& request,
- DeadlineBoundType deadlineBound)>;
+ const OptionalTimePoint& deadline)>;
-static OptionalTimePoint makeOptionalTimePoint(DeadlineBoundType deadlineBoundType) {
- OptionalTimePoint deadline;
+static OptionalTimePoint makeDeadline(DeadlineBoundType deadlineBoundType) {
+ const auto getNanosecondsSinceEpoch = [](const auto& time) -> uint64_t {
+ const auto timeSinceEpoch = time.time_since_epoch();
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(timeSinceEpoch).count();
+ };
+
+ std::chrono::steady_clock::time_point timePoint;
switch (deadlineBoundType) {
- case DeadlineBoundType::NOW: {
- const auto currentTime = std::chrono::steady_clock::now();
- const auto currentTimeInNanoseconds =
- std::chrono::time_point_cast<std::chrono::nanoseconds>(currentTime);
- const uint64_t nanosecondsSinceEpoch =
- currentTimeInNanoseconds.time_since_epoch().count();
- deadline.nanosecondsSinceEpoch(nanosecondsSinceEpoch);
- } break;
- case DeadlineBoundType::UNLIMITED: {
- const auto maxTime = std::chrono::time_point<std::chrono::steady_clock,
- std::chrono::nanoseconds>::max();
- const uint64_t nanosecondsSinceEpoch = maxTime.time_since_epoch().count();
- deadline.nanosecondsSinceEpoch(nanosecondsSinceEpoch);
- } break;
+ case DeadlineBoundType::NOW:
+ timePoint = std::chrono::steady_clock::now();
+ break;
+ case DeadlineBoundType::UNLIMITED:
+ timePoint = std::chrono::steady_clock::time_point::max();
+ break;
+ case DeadlineBoundType::SHORT:
+ timePoint = std::chrono::steady_clock::now() + kShortDuration;
+ break;
}
+
+ OptionalTimePoint deadline;
+ deadline.nanosecondsSinceEpoch(getNanosecondsSinceEpoch(timePoint));
return deadline;
}
@@ -80,7 +87,7 @@
std::optional<DeadlineBoundType> deadlineBound) {
OptionalTimePoint deadline;
if (deadlineBound.has_value()) {
- deadline = makeOptionalTimePoint(deadlineBound.value());
+ deadline = makeDeadline(deadlineBound.value());
}
// see if service can handle model
@@ -127,11 +134,11 @@
} else {
switch (deadlineBound.value()) {
case DeadlineBoundType::NOW:
- // If the execution was launched with a deadline of NOW, the
- // deadline has already passed when the driver would launch the
- // execution. In this case, the driver must return
- // MISSED_DEADLINE_*.
- EXPECT_TRUE(prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ case DeadlineBoundType::SHORT:
+ // Either the driver successfully completed the task or it
+ // aborted and returned MISSED_DEADLINE_*.
+ EXPECT_TRUE(prepareReturnStatus == ErrorStatus::NONE ||
+ prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
break;
case DeadlineBoundType::UNLIMITED:
@@ -145,8 +152,7 @@
ASSERT_EQ(prepareReturnStatus == ErrorStatus::NONE, preparedModel.get() != nullptr);
}
-void runPrepareModelTests(const sp<IDevice>& device, const Model& model,
- bool supportsPrepareModelDeadline) {
+void runPrepareModelTests(const sp<IDevice>& device, const Model& model) {
// test priority
for (auto priority : hidl_enum_range<Priority>{}) {
SCOPED_TRACE("priority: " + toString(priority));
@@ -155,23 +161,21 @@
}
// test deadline
- if (supportsPrepareModelDeadline) {
- for (auto deadlineBound : deadlineBounds) {
- SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
- runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
- }
+ for (auto deadlineBound : deadlineBounds) {
+ SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
+ runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
}
}
static MaybeResults executeAsynchronously(const sp<IPreparedModel>& preparedModel,
- const Request& request, DeadlineBoundType deadlineBound) {
+ const Request& request,
+ const OptionalTimePoint& deadline) {
SCOPED_TRACE("asynchronous");
const MeasureTiming measure = MeasureTiming::NO;
- const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
// launch execution
const sp<ExecutionCallback> callback = new ExecutionCallback();
- Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, callback);
+ Return<ErrorStatus> ret = preparedModel->execute_1_3(request, measure, deadline, {}, callback);
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(ErrorStatus::NONE, ret.withDefault(ErrorStatus::GENERAL_FAILURE));
if (!ret.isOk() || ret != ErrorStatus::NONE) return std::nullopt;
@@ -187,18 +191,21 @@
}
static MaybeResults executeSynchronously(const sp<IPreparedModel>& preparedModel,
- const Request& request, DeadlineBoundType deadlineBound) {
+ const Request& request,
+ const OptionalTimePoint& deadline) {
SCOPED_TRACE("synchronous");
const MeasureTiming measure = MeasureTiming::NO;
- const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
// configure results callback
MaybeResults results;
- const auto cb = [&results](const auto&... args) { *results = {args...}; };
+ const auto cb = [&results](ErrorStatus status, const hidl_vec<OutputShape>& outputShapes,
+ const Timing& timing) {
+ results.emplace(status, outputShapes, timing);
+ };
// run execution
const Return<void> ret =
- preparedModel->executeSynchronously_1_3(request, measure, deadline, cb);
+ preparedModel->executeSynchronously_1_3(request, measure, deadline, {}, cb);
EXPECT_TRUE(ret.isOk());
if (!ret.isOk()) return std::nullopt;
@@ -209,9 +216,10 @@
void runExecutionTest(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
const Request& request, bool synchronous, DeadlineBoundType deadlineBound) {
const ExecutionFunction execute = synchronous ? executeSynchronously : executeAsynchronously;
+ const auto deadline = makeDeadline(deadlineBound);
// Perform execution and unpack results.
- const auto results = execute(preparedModel, request, deadlineBound);
+ const auto results = execute(preparedModel, request, deadline);
if (!results.has_value()) return;
const auto& [status, outputShapes, timing] = results.value();
@@ -222,13 +230,13 @@
// Validate deadline information if applicable.
switch (deadlineBound) {
case DeadlineBoundType::NOW:
- // If the execution was launched with a deadline of NOW, the
- // deadline has already passed when the driver would launch the
- // execution. In this case, the driver must return
- // MISSED_DEADLINE_*.
- ASSERT_TRUE(status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ case DeadlineBoundType::SHORT:
+ // Either the driver successfully completed the task or it
+ // aborted and returned MISSED_DEADLINE_*.
+ ASSERT_TRUE(status == ErrorStatus::NONE ||
+ status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
status == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
- return;
+ break;
case DeadlineBoundType::UNLIMITED:
// If an unlimited deadline is supplied, we expect the execution to
// proceed normally. In this case, check it normally by breaking out
@@ -256,7 +264,9 @@
const std::vector<TestBuffer> outputs = getOutputBuffers(request10);
// We want "close-enough" results.
- checkResults(testModel, outputs);
+ if (status == ErrorStatus::NONE) {
+ checkResults(testModel, outputs);
+ }
}
void runExecutionTests(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
@@ -268,32 +278,27 @@
}
}
-void runTests(const sp<IDevice>& device, const TestModel& testModel,
- std::pair<bool, bool> supportsDeadlines) {
+void runTests(const sp<IDevice>& device, const TestModel& testModel) {
// setup
- const auto [supportsPrepareModelDeadline, supportsExecutionDeadline] = supportsDeadlines;
- if (!supportsPrepareModelDeadline && !supportsExecutionDeadline) return;
const Model model = createModel(testModel);
// run prepare model tests
- runPrepareModelTests(device, model, supportsPrepareModelDeadline);
+ runPrepareModelTests(device, model);
- if (supportsExecutionDeadline) {
- // prepare model
- sp<IPreparedModel> preparedModel;
- createPreparedModel(device, model, &preparedModel);
- if (preparedModel == nullptr) return;
+ // prepare model
+ sp<IPreparedModel> preparedModel;
+ createPreparedModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) return;
- // run execution tests
- const Request request = nn::convertToV1_3(createRequest(testModel));
- runExecutionTests(preparedModel, testModel, request);
- }
+ // run execution tests
+ const Request request = nn::convertToV1_3(createRequest(testModel));
+ runExecutionTests(preparedModel, testModel, request);
}
class DeadlineTest : public GeneratedTestBase {};
TEST_P(DeadlineTest, Test) {
- runTests(kDevice, kTestModel, mSupportsDeadlines);
+ runTests(kDevice, kTestModel);
}
INSTANTIATE_GENERATED_TEST(DeadlineTest,
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index 09e9922..7da2da9 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -44,18 +44,12 @@
}
static void validatePrepareModel(const sp<IDevice>& device, const std::string& message,
- const Model& model, ExecutionPreference preference,
- bool testDeadline) {
+ const Model& model, ExecutionPreference preference) {
SCOPED_TRACE(message + " [prepareModel_1_3]");
- OptionalTimePoint deadline;
- if (testDeadline) {
- deadline.nanosecondsSinceEpoch(std::numeric_limits<uint64_t>::max());
- }
-
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
- model, preference, kDefaultPriority, deadline, hidl_vec<hidl_handle>(),
+ model, preference, kDefaultPriority, {}, hidl_vec<hidl_handle>(),
hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(prepareLaunchStatus));
@@ -79,13 +73,12 @@
// to the model does not leave this function.
static void validate(const sp<IDevice>& device, const std::string& message, Model model,
const std::function<void(Model*)>& mutation,
- ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER,
- bool testDeadline = false) {
+ ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER) {
mutation(&model);
- if (validExecutionPreference(preference) && !testDeadline) {
+ if (validExecutionPreference(preference)) {
validateGetSupportedOperations(device, message, model);
}
- validatePrepareModel(device, message, model, preference, testDeadline);
+ validatePrepareModel(device, message, model, preference);
}
static uint32_t addOperand(Model* model) {
@@ -585,6 +578,8 @@
// - CONV_2D, DEPTHWISE_CONV_2D, MAX_POOL_2D, AVERAGE_POOL_2D, L2_POOL_2D, RESIZE_BILINEAR,
// SPACE_TO_DEPTH, SPACE_TO_DEPTH, SPACE_TO_BATCH_ND, BATCH_TO_SPACE_ND can have an optional
// layout parameter.
+ // RESIZE_BILINEAR and RESIZE_NEAREST_NEIGHBOR can have optional
+ // align_corners and half_pixel_centers parameters.
// - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional axis
// parameter.
switch (op.type) {
@@ -607,7 +602,12 @@
}
} break;
case OperationType::RESIZE_BILINEAR: {
- if (op.inputs.size() == 4 && input == 3) {
+ if (op.inputs.size() >= 4 && input >= 3) {
+ return true;
+ }
+ } break;
+ case OperationType::RESIZE_NEAREST_NEIGHBOR: {
+ if (op.inputs.size() >= 5 && input >= 3) {
return true;
}
} break;
@@ -693,7 +693,9 @@
// parameter.
if ((op.type == OperationType::L2_NORMALIZATION && op.inputs.size() == 1) ||
(op.type == OperationType::LOCAL_RESPONSE_NORMALIZATION && op.inputs.size() == 5) ||
- (op.type == OperationType::SOFTMAX && op.inputs.size() == 2)) {
+ (op.type == OperationType::SOFTMAX && op.inputs.size() == 2) ||
+ (op.type == OperationType::RESIZE_BILINEAR && op.inputs.size() < 6) ||
+ (op.type == OperationType::RESIZE_NEAREST_NEIGHBOR && op.inputs.size() < 6)) {
return true;
}
return false;
@@ -744,19 +746,9 @@
}
}
-///////////////////////// DEADLINE /////////////////////////
-
-static void deadlineTest(const sp<IDevice>& device, const Model& model) {
- const std::string message = "deadlineTest: deadline not supported";
- const auto noop = [](Model*) {};
- validate(device, message, model, noop, ExecutionPreference::FAST_SINGLE_ANSWER,
- /*testDeadline=*/true);
-}
-
////////////////////////// ENTRY POINT //////////////////////////////
-void validateModel(const sp<IDevice>& device, const Model& model,
- bool prepareModelDeadlineSupported) {
+void validateModel(const sp<IDevice>& device, const Model& model) {
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
@@ -772,9 +764,6 @@
addOperationInputTest(device, model);
addOperationOutputTest(device, model);
mutateExecutionPreferenceTest(device, model);
- if (!prepareModelDeadlineSupported) {
- deadlineTest(device, model);
- }
}
} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
index 2a4269f..5e806e5 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
@@ -45,8 +45,7 @@
// that use the request. Note that the request here is passed by value, and any
// mutation to the request does not leave this function.
static void validate(const sp<IPreparedModel>& preparedModel, const std::string& message,
- Request request, const std::function<void(Request*)>& mutation,
- bool testDeadline = false) {
+ Request request, const std::function<void(Request*)>& mutation) {
mutation(&request);
// We'd like to test both with timing requested and without timing
@@ -59,18 +58,13 @@
};
MeasureTiming measure = (hash & 1) ? MeasureTiming::YES : MeasureTiming::NO;
- OptionalTimePoint deadline;
- if (testDeadline) {
- deadline.nanosecondsSinceEpoch(std::numeric_limits<uint64_t>::max());
- }
-
// asynchronous
{
SCOPED_TRACE(message + " [execute_1_3]");
sp<ExecutionCallback> executionCallback = new ExecutionCallback();
Return<ErrorStatus> executeLaunchStatus =
- preparedModel->execute_1_3(request, measure, deadline, executionCallback);
+ preparedModel->execute_1_3(request, measure, {}, {}, executionCallback);
ASSERT_TRUE(executeLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeLaunchStatus));
@@ -88,7 +82,7 @@
SCOPED_TRACE(message + " [executeSynchronously_1_3]");
Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
- request, measure, deadline,
+ request, measure, {}, {},
[](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes,
const Timing& timing) {
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
@@ -100,7 +94,7 @@
// burst
// TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
- if (!testDeadline) {
+ {
SCOPED_TRACE(message + " [burst]");
ASSERT_TRUE(nn::compliantWithV1_0(request));
@@ -143,7 +137,7 @@
{
SCOPED_TRACE(message + " [executeFenced]");
Return<void> ret =
- preparedModel->executeFenced(request, {}, MeasureTiming::NO, deadline, {},
+ preparedModel->executeFenced(request, {}, MeasureTiming::NO, {}, {}, {},
[](ErrorStatus error, const hidl_handle& handle,
const sp<IFencedExecutionCallback>& callback) {
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
@@ -174,29 +168,17 @@
}
}
-///////////////////////// DEADLINE ////////////////////////////////////
-
-static void deadlineTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
- const std::string message = "deadlineTest: deadline not supported";
- const auto noop = [](Request*) {};
- validate(preparedModel, message, request, noop, /*testDeadline=*/true);
-}
-
///////////////////////////// ENTRY POINT //////////////////////////////////
-void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
- bool executionDeadlineSupported) {
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request) {
removeInputTest(preparedModel, request);
removeOutputTest(preparedModel, request);
- if (!executionDeadlineSupported) {
- deadlineTest(preparedModel, request);
- }
}
void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request) {
SCOPED_TRACE("Expecting request to fail [executeSynchronously_1_3]");
Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
- request, MeasureTiming::NO, {},
+ request, MeasureTiming::NO, {}, {},
[](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
ASSERT_NE(ErrorStatus::NONE, error);
EXPECT_EQ(outputShapes.size(), 0);
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
index 9a87569..5b07034 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
@@ -123,11 +123,9 @@
INSTANTIATE_DEVICE_TEST(NeuralnetworksHidlTest);
// Forward declaration from ValidateModel.cpp
-void validateModel(const sp<IDevice>& device, const Model& model,
- bool prepareModelDeadlineSupported);
+void validateModel(const sp<IDevice>& device, const Model& model);
// Forward declaration from ValidateRequest.cpp
-void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
- bool executionDeadlineSupported);
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request);
// Forward declaration from ValidateRequest.cpp
void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request);
// Forward declaration from ValidateBurst.cpp
@@ -137,7 +135,7 @@
void validateExecuteFenced(const sp<IPreparedModel>& preparedModel, const Request& request) {
SCOPED_TRACE("Expecting request to fail [executeFenced]");
Return<void> ret_null = preparedModel->executeFenced(
- request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO, {}, {},
+ request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO, {}, {}, {},
[](ErrorStatus error, const hidl_handle& handle,
const sp<IFencedExecutionCallback>& callback) {
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
@@ -147,17 +145,15 @@
ASSERT_TRUE(ret_null.isOk());
}
-void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request,
- std::pair<bool, bool> supportsDeadlines) {
- const auto [prepareModelDeadlineSupported, executionDeadlineSupported] = supportsDeadlines;
- validateModel(device, model, prepareModelDeadlineSupported);
+void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request) {
+ validateModel(device, model);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
createPreparedModel(device, model, &preparedModel);
if (preparedModel == nullptr) return;
- validateRequest(preparedModel, request, executionDeadlineSupported);
+ validateRequest(preparedModel, request);
validateExecuteFenced(preparedModel, request);
// TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
@@ -166,12 +162,10 @@
validateBurst(preparedModel, request10);
}
-void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request,
- std::pair<bool, bool> supportsDeadlines) {
- const bool prepareModelDeadlineSupported = supportsDeadlines.first;
+void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request) {
// TODO: Should this always succeed?
// What if the invalid input is part of the model (i.e., a parameter).
- validateModel(device, model, prepareModelDeadlineSupported);
+ validateModel(device, model);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
@@ -185,9 +179,9 @@
const Model model = createModel(kTestModel);
const Request request = nn::convertToV1_3(createRequest(kTestModel));
if (kTestModel.expectFailure) {
- validateFailure(kDevice, model, request, mSupportsDeadlines);
+ validateFailure(kDevice, model, request);
} else {
- validateEverything(kDevice, model, request, mSupportsDeadlines);
+ validateEverything(kDevice, model, request);
}
}
diff --git a/radio/1.5/vts/functional/Android.bp b/radio/1.5/vts/functional/Android.bp
index 182985e..85c4f99 100644
--- a/radio/1.5/vts/functional/Android.bp
+++ b/radio/1.5/vts/functional/Android.bp
@@ -34,7 +34,6 @@
"android.hardware.radio@1.0",
"android.hardware.radio.config@1.0",
"android.hardware.radio.config@1.1",
- "android.hardware.radio.config@1.3",
],
header_libs: ["radio.util.header@1.0"],
test_suites: ["general-tests"]
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_test.cpp b/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
index c29ebf9..a5d236d 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
@@ -47,9 +47,9 @@
EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
EXPECT_EQ(RadioError::NONE, radioRsp_v1_5->rspInfo.error);
- sp<::android::hardware::radio::config::V1_3::IRadioConfig> radioConfig =
+ sp<::android::hardware::radio::config::V1_1::IRadioConfig> radioConfig =
::testing::VtsHalHidlTargetTestBase::getService<
- ::android::hardware::radio::config::V1_3::IRadioConfig>();
+ ::android::hardware::radio::config::V1_1::IRadioConfig>();
/* Enforce Vts tesing with RadioConfig is existed. */
ASSERT_NE(nullptr, radioConfig.get());
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
index 01cf40a..a7c1cdc 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
+++ b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
@@ -22,7 +22,7 @@
#include <condition_variable>
#include <mutex>
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
+#include <android/hardware/radio/config/1.1/IRadioConfig.h>
#include <android/hardware/radio/1.5/IRadio.h>
#include <android/hardware/radio/1.5/IRadioIndication.h>
diff --git a/radio/config/1.3/Android.bp b/radio/config/1.3/Android.bp
deleted file mode 100644
index 7360270..0000000
--- a/radio/config/1.3/Android.bp
+++ /dev/null
@@ -1,26 +0,0 @@
-// This file is autogenerated by hidl-gen -Landroidbp.
-
-hidl_interface {
- name: "android.hardware.radio.config@1.3",
- root: "android.hardware",
- vndk: {
- enabled: true,
- },
- srcs: [
- "types.hal",
- "IRadioConfig.hal",
- "IRadioConfigIndication.hal",
- "IRadioConfigResponse.hal",
- ],
- interfaces: [
- "android.hardware.radio.config@1.0",
- "android.hardware.radio.config@1.1",
- "android.hardware.radio.config@1.2",
- "android.hardware.radio@1.0",
- "android.hardware.radio@1.1",
- "android.hardware.radio@1.4",
- "android.hardware.radio@1.5",
- "android.hidl.base@1.0",
- ],
- gen_java: true,
-}
diff --git a/radio/config/1.3/IRadioConfig.hal b/radio/config/1.3/IRadioConfig.hal
deleted file mode 100644
index d01f54b..0000000
--- a/radio/config/1.3/IRadioConfig.hal
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.radio.config@1.3;
-
-import @1.1::IRadioConfig;
-
-/**
- * This interface is used by telephony and telecom to talk to cellular radio for the purpose of
- * radio configuration, and it is not associated with any specific modem or slot.
- * All the functions have minimum one parameter:
- * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
- * duration of a method call. If clients provide colliding serials (including passing the same
- * serial to different methods), multiple responses (one for each method call) must still be served.
- */
-interface IRadioConfig extends @1.1::IRadioConfig {
- /**
- * Request current phone capability.
- *
- * @param serial Serial number of request.
- *
- * Response callback is IRadioResponse.getPhoneCapabilityResponse_1_3() which
- * will return <@1.3::PhoneCapability>.
- */
- oneway getPhoneCapability_1_3(int32_t serial);
-};
diff --git a/radio/config/1.3/IRadioConfigResponse.hal b/radio/config/1.3/IRadioConfigResponse.hal
deleted file mode 100644
index e13aa1e..0000000
--- a/radio/config/1.3/IRadioConfigResponse.hal
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.radio.config@1.3;
-
-import android.hardware.radio@1.0::RadioResponseInfo;
-import @1.2::IRadioConfigResponse;
-import @1.3::PhoneCapability;
-
-/**
- * Interface declaring response functions to solicited radio config requests.
- */
-interface IRadioConfigResponse extends @1.2::IRadioConfigResponse {
- /**
- * @param info Response info struct containing response type, serial no. and error
- * @param phoneCapability <@1.3::PhoneCapability> it defines modem's capability for example
- * how many logical modems it has, how many data connections it supports.
- *
- * Valid errors returned:
- * RadioError:NONE
- * RadioError:RADIO_NOT_AVAILABLE
- */
- oneway getPhoneCapabilityResponse_1_3(RadioResponseInfo info, PhoneCapability phoneCapability);
-};
diff --git a/radio/config/1.3/default/Android.bp b/radio/config/1.3/default/Android.bp
deleted file mode 100644
index 163c5c5..0000000
--- a/radio/config/1.3/default/Android.bp
+++ /dev/null
@@ -1,28 +0,0 @@
-cc_binary {
- name: "android.hardware.radio.config@1.3-service",
- init_rc: ["android.hardware.radio.config@1.3-service.rc"],
- relative_install_path: "hw",
- vintf_fragments: ["radio-config-default.xml"],
- vendor: true,
- srcs: [
- "RadioConfig.cpp",
- "RadioConfigIndication.cpp",
- "RadioConfigResponse.cpp",
- "service.cpp",
- ],
- shared_libs: [
- "libhidlbase",
- "liblog",
- "libutils",
- "android.hardware.radio.config@1.0",
- "android.hardware.radio.config@1.1",
- "android.hardware.radio.config@1.2",
- "android.hardware.radio.config@1.3",
- "android.hardware.radio@1.0",
- "android.hardware.radio@1.1",
- "android.hardware.radio@1.2",
- "android.hardware.radio@1.3",
- "android.hardware.radio@1.4",
- "android.hardware.radio@1.5",
- ],
-}
diff --git a/radio/config/1.3/default/RadioConfig.cpp b/radio/config/1.3/default/RadioConfig.cpp
deleted file mode 100644
index 01e98f1..0000000
--- a/radio/config/1.3/default/RadioConfig.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "RadioConfig.h"
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::V1_0;
-
-// Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
-Return<void> RadioConfig::setResponseFunctions(
- const sp<V1_0::IRadioConfigResponse>& radioConfigResponse,
- const sp<V1_0::IRadioConfigIndication>& radioConfigIndication) {
- mRadioConfigResponse = radioConfigResponse;
- mRadioConfigIndication = radioConfigIndication;
-
- mRadioConfigResponseV1_3 =
- V1_3::IRadioConfigResponse::castFrom(mRadioConfigResponse).withDefault(nullptr);
- mRadioConfigIndicationV1_3 =
- V1_3::IRadioConfigIndication::castFrom(mRadioConfigIndication).withDefault(nullptr);
- if (mRadioConfigResponseV1_3 == nullptr || mRadioConfigIndicationV1_3 == nullptr) {
- mRadioConfigResponseV1_3 = nullptr;
- mRadioConfigIndicationV1_3 = nullptr;
- }
-
- mRadioConfigResponseV1_2 =
- V1_2::IRadioConfigResponse::castFrom(mRadioConfigResponse).withDefault(nullptr);
- mRadioConfigIndicationV1_2 =
- V1_2::IRadioConfigIndication::castFrom(mRadioConfigIndication).withDefault(nullptr);
- if (mRadioConfigResponseV1_2 == nullptr || mRadioConfigIndicationV1_2 == nullptr) {
- mRadioConfigResponseV1_2 = nullptr;
- mRadioConfigIndicationV1_2 = nullptr;
- }
-
- mRadioConfigResponseV1_1 =
- V1_1::IRadioConfigResponse::castFrom(mRadioConfigResponse).withDefault(nullptr);
- mRadioConfigIndicationV1_1 =
- V1_1::IRadioConfigIndication::castFrom(mRadioConfigIndication).withDefault(nullptr);
- if (mRadioConfigResponseV1_1 == nullptr || mRadioConfigIndicationV1_1 == nullptr) {
- mRadioConfigResponseV1_1 = nullptr;
- mRadioConfigIndicationV1_1 = nullptr;
- }
-
- return Void();
-}
-
-Return<void> RadioConfig::getSimSlotsStatus(int32_t /* serial */) {
- hidl_vec<V1_0::SimSlotStatus> slotStatus;
- RadioResponseInfo info;
- mRadioConfigResponse->getSimSlotsStatusResponse(info, slotStatus);
- return Void();
-}
-
-Return<void> RadioConfig::setSimSlotsMapping(int32_t /* serial */,
- const hidl_vec<uint32_t>& /* slotMap */) {
- RadioResponseInfo info;
- mRadioConfigResponse->setSimSlotsMappingResponse(info);
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_1::IRadioConfig follow.
-Return<void> RadioConfig::getPhoneCapability(int32_t /* serial */) {
- V1_1::PhoneCapability phoneCapability;
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->getPhoneCapabilityResponse(info, phoneCapability);
- return Void();
-}
-
-Return<void> RadioConfig::setPreferredDataModem(int32_t /* serial */, uint8_t /* modemId */) {
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->setPreferredDataModemResponse(info);
- return Void();
-}
-
-Return<void> RadioConfig::setModemsConfig(int32_t /* serial */,
- const V1_1::ModemsConfig& /* modemsConfig */) {
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->setModemsConfigResponse(info);
- return Void();
-}
-
-Return<void> RadioConfig::getModemsConfig(int32_t /* serial */) {
- V1_1::ModemsConfig modemsConfig;
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->getModemsConfigResponse(info, modemsConfig);
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_3::IRadioConfig follow.
-Return<void> RadioConfig::getPhoneCapability_1_3(int32_t /* serial */) {
- V1_3::PhoneCapability phoneCapability;
- RadioResponseInfo info;
- mRadioConfigResponseV1_3->getPhoneCapabilityResponse_1_3(info, phoneCapability);
- return Void();
-}
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
diff --git a/radio/config/1.3/default/RadioConfig.h b/radio/config/1.3/default/RadioConfig.h
deleted file mode 100644
index 57ff368..0000000
--- a/radio/config/1.3/default/RadioConfig.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIG_H
-#define ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIG_H
-
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
-#include <android/hardware/radio/config/1.3/IRadioConfigIndication.h>
-#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::config;
-
-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 RadioConfig : public V1_3::IRadioConfig {
- sp<V1_0::IRadioConfigResponse> mRadioConfigResponse;
- sp<V1_0::IRadioConfigIndication> mRadioConfigIndication;
- sp<V1_1::IRadioConfigResponse> mRadioConfigResponseV1_1;
- sp<V1_1::IRadioConfigIndication> mRadioConfigIndicationV1_1;
- sp<V1_2::IRadioConfigResponse> mRadioConfigResponseV1_2;
- sp<V1_2::IRadioConfigIndication> mRadioConfigIndicationV1_2;
- sp<V1_3::IRadioConfigResponse> mRadioConfigResponseV1_3;
- sp<V1_3::IRadioConfigIndication> mRadioConfigIndicationV1_3;
-
- // Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
- Return<void> setResponseFunctions(
- const sp<V1_0::IRadioConfigResponse>& radioConfigResponse,
- const sp<V1_0::IRadioConfigIndication>& radioConfigIndication);
- Return<void> getSimSlotsStatus(int32_t serial);
- Return<void> setSimSlotsMapping(int32_t serial, const hidl_vec<uint32_t>& slotMap);
-
- // Methods from ::android::hardware::radio::config::V1_1::IRadioConfig follow.
- Return<void> getPhoneCapability(int32_t serial);
- Return<void> setPreferredDataModem(int32_t serial, uint8_t modemId);
- Return<void> setModemsConfig(int32_t serial, const V1_1::ModemsConfig& modemsConfig);
- Return<void> getModemsConfig(int32_t serial);
-
- // Methods from ::android::hardware::radio::config::V1_3::IRadioConfig follow.
- Return<void> getPhoneCapability_1_3(int32_t serial);
-};
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIG_H
diff --git a/radio/config/1.3/default/RadioConfigIndication.cpp b/radio/config/1.3/default/RadioConfigIndication.cpp
deleted file mode 100644
index 608fa1c..0000000
--- a/radio/config/1.3/default/RadioConfigIndication.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "RadioConfigIndication.h"
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
-Return<void> RadioConfigIndication::simSlotsStatusChanged(
- RadioIndicationType /* type */, const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_2::IRadioConfigIndication follow.
-Return<void> RadioConfigIndication::simSlotsStatusChanged_1_2(
- RadioIndicationType /* type */, const hidl_vec<V1_2::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
diff --git a/radio/config/1.3/default/RadioConfigIndication.h b/radio/config/1.3/default/RadioConfigIndication.h
deleted file mode 100644
index c92446c..0000000
--- a/radio/config/1.3/default/RadioConfigIndication.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGINDICATION_H
-#define ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGINDICATION_H
-
-#include <android/hardware/radio/config/1.3/IRadioConfigIndication.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::V1_0;
-using namespace ::android::hardware::radio::config;
-
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-struct RadioConfigIndication : public IRadioConfigIndication {
- // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
- Return<void> simSlotsStatusChanged(RadioIndicationType type,
- const hidl_vec<V1_0::SimSlotStatus>& slotStatus) override;
-
- // Methods from ::android::hardware::radio::config::V1_2::IRadioConfigIndication follow.
- Return<void> simSlotsStatusChanged_1_2(
- RadioIndicationType type, const hidl_vec<V1_2::SimSlotStatus>& slotStatus) override;
-};
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGINDICATION_H
diff --git a/radio/config/1.3/default/RadioConfigResponse.cpp b/radio/config/1.3/default/RadioConfigResponse.cpp
deleted file mode 100644
index 1d48a13..0000000
--- a/radio/config/1.3/default/RadioConfigResponse.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "RadioConfigResponse.h"
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::setSimSlotsMappingResponse(const RadioResponseInfo& /* info */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_1::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
- const RadioResponseInfo& /* info */, const V1_1::PhoneCapability& /* phoneCapability */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::setPreferredDataModemResponse(
- const RadioResponseInfo& /* info */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::setModemsConfigResponse(const RadioResponseInfo& /* info */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::getModemsConfigResponse(
- const RadioResponseInfo& /* info */, const V1_1::ModemsConfig& /* modemsConfig */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_2::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_2::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_3::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse_1_3(
- const RadioResponseInfo& /* info */, const V1_3::PhoneCapability& /* phoneCapability */) {
- // TODO implement
- return Void();
-}
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
diff --git a/radio/config/1.3/default/RadioConfigResponse.h b/radio/config/1.3/default/RadioConfigResponse.h
deleted file mode 100644
index dc169bb..0000000
--- a/radio/config/1.3/default/RadioConfigResponse.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGRESPONSE_H
-#define ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGRESPONSE_H
-
-#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::config;
-
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-
-using ::android::hardware::radio::V1_0::RadioResponseInfo;
-
-struct RadioConfigResponse : public IRadioConfigResponse {
- // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
- Return<void> getSimSlotsStatusResponse(
- const RadioResponseInfo& info,
- const hidl_vec<V1_0::SimSlotStatus>& slotStatus) override;
- Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info) override;
-
- // Methods from ::android::hardware::radio::config::V1_1::IRadioConfigResponse follow.
- Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
- const V1_1::PhoneCapability& phoneCapability) override;
- Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info) override;
- Return<void> setModemsConfigResponse(const RadioResponseInfo& info) override;
- Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
- const V1_1::ModemsConfig& modemsConfig) override;
-
- // Methods from ::android::hardware::radio::config::V1_2::IRadioConfigResponse follow.
- Return<void> getSimSlotsStatusResponse_1_2(
- const RadioResponseInfo& info,
- const hidl_vec<V1_2::SimSlotStatus>& slotStatus) override;
-
- // Methods from ::android::hardware::radio::config::V1_3::IRadioConfigResponse follow.
- Return<void> getPhoneCapabilityResponse_1_3(
- const RadioResponseInfo& info, const V1_3::PhoneCapability& phoneCapability) override;
-};
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGRESPONSE_H
diff --git a/radio/config/1.3/default/android.hardware.radio.config@1.3-service.rc b/radio/config/1.3/default/android.hardware.radio.config@1.3-service.rc
deleted file mode 100644
index 6df9b52..0000000
--- a/radio/config/1.3/default/android.hardware.radio.config@1.3-service.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service vendor.radio-config-hal-1-3 /vendor/bin/hw/android.hardware.radio.config@1.3-service
- interface android.hardware.radio.config@1.0::IRadioConfig default
- interface android.hardware.radio.config@1.1::IRadioConfig default
- interface android.hardware.radio.config@1.3::IRadioConfig default
- class hal
- user system
- group system
diff --git a/radio/config/1.3/default/radio-config-default.xml b/radio/config/1.3/default/radio-config-default.xml
deleted file mode 100644
index 72f363e..0000000
--- a/radio/config/1.3/default/radio-config-default.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2019, The Android Open Source Project.
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<manifest version="1.0" type="device">
- <hal format="hidl">
- <name>android.hardware.radio.config</name>
- <transport>hwbinder</transport>
- <version>1.3</version>
- <interface>
- <name>IRadioConfig</name>
- <instance>default</instance>
- </interface>
- </hal>
-</manifest>
diff --git a/radio/config/1.3/default/service.cpp b/radio/config/1.3/default/service.cpp
deleted file mode 100644
index b1e6736..0000000
--- a/radio/config/1.3/default/service.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (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.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "android.hardware.radio.config@1.3-service"
-
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
-#include <hidl/HidlTransportSupport.h>
-
-#include "RadioConfig.h"
-
-using android::OK;
-using android::sp;
-using android::status_t;
-using android::hardware::configureRpcThreadpool;
-using android::hardware::joinRpcThreadpool;
-using android::hardware::radio::config::V1_3::IRadioConfig;
-using android::hardware::radio::config::V1_3::implementation::RadioConfig;
-
-int main() {
- configureRpcThreadpool(1, true);
- sp<IRadioConfig> radioConfig = new RadioConfig;
- const status_t status = radioConfig->registerAsService();
- ALOGW_IF(status != OK, "Could not register IRadioConfig 1.3");
- ALOGD("Default service is ready.");
-
- joinRpcThreadpool();
- return 1;
-}
diff --git a/radio/config/1.3/types.hal b/radio/config/1.3/types.hal
deleted file mode 100644
index aef0ff8..0000000
--- a/radio/config/1.3/types.hal
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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.radio.config@1.3;
-
-import android.hardware.radio@1.1::GeranBands;
-import android.hardware.radio@1.1::EutranBands;
-import android.hardware.radio@1.4::RadioAccessFamily;
-import android.hardware.radio@1.5::NgranBands;
-import android.hardware.radio@1.5::UtranBands;
-
-/** Type for the SIM slot. */
-enum SlotType : int32_t {
- /** Slot type for UICC/pSIM (physical SIM). */
- UICC = 1,
- /** Slot type for iUICC/iSIM (integrated SIM). */
- IUICC = 2,
- /** Slot type for eUICC/eSIM (embedded SIM). */
- EUICC = 3,
- /** Slot type for soft SIM (no physical SIM). */
- SOFT_SIM = 4,
-};
-
-/** A field in PhoneCapability that holds information about the SIM slot. */
-struct SimSlotCapability {
- /** Corresponds to physicalSlotId in Radio@1.2::CardStatus. */
- uint32_t physicalSlotId;
-
- /** Type of slot. */
- SlotType slotType;
-};
-
-/** Bitmask of features that can be supported by a single modem. */
-enum ModemFeatures : int32_t {
- /** 3GPP2 capability. */
- THREE_GPP2_REG = 1 << 0,
- /** 3GPP capability. */
- THREE_GPP_REG = 1 << 1,
- /** CDMA 2000 with EHRPD capability. */
- CDMA2000_EHRPD = 1 << 2,
- /** GSM/EDGE capability. */
- GERAN = 1 << 3,
- /** UMTS capability. */
- UTRAN = 1 << 4,
- /** LTE capability. */
- EUTRAN = 1 << 5,
- /** 5G capability. */
- NGRAN = 1 << 6,
- /** 5G dual connectivity capability. */
- EN_DC = 1 << 7,
- /** VoLTE capability (IMS registered). */
- PS_VOICE_REG = 1 << 8,
- /** CS voice call capability. */
- CS_VOICE_SESSION = 1 << 9,
- /** Internet connection capability. */
- INTERACTIVE_DATA_SESSION = 1 << 10,
- /** Dedicated bearer capability. */
- DEDICATED_BEARER = 1 << 11,
- /** Network scanning capability. */
- NETWORK_SCAN = 1 << 12,
- /** CDMA capability for SIM associated with modem. */
- CSIM_APP = 1 << 13,
-};
-
-struct ConcurrentModemFeatures {
- /**
- * A vector of concurrently supportable modem features across all modems.
- * Each entry in the vector is a bitfield of ModemFeatures that can be used
- * concurrently with the other ModemFeatures in that list.
- * Each bitfield must be the full set of features for a single modem.
- *
- * On a Dual-SIM device, each entry will be a vector of length 2.
- * The examples below depict the modemFeatures for four Dual-SIM setups:
- * 1. Only one modem can PS attach (IMS registered).
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG)
- * }
- * or
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * INTERACTIVE_DATA_SESSION),
- * (GERAN_REG | UTRAN_REG | CS_VOICE_SESSION)
- * }
- * 2. Both modems can PS attach (dual VoLTE).
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG)
- * }
- * 3. Both modems can maintain an Internet connection, but they share
- * one RF hardware.
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * INTERACTIVE_DATA_SESSION)
- * }
- * 4. Both modems can maintain an Internet connection, and they have
- * their own RF hardware.
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * INTERACTIVE_DATA_SESSION | DEDICATED_BEARER)
- * }
- */
- vec<bitfield<ModemFeatures>> modemFeatures;
-};
-
-/**
- * Overwritten from @1.1::PhoneCapability to add new capabilities and deprecate
- * maxActiveData, maxActiveInternetData, isInternetLingeringSupported, logicalModemList.
- * Replaces RadioConfig@1.1::ModemInfo and should replace Radio@1.4::RadioCapabilities
- * in the next major version upgrade. In the future, this should be extended instead of overwritten.
- */
-struct PhoneCapability {
- /**
- * 3GPP UE category for UTRAN downlink direction.
- * 25.306 Table 5.1a
- */
- uint8_t utranUeCategoryDl;
- /**
- * 3GPP UE category for UTRAN uplink direction.
- * 25.306 Table 5.1g
- */
- uint8_t utranUeCategoryUl;
- /**
- * 3GPP UE category for EUTRAN downlink direction.
- * 25.306 Table 4.1a
- */
- uint8_t eutranUeCategoryDl;
- /**
- * 3GPP UE category for EUTRAN uplink direction.
- * 25.306 Table 4.1a-2
- */
- uint8_t eutranUeCategoryUl;
-
- /**
- * Length of grace period for switching between logical modems, in milliseconds.
- * Used only when the number of logical modems is greater than the number of
- * Internet connections the device can support, otherwise must be 0.
- */
- uint64_t psDataConnectionLingerTimeMillis;
-
- vec<GeranBands> geranBands;
- vec<UtranBands> utranBands;
- vec<EutranBands> eutranBands;
- vec<NgranBands> ngranBands;
-
- /**
- * 32-bit bitmap of supported Radio@1.4::RadioAccessFamily types.
- * Note that RadioAccessFamily is actually the radio access technologies, so it should be
- * renamed in the next major version upgrade.
- */
- bitfield<RadioAccessFamily> supportedRats;
-
- /**
- * List of unique logical modem UUIDs from Radio@1.4::RadioCapabilities.
- * A UUID is typically "com.xxxx.lmX" where X is the logical modem ID.
- * Must be equal to the number of logical modems in the device.
- * Radio@1.2::RadioConst::MAX_UUID_LENGTH is the max length of each UUID.
- */
- vec<string> logicalModemUuids;
-
- /**
- * List of SIM slot capabilities. The order of physical slot IDs must correspond to
- * the order of modems in logicalModemUuids.
- */
- vec<SimSlotCapability> simSlotCapabilities;
-
- /**
- * A vector of all sets of concurrently supportable modem feature sets. The order of modems
- * in modemFeatures must correspond to the order of modems in logicalModemUuids.
- * Each entry in concurrentFeatureSupport is independent of others in the list
- * and represents a set of concurrently supportable features across all modems.
- * Each entry in ConcurrentModemFeatures::modemFeatures is a bitfield of
- * concurrently supported ModemFeatures for one modem.
- */
- vec<ConcurrentModemFeatures> concurrentFeatureSupport;
-};
diff --git a/radio/config/1.3/vts/functional/Android.bp b/radio/config/1.3/vts/functional/Android.bp
deleted file mode 100644
index 6b28faf..0000000
--- a/radio/config/1.3/vts/functional/Android.bp
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_test {
- name: "VtsHalRadioConfigV1_3TargetTest",
- defaults: ["VtsHalTargetTestDefaults"],
- srcs: [
- "radio_config_hidl_hal_api.cpp",
- "radio_config_hidl_hal_test.cpp",
- "radio_config_response.cpp",
- "VtsHalRadioConfigV1_3TargetTest.cpp",
- ],
- static_libs: [
- "RadioVtsTestUtilBase",
- "android.hardware.radio.config@1.0",
- "android.hardware.radio.config@1.1",
- "android.hardware.radio.config@1.2",
- "android.hardware.radio.config@1.3",
- ],
- header_libs: ["radio.util.header@1.0"],
- test_suites: ["general-tests", "vts-core"],
-}
diff --git a/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp b/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
deleted file mode 100644
index 3bacacf..0000000
--- a/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-INSTANTIATE_TEST_SUITE_P(
- PerInstance, RadioConfigHidlTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(
- ::android::hardware::radio::config::V1_3::IRadioConfig::descriptor)),
- android::hardware::PrintInstanceNameToString);
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
deleted file mode 100644
index 7f90023..0000000
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
-
-/*
- * Test IRadioConfig.getPhoneCapability_1_3()
- */
-TEST_P(RadioConfigHidlTest, getPhoneCapability_1_3) {
- serial = GetRandomSerialNumber();
- Return<void> res = radioConfig->getPhoneCapability_1_3(serial);
- ASSERT_OK(res);
- EXPECT_EQ(std::cv_status::no_timeout, wait());
- EXPECT_EQ(RadioResponseType::SOLICITED, radioConfigRsp->rspInfo.type);
- EXPECT_EQ(serial, radioConfigRsp->rspInfo.serial);
- ALOGI("getPhoneCapability_1_3, rspInfo.error = %s\n",
- toString(radioConfigRsp->rspInfo.error).c_str());
-
- ASSERT_TRUE(CheckAnyOfErrors(
- radioConfigRsp->rspInfo.error,
- {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::INTERNAL_ERR}));
-
- if (radioConfigRsp->rspInfo.error == RadioError ::NONE) {
- int numModems = radioConfigRsp->phoneCap_1_3.logicalModemUuids.size();
- EXPECT_GE(numModems, 0);
- // length of simSlotCapabilities should be equal to length of logicalModemUuids.
- EXPECT_EQ(numModems, radioConfigRsp->phoneCap_1_3.simSlotCapabilities.size());
- // length of modemFeatures in each ConcurrentModemFeatures should be
- // equal to length of logicalModemUuids.
- for (V1_3::ConcurrentModemFeatures cmf :
- radioConfigRsp->phoneCap_1_3.concurrentFeatureSupport) {
- EXPECT_EQ(numModems, cmf.modemFeatures.size());
- }
- }
-}
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
deleted file mode 100644
index cd48b25..0000000
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-void RadioConfigHidlTest::SetUp() {
- radioConfig = V1_3::IRadioConfig::getService(GetParam());
- ASSERT_NE(nullptr, radioConfig.get());
-
- radioConfigRsp = new (std::nothrow) RadioConfigResponse(*this);
- ASSERT_NE(nullptr, radioConfigRsp.get());
-
- count_ = 0;
-
- radioConfig->setResponseFunctions(radioConfigRsp, nullptr);
-}
-
-/*
- * Notify that the response message is received.
- */
-void RadioConfigHidlTest::notify(int receivedSerial) {
- std::unique_lock<std::mutex> lock(mtx_);
- if (serial == receivedSerial) {
- count_++;
- cv_.notify_one();
- }
-}
-
-/*
- * Wait till the response message is notified or till TIMEOUT_PERIOD.
- */
-std::cv_status RadioConfigHidlTest::wait() {
- std::unique_lock<std::mutex> lock(mtx_);
-
- std::cv_status status = std::cv_status::no_timeout;
- auto now = std::chrono::system_clock::now();
- while (count_ == 0) {
- status = cv_.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
- if (status == std::cv_status::timeout) {
- return status;
- }
- }
- count_--;
- return status;
-}
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
deleted file mode 100644
index b21c7c0..0000000
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/logging.h>
-
-#include <chrono>
-#include <condition_variable>
-#include <mutex>
-
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
-#include <android/hardware/radio/config/1.3/IRadioConfigIndication.h>
-#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
-#include <android/hardware/radio/config/1.3/types.h>
-
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-#include <log/log.h>
-
-#include "vts_test_util.h"
-
-using namespace ::android::hardware::radio::config;
-
-using ::android::sp;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-using ::android::hardware::radio::V1_0::RadioIndicationType;
-using ::android::hardware::radio::V1_0::RadioResponseInfo;
-using ::android::hardware::radio::V1_0::RadioResponseType;
-
-#define TIMEOUT_PERIOD 75
-
-class RadioConfigHidlTest;
-
-/* Callback class for radio config response */
-class RadioConfigResponse : public V1_3::IRadioConfigResponse {
- protected:
- RadioConfigHidlTest& parent;
-
- public:
- RadioResponseInfo rspInfo;
- V1_1::PhoneCapability phoneCap_1_1;
- V1_3::PhoneCapability phoneCap_1_3;
-
- RadioConfigResponse(RadioConfigHidlTest& parent);
- virtual ~RadioConfigResponse() = default;
-
- /* 1.0 Api */
- Return<void> getSimSlotsStatusResponse(const RadioResponseInfo& info,
- const hidl_vec<V1_0::SimSlotStatus>& slotStatus);
-
- Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info);
-
- /* 1.1 Api */
- Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
- const V1_1::PhoneCapability& phoneCapability);
-
- Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info);
-
- Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
- const V1_1::ModemsConfig& mConfig);
-
- Return<void> setModemsConfigResponse(const RadioResponseInfo& info);
-
- /* 1.2 Api */
- Return<void> getSimSlotsStatusResponse_1_2(const RadioResponseInfo& info,
- const hidl_vec<V1_2::SimSlotStatus>& slotStatus);
-
- /* 1.3 Api */
- Return<void> getPhoneCapabilityResponse_1_3(const RadioResponseInfo& info,
- const V1_3::PhoneCapability& phoneCapability);
-};
-
-/* Callback class for radio config indication */
-class RadioConfigIndication : public V1_3::IRadioConfigIndication {
- protected:
- RadioConfigHidlTest& parent;
-
- public:
- RadioConfigIndication(RadioConfigHidlTest& parent);
- virtual ~RadioConfigIndication() = default;
-
- /* 1.2 Api */
- Return<void> simSlotsStatusChanged_1_2(RadioIndicationType type,
- const hidl_vec<V1_2::SimSlotStatus>& slotStatus);
-};
-
-// The main test class for Radio config HIDL.
-class RadioConfigHidlTest : public ::testing::TestWithParam<std::string> {
- protected:
- std::mutex mtx_;
- std::condition_variable cv_;
- int count_;
-
- public:
- virtual void SetUp() override;
-
- /* Used as a mechanism to inform the test about data/event callback */
- void notify(int receivedSerial);
-
- /* Test code calls this function to wait for response */
- std::cv_status wait();
-
- void updateSimCardStatus();
-
- /* Serial number for radio request */
- int serial;
-
- /* radio config service handle */
- sp<V1_3::IRadioConfig> radioConfig;
-
- /* radio config response handle */
- sp<RadioConfigResponse> radioConfigRsp;
-};
diff --git a/radio/config/1.3/vts/functional/radio_config_response.cpp b/radio/config/1.3/vts/functional/radio_config_response.cpp
deleted file mode 100644
index 22098d3..0000000
--- a/radio/config/1.3/vts/functional/radio_config_response.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-using namespace ::android::hardware::radio::config;
-
-using ::android::hardware::hidl_vec;
-
-using ::android::hardware::radio::V1_0::RadioResponseInfo;
-
-RadioConfigResponse::RadioConfigResponse(RadioConfigHidlTest& parent) : parent(parent) {}
-
-/* 1.0 Apis */
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
- return Void();
-}
-
-Return<void> RadioConfigResponse::setSimSlotsMappingResponse(const RadioResponseInfo& /* info */) {
- return Void();
-}
-
-/* 1.1 Apis */
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
- const RadioResponseInfo& info, const V1_1::PhoneCapability& phoneCapability) {
- rspInfo = info;
- phoneCap_1_1 = phoneCapability;
- parent.notify(info.serial);
- return Void();
-}
-
-Return<void> RadioConfigResponse::setPreferredDataModemResponse(
- const RadioResponseInfo& /* info */) {
- return Void();
-}
-
-Return<void> RadioConfigResponse::getModemsConfigResponse(const RadioResponseInfo& /* info */,
- const V1_1::ModemsConfig& /* mConfig */) {
- return Void();
-}
-
-Return<void> RadioConfigResponse::setModemsConfigResponse(const RadioResponseInfo& /* info */) {
- return Void();
-}
-
-/* 1.2 Apis */
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_2::SimSlotStatus>& /* slotStatus */) {
- return Void();
-}
-
-/* 1.3 Apis */
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse_1_3(
- const RadioResponseInfo& info, const V1_3::PhoneCapability& phoneCapability) {
- rspInfo = info;
- phoneCap_1_3 = phoneCapability;
- parent.notify(info.serial);
- return Void();
-}
diff --git a/sensors/1.0/vts/functional/Android.bp b/sensors/1.0/vts/functional/Android.bp
index 1167fd4..aaefccb 100644
--- a/sensors/1.0/vts/functional/Android.bp
+++ b/sensors/1.0/vts/functional/Android.bp
@@ -20,7 +20,7 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
"SensorsHidlEnvironmentV1_0.cpp",
- "VtsHalSensorsV1_0TargetTest.cpp"
+ "VtsHalSensorsV1_0TargetTest.cpp",
],
static_libs: [
"android.hardware.graphics.allocator@2.0",
@@ -31,6 +31,8 @@
"android.hardware.sensors@1.0",
"VtsHalSensorsTargetTestUtils",
],
- test_suites: ["general-tests", "vts-core"],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
}
-
diff --git a/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.h b/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.h
index 29bfa50..485ed1e 100644
--- a/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.h
+++ b/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.h
@@ -29,8 +29,9 @@
using ::android::sp;
class SensorsHidlTest;
-class SensorsHidlEnvironmentV1_0 : public SensorsHidlEnvironmentBase {
- public:
+class SensorsHidlEnvironmentV1_0
+ : public SensorsHidlEnvironmentBase<::android::hardware::sensors::V1_0::Event> {
+ public:
using Event = ::android::hardware::sensors::V1_0::Event;
SensorsHidlEnvironmentV1_0(const std::string& service_name)
: SensorsHidlEnvironmentBase(service_name) {}
diff --git a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
index 2cad54d..e298651 100644
--- a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
+++ b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
@@ -33,8 +33,7 @@
using namespace ::android::hardware::sensors::V1_0;
// The main test class for SENSORS HIDL HAL.
-
-class SensorsHidlTest : public SensorsHidlTestBase {
+class SensorsHidlTest : public SensorsHidlTestBase<SensorType, Event, SensorInfo> {
public:
virtual void SetUp() override {
mEnvironment = new SensorsHidlEnvironmentV1_0(GetParam());
@@ -80,7 +79,7 @@
inline sp<ISensors>& S() { return mEnvironment->sensors; }
- SensorsHidlEnvironmentBase* getEnvironment() override { return mEnvironment; }
+ SensorsHidlEnvironmentBase<Event>* getEnvironment() override { return mEnvironment; }
private:
// Test environment for sensors HAL.
@@ -257,55 +256,55 @@
// Test if sensor hal can do UI speed accelerometer streaming properly
TEST_P(SensorsHidlTest, AccelerometerStreamingOperationSlow) {
testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(200),
- std::chrono::seconds(5), sAccelNormChecker);
+ std::chrono::seconds(5), mAccelNormChecker);
}
// Test if sensor hal can do normal speed accelerometer streaming properly
TEST_P(SensorsHidlTest, AccelerometerStreamingOperationNormal) {
testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(20),
- std::chrono::seconds(5), sAccelNormChecker);
+ std::chrono::seconds(5), mAccelNormChecker);
}
// Test if sensor hal can do game speed accelerometer streaming properly
TEST_P(SensorsHidlTest, AccelerometerStreamingOperationFast) {
testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(5),
- std::chrono::seconds(5), sAccelNormChecker);
+ std::chrono::seconds(5), mAccelNormChecker);
}
// Test if sensor hal can do UI speed gyroscope streaming properly
TEST_P(SensorsHidlTest, GyroscopeStreamingOperationSlow) {
testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(200),
- std::chrono::seconds(5), sGyroNormChecker);
+ std::chrono::seconds(5), mGyroNormChecker);
}
// Test if sensor hal can do normal speed gyroscope streaming properly
TEST_P(SensorsHidlTest, GyroscopeStreamingOperationNormal) {
testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(20),
- std::chrono::seconds(5), sGyroNormChecker);
+ std::chrono::seconds(5), mGyroNormChecker);
}
// Test if sensor hal can do game speed gyroscope streaming properly
TEST_P(SensorsHidlTest, GyroscopeStreamingOperationFast) {
testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(5),
- std::chrono::seconds(5), sGyroNormChecker);
+ std::chrono::seconds(5), mGyroNormChecker);
}
// Test if sensor hal can do UI speed magnetometer streaming properly
TEST_P(SensorsHidlTest, MagnetometerStreamingOperationSlow) {
testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(200),
- std::chrono::seconds(5), NullChecker());
+ std::chrono::seconds(5), NullChecker<Event>());
}
// Test if sensor hal can do normal speed magnetometer streaming properly
TEST_P(SensorsHidlTest, MagnetometerStreamingOperationNormal) {
testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(20),
- std::chrono::seconds(5), NullChecker());
+ std::chrono::seconds(5), NullChecker<Event>());
}
// Test if sensor hal can do game speed magnetometer streaming properly
TEST_P(SensorsHidlTest, MagnetometerStreamingOperationFast) {
testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(5),
- std::chrono::seconds(5), NullChecker());
+ std::chrono::seconds(5), NullChecker<Event>());
}
// Test if sensor hal can do accelerometer sampling rate switch properly when sensor is active
@@ -344,109 +343,109 @@
// Test sensor event direct report with ashmem for accel sensor at normal rate
TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationNormal) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM, RateLevel::NORMAL,
- sAccelNormChecker);
+ mAccelNormChecker);
}
// Test sensor event direct report with ashmem for accel sensor at fast rate
TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationFast) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM, RateLevel::FAST,
- sAccelNormChecker);
+ mAccelNormChecker);
}
// Test sensor event direct report with ashmem for accel sensor at very fast rate
TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM,
- RateLevel::VERY_FAST, sAccelNormChecker);
+ RateLevel::VERY_FAST, mAccelNormChecker);
}
// Test sensor event direct report with ashmem for gyro sensor at normal rate
TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationNormal) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::NORMAL,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with ashmem for gyro sensor at fast rate
TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationFast) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::FAST,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with ashmem for gyro sensor at very fast rate
TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::VERY_FAST,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with ashmem for mag sensor at normal rate
TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationNormal) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM, RateLevel::NORMAL,
- NullChecker());
+ NullChecker<Event>());
}
// Test sensor event direct report with ashmem for mag sensor at fast rate
TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationFast) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM, RateLevel::FAST,
- NullChecker());
+ NullChecker<Event>());
}
// Test sensor event direct report with ashmem for mag sensor at very fast rate
TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM,
- RateLevel::VERY_FAST, NullChecker());
+ RateLevel::VERY_FAST, NullChecker<Event>());
}
// Test sensor event direct report with gralloc for accel sensor at normal rate
TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationNormal) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC, RateLevel::NORMAL,
- sAccelNormChecker);
+ mAccelNormChecker);
}
// Test sensor event direct report with gralloc for accel sensor at fast rate
TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationFast) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC, RateLevel::FAST,
- sAccelNormChecker);
+ mAccelNormChecker);
}
// Test sensor event direct report with gralloc for accel sensor at very fast rate
TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC,
- RateLevel::VERY_FAST, sAccelNormChecker);
+ RateLevel::VERY_FAST, mAccelNormChecker);
}
// Test sensor event direct report with gralloc for gyro sensor at normal rate
TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationNormal) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::NORMAL,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with gralloc for gyro sensor at fast rate
TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationFast) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::FAST,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with gralloc for gyro sensor at very fast rate
TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::VERY_FAST,
- sGyroNormChecker);
+ mGyroNormChecker);
}
// Test sensor event direct report with gralloc for mag sensor at normal rate
TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationNormal) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC, RateLevel::NORMAL,
- NullChecker());
+ NullChecker<Event>());
}
// Test sensor event direct report with gralloc for mag sensor at fast rate
TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationFast) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC, RateLevel::FAST,
- NullChecker());
+ NullChecker<Event>());
}
// Test sensor event direct report with gralloc for mag sensor at very fast rate
TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationVeryFast) {
testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC,
- RateLevel::VERY_FAST, NullChecker());
+ RateLevel::VERY_FAST, NullChecker<Event>());
}
INSTANTIATE_TEST_SUITE_P(
diff --git a/sensors/2.0/default/Android.bp b/sensors/2.0/default/Android.bp
index 62c9487..bb38327 100644
--- a/sensors/2.0/default/Android.bp
+++ b/sensors/2.0/default/Android.bp
@@ -20,13 +20,17 @@
relative_install_path: "hw",
srcs: [
"service.cpp",
- "Sensor.cpp",
- "Sensors.cpp",
],
init_rc: ["android.hardware.sensors@2.0-service-mock.rc"],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
shared_libs: [
"android.hardware.sensors@1.0",
"android.hardware.sensors@2.0",
+ // Needed to compile some shared utilities for both 2.0/2.1 impls, but
+ // isn't normally needed for a HAL that only supports 2.0.
+ "android.hardware.sensors@2.1",
"libcutils",
"libfmq",
"libhidlbase",
@@ -34,5 +38,9 @@
"libpower",
"libutils",
],
+ static_libs: [
+ "android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors@2.X-shared-impl",
+ ],
vintf_fragments: ["android.hardware.sensors@2.0.xml"],
}
diff --git a/sensors/2.0/default/Sensors.cpp b/sensors/2.0/default/Sensors.cpp
deleted file mode 100644
index 23dd26b..0000000
--- a/sensors/2.0/default/Sensors.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Sensors.h"
-
-#include <android/hardware/sensors/2.0/types.h>
-#include <log/log.h>
-
-namespace android {
-namespace hardware {
-namespace sensors {
-namespace V2_0 {
-namespace implementation {
-
-using ::android::hardware::sensors::V1_0::Event;
-using ::android::hardware::sensors::V1_0::OperationMode;
-using ::android::hardware::sensors::V1_0::RateLevel;
-using ::android::hardware::sensors::V1_0::Result;
-using ::android::hardware::sensors::V1_0::SharedMemInfo;
-using ::android::hardware::sensors::V2_0::SensorTimeout;
-using ::android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
-
-constexpr const char* kWakeLockName = "SensorsHAL_WAKEUP";
-
-Sensors::Sensors()
- : mEventQueueFlag(nullptr),
- mNextHandle(1),
- mOutstandingWakeUpEvents(0),
- mReadWakeLockQueueRun(false),
- mAutoReleaseWakeLockTime(0),
- mHasWakeLock(false) {
- AddSensor<AccelSensor>();
- AddSensor<GyroSensor>();
- AddSensor<AmbientTempSensor>();
- AddSensor<DeviceTempSensor>();
- AddSensor<PressureSensor>();
- AddSensor<MagnetometerSensor>();
- AddSensor<LightSensor>();
- AddSensor<ProximitySensor>();
- AddSensor<RelativeHumiditySensor>();
-}
-
-Sensors::~Sensors() {
- deleteEventFlag();
- mReadWakeLockQueueRun = false;
- mWakeLockThread.join();
-}
-
-// Methods from ::android::hardware::sensors::V2_0::ISensors follow.
-Return<void> Sensors::getSensorsList(getSensorsList_cb _hidl_cb) {
- std::vector<SensorInfo> sensors;
- for (const auto& sensor : mSensors) {
- sensors.push_back(sensor.second->getSensorInfo());
- }
-
- // Call the HIDL callback with the SensorInfo
- _hidl_cb(sensors);
-
- return Void();
-}
-
-Return<Result> Sensors::setOperationMode(OperationMode mode) {
- for (auto sensor : mSensors) {
- sensor.second->setOperationMode(mode);
- }
- return Result::OK;
-}
-
-Return<Result> Sensors::activate(int32_t sensorHandle, bool enabled) {
- auto sensor = mSensors.find(sensorHandle);
- if (sensor != mSensors.end()) {
- sensor->second->activate(enabled);
- return Result::OK;
- }
- return Result::BAD_VALUE;
-}
-
-Return<Result> Sensors::initialize(
- const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
- const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
- const sp<ISensorsCallback>& sensorsCallback) {
- Result result = Result::OK;
-
- // Ensure that all sensors are disabled
- for (auto sensor : mSensors) {
- sensor.second->activate(false /* enable */);
- }
-
- // Stop the Wake Lock thread if it is currently running
- if (mReadWakeLockQueueRun.load()) {
- mReadWakeLockQueueRun = false;
- mWakeLockThread.join();
- }
-
- // Save a reference to the callback
- mCallback = sensorsCallback;
-
- // Create the Event FMQ from the eventQueueDescriptor. Reset the read/write positions.
- mEventQueue =
- std::make_unique<EventMessageQueue>(eventQueueDescriptor, true /* resetPointers */);
-
- // Ensure that any existing EventFlag is properly deleted
- deleteEventFlag();
-
- // Create the EventFlag that is used to signal to the framework that sensor events have been
- // written to the Event FMQ
- if (EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag) != OK) {
- result = Result::BAD_VALUE;
- }
-
- // Create the Wake Lock FMQ that is used by the framework to communicate whenever WAKE_UP
- // events have been successfully read and handled by the framework.
- mWakeLockQueue =
- std::make_unique<WakeLockMessageQueue>(wakeLockDescriptor, true /* resetPointers */);
-
- if (!mCallback || !mEventQueue || !mWakeLockQueue || mEventQueueFlag == nullptr) {
- result = Result::BAD_VALUE;
- }
-
- // Start the thread to read events from the Wake Lock FMQ
- mReadWakeLockQueueRun = true;
- mWakeLockThread = std::thread(startReadWakeLockThread, this);
-
- return result;
-}
-
-Return<Result> Sensors::batch(int32_t sensorHandle, int64_t samplingPeriodNs,
- int64_t /* maxReportLatencyNs */) {
- auto sensor = mSensors.find(sensorHandle);
- if (sensor != mSensors.end()) {
- sensor->second->batch(samplingPeriodNs);
- return Result::OK;
- }
- return Result::BAD_VALUE;
-}
-
-Return<Result> Sensors::flush(int32_t sensorHandle) {
- auto sensor = mSensors.find(sensorHandle);
- if (sensor != mSensors.end()) {
- return sensor->second->flush();
- }
- return Result::BAD_VALUE;
-}
-
-Return<Result> Sensors::injectSensorData(const Event& event) {
- auto sensor = mSensors.find(event.sensorHandle);
- if (sensor != mSensors.end()) {
- return sensor->second->injectEvent(event);
- }
-
- return Result::BAD_VALUE;
-}
-
-Return<void> Sensors::registerDirectChannel(const SharedMemInfo& /* mem */,
- registerDirectChannel_cb _hidl_cb) {
- _hidl_cb(Result::INVALID_OPERATION, -1 /* channelHandle */);
- return Return<void>();
-}
-
-Return<Result> Sensors::unregisterDirectChannel(int32_t /* channelHandle */) {
- return Result::INVALID_OPERATION;
-}
-
-Return<void> Sensors::configDirectReport(int32_t /* sensorHandle */, int32_t /* channelHandle */,
- RateLevel /* rate */, configDirectReport_cb _hidl_cb) {
- _hidl_cb(Result::INVALID_OPERATION, 0 /* reportToken */);
- return Return<void>();
-}
-
-void Sensors::postEvents(const std::vector<Event>& events, bool wakeup) {
- std::lock_guard<std::mutex> lock(mWriteLock);
- if (mEventQueue->write(events.data(), events.size())) {
- mEventQueueFlag->wake(static_cast<uint32_t>(EventQueueFlagBits::READ_AND_PROCESS));
-
- if (wakeup) {
- // Keep track of the number of outstanding WAKE_UP events in order to properly hold
- // a wake lock until the framework has secured a wake lock
- updateWakeLock(events.size(), 0 /* eventsHandled */);
- }
- }
-}
-
-void Sensors::updateWakeLock(int32_t eventsWritten, int32_t eventsHandled) {
- std::lock_guard<std::mutex> lock(mWakeLockLock);
- int32_t newVal = mOutstandingWakeUpEvents + eventsWritten - eventsHandled;
- if (newVal < 0) {
- mOutstandingWakeUpEvents = 0;
- } else {
- mOutstandingWakeUpEvents = newVal;
- }
-
- if (eventsWritten > 0) {
- // Update the time at which the last WAKE_UP event was sent
- mAutoReleaseWakeLockTime = ::android::uptimeMillis() +
- static_cast<uint32_t>(SensorTimeout::WAKE_LOCK_SECONDS) * 1000;
- }
-
- if (!mHasWakeLock && mOutstandingWakeUpEvents > 0 &&
- acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLockName) == 0) {
- mHasWakeLock = true;
- } else if (mHasWakeLock) {
- // Check if the wake lock should be released automatically if
- // SensorTimeout::WAKE_LOCK_SECONDS has elapsed since the last WAKE_UP event was written to
- // the Wake Lock FMQ.
- if (::android::uptimeMillis() > mAutoReleaseWakeLockTime) {
- ALOGD("No events read from wake lock FMQ for %d seconds, auto releasing wake lock",
- SensorTimeout::WAKE_LOCK_SECONDS);
- mOutstandingWakeUpEvents = 0;
- }
-
- if (mOutstandingWakeUpEvents == 0 && release_wake_lock(kWakeLockName) == 0) {
- mHasWakeLock = false;
- }
- }
-}
-
-void Sensors::readWakeLockFMQ() {
- while (mReadWakeLockQueueRun.load()) {
- constexpr int64_t kReadTimeoutNs = 500 * 1000 * 1000; // 500 ms
- uint32_t eventsHandled = 0;
-
- // Read events from the Wake Lock FMQ. Timeout after a reasonable amount of time to ensure
- // that any held wake lock is able to be released if it is held for too long.
- mWakeLockQueue->readBlocking(&eventsHandled, 1 /* count */, 0 /* readNotification */,
- static_cast<uint32_t>(WakeLockQueueFlagBits::DATA_WRITTEN),
- kReadTimeoutNs);
- updateWakeLock(0 /* eventsWritten */, eventsHandled);
- }
-}
-
-void Sensors::startReadWakeLockThread(Sensors* sensors) {
- sensors->readWakeLockFMQ();
-}
-
-void Sensors::deleteEventFlag() {
- status_t status = EventFlag::deleteEventFlag(&mEventQueueFlag);
- if (status != OK) {
- ALOGI("Failed to delete event flag: %d", status);
- }
-}
-
-} // namespace implementation
-} // namespace V2_0
-} // namespace sensors
-} // namespace hardware
-} // namespace android
diff --git a/sensors/2.0/default/Sensors.h b/sensors/2.0/default/Sensors.h
deleted file mode 100644
index d06dd78..0000000
--- a/sensors/2.0/default/Sensors.h
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_SENSORS_V2_0_SENSORS_H
-#define ANDROID_HARDWARE_SENSORS_V2_0_SENSORS_H
-
-#include "Sensor.h"
-
-#include <android/hardware/sensors/2.0/ISensors.h>
-#include <fmq/MessageQueue.h>
-#include <hardware_legacy/power.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-#include <atomic>
-#include <memory>
-#include <thread>
-
-namespace android {
-namespace hardware {
-namespace sensors {
-namespace V2_0 {
-namespace implementation {
-
-using ::android::sp;
-using ::android::hardware::EventFlag;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::MessageQueue;
-using ::android::hardware::MQDescriptor;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-struct Sensors : public ISensors, public ISensorsEventCallback {
- using Event = ::android::hardware::sensors::V1_0::Event;
- using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
- using RateLevel = ::android::hardware::sensors::V1_0::RateLevel;
- using Result = ::android::hardware::sensors::V1_0::Result;
- using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo;
-
- Sensors();
- virtual ~Sensors();
-
- // Methods from ::android::hardware::sensors::V2_0::ISensors follow.
- Return<void> getSensorsList(getSensorsList_cb _hidl_cb) override;
-
- Return<Result> setOperationMode(OperationMode mode) override;
-
- Return<Result> activate(int32_t sensorHandle, bool enabled) override;
-
- Return<Result> initialize(
- const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
- const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
- const sp<ISensorsCallback>& sensorsCallback) override;
-
- Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
- int64_t maxReportLatencyNs) override;
-
- Return<Result> flush(int32_t sensorHandle) override;
-
- Return<Result> injectSensorData(const Event& event) override;
-
- Return<void> registerDirectChannel(const SharedMemInfo& mem,
- registerDirectChannel_cb _hidl_cb) override;
-
- Return<Result> unregisterDirectChannel(int32_t channelHandle) override;
-
- Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
- configDirectReport_cb _hidl_cb) override;
-
- void postEvents(const std::vector<Event>& events, bool wakeup) override;
-
- private:
- /**
- * Add a new sensor
- */
- template <class SensorType>
- void AddSensor() {
- std::shared_ptr<SensorType> sensor =
- std::make_shared<SensorType>(mNextHandle++ /* sensorHandle */, this /* callback */);
- mSensors[sensor->getSensorInfo().sensorHandle] = sensor;
- }
-
- /**
- * Utility function to delete the Event Flag
- */
- void deleteEventFlag();
-
- /**
- * Function to read the Wake Lock FMQ and release the wake lock when appropriate
- */
- void readWakeLockFMQ();
-
- static void startReadWakeLockThread(Sensors* sensors);
-
- /**
- * Responsible for acquiring and releasing a wake lock when there are unhandled WAKE_UP events
- */
- void updateWakeLock(int32_t eventsWritten, int32_t eventsHandled);
-
- using EventMessageQueue = MessageQueue<Event, kSynchronizedReadWrite>;
- using WakeLockMessageQueue = MessageQueue<uint32_t, kSynchronizedReadWrite>;
-
- /**
- * The Event FMQ where sensor events are written
- */
- std::unique_ptr<EventMessageQueue> mEventQueue;
-
- /**
- * The Wake Lock FMQ that is read to determine when the framework has handled WAKE_UP events
- */
- std::unique_ptr<WakeLockMessageQueue> mWakeLockQueue;
-
- /**
- * Event Flag to signal to the framework when sensor events are available to be read
- */
- EventFlag* mEventQueueFlag;
-
- /**
- * Callback for asynchronous events, such as dynamic sensor connections.
- */
- sp<ISensorsCallback> mCallback;
-
- /**
- * A map of the available sensors
- */
- std::map<int32_t, std::shared_ptr<Sensor>> mSensors;
-
- /**
- * The next available sensor handle
- */
- int32_t mNextHandle;
-
- /**
- * Lock to protect writes to the FMQs
- */
- std::mutex mWriteLock;
-
- /**
- * Lock to protect acquiring and releasing the wake lock
- */
- std::mutex mWakeLockLock;
-
- /**
- * Track the number of WAKE_UP events that have not been handled by the framework
- */
- uint32_t mOutstandingWakeUpEvents;
-
- /**
- * A thread to read the Wake Lock FMQ
- */
- std::thread mWakeLockThread;
-
- /**
- * Flag to indicate that the Wake Lock Thread should continue to run
- */
- std::atomic_bool mReadWakeLockQueueRun;
-
- /**
- * Track the time when the wake lock should automatically be released
- */
- int64_t mAutoReleaseWakeLockTime;
-
- /**
- * Flag to indicate if a wake lock has been acquired
- */
- bool mHasWakeLock;
-};
-
-} // namespace implementation
-} // namespace V2_0
-} // namespace sensors
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_SENSORS_V2_0_SENSORS_H
diff --git a/sensors/2.0/default/SensorsV2_0.h b/sensors/2.0/default/SensorsV2_0.h
new file mode 100644
index 0000000..345835a
--- /dev/null
+++ b/sensors/2.0/default/SensorsV2_0.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SENSORS_V2_0_H
+#define ANDROID_HARDWARE_SENSORS_V2_0_H
+
+#include "Sensors.h"
+
+#include <android/hardware/sensors/2.0/ISensors.h>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_0 {
+namespace implementation {
+
+struct SensorsV2_0 : public ::android::hardware::sensors::V2_X::implementation::Sensors<ISensors> {
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_0_H
\ No newline at end of file
diff --git a/sensors/2.0/default/service.cpp b/sensors/2.0/default/service.cpp
index 5c13e33..e20bf85 100644
--- a/sensors/2.0/default/service.cpp
+++ b/sensors/2.0/default/service.cpp
@@ -20,17 +20,17 @@
#include <hidl/HidlTransportSupport.h>
#include <log/log.h>
#include <utils/StrongPointer.h>
-#include "Sensors.h"
+#include "SensorsV2_0.h"
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::hardware::sensors::V2_0::ISensors;
-using android::hardware::sensors::V2_0::implementation::Sensors;
+using android::hardware::sensors::V2_0::implementation::SensorsV2_0;
int main(int /* argc */, char** /* argv */) {
configureRpcThreadpool(1, true);
- android::sp<ISensors> sensors = new Sensors();
+ android::sp<ISensors> sensors = new SensorsV2_0();
if (sensors->registerAsService() != ::android::OK) {
ALOGE("Failed to register Sensors HAL instance");
return -1;
diff --git a/sensors/2.0/vts/functional/Android.bp b/sensors/2.0/vts/functional/Android.bp
index 4765fa2..08c59b6 100644
--- a/sensors/2.0/vts/functional/Android.bp
+++ b/sensors/2.0/vts/functional/Android.bp
@@ -19,8 +19,10 @@
cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
- "SensorsHidlEnvironmentV2_0.cpp",
- "VtsHalSensorsV2_0TargetTest.cpp"
+ "VtsHalSensorsV2_0TargetTest.cpp",
+ ],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
],
static_libs: [
"android.hardware.graphics.allocator@2.0",
@@ -29,9 +31,15 @@
"android.hardware.graphics.mapper@2.1",
"android.hardware.graphics.mapper@3.0",
"android.hardware.sensors@1.0",
+ "android.hardware.sensors@1.0-convert",
"android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
"libfmq",
"VtsHalSensorsTargetTestUtils",
+ "VtsHalSensorsV2_0TargetTest-lib",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
],
}
-
diff --git a/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp b/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
index 540529d..8895350 100644
--- a/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
+++ b/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
@@ -14,1130 +14,19 @@
* limitations under the License.
*/
-#include "SensorsHidlEnvironmentV2_0.h"
-#include "sensors-vts-utils/SensorsHidlTestBase.h"
-#include "sensors-vts-utils/SensorsTestSharedMemory.h"
+#include "VtsHalSensorsV2_XTargetTest.h"
-#include <android/hardware/sensors/2.0/ISensors.h>
-#include <android/hardware/sensors/2.0/types.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-#include <log/log.h>
-#include <utils/SystemClock.h>
-
-#include <cinttypes>
-#include <condition_variable>
-#include <cstring>
-#include <map>
-#include <vector>
-
-using ::android::sp;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::sensors::V1_0::MetaDataEventType;
-using ::android::hardware::sensors::V1_0::OperationMode;
-using ::android::hardware::sensors::V1_0::SensorsEventFormatOffset;
-using ::android::hardware::sensors::V1_0::SensorStatus;
-using ::android::hardware::sensors::V1_0::SharedMemType;
-using ::android::hardware::sensors::V1_0::Vec3;
-using std::chrono::duration_cast;
-using std::chrono::microseconds;
-using std::chrono::milliseconds;
-using std::chrono::nanoseconds;
-
-constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
-
-class EventCallback : public IEventCallback {
- public:
- void reset() {
- mFlushMap.clear();
- mEventMap.clear();
- }
-
- void onEvent(const ::android::hardware::sensors::V1_0::Event& event) override {
- if (event.sensorType == SensorType::META_DATA &&
- event.u.meta.what == MetaDataEventType::META_DATA_FLUSH_COMPLETE) {
- std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
- mFlushMap[event.sensorHandle]++;
- mFlushCV.notify_all();
- } else if (event.sensorType != SensorType::ADDITIONAL_INFO) {
- std::unique_lock<std::recursive_mutex> lock(mEventMutex);
- mEventMap[event.sensorHandle].push_back(event);
- mEventCV.notify_all();
- }
- }
-
- int32_t getFlushCount(int32_t sensorHandle) {
- std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
- return mFlushMap[sensorHandle];
- }
-
- void waitForFlushEvents(const std::vector<SensorInfo>& sensorsToWaitFor,
- int32_t numCallsToFlush, milliseconds timeout) {
- std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
- mFlushCV.wait_for(lock, timeout,
- [&] { return flushesReceived(sensorsToWaitFor, numCallsToFlush); });
- }
-
- const std::vector<Event> getEvents(int32_t sensorHandle) {
- std::unique_lock<std::recursive_mutex> lock(mEventMutex);
- return mEventMap[sensorHandle];
- }
-
- void waitForEvents(const std::vector<SensorInfo>& sensorsToWaitFor, milliseconds timeout) {
- std::unique_lock<std::recursive_mutex> lock(mEventMutex);
- mEventCV.wait_for(lock, timeout, [&] { return eventsReceived(sensorsToWaitFor); });
- }
-
- protected:
- bool flushesReceived(const std::vector<SensorInfo>& sensorsToWaitFor, int32_t numCallsToFlush) {
- for (const SensorInfo& sensor : sensorsToWaitFor) {
- if (getFlushCount(sensor.sensorHandle) < numCallsToFlush) {
- return false;
- }
- }
- return true;
- }
-
- bool eventsReceived(const std::vector<SensorInfo>& sensorsToWaitFor) {
- for (const SensorInfo& sensor : sensorsToWaitFor) {
- if (getEvents(sensor.sensorHandle).size() == 0) {
- return false;
- }
- }
- return true;
- }
-
- std::map<int32_t, int32_t> mFlushMap;
- std::recursive_mutex mFlushMutex;
- std::condition_variable_any mFlushCV;
-
- std::map<int32_t, std::vector<Event>> mEventMap;
- std::recursive_mutex mEventMutex;
- std::condition_variable_any mEventCV;
-};
-
-// The main test class for SENSORS HIDL HAL.
-
-class SensorsHidlTest : public SensorsHidlTestBase {
- public:
- virtual void SetUp() override {
- mEnvironment = new SensorsHidlEnvironmentV2_0(GetParam());
- mEnvironment->HidlSetUp();
- // Ensure that we have a valid environment before performing tests
- ASSERT_NE(getSensors(), nullptr);
- }
-
- virtual void TearDown() override { mEnvironment->HidlTearDown(); }
-
- protected:
- SensorInfo defaultSensorByType(SensorType type) override;
- std::vector<SensorInfo> getSensorsList();
- // implementation wrapper
- Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) override {
- return getSensors()->getSensorsList(_hidl_cb);
- }
-
- Return<Result> activate(int32_t sensorHandle, bool enabled) override;
-
- Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
- int64_t maxReportLatencyNs) override {
- return getSensors()->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
- }
-
- Return<Result> flush(int32_t sensorHandle) override {
- return getSensors()->flush(sensorHandle);
- }
-
- Return<Result> injectSensorData(const Event& event) override {
- return getSensors()->injectSensorData(event);
- }
-
- Return<void> registerDirectChannel(const SharedMemInfo& mem,
- ISensors::registerDirectChannel_cb _hidl_cb) override;
-
- Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
- return getSensors()->unregisterDirectChannel(channelHandle);
- }
-
- Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
- ISensors::configDirectReport_cb _hidl_cb) override {
- return getSensors()->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
- }
-
- inline sp<::android::hardware::sensors::V2_0::ISensors>& getSensors() {
- return mEnvironment->mSensors;
- }
-
- SensorsHidlEnvironmentBase* getEnvironment() override { return mEnvironment; }
-
- // Test helpers
- void runSingleFlushTest(const std::vector<SensorInfo>& sensors, bool activateSensor,
- int32_t expectedFlushCount, Result expectedResponse);
- void runFlushTest(const std::vector<SensorInfo>& sensors, bool activateSensor,
- int32_t flushCalls, int32_t expectedFlushCount, Result expectedResponse);
-
- // Helper functions
- void activateAllSensors(bool enable);
- std::vector<SensorInfo> getNonOneShotSensors();
- std::vector<SensorInfo> getNonOneShotAndNonSpecialSensors();
- std::vector<SensorInfo> getOneShotSensors();
- std::vector<SensorInfo> getInjectEventSensors();
- int32_t getInvalidSensorHandle();
- bool getDirectChannelSensor(SensorInfo* sensor, SharedMemType* memType, RateLevel* rate);
- void verifyDirectChannel(SharedMemType memType);
- void verifyRegisterDirectChannel(std::shared_ptr<SensorsTestSharedMemory> mem,
- int32_t* directChannelHandle, bool supportsSharedMemType,
- bool supportsAnyDirectChannel);
- void verifyConfigure(const SensorInfo& sensor, SharedMemType memType,
- int32_t directChannelHandle, bool directChannelSupported);
- void verifyUnregisterDirectChannel(int32_t directChannelHandle, bool directChannelSupported);
- void checkRateLevel(const SensorInfo& sensor, int32_t directChannelHandle, RateLevel rateLevel);
- void queryDirectChannelSupport(SharedMemType memType, bool* supportsSharedMemType,
- bool* supportsAnyDirectChannel);
-
- private:
- // Test environment for sensors HAL.
- SensorsHidlEnvironmentV2_0* mEnvironment;
-};
-
-Return<Result> SensorsHidlTest::activate(int32_t sensorHandle, bool enabled) {
- // If activating a sensor, add the handle in a set so that when test fails it can be turned off.
- // The handle is not removed when it is deactivating on purpose so that it is not necessary to
- // check the return value of deactivation. Deactivating a sensor more than once does not have
- // negative effect.
- if (enabled) {
- mSensorHandles.insert(sensorHandle);
- }
- return getSensors()->activate(sensorHandle, enabled);
-}
-
-Return<void> SensorsHidlTest::registerDirectChannel(const SharedMemInfo& mem,
- ISensors::registerDirectChannel_cb cb) {
- // If registeration of a channel succeeds, add the handle of channel to a set so that it can be
- // unregistered when test fails. Unregister a channel does not remove the handle on purpose.
- // Unregistering a channel more than once should not have negative effect.
- getSensors()->registerDirectChannel(mem, [&](auto result, auto channelHandle) {
- if (result == Result::OK) {
- mDirectChannelHandles.insert(channelHandle);
- }
- cb(result, channelHandle);
- });
- return Void();
-}
-
-SensorInfo SensorsHidlTest::defaultSensorByType(SensorType type) {
- SensorInfo ret;
-
- ret.type = (SensorType)-1;
- getSensors()->getSensorsList([&](const auto& list) {
- const size_t count = list.size();
- for (size_t i = 0; i < count; ++i) {
- if (list[i].type == type) {
- ret = list[i];
- return;
- }
- }
- });
-
- return ret;
-}
-
-std::vector<SensorInfo> SensorsHidlTest::getSensorsList() {
- std::vector<SensorInfo> ret;
-
- getSensors()->getSensorsList([&](const auto& list) {
- const size_t count = list.size();
- ret.reserve(list.size());
- for (size_t i = 0; i < count; ++i) {
- ret.push_back(list[i]);
- }
- });
-
- return ret;
-}
-
-std::vector<SensorInfo> SensorsHidlTest::getNonOneShotSensors() {
- std::vector<SensorInfo> sensors;
- for (const SensorInfo& info : getSensorsList()) {
- if (extractReportMode(info.flags) != SensorFlagBits::ONE_SHOT_MODE) {
- sensors.push_back(info);
- }
- }
- return sensors;
-}
-
-std::vector<SensorInfo> SensorsHidlTest::getNonOneShotAndNonSpecialSensors() {
- std::vector<SensorInfo> sensors;
- for (const SensorInfo& info : getSensorsList()) {
- SensorFlagBits reportMode = extractReportMode(info.flags);
- if (reportMode != SensorFlagBits::ONE_SHOT_MODE &&
- reportMode != SensorFlagBits::SPECIAL_REPORTING_MODE) {
- sensors.push_back(info);
- }
- }
- return sensors;
-}
-
-std::vector<SensorInfo> SensorsHidlTest::getOneShotSensors() {
- std::vector<SensorInfo> sensors;
- for (const SensorInfo& info : getSensorsList()) {
- if (extractReportMode(info.flags) == SensorFlagBits::ONE_SHOT_MODE) {
- sensors.push_back(info);
- }
- }
- return sensors;
-}
-
-std::vector<SensorInfo> SensorsHidlTest::getInjectEventSensors() {
- std::vector<SensorInfo> sensors;
- for (const SensorInfo& info : getSensorsList()) {
- if (info.flags & static_cast<uint32_t>(SensorFlagBits::DATA_INJECTION)) {
- sensors.push_back(info);
- }
- }
- return sensors;
-}
-
-int32_t SensorsHidlTest::getInvalidSensorHandle() {
- // Find a sensor handle that does not exist in the sensor list
- int32_t maxHandle = 0;
- for (const SensorInfo& sensor : getSensorsList()) {
- maxHandle = std::max(maxHandle, sensor.sensorHandle);
- }
- return maxHandle + 1;
-}
-
-// Test if sensor list returned is valid
-TEST_P(SensorsHidlTest, SensorListValid) {
+TEST_P(SensorsHidlTest, SensorListDoesntContainInvalidType) {
getSensors()->getSensorsList([&](const auto& list) {
const size_t count = list.size();
for (size_t i = 0; i < count; ++i) {
const auto& s = list[i];
- SCOPED_TRACE(::testing::Message()
- << i << "/" << count << ": "
- << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
- << s.sensorHandle << std::dec << " type=" << static_cast<int>(s.type)
- << " name=" << s.name);
-
- // Test non-empty type string
- EXPECT_FALSE(s.typeAsString.empty());
-
- // Test defined type matches defined string type
- EXPECT_NO_FATAL_FAILURE(assertTypeMatchStringType(s.type, s.typeAsString));
-
- // Test if all sensor has name and vendor
- EXPECT_FALSE(s.name.empty());
- EXPECT_FALSE(s.vendor.empty());
-
- // Test power > 0, maxRange > 0
- EXPECT_LE(0, s.power);
- EXPECT_LT(0, s.maxRange);
-
- // Info type, should have no sensor
- EXPECT_FALSE(s.type == SensorType::ADDITIONAL_INFO || s.type == SensorType::META_DATA);
-
- // Test fifoMax >= fifoReserved
- EXPECT_GE(s.fifoMaxEventCount, s.fifoReservedEventCount)
- << "max=" << s.fifoMaxEventCount << " reserved=" << s.fifoReservedEventCount;
-
- // Test Reporting mode valid
- EXPECT_NO_FATAL_FAILURE(assertTypeMatchReportMode(s.type, extractReportMode(s.flags)));
-
- // Test min max are in the right order
- EXPECT_LE(s.minDelay, s.maxDelay);
- // Test min/max delay matches reporting mode
- EXPECT_NO_FATAL_FAILURE(
- assertDelayMatchReportMode(s.minDelay, s.maxDelay, extractReportMode(s.flags)));
+ EXPECT_FALSE(s.type == ::android::hardware::sensors::V2_1::SensorType::HINGE_ANGLE);
}
});
}
-// Test that SetOperationMode returns the expected value
-TEST_P(SensorsHidlTest, SetOperationMode) {
- std::vector<SensorInfo> sensors = getInjectEventSensors();
- if (getInjectEventSensors().size() > 0) {
- ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
- ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
- ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
- } else {
- ASSERT_EQ(Result::BAD_VALUE, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
- }
-}
-
-// Test that an injected event is written back to the Event FMQ
-TEST_P(SensorsHidlTest, InjectSensorEventData) {
- std::vector<SensorInfo> sensors = getInjectEventSensors();
- if (sensors.size() == 0) {
- return;
- }
-
- ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
-
- EventCallback callback;
- getEnvironment()->registerCallback(&callback);
-
- // AdditionalInfo event should not be sent to Event FMQ
- Event additionalInfoEvent;
- additionalInfoEvent.sensorType = SensorType::ADDITIONAL_INFO;
- additionalInfoEvent.timestamp = android::elapsedRealtimeNano();
-
- Event injectedEvent;
- injectedEvent.timestamp = android::elapsedRealtimeNano();
- Vec3 data = {1, 2, 3, SensorStatus::ACCURACY_HIGH};
- injectedEvent.u.vec3 = data;
-
- for (const auto& s : sensors) {
- additionalInfoEvent.sensorHandle = s.sensorHandle;
- EXPECT_EQ(Result::OK, getSensors()->injectSensorData(additionalInfoEvent));
-
- injectedEvent.sensorType = s.type;
- injectedEvent.sensorHandle = s.sensorHandle;
- EXPECT_EQ(Result::OK, getSensors()->injectSensorData(injectedEvent));
- }
-
- // Wait for events to be written back to the Event FMQ
- callback.waitForEvents(sensors, milliseconds(1000) /* timeout */);
-
- for (const auto& s : sensors) {
- auto events = callback.getEvents(s.sensorHandle);
- auto lastEvent = events.back();
-
- // Verify that only a single event has been received
- ASSERT_EQ(events.size(), 1);
-
- // Verify that the event received matches the event injected and is not the additional
- // info event
- ASSERT_EQ(lastEvent.sensorType, s.type);
- ASSERT_EQ(lastEvent.sensorType, s.type);
- ASSERT_EQ(lastEvent.timestamp, injectedEvent.timestamp);
- ASSERT_EQ(lastEvent.u.vec3.x, injectedEvent.u.vec3.x);
- ASSERT_EQ(lastEvent.u.vec3.y, injectedEvent.u.vec3.y);
- ASSERT_EQ(lastEvent.u.vec3.z, injectedEvent.u.vec3.z);
- ASSERT_EQ(lastEvent.u.vec3.status, injectedEvent.u.vec3.status);
- }
-
- getEnvironment()->unregisterCallback();
- ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
-}
-
-// Test if sensor hal can do UI speed accelerometer streaming properly
-TEST_P(SensorsHidlTest, AccelerometerStreamingOperationSlow) {
- testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(200),
- std::chrono::seconds(5), sAccelNormChecker);
-}
-
-// Test if sensor hal can do normal speed accelerometer streaming properly
-TEST_P(SensorsHidlTest, AccelerometerStreamingOperationNormal) {
- testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(20),
- std::chrono::seconds(5), sAccelNormChecker);
-}
-
-// Test if sensor hal can do game speed accelerometer streaming properly
-TEST_P(SensorsHidlTest, AccelerometerStreamingOperationFast) {
- testStreamingOperation(SensorType::ACCELEROMETER, std::chrono::milliseconds(5),
- std::chrono::seconds(5), sAccelNormChecker);
-}
-
-// Test if sensor hal can do UI speed gyroscope streaming properly
-TEST_P(SensorsHidlTest, GyroscopeStreamingOperationSlow) {
- testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(200),
- std::chrono::seconds(5), sGyroNormChecker);
-}
-
-// Test if sensor hal can do normal speed gyroscope streaming properly
-TEST_P(SensorsHidlTest, GyroscopeStreamingOperationNormal) {
- testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(20),
- std::chrono::seconds(5), sGyroNormChecker);
-}
-
-// Test if sensor hal can do game speed gyroscope streaming properly
-TEST_P(SensorsHidlTest, GyroscopeStreamingOperationFast) {
- testStreamingOperation(SensorType::GYROSCOPE, std::chrono::milliseconds(5),
- std::chrono::seconds(5), sGyroNormChecker);
-}
-
-// Test if sensor hal can do UI speed magnetometer streaming properly
-TEST_P(SensorsHidlTest, MagnetometerStreamingOperationSlow) {
- testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(200),
- std::chrono::seconds(5), NullChecker());
-}
-
-// Test if sensor hal can do normal speed magnetometer streaming properly
-TEST_P(SensorsHidlTest, MagnetometerStreamingOperationNormal) {
- testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(20),
- std::chrono::seconds(5), NullChecker());
-}
-
-// Test if sensor hal can do game speed magnetometer streaming properly
-TEST_P(SensorsHidlTest, MagnetometerStreamingOperationFast) {
- testStreamingOperation(SensorType::MAGNETIC_FIELD, std::chrono::milliseconds(5),
- std::chrono::seconds(5), NullChecker());
-}
-
-// Test if sensor hal can do accelerometer sampling rate switch properly when sensor is active
-TEST_P(SensorsHidlTest, AccelerometerSamplingPeriodHotSwitchOperation) {
- testSamplingRateHotSwitchOperation(SensorType::ACCELEROMETER);
- testSamplingRateHotSwitchOperation(SensorType::ACCELEROMETER, false /*fastToSlow*/);
-}
-
-// Test if sensor hal can do gyroscope sampling rate switch properly when sensor is active
-TEST_P(SensorsHidlTest, GyroscopeSamplingPeriodHotSwitchOperation) {
- testSamplingRateHotSwitchOperation(SensorType::GYROSCOPE);
- testSamplingRateHotSwitchOperation(SensorType::GYROSCOPE, false /*fastToSlow*/);
-}
-
-// Test if sensor hal can do magnetometer sampling rate switch properly when sensor is active
-TEST_P(SensorsHidlTest, MagnetometerSamplingPeriodHotSwitchOperation) {
- testSamplingRateHotSwitchOperation(SensorType::MAGNETIC_FIELD);
- testSamplingRateHotSwitchOperation(SensorType::MAGNETIC_FIELD, false /*fastToSlow*/);
-}
-
-// Test if sensor hal can do accelerometer batching properly
-TEST_P(SensorsHidlTest, AccelerometerBatchingOperation) {
- testBatchingOperation(SensorType::ACCELEROMETER);
-}
-
-// Test if sensor hal can do gyroscope batching properly
-TEST_P(SensorsHidlTest, GyroscopeBatchingOperation) {
- testBatchingOperation(SensorType::GYROSCOPE);
-}
-
-// Test if sensor hal can do magnetometer batching properly
-TEST_P(SensorsHidlTest, MagnetometerBatchingOperation) {
- testBatchingOperation(SensorType::MAGNETIC_FIELD);
-}
-
-// Test sensor event direct report with ashmem for accel sensor at normal rate
-TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM, RateLevel::NORMAL,
- sAccelNormChecker);
-}
-
-// Test sensor event direct report with ashmem for accel sensor at fast rate
-TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationFast) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM, RateLevel::FAST,
- sAccelNormChecker);
-}
-
-// Test sensor event direct report with ashmem for accel sensor at very fast rate
-TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::ASHMEM,
- RateLevel::VERY_FAST, sAccelNormChecker);
-}
-
-// Test sensor event direct report with ashmem for gyro sensor at normal rate
-TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::NORMAL,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with ashmem for gyro sensor at fast rate
-TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationFast) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::FAST,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with ashmem for gyro sensor at very fast rate
-TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::VERY_FAST,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with ashmem for mag sensor at normal rate
-TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM, RateLevel::NORMAL,
- NullChecker());
-}
-
-// Test sensor event direct report with ashmem for mag sensor at fast rate
-TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationFast) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM, RateLevel::FAST,
- NullChecker());
-}
-
-// Test sensor event direct report with ashmem for mag sensor at very fast rate
-TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::ASHMEM,
- RateLevel::VERY_FAST, NullChecker());
-}
-
-// Test sensor event direct report with gralloc for accel sensor at normal rate
-TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC, RateLevel::NORMAL,
- sAccelNormChecker);
-}
-
-// Test sensor event direct report with gralloc for accel sensor at fast rate
-TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationFast) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC, RateLevel::FAST,
- sAccelNormChecker);
-}
-
-// Test sensor event direct report with gralloc for accel sensor at very fast rate
-TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::ACCELEROMETER, SharedMemType::GRALLOC,
- RateLevel::VERY_FAST, sAccelNormChecker);
-}
-
-// Test sensor event direct report with gralloc for gyro sensor at normal rate
-TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::NORMAL,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with gralloc for gyro sensor at fast rate
-TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationFast) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::FAST,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with gralloc for gyro sensor at very fast rate
-TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::VERY_FAST,
- sGyroNormChecker);
-}
-
-// Test sensor event direct report with gralloc for mag sensor at normal rate
-TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationNormal) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC, RateLevel::NORMAL,
- NullChecker());
-}
-
-// Test sensor event direct report with gralloc for mag sensor at fast rate
-TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationFast) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC, RateLevel::FAST,
- NullChecker());
-}
-
-// Test sensor event direct report with gralloc for mag sensor at very fast rate
-TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationVeryFast) {
- testDirectReportOperation(SensorType::MAGNETIC_FIELD, SharedMemType::GRALLOC,
- RateLevel::VERY_FAST, NullChecker());
-}
-
-void SensorsHidlTest::activateAllSensors(bool enable) {
- for (const SensorInfo& sensorInfo : getSensorsList()) {
- if (isValidType(sensorInfo.type)) {
- batch(sensorInfo.sensorHandle, sensorInfo.minDelay, 0 /* maxReportLatencyNs */);
- activate(sensorInfo.sensorHandle, enable);
- }
- }
-}
-
-// Test that if initialize is called twice, then the HAL writes events to the FMQs from the second
-// call to the function.
-TEST_P(SensorsHidlTest, CallInitializeTwice) {
- // Create a helper class so that a second environment is able to be instantiated
- class SensorsHidlEnvironmentTest : public SensorsHidlEnvironmentV2_0 {
- public:
- SensorsHidlEnvironmentTest(const std::string& service_name)
- : SensorsHidlEnvironmentV2_0(service_name) {}
- };
-
- if (getSensorsList().size() == 0) {
- // No sensors
- return;
- }
-
- constexpr useconds_t kCollectionTimeoutUs = 1000 * 1000; // 1s
- constexpr int32_t kNumEvents = 1;
-
- // Create a new environment that calls initialize()
- std::unique_ptr<SensorsHidlEnvironmentTest> newEnv =
- std::make_unique<SensorsHidlEnvironmentTest>(GetParam());
- newEnv->HidlSetUp();
- if (HasFatalFailure()) {
- return; // Exit early if setting up the new environment failed
- }
-
- activateAllSensors(true);
- // Verify that the old environment does not receive any events
- ASSERT_EQ(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), 0);
- // Verify that the new event queue receives sensor events
- ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, newEnv.get()).size(), kNumEvents);
- activateAllSensors(false);
-
- // Cleanup the test environment
- newEnv->HidlTearDown();
-
- // Restore the test environment for future tests
- getEnvironment()->HidlTearDown();
- getEnvironment()->HidlSetUp();
- if (HasFatalFailure()) {
- return; // Exit early if resetting the environment failed
- }
-
- // Ensure that the original environment is receiving events
- activateAllSensors(true);
- ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents).size(), kNumEvents);
- activateAllSensors(false);
-}
-
-TEST_P(SensorsHidlTest, CleanupConnectionsOnInitialize) {
- activateAllSensors(true);
-
- // Verify that events are received
- constexpr useconds_t kCollectionTimeoutUs = 1000 * 1000; // 1s
- constexpr int32_t kNumEvents = 1;
- ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), kNumEvents);
-
- // Clear the active sensor handles so they are not disabled during TearDown
- auto handles = mSensorHandles;
- mSensorHandles.clear();
- getEnvironment()->HidlTearDown();
- getEnvironment()->HidlSetUp();
- if (HasFatalFailure()) {
- return; // Exit early if resetting the environment failed
- }
-
- // Verify no events are received until sensors are re-activated
- ASSERT_EQ(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), 0);
- activateAllSensors(true);
- ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), kNumEvents);
-
- // Disable sensors
- activateAllSensors(false);
-
- // Restore active sensors prior to clearing the environment
- mSensorHandles = handles;
-}
-
-void SensorsHidlTest::runSingleFlushTest(const std::vector<SensorInfo>& sensors,
- bool activateSensor, int32_t expectedFlushCount,
- Result expectedResponse) {
- runFlushTest(sensors, activateSensor, 1 /* flushCalls */, expectedFlushCount, expectedResponse);
-}
-
-void SensorsHidlTest::runFlushTest(const std::vector<SensorInfo>& sensors, bool activateSensor,
- int32_t flushCalls, int32_t expectedFlushCount,
- Result expectedResponse) {
- EventCallback callback;
- getEnvironment()->registerCallback(&callback);
-
- for (const SensorInfo& sensor : sensors) {
- // Configure and activate the sensor
- batch(sensor.sensorHandle, sensor.maxDelay, 0 /* maxReportLatencyNs */);
- activate(sensor.sensorHandle, activateSensor);
-
- // Flush the sensor
- for (int32_t i = 0; i < flushCalls; i++) {
- Result flushResult = flush(sensor.sensorHandle);
- ASSERT_EQ(flushResult, expectedResponse);
- }
- }
-
- // Wait up to one second for the flush events
- callback.waitForFlushEvents(sensors, flushCalls, milliseconds(1000) /* timeout */);
-
- // Deactivate all sensors after waiting for flush events so pending flush events are not
- // abandoned by the HAL.
- for (const SensorInfo& sensor : sensors) {
- activate(sensor.sensorHandle, false);
- }
- getEnvironment()->unregisterCallback();
-
- // Check that the correct number of flushes are present for each sensor
- for (const SensorInfo& sensor : sensors) {
- ASSERT_EQ(callback.getFlushCount(sensor.sensorHandle), expectedFlushCount);
- }
-}
-
-TEST_P(SensorsHidlTest, FlushSensor) {
- // Find a sensor that is not a one-shot sensor
- std::vector<SensorInfo> sensors = getNonOneShotSensors();
- if (sensors.size() == 0) {
- return;
- }
-
- constexpr int32_t kFlushes = 5;
- runSingleFlushTest(sensors, true /* activateSensor */, 1 /* expectedFlushCount */, Result::OK);
- runFlushTest(sensors, true /* activateSensor */, kFlushes, kFlushes, Result::OK);
-}
-
-TEST_P(SensorsHidlTest, FlushOneShotSensor) {
- // Find a sensor that is a one-shot sensor
- std::vector<SensorInfo> sensors = getOneShotSensors();
- if (sensors.size() == 0) {
- return;
- }
-
- runSingleFlushTest(sensors, true /* activateSensor */, 0 /* expectedFlushCount */,
- Result::BAD_VALUE);
-}
-
-TEST_P(SensorsHidlTest, FlushInactiveSensor) {
- // Attempt to find a non-one shot sensor, then a one-shot sensor if necessary
- std::vector<SensorInfo> sensors = getNonOneShotSensors();
- if (sensors.size() == 0) {
- sensors = getOneShotSensors();
- if (sensors.size() == 0) {
- return;
- }
- }
-
- runSingleFlushTest(sensors, false /* activateSensor */, 0 /* expectedFlushCount */,
- Result::BAD_VALUE);
-}
-
-TEST_P(SensorsHidlTest, FlushNonexistentSensor) {
- SensorInfo sensor;
- std::vector<SensorInfo> sensors = getNonOneShotSensors();
- if (sensors.size() == 0) {
- sensors = getOneShotSensors();
- if (sensors.size() == 0) {
- return;
- }
- }
- sensor = sensors.front();
- sensor.sensorHandle = getInvalidSensorHandle();
- runSingleFlushTest(std::vector<SensorInfo>{sensor}, false /* activateSensor */,
- 0 /* expectedFlushCount */, Result::BAD_VALUE);
-}
-
-TEST_P(SensorsHidlTest, Batch) {
- if (getSensorsList().size() == 0) {
- return;
- }
-
- activateAllSensors(false /* enable */);
- for (const SensorInfo& sensor : getSensorsList()) {
- // Call batch on inactive sensor
- // One shot sensors have minDelay set to -1 which is an invalid
- // parameter. Use 0 instead to avoid errors.
- int64_t samplingPeriodNs = extractReportMode(sensor.flags) == SensorFlagBits::ONE_SHOT_MODE
- ? 0
- : sensor.minDelay;
- ASSERT_EQ(batch(sensor.sensorHandle, samplingPeriodNs, 0 /* maxReportLatencyNs */),
- Result::OK);
-
- // Activate the sensor
- activate(sensor.sensorHandle, true /* enabled */);
-
- // Call batch on an active sensor
- ASSERT_EQ(batch(sensor.sensorHandle, sensor.maxDelay, 0 /* maxReportLatencyNs */),
- Result::OK);
- }
- activateAllSensors(false /* enable */);
-
- // Call batch on an invalid sensor
- SensorInfo sensor = getSensorsList().front();
- sensor.sensorHandle = getInvalidSensorHandle();
- ASSERT_EQ(batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */),
- Result::BAD_VALUE);
-}
-
-TEST_P(SensorsHidlTest, Activate) {
- if (getSensorsList().size() == 0) {
- return;
- }
-
- // Verify that sensor events are generated when activate is called
- for (const SensorInfo& sensor : getSensorsList()) {
- batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */);
- ASSERT_EQ(activate(sensor.sensorHandle, true), Result::OK);
-
- // Call activate on a sensor that is already activated
- ASSERT_EQ(activate(sensor.sensorHandle, true), Result::OK);
-
- // Deactivate the sensor
- ASSERT_EQ(activate(sensor.sensorHandle, false), Result::OK);
-
- // Call deactivate on a sensor that is already deactivated
- ASSERT_EQ(activate(sensor.sensorHandle, false), Result::OK);
- }
-
- // Attempt to activate an invalid sensor
- int32_t invalidHandle = getInvalidSensorHandle();
- ASSERT_EQ(activate(invalidHandle, true), Result::BAD_VALUE);
- ASSERT_EQ(activate(invalidHandle, false), Result::BAD_VALUE);
-}
-
-TEST_P(SensorsHidlTest, NoStaleEvents) {
- constexpr milliseconds kFiveHundredMs(500);
- constexpr milliseconds kOneSecond(1000);
-
- // Register the callback to receive sensor events
- EventCallback callback;
- getEnvironment()->registerCallback(&callback);
-
- // This test is not valid for one-shot or special-report-mode sensors
- const std::vector<SensorInfo> sensors = getNonOneShotAndNonSpecialSensors();
- milliseconds maxMinDelay(0);
- for (const SensorInfo& sensor : sensors) {
- milliseconds minDelay = duration_cast<milliseconds>(microseconds(sensor.minDelay));
- maxMinDelay = milliseconds(std::max(maxMinDelay.count(), minDelay.count()));
- }
-
- // Activate the sensors so that they start generating events
- activateAllSensors(true);
-
- // According to the CDD, the first sample must be generated within 400ms + 2 * sample_time
- // and the maximum reporting latency is 100ms + 2 * sample_time. Wait a sufficient amount
- // of time to guarantee that a sample has arrived.
- callback.waitForEvents(sensors, kFiveHundredMs + (5 * maxMinDelay));
- activateAllSensors(false);
-
- // Save the last received event for each sensor
- std::map<int32_t, int64_t> lastEventTimestampMap;
- for (const SensorInfo& sensor : sensors) {
- // Some on-change sensors may not report an event without stimulus
- if (extractReportMode(sensor.flags) != SensorFlagBits::ON_CHANGE_MODE) {
- ASSERT_GE(callback.getEvents(sensor.sensorHandle).size(), 1);
- }
- if (callback.getEvents(sensor.sensorHandle).size() >= 1) {
- lastEventTimestampMap[sensor.sensorHandle] =
- callback.getEvents(sensor.sensorHandle).back().timestamp;
- }
- }
-
- // Allow some time to pass, reset the callback, then reactivate the sensors
- usleep(duration_cast<microseconds>(kOneSecond + (5 * maxMinDelay)).count());
- callback.reset();
- activateAllSensors(true);
- callback.waitForEvents(sensors, kFiveHundredMs + (5 * maxMinDelay));
- activateAllSensors(false);
-
- for (const SensorInfo& sensor : sensors) {
- // Skip sensors that did not previously report an event
- if (lastEventTimestampMap.find(sensor.sensorHandle) == lastEventTimestampMap.end()) {
- continue;
- }
- // Skip on-change sensors that do not consistently report an initial event
- if (callback.getEvents(sensor.sensorHandle).size() < 1) {
- continue;
- }
- // Ensure that the first event received is not stale by ensuring that its timestamp is
- // sufficiently different from the previous event
- const Event newEvent = callback.getEvents(sensor.sensorHandle).front();
- milliseconds delta = duration_cast<milliseconds>(
- nanoseconds(newEvent.timestamp - lastEventTimestampMap[sensor.sensorHandle]));
- milliseconds sensorMinDelay = duration_cast<milliseconds>(microseconds(sensor.minDelay));
- ASSERT_GE(delta, kFiveHundredMs + (3 * sensorMinDelay));
- }
-}
-
-void SensorsHidlTest::checkRateLevel(const SensorInfo& sensor, int32_t directChannelHandle,
- RateLevel rateLevel) {
- configDirectReport(sensor.sensorHandle, directChannelHandle, rateLevel,
- [&](Result result, int32_t reportToken) {
- if (isDirectReportRateSupported(sensor, rateLevel)) {
- ASSERT_EQ(result, Result::OK);
- if (rateLevel != RateLevel::STOP) {
- ASSERT_GT(reportToken, 0);
- }
- } else {
- ASSERT_EQ(result, Result::BAD_VALUE);
- }
- });
-}
-
-void SensorsHidlTest::queryDirectChannelSupport(SharedMemType memType, bool* supportsSharedMemType,
- bool* supportsAnyDirectChannel) {
- *supportsSharedMemType = false;
- *supportsAnyDirectChannel = false;
- for (const SensorInfo& curSensor : getSensorsList()) {
- if (isDirectChannelTypeSupported(curSensor, memType)) {
- *supportsSharedMemType = true;
- }
- if (isDirectChannelTypeSupported(curSensor, SharedMemType::ASHMEM) ||
- isDirectChannelTypeSupported(curSensor, SharedMemType::GRALLOC)) {
- *supportsAnyDirectChannel = true;
- }
-
- if (*supportsSharedMemType && *supportsAnyDirectChannel) {
- break;
- }
- }
-}
-
-void SensorsHidlTest::verifyRegisterDirectChannel(std::shared_ptr<SensorsTestSharedMemory> mem,
- int32_t* directChannelHandle,
- bool supportsSharedMemType,
- bool supportsAnyDirectChannel) {
- char* buffer = mem->getBuffer();
- memset(buffer, 0xff, mem->getSize());
-
- registerDirectChannel(mem->getSharedMemInfo(), [&](Result result, int32_t channelHandle) {
- if (supportsSharedMemType) {
- ASSERT_EQ(result, Result::OK);
- ASSERT_GT(channelHandle, 0);
-
- // Verify that the memory has been zeroed
- for (size_t i = 0; i < mem->getSize(); i++) {
- ASSERT_EQ(buffer[i], 0x00);
- }
- } else {
- Result expectedResult =
- supportsAnyDirectChannel ? Result::BAD_VALUE : Result::INVALID_OPERATION;
- ASSERT_EQ(result, expectedResult);
- ASSERT_EQ(channelHandle, -1);
- }
- *directChannelHandle = channelHandle;
- });
-}
-
-void SensorsHidlTest::verifyConfigure(const SensorInfo& sensor, SharedMemType memType,
- int32_t directChannelHandle, bool supportsAnyDirectChannel) {
- if (isDirectChannelTypeSupported(sensor, memType)) {
- // Verify that each rate level is properly supported
- checkRateLevel(sensor, directChannelHandle, RateLevel::NORMAL);
- checkRateLevel(sensor, directChannelHandle, RateLevel::FAST);
- checkRateLevel(sensor, directChannelHandle, RateLevel::VERY_FAST);
- checkRateLevel(sensor, directChannelHandle, RateLevel::STOP);
-
- // Verify that a sensor handle of -1 is only acceptable when using RateLevel::STOP
- configDirectReport(
- -1 /* sensorHandle */, directChannelHandle, RateLevel::NORMAL,
- [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::BAD_VALUE); });
- configDirectReport(
- -1 /* sensorHandle */, directChannelHandle, RateLevel::STOP,
- [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::OK); });
- } else {
- // directChannelHandle will be -1 here, HAL should either reject it as a bad value if there
- // is some level of direct channel report, otherwise return INVALID_OPERATION if direct
- // channel is not supported at all
- Result expectedResult =
- supportsAnyDirectChannel ? Result::BAD_VALUE : Result::INVALID_OPERATION;
- configDirectReport(sensor.sensorHandle, directChannelHandle, RateLevel::NORMAL,
- [expectedResult](Result result, int32_t /* reportToken */) {
- ASSERT_EQ(result, expectedResult);
- });
- }
-}
-
-void SensorsHidlTest::verifyUnregisterDirectChannel(int32_t directChannelHandle,
- bool supportsAnyDirectChannel) {
- Result expectedResult = supportsAnyDirectChannel ? Result::OK : Result::INVALID_OPERATION;
- ASSERT_EQ(unregisterDirectChannel(directChannelHandle), expectedResult);
-}
-
-void SensorsHidlTest::verifyDirectChannel(SharedMemType memType) {
- constexpr size_t kNumEvents = 1;
- constexpr size_t kMemSize = kNumEvents * kEventSize;
-
- std::shared_ptr<SensorsTestSharedMemory> mem(
- SensorsTestSharedMemory::create(memType, kMemSize));
- ASSERT_NE(mem, nullptr);
-
- bool supportsSharedMemType;
- bool supportsAnyDirectChannel;
- queryDirectChannelSupport(memType, &supportsSharedMemType, &supportsAnyDirectChannel);
-
- for (const SensorInfo& sensor : getSensorsList()) {
- int32_t directChannelHandle = 0;
- verifyRegisterDirectChannel(mem, &directChannelHandle, supportsSharedMemType,
- supportsAnyDirectChannel);
- verifyConfigure(sensor, memType, directChannelHandle, supportsAnyDirectChannel);
- verifyUnregisterDirectChannel(directChannelHandle, supportsAnyDirectChannel);
- }
-}
-
-TEST_P(SensorsHidlTest, DirectChannelAshmem) {
- verifyDirectChannel(SharedMemType::ASHMEM);
-}
-
-TEST_P(SensorsHidlTest, DirectChannelGralloc) {
- verifyDirectChannel(SharedMemType::GRALLOC);
-}
-
-bool SensorsHidlTest::getDirectChannelSensor(SensorInfo* sensor, SharedMemType* memType,
- RateLevel* rate) {
- bool found = false;
- for (const SensorInfo& curSensor : getSensorsList()) {
- if (isDirectChannelTypeSupported(curSensor, SharedMemType::ASHMEM)) {
- *memType = SharedMemType::ASHMEM;
- *sensor = curSensor;
- found = true;
- break;
- } else if (isDirectChannelTypeSupported(curSensor, SharedMemType::GRALLOC)) {
- *memType = SharedMemType::GRALLOC;
- *sensor = curSensor;
- found = true;
- break;
- }
- }
-
- if (found) {
- // Find a supported rate level
- constexpr int kNumRateLevels = 3;
- RateLevel rates[kNumRateLevels] = {RateLevel::NORMAL, RateLevel::FAST,
- RateLevel::VERY_FAST};
- *rate = RateLevel::STOP;
- for (int i = 0; i < kNumRateLevels; i++) {
- if (isDirectReportRateSupported(*sensor, rates[i])) {
- *rate = rates[i];
- }
- }
-
- // At least one rate level must be supported
- EXPECT_NE(*rate, RateLevel::STOP);
- }
- return found;
-}
-
-TEST_P(SensorsHidlTest, ConfigureDirectChannelWithInvalidHandle) {
- SensorInfo sensor;
- SharedMemType memType;
- RateLevel rate;
- if (!getDirectChannelSensor(&sensor, &memType, &rate)) {
- return;
- }
-
- // Verify that an invalid channel handle produces a BAD_VALUE result
- configDirectReport(sensor.sensorHandle, -1, rate, [](Result result, int32_t /* reportToken */) {
- ASSERT_EQ(result, Result::BAD_VALUE);
- });
-}
-
-TEST_P(SensorsHidlTest, CleanupDirectConnectionOnInitialize) {
- constexpr size_t kNumEvents = 1;
- constexpr size_t kMemSize = kNumEvents * kEventSize;
-
- SensorInfo sensor;
- SharedMemType memType;
- RateLevel rate;
-
- if (!getDirectChannelSensor(&sensor, &memType, &rate)) {
- return;
- }
-
- std::shared_ptr<SensorsTestSharedMemory> mem(
- SensorsTestSharedMemory::create(memType, kMemSize));
- ASSERT_NE(mem, nullptr);
-
- int32_t directChannelHandle = 0;
- registerDirectChannel(mem->getSharedMemInfo(), [&](Result result, int32_t channelHandle) {
- ASSERT_EQ(result, Result::OK);
- directChannelHandle = channelHandle;
- });
-
- // Configure the channel and expect success
- configDirectReport(
- sensor.sensorHandle, directChannelHandle, rate,
- [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::OK); });
-
- // Call initialize() via the environment setup to cause the HAL to re-initialize
- // Clear the active direct connections so they are not stopped during TearDown
- auto handles = mDirectChannelHandles;
- mDirectChannelHandles.clear();
- getEnvironment()->HidlTearDown();
- getEnvironment()->HidlSetUp();
- if (HasFatalFailure()) {
- return; // Exit early if resetting the environment failed
- }
-
- // Attempt to configure the direct channel and expect it to fail
- configDirectReport(
- sensor.sensorHandle, directChannelHandle, rate,
- [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::BAD_VALUE); });
-
- // Restore original handles, though they should already be deactivated
- mDirectChannelHandles = handles;
-}
-
INSTANTIATE_TEST_SUITE_P(PerInstance, SensorsHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(
android::hardware::sensors::V2_0::ISensors::descriptor)),
- android::hardware::PrintInstanceNameToString);
-// vim: set ts=2 sw=2
+ android::hardware::PrintInstanceNameToString);
\ No newline at end of file
diff --git a/sensors/2.1/Android.bp b/sensors/2.1/Android.bp
new file mode 100644
index 0000000..8e80e1f
--- /dev/null
+++ b/sensors/2.1/Android.bp
@@ -0,0 +1,21 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.sensors@2.1",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "ISensors.hal",
+ "ISensorsCallback.hal",
+ ],
+ interfaces: [
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@2.0",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: false,
+ gen_java_constants: true,
+}
diff --git a/sensors/2.1/ISensors.hal b/sensors/2.1/ISensors.hal
new file mode 100644
index 0000000..d401fa5
--- /dev/null
+++ b/sensors/2.1/ISensors.hal
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.sensors@2.1;
+
+import @1.0::Result;
+import @2.0::ISensors;
+import @2.1::ISensorsCallback;
+
+interface ISensors extends @2.0::ISensors {
+ /**
+ * Enumerate all available (static) sensors.
+ *
+ * The SensorInfo for each sensor returned by getSensorsList must be stable
+ * from the initial call to getSensorsList after a device boot until the
+ * entire system restarts. The SensorInfo for each sensor must not change
+ * between subsequent calls to getSensorsList, even across restarts of the
+ * HAL and its dependencies (for example, the sensor handle for a given
+ * sensor must not change across HAL restarts).
+ */
+ getSensorsList_2_1() generates (vec<SensorInfo> list);
+
+ /**
+ * Initialize the Sensors HAL's Fast Message Queues (FMQ) and callback.
+ *
+ * The Fast Message Queues (FMQ) that are used to send data between the
+ * framework and the HAL. The callback is used by the HAL to notify the
+ * framework of asynchronous events, such as a dynamic sensor connection.
+ *
+ * The Event FMQ is used to transport sensor events from the HAL to the
+ * framework. The Event FMQ is created using the eventQueueDescriptor.
+ * Data may only be written to the Event FMQ. Data must not be read from
+ * the Event FMQ since the framework is the only reader. Upon receiving
+ * sensor events, the HAL writes the sensor events to the Event FMQ.
+ *
+ * Once the HAL is finished writing sensor events to the Event FMQ, the HAL
+ * must notify the framework that sensor events are available to be read and
+ * processed. This is accomplished by either:
+ * 1) Calling the Event FMQ’s EventFlag::wake() function with
+ EventQueueFlagBits::READ_AND_PROCESS
+ * 2) Setting the write notification in the Event FMQ’s writeBlocking()
+ * function to EventQueueFlagBits::READ_AND_PROCESS.
+ *
+ * If the Event FMQ’s writeBlocking() function is used, the read
+ * notification must be set to EventQueueFlagBits::EVENTS_READ in order to
+ * be notified and unblocked when the framework has successfully read events
+ * from the Event FMQ.
+ *
+ * The Wake Lock FMQ is used by the framework to notify the HAL when it is
+ * safe to release its wake_lock. When the framework receives WAKE_UP events
+ * from the Event FMQ and the framework has acquired a wake_lock, the
+ * framework must write the number of WAKE_UP events processed to the Wake
+ * Lock FMQ. When the HAL reads the data from the Wake Lock FMQ, the HAL
+ * decrements its current count of unprocessed WAKE_UP events and releases
+ * its wake_lock if the current count of unprocessed WAKE_UP events is
+ * zero. It is important to note that the HAL must acquire the wake lock and
+ * update its internal state regarding the number of outstanding WAKE_UP
+ * events _before_ posting the event to the Wake Lock FMQ, in order to avoid
+ * a race condition that can lead to loss of wake lock synchronization with
+ * the framework.
+ *
+ * The framework must use the WakeLockQueueFlagBits::DATA_WRITTEN value to
+ * notify the HAL that data has been written to the Wake Lock FMQ and must
+ * be read by HAL.
+ *
+ * The ISensorsCallback is used by the HAL to notify the framework of
+ * asynchronous events, such as a dynamic sensor connection.
+ *
+ * The name of any wake_lock acquired by the Sensors HAL for WAKE_UP events
+ * must begin with "SensorsHAL_WAKEUP".
+ *
+ * If WAKE_LOCK_TIMEOUT_SECONDS has elapsed since the most recent WAKE_UP
+ * event was written to the Event FMQ without receiving a message on the
+ * Wake Lock FMQ, then any held wake_lock for WAKE_UP events must be
+ * released.
+ *
+ * If either the Event FMQ or the Wake Lock FMQ is already initialized when
+ * initialize is invoked, then both existing FMQs must be discarded and the
+ * new descriptors must be used to create new FMQs within the HAL. The
+ * number of outstanding WAKE_UP events should also be reset to zero, and
+ * any outstanding wake_locks held as a result of WAKE_UP events should be
+ * released.
+ *
+ * All active sensor requests and direct channels must be closed and
+ * properly cleaned up when initialize is called in order to ensure that the
+ * HAL and framework's state is consistent (e.g. after a runtime restart).
+ *
+ * initialize must be thread safe and prevent concurrent calls
+ * to initialize from simultaneously modifying state.
+ *
+ * @param eventQueueDescriptor Fast Message Queue descriptor that is used to
+ * create the Event FMQ which is where sensor events are written. The
+ * descriptor is obtained from the framework's FMQ that is used to read
+ * sensor events.
+ * @param wakeLockDescriptor Fast Message Queue descriptor that is used to
+ * create the Wake Lock FMQ which is where wake_lock events are read
+ * from. The descriptor is obtained from the framework's FMQ that is
+ * used to write wake_lock events.
+ * @param sensorsCallback sensors callback that receives asynchronous data
+ * from the Sensors HAL.
+ * @return result OK on success; BAD_VALUE if descriptor is invalid (such
+ * as null)
+ */
+ @entry
+ @callflow(next = {"getSensorsList"})
+ initialize_2_1(fmq_sync<Event> eventQueueDescriptor,
+ fmq_sync<uint32_t> wakeLockDescriptor,
+ ISensorsCallback sensorsCallback)
+ generates
+ (Result result);
+
+ /**
+ * Inject a single sensor event or push operation environment parameters to
+ * device.
+ *
+ * When device is in NORMAL mode, this function is called to push operation
+ * environment data to device. In this operation, Event is always of
+ * SensorType::AdditionalInfo type. See operation evironment parameters
+ * section in AdditionalInfoType.
+ *
+ * When device is in DATA_INJECTION mode, this function is also used for
+ * injecting sensor events.
+ *
+ * Regardless of OperationMode, injected SensorType::ADDITIONAL_INFO
+ * type events should not be routed back to the sensor event queue.
+ *
+ * @see AdditionalInfoType
+ * @see OperationMode
+ * @param event sensor event to be injected
+ * @return result OK on success; PERMISSION_DENIED if operation is not
+ * allowed; INVALID_OPERATION, if this functionality is unsupported;
+ * BAD_VALUE if sensor event cannot be injected.
+ */
+ injectSensorData_2_1(Event event) generates (Result result);
+};
diff --git a/sensors/2.1/ISensorsCallback.hal b/sensors/2.1/ISensorsCallback.hal
new file mode 100644
index 0000000..de521d5
--- /dev/null
+++ b/sensors/2.1/ISensorsCallback.hal
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.sensors@2.1;
+
+import @2.0::ISensorsCallback;
+import @2.1::SensorInfo;
+
+interface ISensorsCallback extends @2.0::ISensorsCallback {
+ /**
+ * Notify the framework that new dynamic sensors have been connected.
+ *
+ * If a dynamic sensor was previously connected and has not been
+ * disconnected, then that sensor must not be included in sensorInfos.
+ *
+ * @param sensorInfos vector of SensorInfo for each dynamic sensor that
+ * was connected.
+ */
+ oneway onDynamicSensorsConnected_2_1(vec<SensorInfo> sensorInfos);
+};
diff --git a/sensors/2.1/default/Android.bp b/sensors/2.1/default/Android.bp
new file mode 100644
index 0000000..27b439d
--- /dev/null
+++ b/sensors/2.1/default/Android.bp
@@ -0,0 +1,45 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+ name: "android.hardware.sensors@2.1-service.mock",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "SensorsV2_1.cpp",
+ "service.cpp",
+ ],
+ init_rc: ["android.hardware.sensors@2.1-service-mock.rc"],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
+ shared_libs: [
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libpower",
+ "libutils",
+ ],
+ static_libs: [
+ "android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors@2.X-shared-impl",
+ ],
+ vintf_fragments: ["android.hardware.sensors@2.1.xml"],
+}
diff --git a/sensors/2.1/default/OWNERS b/sensors/2.1/default/OWNERS
new file mode 100644
index 0000000..90c2330
--- /dev/null
+++ b/sensors/2.1/default/OWNERS
@@ -0,0 +1,3 @@
+arthuri@google.com
+bduddie@google.com
+stange@google.com
diff --git a/sensors/2.1/default/SensorsV2_1.cpp b/sensors/2.1/default/SensorsV2_1.cpp
new file mode 100644
index 0000000..2e3d315
--- /dev/null
+++ b/sensors/2.1/default/SensorsV2_1.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SensorsV2_1.h"
+
+#include "Sensor.h"
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+using V2_X::implementation::ISensorsEventCallback;
+using V2_X::implementation::OnChangeSensor;
+
+class HingeAngleSensor : public OnChangeSensor {
+ public:
+ HingeAngleSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
+ : OnChangeSensor(callback) {
+ mSensorInfo.sensorHandle = sensorHandle;
+ mSensorInfo.name = "Hinge Angle Sensor";
+ mSensorInfo.vendor = "Vendor String";
+ mSensorInfo.version = 1;
+ mSensorInfo.type = SensorType::HINGE_ANGLE;
+ mSensorInfo.typeAsString = "";
+ mSensorInfo.maxRange = 360.0f;
+ mSensorInfo.resolution = 1.0f;
+ mSensorInfo.power = 0.001f;
+ mSensorInfo.minDelay = 40 * 1000; // microseconds
+ mSensorInfo.maxDelay = V2_X::implementation::kDefaultMaxDelayUs;
+ mSensorInfo.fifoReservedEventCount = 0;
+ mSensorInfo.fifoMaxEventCount = 0;
+ mSensorInfo.requiredPermission = "";
+ mSensorInfo.flags = static_cast<uint32_t>(V1_0::SensorFlagBits::ON_CHANGE_MODE);
+ }
+};
+
+SensorsV2_1::SensorsV2_1() {
+ AddSensor<HingeAngleSensor>();
+}
+
+// Methods from ::android::hardware::sensors::V2_1::ISensors follow.
+Return<void> SensorsV2_1::getSensorsList_2_1(ISensors::getSensorsList_2_1_cb _hidl_cb) {
+ std::vector<SensorInfo> sensors;
+ for (const auto& sensor : mSensors) {
+ sensors.push_back(sensor.second->getSensorInfo());
+ }
+
+ // Call the HIDL callback with the SensorInfo
+ _hidl_cb(sensors);
+
+ return Void();
+}
+
+Return<Result> SensorsV2_1::initialize_2_1(
+ const ::android::hardware::MQDescriptorSync<V2_1::Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<V2_1::ISensorsCallback>& sensorsCallback) {
+ auto eventQueue = std::make_unique<MessageQueue<V2_1::Event, kSynchronizedReadWrite>>(
+ eventQueueDescriptor, true /* resetPointers */);
+ std::unique_ptr<EventMessageQueueWrapperBase> wrapper =
+ std::make_unique<EventMessageQueueWrapperV2_1>(eventQueue);
+ mCallbackWrapper = new ISensorsCallbackWrapper(sensorsCallback);
+ return initializeBase(wrapper, wakeLockDescriptor, mCallbackWrapper);
+}
+
+Return<Result> SensorsV2_1::injectSensorData_2_1(const V2_1::Event& event) {
+ return injectSensorData(convertToOldEvent(event));
+}
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/sensors/2.1/default/SensorsV2_1.h b/sensors/2.1/default/SensorsV2_1.h
new file mode 100644
index 0000000..9f7fe04
--- /dev/null
+++ b/sensors/2.1/default/SensorsV2_1.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SENSORS_V2_1_H
+#define ANDROID_HARDWARE_SENSORS_V2_1_H
+
+#include "Sensors.h"
+
+#include "EventMessageQueueWrapper.h"
+
+#include <android/hardware/sensors/2.1/ISensors.h>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+using Result = ::android::hardware::sensors::V1_0::Result;
+using Sensors = ::android::hardware::sensors::V2_X::implementation::Sensors<ISensors>;
+
+class ISensorsCallbackWrapper : public V2_0::ISensorsCallback {
+ public:
+ ISensorsCallbackWrapper(const sp<V2_1::ISensorsCallback>& callback) : mCallback(callback) {}
+
+ Return<void> onDynamicSensorsConnected(const hidl_vec<V1_0::SensorInfo>& sensorInfos) override {
+ return mCallback->onDynamicSensorsConnected_2_1(convertToNewSensorInfos(sensorInfos));
+ }
+
+ Return<void> onDynamicSensorsDisconnected(const hidl_vec<int32_t>& sensorHandles) override {
+ return mCallback->onDynamicSensorsDisconnected(sensorHandles);
+ }
+
+ private:
+ sp<V2_1::ISensorsCallback> mCallback;
+};
+
+struct SensorsV2_1 : public Sensors {
+ SensorsV2_1();
+
+ // Methods from ::android::hardware::sensors::V2_1::ISensors follow.
+ Return<void> getSensorsList_2_1(ISensors::getSensorsList_2_1_cb _hidl_cb) override;
+
+ Return<Result> initialize_2_1(
+ const ::android::hardware::MQDescriptorSync<V2_1::Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<V2_1::ISensorsCallback>& sensorsCallback) override;
+
+ Return<Result> injectSensorData_2_1(const V2_1::Event& event) override;
+
+ private:
+ sp<ISensorsCallbackWrapper> mCallbackWrapper;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_1_H
\ No newline at end of file
diff --git a/sensors/2.1/default/android.hardware.sensors@2.1-service-mock.rc b/sensors/2.1/default/android.hardware.sensors@2.1-service-mock.rc
new file mode 100644
index 0000000..d4147e7
--- /dev/null
+++ b/sensors/2.1/default/android.hardware.sensors@2.1-service-mock.rc
@@ -0,0 +1,7 @@
+service vendor.sensors-hal-2-1-mock /vendor/bin/hw/android.hardware.sensors@2.1-service.mock
+ interface android.hardware.sensors@2.0::ISensors default
+ interface android.hardware.sensors@2.1::ISensors default
+ class hal
+ user system
+ group system
+ rlimit rtprio 10 10
diff --git a/sensors/2.1/default/android.hardware.sensors@2.1.xml b/sensors/2.1/default/android.hardware.sensors@2.1.xml
new file mode 100644
index 0000000..18bd3ae
--- /dev/null
+++ b/sensors/2.1/default/android.hardware.sensors@2.1.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.sensors</name>
+ <transport>hwbinder</transport>
+ <version>2.1</version>
+ <interface>
+ <name>ISensors</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/sensors/2.1/default/service.cpp b/sensors/2.1/default/service.cpp
new file mode 100644
index 0000000..1f3087c
--- /dev/null
+++ b/sensors/2.1/default/service.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.sensors@2.1-service"
+
+#include <android/hardware/sensors/2.1/ISensors.h>
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+#include "SensorsV2_1.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::sensors::V2_1::ISensors;
+using android::hardware::sensors::V2_1::implementation::SensorsV2_1;
+
+int main(int /* argc */, char** /* argv */) {
+ configureRpcThreadpool(1, true);
+
+ android::sp<ISensors> sensors = new SensorsV2_1();
+ if (sensors->registerAsService() != ::android::OK) {
+ ALOGE("Failed to register Sensors HAL instance");
+ return -1;
+ }
+
+ joinRpcThreadpool();
+ return 1; // joinRpcThreadpool shouldn't exit
+}
diff --git a/sensors/2.1/types.hal b/sensors/2.1/types.hal
new file mode 100644
index 0000000..503bece
--- /dev/null
+++ b/sensors/2.1/types.hal
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.sensors@2.1;
+
+import @1.0::EventPayload;
+import @1.0::SensorType;
+import @1.0::SensorFlagBits;
+
+@export(name="", value_prefix="SENSOR_TYPE_")
+enum SensorType : @1.0::SensorType {
+ /**
+ * HINGE_ANGLE
+ * reporting-mode: on-change
+ * wake-up sensor: yes
+ *
+ * A sensor of this type measures the angle, in degrees, between two
+ * integral parts of the device. Movement of a hinge measured by this sensor
+ * type is expected to alter the ways in which the user may interact with
+ * the device, for example by unfolding or revealing a display.
+ *
+ * Sensor data is output using @1.0::EventPayload.scalar.
+ *
+ * Implement wake-up proximity sensor before implementing a non wake-up
+ * proximity sensor.
+ */
+ HINGE_ANGLE = 36,
+};
+
+struct Event {
+ /** Time measured in nanoseconds, in "elapsedRealtimeNano()'s" timebase. */
+ int64_t timestamp;
+
+ /** sensor identifier */
+ int32_t sensorHandle;
+
+ @2.1::SensorType sensorType;
+
+ /** Union discriminated on sensorType */
+ EventPayload u;
+};
+
+struct SensorInfo {
+ /**
+ * handle that identifies this sensors. This handle is used to reference
+ * this sensor throughout the HAL API.
+ */
+ int32_t sensorHandle;
+
+ /**
+ * Name of this sensor.
+ * All sensors of the same "type" must have a different "name".
+ */
+ string name;
+
+ /** vendor of the hardware part */
+ string vendor;
+
+ /**
+ * version of the hardware part + driver. The value of this field
+ * must increase when the driver is updated in a way that changes the
+ * output of this sensor. This is important for fused sensors when the
+ * fusion algorithm is updated.
+ */
+ int32_t version;
+
+ /** this sensor's type. */
+ @2.1::SensorType type;
+
+ /**
+ * type of this sensor as a string.
+ *
+ * When defining an OEM specific sensor or sensor manufacturer specific
+ * sensor, use your reserve domain name as a prefix.
+ * e.g. com.google.glass.onheaddetector
+ *
+ * For sensors of known type defined in SensorType (value <
+ * SensorType::DEVICE_PRIVATE_BASE), this can be an empty string.
+ */
+ string typeAsString;
+
+ /** maximum range of this sensor's value in SI units */
+ float maxRange;
+
+ /** smallest difference between two values reported by this sensor */
+ float resolution;
+
+ /** rough estimate of this sensor's power consumption in mA */
+ float power;
+
+ /**
+ * this value depends on the reporting mode:
+ *
+ * continuous: minimum sample period allowed in microseconds
+ * on-change : 0
+ * one-shot :-1
+ * special : 0, unless otherwise noted
+ */
+ int32_t minDelay;
+
+ /**
+ * number of events reserved for this sensor in the batch mode FIFO.
+ * If there is a dedicated FIFO for this sensor, then this is the
+ * size of this FIFO. If the FIFO is shared with other sensors,
+ * this is the size reserved for that sensor and it can be zero.
+ */
+ uint32_t fifoReservedEventCount;
+
+ /**
+ * maximum number of events of this sensor that could be batched.
+ * This is especially relevant when the FIFO is shared between
+ * several sensors; this value is then set to the size of that FIFO.
+ */
+ uint32_t fifoMaxEventCount;
+
+ /**
+ * permission required to see this sensor, register to it and receive data.
+ * Set to "" if no permission is required. Some sensor types like the
+ * heart rate monitor have a mandatory require_permission.
+ * For sensors that always require a specific permission, like the heart
+ * rate monitor, the android framework might overwrite this string
+ * automatically.
+ */
+ string requiredPermission;
+
+ /**
+ * This value is defined only for continuous mode and on-change sensors.
+ * It is the delay between two sensor events corresponding to the lowest
+ * frequency that this sensor supports. When lower frequencies are requested
+ * through batch()/setDelay() the events will be generated at this frequency
+ * instead.
+ * It can be used by the framework or applications to estimate when the
+ * batch FIFO may be full.
+ *
+ * NOTE: periodNs is in nanoseconds where as maxDelay/minDelay are in
+ * microseconds.
+ *
+ * continuous, on-change: maximum sampling period allowed in
+ * microseconds.
+ *
+ * one-shot, special : 0
+ */
+ int32_t maxDelay;
+
+ /** Bitmask of SensorFlagBits */
+ bitfield<SensorFlagBits> flags;
+};
\ No newline at end of file
diff --git a/sensors/2.1/vts/functional/Android.bp b/sensors/2.1/vts/functional/Android.bp
new file mode 100644
index 0000000..c4f5e9d
--- /dev/null
+++ b/sensors/2.1/vts/functional/Android.bp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "VtsHalSensorsV2_1TargetTest",
+ cflags: [
+ "-DLOG_TAG=\"sensors_hidl_hal_test\"",
+ ],
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "VtsHalSensorsV2_1TargetTest.cpp",
+ ],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
+ static_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@2.1",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
+ "libfmq",
+ "VtsHalSensorsTargetTestUtils",
+ "VtsHalSensorsV2_1TargetTest-lib",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
+}
diff --git a/sensors/2.1/vts/functional/AndroidTest.xml b/sensors/2.1/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..0d8593e
--- /dev/null
+++ b/sensors/2.1/vts/functional/AndroidTest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsHalSensorsV2_1TargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="stop"/>
+ <option name="teardown-command" value="start"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalSensorsV2_1TargetTest->/data/local/tmp/VtsHalSensorsV2_1TargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-timeout" value="900000" />
+ <option name="runtime-hint" value="300000"/>
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalSensorsV2_1TargetTest" />
+ </test>
+</configuration>
diff --git a/sensors/2.1/vts/functional/OWNERS b/sensors/2.1/vts/functional/OWNERS
new file mode 100644
index 0000000..892da15
--- /dev/null
+++ b/sensors/2.1/vts/functional/OWNERS
@@ -0,0 +1,8 @@
+# Sensors team
+arthuri@google.com
+bduddie@google.com
+stange@google.com
+
+# VTS team
+trong@google.com
+yim@google.com
diff --git a/sensors/2.1/vts/functional/VtsHalSensorsV2_1TargetTest.cpp b/sensors/2.1/vts/functional/VtsHalSensorsV2_1TargetTest.cpp
new file mode 100644
index 0000000..230bb6c
--- /dev/null
+++ b/sensors/2.1/vts/functional/VtsHalSensorsV2_1TargetTest.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VtsHalSensorsV2_XTargetTest.h"
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, SensorsHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ android::hardware::sensors::V2_1::ISensors::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/sensors/common/default/2.X/Android.bp b/sensors/common/default/2.X/Android.bp
new file mode 100644
index 0000000..8b0d52f
--- /dev/null
+++ b/sensors/common/default/2.X/Android.bp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+ name: "android.hardware.sensors@2.X-shared-impl",
+ vendor: true,
+ export_include_dirs: ["."],
+ srcs: [
+ "Sensor.cpp",
+ ],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
+ shared_libs: [
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libpower",
+ "libutils",
+ ],
+}
diff --git a/sensors/common/default/2.X/OWNERS b/sensors/common/default/2.X/OWNERS
new file mode 100644
index 0000000..90c2330
--- /dev/null
+++ b/sensors/common/default/2.X/OWNERS
@@ -0,0 +1,3 @@
+arthuri@google.com
+bduddie@google.com
+stange@google.com
diff --git a/sensors/2.0/default/Sensor.cpp b/sensors/common/default/2.X/Sensor.cpp
similarity index 94%
rename from sensors/2.0/default/Sensor.cpp
rename to sensors/common/default/2.X/Sensor.cpp
index c09173f..1841dff 100644
--- a/sensors/2.0/default/Sensor.cpp
+++ b/sensors/common/default/2.X/Sensor.cpp
@@ -23,14 +23,17 @@
namespace android {
namespace hardware {
namespace sensors {
-namespace V2_0 {
+namespace V2_X {
namespace implementation {
using ::android::hardware::sensors::V1_0::MetaDataEventType;
+using ::android::hardware::sensors::V1_0::OperationMode;
+using ::android::hardware::sensors::V1_0::Result;
using ::android::hardware::sensors::V1_0::SensorFlagBits;
using ::android::hardware::sensors::V1_0::SensorStatus;
-
-static constexpr float kDefaultMaxDelayUs = 10 * 1000 * 1000;
+using ::android::hardware::sensors::V2_1::Event;
+using ::android::hardware::sensors::V2_1::SensorInfo;
+using ::android::hardware::sensors::V2_1::SensorType;
Sensor::Sensor(ISensorsEventCallback* callback)
: mIsEnabled(false),
@@ -204,8 +207,8 @@
mSensorInfo.typeAsString = "";
mSensorInfo.maxRange = 78.4f; // +/- 8g
mSensorInfo.resolution = 1.52e-5;
- mSensorInfo.power = 0.001f; // mA
- mSensorInfo.minDelay = 20 * 1000; // microseconds
+ mSensorInfo.power = 0.001f; // mA
+ mSensorInfo.minDelay = 20 * 1000; // microseconds
mSensorInfo.maxDelay = kDefaultMaxDelayUs;
mSensorInfo.fifoReservedEventCount = 0;
mSensorInfo.fifoMaxEventCount = 0;
@@ -221,9 +224,9 @@
mSensorInfo.version = 1;
mSensorInfo.type = SensorType::PRESSURE;
mSensorInfo.typeAsString = "";
- mSensorInfo.maxRange = 1100.0f; // hPa
- mSensorInfo.resolution = 0.005f; // hPa
- mSensorInfo.power = 0.001f; // mA
+ mSensorInfo.maxRange = 1100.0f; // hPa
+ mSensorInfo.resolution = 0.005f; // hPa
+ mSensorInfo.power = 0.001f; // mA
mSensorInfo.minDelay = 100 * 1000; // microseconds
mSensorInfo.maxDelay = kDefaultMaxDelayUs;
mSensorInfo.fifoReservedEventCount = 0;
@@ -242,7 +245,7 @@
mSensorInfo.typeAsString = "";
mSensorInfo.maxRange = 1300.0f;
mSensorInfo.resolution = 0.01f;
- mSensorInfo.power = 0.001f; // mA
+ mSensorInfo.power = 0.001f; // mA
mSensorInfo.minDelay = 20 * 1000; // microseconds
mSensorInfo.maxDelay = kDefaultMaxDelayUs;
mSensorInfo.fifoReservedEventCount = 0;
@@ -261,8 +264,8 @@
mSensorInfo.typeAsString = "";
mSensorInfo.maxRange = 43000.0f;
mSensorInfo.resolution = 10.0f;
- mSensorInfo.power = 0.001f; // mA
- mSensorInfo.minDelay = 200 * 1000; // microseconds
+ mSensorInfo.power = 0.001f; // mA
+ mSensorInfo.minDelay = 200 * 1000; // microseconds
mSensorInfo.maxDelay = kDefaultMaxDelayUs;
mSensorInfo.fifoReservedEventCount = 0;
mSensorInfo.fifoMaxEventCount = 0;
@@ -280,7 +283,7 @@
mSensorInfo.typeAsString = "";
mSensorInfo.maxRange = 5.0f;
mSensorInfo.resolution = 1.0f;
- mSensorInfo.power = 0.012f; // mA
+ mSensorInfo.power = 0.012f; // mA
mSensorInfo.minDelay = 200 * 1000; // microseconds
mSensorInfo.maxDelay = kDefaultMaxDelayUs;
mSensorInfo.fifoReservedEventCount = 0;
@@ -367,7 +370,7 @@
}
} // namespace implementation
-} // namespace V2_0
+} // namespace V2_X
} // namespace sensors
} // namespace hardware
} // namespace android
diff --git a/sensors/2.0/default/Sensor.h b/sensors/common/default/2.X/Sensor.h
similarity index 79%
rename from sensors/2.0/default/Sensor.h
rename to sensors/common/default/2.X/Sensor.h
index 61900fa..2f8a143 100644
--- a/sensors/2.0/default/Sensor.h
+++ b/sensors/common/default/2.X/Sensor.h
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_SENSORS_V2_0_SENSOR_H
-#define ANDROID_HARDWARE_SENSORS_V2_0_SENSOR_H
+#ifndef ANDROID_HARDWARE_SENSORS_V2_X_SENSOR_H
+#define ANDROID_HARDWARE_SENSORS_V2_X_SENSOR_H
#include <android/hardware/sensors/1.0/types.h>
+#include <android/hardware/sensors/2.1/types.h>
#include <condition_variable>
#include <memory>
@@ -25,26 +26,30 @@
#include <thread>
#include <vector>
-using ::android::hardware::sensors::V1_0::Event;
-using ::android::hardware::sensors::V1_0::OperationMode;
-using ::android::hardware::sensors::V1_0::Result;
-using ::android::hardware::sensors::V1_0::SensorInfo;
-using ::android::hardware::sensors::V1_0::SensorType;
-
namespace android {
namespace hardware {
namespace sensors {
-namespace V2_0 {
+namespace V2_X {
namespace implementation {
+static constexpr float kDefaultMaxDelayUs = 10 * 1000 * 1000;
+
class ISensorsEventCallback {
- public:
+ public:
+ using Event = ::android::hardware::sensors::V2_1::Event;
+
virtual ~ISensorsEventCallback(){};
virtual void postEvents(const std::vector<Event>& events, bool wakeup) = 0;
};
class Sensor {
- public:
+ public:
+ using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
+ using Result = ::android::hardware::sensors::V1_0::Result;
+ using Event = ::android::hardware::sensors::V2_1::Event;
+ using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
+ using SensorType = ::android::hardware::sensors::V2_1::SensorType;
+
Sensor(ISensorsEventCallback* callback);
virtual ~Sensor();
@@ -57,7 +62,7 @@
bool supportsDataInjection() const;
Result injectEvent(const Event& event);
- protected:
+ protected:
void run();
virtual std::vector<Event> readEvents();
static void startThread(Sensor* sensor);
@@ -80,68 +85,68 @@
};
class OnChangeSensor : public Sensor {
- public:
+ public:
OnChangeSensor(ISensorsEventCallback* callback);
virtual void activate(bool enable) override;
- protected:
+ protected:
virtual std::vector<Event> readEvents() override;
- protected:
+ protected:
Event mPreviousEvent;
bool mPreviousEventSet;
};
class AccelSensor : public Sensor {
- public:
+ public:
AccelSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class GyroSensor : public Sensor {
- public:
+ public:
GyroSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class AmbientTempSensor : public OnChangeSensor {
- public:
+ public:
AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class DeviceTempSensor : public OnChangeSensor {
- public:
+ public:
DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class PressureSensor : public Sensor {
- public:
+ public:
PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class MagnetometerSensor : public Sensor {
- public:
+ public:
MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class LightSensor : public OnChangeSensor {
- public:
+ public:
LightSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class ProximitySensor : public OnChangeSensor {
- public:
+ public:
ProximitySensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
class RelativeHumiditySensor : public OnChangeSensor {
- public:
+ public:
RelativeHumiditySensor(int32_t sensorHandle, ISensorsEventCallback* callback);
};
} // namespace implementation
-} // namespace V2_0
+} // namespace V2_X
} // namespace sensors
} // namespace hardware
} // namespace android
-#endif // ANDROID_HARDWARE_SENSORS_V2_0_SENSOR_H
+#endif // ANDROID_HARDWARE_SENSORS_V2_X_SENSOR_H
diff --git a/sensors/common/default/2.X/Sensors.h b/sensors/common/default/2.X/Sensors.h
new file mode 100644
index 0000000..ee8240d
--- /dev/null
+++ b/sensors/common/default/2.X/Sensors.h
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SENSORS_V2_X_SENSORS_H
+#define ANDROID_HARDWARE_SENSORS_V2_X_SENSORS_H
+
+#include "EventMessageQueueWrapper.h"
+#include "Sensor.h"
+
+#include <android/hardware/sensors/2.0/ISensors.h>
+#include <android/hardware/sensors/2.0/types.h>
+#include <fmq/MessageQueue.h>
+#include <hardware_legacy/power.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <log/log.h>
+
+#include <atomic>
+#include <memory>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_X {
+namespace implementation {
+
+template <class ISensorsInterface>
+struct Sensors : public ISensorsInterface, public ISensorsEventCallback {
+ using Event = ::android::hardware::sensors::V1_0::Event;
+ using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
+ using RateLevel = ::android::hardware::sensors::V1_0::RateLevel;
+ using Result = ::android::hardware::sensors::V1_0::Result;
+ using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo;
+ using EventQueueFlagBits = ::android::hardware::sensors::V2_0::EventQueueFlagBits;
+ using SensorTimeout = ::android::hardware::sensors::V2_0::SensorTimeout;
+ using WakeLockQueueFlagBits = ::android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
+ using ISensorsCallback = ::android::hardware::sensors::V2_0::ISensorsCallback;
+ using EventMessageQueue = MessageQueue<Event, kSynchronizedReadWrite>;
+ using WakeLockMessageQueue = MessageQueue<uint32_t, kSynchronizedReadWrite>;
+
+ static constexpr const char* kWakeLockName = "SensorsHAL_WAKEUP";
+
+ Sensors()
+ : mEventQueueFlag(nullptr),
+ mNextHandle(1),
+ mOutstandingWakeUpEvents(0),
+ mReadWakeLockQueueRun(false),
+ mAutoReleaseWakeLockTime(0),
+ mHasWakeLock(false) {
+ AddSensor<AccelSensor>();
+ AddSensor<GyroSensor>();
+ AddSensor<AmbientTempSensor>();
+ AddSensor<DeviceTempSensor>();
+ AddSensor<PressureSensor>();
+ AddSensor<MagnetometerSensor>();
+ AddSensor<LightSensor>();
+ AddSensor<ProximitySensor>();
+ AddSensor<RelativeHumiditySensor>();
+ }
+
+ virtual ~Sensors() {
+ deleteEventFlag();
+ mReadWakeLockQueueRun = false;
+ mWakeLockThread.join();
+ }
+
+ // Methods from ::android::hardware::sensors::V2_0::ISensors follow.
+ Return<void> getSensorsList(V2_0::ISensors::getSensorsList_cb _hidl_cb) override {
+ std::vector<V1_0::SensorInfo> sensors;
+ for (const auto& sensor : mSensors) {
+ sensors.push_back(
+ V2_1::implementation::convertToOldSensorInfo(sensor.second->getSensorInfo()));
+ }
+
+ // Call the HIDL callback with the SensorInfo
+ _hidl_cb(sensors);
+
+ return Void();
+ }
+
+ Return<Result> setOperationMode(OperationMode mode) override {
+ for (auto sensor : mSensors) {
+ sensor.second->setOperationMode(mode);
+ }
+ return Result::OK;
+ }
+
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override {
+ auto sensor = mSensors.find(sensorHandle);
+ if (sensor != mSensors.end()) {
+ sensor->second->activate(enabled);
+ return Result::OK;
+ }
+ return Result::BAD_VALUE;
+ }
+
+ Return<Result> initialize(
+ const ::android::hardware::MQDescriptorSync<Event>& eventQueueDescriptor,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<ISensorsCallback>& sensorsCallback) override {
+ auto eventQueue =
+ std::make_unique<EventMessageQueue>(eventQueueDescriptor, true /* resetPointers */);
+ std::unique_ptr<V2_1::implementation::EventMessageQueueWrapperBase> wrapper =
+ std::make_unique<V2_1::implementation::EventMessageQueueWrapperV1_0>(eventQueue);
+ return initializeBase(wrapper, wakeLockDescriptor, sensorsCallback);
+ }
+
+ Return<Result> initializeBase(
+ std::unique_ptr<V2_1::implementation::EventMessageQueueWrapperBase>& eventQueue,
+ const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
+ const sp<ISensorsCallback>& sensorsCallback) {
+ Result result = Result::OK;
+
+ // Ensure that all sensors are disabled
+ for (auto sensor : mSensors) {
+ sensor.second->activate(false /* enable */);
+ }
+
+ // Stop the Wake Lock thread if it is currently running
+ if (mReadWakeLockQueueRun.load()) {
+ mReadWakeLockQueueRun = false;
+ mWakeLockThread.join();
+ }
+
+ // Save a reference to the callback
+ mCallback = sensorsCallback;
+
+ // Save the event queue.
+ mEventQueue = std::move(eventQueue);
+
+ // Ensure that any existing EventFlag is properly deleted
+ deleteEventFlag();
+
+ // Create the EventFlag that is used to signal to the framework that sensor events have been
+ // written to the Event FMQ
+ if (EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag) != OK) {
+ result = Result::BAD_VALUE;
+ }
+
+ // Create the Wake Lock FMQ that is used by the framework to communicate whenever WAKE_UP
+ // events have been successfully read and handled by the framework.
+ mWakeLockQueue = std::make_unique<WakeLockMessageQueue>(wakeLockDescriptor,
+ true /* resetPointers */);
+
+ if (!mCallback || !mEventQueue || !mWakeLockQueue || mEventQueueFlag == nullptr) {
+ result = Result::BAD_VALUE;
+ }
+
+ // Start the thread to read events from the Wake Lock FMQ
+ mReadWakeLockQueueRun = true;
+ mWakeLockThread = std::thread(startReadWakeLockThread, this);
+
+ return result;
+ }
+
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t /* maxReportLatencyNs */) override {
+ auto sensor = mSensors.find(sensorHandle);
+ if (sensor != mSensors.end()) {
+ sensor->second->batch(samplingPeriodNs);
+ return Result::OK;
+ }
+ return Result::BAD_VALUE;
+ }
+
+ Return<Result> flush(int32_t sensorHandle) override {
+ auto sensor = mSensors.find(sensorHandle);
+ if (sensor != mSensors.end()) {
+ return sensor->second->flush();
+ }
+ return Result::BAD_VALUE;
+ }
+
+ Return<Result> injectSensorData(const Event& event) override {
+ auto sensor = mSensors.find(event.sensorHandle);
+ if (sensor != mSensors.end()) {
+ return sensor->second->injectEvent(V2_1::implementation::convertToNewEvent(event));
+ }
+
+ return Result::BAD_VALUE;
+ }
+
+ Return<void> registerDirectChannel(const SharedMemInfo& /* mem */,
+ V2_0::ISensors::registerDirectChannel_cb _hidl_cb) override {
+ _hidl_cb(Result::INVALID_OPERATION, -1 /* channelHandle */);
+ return Return<void>();
+ }
+
+ Return<Result> unregisterDirectChannel(int32_t /* channelHandle */) override {
+ return Result::INVALID_OPERATION;
+ }
+
+ Return<void> configDirectReport(int32_t /* sensorHandle */, int32_t /* channelHandle */,
+ RateLevel /* rate */,
+ V2_0::ISensors::configDirectReport_cb _hidl_cb) override {
+ _hidl_cb(Result::INVALID_OPERATION, 0 /* reportToken */);
+ return Return<void>();
+ }
+
+ void postEvents(const std::vector<V2_1::Event>& events, bool wakeup) override {
+ std::lock_guard<std::mutex> lock(mWriteLock);
+ if (mEventQueue->write(events)) {
+ mEventQueueFlag->wake(static_cast<uint32_t>(EventQueueFlagBits::READ_AND_PROCESS));
+
+ if (wakeup) {
+ // Keep track of the number of outstanding WAKE_UP events in order to properly hold
+ // a wake lock until the framework has secured a wake lock
+ updateWakeLock(events.size(), 0 /* eventsHandled */);
+ }
+ }
+ }
+
+ protected:
+ /**
+ * Add a new sensor
+ */
+ template <class SensorType>
+ void AddSensor() {
+ std::shared_ptr<SensorType> sensor =
+ std::make_shared<SensorType>(mNextHandle++ /* sensorHandle */, this /* callback */);
+ mSensors[sensor->getSensorInfo().sensorHandle] = sensor;
+ }
+
+ /**
+ * Utility function to delete the Event Flag
+ */
+ void deleteEventFlag() {
+ status_t status = EventFlag::deleteEventFlag(&mEventQueueFlag);
+ if (status != OK) {
+ ALOGI("Failed to delete event flag: %d", status);
+ }
+ }
+
+ static void startReadWakeLockThread(Sensors* sensors) { sensors->readWakeLockFMQ(); }
+
+ /**
+ * Function to read the Wake Lock FMQ and release the wake lock when appropriate
+ */
+ void readWakeLockFMQ() {
+ while (mReadWakeLockQueueRun.load()) {
+ constexpr int64_t kReadTimeoutNs = 500 * 1000 * 1000; // 500 ms
+ uint32_t eventsHandled = 0;
+
+ // Read events from the Wake Lock FMQ. Timeout after a reasonable amount of time to
+ // ensure that any held wake lock is able to be released if it is held for too long.
+ mWakeLockQueue->readBlocking(&eventsHandled, 1 /* count */, 0 /* readNotification */,
+ static_cast<uint32_t>(WakeLockQueueFlagBits::DATA_WRITTEN),
+ kReadTimeoutNs);
+ updateWakeLock(0 /* eventsWritten */, eventsHandled);
+ }
+ }
+
+ /**
+ * Responsible for acquiring and releasing a wake lock when there are unhandled WAKE_UP events
+ */
+ void updateWakeLock(int32_t eventsWritten, int32_t eventsHandled) {
+ std::lock_guard<std::mutex> lock(mWakeLockLock);
+ int32_t newVal = mOutstandingWakeUpEvents + eventsWritten - eventsHandled;
+ if (newVal < 0) {
+ mOutstandingWakeUpEvents = 0;
+ } else {
+ mOutstandingWakeUpEvents = newVal;
+ }
+
+ if (eventsWritten > 0) {
+ // Update the time at which the last WAKE_UP event was sent
+ mAutoReleaseWakeLockTime =
+ ::android::uptimeMillis() +
+ static_cast<uint32_t>(SensorTimeout::WAKE_LOCK_SECONDS) * 1000;
+ }
+
+ if (!mHasWakeLock && mOutstandingWakeUpEvents > 0 &&
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLockName) == 0) {
+ mHasWakeLock = true;
+ } else if (mHasWakeLock) {
+ // Check if the wake lock should be released automatically if
+ // SensorTimeout::WAKE_LOCK_SECONDS has elapsed since the last WAKE_UP event was written
+ // to the Wake Lock FMQ.
+ if (::android::uptimeMillis() > mAutoReleaseWakeLockTime) {
+ ALOGD("No events read from wake lock FMQ for %d seconds, auto releasing wake lock",
+ SensorTimeout::WAKE_LOCK_SECONDS);
+ mOutstandingWakeUpEvents = 0;
+ }
+
+ if (mOutstandingWakeUpEvents == 0 && release_wake_lock(kWakeLockName) == 0) {
+ mHasWakeLock = false;
+ }
+ }
+ }
+
+ /**
+ * The Event FMQ where sensor events are written
+ */
+ std::unique_ptr<V2_1::implementation::EventMessageQueueWrapperBase> mEventQueue;
+
+ /**
+ * The Wake Lock FMQ that is read to determine when the framework has handled WAKE_UP events
+ */
+ std::unique_ptr<WakeLockMessageQueue> mWakeLockQueue;
+
+ /**
+ * Event Flag to signal to the framework when sensor events are available to be read
+ */
+ EventFlag* mEventQueueFlag;
+
+ /**
+ * Callback for asynchronous events, such as dynamic sensor connections.
+ */
+ sp<ISensorsCallback> mCallback;
+
+ /**
+ * A map of the available sensors
+ */
+ std::map<int32_t, std::shared_ptr<Sensor>> mSensors;
+
+ /**
+ * The next available sensor handle
+ */
+ int32_t mNextHandle;
+
+ /**
+ * Lock to protect writes to the FMQs
+ */
+ std::mutex mWriteLock;
+
+ /**
+ * Lock to protect acquiring and releasing the wake lock
+ */
+ std::mutex mWakeLockLock;
+
+ /**
+ * Track the number of WAKE_UP events that have not been handled by the framework
+ */
+ uint32_t mOutstandingWakeUpEvents;
+
+ /**
+ * A thread to read the Wake Lock FMQ
+ */
+ std::thread mWakeLockThread;
+
+ /**
+ * Flag to indicate that the Wake Lock Thread should continue to run
+ */
+ std::atomic_bool mReadWakeLockQueueRun;
+
+ /**
+ * Track the time when the wake lock should automatically be released
+ */
+ int64_t mAutoReleaseWakeLockTime;
+
+ /**
+ * Flag to indicate if a wake lock has been acquired
+ */
+ bool mHasWakeLock;
+};
+
+} // namespace implementation
+} // namespace V2_X
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_X_SENSORS_H
diff --git a/sensors/common/utils/Android.bp b/sensors/common/utils/Android.bp
new file mode 100644
index 0000000..aec6c4b
--- /dev/null
+++ b/sensors/common/utils/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_headers {
+ name: "android.hardware.sensors@2.X-shared-utils",
+ vendor_available: true,
+ defaults: ["hidl_defaults"],
+ export_include_dirs: ["."],
+ shared_libs: [
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
+ "libbinder",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libpower",
+ "libutils",
+ ],
+ static_libs: [
+ "android.hardware.sensors@1.0-convert",
+ ],
+}
diff --git a/sensors/common/utils/EventMessageQueueWrapper.h b/sensors/common/utils/EventMessageQueueWrapper.h
new file mode 100644
index 0000000..bf3261f
--- /dev/null
+++ b/sensors/common/utils/EventMessageQueueWrapper.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SENSORS_V2_1_EVENTMESSAGEQUEUEWRAPPER_H
+#define ANDROID_HARDWARE_SENSORS_V2_1_EVENTMESSAGEQUEUEWRAPPER_H
+
+#include "convertV2_1.h"
+
+#include <android/hardware/sensors/2.1/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <log/log.h>
+
+#include <atomic>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+class EventMessageQueueWrapperBase : public RefBase {
+ public:
+ virtual ~EventMessageQueueWrapperBase() {}
+
+ virtual std::atomic<uint32_t>* getEventFlagWord() = 0;
+ virtual size_t availableToRead() = 0;
+ virtual bool read(V2_1::Event* events, size_t numToRead) = 0;
+ virtual bool write(const std::vector<V2_1::Event>& events) = 0;
+};
+
+class EventMessageQueueWrapperV1_0 : public EventMessageQueueWrapperBase {
+ public:
+ using EventMessageQueue = MessageQueue<V1_0::Event, kSynchronizedReadWrite>;
+
+ EventMessageQueueWrapperV1_0(std::unique_ptr<EventMessageQueue>& queue)
+ : mQueue(std::move(queue)) {}
+
+ const ::android::hardware::MQDescriptorSync<V1_0::Event>* getDesc() {
+ return mQueue->getDesc();
+ }
+
+ virtual std::atomic<uint32_t>* getEventFlagWord() override {
+ return mQueue->getEventFlagWord();
+ }
+
+ virtual size_t availableToRead() override { return mQueue->availableToRead(); }
+
+ virtual bool read(V2_1::Event* events, size_t numToRead) override {
+ return mQueue->read(reinterpret_cast<V1_0::Event*>(events), numToRead);
+ }
+
+ virtual bool write(const std::vector<V2_1::Event>& events) override {
+ const std::vector<V1_0::Event>& oldEvents = convertToOldEvents(events);
+ return mQueue->write(oldEvents.data(), oldEvents.size());
+ }
+
+ private:
+ std::unique_ptr<EventMessageQueue> mQueue;
+};
+
+class EventMessageQueueWrapperV2_1 : public EventMessageQueueWrapperBase {
+ public:
+ using EventMessageQueue = MessageQueue<V2_1::Event, kSynchronizedReadWrite>;
+
+ EventMessageQueueWrapperV2_1(std::unique_ptr<EventMessageQueue>& queue)
+ : mQueue(std::move(queue)) {}
+
+ const ::android::hardware::MQDescriptorSync<V2_1::Event>* getDesc() {
+ return mQueue->getDesc();
+ }
+
+ std::atomic<uint32_t>* getEventFlagWord() override { return mQueue->getEventFlagWord(); }
+
+ virtual size_t availableToRead() override { return mQueue->availableToRead(); }
+
+ virtual bool read(V2_1::Event* events, size_t numToRead) override {
+ return mQueue->read(events, numToRead);
+ }
+
+ bool write(const std::vector<V2_1::Event>& events) override {
+ return mQueue->write(events.data(), events.size());
+ }
+
+ private:
+ std::unique_ptr<EventMessageQueue> mQueue;
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_1_EVENTMESSAGEQUEUEWRAPPER_H
\ No newline at end of file
diff --git a/sensors/common/utils/ISensorsWrapper.h b/sensors/common/utils/ISensorsWrapper.h
new file mode 100644
index 0000000..e9c22b1
--- /dev/null
+++ b/sensors/common/utils/ISensorsWrapper.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SENSORS_V2_1_ISENSORSWRAPPER_H
+#define ANDROID_HARDWARE_SENSORS_V2_1_ISENSORSWRAPPER_H
+
+#include "EventMessageQueueWrapper.h"
+#include "ISensorsWrapper.h"
+
+#include "android/hardware/sensors/1.0/ISensors.h"
+#include "android/hardware/sensors/1.0/types.h"
+#include "android/hardware/sensors/2.0/ISensors.h"
+#include "android/hardware/sensors/2.0/ISensorsCallback.h"
+#include "android/hardware/sensors/2.1/ISensors.h"
+#include "android/hardware/sensors/2.1/ISensorsCallback.h"
+#include "android/hardware/sensors/2.1/types.h"
+
+#include <utils/LightRefBase.h>
+
+#include <cassert>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::hardware::MessageQueue;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::Return;
+using ::android::hardware::sensors::V1_0::ISensors;
+using ::android::hardware::sensors::V1_0::OperationMode;
+using ::android::hardware::sensors::V1_0::RateLevel;
+using ::android::hardware::sensors::V1_0::Result;
+using ::android::hardware::sensors::V1_0::SharedMemInfo;
+using ::android::hardware::sensors::V2_1::Event;
+using ::android::hardware::sensors::V2_1::ISensorsCallback;
+
+// TODO: Look into providing this as a param if it needs to be a different value
+// than the framework.
+static constexpr size_t MAX_RECEIVE_BUFFER_EVENT_COUNT = 256;
+
+/*
+ * The ISensorsWrapper interface includes all function from supported Sensors HAL versions. This
+ * allows for the SensorDevice to use the ISensorsWrapper interface to interact with the Sensors
+ * HAL regardless of the current version of the Sensors HAL that is loaded. Each concrete
+ * instantiation of ISensorsWrapper must correspond to a specific Sensors HAL version. This design
+ * is beneficial because only the functions that change between Sensors HAL versions must be newly
+ * implemented, any previously implemented function that does not change may remain the same.
+ *
+ * Functions that exist across all versions of the Sensors HAL should be implemented as pure
+ * virtual functions which forces the concrete instantiations to implement the functions.
+ *
+ * Functions that do not exist across all versions of the Sensors HAL should include a default
+ * implementation that generates an error if called. The default implementation should never
+ * be called and must be overridden by Sensors HAL versions that support the function.
+ */
+class ISensorsWrapperBase : public VirtualLightRefBase {
+ public:
+ virtual bool supportsPolling() const = 0;
+
+ virtual bool supportsMessageQueues() const = 0;
+
+ virtual void linkToDeath(android::sp<android::hardware::hidl_death_recipient> deathRecipient,
+ uint64_t cookie) = 0;
+
+ virtual Return<void> getSensorsList(
+ ::android::hardware::sensors::V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb) = 0;
+
+ virtual Return<Result> setOperationMode(OperationMode mode) = 0;
+
+ virtual Return<Result> activate(int32_t sensorHandle, bool enabled) = 0;
+
+ virtual Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) = 0;
+
+ virtual Return<Result> flush(int32_t sensorHandle) = 0;
+
+ virtual Return<Result> injectSensorData(const Event& event) = 0;
+
+ virtual Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ ISensors::registerDirectChannel_cb _hidl_cb) = 0;
+
+ virtual Return<Result> unregisterDirectChannel(int32_t channelHandle) = 0;
+
+ virtual Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle,
+ RateLevel rate,
+ ISensors::configDirectReport_cb _hidl_cb) = 0;
+
+ virtual Return<void> poll(int32_t /* maxCount */, ISensors::poll_cb /* _hidl_cb */) {
+ // Enforce this method is never invoked as it should be overridden if it's meant to be used.
+ assert(false);
+ return Return<void>();
+ }
+
+ virtual EventMessageQueueWrapperBase* getEventQueue() { return nullptr; }
+
+ virtual Return<Result> initialize(const MQDescriptorSync<uint32_t>& /* wakeLockDesc */,
+ const ::android::sp<ISensorsCallback>& /* callback */) {
+ // Enforce this method is never invoked as it should be overridden if it's meant to be used.
+ assert(false);
+ return Result::INVALID_OPERATION;
+ }
+};
+
+template <typename T>
+class SensorsWrapperBase : public ISensorsWrapperBase {
+ public:
+ SensorsWrapperBase(sp<T> sensors) : mSensors(sensors){};
+
+ void linkToDeath(android::sp<android::hardware::hidl_death_recipient> deathRecipient,
+ uint64_t cookie) override {
+ mSensors->linkToDeath(deathRecipient, cookie);
+ }
+
+ virtual Return<void> getSensorsList(
+ ::android::hardware::sensors::V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb) override {
+ return mSensors->getSensorsList(
+ [&](const auto& list) { _hidl_cb(convertToNewSensorInfos(list)); });
+ }
+
+ Return<Result> setOperationMode(OperationMode mode) override {
+ return mSensors->setOperationMode(mode);
+ }
+
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override {
+ return mSensors->activate(sensorHandle, enabled);
+ }
+
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) override {
+ return mSensors->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
+ }
+
+ Return<Result> flush(int32_t sensorHandle) override { return mSensors->flush(sensorHandle); }
+
+ virtual Return<Result> injectSensorData(const Event& event) override {
+ return mSensors->injectSensorData(convertToOldEvent(event));
+ }
+
+ Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ ISensors::registerDirectChannel_cb _hidl_cb) override {
+ return mSensors->registerDirectChannel(mem, _hidl_cb);
+ }
+
+ Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
+ return mSensors->unregisterDirectChannel(channelHandle);
+ }
+
+ Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
+ ISensors::configDirectReport_cb _hidl_cb) override {
+ return mSensors->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
+ }
+
+ protected:
+ sp<T> mSensors;
+};
+
+class ISensorsWrapperV1_0 : public SensorsWrapperBase<hardware::sensors::V1_0::ISensors> {
+ public:
+ ISensorsWrapperV1_0(sp<hardware::sensors::V1_0::ISensors> sensors)
+ : SensorsWrapperBase(sensors){};
+
+ bool supportsPolling() const override { return true; }
+
+ bool supportsMessageQueues() const override { return false; }
+
+ Return<void> poll(int32_t maxCount,
+ hardware::sensors::V1_0::ISensors::poll_cb _hidl_cb) override {
+ return mSensors->poll(maxCount, _hidl_cb);
+ }
+};
+
+class ISensorsWrapperV2_0 : public SensorsWrapperBase<hardware::sensors::V2_0::ISensors> {
+ public:
+ typedef MessageQueue<::android::hardware::sensors::V1_0::Event,
+ ::android::hardware::kSynchronizedReadWrite>
+ EventMessageQueue;
+
+ ISensorsWrapperV2_0(sp<hardware::sensors::V2_0::ISensors> sensors)
+ : SensorsWrapperBase(sensors) {
+ auto eventQueue = std::make_unique<EventMessageQueue>(MAX_RECEIVE_BUFFER_EVENT_COUNT,
+ true /* configureEventFlagWord */);
+ mEventQueue = std::make_unique<EventMessageQueueWrapperV1_0>(eventQueue);
+ };
+
+ bool supportsPolling() const override { return false; }
+
+ bool supportsMessageQueues() const override { return true; }
+
+ EventMessageQueueWrapperBase* getEventQueue() override { return mEventQueue.get(); }
+
+ Return<Result> initialize(const MQDescriptorSync<uint32_t>& wakeLockDesc,
+ const ::android::sp<ISensorsCallback>& callback) override {
+ return mSensors->initialize(*mEventQueue->getDesc(), wakeLockDesc, callback);
+ }
+
+ private:
+ std::unique_ptr<EventMessageQueueWrapperV1_0> mEventQueue;
+};
+
+class ISensorsWrapperV2_1 : public SensorsWrapperBase<hardware::sensors::V2_1::ISensors> {
+ public:
+ typedef MessageQueue<Event, ::android::hardware::kSynchronizedReadWrite> EventMessageQueueV2_1;
+
+ ISensorsWrapperV2_1(sp<hardware::sensors::V2_1::ISensors> sensors)
+ : SensorsWrapperBase(sensors) {
+ auto eventQueue = std::make_unique<EventMessageQueueV2_1>(
+ MAX_RECEIVE_BUFFER_EVENT_COUNT, true /* configureEventFlagWord */);
+ mEventQueue = std::make_unique<EventMessageQueueWrapperV2_1>(eventQueue);
+ };
+
+ bool supportsPolling() const override { return false; }
+
+ bool supportsMessageQueues() const override { return true; }
+
+ EventMessageQueueWrapperBase* getEventQueue() override { return mEventQueue.get(); }
+
+ Return<void> getSensorsList(
+ ::android::hardware::sensors::V2_1::ISensors::getSensorsList_2_1_cb _hidl_cb) override {
+ return mSensors->getSensorsList_2_1(_hidl_cb);
+ }
+
+ Return<Result> injectSensorData(const Event& event) override {
+ return mSensors->injectSensorData_2_1(event);
+ }
+
+ Return<Result> initialize(const MQDescriptorSync<uint32_t>& wakeLockDesc,
+ const ::android::sp<ISensorsCallback>& callback) override {
+ return mSensors->initialize_2_1(*mEventQueue->getDesc(), wakeLockDesc, callback);
+ }
+
+ private:
+ std::unique_ptr<EventMessageQueueWrapperV2_1> mEventQueue;
+};
+
+inline sp<ISensorsWrapperV2_0> wrapISensors(sp<V2_0::ISensors> sensors) {
+ return new ISensorsWrapperV2_0(sensors);
+}
+
+inline sp<ISensorsWrapperV2_1> wrapISensors(sp<V2_1::ISensors> sensors) {
+ return new ISensorsWrapperV2_1(sensors);
+}
+
+class NoOpSensorsCallback : public ISensorsCallback {
+ public:
+ Return<void> onDynamicSensorsConnected(
+ const hidl_vec<V1_0::SensorInfo>& /* sensorInfos */) override {
+ return Return<void>();
+ }
+
+ Return<void> onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t>& /* sensorHandles */) override {
+ return Return<void>();
+ }
+
+ Return<void> onDynamicSensorsConnected_2_1(
+ const hidl_vec<SensorInfo>& /* sensorInfos */) override {
+ return Return<void>();
+ }
+};
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_1_ISENSORSWRAPPER_H
\ No newline at end of file
diff --git a/sensors/common/utils/OWNERS b/sensors/common/utils/OWNERS
new file mode 100644
index 0000000..90c2330
--- /dev/null
+++ b/sensors/common/utils/OWNERS
@@ -0,0 +1,3 @@
+arthuri@google.com
+bduddie@google.com
+stange@google.com
diff --git a/sensors/common/utils/convertV2_1.h b/sensors/common/utils/convertV2_1.h
new file mode 100644
index 0000000..9231011
--- /dev/null
+++ b/sensors/common/utils/convertV2_1.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SENSORS_V2_1_CONVERT_H
+#define ANDROID_HARDWARE_SENSORS_V2_1_CONVERT_H
+
+#include <android/hardware/sensors/2.1/types.h>
+#include <hardware/sensors.h>
+#include <sensors/convert.h>
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_1 {
+namespace implementation {
+
+static_assert(sizeof(V1_0::Event) == sizeof(V2_1::Event),
+ "New and old Event types must have the same size");
+static_assert(sizeof(V1_0::SensorInfo) == sizeof(V2_1::SensorInfo),
+ "New and old SensorInfo types must have the same size");
+
+// The following conversion methods are safe as the only difference between
+// V1_0 and V2_1 for these types is an added enum value to SensorType which doesn't
+// change the memory layout of the types.
+inline const V1_0::Event& convertToOldEvent(const V2_1::Event& event) {
+ return reinterpret_cast<const V1_0::Event&>(event);
+}
+
+inline const std::vector<V1_0::Event>& convertToOldEvents(const std::vector<V2_1::Event>& events) {
+ return reinterpret_cast<const std::vector<V1_0::Event>&>(events);
+}
+
+inline V1_0::Event* convertToOldEvent(V2_1::Event* event) {
+ return reinterpret_cast<V1_0::Event*>(event);
+}
+
+inline const V2_1::SensorInfo& convertToNewSensorInfo(const V1_0::SensorInfo& info) {
+ return reinterpret_cast<const V2_1::SensorInfo&>(info);
+}
+
+inline const V1_0::SensorInfo& convertToOldSensorInfo(const V2_1::SensorInfo& info) {
+ return reinterpret_cast<const V1_0::SensorInfo&>(info);
+}
+
+inline const V2_1::Event& convertToNewEvent(const V1_0::Event& event) {
+ return reinterpret_cast<const V2_1::Event&>(event);
+}
+
+inline const std::vector<V2_1::Event>& convertToNewEvents(const std::vector<V1_0::Event>& events) {
+ return reinterpret_cast<const std::vector<V2_1::Event>&>(events);
+}
+
+inline const hidl_vec<V2_1::Event>& convertToNewEvents(const hidl_vec<V1_0::Event>& events) {
+ return reinterpret_cast<const hidl_vec<V2_1::Event>&>(events);
+}
+
+inline const hidl_vec<V2_1::SensorInfo>& convertToNewSensorInfos(
+ const hidl_vec<V1_0::SensorInfo>& infos) {
+ return reinterpret_cast<const hidl_vec<V2_1::SensorInfo>&>(infos);
+}
+
+inline const hidl_vec<V1_0::SensorInfo>& convertToOldSensorInfos(
+ const hidl_vec<V2_1::SensorInfo>& infos) {
+ return reinterpret_cast<const hidl_vec<V1_0::SensorInfo>&>(infos);
+}
+
+inline void convertFromSensorEvent(const sensors_event_t& src, V2_1::Event* dst) {
+ switch ((SensorType)src.type) {
+ case SensorType::HINGE_ANGLE:
+ // Only fill in values for hinge angle as other sensors
+ // will have it filled in by legacy code.
+ *dst = {
+ .timestamp = src.timestamp,
+ .sensorHandle = src.sensor,
+ .sensorType = (SensorType)src.type,
+ };
+ dst->u.scalar = src.data[0];
+ break;
+ default:
+ V1_0::implementation::convertFromSensorEvent(src, convertToOldEvent(dst));
+ break;
+ }
+}
+
+inline void convertToSensorEvent(const V2_1::Event& src, sensors_event_t* dst) {
+ switch (src.sensorType) {
+ case SensorType::HINGE_ANGLE:
+ // Only fill in values for hinge angle as other sensors
+ // will have it filled in by legacy code.
+ *dst = {.version = sizeof(sensors_event_t),
+ .sensor = src.sensorHandle,
+ .type = (int32_t)src.sensorType,
+ .reserved0 = 0,
+ .timestamp = src.timestamp};
+ dst->data[0] = src.u.scalar;
+ break;
+ default:
+ V1_0::implementation::convertToSensorEvent(convertToOldEvent(src), dst);
+ break;
+ }
+}
+
+} // namespace implementation
+} // namespace V2_1
+} // namespace sensors
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SENSORS_V2_1_CONVERT_H
diff --git a/sensors/common/vts/2_X/Android.bp b/sensors/common/vts/2_X/Android.bp
new file mode 100644
index 0000000..8cdb5d1
--- /dev/null
+++ b/sensors/common/vts/2_X/Android.bp
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "VtsHalSensorsV2_XTargetTest-defaults",
+ cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "SensorsHidlEnvironmentV2_X.cpp",
+ ],
+ export_include_dirs: ["."],
+ header_libs: [
+ "android.hardware.sensors@2.X-shared-utils",
+ ],
+ shared_libs: [
+ "libbinder",
+ ],
+ static_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.allocator@3.0",
+ "android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@2.1",
+ "android.hardware.graphics.mapper@3.0",
+ "android.hardware.sensors@1.0",
+ "android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
+ "libfmq",
+ "VtsHalSensorsTargetTestUtils",
+ ],
+}
+
+cc_test_library {
+ name: "VtsHalSensorsV2_0TargetTest-lib",
+ defaults: ["VtsHalSensorsV2_XTargetTest-defaults"],
+}
+
+cc_test_library {
+ name: "VtsHalSensorsV2_1TargetTest-lib",
+ cflags: ["-DSENSORS_HAL_2_1"],
+ defaults: ["VtsHalSensorsV2_XTargetTest-defaults"],
+}
diff --git a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp b/sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.cpp
similarity index 69%
rename from sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp
rename to sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.cpp
index 81db5a0..a8c2513 100644
--- a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp
+++ b/sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.cpp
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-#include "SensorsHidlEnvironmentV2_0.h"
+#include "SensorsHidlEnvironmentV2_X.h"
#include <android/hardware/sensors/2.0/types.h>
+#include <android/hardware/sensors/2.1/types.h>
+
#include <log/log.h>
#include <algorithm>
@@ -26,18 +28,20 @@
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::sensors::V1_0::Result;
-using ::android::hardware::sensors::V1_0::SensorInfo;
using ::android::hardware::sensors::V2_0::EventQueueFlagBits;
+using ::android::hardware::sensors::V2_1::SensorInfo;
+#ifdef SENSORS_HAL_2_1
+using ::android::hardware::sensors::V2_1::ISensors;
+#else
using ::android::hardware::sensors::V2_0::ISensors;
-using ::android::hardware::sensors::V2_0::ISensorsCallback;
+#endif
+using ::android::hardware::sensors::V2_1::ISensorsCallback;
template <typename EnumType>
constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
return static_cast<typename std::underlying_type<EnumType>::type>(value);
}
-constexpr size_t SensorsHidlEnvironmentV2_0::MAX_RECEIVE_BUFFER_EVENT_COUNT;
-
void SensorsHalDeathRecipient::serviceDied(
uint64_t /* cookie */,
const ::android::wp<::android::hidl::base::V1_0::IBase>& /* service */) {
@@ -45,48 +49,34 @@
FAIL() << "Sensors HAL died during test";
}
-struct SensorsCallback : ISensorsCallback {
- Return<void> onDynamicSensorsConnected(const hidl_vec<SensorInfo>& /* sensorInfos */) {
- return Return<void>();
- }
-
- Return<void> onDynamicSensorsDisconnected(const hidl_vec<int32_t>& /* sensorHandles */) {
- return Return<void>();
- }
-};
-
-bool SensorsHidlEnvironmentV2_0::resetHal() {
+bool SensorsHidlEnvironmentV2_X::resetHal() {
bool succeed = false;
do {
- mSensors = ISensors::getService(mServiceName);
+ mSensors = wrapISensors(ISensors::getService(mServiceName));
if (mSensors == nullptr) {
break;
}
mSensors->linkToDeath(mDeathRecipient, 0 /* cookie */);
// Initialize FMQs
- mEventQueue = std::make_unique<EventMessageQueue>(MAX_RECEIVE_BUFFER_EVENT_COUNT,
- true /* configureEventFlagWord */);
-
mWakeLockQueue = std::make_unique<WakeLockQueue>(MAX_RECEIVE_BUFFER_EVENT_COUNT,
true /* configureEventFlagWord */);
- if (mEventQueue == nullptr || mWakeLockQueue == nullptr) {
+ if (mWakeLockQueue == nullptr) {
break;
}
EventFlag::deleteEventFlag(&mEventQueueFlag);
- EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag);
+ EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(), &mEventQueueFlag);
if (mEventQueueFlag == nullptr) {
break;
}
- mSensors->initialize(*mEventQueue->getDesc(), *mWakeLockQueue->getDesc(),
- new SensorsCallback());
+ mSensors->initialize(*mWakeLockQueue->getDesc(), new NoOpSensorsCallback());
std::vector<SensorInfo> sensorList;
if (!mSensors->getSensorsList([&](const hidl_vec<SensorInfo>& list) { sensorList = list; })
- .isOk()) {
+ .isOk()) {
break;
}
@@ -113,7 +103,7 @@
return succeed;
}
-void SensorsHidlEnvironmentV2_0::HidlTearDown() {
+void SensorsHidlEnvironmentV2_X::HidlTearDown() {
mStopThread = true;
if (mEventQueueFlag != nullptr) {
@@ -127,25 +117,25 @@
}
}
-void SensorsHidlEnvironmentV2_0::startPollingThread() {
+void SensorsHidlEnvironmentV2_X::startPollingThread() {
mStopThread = false;
mEvents.reserve(MAX_RECEIVE_BUFFER_EVENT_COUNT);
mPollThread = std::thread(pollingThread, this);
}
-void SensorsHidlEnvironmentV2_0::readEvents() {
- size_t availableEvents = mEventQueue->availableToRead();
+void SensorsHidlEnvironmentV2_X::readEvents() {
+ size_t availableEvents = mSensors->getEventQueue()->availableToRead();
if (availableEvents == 0) {
uint32_t eventFlagState = 0;
mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS), &eventFlagState);
- availableEvents = mEventQueue->availableToRead();
+ availableEvents = mSensors->getEventQueue()->availableToRead();
}
size_t eventsToRead = std::min(availableEvents, mEventBuffer.size());
if (eventsToRead > 0) {
- if (mEventQueue->read(mEventBuffer.data(), eventsToRead)) {
+ if (mSensors->getEventQueue()->read(mEventBuffer.data(), eventsToRead)) {
mEventQueueFlag->wake(asBaseType(EventQueueFlagBits::EVENTS_READ));
for (size_t i = 0; i < eventsToRead; i++) {
addEvent(mEventBuffer[i]);
@@ -154,7 +144,7 @@
}
}
-void SensorsHidlEnvironmentV2_0::pollingThread(SensorsHidlEnvironmentV2_0* env) {
+void SensorsHidlEnvironmentV2_X::pollingThread(SensorsHidlEnvironmentV2_X* env) {
ALOGD("polling thread start");
while (!env->mStopThread.load()) {
diff --git a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.h b/sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.h
similarity index 68%
rename from sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.h
rename to sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.h
index 819cdd4..01f451f 100644
--- a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.h
+++ b/sensors/common/vts/2_X/SensorsHidlEnvironmentV2_X.h
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-#ifndef ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_0_H
-#define ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_0_H
+#ifndef ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_X_H
+#define ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_X_H
+#include "ISensorsWrapper.h"
#include "sensors-vts-utils/SensorsHidlEnvironmentBase.h"
-#include <android/hardware/sensors/1.0/types.h>
-#include <android/hardware/sensors/2.0/ISensors.h>
+#include <android/hardware/sensors/2.1/ISensors.h>
+#include <android/hardware/sensors/2.1/types.h>
+
#include <fmq/MessageQueue.h>
#include <utils/StrongPointer.h>
@@ -30,6 +32,10 @@
using ::android::sp;
using ::android::hardware::MessageQueue;
+using ::android::hardware::sensors::V2_1::implementation::ISensorsWrapperBase;
+using ::android::hardware::sensors::V2_1::implementation::MAX_RECEIVE_BUFFER_EVENT_COUNT;
+using ::android::hardware::sensors::V2_1::implementation::NoOpSensorsCallback;
+using ::android::hardware::sensors::V2_1::implementation::wrapISensors;
class SensorsHidlTest;
@@ -39,14 +45,14 @@
const ::android::wp<::android::hidl::base::V1_0::IBase>& service) override;
};
-class SensorsHidlEnvironmentV2_0 : public SensorsHidlEnvironmentBase {
- public:
- using Event = ::android::hardware::sensors::V1_0::Event;
+class SensorsHidlEnvironmentV2_X
+ : public SensorsHidlEnvironmentBase<::android::hardware::sensors::V2_1::Event> {
+ public:
virtual void HidlTearDown() override;
- protected:
+ protected:
friend SensorsHidlTest;
- SensorsHidlEnvironmentV2_0(const std::string& service_name)
+ SensorsHidlEnvironmentV2_X(const std::string& service_name)
: SensorsHidlEnvironmentBase(service_name), mEventQueueFlag(nullptr) {}
/**
@@ -66,19 +72,19 @@
*
* @param env SensorEnvironment to being polling for events on
*/
- static void pollingThread(SensorsHidlEnvironmentV2_0* env);
+ static void pollingThread(SensorsHidlEnvironmentV2_X* env);
/**
* Reads and saves sensor events from the Event FMQ
*/
void readEvents();
- GTEST_DISALLOW_COPY_AND_ASSIGN_(SensorsHidlEnvironmentV2_0);
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(SensorsHidlEnvironmentV2_X);
/**
* Pointer to the Sensors HAL Interface that allows the test to call HAL functions.
*/
- sp<android::hardware::sensors::V2_0::ISensors> mSensors;
+ sp<ISensorsWrapperBase> mSensors;
/**
* Monitors the HAL for crashes, triggering test failure if seen
@@ -86,22 +92,11 @@
sp<SensorsHalDeathRecipient> mDeathRecipient = new SensorsHalDeathRecipient();
/**
- * Type used to simplify the creation of the Event FMQ
- */
- typedef MessageQueue<Event, ::android::hardware::kSynchronizedReadWrite> EventMessageQueue;
-
- /**
* Type used to simplify the creation of the Wake Lock FMQ
*/
typedef MessageQueue<uint32_t, ::android::hardware::kSynchronizedReadWrite> WakeLockQueue;
/**
- * The Event FMQ where the test framework is able to read sensor events that the Sensors HAL
- * has written.
- */
- std::unique_ptr<EventMessageQueue> mEventQueue;
-
- /**
* The Wake Lock FMQ is used by the test to notify the Sensors HAL whenever it has processed
* WAKE_UP sensor events.
*/
@@ -114,14 +109,10 @@
::android::hardware::EventFlag* mEventQueueFlag;
/**
- * The maximum number of sensor events that can be read from the Event FMQ at one time.
- */
- static constexpr size_t MAX_RECEIVE_BUFFER_EVENT_COUNT = 128;
-
- /**
* An array that is used to store sensor events read from the Event FMQ
*/
- std::array<Event, MAX_RECEIVE_BUFFER_EVENT_COUNT> mEventBuffer;
+ std::array<::android::hardware::sensors::V2_1::Event, MAX_RECEIVE_BUFFER_EVENT_COUNT>
+ mEventBuffer;
};
-#endif // ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_0_H
+#endif // ANDROID_SENSORS_HIDL_ENVIRONMENT_V2_X_H
diff --git a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
new file mode 100644
index 0000000..53ed259
--- /dev/null
+++ b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
@@ -0,0 +1,1186 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "SensorsHidlEnvironmentV2_X.h"
+#include "convertV2_1.h"
+#include "sensors-vts-utils/SensorsHidlTestBase.h"
+#include "sensors-vts-utils/SensorsTestSharedMemory.h"
+
+#include <android/hardware/sensors/2.1/ISensors.h>
+#include <android/hardware/sensors/2.1/types.h>
+
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+#include <utils/SystemClock.h>
+
+#include <cinttypes>
+#include <condition_variable>
+#include <cstring>
+#include <map>
+#include <vector>
+
+/**
+ * This file contains the core tests and test logic for both sensors HAL 2.0
+ * and 2.1. To make it easier to share the code between both VTS test suites,
+ * this is defined as a header so they can both include and use all pieces of
+ * code.
+ */
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::sensors::V1_0::MetaDataEventType;
+using ::android::hardware::sensors::V1_0::OperationMode;
+using ::android::hardware::sensors::V1_0::SensorsEventFormatOffset;
+using ::android::hardware::sensors::V1_0::SensorStatus;
+using ::android::hardware::sensors::V1_0::SharedMemType;
+using ::android::hardware::sensors::V1_0::Vec3;
+using ::android::hardware::sensors::V2_1::implementation::convertToOldSensorInfos;
+using std::chrono::duration_cast;
+using std::chrono::microseconds;
+using std::chrono::milliseconds;
+using std::chrono::nanoseconds;
+
+using EventV1_0 = ::android::hardware::sensors::V1_0::Event;
+using ISensorsType = ::android::hardware::sensors::V2_1::ISensors;
+using SensorTypeVersion = ::android::hardware::sensors::V2_1::SensorType;
+using EventType = ::android::hardware::sensors::V2_1::Event;
+using SensorInfoType = ::android::hardware::sensors::V2_1::SensorInfo;
+using SensorsHidlTestBaseV2_X = SensorsHidlTestBase<SensorTypeVersion, EventType, SensorInfoType>;
+
+constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
+
+class EventCallback : public IEventCallback<EventType> {
+ public:
+ void reset() {
+ mFlushMap.clear();
+ mEventMap.clear();
+ }
+
+ void onEvent(const EventType& event) override {
+ if (event.sensorType == SensorTypeVersion::META_DATA &&
+ event.u.meta.what == MetaDataEventType::META_DATA_FLUSH_COMPLETE) {
+ std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
+ mFlushMap[event.sensorHandle]++;
+ mFlushCV.notify_all();
+ } else if (event.sensorType != SensorTypeVersion::ADDITIONAL_INFO) {
+ std::unique_lock<std::recursive_mutex> lock(mEventMutex);
+ mEventMap[event.sensorHandle].push_back(event);
+ mEventCV.notify_all();
+ }
+ }
+
+ int32_t getFlushCount(int32_t sensorHandle) {
+ std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
+ return mFlushMap[sensorHandle];
+ }
+
+ void waitForFlushEvents(const std::vector<SensorInfoType>& sensorsToWaitFor,
+ int32_t numCallsToFlush, milliseconds timeout) {
+ std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
+ mFlushCV.wait_for(lock, timeout,
+ [&] { return flushesReceived(sensorsToWaitFor, numCallsToFlush); });
+ }
+
+ const std::vector<EventType> getEvents(int32_t sensorHandle) {
+ std::unique_lock<std::recursive_mutex> lock(mEventMutex);
+ return mEventMap[sensorHandle];
+ }
+
+ void waitForEvents(const std::vector<SensorInfoType>& sensorsToWaitFor, milliseconds timeout) {
+ std::unique_lock<std::recursive_mutex> lock(mEventMutex);
+ mEventCV.wait_for(lock, timeout, [&] { return eventsReceived(sensorsToWaitFor); });
+ }
+
+ protected:
+ bool flushesReceived(const std::vector<SensorInfoType>& sensorsToWaitFor,
+ int32_t numCallsToFlush) {
+ for (const SensorInfoType& sensor : sensorsToWaitFor) {
+ if (getFlushCount(sensor.sensorHandle) < numCallsToFlush) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool eventsReceived(const std::vector<SensorInfoType>& sensorsToWaitFor) {
+ for (const SensorInfoType& sensor : sensorsToWaitFor) {
+ if (getEvents(sensor.sensorHandle).size() == 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ std::map<int32_t, int32_t> mFlushMap;
+ std::recursive_mutex mFlushMutex;
+ std::condition_variable_any mFlushCV;
+
+ std::map<int32_t, std::vector<EventType>> mEventMap;
+ std::recursive_mutex mEventMutex;
+ std::condition_variable_any mEventCV;
+};
+
+/**
+ * Define the template specific versions of the static helper methods in
+ * SensorsHidlTestBase used to test that hinge angle is exposed properly.
+ */
+template <>
+SensorFlagBits expectedReportModeForType(::android::hardware::sensors::V2_1::SensorType type) {
+ switch (type) {
+ case ::android::hardware::sensors::V2_1::SensorType::HINGE_ANGLE:
+ return SensorFlagBits::ON_CHANGE_MODE;
+ default:
+ return expectedReportModeForType(
+ static_cast<::android::hardware::sensors::V1_0::SensorType>(type));
+ }
+}
+
+template <>
+void assertTypeMatchStringType(::android::hardware::sensors::V2_1::SensorType type,
+ const hidl_string& stringType) {
+ switch (type) {
+ case (::android::hardware::sensors::V2_1::SensorType::HINGE_ANGLE):
+ ASSERT_STREQ(SENSOR_STRING_TYPE_HINGE_ANGLE, stringType.c_str());
+ break;
+ default:
+ assertTypeMatchStringType(
+ static_cast<::android::hardware::sensors::V1_0::SensorType>(type), stringType);
+ break;
+ }
+}
+
+// The main test class for SENSORS HIDL HAL.
+class SensorsHidlTest : public SensorsHidlTestBaseV2_X {
+ public:
+ virtual void SetUp() override {
+ mEnvironment = new SensorsHidlEnvironmentV2_X(GetParam());
+ mEnvironment->HidlSetUp();
+ // Ensure that we have a valid environment before performing tests
+ ASSERT_NE(getSensors(), nullptr);
+ }
+
+ virtual void TearDown() override { mEnvironment->HidlTearDown(); }
+
+ protected:
+ SensorInfoType defaultSensorByType(SensorTypeVersion type) override;
+ std::vector<SensorInfoType> getSensorsList();
+ // implementation wrapper
+
+ Return<void> getSensorsList(ISensorsType::getSensorsList_cb _hidl_cb) override {
+ return getSensors()->getSensorsList(
+ [&](const auto& list) { _hidl_cb(convertToOldSensorInfos(list)); });
+ }
+
+ Return<Result> activate(int32_t sensorHandle, bool enabled) override;
+
+ Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) override {
+ return getSensors()->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
+ }
+
+ Return<Result> flush(int32_t sensorHandle) override {
+ return getSensors()->flush(sensorHandle);
+ }
+
+ Return<Result> injectSensorData(const EventType& event) override {
+ return getSensors()->injectSensorData(event);
+ }
+
+ Return<void> registerDirectChannel(const SharedMemInfo& mem,
+ ISensorsType::registerDirectChannel_cb _hidl_cb) override;
+
+ Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
+ return getSensors()->unregisterDirectChannel(channelHandle);
+ }
+
+ Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, RateLevel rate,
+ ISensorsType::configDirectReport_cb _hidl_cb) override {
+ return getSensors()->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
+ }
+
+ inline sp<ISensorsWrapperBase>& getSensors() { return mEnvironment->mSensors; }
+
+ SensorsHidlEnvironmentBase<EventType>* getEnvironment() override { return mEnvironment; }
+
+ // Test helpers
+ void runSingleFlushTest(const std::vector<SensorInfoType>& sensors, bool activateSensor,
+ int32_t expectedFlushCount, Result expectedResponse);
+ void runFlushTest(const std::vector<SensorInfoType>& sensors, bool activateSensor,
+ int32_t flushCalls, int32_t expectedFlushCount, Result expectedResponse);
+
+ // Helper functions
+ void activateAllSensors(bool enable);
+ std::vector<SensorInfoType> getNonOneShotSensors();
+ std::vector<SensorInfoType> getNonOneShotAndNonSpecialSensors();
+ std::vector<SensorInfoType> getOneShotSensors();
+ std::vector<SensorInfoType> getInjectEventSensors();
+ int32_t getInvalidSensorHandle();
+ bool getDirectChannelSensor(SensorInfoType* sensor, SharedMemType* memType, RateLevel* rate);
+ void verifyDirectChannel(SharedMemType memType);
+ void verifyRegisterDirectChannel(
+ std::shared_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem,
+ int32_t* directChannelHandle, bool supportsSharedMemType,
+ bool supportsAnyDirectChannel);
+ void verifyConfigure(const SensorInfoType& sensor, SharedMemType memType,
+ int32_t directChannelHandle, bool directChannelSupported);
+ void verifyUnregisterDirectChannel(int32_t directChannelHandle, bool directChannelSupported);
+ void checkRateLevel(const SensorInfoType& sensor, int32_t directChannelHandle,
+ RateLevel rateLevel);
+ void queryDirectChannelSupport(SharedMemType memType, bool* supportsSharedMemType,
+ bool* supportsAnyDirectChannel);
+
+ private:
+ // Test environment for sensors HAL.
+ SensorsHidlEnvironmentV2_X* mEnvironment;
+};
+
+Return<Result> SensorsHidlTest::activate(int32_t sensorHandle, bool enabled) {
+ // If activating a sensor, add the handle in a set so that when test fails it can be turned off.
+ // The handle is not removed when it is deactivating on purpose so that it is not necessary to
+ // check the return value of deactivation. Deactivating a sensor more than once does not have
+ // negative effect.
+ if (enabled) {
+ mSensorHandles.insert(sensorHandle);
+ }
+ return getSensors()->activate(sensorHandle, enabled);
+}
+
+Return<void> SensorsHidlTest::registerDirectChannel(const SharedMemInfo& mem,
+ ISensors::registerDirectChannel_cb cb) {
+ // If registeration of a channel succeeds, add the handle of channel to a set so that it can be
+ // unregistered when test fails. Unregister a channel does not remove the handle on purpose.
+ // Unregistering a channel more than once should not have negative effect.
+ getSensors()->registerDirectChannel(mem, [&](auto result, auto channelHandle) {
+ if (result == Result::OK) {
+ mDirectChannelHandles.insert(channelHandle);
+ }
+ cb(result, channelHandle);
+ });
+ return Void();
+}
+
+SensorInfoType SensorsHidlTest::defaultSensorByType(SensorTypeVersion type) {
+ SensorInfoType ret;
+
+ ret.type = (SensorTypeVersion)-1;
+ getSensors()->getSensorsList([&](const auto& list) {
+ const size_t count = list.size();
+ for (size_t i = 0; i < count; ++i) {
+ if (list[i].type == type) {
+ ret = list[i];
+ return;
+ }
+ }
+ });
+
+ return ret;
+}
+
+std::vector<SensorInfoType> SensorsHidlTest::getSensorsList() {
+ std::vector<SensorInfoType> ret;
+
+ getSensors()->getSensorsList([&](const auto& list) {
+ const size_t count = list.size();
+ ret.reserve(list.size());
+ for (size_t i = 0; i < count; ++i) {
+ ret.push_back(list[i]);
+ }
+ });
+
+ return ret;
+}
+
+std::vector<SensorInfoType> SensorsHidlTest::getNonOneShotSensors() {
+ std::vector<SensorInfoType> sensors;
+ for (const SensorInfoType& info : getSensorsList()) {
+ if (extractReportMode(info.flags) != SensorFlagBits::ONE_SHOT_MODE) {
+ sensors.push_back(info);
+ }
+ }
+ return sensors;
+}
+
+std::vector<SensorInfoType> SensorsHidlTest::getNonOneShotAndNonSpecialSensors() {
+ std::vector<SensorInfoType> sensors;
+ for (const SensorInfoType& info : getSensorsList()) {
+ SensorFlagBits reportMode = extractReportMode(info.flags);
+ if (reportMode != SensorFlagBits::ONE_SHOT_MODE &&
+ reportMode != SensorFlagBits::SPECIAL_REPORTING_MODE) {
+ sensors.push_back(info);
+ }
+ }
+ return sensors;
+}
+
+std::vector<SensorInfoType> SensorsHidlTest::getOneShotSensors() {
+ std::vector<SensorInfoType> sensors;
+ for (const SensorInfoType& info : getSensorsList()) {
+ if (extractReportMode(info.flags) == SensorFlagBits::ONE_SHOT_MODE) {
+ sensors.push_back(info);
+ }
+ }
+ return sensors;
+}
+
+std::vector<SensorInfoType> SensorsHidlTest::getInjectEventSensors() {
+ std::vector<SensorInfoType> sensors;
+ for (const SensorInfoType& info : getSensorsList()) {
+ if (info.flags & static_cast<uint32_t>(SensorFlagBits::DATA_INJECTION)) {
+ sensors.push_back(info);
+ }
+ }
+ return sensors;
+}
+
+int32_t SensorsHidlTest::getInvalidSensorHandle() {
+ // Find a sensor handle that does not exist in the sensor list
+ int32_t maxHandle = 0;
+ for (const SensorInfoType& sensor : getSensorsList()) {
+ maxHandle = std::max(maxHandle, sensor.sensorHandle);
+ }
+ return maxHandle + 1;
+}
+
+// Test if sensor list returned is valid
+TEST_P(SensorsHidlTest, SensorListValid) {
+ getSensors()->getSensorsList([&](const auto& list) {
+ const size_t count = list.size();
+ for (size_t i = 0; i < count; ++i) {
+ const auto& s = list[i];
+ SCOPED_TRACE(::testing::Message()
+ << i << "/" << count << ": "
+ << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
+ << s.sensorHandle << std::dec << " type=" << static_cast<int>(s.type)
+ << " name=" << s.name);
+
+ // Test non-empty type string
+ EXPECT_FALSE(s.typeAsString.empty());
+
+ // Test defined type matches defined string type
+ EXPECT_NO_FATAL_FAILURE(assertTypeMatchStringType(s.type, s.typeAsString));
+
+ // Test if all sensor has name and vendor
+ EXPECT_FALSE(s.name.empty());
+ EXPECT_FALSE(s.vendor.empty());
+
+ // Test power > 0, maxRange > 0
+ EXPECT_LE(0, s.power);
+ EXPECT_LT(0, s.maxRange);
+
+ // Info type, should have no sensor
+ EXPECT_FALSE(s.type == SensorTypeVersion::ADDITIONAL_INFO ||
+ s.type == SensorTypeVersion::META_DATA);
+
+ // Test fifoMax >= fifoReserved
+ EXPECT_GE(s.fifoMaxEventCount, s.fifoReservedEventCount)
+ << "max=" << s.fifoMaxEventCount << " reserved=" << s.fifoReservedEventCount;
+
+ // Test Reporting mode valid
+ EXPECT_NO_FATAL_FAILURE(assertTypeMatchReportMode(s.type, extractReportMode(s.flags)));
+
+ // Test min max are in the right order
+ EXPECT_LE(s.minDelay, s.maxDelay);
+ // Test min/max delay matches reporting mode
+ EXPECT_NO_FATAL_FAILURE(
+ assertDelayMatchReportMode(s.minDelay, s.maxDelay, extractReportMode(s.flags)));
+ }
+ });
+}
+
+// Test that SetOperationMode returns the expected value
+TEST_P(SensorsHidlTest, SetOperationMode) {
+ std::vector<SensorInfoType> sensors = getInjectEventSensors();
+ if (getInjectEventSensors().size() > 0) {
+ ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
+ ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
+ ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
+ } else {
+ ASSERT_EQ(Result::BAD_VALUE, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
+ }
+}
+
+// Test that an injected event is written back to the Event FMQ
+TEST_P(SensorsHidlTest, InjectSensorEventData) {
+ std::vector<SensorInfoType> sensors = getInjectEventSensors();
+ if (sensors.size() == 0) {
+ return;
+ }
+
+ ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::DATA_INJECTION));
+
+ EventCallback callback;
+ getEnvironment()->registerCallback(&callback);
+
+ // AdditionalInfo event should not be sent to Event FMQ
+ EventType additionalInfoEvent;
+ additionalInfoEvent.sensorType = SensorTypeVersion::ADDITIONAL_INFO;
+ additionalInfoEvent.timestamp = android::elapsedRealtimeNano();
+
+ EventType injectedEvent;
+ injectedEvent.timestamp = android::elapsedRealtimeNano();
+ Vec3 data = {1, 2, 3, SensorStatus::ACCURACY_HIGH};
+ injectedEvent.u.vec3 = data;
+
+ for (const auto& s : sensors) {
+ additionalInfoEvent.sensorHandle = s.sensorHandle;
+ EXPECT_EQ(Result::OK, getSensors()->injectSensorData(additionalInfoEvent));
+
+ injectedEvent.sensorType = s.type;
+ injectedEvent.sensorHandle = s.sensorHandle;
+ EXPECT_EQ(Result::OK, getSensors()->injectSensorData(injectedEvent));
+ }
+
+ // Wait for events to be written back to the Event FMQ
+ callback.waitForEvents(sensors, milliseconds(1000) /* timeout */);
+
+ for (const auto& s : sensors) {
+ auto events = callback.getEvents(s.sensorHandle);
+ auto lastEvent = events.back();
+
+ // Verify that only a single event has been received
+ ASSERT_EQ(events.size(), 1);
+
+ // Verify that the event received matches the event injected and is not the additional
+ // info event
+ ASSERT_EQ(lastEvent.sensorType, s.type);
+ ASSERT_EQ(lastEvent.sensorType, s.type);
+ ASSERT_EQ(lastEvent.timestamp, injectedEvent.timestamp);
+ ASSERT_EQ(lastEvent.u.vec3.x, injectedEvent.u.vec3.x);
+ ASSERT_EQ(lastEvent.u.vec3.y, injectedEvent.u.vec3.y);
+ ASSERT_EQ(lastEvent.u.vec3.z, injectedEvent.u.vec3.z);
+ ASSERT_EQ(lastEvent.u.vec3.status, injectedEvent.u.vec3.status);
+ }
+
+ getEnvironment()->unregisterCallback();
+ ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
+}
+
+// Test if sensor hal can do UI speed accelerometer streaming properly
+TEST_P(SensorsHidlTest, AccelerometerStreamingOperationSlow) {
+ testStreamingOperation(SensorTypeVersion::ACCELEROMETER, std::chrono::milliseconds(200),
+ std::chrono::seconds(5), mAccelNormChecker);
+}
+
+// Test if sensor hal can do normal speed accelerometer streaming properly
+TEST_P(SensorsHidlTest, AccelerometerStreamingOperationNormal) {
+ testStreamingOperation(SensorTypeVersion::ACCELEROMETER, std::chrono::milliseconds(20),
+ std::chrono::seconds(5), mAccelNormChecker);
+}
+
+// Test if sensor hal can do game speed accelerometer streaming properly
+TEST_P(SensorsHidlTest, AccelerometerStreamingOperationFast) {
+ testStreamingOperation(SensorTypeVersion::ACCELEROMETER, std::chrono::milliseconds(5),
+ std::chrono::seconds(5), mAccelNormChecker);
+}
+
+// Test if sensor hal can do UI speed gyroscope streaming properly
+TEST_P(SensorsHidlTest, GyroscopeStreamingOperationSlow) {
+ testStreamingOperation(SensorTypeVersion::GYROSCOPE, std::chrono::milliseconds(200),
+ std::chrono::seconds(5), mGyroNormChecker);
+}
+
+// Test if sensor hal can do normal speed gyroscope streaming properly
+TEST_P(SensorsHidlTest, GyroscopeStreamingOperationNormal) {
+ testStreamingOperation(SensorTypeVersion::GYROSCOPE, std::chrono::milliseconds(20),
+ std::chrono::seconds(5), mGyroNormChecker);
+}
+
+// Test if sensor hal can do game speed gyroscope streaming properly
+TEST_P(SensorsHidlTest, GyroscopeStreamingOperationFast) {
+ testStreamingOperation(SensorTypeVersion::GYROSCOPE, std::chrono::milliseconds(5),
+ std::chrono::seconds(5), mGyroNormChecker);
+}
+
+// Test if sensor hal can do UI speed magnetometer streaming properly
+TEST_P(SensorsHidlTest, MagnetometerStreamingOperationSlow) {
+ testStreamingOperation(SensorTypeVersion::MAGNETIC_FIELD, std::chrono::milliseconds(200),
+ std::chrono::seconds(5), NullChecker<EventType>());
+}
+
+// Test if sensor hal can do normal speed magnetometer streaming properly
+TEST_P(SensorsHidlTest, MagnetometerStreamingOperationNormal) {
+ testStreamingOperation(SensorTypeVersion::MAGNETIC_FIELD, std::chrono::milliseconds(20),
+ std::chrono::seconds(5), NullChecker<EventType>());
+}
+
+// Test if sensor hal can do game speed magnetometer streaming properly
+TEST_P(SensorsHidlTest, MagnetometerStreamingOperationFast) {
+ testStreamingOperation(SensorTypeVersion::MAGNETIC_FIELD, std::chrono::milliseconds(5),
+ std::chrono::seconds(5), NullChecker<EventType>());
+}
+
+// Test if sensor hal can do accelerometer sampling rate switch properly when sensor is active
+TEST_P(SensorsHidlTest, AccelerometerSamplingPeriodHotSwitchOperation) {
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::ACCELEROMETER);
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::ACCELEROMETER, false /*fastToSlow*/);
+}
+
+// Test if sensor hal can do gyroscope sampling rate switch properly when sensor is active
+TEST_P(SensorsHidlTest, GyroscopeSamplingPeriodHotSwitchOperation) {
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::GYROSCOPE);
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::GYROSCOPE, false /*fastToSlow*/);
+}
+
+// Test if sensor hal can do magnetometer sampling rate switch properly when sensor is active
+TEST_P(SensorsHidlTest, MagnetometerSamplingPeriodHotSwitchOperation) {
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::MAGNETIC_FIELD);
+ testSamplingRateHotSwitchOperation(SensorTypeVersion::MAGNETIC_FIELD, false /*fastToSlow*/);
+}
+
+// Test if sensor hal can do accelerometer batching properly
+TEST_P(SensorsHidlTest, AccelerometerBatchingOperation) {
+ testBatchingOperation(SensorTypeVersion::ACCELEROMETER);
+}
+
+// Test if sensor hal can do gyroscope batching properly
+TEST_P(SensorsHidlTest, GyroscopeBatchingOperation) {
+ testBatchingOperation(SensorTypeVersion::GYROSCOPE);
+}
+
+// Test if sensor hal can do magnetometer batching properly
+TEST_P(SensorsHidlTest, MagnetometerBatchingOperation) {
+ testBatchingOperation(SensorTypeVersion::MAGNETIC_FIELD);
+}
+
+// Test sensor event direct report with ashmem for accel sensor at normal rate
+TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::ASHMEM,
+ RateLevel::NORMAL, mAccelNormChecker);
+}
+
+// Test sensor event direct report with ashmem for accel sensor at fast rate
+TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::ASHMEM,
+ RateLevel::FAST, mAccelNormChecker);
+}
+
+// Test sensor event direct report with ashmem for accel sensor at very fast rate
+TEST_P(SensorsHidlTest, AccelerometerAshmemDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::ASHMEM,
+ RateLevel::VERY_FAST, mAccelNormChecker);
+}
+
+// Test sensor event direct report with ashmem for gyro sensor at normal rate
+TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::ASHMEM,
+ RateLevel::NORMAL, mGyroNormChecker);
+}
+
+// Test sensor event direct report with ashmem for gyro sensor at fast rate
+TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::ASHMEM, RateLevel::FAST,
+ mGyroNormChecker);
+}
+
+// Test sensor event direct report with ashmem for gyro sensor at very fast rate
+TEST_P(SensorsHidlTest, GyroscopeAshmemDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::ASHMEM,
+ RateLevel::VERY_FAST, mGyroNormChecker);
+}
+
+// Test sensor event direct report with ashmem for mag sensor at normal rate
+TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::ASHMEM,
+ RateLevel::NORMAL, NullChecker<EventType>());
+}
+
+// Test sensor event direct report with ashmem for mag sensor at fast rate
+TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::ASHMEM,
+ RateLevel::FAST, NullChecker<EventType>());
+}
+
+// Test sensor event direct report with ashmem for mag sensor at very fast rate
+TEST_P(SensorsHidlTest, MagnetometerAshmemDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::ASHMEM,
+ RateLevel::VERY_FAST, NullChecker<EventType>());
+}
+
+// Test sensor event direct report with gralloc for accel sensor at normal rate
+TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::GRALLOC,
+ RateLevel::NORMAL, mAccelNormChecker);
+}
+
+// Test sensor event direct report with gralloc for accel sensor at fast rate
+TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::GRALLOC,
+ RateLevel::FAST, mAccelNormChecker);
+}
+
+// Test sensor event direct report with gralloc for accel sensor at very fast rate
+TEST_P(SensorsHidlTest, AccelerometerGrallocDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::ACCELEROMETER, SharedMemType::GRALLOC,
+ RateLevel::VERY_FAST, mAccelNormChecker);
+}
+
+// Test sensor event direct report with gralloc for gyro sensor at normal rate
+TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::GRALLOC,
+ RateLevel::NORMAL, mGyroNormChecker);
+}
+
+// Test sensor event direct report with gralloc for gyro sensor at fast rate
+TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::GRALLOC, RateLevel::FAST,
+ mGyroNormChecker);
+}
+
+// Test sensor event direct report with gralloc for gyro sensor at very fast rate
+TEST_P(SensorsHidlTest, GyroscopeGrallocDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::GYROSCOPE, SharedMemType::GRALLOC,
+ RateLevel::VERY_FAST, mGyroNormChecker);
+}
+
+// Test sensor event direct report with gralloc for mag sensor at normal rate
+TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationNormal) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::GRALLOC,
+ RateLevel::NORMAL, NullChecker<EventType>());
+}
+
+// Test sensor event direct report with gralloc for mag sensor at fast rate
+TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationFast) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::GRALLOC,
+ RateLevel::FAST, NullChecker<EventType>());
+}
+
+// Test sensor event direct report with gralloc for mag sensor at very fast rate
+TEST_P(SensorsHidlTest, MagnetometerGrallocDirectReportOperationVeryFast) {
+ testDirectReportOperation(SensorTypeVersion::MAGNETIC_FIELD, SharedMemType::GRALLOC,
+ RateLevel::VERY_FAST, NullChecker<EventType>());
+}
+
+void SensorsHidlTest::activateAllSensors(bool enable) {
+ for (const SensorInfoType& sensorInfo : getSensorsList()) {
+ if (isValidType(sensorInfo.type)) {
+ batch(sensorInfo.sensorHandle, sensorInfo.minDelay, 0 /* maxReportLatencyNs */);
+ activate(sensorInfo.sensorHandle, enable);
+ }
+ }
+}
+
+// Test that if initialize is called twice, then the HAL writes events to the FMQs from the second
+// call to the function.
+TEST_P(SensorsHidlTest, CallInitializeTwice) {
+ // Create a helper class so that a second environment is able to be instantiated
+ class SensorsHidlEnvironmentTest : public SensorsHidlEnvironmentV2_X {
+ public:
+ SensorsHidlEnvironmentTest(const std::string& service_name)
+ : SensorsHidlEnvironmentV2_X(service_name) {}
+ };
+
+ if (getSensorsList().size() == 0) {
+ // No sensors
+ return;
+ }
+
+ constexpr useconds_t kCollectionTimeoutUs = 1000 * 1000; // 1s
+ constexpr int32_t kNumEvents = 1;
+
+ // Create a new environment that calls initialize()
+ std::unique_ptr<SensorsHidlEnvironmentTest> newEnv =
+ std::make_unique<SensorsHidlEnvironmentTest>(GetParam());
+ newEnv->HidlSetUp();
+ if (HasFatalFailure()) {
+ return; // Exit early if setting up the new environment failed
+ }
+
+ activateAllSensors(true);
+ // Verify that the old environment does not receive any events
+ EXPECT_EQ(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), 0);
+ // Verify that the new event queue receives sensor events
+ EXPECT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, newEnv.get(), newEnv.get()).size(),
+ kNumEvents);
+ activateAllSensors(false);
+
+ // Cleanup the test environment
+ newEnv->HidlTearDown();
+
+ // Restore the test environment for future tests
+ getEnvironment()->HidlTearDown();
+ getEnvironment()->HidlSetUp();
+ if (HasFatalFailure()) {
+ return; // Exit early if resetting the environment failed
+ }
+
+ // Ensure that the original environment is receiving events
+ activateAllSensors(true);
+ EXPECT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents).size(), kNumEvents);
+ activateAllSensors(false);
+}
+
+TEST_P(SensorsHidlTest, CleanupConnectionsOnInitialize) {
+ activateAllSensors(true);
+
+ // Verify that events are received
+ constexpr useconds_t kCollectionTimeoutUs = 1000 * 1000; // 1s
+ constexpr int32_t kNumEvents = 1;
+ ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), kNumEvents);
+
+ // Clear the active sensor handles so they are not disabled during TearDown
+ auto handles = mSensorHandles;
+ mSensorHandles.clear();
+ getEnvironment()->HidlTearDown();
+ getEnvironment()->HidlSetUp();
+ if (HasFatalFailure()) {
+ return; // Exit early if resetting the environment failed
+ }
+
+ // Verify no events are received until sensors are re-activated
+ ASSERT_EQ(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), 0);
+ activateAllSensors(true);
+ ASSERT_GE(collectEvents(kCollectionTimeoutUs, kNumEvents, getEnvironment()).size(), kNumEvents);
+
+ // Disable sensors
+ activateAllSensors(false);
+
+ // Restore active sensors prior to clearing the environment
+ mSensorHandles = handles;
+}
+
+void SensorsHidlTest::runSingleFlushTest(const std::vector<SensorInfoType>& sensors,
+ bool activateSensor, int32_t expectedFlushCount,
+ Result expectedResponse) {
+ runFlushTest(sensors, activateSensor, 1 /* flushCalls */, expectedFlushCount, expectedResponse);
+}
+
+void SensorsHidlTest::runFlushTest(const std::vector<SensorInfoType>& sensors, bool activateSensor,
+ int32_t flushCalls, int32_t expectedFlushCount,
+ Result expectedResponse) {
+ EventCallback callback;
+ getEnvironment()->registerCallback(&callback);
+
+ for (const SensorInfoType& sensor : sensors) {
+ // Configure and activate the sensor
+ batch(sensor.sensorHandle, sensor.maxDelay, 0 /* maxReportLatencyNs */);
+ activate(sensor.sensorHandle, activateSensor);
+
+ // Flush the sensor
+ for (int32_t i = 0; i < flushCalls; i++) {
+ Result flushResult = flush(sensor.sensorHandle);
+ ASSERT_EQ(flushResult, expectedResponse);
+ }
+ }
+
+ // Wait up to one second for the flush events
+ callback.waitForFlushEvents(sensors, flushCalls, milliseconds(1000) /* timeout */);
+
+ // Deactivate all sensors after waiting for flush events so pending flush events are not
+ // abandoned by the HAL.
+ for (const SensorInfoType& sensor : sensors) {
+ activate(sensor.sensorHandle, false);
+ }
+ getEnvironment()->unregisterCallback();
+
+ // Check that the correct number of flushes are present for each sensor
+ for (const SensorInfoType& sensor : sensors) {
+ ASSERT_EQ(callback.getFlushCount(sensor.sensorHandle), expectedFlushCount);
+ }
+}
+
+TEST_P(SensorsHidlTest, FlushSensor) {
+ // Find a sensor that is not a one-shot sensor
+ std::vector<SensorInfoType> sensors = getNonOneShotSensors();
+ if (sensors.size() == 0) {
+ return;
+ }
+
+ constexpr int32_t kFlushes = 5;
+ runSingleFlushTest(sensors, true /* activateSensor */, 1 /* expectedFlushCount */, Result::OK);
+ runFlushTest(sensors, true /* activateSensor */, kFlushes, kFlushes, Result::OK);
+}
+
+TEST_P(SensorsHidlTest, FlushOneShotSensor) {
+ // Find a sensor that is a one-shot sensor
+ std::vector<SensorInfoType> sensors = getOneShotSensors();
+ if (sensors.size() == 0) {
+ return;
+ }
+
+ runSingleFlushTest(sensors, true /* activateSensor */, 0 /* expectedFlushCount */,
+ Result::BAD_VALUE);
+}
+
+TEST_P(SensorsHidlTest, FlushInactiveSensor) {
+ // Attempt to find a non-one shot sensor, then a one-shot sensor if necessary
+ std::vector<SensorInfoType> sensors = getNonOneShotSensors();
+ if (sensors.size() == 0) {
+ sensors = getOneShotSensors();
+ if (sensors.size() == 0) {
+ return;
+ }
+ }
+
+ runSingleFlushTest(sensors, false /* activateSensor */, 0 /* expectedFlushCount */,
+ Result::BAD_VALUE);
+}
+
+TEST_P(SensorsHidlTest, FlushNonexistentSensor) {
+ SensorInfoType sensor;
+ std::vector<SensorInfoType> sensors = getNonOneShotSensors();
+ if (sensors.size() == 0) {
+ sensors = getOneShotSensors();
+ if (sensors.size() == 0) {
+ return;
+ }
+ }
+ sensor = sensors.front();
+ sensor.sensorHandle = getInvalidSensorHandle();
+ runSingleFlushTest(std::vector<SensorInfoType>{sensor}, false /* activateSensor */,
+ 0 /* expectedFlushCount */, Result::BAD_VALUE);
+}
+
+TEST_P(SensorsHidlTest, Batch) {
+ if (getSensorsList().size() == 0) {
+ return;
+ }
+
+ activateAllSensors(false /* enable */);
+ for (const SensorInfoType& sensor : getSensorsList()) {
+ // Call batch on inactive sensor
+ // One shot sensors have minDelay set to -1 which is an invalid
+ // parameter. Use 0 instead to avoid errors.
+ int64_t samplingPeriodNs = extractReportMode(sensor.flags) == SensorFlagBits::ONE_SHOT_MODE
+ ? 0
+ : sensor.minDelay;
+ ASSERT_EQ(batch(sensor.sensorHandle, samplingPeriodNs, 0 /* maxReportLatencyNs */),
+ Result::OK);
+
+ // Activate the sensor
+ activate(sensor.sensorHandle, true /* enabled */);
+
+ // Call batch on an active sensor
+ ASSERT_EQ(batch(sensor.sensorHandle, sensor.maxDelay, 0 /* maxReportLatencyNs */),
+ Result::OK);
+ }
+ activateAllSensors(false /* enable */);
+
+ // Call batch on an invalid sensor
+ SensorInfoType sensor = getSensorsList().front();
+ sensor.sensorHandle = getInvalidSensorHandle();
+ ASSERT_EQ(batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */),
+ Result::BAD_VALUE);
+}
+
+TEST_P(SensorsHidlTest, Activate) {
+ if (getSensorsList().size() == 0) {
+ return;
+ }
+
+ // Verify that sensor events are generated when activate is called
+ for (const SensorInfoType& sensor : getSensorsList()) {
+ batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */);
+ ASSERT_EQ(activate(sensor.sensorHandle, true), Result::OK);
+
+ // Call activate on a sensor that is already activated
+ ASSERT_EQ(activate(sensor.sensorHandle, true), Result::OK);
+
+ // Deactivate the sensor
+ ASSERT_EQ(activate(sensor.sensorHandle, false), Result::OK);
+
+ // Call deactivate on a sensor that is already deactivated
+ ASSERT_EQ(activate(sensor.sensorHandle, false), Result::OK);
+ }
+
+ // Attempt to activate an invalid sensor
+ int32_t invalidHandle = getInvalidSensorHandle();
+ ASSERT_EQ(activate(invalidHandle, true), Result::BAD_VALUE);
+ ASSERT_EQ(activate(invalidHandle, false), Result::BAD_VALUE);
+}
+
+TEST_P(SensorsHidlTest, NoStaleEvents) {
+ constexpr milliseconds kFiveHundredMs(500);
+ constexpr milliseconds kOneSecond(1000);
+
+ // Register the callback to receive sensor events
+ EventCallback callback;
+ getEnvironment()->registerCallback(&callback);
+
+ // This test is not valid for one-shot or special-report-mode sensors
+ const std::vector<SensorInfoType> sensors = getNonOneShotAndNonSpecialSensors();
+ milliseconds maxMinDelay(0);
+ for (const SensorInfoType& sensor : sensors) {
+ milliseconds minDelay = duration_cast<milliseconds>(microseconds(sensor.minDelay));
+ maxMinDelay = milliseconds(std::max(maxMinDelay.count(), minDelay.count()));
+ }
+
+ // Activate the sensors so that they start generating events
+ activateAllSensors(true);
+
+ // According to the CDD, the first sample must be generated within 400ms + 2 * sample_time
+ // and the maximum reporting latency is 100ms + 2 * sample_time. Wait a sufficient amount
+ // of time to guarantee that a sample has arrived.
+ callback.waitForEvents(sensors, kFiveHundredMs + (5 * maxMinDelay));
+ activateAllSensors(false);
+
+ // Save the last received event for each sensor
+ std::map<int32_t, int64_t> lastEventTimestampMap;
+ for (const SensorInfoType& sensor : sensors) {
+ // Some on-change sensors may not report an event without stimulus
+ if (extractReportMode(sensor.flags) != SensorFlagBits::ON_CHANGE_MODE) {
+ ASSERT_GE(callback.getEvents(sensor.sensorHandle).size(), 1);
+ }
+ if (callback.getEvents(sensor.sensorHandle).size() >= 1) {
+ lastEventTimestampMap[sensor.sensorHandle] =
+ callback.getEvents(sensor.sensorHandle).back().timestamp;
+ }
+ }
+
+ // Allow some time to pass, reset the callback, then reactivate the sensors
+ usleep(duration_cast<microseconds>(kOneSecond + (5 * maxMinDelay)).count());
+ callback.reset();
+ activateAllSensors(true);
+ callback.waitForEvents(sensors, kFiveHundredMs + (5 * maxMinDelay));
+ activateAllSensors(false);
+
+ for (const SensorInfoType& sensor : sensors) {
+ // Skip sensors that did not previously report an event
+ if (lastEventTimestampMap.find(sensor.sensorHandle) == lastEventTimestampMap.end()) {
+ continue;
+ }
+ // Skip on-change sensors that do not consistently report an initial event
+ if (callback.getEvents(sensor.sensorHandle).size() < 1) {
+ continue;
+ }
+ // Ensure that the first event received is not stale by ensuring that its timestamp is
+ // sufficiently different from the previous event
+ const EventType newEvent = callback.getEvents(sensor.sensorHandle).front();
+ milliseconds delta = duration_cast<milliseconds>(
+ nanoseconds(newEvent.timestamp - lastEventTimestampMap[sensor.sensorHandle]));
+ milliseconds sensorMinDelay = duration_cast<milliseconds>(microseconds(sensor.minDelay));
+ ASSERT_GE(delta, kFiveHundredMs + (3 * sensorMinDelay));
+ }
+}
+
+void SensorsHidlTest::checkRateLevel(const SensorInfoType& sensor, int32_t directChannelHandle,
+ RateLevel rateLevel) {
+ configDirectReport(sensor.sensorHandle, directChannelHandle, rateLevel,
+ [&](Result result, int32_t reportToken) {
+ if (isDirectReportRateSupported(sensor, rateLevel)) {
+ ASSERT_EQ(result, Result::OK);
+ if (rateLevel != RateLevel::STOP) {
+ ASSERT_GT(reportToken, 0);
+ }
+ } else {
+ ASSERT_EQ(result, Result::BAD_VALUE);
+ }
+ });
+}
+
+void SensorsHidlTest::queryDirectChannelSupport(SharedMemType memType, bool* supportsSharedMemType,
+ bool* supportsAnyDirectChannel) {
+ *supportsSharedMemType = false;
+ *supportsAnyDirectChannel = false;
+ for (const SensorInfoType& curSensor : getSensorsList()) {
+ if (isDirectChannelTypeSupported(curSensor, memType)) {
+ *supportsSharedMemType = true;
+ }
+ if (isDirectChannelTypeSupported(curSensor, SharedMemType::ASHMEM) ||
+ isDirectChannelTypeSupported(curSensor, SharedMemType::GRALLOC)) {
+ *supportsAnyDirectChannel = true;
+ }
+
+ if (*supportsSharedMemType && *supportsAnyDirectChannel) {
+ break;
+ }
+ }
+}
+
+void SensorsHidlTest::verifyRegisterDirectChannel(
+ std::shared_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem,
+ int32_t* directChannelHandle, bool supportsSharedMemType, bool supportsAnyDirectChannel) {
+ char* buffer = mem->getBuffer();
+ memset(buffer, 0xff, mem->getSize());
+
+ registerDirectChannel(mem->getSharedMemInfo(), [&](Result result, int32_t channelHandle) {
+ if (supportsSharedMemType) {
+ ASSERT_EQ(result, Result::OK);
+ ASSERT_GT(channelHandle, 0);
+
+ // Verify that the memory has been zeroed
+ for (size_t i = 0; i < mem->getSize(); i++) {
+ ASSERT_EQ(buffer[i], 0x00);
+ }
+ } else {
+ Result expectedResult =
+ supportsAnyDirectChannel ? Result::BAD_VALUE : Result::INVALID_OPERATION;
+ ASSERT_EQ(result, expectedResult);
+ ASSERT_EQ(channelHandle, -1);
+ }
+ *directChannelHandle = channelHandle;
+ });
+}
+
+void SensorsHidlTest::verifyConfigure(const SensorInfoType& sensor, SharedMemType memType,
+ int32_t directChannelHandle, bool supportsAnyDirectChannel) {
+ if (isDirectChannelTypeSupported(sensor, memType)) {
+ // Verify that each rate level is properly supported
+ checkRateLevel(sensor, directChannelHandle, RateLevel::NORMAL);
+ checkRateLevel(sensor, directChannelHandle, RateLevel::FAST);
+ checkRateLevel(sensor, directChannelHandle, RateLevel::VERY_FAST);
+ checkRateLevel(sensor, directChannelHandle, RateLevel::STOP);
+
+ // Verify that a sensor handle of -1 is only acceptable when using RateLevel::STOP
+ configDirectReport(-1 /* sensorHandle */, directChannelHandle, RateLevel::NORMAL,
+ [](Result result, int32_t /* reportToken */) {
+ ASSERT_EQ(result, Result::BAD_VALUE);
+ });
+ configDirectReport(
+ -1 /* sensorHandle */, directChannelHandle, RateLevel::STOP,
+ [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::OK); });
+ } else {
+ // directChannelHandle will be -1 here, HAL should either reject it as a bad value if there
+ // is some level of direct channel report, otherwise return INVALID_OPERATION if direct
+ // channel is not supported at all
+ Result expectedResult =
+ supportsAnyDirectChannel ? Result::BAD_VALUE : Result::INVALID_OPERATION;
+ configDirectReport(sensor.sensorHandle, directChannelHandle, RateLevel::NORMAL,
+ [expectedResult](Result result, int32_t /* reportToken */) {
+ ASSERT_EQ(result, expectedResult);
+ });
+ }
+}
+
+void SensorsHidlTest::verifyUnregisterDirectChannel(int32_t directChannelHandle,
+ bool supportsAnyDirectChannel) {
+ Result expectedResult = supportsAnyDirectChannel ? Result::OK : Result::INVALID_OPERATION;
+ ASSERT_EQ(unregisterDirectChannel(directChannelHandle), expectedResult);
+}
+
+void SensorsHidlTest::verifyDirectChannel(SharedMemType memType) {
+ constexpr size_t kNumEvents = 1;
+ constexpr size_t kMemSize = kNumEvents * kEventSize;
+
+ std::shared_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem(
+ SensorsTestSharedMemory<SensorTypeVersion, EventType>::create(memType, kMemSize));
+ ASSERT_NE(mem, nullptr);
+
+ bool supportsSharedMemType;
+ bool supportsAnyDirectChannel;
+ queryDirectChannelSupport(memType, &supportsSharedMemType, &supportsAnyDirectChannel);
+
+ for (const SensorInfoType& sensor : getSensorsList()) {
+ int32_t directChannelHandle = 0;
+ verifyRegisterDirectChannel(mem, &directChannelHandle, supportsSharedMemType,
+ supportsAnyDirectChannel);
+ verifyConfigure(sensor, memType, directChannelHandle, supportsAnyDirectChannel);
+ verifyUnregisterDirectChannel(directChannelHandle, supportsAnyDirectChannel);
+ }
+}
+
+TEST_P(SensorsHidlTest, DirectChannelAshmem) {
+ verifyDirectChannel(SharedMemType::ASHMEM);
+}
+
+TEST_P(SensorsHidlTest, DirectChannelGralloc) {
+ verifyDirectChannel(SharedMemType::GRALLOC);
+}
+
+bool SensorsHidlTest::getDirectChannelSensor(SensorInfoType* sensor, SharedMemType* memType,
+ RateLevel* rate) {
+ bool found = false;
+ for (const SensorInfoType& curSensor : getSensorsList()) {
+ if (isDirectChannelTypeSupported(curSensor, SharedMemType::ASHMEM)) {
+ *memType = SharedMemType::ASHMEM;
+ *sensor = curSensor;
+ found = true;
+ break;
+ } else if (isDirectChannelTypeSupported(curSensor, SharedMemType::GRALLOC)) {
+ *memType = SharedMemType::GRALLOC;
+ *sensor = curSensor;
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ // Find a supported rate level
+ constexpr int kNumRateLevels = 3;
+ RateLevel rates[kNumRateLevels] = {RateLevel::NORMAL, RateLevel::FAST,
+ RateLevel::VERY_FAST};
+ *rate = RateLevel::STOP;
+ for (int i = 0; i < kNumRateLevels; i++) {
+ if (isDirectReportRateSupported(*sensor, rates[i])) {
+ *rate = rates[i];
+ }
+ }
+
+ // At least one rate level must be supported
+ EXPECT_NE(*rate, RateLevel::STOP);
+ }
+ return found;
+}
+
+TEST_P(SensorsHidlTest, ConfigureDirectChannelWithInvalidHandle) {
+ SensorInfoType sensor;
+ SharedMemType memType;
+ RateLevel rate;
+ if (!getDirectChannelSensor(&sensor, &memType, &rate)) {
+ return;
+ }
+
+ // Verify that an invalid channel handle produces a BAD_VALUE result
+ configDirectReport(sensor.sensorHandle, -1, rate, [](Result result, int32_t /* reportToken */) {
+ ASSERT_EQ(result, Result::BAD_VALUE);
+ });
+}
+
+TEST_P(SensorsHidlTest, CleanupDirectConnectionOnInitialize) {
+ constexpr size_t kNumEvents = 1;
+ constexpr size_t kMemSize = kNumEvents * kEventSize;
+
+ SensorInfoType sensor;
+ SharedMemType memType;
+ RateLevel rate;
+
+ if (!getDirectChannelSensor(&sensor, &memType, &rate)) {
+ return;
+ }
+
+ std::shared_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem(
+ SensorsTestSharedMemory<SensorTypeVersion, EventType>::create(memType, kMemSize));
+ ASSERT_NE(mem, nullptr);
+
+ int32_t directChannelHandle = 0;
+ registerDirectChannel(mem->getSharedMemInfo(), [&](Result result, int32_t channelHandle) {
+ ASSERT_EQ(result, Result::OK);
+ directChannelHandle = channelHandle;
+ });
+
+ // Configure the channel and expect success
+ configDirectReport(
+ sensor.sensorHandle, directChannelHandle, rate,
+ [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::OK); });
+
+ // Call initialize() via the environment setup to cause the HAL to re-initialize
+ // Clear the active direct connections so they are not stopped during TearDown
+ auto handles = mDirectChannelHandles;
+ mDirectChannelHandles.clear();
+ getEnvironment()->HidlTearDown();
+ getEnvironment()->HidlSetUp();
+ if (HasFatalFailure()) {
+ return; // Exit early if resetting the environment failed
+ }
+
+ // Attempt to configure the direct channel and expect it to fail
+ configDirectReport(
+ sensor.sensorHandle, directChannelHandle, rate,
+ [](Result result, int32_t /* reportToken */) { ASSERT_EQ(result, Result::BAD_VALUE); });
+
+ // Restore original handles, though they should already be deactivated
+ mDirectChannelHandles = handles;
+}
diff --git a/sensors/common/vts/utils/Android.bp b/sensors/common/vts/utils/Android.bp
index bb4d329..ca4346a 100644
--- a/sensors/common/vts/utils/Android.bp
+++ b/sensors/common/vts/utils/Android.bp
@@ -20,9 +20,6 @@
cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
srcs: [
"GrallocWrapper.cpp",
- "SensorsHidlEnvironmentBase.cpp",
- "SensorsHidlTestBase.cpp",
- "SensorsTestSharedMemory.cpp",
],
export_include_dirs: [
"include",
@@ -30,6 +27,9 @@
local_include_dirs: [
"include/sensors-vts-utils",
],
+ shared_libs: [
+ "libutils",
+ ],
static_libs: [
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
@@ -37,5 +37,7 @@
"android.hardware.graphics.mapper@2.1",
"android.hardware.graphics.mapper@3.0",
"android.hardware.sensors@1.0",
+ "android.hardware.sensors@2.0",
+ "android.hardware.sensors@2.1",
],
}
diff --git a/sensors/common/vts/utils/SensorsHidlEnvironmentBase.cpp b/sensors/common/vts/utils/SensorsHidlEnvironmentBase.cpp
deleted file mode 100644
index fa0e2e9..0000000
--- a/sensors/common/vts/utils/SensorsHidlEnvironmentBase.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SensorsHidlEnvironmentBase.h"
-
-void SensorsHidlEnvironmentBase::HidlSetUp() {
- ASSERT_TRUE(resetHal()) << "could not get hidl service";
-
- mCollectionEnabled = false;
- startPollingThread();
-
- // In case framework just stopped for test and there is sensor events in the pipe,
- // wait some time for those events to be cleared to avoid them messing up the test.
- std::this_thread::sleep_for(std::chrono::seconds(3));
-}
-
-void SensorsHidlEnvironmentBase::HidlTearDown() {
- mStopThread = true;
- if (mPollThread.joinable()) {
- mPollThread.detach();
- }
-}
-
-void SensorsHidlEnvironmentBase::catEvents(std::vector<Event>* output) {
- std::lock_guard<std::mutex> lock(mEventsMutex);
- if (output) {
- output->insert(output->end(), mEvents.begin(), mEvents.end());
- }
- mEvents.clear();
-}
-
-void SensorsHidlEnvironmentBase::setCollection(bool enable) {
- std::lock_guard<std::mutex> lock(mEventsMutex);
- mCollectionEnabled = enable;
-}
-
-void SensorsHidlEnvironmentBase::addEvent(const Event& ev) {
- std::lock_guard<std::mutex> lock(mEventsMutex);
- if (mCollectionEnabled) {
- mEvents.push_back(ev);
- }
-
- if (mCallback != nullptr) {
- mCallback->onEvent(ev);
- }
-}
-
-void SensorsHidlEnvironmentBase::registerCallback(IEventCallback* callback) {
- std::lock_guard<std::mutex> lock(mEventsMutex);
- mCallback = callback;
-}
-
-void SensorsHidlEnvironmentBase::unregisterCallback() {
- std::lock_guard<std::mutex> lock(mEventsMutex);
- mCallback = nullptr;
-}
\ No newline at end of file
diff --git a/sensors/common/vts/utils/SensorsHidlTestBase.cpp b/sensors/common/vts/utils/SensorsHidlTestBase.cpp
deleted file mode 100644
index 18549df..0000000
--- a/sensors/common/vts/utils/SensorsHidlTestBase.cpp
+++ /dev/null
@@ -1,584 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SensorsHidlTestBase.h"
-
-#include "sensors-vts-utils/GrallocWrapper.h"
-#include "sensors-vts-utils/SensorsTestSharedMemory.h"
-
-#include <hardware/sensors.h> // for sensor type strings
-#include <log/log.h>
-#include <utils/SystemClock.h>
-
-#include <cinttypes>
-
-using ::android::sp;
-using ::android::hardware::hidl_string;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::sensors::V1_0::SensorFlagShift;
-using ::android::hardware::sensors::V1_0::SensorsEventFormatOffset;
-
-const Vec3NormChecker SensorsHidlTestBase::sAccelNormChecker(
- Vec3NormChecker::byNominal(GRAVITY_EARTH, 1.0f /*m/s^2*/));
-const Vec3NormChecker SensorsHidlTestBase::sGyroNormChecker(
- Vec3NormChecker::byNominal(0.f, 0.1f /*rad/s*/));
-
-std::vector<Event> SensorsHidlTestBase::collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
- bool clearBeforeStart,
- bool changeCollection) {
- return collectEvents(timeLimitUs, nEventLimit, getEnvironment(), clearBeforeStart,
- changeCollection);
-}
-
-std::vector<Event> SensorsHidlTestBase::collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
- SensorsHidlEnvironmentBase* environment,
- bool clearBeforeStart,
- bool changeCollection) {
- std::vector<Event> events;
- constexpr useconds_t SLEEP_GRANULARITY = 100 * 1000; // granularity 100 ms
-
- ALOGI("collect max of %zu events for %d us, clearBeforeStart %d", nEventLimit, timeLimitUs,
- clearBeforeStart);
-
- if (changeCollection) {
- environment->setCollection(true);
- }
- if (clearBeforeStart) {
- environment->catEvents(nullptr);
- }
-
- while (timeLimitUs > 0) {
- useconds_t duration = std::min(SLEEP_GRANULARITY, timeLimitUs);
- usleep(duration);
- timeLimitUs -= duration;
-
- environment->catEvents(&events);
- if (events.size() >= nEventLimit) {
- break;
- }
- ALOGV("time to go = %d, events to go = %d", (int)timeLimitUs,
- (int)(nEventLimit - events.size()));
- }
-
- if (changeCollection) {
- environment->setCollection(false);
- }
- return events;
-}
-
-void SensorsHidlTestBase::assertTypeMatchStringType(SensorType type,
- const hidl_string& stringType) {
- if (type >= SensorType::DEVICE_PRIVATE_BASE) {
- return;
- }
-
- switch (type) {
-#define CHECK_TYPE_STRING_FOR_SENSOR_TYPE(type) \
- case SensorType::type: \
- ASSERT_STREQ(SENSOR_STRING_TYPE_##type, stringType.c_str()); \
- break;
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ACCELEROMETER);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ACCELEROMETER_UNCALIBRATED);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ADDITIONAL_INFO);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(AMBIENT_TEMPERATURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(DEVICE_ORIENTATION);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(DYNAMIC_SENSOR_META);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GAME_ROTATION_VECTOR);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GEOMAGNETIC_ROTATION_VECTOR);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GLANCE_GESTURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GRAVITY);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GYROSCOPE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GYROSCOPE_UNCALIBRATED);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(HEART_BEAT);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(HEART_RATE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LIGHT);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LINEAR_ACCELERATION);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LOW_LATENCY_OFFBODY_DETECT);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MAGNETIC_FIELD);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MAGNETIC_FIELD_UNCALIBRATED);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MOTION_DETECT);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ORIENTATION);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PICK_UP_GESTURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(POSE_6DOF);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PRESSURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PROXIMITY);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(RELATIVE_HUMIDITY);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ROTATION_VECTOR);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(SIGNIFICANT_MOTION);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STATIONARY_DETECT);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STEP_COUNTER);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STEP_DETECTOR);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(TEMPERATURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(TILT_DETECTOR);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(WAKE_GESTURE);
- CHECK_TYPE_STRING_FOR_SENSOR_TYPE(WRIST_TILT_GESTURE);
- default:
- FAIL() << "Type " << static_cast<int>(type)
- << " in android defined range is not checked, "
- << "stringType = " << stringType;
-#undef CHECK_TYPE_STRING_FOR_SENSOR_TYPE
- }
-}
-
-void SensorsHidlTestBase::assertTypeMatchReportMode(SensorType type, SensorFlagBits reportMode) {
- if (type >= SensorType::DEVICE_PRIVATE_BASE) {
- return;
- }
-
- SensorFlagBits expected = expectedReportModeForType(type);
-
- ASSERT_TRUE(expected == (SensorFlagBits)-1 || expected == reportMode)
- << "reportMode=" << static_cast<int>(reportMode)
- << "expected=" << static_cast<int>(expected);
-}
-
-void SensorsHidlTestBase::assertDelayMatchReportMode(int32_t minDelay, int32_t maxDelay,
- SensorFlagBits reportMode) {
- switch (reportMode) {
- case SensorFlagBits::CONTINUOUS_MODE:
- ASSERT_LT(0, minDelay);
- ASSERT_LE(0, maxDelay);
- break;
- case SensorFlagBits::ON_CHANGE_MODE:
- ASSERT_LE(0, minDelay);
- ASSERT_LE(0, maxDelay);
- break;
- case SensorFlagBits::ONE_SHOT_MODE:
- ASSERT_EQ(-1, minDelay);
- ASSERT_EQ(0, maxDelay);
- break;
- case SensorFlagBits::SPECIAL_REPORTING_MODE:
- // do not enforce anything for special reporting mode
- break;
- default:
- FAIL() << "Report mode " << static_cast<int>(reportMode) << " not checked";
- }
-}
-
-// return -1 means no expectation for this type
-SensorFlagBits SensorsHidlTestBase::expectedReportModeForType(SensorType type) {
- switch (type) {
- case SensorType::ACCELEROMETER:
- case SensorType::ACCELEROMETER_UNCALIBRATED:
- case SensorType::GYROSCOPE:
- case SensorType::MAGNETIC_FIELD:
- case SensorType::ORIENTATION:
- case SensorType::PRESSURE:
- case SensorType::TEMPERATURE:
- case SensorType::GRAVITY:
- case SensorType::LINEAR_ACCELERATION:
- case SensorType::ROTATION_VECTOR:
- case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
- case SensorType::GAME_ROTATION_VECTOR:
- case SensorType::GYROSCOPE_UNCALIBRATED:
- case SensorType::GEOMAGNETIC_ROTATION_VECTOR:
- case SensorType::POSE_6DOF:
- case SensorType::HEART_BEAT:
- return SensorFlagBits::CONTINUOUS_MODE;
-
- case SensorType::LIGHT:
- case SensorType::PROXIMITY:
- case SensorType::RELATIVE_HUMIDITY:
- case SensorType::AMBIENT_TEMPERATURE:
- case SensorType::HEART_RATE:
- case SensorType::DEVICE_ORIENTATION:
- case SensorType::STEP_COUNTER:
- case SensorType::LOW_LATENCY_OFFBODY_DETECT:
- return SensorFlagBits::ON_CHANGE_MODE;
-
- case SensorType::SIGNIFICANT_MOTION:
- case SensorType::WAKE_GESTURE:
- case SensorType::GLANCE_GESTURE:
- case SensorType::PICK_UP_GESTURE:
- case SensorType::MOTION_DETECT:
- case SensorType::STATIONARY_DETECT:
- return SensorFlagBits::ONE_SHOT_MODE;
-
- case SensorType::STEP_DETECTOR:
- case SensorType::TILT_DETECTOR:
- case SensorType::WRIST_TILT_GESTURE:
- case SensorType::DYNAMIC_SENSOR_META:
- return SensorFlagBits::SPECIAL_REPORTING_MODE;
-
- default:
- ALOGW("Type %d is not implemented in expectedReportModeForType", (int)type);
- return (SensorFlagBits)-1;
- }
-}
-
-bool SensorsHidlTestBase::isDirectReportRateSupported(SensorInfo sensor, RateLevel rate) {
- unsigned int r = static_cast<unsigned int>(sensor.flags & SensorFlagBits::MASK_DIRECT_REPORT) >>
- static_cast<unsigned int>(SensorFlagShift::DIRECT_REPORT);
- return r >= static_cast<unsigned int>(rate);
-}
-
-bool SensorsHidlTestBase::isDirectChannelTypeSupported(SensorInfo sensor, SharedMemType type) {
- switch (type) {
- case SharedMemType::ASHMEM:
- return (sensor.flags & SensorFlagBits::DIRECT_CHANNEL_ASHMEM) != 0;
- case SharedMemType::GRALLOC:
- return (sensor.flags & SensorFlagBits::DIRECT_CHANNEL_GRALLOC) != 0;
- default:
- return false;
- }
-}
-
-void SensorsHidlTestBase::testDirectReportOperation(SensorType type, SharedMemType memType,
- RateLevel rate,
- const SensorEventsChecker& checker) {
- constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
- constexpr size_t kNEvent = 4096;
- constexpr size_t kMemSize = kEventSize * kNEvent;
-
- constexpr float kNormalNominal = 50;
- constexpr float kFastNominal = 200;
- constexpr float kVeryFastNominal = 800;
-
- constexpr float kNominalTestTimeSec = 1.f;
- constexpr float kMaxTestTimeSec = kNominalTestTimeSec + 0.5f; // 0.5 second for initialization
-
- SensorInfo sensor = defaultSensorByType(type);
-
- if (!isValidType(sensor.type)) {
- // no default sensor of this type
- return;
- }
-
- if (!isDirectReportRateSupported(sensor, rate)) {
- return;
- }
-
- if (!isDirectChannelTypeSupported(sensor, memType)) {
- return;
- }
-
- std::unique_ptr<SensorsTestSharedMemory> mem(
- SensorsTestSharedMemory::create(memType, kMemSize));
- ASSERT_NE(mem, nullptr);
-
- char* buffer = mem->getBuffer();
- // fill memory with data
- for (size_t i = 0; i < kMemSize; ++i) {
- buffer[i] = '\xcc';
- }
-
- int32_t channelHandle;
- registerDirectChannel(mem->getSharedMemInfo(),
- [&channelHandle](auto result, auto channelHandle_) {
- ASSERT_EQ(result, Result::OK);
- channelHandle = channelHandle_;
- });
-
- // check memory is zeroed
- for (size_t i = 0; i < kMemSize; ++i) {
- ASSERT_EQ(buffer[i], '\0');
- }
-
- int32_t eventToken;
- configDirectReport(sensor.sensorHandle, channelHandle, rate,
- [&eventToken](auto result, auto token) {
- ASSERT_EQ(result, Result::OK);
- eventToken = token;
- });
-
- usleep(static_cast<useconds_t>(kMaxTestTimeSec * 1e6f));
- auto events = mem->parseEvents();
-
- // find norminal rate
- float nominalFreq = 0.f;
- switch (rate) {
- case RateLevel::NORMAL:
- nominalFreq = kNormalNominal;
- break;
- case RateLevel::FAST:
- nominalFreq = kFastNominal;
- break;
- case RateLevel::VERY_FAST:
- nominalFreq = kVeryFastNominal;
- break;
- case RateLevel::STOP:
- FAIL();
- }
-
- // allowed to be between 55% and 220% of nominal freq
- ASSERT_GT(events.size(), static_cast<size_t>(nominalFreq * 0.55f * kNominalTestTimeSec));
- ASSERT_LT(events.size(), static_cast<size_t>(nominalFreq * 2.2f * kMaxTestTimeSec));
-
- int64_t lastTimestamp = 0;
- bool typeErrorReported = false;
- bool tokenErrorReported = false;
- bool timestampErrorReported = false;
- std::vector<Event> sensorEvents;
- for (auto& e : events) {
- if (!tokenErrorReported) {
- EXPECT_EQ(eventToken, e.sensorHandle)
- << (tokenErrorReported = true,
- "Event token does not match that retured from configDirectReport");
- }
-
- if (isMetaSensorType(e.sensorType)) {
- continue;
- }
- sensorEvents.push_back(e);
-
- if (!typeErrorReported) {
- EXPECT_EQ(type, e.sensorType)
- << (typeErrorReported = true,
- "Type in event does not match type of sensor registered.");
- }
- if (!timestampErrorReported) {
- EXPECT_GT(e.timestamp, lastTimestamp)
- << (timestampErrorReported = true, "Timestamp not monotonically increasing");
- }
- lastTimestamp = e.timestamp;
- }
-
- std::string s;
- EXPECT_TRUE(checker.check(sensorEvents, &s)) << s;
-
- // stop sensor and unregister channel
- configDirectReport(sensor.sensorHandle, channelHandle, RateLevel::STOP,
- [](auto result, auto) { EXPECT_EQ(result, Result::OK); });
- EXPECT_EQ(unregisterDirectChannel(channelHandle), Result::OK);
-}
-
-void SensorsHidlTestBase::testStreamingOperation(SensorType type,
- std::chrono::nanoseconds samplingPeriod,
- std::chrono::seconds duration,
- const SensorEventsChecker& checker) {
- std::vector<Event> events;
- std::vector<Event> sensorEvents;
-
- const int64_t samplingPeriodInNs = samplingPeriod.count();
- const int64_t batchingPeriodInNs = 0; // no batching
- const useconds_t minTimeUs = std::chrono::microseconds(duration).count();
- const size_t minNEvent = duration / samplingPeriod;
-
- SensorInfo sensor = defaultSensorByType(type);
-
- if (!isValidType(sensor.type)) {
- // no default sensor of this type
- return;
- }
-
- if (std::chrono::microseconds(sensor.minDelay) > samplingPeriod) {
- // rate not supported
- return;
- }
-
- int32_t handle = sensor.sensorHandle;
-
- ASSERT_EQ(batch(handle, samplingPeriodInNs, batchingPeriodInNs), Result::OK);
- ASSERT_EQ(activate(handle, 1), Result::OK);
- events = collectEvents(minTimeUs, minNEvent, true /*clearBeforeStart*/);
- ASSERT_EQ(activate(handle, 0), Result::OK);
-
- ALOGI("Collected %zu samples", events.size());
-
- ASSERT_GT(events.size(), 0u);
-
- bool handleMismatchReported = false;
- bool metaSensorTypeErrorReported = false;
- for (auto& e : events) {
- if (e.sensorType == type) {
- // avoid generating hundreds of error
- if (!handleMismatchReported) {
- EXPECT_EQ(e.sensorHandle, handle)
- << (handleMismatchReported = true,
- "Event of the same type must come from the sensor registered");
- }
- sensorEvents.push_back(e);
- } else {
- // avoid generating hundreds of error
- if (!metaSensorTypeErrorReported) {
- EXPECT_TRUE(isMetaSensorType(e.sensorType))
- << (metaSensorTypeErrorReported = true,
- "Only meta types are allowed besides the type registered");
- }
- }
- }
-
- std::string s;
- EXPECT_TRUE(checker.check(sensorEvents, &s)) << s;
-
- EXPECT_GE(sensorEvents.size(),
- minNEvent / 2); // make sure returned events are not all meta
-}
-
-void SensorsHidlTestBase::testSamplingRateHotSwitchOperation(SensorType type, bool fastToSlow) {
- std::vector<Event> events1, events2;
-
- constexpr int64_t batchingPeriodInNs = 0; // no batching
- constexpr int64_t collectionTimeoutUs = 60000000; // 60s
- constexpr size_t minNEvent = 50;
-
- SensorInfo sensor = defaultSensorByType(type);
-
- if (!isValidType(sensor.type)) {
- // no default sensor of this type
- return;
- }
-
- int32_t handle = sensor.sensorHandle;
- int64_t minSamplingPeriodInNs = sensor.minDelay * 1000ll;
- int64_t maxSamplingPeriodInNs = sensor.maxDelay * 1000ll;
-
- if (minSamplingPeriodInNs == maxSamplingPeriodInNs) {
- // only support single rate
- return;
- }
-
- int64_t firstCollectionPeriod = fastToSlow ? minSamplingPeriodInNs : maxSamplingPeriodInNs;
- int64_t secondCollectionPeriod = !fastToSlow ? minSamplingPeriodInNs : maxSamplingPeriodInNs;
-
- // first collection
- ASSERT_EQ(batch(handle, firstCollectionPeriod, batchingPeriodInNs), Result::OK);
- ASSERT_EQ(activate(handle, 1), Result::OK);
-
- usleep(500000); // sleep 0.5 sec to wait for change rate to happen
- events1 = collectEvents(collectionTimeoutUs, minNEvent);
-
- // second collection, without stop sensor
- ASSERT_EQ(batch(handle, secondCollectionPeriod, batchingPeriodInNs), Result::OK);
-
- usleep(500000); // sleep 0.5 sec to wait for change rate to happen
- events2 = collectEvents(collectionTimeoutUs, minNEvent);
-
- // end of collection, stop sensor
- ASSERT_EQ(activate(handle, 0), Result::OK);
-
- ALOGI("Collected %zu fast samples and %zu slow samples", events1.size(), events2.size());
-
- ASSERT_GT(events1.size(), 0u);
- ASSERT_GT(events2.size(), 0u);
-
- int64_t minDelayAverageInterval, maxDelayAverageInterval;
- std::vector<Event>& minDelayEvents(fastToSlow ? events1 : events2);
- std::vector<Event>& maxDelayEvents(fastToSlow ? events2 : events1);
-
- size_t nEvent = 0;
- int64_t prevTimestamp = -1;
- int64_t timestampInterval = 0;
- for (auto& e : minDelayEvents) {
- if (e.sensorType == type) {
- ASSERT_EQ(e.sensorHandle, handle);
- if (prevTimestamp > 0) {
- timestampInterval += e.timestamp - prevTimestamp;
- }
- prevTimestamp = e.timestamp;
- ++nEvent;
- }
- }
- ASSERT_GT(nEvent, 2u);
- minDelayAverageInterval = timestampInterval / (nEvent - 1);
-
- nEvent = 0;
- prevTimestamp = -1;
- timestampInterval = 0;
- for (auto& e : maxDelayEvents) {
- if (e.sensorType == type) {
- ASSERT_EQ(e.sensorHandle, handle);
- if (prevTimestamp > 0) {
- timestampInterval += e.timestamp - prevTimestamp;
- }
- prevTimestamp = e.timestamp;
- ++nEvent;
- }
- }
- ASSERT_GT(nEvent, 2u);
- maxDelayAverageInterval = timestampInterval / (nEvent - 1);
-
- // change of rate is significant.
- ALOGI("min/maxDelayAverageInterval = %" PRId64 " %" PRId64, minDelayAverageInterval,
- maxDelayAverageInterval);
- EXPECT_GT((maxDelayAverageInterval - minDelayAverageInterval), minDelayAverageInterval / 10);
-
- // fastest rate sampling time is close to spec
- EXPECT_LT(std::abs(minDelayAverageInterval - minSamplingPeriodInNs),
- minSamplingPeriodInNs / 10);
-
- // slowest rate sampling time is close to spec
- EXPECT_LT(std::abs(maxDelayAverageInterval - maxSamplingPeriodInNs),
- maxSamplingPeriodInNs / 10);
-}
-
-void SensorsHidlTestBase::testBatchingOperation(SensorType type) {
- std::vector<Event> events;
-
- constexpr int64_t maxBatchingTestTimeNs = 30ull * 1000 * 1000 * 1000;
- constexpr int64_t oneSecondInNs = 1ull * 1000 * 1000 * 1000;
-
- SensorInfo sensor = defaultSensorByType(type);
-
- if (!isValidType(sensor.type)) {
- // no default sensor of this type
- return;
- }
-
- int32_t handle = sensor.sensorHandle;
- int64_t minSamplingPeriodInNs = sensor.minDelay * 1000ll;
- uint32_t minFifoCount = sensor.fifoReservedEventCount;
- int64_t batchingPeriodInNs = minFifoCount * minSamplingPeriodInNs;
-
- if (batchingPeriodInNs < oneSecondInNs) {
- // batching size too small to test reliably
- return;
- }
-
- batchingPeriodInNs = std::min(batchingPeriodInNs, maxBatchingTestTimeNs);
-
- ALOGI("Test batching for %d ms", (int)(batchingPeriodInNs / 1000 / 1000));
-
- int64_t allowedBatchDeliverTimeNs = std::max(oneSecondInNs, batchingPeriodInNs / 10);
-
- ASSERT_EQ(batch(handle, minSamplingPeriodInNs, INT64_MAX), Result::OK);
- ASSERT_EQ(activate(handle, 1), Result::OK);
-
- usleep(500000); // sleep 0.5 sec to wait for initialization
- ASSERT_EQ(flush(handle), Result::OK);
-
- // wait for 80% of the reserved batching period
- // there should not be any significant amount of events
- // since collection is not enabled all events will go down the drain
- usleep(batchingPeriodInNs / 1000 * 8 / 10);
-
- getEnvironment()->setCollection(true);
- // clean existing collections
- collectEvents(0 /*timeLimitUs*/, 0 /*nEventLimit*/, true /*clearBeforeStart*/,
- false /*change collection*/);
-
- // 0.8 + 0.2 times the batching period
- usleep(batchingPeriodInNs / 1000 * 8 / 10);
- ASSERT_EQ(flush(handle), Result::OK);
-
- // plus some time for the event to deliver
- events = collectEvents(allowedBatchDeliverTimeNs / 1000, minFifoCount,
- false /*clearBeforeStart*/, false /*change collection*/);
-
- getEnvironment()->setCollection(false);
- ASSERT_EQ(activate(handle, 0), Result::OK);
-
- size_t nEvent = 0;
- for (auto& e : events) {
- if (e.sensorType == type && e.sensorHandle == handle) {
- ++nEvent;
- }
- }
-
- // at least reach 90% of advertised capacity
- ASSERT_GT(nEvent, (size_t)(minFifoCount * 9 / 10));
-}
diff --git a/sensors/common/vts/utils/SensorsTestSharedMemory.cpp b/sensors/common/vts/utils/SensorsTestSharedMemory.cpp
deleted file mode 100644
index 3b068bd..0000000
--- a/sensors/common/vts/utils/SensorsTestSharedMemory.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SensorsTestSharedMemory.h"
-
-#include <log/log.h>
-
-#include <sys/mman.h>
-#include <cinttypes>
-
-using namespace ::android::hardware::sensors::V1_0;
-
-SharedMemInfo SensorsTestSharedMemory::getSharedMemInfo() const {
- SharedMemInfo mem = {.type = mType,
- .format = SharedMemFormat::SENSORS_EVENT,
- .size = static_cast<uint32_t>(mSize),
- .memoryHandle = mNativeHandle};
- return mem;
-}
-
-char* SensorsTestSharedMemory::getBuffer() const {
- return mBuffer;
-}
-
-size_t SensorsTestSharedMemory::getSize() const {
- return mSize;
-}
-
-std::vector<Event> SensorsTestSharedMemory::parseEvents(int64_t lastCounter, size_t offset) const {
- constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
- constexpr size_t kOffsetSize = static_cast<size_t>(SensorsEventFormatOffset::SIZE_FIELD);
- constexpr size_t kOffsetToken = static_cast<size_t>(SensorsEventFormatOffset::REPORT_TOKEN);
- constexpr size_t kOffsetType = static_cast<size_t>(SensorsEventFormatOffset::SENSOR_TYPE);
- constexpr size_t kOffsetAtomicCounter =
- static_cast<size_t>(SensorsEventFormatOffset::ATOMIC_COUNTER);
- constexpr size_t kOffsetTimestamp = static_cast<size_t>(SensorsEventFormatOffset::TIMESTAMP);
- constexpr size_t kOffsetData = static_cast<size_t>(SensorsEventFormatOffset::DATA);
-
- std::vector<Event> events;
- std::vector<float> data(16);
-
- while (offset + kEventSize <= mSize) {
- int64_t atomicCounter =
- *reinterpret_cast<uint32_t*>(mBuffer + offset + kOffsetAtomicCounter);
- if (atomicCounter <= lastCounter) {
- ALOGV("atomicCounter = %" PRId64 ", lastCounter = %" PRId64, atomicCounter,
- lastCounter);
- break;
- }
-
- int32_t size = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetSize);
- if (size != kEventSize) {
- // unknown error, events parsed may be wrong, remove all
- events.clear();
- break;
- }
-
- int32_t token = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetToken);
- int32_t type = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetType);
- int64_t timestamp = *reinterpret_cast<int64_t*>(mBuffer + offset + kOffsetTimestamp);
-
- ALOGV("offset = %zu, cnt %" PRId64 ", token %" PRId32 ", type %" PRId32
- ", timestamp %" PRId64,
- offset, atomicCounter, token, type, timestamp);
-
- Event event = {
- .timestamp = timestamp,
- .sensorHandle = token,
- .sensorType = static_cast<SensorType>(type),
- };
- event.u.data = android::hardware::hidl_array<float, 16>(
- reinterpret_cast<float*>(mBuffer + offset + kOffsetData));
-
- events.push_back(event);
-
- lastCounter = atomicCounter;
- offset += kEventSize;
- }
-
- return events;
-}
-
-SensorsTestSharedMemory::SensorsTestSharedMemory(SharedMemType type, size_t size)
- : mType(type), mSize(0), mBuffer(nullptr) {
- native_handle_t* handle = nullptr;
- char* buffer = nullptr;
- switch (type) {
- case SharedMemType::ASHMEM: {
- int fd;
- handle = ::native_handle_create(1 /*nFds*/, 0 /*nInts*/);
- if (handle != nullptr) {
- handle->data[0] = fd = ::ashmem_create_region("SensorsTestSharedMemory", size);
- if (handle->data[0] > 0) {
- // memory is pinned by default
- buffer = static_cast<char*>(
- ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
- if (buffer != reinterpret_cast<char*>(MAP_FAILED)) {
- break;
- }
- ::native_handle_close(handle);
- }
- ::native_handle_delete(handle);
- handle = nullptr;
- }
- break;
- }
- case SharedMemType::GRALLOC: {
- mGrallocWrapper = std::make_unique<::android::GrallocWrapper>();
- if (!mGrallocWrapper->isInitialized()) {
- break;
- }
-
- std::pair<native_handle_t*, void*> buf = mGrallocWrapper->allocate(size);
- handle = buf.first;
- buffer = static_cast<char*>(buf.second);
- break;
- }
- default:
- break;
- }
-
- if (buffer != nullptr) {
- mNativeHandle = handle;
- mSize = size;
- mBuffer = buffer;
- }
-}
-
-SensorsTestSharedMemory::~SensorsTestSharedMemory() {
- switch (mType) {
- case SharedMemType::ASHMEM: {
- if (mSize != 0) {
- ::munmap(mBuffer, mSize);
- mBuffer = nullptr;
-
- ::native_handle_close(mNativeHandle);
- ::native_handle_delete(mNativeHandle);
-
- mNativeHandle = nullptr;
- mSize = 0;
- }
- break;
- }
- case SharedMemType::GRALLOC: {
- if (mSize != 0) {
- mGrallocWrapper->freeBuffer(mNativeHandle);
- mNativeHandle = nullptr;
- mSize = 0;
- }
- break;
- }
- default: {
- if (mNativeHandle != nullptr || mSize != 0 || mBuffer != nullptr) {
- ALOGE(
- "SensorsTestSharedMemory %p not properly destructed: "
- "type %d, native handle %p, size %zu, buffer %p",
- this, static_cast<int>(mType), mNativeHandle, mSize, mBuffer);
- }
- break;
- }
- }
-}
-
-SensorsTestSharedMemory* SensorsTestSharedMemory::create(SharedMemType type, size_t size) {
- constexpr size_t kMaxSize = 128 * 1024 * 1024; // sensor test should not need more than 128M
- if (size == 0 || size >= kMaxSize) {
- return nullptr;
- }
-
- auto m = new SensorsTestSharedMemory(type, size);
- if (m->mSize != size || m->mBuffer == nullptr) {
- delete m;
- m = nullptr;
- }
- return m;
-}
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorEventsChecker.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorEventsChecker.h
index b5daccc..d6d3227 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorEventsChecker.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorEventsChecker.h
@@ -17,26 +17,26 @@
#ifndef ANDROID_SENSOR_EVENTS_CHECKER_H
#define ANDROID_SENSOR_EVENTS_CHECKER_H
-#include <android/hardware/sensors/1.0/types.h>
-
#include <cmath>
+template <class EventType>
class SensorEventsChecker {
- public:
- using Event = ::android::hardware::sensors::V1_0::Event;
- virtual bool check(const std::vector<Event>& events, std::string* out) const = 0;
+ public:
+ virtual bool check(const std::vector<EventType>& events, std::string* out) const = 0;
virtual ~SensorEventsChecker() {}
};
-class NullChecker : public SensorEventsChecker {
- public:
- virtual bool check(const std::vector<Event>&, std::string*) const { return true; }
+template <class EventType>
+class NullChecker : public SensorEventsChecker<EventType> {
+ public:
+ virtual bool check(const std::vector<EventType>&, std::string*) const { return true; }
};
-class SensorEventPerEventChecker : public SensorEventsChecker {
- public:
- virtual bool checkEvent(const Event& event, std::string* out) const = 0;
- virtual bool check(const std::vector<Event>& events, std::string* out) const {
+template <class EventType>
+class SensorEventPerEventChecker : public SensorEventsChecker<EventType> {
+ public:
+ virtual bool checkEvent(const EventType& event, std::string* out) const = 0;
+ virtual bool check(const std::vector<EventType>& events, std::string* out) const {
for (const auto& e : events) {
if (!checkEvent(e, out)) {
return false;
@@ -46,14 +46,15 @@
}
};
-class Vec3NormChecker : public SensorEventPerEventChecker {
- public:
+template <class EventType>
+class Vec3NormChecker : public SensorEventPerEventChecker<EventType> {
+ public:
Vec3NormChecker(float min, float max) : mLowerLimit(min), mUpperLimit(max) {}
- static Vec3NormChecker byNominal(float nominal, float allowedError) {
- return Vec3NormChecker(nominal - allowedError, nominal + allowedError);
+ static Vec3NormChecker<EventType> byNominal(float nominal, float allowedError) {
+ return Vec3NormChecker<EventType>(nominal - allowedError, nominal + allowedError);
}
- virtual bool checkEvent(const Event& event, std::string* out) const {
+ virtual bool checkEvent(const EventType& event, std::string* out) const {
android::hardware::sensors::V1_0::Vec3 v = event.u.vec3;
float norm = std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
if (norm < mLowerLimit || norm > mUpperLimit) {
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h
index dbc9392..781427d 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h
@@ -17,7 +17,6 @@
#ifndef ANDROID_SENSORS_HIDL_ENVIRONMENT_BASE_H
#define ANDROID_SENSORS_HIDL_ENVIRONMENT_BASE_H
-#include <android/hardware/sensors/1.0/types.h>
#include <gtest/gtest.h>
#include <atomic>
@@ -26,27 +25,59 @@
#include <thread>
#include <vector>
+template <class Event>
class IEventCallback {
- public:
+ public:
virtual ~IEventCallback() = default;
- virtual void onEvent(const ::android::hardware::sensors::V1_0::Event& event) = 0;
+ virtual void onEvent(const Event& event) = 0;
};
+template <class Event>
class SensorsHidlEnvironmentBase {
public:
- using Event = ::android::hardware::sensors::V1_0::Event;
- virtual void HidlSetUp();
- virtual void HidlTearDown();
+ virtual void HidlSetUp() {
+ ASSERT_TRUE(resetHal()) << "could not get hidl service";
+
+ mCollectionEnabled = false;
+ startPollingThread();
+
+ // In case framework just stopped for test and there is sensor events in the pipe,
+ // wait some time for those events to be cleared to avoid them messing up the test.
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ }
+
+ virtual void HidlTearDown() {
+ mStopThread = true;
+ if (mPollThread.joinable()) {
+ mPollThread.join();
+ }
+ }
// Get and clear all events collected so far (like "cat" shell command).
// If output is nullptr, it clears all collected events.
- void catEvents(std::vector<Event>* output);
+ void catEvents(std::vector<Event>* output) {
+ std::lock_guard<std::mutex> lock(mEventsMutex);
+ if (output) {
+ output->insert(output->end(), mEvents.begin(), mEvents.end());
+ }
+ mEvents.clear();
+ }
// set sensor event collection status
- void setCollection(bool enable);
+ void setCollection(bool enable) {
+ std::lock_guard<std::mutex> lock(mEventsMutex);
+ mCollectionEnabled = enable;
+ }
- void registerCallback(IEventCallback* callback);
- void unregisterCallback();
+ void registerCallback(IEventCallback<Event>* callback) {
+ std::lock_guard<std::mutex> lock(mEventsMutex);
+ mCallback = callback;
+ }
+
+ void unregisterCallback() {
+ std::lock_guard<std::mutex> lock(mEventsMutex);
+ mCallback = nullptr;
+ }
protected:
SensorsHidlEnvironmentBase(const std::string& service_name)
@@ -55,7 +86,16 @@
}
virtual ~SensorsHidlEnvironmentBase(){};
- void addEvent(const Event& ev);
+ void addEvent(const Event& ev) {
+ std::lock_guard<std::mutex> lock(mEventsMutex);
+ if (mCollectionEnabled) {
+ mEvents.push_back(ev);
+ }
+
+ if (mCallback != nullptr) {
+ mCallback->onEvent(ev);
+ }
+ }
virtual void startPollingThread() = 0;
virtual bool resetHal() = 0;
@@ -67,9 +107,9 @@
std::vector<Event> mEvents;
std::mutex mEventsMutex;
- IEventCallback* mCallback;
+ IEventCallback<Event>* mCallback;
- GTEST_DISALLOW_COPY_AND_ASSIGN_(SensorsHidlEnvironmentBase);
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(SensorsHidlEnvironmentBase<Event>);
};
#endif // ANDROID_SENSORS_HIDL_ENVIRONMENT_BASE_H
\ No newline at end of file
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
index 54e899b..03bec87 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
@@ -19,11 +19,15 @@
#include "sensors-vts-utils/SensorEventsChecker.h"
#include "sensors-vts-utils/SensorsHidlEnvironmentBase.h"
+#include "sensors-vts-utils/SensorsTestSharedMemory.h"
#include <android/hardware/sensors/1.0/ISensors.h>
#include <android/hardware/sensors/1.0/types.h>
#include <gtest/gtest.h>
+#include <hardware/sensors.h>
+#include <log/log.h>
+#include <cinttypes>
#include <unordered_set>
#include <vector>
@@ -34,19 +38,130 @@
using ::android::sp;
using ::android::hardware::hidl_string;
-using ::android::hardware::sensors::V1_0::Event;
-using ::android::hardware::sensors::V1_0::ISensors;
using ::android::hardware::sensors::V1_0::RateLevel;
using ::android::hardware::sensors::V1_0::Result;
using ::android::hardware::sensors::V1_0::SensorFlagBits;
-using ::android::hardware::sensors::V1_0::SensorInfo;
-using ::android::hardware::sensors::V1_0::SensorType;
+using ::android::hardware::sensors::V1_0::SensorFlagShift;
+using ::android::hardware::sensors::V1_0::SensorsEventFormatOffset;
using ::android::hardware::sensors::V1_0::SharedMemInfo;
using ::android::hardware::sensors::V1_0::SharedMemType;
+template <class SensorTypeT>
+static void assertTypeMatchStringType(SensorTypeT type, const hidl_string& stringType) {
+ if (type >= SensorTypeT::DEVICE_PRIVATE_BASE) {
+ return;
+ }
+
+ switch (type) {
+#define CHECK_TYPE_STRING_FOR_SENSOR_TYPE(type) \
+ case SensorTypeT::type: \
+ ASSERT_STREQ(SENSOR_STRING_TYPE_##type, stringType.c_str()); \
+ break;
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ACCELEROMETER);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ACCELEROMETER_UNCALIBRATED);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ADDITIONAL_INFO);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(AMBIENT_TEMPERATURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(DEVICE_ORIENTATION);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(DYNAMIC_SENSOR_META);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GAME_ROTATION_VECTOR);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GEOMAGNETIC_ROTATION_VECTOR);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GLANCE_GESTURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GRAVITY);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GYROSCOPE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(GYROSCOPE_UNCALIBRATED);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(HEART_BEAT);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(HEART_RATE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LIGHT);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LINEAR_ACCELERATION);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(LOW_LATENCY_OFFBODY_DETECT);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MAGNETIC_FIELD);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MAGNETIC_FIELD_UNCALIBRATED);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(MOTION_DETECT);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ORIENTATION);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PICK_UP_GESTURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(POSE_6DOF);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PRESSURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(PROXIMITY);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(RELATIVE_HUMIDITY);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(ROTATION_VECTOR);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(SIGNIFICANT_MOTION);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STATIONARY_DETECT);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STEP_COUNTER);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(STEP_DETECTOR);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(TEMPERATURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(TILT_DETECTOR);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(WAKE_GESTURE);
+ CHECK_TYPE_STRING_FOR_SENSOR_TYPE(WRIST_TILT_GESTURE);
+ default:
+ FAIL() << "Type " << static_cast<int>(type)
+ << " in android defined range is not checked, "
+ << "stringType = " << stringType;
+#undef CHECK_TYPE_STRING_FOR_SENSOR_TYPE
+ }
+}
+
+template <class SensorTypeT>
+static SensorFlagBits expectedReportModeForType(SensorTypeT type) {
+ switch (type) {
+ case SensorTypeT::ACCELEROMETER:
+ case SensorTypeT::ACCELEROMETER_UNCALIBRATED:
+ case SensorTypeT::GYROSCOPE:
+ case SensorTypeT::MAGNETIC_FIELD:
+ case SensorTypeT::ORIENTATION:
+ case SensorTypeT::PRESSURE:
+ case SensorTypeT::TEMPERATURE:
+ case SensorTypeT::GRAVITY:
+ case SensorTypeT::LINEAR_ACCELERATION:
+ case SensorTypeT::ROTATION_VECTOR:
+ case SensorTypeT::MAGNETIC_FIELD_UNCALIBRATED:
+ case SensorTypeT::GAME_ROTATION_VECTOR:
+ case SensorTypeT::GYROSCOPE_UNCALIBRATED:
+ case SensorTypeT::GEOMAGNETIC_ROTATION_VECTOR:
+ case SensorTypeT::POSE_6DOF:
+ case SensorTypeT::HEART_BEAT:
+ return SensorFlagBits::CONTINUOUS_MODE;
+
+ case SensorTypeT::LIGHT:
+ case SensorTypeT::PROXIMITY:
+ case SensorTypeT::RELATIVE_HUMIDITY:
+ case SensorTypeT::AMBIENT_TEMPERATURE:
+ case SensorTypeT::HEART_RATE:
+ case SensorTypeT::DEVICE_ORIENTATION:
+ case SensorTypeT::STEP_COUNTER:
+ case SensorTypeT::LOW_LATENCY_OFFBODY_DETECT:
+ return SensorFlagBits::ON_CHANGE_MODE;
+
+ case SensorTypeT::SIGNIFICANT_MOTION:
+ case SensorTypeT::WAKE_GESTURE:
+ case SensorTypeT::GLANCE_GESTURE:
+ case SensorTypeT::PICK_UP_GESTURE:
+ case SensorTypeT::MOTION_DETECT:
+ case SensorTypeT::STATIONARY_DETECT:
+ return SensorFlagBits::ONE_SHOT_MODE;
+
+ case SensorTypeT::STEP_DETECTOR:
+ case SensorTypeT::TILT_DETECTOR:
+ case SensorTypeT::WRIST_TILT_GESTURE:
+ case SensorTypeT::DYNAMIC_SENSOR_META:
+ return SensorFlagBits::SPECIAL_REPORTING_MODE;
+
+ default:
+ ALOGW("Type %d is not implemented in expectedReportModeForType", (int)type);
+ return (SensorFlagBits)-1;
+ }
+}
+
+template <class SensorTypeVersion, class EventType, class SensorInfoType>
class SensorsHidlTestBase : public testing::TestWithParam<std::string> {
public:
- virtual SensorsHidlEnvironmentBase* getEnvironment() = 0;
+ using ISensors = ::android::hardware::sensors::V1_0::ISensors;
+
+ SensorsHidlTestBase()
+ : mAccelNormChecker(Vec3NormChecker<EventType>::byNominal(GRAVITY_EARTH, 1.0f /*m/s^2*/)),
+ mGyroNormChecker(Vec3NormChecker<EventType>::byNominal(0.f, 0.1f /*rad/s*/)) {}
+
+ virtual SensorsHidlEnvironmentBase<EventType>* getEnvironment() = 0;
+
virtual void SetUp() override {}
virtual void TearDown() override {
@@ -66,16 +181,13 @@
}
// implementation wrapper
- virtual SensorInfo defaultSensorByType(SensorType type) = 0;
+ virtual SensorInfoType defaultSensorByType(SensorTypeVersion type) = 0;
virtual Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) = 0;
-
+ virtual Return<Result> injectSensorData(const EventType& event) = 0;
virtual Return<Result> activate(int32_t sensorHandle, bool enabled) = 0;
-
virtual Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
int64_t maxReportLatencyNs) = 0;
-
virtual Return<Result> flush(int32_t sensorHandle) = 0;
- virtual Return<Result> injectSensorData(const Event& event) = 0;
virtual Return<void> registerDirectChannel(const SharedMemInfo& mem,
ISensors::registerDirectChannel_cb _hidl_cb) = 0;
virtual Return<Result> unregisterDirectChannel(int32_t channelHandle) = 0;
@@ -83,12 +195,395 @@
RateLevel rate,
ISensors::configDirectReport_cb _hidl_cb) = 0;
- std::vector<Event> collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
- bool clearBeforeStart = true, bool changeCollection = true);
- static std::vector<Event> collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
- SensorsHidlEnvironmentBase* environment,
- bool clearBeforeStart = true,
- bool changeCollection = true);
+ std::vector<EventType> collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
+ bool clearBeforeStart = true,
+ bool changeCollection = true) {
+ return collectEvents(timeLimitUs, nEventLimit, getEnvironment(), clearBeforeStart,
+ changeCollection);
+ }
+
+ std::vector<EventType> collectEvents(useconds_t timeLimitUs, size_t nEventLimit,
+ SensorsHidlEnvironmentBase<EventType>* environment,
+ bool clearBeforeStart = true,
+ bool changeCollection = true) {
+ std::vector<EventType> events;
+ constexpr useconds_t SLEEP_GRANULARITY = 100 * 1000; // granularity 100 ms
+
+ ALOGI("collect max of %zu events for %d us, clearBeforeStart %d", nEventLimit, timeLimitUs,
+ clearBeforeStart);
+
+ if (changeCollection) {
+ environment->setCollection(true);
+ }
+ if (clearBeforeStart) {
+ environment->catEvents(nullptr);
+ }
+
+ while (timeLimitUs > 0) {
+ useconds_t duration = std::min(SLEEP_GRANULARITY, timeLimitUs);
+ usleep(duration);
+ timeLimitUs -= duration;
+
+ environment->catEvents(&events);
+ if (events.size() >= nEventLimit) {
+ break;
+ }
+ ALOGV("time to go = %d, events to go = %d", (int)timeLimitUs,
+ (int)(nEventLimit - events.size()));
+ }
+
+ if (changeCollection) {
+ environment->setCollection(false);
+ }
+ return events;
+ }
+
+ void testStreamingOperation(SensorTypeVersion type, std::chrono::nanoseconds samplingPeriod,
+ std::chrono::seconds duration,
+ const SensorEventsChecker<EventType>& checker) {
+ std::vector<EventType> events;
+ std::vector<EventType> sensorEvents;
+
+ const int64_t samplingPeriodInNs = samplingPeriod.count();
+ const int64_t batchingPeriodInNs = 0; // no batching
+ const useconds_t minTimeUs = std::chrono::microseconds(duration).count();
+ const size_t minNEvent = duration / samplingPeriod;
+
+ SensorInfoType sensor = defaultSensorByType(type);
+
+ if (!isValidType(sensor.type)) {
+ // no default sensor of this type
+ return;
+ }
+
+ if (std::chrono::microseconds(sensor.minDelay) > samplingPeriod) {
+ // rate not supported
+ return;
+ }
+
+ int32_t handle = sensor.sensorHandle;
+
+ ASSERT_EQ(batch(handle, samplingPeriodInNs, batchingPeriodInNs), Result::OK);
+ ASSERT_EQ(activate(handle, 1), Result::OK);
+ events = collectEvents(minTimeUs, minNEvent, getEnvironment(), true /*clearBeforeStart*/);
+ ASSERT_EQ(activate(handle, 0), Result::OK);
+
+ ALOGI("Collected %zu samples", events.size());
+
+ ASSERT_GT(events.size(), 0u);
+
+ bool handleMismatchReported = false;
+ bool metaSensorTypeErrorReported = false;
+ for (auto& e : events) {
+ if (e.sensorType == type) {
+ // avoid generating hundreds of error
+ if (!handleMismatchReported) {
+ EXPECT_EQ(e.sensorHandle, handle)
+ << (handleMismatchReported = true,
+ "Event of the same type must come from the sensor registered");
+ }
+ sensorEvents.push_back(e);
+ } else {
+ // avoid generating hundreds of error
+ if (!metaSensorTypeErrorReported) {
+ EXPECT_TRUE(isMetaSensorType(e.sensorType))
+ << (metaSensorTypeErrorReported = true,
+ "Only meta types are allowed besides the type registered");
+ }
+ }
+ }
+
+ std::string s;
+ EXPECT_TRUE(checker.check(sensorEvents, &s)) << s;
+
+ EXPECT_GE(sensorEvents.size(),
+ minNEvent / 2); // make sure returned events are not all meta
+ }
+
+ void testSamplingRateHotSwitchOperation(SensorTypeVersion type, bool fastToSlow = true) {
+ std::vector<EventType> events1, events2;
+
+ constexpr int64_t batchingPeriodInNs = 0; // no batching
+ constexpr int64_t collectionTimeoutUs = 60000000; // 60s
+ constexpr size_t minNEvent = 50;
+
+ SensorInfoType sensor = defaultSensorByType(type);
+
+ if (!isValidType(sensor.type)) {
+ // no default sensor of this type
+ return;
+ }
+
+ int32_t handle = sensor.sensorHandle;
+ int64_t minSamplingPeriodInNs = sensor.minDelay * 1000ll;
+ int64_t maxSamplingPeriodInNs = sensor.maxDelay * 1000ll;
+
+ if (minSamplingPeriodInNs == maxSamplingPeriodInNs) {
+ // only support single rate
+ return;
+ }
+
+ int64_t firstCollectionPeriod = fastToSlow ? minSamplingPeriodInNs : maxSamplingPeriodInNs;
+ int64_t secondCollectionPeriod =
+ !fastToSlow ? minSamplingPeriodInNs : maxSamplingPeriodInNs;
+
+ // first collection
+ ASSERT_EQ(batch(handle, firstCollectionPeriod, batchingPeriodInNs), Result::OK);
+ ASSERT_EQ(activate(handle, 1), Result::OK);
+
+ usleep(500000); // sleep 0.5 sec to wait for change rate to happen
+ events1 = collectEvents(collectionTimeoutUs, minNEvent, getEnvironment());
+
+ // second collection, without stop sensor
+ ASSERT_EQ(batch(handle, secondCollectionPeriod, batchingPeriodInNs), Result::OK);
+
+ usleep(500000); // sleep 0.5 sec to wait for change rate to happen
+ events2 = collectEvents(collectionTimeoutUs, minNEvent, getEnvironment());
+
+ // end of collection, stop sensor
+ ASSERT_EQ(activate(handle, 0), Result::OK);
+
+ ALOGI("Collected %zu fast samples and %zu slow samples", events1.size(), events2.size());
+
+ ASSERT_GT(events1.size(), 0u);
+ ASSERT_GT(events2.size(), 0u);
+
+ int64_t minDelayAverageInterval, maxDelayAverageInterval;
+ std::vector<EventType>& minDelayEvents(fastToSlow ? events1 : events2);
+ std::vector<EventType>& maxDelayEvents(fastToSlow ? events2 : events1);
+
+ size_t nEvent = 0;
+ int64_t prevTimestamp = -1;
+ int64_t timestampInterval = 0;
+ for (auto& e : minDelayEvents) {
+ if (e.sensorType == type) {
+ ASSERT_EQ(e.sensorHandle, handle);
+ if (prevTimestamp > 0) {
+ timestampInterval += e.timestamp - prevTimestamp;
+ }
+ prevTimestamp = e.timestamp;
+ ++nEvent;
+ }
+ }
+ ASSERT_GT(nEvent, 2u);
+ minDelayAverageInterval = timestampInterval / (nEvent - 1);
+
+ nEvent = 0;
+ prevTimestamp = -1;
+ timestampInterval = 0;
+ for (auto& e : maxDelayEvents) {
+ if (e.sensorType == type) {
+ ASSERT_EQ(e.sensorHandle, handle);
+ if (prevTimestamp > 0) {
+ timestampInterval += e.timestamp - prevTimestamp;
+ }
+ prevTimestamp = e.timestamp;
+ ++nEvent;
+ }
+ }
+ ASSERT_GT(nEvent, 2u);
+ maxDelayAverageInterval = timestampInterval / (nEvent - 1);
+
+ // change of rate is significant.
+ ALOGI("min/maxDelayAverageInterval = %" PRId64 " %" PRId64, minDelayAverageInterval,
+ maxDelayAverageInterval);
+ EXPECT_GT((maxDelayAverageInterval - minDelayAverageInterval),
+ minDelayAverageInterval / 10);
+
+ // fastest rate sampling time is close to spec
+ EXPECT_LT(std::abs(minDelayAverageInterval - minSamplingPeriodInNs),
+ minSamplingPeriodInNs / 10);
+
+ // slowest rate sampling time is close to spec
+ EXPECT_LT(std::abs(maxDelayAverageInterval - maxSamplingPeriodInNs),
+ maxSamplingPeriodInNs / 10);
+ }
+
+ void testBatchingOperation(SensorTypeVersion type) {
+ std::vector<EventType> events;
+
+ constexpr int64_t maxBatchingTestTimeNs = 30ull * 1000 * 1000 * 1000;
+ constexpr int64_t oneSecondInNs = 1ull * 1000 * 1000 * 1000;
+
+ SensorInfoType sensor = defaultSensorByType(type);
+
+ if (!isValidType(sensor.type)) {
+ // no default sensor of this type
+ return;
+ }
+
+ int32_t handle = sensor.sensorHandle;
+ int64_t minSamplingPeriodInNs = sensor.minDelay * 1000ll;
+ uint32_t minFifoCount = sensor.fifoReservedEventCount;
+ int64_t batchingPeriodInNs = minFifoCount * minSamplingPeriodInNs;
+
+ if (batchingPeriodInNs < oneSecondInNs) {
+ // batching size too small to test reliably
+ return;
+ }
+
+ batchingPeriodInNs = std::min(batchingPeriodInNs, maxBatchingTestTimeNs);
+
+ ALOGI("Test batching for %d ms", (int)(batchingPeriodInNs / 1000 / 1000));
+
+ int64_t allowedBatchDeliverTimeNs = std::max(oneSecondInNs, batchingPeriodInNs / 10);
+
+ ASSERT_EQ(batch(handle, minSamplingPeriodInNs, INT64_MAX), Result::OK);
+ ASSERT_EQ(activate(handle, 1), Result::OK);
+
+ usleep(500000); // sleep 0.5 sec to wait for initialization
+ ASSERT_EQ(flush(handle), Result::OK);
+
+ // wait for 80% of the reserved batching period
+ // there should not be any significant amount of events
+ // since collection is not enabled all events will go down the drain
+ usleep(batchingPeriodInNs / 1000 * 8 / 10);
+
+ getEnvironment()->setCollection(true);
+ // clean existing collections
+ collectEvents(0 /*timeLimitUs*/, 0 /*nEventLimit*/, true /*clearBeforeStart*/,
+ false /*change collection*/);
+
+ // 0.8 + 0.2 times the batching period
+ usleep(batchingPeriodInNs / 1000 * 8 / 10);
+ ASSERT_EQ(flush(handle), Result::OK);
+
+ // plus some time for the event to deliver
+ events = collectEvents(allowedBatchDeliverTimeNs / 1000, minFifoCount,
+ false /*clearBeforeStart*/, false /*change collection*/);
+
+ getEnvironment()->setCollection(false);
+ ASSERT_EQ(activate(handle, 0), Result::OK);
+
+ size_t nEvent = 0;
+ for (auto& e : events) {
+ if (e.sensorType == type && e.sensorHandle == handle) {
+ ++nEvent;
+ }
+ }
+
+ // at least reach 90% of advertised capacity
+ ASSERT_GT(nEvent, (size_t)(minFifoCount * 9 / 10));
+ }
+
+ void testDirectReportOperation(SensorTypeVersion type, SharedMemType memType, RateLevel rate,
+ const SensorEventsChecker<EventType>& checker) {
+ constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
+ constexpr size_t kNEvent = 4096;
+ constexpr size_t kMemSize = kEventSize * kNEvent;
+
+ constexpr float kNormalNominal = 50;
+ constexpr float kFastNominal = 200;
+ constexpr float kVeryFastNominal = 800;
+
+ constexpr float kNominalTestTimeSec = 1.f;
+ constexpr float kMaxTestTimeSec =
+ kNominalTestTimeSec + 0.5f; // 0.5 second for initialization
+
+ SensorInfoType sensor = defaultSensorByType(type);
+
+ if (!isValidType(sensor.type)) {
+ // no default sensor of this type
+ return;
+ }
+
+ if (!isDirectReportRateSupported(sensor, rate)) {
+ return;
+ }
+
+ if (!isDirectChannelTypeSupported(sensor, memType)) {
+ return;
+ }
+
+ std::unique_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem(
+ SensorsTestSharedMemory<SensorTypeVersion, EventType>::create(memType, kMemSize));
+ ASSERT_NE(mem, nullptr);
+
+ char* buffer = mem->getBuffer();
+ // fill memory with data
+ for (size_t i = 0; i < kMemSize; ++i) {
+ buffer[i] = '\xcc';
+ }
+
+ int32_t channelHandle;
+ registerDirectChannel(mem->getSharedMemInfo(),
+ [&channelHandle](auto result, auto channelHandle_) {
+ ASSERT_EQ(result, Result::OK);
+ channelHandle = channelHandle_;
+ });
+
+ // check memory is zeroed
+ for (size_t i = 0; i < kMemSize; ++i) {
+ ASSERT_EQ(buffer[i], '\0');
+ }
+
+ int32_t eventToken;
+ configDirectReport(sensor.sensorHandle, channelHandle, rate,
+ [&eventToken](auto result, auto token) {
+ ASSERT_EQ(result, Result::OK);
+ eventToken = token;
+ });
+
+ usleep(static_cast<useconds_t>(kMaxTestTimeSec * 1e6f));
+ auto events = mem->parseEvents();
+
+ // find norminal rate
+ float nominalFreq = 0.f;
+ switch (rate) {
+ case RateLevel::NORMAL:
+ nominalFreq = kNormalNominal;
+ break;
+ case RateLevel::FAST:
+ nominalFreq = kFastNominal;
+ break;
+ case RateLevel::VERY_FAST:
+ nominalFreq = kVeryFastNominal;
+ break;
+ case RateLevel::STOP:
+ FAIL();
+ }
+
+ // allowed to be between 55% and 220% of nominal freq
+ ASSERT_GT(events.size(), static_cast<size_t>(nominalFreq * 0.55f * kNominalTestTimeSec));
+ ASSERT_LT(events.size(), static_cast<size_t>(nominalFreq * 2.2f * kMaxTestTimeSec));
+
+ int64_t lastTimestamp = 0;
+ bool typeErrorReported = false;
+ bool tokenErrorReported = false;
+ bool timestampErrorReported = false;
+ std::vector<EventType> sensorEvents;
+ for (auto& e : events) {
+ if (!tokenErrorReported) {
+ EXPECT_EQ(eventToken, e.sensorHandle)
+ << (tokenErrorReported = true,
+ "Event token does not match that retured from configDirectReport");
+ }
+
+ if (isMetaSensorType(e.sensorType)) {
+ continue;
+ }
+ sensorEvents.push_back(e);
+
+ if (!typeErrorReported) {
+ EXPECT_EQ(type, e.sensorType)
+ << (typeErrorReported = true,
+ "Type in event does not match type of sensor registered.");
+ }
+ if (!timestampErrorReported) {
+ EXPECT_GT(e.timestamp, lastTimestamp) << (timestampErrorReported = true,
+ "Timestamp not monotonically increasing");
+ }
+ lastTimestamp = e.timestamp;
+ }
+
+ std::string s;
+ EXPECT_TRUE(checker.check(sensorEvents, &s)) << s;
+
+ // stop sensor and unregister channel
+ configDirectReport(sensor.sensorHandle, channelHandle, RateLevel::STOP,
+ [](auto result, auto) { EXPECT_EQ(result, Result::OK); });
+ EXPECT_EQ(unregisterDirectChannel(channelHandle), Result::OK);
+ }
inline static SensorFlagBits extractReportMode(uint64_t flag) {
return (SensorFlagBits)(flag & ((uint64_t)SensorFlagBits::CONTINUOUS_MODE |
@@ -97,32 +592,71 @@
(uint64_t)SensorFlagBits::SPECIAL_REPORTING_MODE));
}
- inline static bool isMetaSensorType(SensorType type) {
- return (type == SensorType::META_DATA || type == SensorType::DYNAMIC_SENSOR_META ||
- type == SensorType::ADDITIONAL_INFO);
+ inline static bool isMetaSensorType(SensorTypeVersion type) {
+ return (type == SensorTypeVersion::META_DATA ||
+ type == SensorTypeVersion::DYNAMIC_SENSOR_META ||
+ type == SensorTypeVersion::ADDITIONAL_INFO);
}
- inline static bool isValidType(SensorType type) { return (int32_t)type > 0; }
+ inline static bool isValidType(SensorTypeVersion type) { return (int32_t)type > 0; }
- void testStreamingOperation(SensorType type, std::chrono::nanoseconds samplingPeriod,
- std::chrono::seconds duration, const SensorEventsChecker& checker);
- void testSamplingRateHotSwitchOperation(SensorType type, bool fastToSlow = true);
- void testBatchingOperation(SensorType type);
- void testDirectReportOperation(SensorType type, SharedMemType memType, RateLevel rate,
- const SensorEventsChecker& checker);
-
- static void assertTypeMatchStringType(SensorType type, const hidl_string& stringType);
- static void assertTypeMatchReportMode(SensorType type, SensorFlagBits reportMode);
static void assertDelayMatchReportMode(int32_t minDelay, int32_t maxDelay,
- SensorFlagBits reportMode);
- static SensorFlagBits expectedReportModeForType(SensorType type);
- static bool isDirectReportRateSupported(SensorInfo sensor, RateLevel rate);
- static bool isDirectChannelTypeSupported(SensorInfo sensor, SharedMemType type);
+ SensorFlagBits reportMode) {
+ switch (reportMode) {
+ case SensorFlagBits::CONTINUOUS_MODE:
+ ASSERT_LT(0, minDelay);
+ ASSERT_LE(0, maxDelay);
+ break;
+ case SensorFlagBits::ON_CHANGE_MODE:
+ ASSERT_LE(0, minDelay);
+ ASSERT_LE(0, maxDelay);
+ break;
+ case SensorFlagBits::ONE_SHOT_MODE:
+ ASSERT_EQ(-1, minDelay);
+ ASSERT_EQ(0, maxDelay);
+ break;
+ case SensorFlagBits::SPECIAL_REPORTING_MODE:
+ // do not enforce anything for special reporting mode
+ break;
+ default:
+ FAIL() << "Report mode " << static_cast<int>(reportMode) << " not checked";
+ }
+ }
- protected:
- // checkers
- static const Vec3NormChecker sAccelNormChecker;
- static const Vec3NormChecker sGyroNormChecker;
+ protected:
+ static void assertTypeMatchReportMode(SensorTypeVersion type, SensorFlagBits reportMode) {
+ if (type >= SensorTypeVersion::DEVICE_PRIVATE_BASE) {
+ return;
+ }
+
+ SensorFlagBits expected = expectedReportModeForType(type);
+
+ ASSERT_TRUE(expected == (SensorFlagBits)-1 || expected == reportMode)
+ << "reportMode=" << static_cast<int>(reportMode)
+ << "expected=" << static_cast<int>(expected);
+ }
+
+ static bool isDirectReportRateSupported(SensorInfoType sensor, RateLevel rate) {
+ unsigned int r =
+ static_cast<unsigned int>(sensor.flags & SensorFlagBits::MASK_DIRECT_REPORT) >>
+ static_cast<unsigned int>(SensorFlagShift::DIRECT_REPORT);
+ return r >= static_cast<unsigned int>(rate);
+ }
+
+ static bool isDirectChannelTypeSupported(SensorInfoType sensor, SharedMemType type) {
+ switch (type) {
+ case SharedMemType::ASHMEM:
+ return (sensor.flags & SensorFlagBits::DIRECT_CHANNEL_ASHMEM) != 0;
+ case SharedMemType::GRALLOC:
+ return (sensor.flags & SensorFlagBits::DIRECT_CHANNEL_GRALLOC) != 0;
+ default:
+ return false;
+ }
+ }
+
+ // Checkers
+ Vec3NormChecker<EventType> mAccelNormChecker;
+ Vec3NormChecker<EventType> mGyroNormChecker;
// all sensors and direct channnels used
std::unordered_set<int32_t> mSensorHandles;
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h
index 002f42c..39084a4 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsTestSharedMemory.h
@@ -20,25 +20,177 @@
#include "GrallocWrapper.h"
#include <android-base/macros.h>
-#include <android/hardware/sensors/1.0/types.h>
+#include <log/log.h>
+
+#include <sys/mman.h>
+#include <cinttypes>
#include <cutils/ashmem.h>
+using namespace ::android::hardware::sensors::V1_0;
+
+template <class SensorTypeVersion, class EventType>
class SensorsTestSharedMemory {
- using SharedMemType = ::android::hardware::sensors::V1_0::SharedMemType;
- using SharedMemInfo = ::android::hardware::sensors::V1_0::SharedMemInfo;
- using Event = ::android::hardware::sensors::V1_0::Event;
+ public:
+ static SensorsTestSharedMemory* create(SharedMemType type, size_t size) {
+ constexpr size_t kMaxSize =
+ 128 * 1024 * 1024; // sensor test should not need more than 128M
+ if (size == 0 || size >= kMaxSize) {
+ return nullptr;
+ }
- public:
- static SensorsTestSharedMemory* create(SharedMemType type, size_t size);
- SharedMemInfo getSharedMemInfo() const;
- char* getBuffer() const;
- size_t getSize() const;
- std::vector<Event> parseEvents(int64_t lastCounter = -1, size_t offset = 0) const;
- virtual ~SensorsTestSharedMemory();
+ auto m = new SensorsTestSharedMemory<SensorTypeVersion, EventType>(type, size);
+ if (m->mSize != size || m->mBuffer == nullptr) {
+ delete m;
+ m = nullptr;
+ }
+ return m;
+ }
- private:
- SensorsTestSharedMemory(SharedMemType type, size_t size);
+ SharedMemInfo getSharedMemInfo() const {
+ SharedMemInfo mem = {.type = mType,
+ .format = SharedMemFormat::SENSORS_EVENT,
+ .size = static_cast<uint32_t>(mSize),
+ .memoryHandle = mNativeHandle};
+ return mem;
+ }
+ char* getBuffer() const { return mBuffer; }
+ size_t getSize() const { return mSize; }
+ std::vector<EventType> parseEvents(int64_t lastCounter = -1, size_t offset = 0) const {
+ constexpr size_t kEventSize = static_cast<size_t>(SensorsEventFormatOffset::TOTAL_LENGTH);
+ constexpr size_t kOffsetSize = static_cast<size_t>(SensorsEventFormatOffset::SIZE_FIELD);
+ constexpr size_t kOffsetToken = static_cast<size_t>(SensorsEventFormatOffset::REPORT_TOKEN);
+ constexpr size_t kOffsetType = static_cast<size_t>(SensorsEventFormatOffset::SENSOR_TYPE);
+ constexpr size_t kOffsetAtomicCounter =
+ static_cast<size_t>(SensorsEventFormatOffset::ATOMIC_COUNTER);
+ constexpr size_t kOffsetTimestamp =
+ static_cast<size_t>(SensorsEventFormatOffset::TIMESTAMP);
+ constexpr size_t kOffsetData = static_cast<size_t>(SensorsEventFormatOffset::DATA);
+
+ std::vector<EventType> events;
+ std::vector<float> data(16);
+
+ while (offset + kEventSize <= mSize) {
+ int64_t atomicCounter =
+ *reinterpret_cast<uint32_t*>(mBuffer + offset + kOffsetAtomicCounter);
+ if (atomicCounter <= lastCounter) {
+ ALOGV("atomicCounter = %" PRId64 ", lastCounter = %" PRId64, atomicCounter,
+ lastCounter);
+ break;
+ }
+
+ int32_t size = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetSize);
+ if (size != kEventSize) {
+ // unknown error, events parsed may be wrong, remove all
+ events.clear();
+ break;
+ }
+
+ int32_t token = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetToken);
+ int32_t type = *reinterpret_cast<int32_t*>(mBuffer + offset + kOffsetType);
+ int64_t timestamp = *reinterpret_cast<int64_t*>(mBuffer + offset + kOffsetTimestamp);
+
+ ALOGV("offset = %zu, cnt %" PRId64 ", token %" PRId32 ", type %" PRId32
+ ", timestamp %" PRId64,
+ offset, atomicCounter, token, type, timestamp);
+
+ EventType event = {
+ .timestamp = timestamp,
+ .sensorHandle = token,
+ .sensorType = static_cast<SensorTypeVersion>(type),
+ };
+ event.u.data = android::hardware::hidl_array<float, 16>(
+ reinterpret_cast<float*>(mBuffer + offset + kOffsetData));
+
+ events.push_back(event);
+
+ lastCounter = atomicCounter;
+ offset += kEventSize;
+ }
+
+ return events;
+ }
+
+ virtual ~SensorsTestSharedMemory() {
+ switch (mType) {
+ case SharedMemType::ASHMEM: {
+ if (mSize != 0) {
+ ::munmap(mBuffer, mSize);
+ mBuffer = nullptr;
+
+ ::native_handle_close(mNativeHandle);
+ ::native_handle_delete(mNativeHandle);
+
+ mNativeHandle = nullptr;
+ mSize = 0;
+ }
+ break;
+ }
+ case SharedMemType::GRALLOC: {
+ if (mSize != 0) {
+ mGrallocWrapper->freeBuffer(mNativeHandle);
+ mNativeHandle = nullptr;
+ mSize = 0;
+ }
+ break;
+ }
+ default: {
+ if (mNativeHandle != nullptr || mSize != 0 || mBuffer != nullptr) {
+ ALOGE("SensorsTestSharedMemory %p not properly destructed: "
+ "type %d, native handle %p, size %zu, buffer %p",
+ this, static_cast<int>(mType), mNativeHandle, mSize, mBuffer);
+ }
+ break;
+ }
+ }
+ }
+
+ private:
+ SensorsTestSharedMemory(SharedMemType type, size_t size)
+ : mType(type), mSize(0), mBuffer(nullptr) {
+ native_handle_t* handle = nullptr;
+ char* buffer = nullptr;
+ switch (type) {
+ case SharedMemType::ASHMEM: {
+ int fd;
+ handle = ::native_handle_create(1 /*nFds*/, 0 /*nInts*/);
+ if (handle != nullptr) {
+ handle->data[0] = fd = ::ashmem_create_region("SensorsTestSharedMemory", size);
+ if (handle->data[0] > 0) {
+ // memory is pinned by default
+ buffer = static_cast<char*>(
+ ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
+ if (buffer != reinterpret_cast<char*>(MAP_FAILED)) {
+ break;
+ }
+ ::native_handle_close(handle);
+ }
+ ::native_handle_delete(handle);
+ handle = nullptr;
+ }
+ break;
+ }
+ case SharedMemType::GRALLOC: {
+ mGrallocWrapper = std::make_unique<::android::GrallocWrapper>();
+ if (!mGrallocWrapper->isInitialized()) {
+ break;
+ }
+
+ std::pair<native_handle_t*, void*> buf = mGrallocWrapper->allocate(size);
+ handle = buf.first;
+ buffer = static_cast<char*>(buf.second);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (buffer != nullptr) {
+ mNativeHandle = handle;
+ mSize = size;
+ mBuffer = buffer;
+ }
+ }
SharedMemType mType;
native_handle_t* mNativeHandle;
diff --git a/soundtrigger/2.3/ISoundTriggerHw.hal b/soundtrigger/2.3/ISoundTriggerHw.hal
index 270b00e..3e761e5 100644
--- a/soundtrigger/2.3/ISoundTriggerHw.hal
+++ b/soundtrigger/2.3/ISoundTriggerHw.hal
@@ -114,8 +114,10 @@
* @return status Operation completion status: 0 in case of success
* -ENODEV if the native service cannot be reached
* -EINVAL invalid input parameter
- * @return retval ModelParameter structure indicating supported attributes
- * of the parameter for the given model handle
+ * @return retval OptionalModelParameterRange safe union structure wrapping
+ * ModelParameterRange. This structure indicates supported attributes
+ * of the parameter for the given model handle. If the parameter is not
+ * supported the Monostate of the union is used.
*/
queryParameter(SoundModelHandle modelHandle, ModelParameter modelParam)
generates (int32_t status, OptionalModelParameterRange retval);
diff --git a/soundtrigger/2.3/default/SoundTriggerHw.cpp b/soundtrigger/2.3/default/SoundTriggerHw.cpp
index d3136b9..8fe3108 100644
--- a/soundtrigger/2.3/default/SoundTriggerHw.cpp
+++ b/soundtrigger/2.3/default/SoundTriggerHw.cpp
@@ -889,7 +889,7 @@
int32_t status = mHwDevice->query_parameter(
mHwDevice, client->getHalHandle(), convertModelParameterToHal(modelParam), ¶mRange);
- if (status == 0) {
+ if (status == 0 && paramRange.is_supported) {
optionalParamRange.range({.start = paramRange.start, .end = paramRange.end});
}
_hidl_cb(status, optionalParamRange);
diff --git a/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp b/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp
index 6e55664..96656f3 100644
--- a/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp
+++ b/wifi/1.2/vts/functional/wifi_nan_iface_hidl_test.cpp
@@ -439,7 +439,7 @@
// synchronization objects
std::mutex mtx_;
std::condition_variable cv_;
- int count_;
+ int count_ = 0;
protected:
android::sp<::android::hardware::wifi::V1_2::IWifiNanIface> iwifiNanIface;
diff --git a/wifi/1.4/default/wifi_legacy_hal.cpp b/wifi/1.4/default/wifi_legacy_hal.cpp
index a040c89..f596195 100644
--- a/wifi/1.4/default/wifi_legacy_hal.cpp
+++ b/wifi/1.4/default/wifi_legacy_hal.cpp
@@ -831,6 +831,16 @@
global_handle_, mode, completion_window);
}
+wifi_error WifiLegacyHal::setDscpToAccessCategoryMapping(
+ uint32_t start, uint32_t end, uint32_t access_category) {
+ return global_func_table_.wifi_map_dscp_access_category(
+ global_handle_, start, end, access_category);
+}
+
+wifi_error WifiLegacyHal::resetDscpToAccessCategoryMapping() {
+ return global_func_table_.wifi_reset_dscp_mapping(global_handle_);
+}
+
std::pair<wifi_error, uint32_t> WifiLegacyHal::getLoggerSupportedFeatureSet(
const std::string& iface_name) {
uint32_t supported_feature_flags;
diff --git a/wifi/1.4/default/wifi_legacy_hal.h b/wifi/1.4/default/wifi_legacy_hal.h
index 72cf197..c21563e 100644
--- a/wifi/1.4/default/wifi_legacy_hal.h
+++ b/wifi/1.4/default/wifi_legacy_hal.h
@@ -261,6 +261,9 @@
wifi_latency_mode mode);
wifi_error setThermalMitigationMode(wifi_thermal_mode mode,
uint32_t completion_window);
+ wifi_error setDscpToAccessCategoryMapping(uint32_t start, uint32_t end,
+ uint32_t access_category);
+ wifi_error resetDscpToAccessCategoryMapping();
// Logger/debug functions.
std::pair<wifi_error, uint32_t> getLoggerSupportedFeatureSet(
const std::string& iface_name);
diff --git a/wifi/1.4/default/wifi_legacy_hal_stubs.cpp b/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
index 6945b4c..153a685 100644
--- a/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
@@ -141,6 +141,8 @@
populateStubFor(&hal_fn->wifi_set_thermal_mitigation_mode);
populateStubFor(&hal_fn->wifi_virtual_interface_create);
populateStubFor(&hal_fn->wifi_virtual_interface_delete);
+ populateStubFor(&hal_fn->wifi_map_dscp_access_category);
+ populateStubFor(&hal_fn->wifi_reset_dscp_mapping);
return true;
}
} // namespace legacy_hal
diff --git a/wifi/1.4/vts/functional/Android.bp b/wifi/1.4/vts/functional/Android.bp
index 46ac3ee..d857be1 100644
--- a/wifi/1.4/vts/functional/Android.bp
+++ b/wifi/1.4/vts/functional/Android.bp
@@ -22,7 +22,8 @@
"VtsHalWifiV1_4TargetTest.cpp",
"wifi_ap_iface_hidl_test.cpp",
"wifi_chip_hidl_test.cpp",
- "wifi_nan_iface_hidl_test.cpp"
+ "wifi_nan_iface_hidl_test.cpp",
+ "wifi_rtt_controller_hidl_test.cpp",
],
static_libs: [
"VtsHalWifiV1_0TargetTestUtil",
@@ -31,7 +32,10 @@
"android.hardware.wifi@1.2",
"android.hardware.wifi@1.3",
"android.hardware.wifi@1.4",
- "libwifi-system-iface"
+ "libwifi-system-iface",
],
- test_suites: ["general-tests", "vts-core"],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
}
diff --git a/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
index 7896067..1c39550 100644
--- a/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
+++ b/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
@@ -19,6 +19,7 @@
#undef NAN // NAN is defined in bionic/libc/include/math.h:38
+#include <android/hardware/wifi/1.3/IWifiStaIface.h>
#include <android/hardware/wifi/1.4/IWifi.h>
#include <android/hardware/wifi/1.4/IWifiChip.h>
#include <android/hardware/wifi/1.4/IWifiChipEventCallback.h>
@@ -34,10 +35,12 @@
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
+using ::android::hardware::wifi::V1_0::ChipModeId;
using ::android::hardware::wifi::V1_0::IfaceType;
using ::android::hardware::wifi::V1_0::WifiDebugRingBufferStatus;
using ::android::hardware::wifi::V1_0::WifiStatus;
using ::android::hardware::wifi::V1_0::WifiStatusCode;
+using ::android::hardware::wifi::V1_3::IWifiStaIface;
using ::android::hardware::wifi::V1_4::IWifiChip;
using ::android::hardware::wifi::V1_4::IWifiChipEventCallback;
@@ -110,6 +113,16 @@
};
protected:
+ // Helper function to configure the Chip in one of the supported modes.
+ // Most of the non-mode-configuration-related methods require chip
+ // to be first configured.
+ ChipModeId configureChipForIfaceType(IfaceType type, bool expectSuccess) {
+ ChipModeId mode_id;
+ EXPECT_EQ(expectSuccess,
+ configureChipToSupportIfaceType(wifi_chip_, type, &mode_id));
+ return mode_id;
+ }
+
sp<IWifiChip> wifi_chip_;
private:
@@ -136,3 +149,31 @@
return;
}
}
+
+/*
+ * createRttController_1_4
+ * Ensures that an instance of the IWifiRttController proxy object is
+ * successfully created.
+ */
+TEST_P(WifiChipHidlTest, createRttController_1_4) {
+ configureChipForIfaceType(IfaceType::STA, true);
+
+ const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createStaIface);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface.first.code);
+ sp<IWifiStaIface> iface = IWifiStaIface::castFrom(status_and_iface.second);
+ EXPECT_NE(nullptr, iface.get());
+
+ const auto& status_and_controller =
+ HIDL_INVOKE(wifi_chip_, createRttController_1_4, iface);
+ if (status_and_controller.first.code !=
+ WifiStatusCode::ERROR_NOT_SUPPORTED) {
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_controller.first.code);
+ EXPECT_NE(nullptr, status_and_controller.second.get());
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, WifiChipHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ ::android::hardware::wifi::V1_4::IWifi::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/wifi/1.4/vts/functional/wifi_nan_iface_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_nan_iface_hidl_test.cpp
index 688faf1..24daee6 100644
--- a/wifi/1.4/vts/functional/wifi_nan_iface_hidl_test.cpp
+++ b/wifi/1.4/vts/functional/wifi_nan_iface_hidl_test.cpp
@@ -440,7 +440,7 @@
// synchronization objects
std::mutex mtx_;
std::condition_variable cv_;
- int count_;
+ int count_ = 0;
protected:
android::sp<::android::hardware::wifi::V1_4::IWifiNanIface> iwifiNanIface;
@@ -545,3 +545,9 @@
nanConfigRequest, nanConfigRequestSupp)
.code);
}
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, WifiNanIfaceHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ ::android::hardware::wifi::V1_4::IWifi::descriptor)),
+ android::hardware::PrintInstanceNameToString);
diff --git a/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp
new file mode 100644
index 0000000..726470c
--- /dev/null
+++ b/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/logging.h>
+
+#undef NAN // NAN is defined in bionic/libc/include/math.h:38
+
+#include <android/hardware/wifi/1.3/IWifiStaIface.h>
+#include <android/hardware/wifi/1.4/IWifi.h>
+#include <android/hardware/wifi/1.4/IWifiChip.h>
+#include <android/hardware/wifi/1.4/IWifiRttController.h>
+#include <android/hardware/wifi/1.4/IWifiRttControllerEventCallback.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include "wifi_hidl_call_util.h"
+#include "wifi_hidl_test_utils.h"
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::wifi::V1_0::CommandId;
+using ::android::hardware::wifi::V1_0::RttBw;
+using ::android::hardware::wifi::V1_0::RttPeerType;
+using ::android::hardware::wifi::V1_0::RttType;
+using ::android::hardware::wifi::V1_0::WifiChannelInfo;
+using ::android::hardware::wifi::V1_0::WifiChannelWidthInMhz;
+using ::android::hardware::wifi::V1_0::WifiStatus;
+using ::android::hardware::wifi::V1_0::WifiStatusCode;
+using ::android::hardware::wifi::V1_3::IWifiStaIface;
+using ::android::hardware::wifi::V1_4::IWifiChip;
+using ::android::hardware::wifi::V1_4::IWifiRttController;
+using ::android::hardware::wifi::V1_4::IWifiRttControllerEventCallback;
+using ::android::hardware::wifi::V1_4::RttCapabilities;
+using ::android::hardware::wifi::V1_4::RttConfig;
+using ::android::hardware::wifi::V1_4::RttPreamble;
+using ::android::hardware::wifi::V1_4::RttResponder;
+using ::android::hardware::wifi::V1_4::RttResult;
+
+/**
+ * Fixture to use for all RTT controller HIDL interface tests.
+ */
+class WifiRttControllerHidlTest : public ::testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ // Make sure to start with a clean state
+ stopWifi(GetInstanceName());
+
+ wifi_rtt_controller_ = getWifiRttController();
+ ASSERT_NE(nullptr, wifi_rtt_controller_.get());
+ }
+
+ virtual void TearDown() override { stopWifi(GetInstanceName()); }
+
+ // A simple test implementation of WifiChipEventCallback.
+ class WifiRttControllerEventCallback
+ : public ::testing::VtsHalHidlTargetCallbackBase<
+ WifiRttControllerHidlTest>,
+ public IWifiRttControllerEventCallback {
+ public:
+ WifiRttControllerEventCallback(){};
+
+ virtual ~WifiRttControllerEventCallback() = default;
+
+ Return<void> onResults(
+ CommandId cmdId __unused,
+ const hidl_vec<::android::hardware::wifi::V1_0::RttResult>& results
+ __unused) {
+ return Void();
+ };
+
+ Return<void> onResults_1_4(CommandId cmdId __unused,
+ const hidl_vec<RttResult>& results
+ __unused) {
+ return Void();
+ };
+ };
+
+ protected:
+ sp<IWifiRttController> wifi_rtt_controller_;
+
+ private:
+ std::string GetInstanceName() { return GetParam(); }
+
+ sp<IWifiRttController> getWifiRttController() {
+ const std::string& instance_name = GetInstanceName();
+
+ sp<IWifiChip> wifi_chip =
+ IWifiChip::castFrom(getWifiChip(instance_name));
+ EXPECT_NE(nullptr, wifi_chip.get());
+
+ sp<IWifiStaIface> wifi_sta_iface =
+ IWifiStaIface::castFrom(getWifiStaIface(instance_name));
+ EXPECT_NE(nullptr, wifi_sta_iface.get());
+
+ const auto& status_and_controller =
+ HIDL_INVOKE(wifi_chip, createRttController_1_4, wifi_sta_iface);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_controller.first.code);
+ EXPECT_NE(nullptr, status_and_controller.second.get());
+
+ return status_and_controller.second.get();
+ }
+};
+
+/*
+ * registerEventCallback_1_4
+ * This test case tests the registerEventCallback_1_4() API which registers
+ * a call back function with the hal implementation
+ *
+ * Note: it is not feasible to test the invocation of the call back function
+ * since event is triggered internally in the HAL implementation, and can not be
+ * triggered from the test case
+ */
+TEST_P(WifiRttControllerHidlTest, RegisterEventCallback_1_4) {
+ sp<WifiRttControllerEventCallback> wifiRttControllerEventCallback =
+ new WifiRttControllerEventCallback();
+ const auto& status =
+ HIDL_INVOKE(wifi_rtt_controller_, registerEventCallback_1_4,
+ wifiRttControllerEventCallback);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+
+/*
+ * rangeRequest_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, RangeRequest_1_4) {
+ std::vector<RttConfig> configs;
+ RttConfig config;
+ int cmdId = 55;
+ // Set the config with test data
+ for (int i = 0; i < 6; i++) {
+ config.addr[i] = i;
+ }
+ config.type = RttType::ONE_SIDED;
+ config.peer = RttPeerType::STA;
+ config.channel.width = WifiChannelWidthInMhz::WIDTH_80;
+ config.channel.centerFreq = 5765;
+ config.channel.centerFreq0 = 5775;
+ config.channel.centerFreq1 = 0;
+ config.bw = RttBw::BW_80MHZ;
+ config.preamble = RttPreamble::HE;
+ config.mustRequestLci = false;
+ config.mustRequestLcr = false;
+ config.burstPeriod = 0;
+ config.numBurst = 0;
+ config.numFramesPerBurst = 8;
+ config.numRetriesPerRttFrame = 3;
+ config.numRetriesPerFtmr = 3;
+ config.burstDuration = 9;
+ // Insert config in the vector
+ configs.push_back(config);
+
+ // Invoke the call
+ const auto& status =
+ HIDL_INVOKE(wifi_rtt_controller_, rangeRequest_1_4, cmdId, configs);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+
+/*
+ * getCapabilities_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, GetCapabilities_1_4) {
+ std::pair<WifiStatus, RttCapabilities> status_and_caps;
+
+ // Invoke the call
+ status_and_caps = HIDL_INVOKE(wifi_rtt_controller_, getCapabilities_1_4);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_caps.first.code);
+}
+
+/*
+ * getResponderInfo_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, GetResponderInfo_1_4) {
+ std::pair<WifiStatus, RttResponder> status_and_info;
+
+ // Invoke the call
+ status_and_info = HIDL_INVOKE(wifi_rtt_controller_, getResponderInfo_1_4);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_info.first.code);
+}
+
+/*
+ * enableResponder_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, EnableResponder_1_4) {
+ std::pair<WifiStatus, RttResponder> status_and_info;
+ int cmdId = 55;
+ WifiChannelInfo channelInfo;
+ channelInfo.width = WifiChannelWidthInMhz::WIDTH_80;
+ channelInfo.centerFreq = 5690;
+ channelInfo.centerFreq0 = 5690;
+ channelInfo.centerFreq1 = 0;
+
+ // Get the responder first
+ status_and_info = HIDL_INVOKE(wifi_rtt_controller_, getResponderInfo_1_4);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_info.first.code);
+
+ // Invoke the call
+ const auto& status =
+ HIDL_INVOKE(wifi_rtt_controller_, enableResponder_1_4, cmdId,
+ channelInfo, 10, status_and_info.second);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, WifiRttControllerHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ ::android::hardware::wifi::V1_4::IWifi::descriptor)),
+ android::hardware::PrintInstanceNameToString);