Merge "Use NativeHandle in MQDescriptor instead of ParcelFileDescriptor"
diff --git a/audio/7.0/IDevice.hal b/audio/7.0/IDevice.hal
index d9e0ad2..e423f29 100644
--- a/audio/7.0/IDevice.hal
+++ b/audio/7.0/IDevice.hal
@@ -174,6 +174,9 @@
* Creates an audio patch between several source and sink ports. The handle
* is allocated by the HAL and must be unique for this audio HAL module.
*
+ * Optional method. HAL must support it if 'supportsAudioPatches' returns
+ * 'true'.
+ *
* @param sources patch sources.
* @param sinks patch sinks.
* @return retval operation completion status.
@@ -189,6 +192,9 @@
* as the HAL module can figure out a way of switching the route without
* causing audio disruption.
*
+ * Optional method. HAL must support it if 'supportsAudioPatches' returns
+ * 'true'.
+ *
* @param previousPatch handle of the previous patch to update.
* @param sources new patch sources.
* @param sinks new patch sinks.
@@ -204,6 +210,9 @@
/**
* Release an audio patch.
*
+ * Optional method. HAL must support it if 'supportsAudioPatches' returns
+ * 'true'.
+ *
* @param patch patch handle.
* @return retval operation completion status.
*/
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index 05c1066..7caed44 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -325,11 +325,17 @@
const hidl_vec<AudioPortConfig>& sinks) {
Result retval(Result::NOT_SUPPORTED);
if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
- std::unique_ptr<audio_port_config[]> halSources;
- HidlUtils::audioPortConfigsToHal(sources, &halSources);
- std::unique_ptr<audio_port_config[]> halSinks;
- HidlUtils::audioPortConfigsToHal(sinks, &halSinks);
audio_patch_handle_t halPatch = static_cast<audio_patch_handle_t>(patch);
+ std::unique_ptr<audio_port_config[]> halSources;
+ if (status_t status = HidlUtils::audioPortConfigsToHal(sources, &halSources);
+ status != NO_ERROR) {
+ return {analyzeStatus("audioPortConfigsToHal;sources", status), patch};
+ }
+ std::unique_ptr<audio_port_config[]> halSinks;
+ if (status_t status = HidlUtils::audioPortConfigsToHal(sinks, &halSinks);
+ status != NO_ERROR) {
+ return {analyzeStatus("audioPortConfigsToHal;sinks", status), patch};
+ }
retval = analyzeStatus("create_audio_patch",
mDevice->create_audio_patch(mDevice, sources.size(), &halSources[0],
sinks.size(), &halSinks[0], &halPatch));
@@ -364,7 +370,10 @@
Return<Result> Device::setAudioPortConfig(const AudioPortConfig& config) {
if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
struct audio_port_config halPortConfig;
- HidlUtils::audioPortConfigToHal(config, &halPortConfig);
+ if (status_t status = HidlUtils::audioPortConfigToHal(config, &halPortConfig);
+ status != NO_ERROR) {
+ return analyzeStatus("audioPortConfigToHal", status);
+ }
return analyzeStatus("set_audio_port_config",
mDevice->set_audio_port_config(mDevice, &halPortConfig));
}
diff --git a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
index 0f0cdcf..0ebe4c2 100644
--- a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
@@ -128,7 +128,7 @@
INSTANTIATE_TEST_CASE_P(SingleConfigOutputStream, SingleConfigOutputStreamTest,
::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
&DeviceConfigParameterToString);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleConfigOutputStream);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleConfigOutputStreamTest);
class SingleConfigInputStreamTest : public InputStreamTest {};
TEST_P(SingleConfigInputStreamTest, CloseDeviceWithOpenedInputStreams) {
@@ -142,7 +142,7 @@
INSTANTIATE_TEST_CASE_P(SingleConfigInputStream, SingleConfigInputStreamTest,
::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
&DeviceConfigParameterToString);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleConfigInputStream);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleConfigInputStreamTest);
TEST_P(AudioPatchHidlTest, UpdatePatchInvalidHandle) {
doc::test("Verify that passing an invalid handle to updateAudioPatch is checked");
diff --git a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
index 0f52a90..7fca610 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -296,7 +296,19 @@
InputBufferSizeInvalidConfig, InvalidInputConfigNoFlagsTest,
::testing::ValuesIn(getInputDeviceInvalidConfigParameters(false /*generateInvalidFlags*/)),
&DeviceConfigParameterToString);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputBufferSizeInvalidConfig);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InvalidInputConfigNoFlagsTest);
+
+static const DeviceAddress& getValidInputDeviceAddress() {
+ static const DeviceAddress valid = {
+ .deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT)};
+ return valid;
+}
+
+static const DeviceAddress& getValidOutputDeviceAddress() {
+ static const DeviceAddress valid = {
+ .deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT)};
+ return valid;
+}
static const DeviceAddress& getInvalidDeviceAddress() {
static const DeviceAddress valid = {.deviceType = "random_string"};
@@ -311,6 +323,141 @@
getDevice()->setConnectedState(getInvalidDeviceAddress(), false));
}
+static std::vector<AudioPortConfig>& generatePortConfigs(bool valid) {
+ enum { // Note: This is for convenience when deriving "invalid" configs from "valid".
+ PORT_CONF_MINIMAL,
+ PORT_CONF_WITH_GAIN,
+ PORT_CONF_EXT_DEVICE,
+ PORT_CONF_EXT_MIX_SOURCE,
+ PORT_CONF_EXT_MIX_SINK,
+ PORT_CONF_EXT_SESSION
+ };
+ static std::vector<AudioPortConfig> valids = [] {
+ std::vector<AudioPortConfig> result;
+ result.reserve(PORT_CONF_EXT_SESSION + 1);
+ result.push_back(AudioPortConfig{});
+ AudioPortConfig configWithGain{};
+ configWithGain.gain.config(AudioGainConfig{
+ .index = 0,
+ .mode = {toString(xsd::AudioGainMode::AUDIO_GAIN_MODE_JOINT)},
+ .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
+ .rampDurationMs = 1});
+ configWithGain.gain.config().values.resize(1);
+ configWithGain.gain.config().values[0] = 1000;
+ result.push_back(std::move(configWithGain));
+ AudioPortConfig configWithPortExtDevice{};
+ configWithPortExtDevice.ext.device(getValidOutputDeviceAddress());
+ result.push_back(std::move(configWithPortExtDevice));
+ AudioPortConfig configWithPortExtMixSource{};
+ configWithPortExtMixSource.ext.mix({});
+ configWithPortExtMixSource.ext.mix().useCase.stream(
+ toString(xsd::AudioStreamType::AUDIO_STREAM_VOICE_CALL));
+ result.push_back(std::move(configWithPortExtMixSource));
+ AudioPortConfig configWithPortExtMixSink{};
+ configWithPortExtMixSink.ext.mix({});
+ configWithPortExtMixSink.ext.mix().useCase.source(
+ toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT));
+ result.push_back(std::move(configWithPortExtMixSink));
+ AudioPortConfig configWithPortExtSession{};
+ configWithPortExtSession.ext.session(
+ static_cast<AudioSession>(AudioSessionConsts::OUTPUT_MIX));
+ result.push_back(std::move(configWithPortExtSession));
+ return result;
+ }();
+ static std::vector<AudioPortConfig> invalids = [&] {
+ std::vector<AudioPortConfig> result;
+ AudioPortConfig invalidBaseChannelMask = valids[PORT_CONF_MINIMAL];
+ invalidBaseChannelMask.base.channelMask = "random_string";
+ result.push_back(std::move(invalidBaseChannelMask));
+ AudioPortConfig invalidBaseFormat = valids[PORT_CONF_MINIMAL];
+ invalidBaseFormat.base.format = "random_string";
+ result.push_back(std::move(invalidBaseFormat));
+ AudioPortConfig invalidGainMode = valids[PORT_CONF_WITH_GAIN];
+ invalidGainMode.gain.config().mode = {{"random_string"}};
+ result.push_back(std::move(invalidGainMode));
+ AudioPortConfig invalidGainChannelMask = valids[PORT_CONF_WITH_GAIN];
+ invalidGainChannelMask.gain.config().channelMask = "random_string";
+ result.push_back(std::move(invalidGainChannelMask));
+ AudioPortConfig invalidDeviceType = valids[PORT_CONF_EXT_DEVICE];
+ invalidDeviceType.ext.device().deviceType = "random_string";
+ result.push_back(std::move(invalidDeviceType));
+ AudioPortConfig invalidStreamType = valids[PORT_CONF_EXT_MIX_SOURCE];
+ invalidStreamType.ext.mix().useCase.stream() = "random_string";
+ result.push_back(std::move(invalidStreamType));
+ AudioPortConfig invalidSource = valids[PORT_CONF_EXT_MIX_SINK];
+ invalidSource.ext.mix().useCase.source() = "random_string";
+ result.push_back(std::move(invalidSource));
+ return result;
+ }();
+ return valid ? valids : invalids;
+}
+
+TEST_P(AudioHidlDeviceTest, SetAudioPortConfigInvalidArguments) {
+ doc::test("Check that invalid port configs are rejected by IDevice::setAudioPortConfig");
+ for (const auto& invalidConfig : generatePortConfigs(false /*valid*/)) {
+ EXPECT_RESULT(invalidArgsOrNotSupported, getDevice()->setAudioPortConfig(invalidConfig))
+ << ::testing::PrintToString(invalidConfig);
+ }
+}
+
+TEST_P(AudioPatchHidlTest, CreatePatchInvalidArguments) {
+ doc::test("Check that invalid port configs are rejected by IDevice::createAudioPatch");
+ // Note that HAL actually might reject the proposed source / sink combo
+ // due to other reasons than presence of invalid enum-strings.
+ // TODO: Come up with a way to guarantee validity of a source / sink combo.
+ for (const auto& validSource : generatePortConfigs(true /*valid*/)) {
+ for (const auto& invalidSink : generatePortConfigs(false /*valid*/)) {
+ AudioPatchHandle handle;
+ EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{validSource},
+ hidl_vec<AudioPortConfig>{invalidSink},
+ returnIn(res, handle)));
+ EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
+ << "Source: " << ::testing::PrintToString(validSource)
+ << "; Sink: " << ::testing::PrintToString(invalidSink);
+ }
+ }
+ for (const auto& validSink : generatePortConfigs(true /*valid*/)) {
+ for (const auto& invalidSource : generatePortConfigs(false /*valid*/)) {
+ AudioPatchHandle handle;
+ EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{invalidSource},
+ hidl_vec<AudioPortConfig>{validSink},
+ returnIn(res, handle)));
+ EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
+ << "Source: " << ::testing::PrintToString(invalidSource)
+ << "; Sink: " << ::testing::PrintToString(validSink);
+ }
+ }
+}
+
+TEST_P(AudioPatchHidlTest, UpdatePatchInvalidArguments) {
+ doc::test("Check that invalid port configs are rejected by IDevice::updateAudioPatch");
+ // Note that HAL actually might reject the proposed source / sink combo
+ // due to other reasons than presence of invalid enum-strings.
+ // TODO: Come up with a way to guarantee validity of a source / sink combo.
+ for (const auto& validSource : generatePortConfigs(true /*valid*/)) {
+ for (const auto& invalidSink : generatePortConfigs(false /*valid*/)) {
+ AudioPatchHandle handle{};
+ EXPECT_OK(getDevice()->updateAudioPatch(handle, hidl_vec<AudioPortConfig>{validSource},
+ hidl_vec<AudioPortConfig>{invalidSink},
+ returnIn(res, handle)));
+ EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
+ << "Source: " << ::testing::PrintToString(validSource)
+ << "; Sink: " << ::testing::PrintToString(invalidSink);
+ }
+ }
+ for (const auto& validSink : generatePortConfigs(true /*valid*/)) {
+ for (const auto& invalidSource : generatePortConfigs(false /*valid*/)) {
+ AudioPatchHandle handle{};
+ EXPECT_OK(getDevice()->updateAudioPatch(
+ handle, hidl_vec<AudioPortConfig>{invalidSource},
+ hidl_vec<AudioPortConfig>{validSink}, returnIn(res, handle)));
+ EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
+ << "Source: " << ::testing::PrintToString(invalidSource)
+ << "; Sink: " << ::testing::PrintToString(validSink);
+ }
+ }
+}
+
enum { PARAM_DEVICE_CONFIG, PARAM_ADDRESS, PARAM_METADATA };
enum { INDEX_SINK, INDEX_SOURCE };
using SinkOrSourceMetadata = std::variant<SinkMetadata, SourceMetadata>;
@@ -362,18 +509,6 @@
}
};
-static const DeviceAddress& getValidInputDeviceAddress() {
- static const DeviceAddress valid = {
- .deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT)};
- return valid;
-}
-
-static const DeviceAddress& getValidOutputDeviceAddress() {
- static const DeviceAddress valid = {
- .deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT)};
- return valid;
-}
-
static const RecordTrackMetadata& getValidRecordTrackMetadata() {
static const RecordTrackMetadata valid = {
.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT), .gain = 1};
@@ -547,9 +682,7 @@
::testing::Values(getValidInputDeviceAddress()),
::testing::ValuesIn(wrapMetadata(getInvalidSinkMetadatas()))),
&StreamOpenParameterToString);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputStreamInvalidConfig);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputStreamInvalidAddress);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputStreamInvalidMetadata);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(StreamOpenTest);
INSTANTIATE_TEST_CASE_P(
OutputStreamInvalidConfig, StreamOpenTest,
@@ -571,9 +704,6 @@
::testing::Values(getValidOutputDeviceAddress()),
::testing::ValuesIn(wrapMetadata(getInvalidSourceMetadatas()))),
&StreamOpenParameterToString);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OutputStreamInvalidConfig);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OutputStreamInvalidAddress);
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OutputStreamInvalidMetadata);
#define TEST_SINGLE_CONFIG_IO_STREAM(test_name, documentation, code) \
TEST_P(SingleConfigInputStreamTest, test_name) { \
diff --git a/authsecret/aidl/vts/OWNERS b/authsecret/aidl/vts/OWNERS
new file mode 100644
index 0000000..40d95e4
--- /dev/null
+++ b/authsecret/aidl/vts/OWNERS
@@ -0,0 +1,2 @@
+chengyouho@google.com
+frankwoo@google.com
diff --git a/bluetooth/1.0/default/test/bluetooth_address_test.cc b/bluetooth/1.0/default/test/bluetooth_address_test.cc
index ee52d33..422d6a3 100644
--- a/bluetooth/1.0/default/test/bluetooth_address_test.cc
+++ b/bluetooth/1.0/default/test/bluetooth_address_test.cc
@@ -14,7 +14,6 @@
// limitations under the License.
//
-#include <cutils/properties.h>
#include <errno.h>
#include <fcntl.h>
#include <gtest/gtest.h>
@@ -39,12 +38,6 @@
"00:00:00:00:00:00";
constexpr uint8_t kZeros_bytes[BluetoothAddress::kBytes] = {0x00, 0x00, 0x00,
0x00, 0x00, 0x00};
-constexpr char kTestAddrBad1[BluetoothAddress::kStringLength + 1] =
- "bb:aa:dd:00:00:01";
-constexpr uint8_t kTestAddrBad1_bytes[BluetoothAddress::kBytes] = {
- 0xbb, 0xaa, 0xdd, 0x00, 0x00, 0x01};
-
-constexpr char kAddrPath[] = "/tmp/my_address_in_a_file.txt";
class BluetoothAddressTest : public ::testing::Test {
public:
@@ -120,45 +113,6 @@
EXPECT_FALSE(memcmp(addrA, addrB, BluetoothAddress::kStringLength) == 0);
}
-TEST_F(BluetoothAddressTest, get_local_address) {
- EXPECT_TRUE(property_set(PERSIST_BDADDR_PROPERTY, "") == 0);
- EXPECT_TRUE(property_set(FACTORY_BDADDR_PROPERTY, "") == 0);
- uint8_t address[BluetoothAddress::kBytes];
-
- // File contains a non-zero Address.
- FileWriteString(kAddrPath, kTestAddr1);
- EXPECT_TRUE(property_set(PROPERTY_BT_BDADDR_PATH, kAddrPath) == 0);
- EXPECT_TRUE(BluetoothAddress::get_local_address(address));
- EXPECT_TRUE(memcmp(address, kTestAddr1_bytes, BluetoothAddress::kBytes) == 0);
-
- // File contains a zero address. A random address will be generated.
- FileWriteString(kAddrPath, kZeros);
- EXPECT_TRUE(property_set(PROPERTY_BT_BDADDR_PATH, kAddrPath) == 0);
- EXPECT_TRUE(property_set(PERSIST_BDADDR_PROPERTY, kTestAddrBad1) == 0);
- EXPECT_TRUE(BluetoothAddress::get_local_address(address));
- EXPECT_TRUE(memcmp(address, kZeros_bytes, BluetoothAddress::kBytes) != 0);
- char prop[PROP_VALUE_MAX] = "Before reading";
- EXPECT_TRUE(property_get(PERSIST_BDADDR_PROPERTY, prop, NULL) ==
- BluetoothAddress::kStringLength);
- char address_str[BluetoothAddress::kStringLength + 1];
- BluetoothAddress::bytes_to_string(address, address_str);
- EXPECT_TRUE(memcmp(address_str, prop, BluetoothAddress::kStringLength) == 0);
-
- // Factory property contains an address.
- EXPECT_TRUE(property_set(PERSIST_BDADDR_PROPERTY, kTestAddrBad1) == 0);
- EXPECT_TRUE(property_set(FACTORY_BDADDR_PROPERTY, kTestAddr1) == 0);
- EXPECT_TRUE(BluetoothAddress::get_local_address(address));
- EXPECT_TRUE(memcmp(address, kTestAddr1_bytes, BluetoothAddress::kBytes) == 0);
-
- // Persistent property contains an address.
- memcpy(address, kTestAddrBad1_bytes, BluetoothAddress::kBytes);
- EXPECT_TRUE(property_set(PERSIST_BDADDR_PROPERTY, kTestAddr1) == 0);
- EXPECT_TRUE(property_set(FACTORY_BDADDR_PROPERTY, "") == 0);
- EXPECT_TRUE(property_set(PROPERTY_BT_BDADDR_PATH, "") == 0);
- EXPECT_TRUE(BluetoothAddress::get_local_address(address));
- EXPECT_TRUE(memcmp(address, kTestAddr1_bytes, BluetoothAddress::kBytes) == 0);
-}
-
} // namespace implementation
} // namespace V1_0
} // namespace bluetooth
diff --git a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
index ca57243..ce50f25 100644
--- a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
+++ b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
@@ -415,7 +415,7 @@
TEST_P(BroadcastRadioHalTest, FmTune) {
ASSERT_TRUE(openSession());
- uint64_t freq = 100100; // 100.1 FM
+ uint64_t freq = 90900; // 90.9 FM
auto sel = make_selector_amfm(freq);
/* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index d39e339..a8d9a1b 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -258,9 +258,9 @@
<instance>default</instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
+ <hal format="aidl" optional="true">
<name>android.hardware.health.storage</name>
- <version>1.0</version>
+ <version>1</version>
<interface>
<name>IStorage</name>
<instance>default</instance>
@@ -435,6 +435,14 @@
</interface>
</hal>
<hal format="hidl" optional="true">
+ <name>android.hardware.radio.config</name>
+ <version>1.3</version>
+ <interface>
+ <name>IRadioConfig</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
<name>android.hardware.renderscript</name>
<version>1.0</version>
<interface>
@@ -569,6 +577,14 @@
<instance>default</instance>
</interface>
</hal>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.weaver</name>
+ <version>1</version>
+ <interface>
+ <name>IWeaver</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
<hal format="hidl" optional="true">
<name>android.hardware.wifi</name>
<version>1.3-4</version>
diff --git a/health/1.0/Android.bp b/health/1.0/Android.bp
index 7845871..7786c08 100644
--- a/health/1.0/Android.bp
+++ b/health/1.0/Android.bp
@@ -5,7 +5,6 @@
root: "android.hardware",
srcs: [
"types.hal",
- "IHealth.hal",
],
interfaces: [
"android.hidl.base@1.0",
diff --git a/health/1.0/IHealth.hal b/health/1.0/IHealth.hal
deleted file mode 100644
index 3828589..0000000
--- a/health/1.0/IHealth.hal
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2016 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.health@1.0;
-
-interface IHealth {
- /**
- * This function lets you change healthd configuration from default if
- * desired. It must be called exactly once at startup time.
- *
- * The configuration values are described in 'struct HealthConfig'.
- * To use default configuration, simply return without modifying the
- * fields of the config parameter.
- *
- * @param default healthd configuration.
- */
- init(HealthConfig config) generates (HealthConfig configOut);
-
- /**
- * This function is a hook to update/change device's HealthInfo (as described
- * in 'struct HealthInfo').
- *
- * 'HealthInfo' describes device's battery and charging status, typically
- * read from kernel. These values may be modified in this call.
- *
- * @param Device Health info as described in 'struct HealthInfo'.
- * @return skipLogging Indication to the caller to add 'or' skip logging the health
- * information. Return 'true' to skip logging the update.
- * @return infoOut HealthInfo to be sent to client code. (May or may
- * not be modified).
- */
- update(HealthInfo info) generates (bool skipLogging, HealthInfo infoOut);
-
- /**
- * This function is called by healthd when framework queries for remaining
- * energy in the Battery through BatteryManager APIs.
- *
- * @return result Result of querying enery counter for the battery.
- * @return energy Battery remaining energy in nanowatt-hours.
- * Must be '0' if result is anything other than Result::SUCCESS.
- */
- energyCounter() generates (Result result, int64_t energy);
-};
diff --git a/health/1.0/default/Android.bp b/health/1.0/default/Android.bp
index aab9cc7..ff4b875 100644
--- a/health/1.0/default/Android.bp
+++ b/health/1.0/default/Android.bp
@@ -17,62 +17,3 @@
],
}
-
-cc_library_static {
- name: "android.hardware.health@1.0-impl-helper",
- vendor: true,
- srcs: ["Health.cpp"],
-
- header_libs: [
- "libbase_headers",
- "libhealthd_headers",
- ],
-
- shared_libs: [
- "libcutils",
- "libhidlbase",
- "liblog",
- "libutils",
- "android.hardware.health@1.0",
- ],
-
- static_libs: [
- "android.hardware.health@1.0-convert",
- ],
-}
-
-cc_library_shared {
- name: "android.hardware.health@1.0-impl",
- vendor: true,
- relative_install_path: "hw",
-
- static_libs: [
- "android.hardware.health@1.0-impl-helper",
- "android.hardware.health@1.0-convert",
- "libhealthd.default",
- ],
-
- shared_libs: [
- "libhidlbase",
- "libutils",
- "android.hardware.health@1.0",
- ],
-}
-
-cc_binary {
- name: "android.hardware.health@1.0-service",
- vendor: true,
- relative_install_path: "hw",
- init_rc: ["android.hardware.health@1.0-service.rc"],
- srcs: ["HealthService.cpp"],
-
- shared_libs: [
- "liblog",
- "libcutils",
- "libdl",
- "libbase",
- "libutils",
- "libhidlbase",
- "android.hardware.health@1.0",
- ],
-}
diff --git a/health/1.0/default/Health.cpp b/health/1.0/default/Health.cpp
deleted file mode 100644
index 1a02956..0000000
--- a/health/1.0/default/Health.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2016 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 "health-hal"
-
-#include <Health.h>
-#include <include/hal_conversion.h>
-
-namespace android {
-namespace hardware {
-namespace health {
-namespace V1_0 {
-namespace implementation {
-
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
-
-// Methods from ::android::hardware::health::V1_0::IHealth follow.
-Return<void> Health::init(const HealthConfig& config, init_cb _hidl_cb) {
- struct healthd_config healthd_config = {};
- HealthConfig configOut;
-
- // To keep working with existing healthd static HALs,
- // convert the new HealthConfig to the old healthd_config
- // and back.
-
- convertFromHealthConfig(config, &healthd_config);
- healthd_board_init(&healthd_config);
- mGetEnergyCounter = healthd_config.energyCounter;
- convertToHealthConfig(&healthd_config, configOut);
-
- _hidl_cb(configOut);
-
- return Void();
-}
-
-Return<void> Health::update(const HealthInfo& info, update_cb _hidl_cb) {
- struct android::BatteryProperties p = {};
- HealthInfo infoOut;
-
- // To keep working with existing healthd static HALs,
- // convert the new HealthInfo to android::Batteryproperties
- // and back.
-
- convertFromHealthInfo(info, &p);
- int skipLogging = healthd_board_battery_update(&p);
- convertToHealthInfo(&p, infoOut);
-
- _hidl_cb(!!skipLogging, infoOut);
-
- return Void();
-}
-
-Return<void> Health::energyCounter(energyCounter_cb _hidl_cb) {
- int64_t energy = 0;
- Result result = Result::NOT_SUPPORTED;
-
- if (mGetEnergyCounter) {
- int status = mGetEnergyCounter(&energy);
- if (status == 0) {
- result = Result::SUCCESS;
- }
- }
-
- _hidl_cb(result, energy);
-
- return Void();
-}
-
-IHealth* HIDL_FETCH_IHealth(const char* /* name */) {
- return new Health();
-}
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace health
-} // namespace hardware
-} // namespace android
diff --git a/health/1.0/default/Health.h b/health/1.0/default/Health.h
deleted file mode 100644
index ed364c1..0000000
--- a/health/1.0/default/Health.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2016 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_HEALTH_V1_0_HEALTH_H
-#define ANDROID_HARDWARE_HEALTH_V1_0_HEALTH_H
-
-#include <android/hardware/health/1.0/IHealth.h>
-#include <hidl/Status.h>
-#include <hidl/MQDescriptor.h>
-#include <healthd/healthd.h>
-#include <utils/String8.h>
-
-namespace android {
-namespace hardware {
-namespace health {
-namespace V1_0 {
-namespace implementation {
-
-using ::android::hardware::health::V1_0::HealthInfo;
-using ::android::hardware::health::V1_0::HealthConfig;
-using ::android::hardware::health::V1_0::IHealth;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::hidl_string;
-using ::android::sp;
-
-struct Health : public IHealth {
- // Methods from ::android::hardware::health::V1_0::IHealth follow.
- Return<void> init(const HealthConfig& config, init_cb _hidl_cb) override;
- Return<void> update(const HealthInfo& info, update_cb _hidl_cb) override;
- Return<void> energyCounter(energyCounter_cb _hidl_cb) override;
-private:
- std::function<int(int64_t *)> mGetEnergyCounter;
-};
-
-extern "C" IHealth* HIDL_FETCH_IHealth(const char* name);
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace health
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_HEALTH_V1_0_HEALTH_H
diff --git a/health/1.0/default/HealthService.cpp b/health/1.0/default/HealthService.cpp
deleted file mode 100644
index 55848d2..0000000
--- a/health/1.0/default/HealthService.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2016 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.health@1.0-service"
-
-#include <android/hardware/health/1.0/IHealth.h>
-#include <hidl/LegacySupport.h>
-
-using android::hardware::health::V1_0::IHealth;
-using android::hardware::defaultPassthroughServiceImplementation;
-
-int main() {
- return defaultPassthroughServiceImplementation<IHealth>();
-}
diff --git a/health/1.0/default/README.md b/health/1.0/default/README.md
deleted file mode 100644
index 1ded7de..0000000
--- a/health/1.0/default/README.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# Implement the 2.1 HAL instead!
-
-It is strongly recommended that you implement the 2.1 HAL directly. See
-`hardware/interfaces/health/2.1/README.md` for more details.
-
-# Implement Health 1.0 HAL
-
-1. Install common binderized service. The binderized service `dlopen()`s
- passthrough implementations on the device, so there is no need to write
- your own.
-
- ```mk
- # Install default binderized implementation to vendor.
- PRODUCT_PACKAGES += android.hardware.health@1.0-service
- ```
-
-1. Add proper VINTF manifest entry to your device manifest. Example:
-
- ```xml
- <hal format="hidl">
- <name>android.hardware.health</name>
- <transport>hwbinder</transport>
- <version>1.0</version>
- <interface>
- <name>IHealth</name>
- <instance>default</instance>
- </interface>
- </hal>
- ```
-
-1. Install the proper passthrough implemetation.
-
- 1. If you want to use the default implementation (with default `libhealthd`),
- add the following to `device.mk`:
-
- ```mk
- PRODUCT_PACKAGES += \
- android.hardware.health@1.0-impl
- ```
-
- 1. Otherwise, if you have a customized `libhealthd.<board>`:
-
- 1. Define your passthrough implementation. Example (replace `<device>`
- and `<board>` accordingly):
-
- ```bp
- cc_library_shared {
- name: "android.hardware.health@1.0-impl-<device>",
- vendor: true,
- relative_install_path: "hw",
-
- static_libs: [
- "android.hardware.health@1.0-impl-helper",
- "android.hardware.health@1.0-convert",
- "libhealthd.<board>",
- ],
- }
- ```
-
- 1. Add to `device.mk`.
-
- ```
- PRODUCT_PACKAGES += android.hardware.health@1.0-impl-<device>
- ```
-
- 1. Define appropriate SELinux permissions.
diff --git a/health/1.0/default/android.hardware.health@1.0-service.rc b/health/1.0/default/android.hardware.health@1.0-service.rc
deleted file mode 100644
index 569dc88..0000000
--- a/health/1.0/default/android.hardware.health@1.0-service.rc
+++ /dev/null
@@ -1,5 +0,0 @@
-service vendor.health-hal-1-0 /vendor/bin/hw/android.hardware.health@1.0-service
- class hal
- user system
- group system
- capabilities WAKE_ALARM BLOCK_SUSPEND
diff --git a/health/1.0/default/include/hal_conversion.h b/health/1.0/default/include/hal_conversion.h
index a92b208..a8ddb73 100644
--- a/health/1.0/default/include/hal_conversion.h
+++ b/health/1.0/default/include/hal_conversion.h
@@ -17,7 +17,7 @@
#ifndef HARDWARE_INTERFACES_HEALTH_V1_0_DEFAULT_INCLUDE_HAL_CONVERSION_H_
#define HARDWARE_INTERFACES_HEALTH_V1_0_DEFAULT_INCLUDE_HAL_CONVERSION_H_
-#include <android/hardware/health/1.0/IHealth.h>
+#include <android/hardware/health/1.0/types.h>
#include <healthd/healthd.h>
namespace android {
diff --git a/health/1.0/default/libhealthd/Android.bp b/health/1.0/default/libhealthd/Android.bp
deleted file mode 100644
index 43463eb..0000000
--- a/health/1.0/default/libhealthd/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2016 The Android Open Source Project
-
-cc_library_static {
- srcs: ["healthd_board_default.cpp"],
- name: "libhealthd.default",
- vendor_available: true,
- recovery_available: true,
- cflags: ["-Werror"],
- include_dirs: ["system/libbase/include"],
- header_libs: ["libhealthd_headers"],
-}
diff --git a/health/1.0/vts/functional/Android.bp b/health/1.0/vts/functional/Android.bp
deleted file mode 100644
index f4a04a7..0000000
--- a/health/1.0/vts/functional/Android.bp
+++ /dev/null
@@ -1,23 +0,0 @@
-//
-// Copyright (C) 2017 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: "VtsHalHealthV1_0TargetTest",
- defaults: ["VtsHalTargetTestDefaults"],
- srcs: ["VtsHalHealthV1_0TargetTest.cpp"],
- static_libs: ["android.hardware.health@1.0"],
- test_suites: ["general-tests", "vts"],
-}
diff --git a/health/1.0/vts/functional/VtsHalHealthV1_0TargetTest.cpp b/health/1.0/vts/functional/VtsHalHealthV1_0TargetTest.cpp
deleted file mode 100644
index 8b3dcc1..0000000
--- a/health/1.0/vts/functional/VtsHalHealthV1_0TargetTest.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 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 "health_hidl_hal_test"
-
-#include <android/hardware/health/1.0/IHealth.h>
-#include <android/hardware/health/1.0/types.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-#include <log/log.h>
-
-using HealthConfig = ::android::hardware::health::V1_0::HealthConfig;
-using HealthInfo = ::android::hardware::health::V1_0::HealthInfo;
-using IHealth = ::android::hardware::health::V1_0::IHealth;
-using Result = ::android::hardware::health::V1_0::Result;
-
-using ::android::sp;
-
-class HealthHidlTest : public ::testing::TestWithParam<std::string> {
- public:
- virtual void SetUp() override {
- health = IHealth::getService(GetParam());
- ASSERT_NE(health, nullptr);
- health->init(config,
- [&](const auto& halConfigOut) { config = halConfigOut; });
- }
-
- sp<IHealth> health;
- HealthConfig config;
-};
-
-/**
- * Ensure EnergyCounter call returns positive energy counter or NOT_SUPPORTED
- */
-TEST_P(HealthHidlTest, TestEnergyCounter) {
- Result result;
- int64_t energy = 0;
- health->energyCounter([&](Result ret, int64_t energyOut) {
- result = ret;
- energy = energyOut;
- });
-
- ASSERT_TRUE(result == Result::SUCCESS || result == Result::NOT_SUPPORTED);
- ASSERT_TRUE(result != Result::SUCCESS || energy > 0);
-}
-
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HealthHidlTest);
-INSTANTIATE_TEST_SUITE_P(
- PerInstance, HealthHidlTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(IHealth::descriptor)),
- android::hardware::PrintInstanceNameToString);
diff --git a/health/storage/1.0/default/Android.bp b/health/storage/1.0/default/Android.bp
index 3156dfe..3834244 100644
--- a/health/storage/1.0/default/Android.bp
+++ b/health/storage/1.0/default/Android.bp
@@ -38,6 +38,7 @@
],
static_libs: [
+ "libhealth_storage_impl_common",
"libfstab",
],
diff --git a/health/storage/1.0/default/Storage.cpp b/health/storage/1.0/default/Storage.cpp
index 561deaa..02b6a3d 100644
--- a/health/storage/1.0/default/Storage.cpp
+++ b/health/storage/1.0/default/Storage.cpp
@@ -18,11 +18,8 @@
#include <sstream>
-#include <android-base/chrono_utils.h>
-#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <fstab/fstab.h>
+#include <health-storage-impl/common.h>
namespace android {
namespace hardware {
@@ -31,69 +28,9 @@
namespace V1_0 {
namespace implementation {
-using base::ReadFileToString;
-using base::Timer;
-using base::Trim;
-using base::WriteStringToFd;
-using base::WriteStringToFile;
-using fs_mgr::Fstab;
-using fs_mgr::ReadDefaultFstab;
-
-std::string getGarbageCollectPath() {
- Fstab fstab;
- ReadDefaultFstab(&fstab);
-
- for (const auto& entry : fstab) {
- if (!entry.sysfs_path.empty()) {
- return entry.sysfs_path + "/manual_gc";
- }
- }
-
- return "";
-}
-
Return<void> Storage::garbageCollect(uint64_t timeoutSeconds,
const sp<IGarbageCollectCallback>& cb) {
- Result result = Result::SUCCESS;
- std::string path = getGarbageCollectPath();
-
- if (path.empty()) {
- LOG(WARNING) << "Cannot find Dev GC path";
- result = Result::UNKNOWN_ERROR;
- } else {
- Timer timer;
- LOG(INFO) << "Start Dev GC on " << path;
- while (1) {
- std::string require;
- if (!ReadFileToString(path, &require)) {
- PLOG(WARNING) << "Reading manual_gc failed in " << path;
- result = Result::IO_ERROR;
- break;
- }
- require = Trim(require);
- if (require == "" || require == "off" || require == "disabled") {
- LOG(DEBUG) << "No more to do Dev GC";
- break;
- }
- LOG(DEBUG) << "Trigger Dev GC on " << path;
- if (!WriteStringToFile("1", path)) {
- PLOG(WARNING) << "Start Dev GC failed on " << path;
- result = Result::IO_ERROR;
- break;
- }
- if (timer.duration() >= std::chrono::seconds(timeoutSeconds)) {
- LOG(WARNING) << "Dev GC timeout";
- // Timeout is not treated as an error. Try next time.
- break;
- }
- sleep(2);
- }
- LOG(INFO) << "Stop Dev GC on " << path;
- if (!WriteStringToFile("0", path)) {
- PLOG(WARNING) << "Stop Dev GC failed on " << path;
- result = Result::IO_ERROR;
- }
- }
+ Result result = GarbageCollect(timeoutSeconds);
if (cb != nullptr) {
auto ret = cb->onFinish(result);
@@ -110,28 +47,7 @@
}
int fd = handle->data[0];
- std::stringstream output;
-
- std::string path = getGarbageCollectPath();
- if (path.empty()) {
- output << "Cannot find Dev GC path";
- } else {
- std::string require;
-
- if (ReadFileToString(path, &require)) {
- output << path << ":" << require << std::endl;
- }
-
- if (WriteStringToFile("0", path)) {
- output << "stop success" << std::endl;
- }
- }
-
- if (!WriteStringToFd(output.str(), fd)) {
- PLOG(WARNING) << "debug: cannot write to fd";
- }
-
- fsync(fd);
+ DebugDump(fd);
return Void();
}
diff --git a/health/storage/1.0/vts/functional/Android.bp b/health/storage/1.0/vts/functional/Android.bp
index 2201031..731ad62 100644
--- a/health/storage/1.0/vts/functional/Android.bp
+++ b/health/storage/1.0/vts/functional/Android.bp
@@ -19,6 +19,9 @@
defaults: ["VtsHalTargetTestDefaults"],
srcs: ["VtsHalHealthStorageV1_0TargetTest.cpp"],
static_libs: ["android.hardware.health.storage@1.0"],
+ header_libs: [
+ "libhealth_storage_test_common_headers",
+ ],
shared_libs: [
"libhidlbase",
],
diff --git a/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp b/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
index 24ddc5d..ddb6b5a 100644
--- a/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
+++ b/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
@@ -14,14 +14,17 @@
* limitations under the License.
*/
+#include <unistd.h>
+
+#include <thread>
+
#include <android-base/logging.h>
#include <android/hardware/health/storage/1.0/IStorage.h>
#include <gtest/gtest.h>
+#include <health-storage-test/common.h>
#include <hidl/GtestPrinter.h>
#include <hidl/HidlTransportSupport.h>
#include <hidl/ServiceManagement.h>
-#include <unistd.h>
-#include <thread>
namespace android {
namespace hardware {
@@ -29,61 +32,17 @@
namespace storage {
namespace V1_0 {
+using namespace ::android::hardware::health::storage::test;
using ::std::literals::chrono_literals::operator""ms;
#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) << ret.description()
-// Dev GC timeout. This is the timeout used by vold.
-const uint64_t kDevGcTimeoutSec = 120;
-const std::chrono::seconds kDevGcTimeout{kDevGcTimeoutSec};
-// Dev GC timeout tolerance. The HAL may not immediately return after the
-// timeout, so include an acceptable tolerance.
-const std::chrono::seconds kDevGcTolerance{3};
-// Time accounted for RPC calls.
-const std::chrono::milliseconds kRpcTime{1000};
-
-template <typename R>
-std::string toString(std::chrono::duration<R, std::milli> time) {
- return std::to_string(time.count()) + "ms";
-}
-
-/** An atomic boolean flag that indicates whether a task has finished. */
-class Flag {
- public:
- void onFinish() {
- std::unique_lock<std::mutex> lock(mMutex);
- onFinishLocked(&lock);
- }
- template <typename R, typename P>
- bool wait(std::chrono::duration<R, P> duration) {
- std::unique_lock<std::mutex> lock(mMutex);
- return waitLocked(&lock, duration);
- }
-
- protected:
- /** Will unlock. */
- void onFinishLocked(std::unique_lock<std::mutex>* lock) {
- mFinished = true;
- lock->unlock();
- mCv.notify_all();
- }
- template <typename R, typename P>
- bool waitLocked(std::unique_lock<std::mutex>* lock, std::chrono::duration<R, P> duration) {
- mCv.wait_for(*lock, duration, [this] { return mFinished; });
- return mFinished;
- }
-
- bool mFinished{false};
- std::mutex mMutex;
- std::condition_variable mCv;
-};
-
class GcCallback : public IGarbageCollectCallback, public Flag {
- public:
+ public:
Return<void> onFinish(Result result) override {
- std::unique_lock<std::mutex> lock(mMutex);
- mResult = result;
- Flag::onFinishLocked(&lock);
+ std::unique_lock<std::mutex> lock(mutex_);
+ result_ = result;
+ Flag::OnFinishLocked(&lock);
return Void();
}
@@ -93,13 +52,13 @@
*/
template <typename R, typename P>
void waitForResult(std::chrono::duration<R, P> timeout, Result expected) {
- std::unique_lock<std::mutex> lock(mMutex);
- ASSERT_TRUE(waitLocked(&lock, timeout)) << "timeout after " << toString(timeout);
- EXPECT_EQ(expected, mResult);
+ std::unique_lock<std::mutex> lock(mutex_);
+ ASSERT_TRUE(WaitLocked(&lock, timeout)) << "timeout after " << to_string(timeout);
+ EXPECT_EQ(expected, result_);
}
- private:
- Result mResult{Result::UNKNOWN_ERROR};
+ private:
+ Result result_{Result::UNKNOWN_ERROR};
};
class HealthStorageHidlTest : public ::testing::TestWithParam<std::string> {
@@ -127,10 +86,10 @@
auto pingFlag = std::make_shared<Flag>();
std::thread([service, pingFlag] {
service->ping();
- pingFlag->onFinish();
+ pingFlag->OnFinish();
})
.detach();
- return pingFlag->wait(timeout);
+ return pingFlag->Wait(timeout);
}
sp<IStorage> fs;
@@ -147,7 +106,7 @@
// Hold test process because HAL can be single-threaded and doing GC.
ASSERT_TRUE(ping(kDevGcTimeout + kDevGcTolerance + kRpcTime))
<< "Service must be available after "
- << toString(kDevGcTimeout + kDevGcTolerance + kRpcTime);
+ << to_string(kDevGcTimeout + kDevGcTolerance + kRpcTime);
}
/**
diff --git a/health/storage/aidl/Android.bp b/health/storage/aidl/Android.bp
new file mode 100644
index 0000000..c39a46d
--- /dev/null
+++ b/health/storage/aidl/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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.
+
+aidl_interface {
+ name: "android.hardware.health.storage",
+ vendor_available: true,
+ srcs: ["android/hardware/health/storage/*.aidl"],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ enabled: false,
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IGarbageCollectCallback.aidl
similarity index 87%
copy from security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
copy to health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IGarbageCollectCallback.aidl
index 963e66e..0f382d7 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
+++ b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IGarbageCollectCallback.aidl
@@ -16,8 +16,8 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.security.keymint;
+package android.hardware.health.storage;
@VintfStability
-parcelable Timestamp {
- long milliSeconds;
+interface IGarbageCollectCallback {
+ oneway void onFinish(in android.hardware.health.storage.Result result);
}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IStorage.aidl
similarity index 84%
copy from security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
copy to health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IStorage.aidl
index 963e66e..61f838a 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
+++ b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IStorage.aidl
@@ -16,8 +16,8 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.security.keymint;
+package android.hardware.health.storage;
@VintfStability
-parcelable Timestamp {
- long milliSeconds;
+interface IStorage {
+ oneway void garbageCollect(in long timeoutSeconds, in android.hardware.health.storage.IGarbageCollectCallback callback);
}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/Result.aidl
similarity index 87%
copy from security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
copy to health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/Result.aidl
index 963e66e..a345808 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
+++ b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/Result.aidl
@@ -16,8 +16,10 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.security.keymint;
-@VintfStability
-parcelable Timestamp {
- long milliSeconds;
+package android.hardware.health.storage;
+@Backing(type="int") @VintfStability
+enum Result {
+ SUCCESS = 0,
+ IO_ERROR = 1,
+ UNKNOWN_ERROR = 2,
}
diff --git a/health/storage/aidl/android/hardware/health/storage/IGarbageCollectCallback.aidl b/health/storage/aidl/android/hardware/health/storage/IGarbageCollectCallback.aidl
new file mode 100644
index 0000000..ccd1b44
--- /dev/null
+++ b/health/storage/aidl/android/hardware/health/storage/IGarbageCollectCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 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.health.storage;
+
+import android.hardware.health.storage.Result;
+
+/**
+ * Callback interface to IStorage.garbageCollect.
+ */
+@VintfStability
+interface IGarbageCollectCallback {
+ /**
+ * When garbage collection has finished, the implementation must
+ * invoke this function to indicate the result of the garbage collection.
+ *
+ * @param out result Execution result. See documentation for Result for
+ * details.
+ */
+ oneway void onFinish(in Result result);
+}
diff --git a/health/storage/aidl/android/hardware/health/storage/IStorage.aidl b/health/storage/aidl/android/hardware/health/storage/IStorage.aidl
new file mode 100644
index 0000000..78992a2
--- /dev/null
+++ b/health/storage/aidl/android/hardware/health/storage/IStorage.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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.health.storage;
+
+import android.hardware.health.storage.IGarbageCollectCallback;
+
+/**
+ * IStorage is an interface that provides operations on underlying storage
+ * devices, including flash memory.
+ */
+@VintfStability
+interface IStorage {
+ /**
+ * Start garbage collection on the driver of storage devices.
+ *
+ * Garbage collection must be started at regular intervals when it is a good
+ * time for a longer-running cleanup tasks, roughly daily.
+ *
+ * When garbage collection finishes or encounters an error before the
+ * specified timeout, the implementation must call IGarbageCollect.finish
+ * immediately with appropriate result.
+ *
+ * If garbage collection does not finish within the specified timeout,
+ * the implementation must stop garbage collection, and must not call
+ * IGarbageCollect.finish.
+ *
+ * @param timeoutSeconds timeout in seconds. The implementation must
+ * return after the timeout is reached.
+ *
+ * @param callback callback interface. Callback must be null if the client
+ * does not need to receive any callbacks.
+ *
+ */
+ oneway void garbageCollect(in long timeoutSeconds, in IGarbageCollectCallback callback);
+}
diff --git a/health/storage/aidl/android/hardware/health/storage/Result.aidl b/health/storage/aidl/android/hardware/health/storage/Result.aidl
new file mode 100644
index 0000000..73bb779
--- /dev/null
+++ b/health/storage/aidl/android/hardware/health/storage/Result.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 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.health.storage;
+
+/**
+ * Status values for HAL methods.
+ */
+@VintfStability
+@Backing(type="int")
+enum Result {
+ /**
+ * Execution of the method is successful.
+ */
+ SUCCESS = 0,
+ /**
+ * An IO error is encountered when the HAL communicates with the device.
+ */
+ IO_ERROR,
+ /**
+ * An unknown error is encountered.
+ */
+ UNKNOWN_ERROR,
+}
diff --git a/health/storage/aidl/default/Android.bp b/health/storage/aidl/default/Android.bp
new file mode 100644
index 0000000..68a8ee2
--- /dev/null
+++ b/health/storage/aidl/default/Android.bp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 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: "libhealth_storage_impl_defaults",
+ vendor: true,
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "android.hardware.health.storage-unstable-ndk_platform",
+ ],
+ static_libs: [
+ "libfstab",
+ "libhealth_storage_impl_common",
+ ],
+}
+
+cc_library_static {
+ name: "libhealth_storage_default_impl",
+ defaults: ["libhealth_storage_impl_defaults"],
+ srcs: [
+ "Storage.cpp",
+ ],
+ visibility: [
+ ":__subpackages__",
+ "//hardware/interfaces/tests/extension/health/storage:__subpackages__",
+ ],
+}
+
+cc_binary {
+ name: "android.hardware.health.storage-service.default",
+ defaults: ["libhealth_storage_impl_defaults"],
+ relative_install_path: "hw",
+ init_rc: ["health-storage-default.rc"],
+ vintf_fragments: ["health-storage-default.xml"],
+ srcs: ["main.cpp"],
+ static_libs: [
+ "libhealth_storage_default_impl",
+ ],
+}
diff --git a/health/storage/aidl/default/Storage.cpp b/health/storage/aidl/default/Storage.cpp
new file mode 100644
index 0000000..faa4ff6
--- /dev/null
+++ b/health/storage/aidl/default/Storage.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 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 "Storage.h"
+
+#include <sstream>
+
+#include <android-base/logging.h>
+#include <health-storage-impl/common.h>
+
+using ::android::hardware::health::storage::DebugDump;
+using ::android::hardware::health::storage::GarbageCollect;
+
+using HResult = android::hardware::health::storage::V1_0::Result;
+using AResult = aidl::android::hardware::health::storage::Result;
+// Ensure static_cast<AResult>(any HResult) works
+static_assert(static_cast<AResult>(HResult::SUCCESS) == AResult::SUCCESS);
+static_assert(static_cast<AResult>(HResult::IO_ERROR) == AResult::IO_ERROR);
+static_assert(static_cast<AResult>(HResult::UNKNOWN_ERROR) == AResult::UNKNOWN_ERROR);
+
+namespace aidl::android::hardware::health::storage {
+
+ndk::ScopedAStatus Storage::garbageCollect(
+ int64_t timeout_seconds, const std::shared_ptr<IGarbageCollectCallback>& callback) {
+ AResult result = static_cast<AResult>(GarbageCollect(static_cast<uint64_t>(timeout_seconds)));
+ if (callback != nullptr) {
+ auto status = callback->onFinish(result);
+ if (!status.isOk()) {
+ LOG(WARNING) << "Cannot return result " << toString(result)
+ << " to callback: " << status.getDescription();
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+binder_status_t Storage::dump(int fd, const char**, uint32_t) {
+ DebugDump(fd);
+ return STATUS_OK;
+}
+
+} // namespace aidl::android::hardware::health::storage
diff --git a/health/storage/aidl/default/Storage.h b/health/storage/aidl/default/Storage.h
new file mode 100644
index 0000000..049991b
--- /dev/null
+++ b/health/storage/aidl/default/Storage.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 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 <aidl/android/hardware/health/storage/BnStorage.h>
+
+namespace aidl::android::hardware::health::storage {
+
+class Storage : public BnStorage {
+ ndk::ScopedAStatus garbageCollect(
+ int64_t timeout_seconds,
+ const std::shared_ptr<IGarbageCollectCallback>& callback) override;
+ binder_status_t dump(int fd, const char** args, uint32_t num_args) override;
+};
+
+} // namespace aidl::android::hardware::health::storage
diff --git a/health/storage/aidl/default/health-storage-default.rc b/health/storage/aidl/default/health-storage-default.rc
new file mode 100644
index 0000000..fc1cc8b
--- /dev/null
+++ b/health/storage/aidl/default/health-storage-default.rc
@@ -0,0 +1,7 @@
+service vendor.health-storage-default /vendor/bin/hw/android.hardware.health.storage-service.default
+ interface aidl android.hardware.health.storage.IStorage/default
+ oneshot
+ disabled
+ class hal
+ user system
+ group system
diff --git a/health/storage/aidl/default/health-storage-default.xml b/health/storage/aidl/default/health-storage-default.xml
new file mode 100644
index 0000000..14d4901
--- /dev/null
+++ b/health/storage/aidl/default/health-storage-default.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.health.storage</name>
+ <version>1</version>
+ <fqname>IStorage/default</fqname>
+ </hal>
+</manifest>
diff --git a/health/storage/aidl/default/main.cpp b/health/storage/aidl/default/main.cpp
new file mode 100644
index 0000000..186b64c
--- /dev/null
+++ b/health/storage/aidl/default/main.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "Storage.h"
+
+using aidl::android::hardware::health::storage::Storage;
+using std::string_literals::operator""s;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ // make a default storage service
+ auto storage = ndk::SharedRefBase::make<Storage>();
+ const std::string name = Storage::descriptor + "/default"s;
+ CHECK_EQ(STATUS_OK,
+ AServiceManager_registerLazyService(storage->asBinder().get(), name.c_str()));
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/health/storage/aidl/vts/functional/Android.bp b/health/storage/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..86b72a7
--- /dev/null
+++ b/health/storage/aidl/vts/functional/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 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: "VtsHalHealthStorageTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalHealthStorageTargetTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "android.hardware.health.storage-ndk_platform",
+ ],
+ header_libs: [
+ "libhealth_storage_test_common_headers",
+ ],
+ test_suites: [
+ "vts",
+ ],
+ test_config: "VtsHalHealthStorageTargetTest.xml",
+}
diff --git a/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.cpp b/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.cpp
new file mode 100644
index 0000000..3b6b6b4
--- /dev/null
+++ b/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2021 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 <unistd.h>
+
+#include <chrono>
+#include <set>
+#include <string>
+#include <thread>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/health/storage/BnGarbageCollectCallback.h>
+#include <aidl/android/hardware/health/storage/IStorage.h>
+#include <android-base/logging.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <health-storage-test/common.h>
+
+namespace aidl::android::hardware::health::storage {
+
+using namespace ::android::hardware::health::storage::test;
+using std::chrono_literals::operator""ms;
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) << ret.getDescription()
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) << ret.getDescription()
+
+class GcCallback : public BnGarbageCollectCallback, public Flag {
+ public:
+ ndk::ScopedAStatus onFinish(Result result) override {
+ std::unique_lock<std::mutex> lock(mutex_);
+ result_ = result;
+ OnFinishLocked(&lock);
+ return ndk::ScopedAStatus::ok();
+ }
+
+ /**
+ * Wait for a specific "timeout". If GC has finished, test that the result
+ * is equal to the "expected" value.
+ */
+ template <typename R, typename P>
+ void WaitForResult(std::chrono::duration<R, P> timeout, Result expected) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ ASSERT_TRUE(WaitLocked(&lock, timeout)) << "timeout after " << to_string(timeout);
+ EXPECT_EQ(expected, result_);
+ }
+
+ private:
+ Result result_{Result::UNKNOWN_ERROR};
+};
+
+class HealthStorageAidl : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ std::string name = GetParam();
+ ASSERT_TRUE(AServiceManager_isDeclared(name.c_str())) << name;
+ ndk::SpAIBinder binder(AServiceManager_waitForService(name.c_str()));
+ ASSERT_NE(binder, nullptr);
+ storage_ = IStorage::fromBinder(binder);
+ ASSERT_NE(storage_, nullptr);
+ }
+
+ virtual void TearDown() override {
+ EXPECT_TRUE(ping(kRpcTime))
+ << "Service is not responsive; expect subsequent tests to fail.";
+ }
+
+ /**
+ * Ping the service and expect it to return after "timeout". Return true
+ * iff the service is responsive within "timeout".
+ */
+ template <typename R, typename P>
+ bool ping(std::chrono::duration<R, P> timeout) {
+ // Ensure the service is responsive after the test.
+ std::shared_ptr<IStorage> service = storage_;
+ auto ping_flag = std::make_shared<Flag>();
+ std::thread([service, ping_flag] {
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(service->asBinder().get()));
+ ping_flag->OnFinish();
+ }).detach();
+ return ping_flag->Wait(timeout);
+ }
+
+ std::shared_ptr<IStorage> storage_;
+};
+
+/**
+ * Ensure garbage collection works on null callback.
+ */
+TEST_P(HealthStorageAidl, GcNullCallback) {
+ ASSERT_OK(storage_->garbageCollect(kDevGcTimeoutSec, nullptr));
+
+ // Hold test process because HAL can be single-threaded and doing GC.
+ ASSERT_TRUE(ping(kDevGcTimeout + kDevGcTolerance + kRpcTime))
+ << "Service must be available after "
+ << to_string(kDevGcTimeout + kDevGcTolerance + kRpcTime);
+}
+
+/**
+ * Ensure garbage collection works on non-null callback.
+ */
+TEST_P(HealthStorageAidl, GcNonNullCallback) {
+ std::shared_ptr<GcCallback> cb = ndk::SharedRefBase::make<GcCallback>();
+ ASSERT_OK(storage_->garbageCollect(kDevGcTimeoutSec, cb));
+ cb->WaitForResult(kDevGcTimeout + kDevGcTolerance + kRpcTime, Result::SUCCESS);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HealthStorageAidl);
+INSTANTIATE_TEST_SUITE_P(
+ HealthStorage, HealthStorageAidl,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(IStorage::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+} // namespace aidl::android::hardware::health::storage
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.xml b/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.xml
new file mode 100644
index 0000000..f8a1c87
--- /dev/null
+++ b/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 VtsHalHealthStorageTargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalHealthStorageTargetTest->/data/local/tmp/VtsHalHealthStorageTargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalHealthStorageTargetTest" />
+ <option name="native-test-timeout" value="3m" />
+ </test>
+</configuration>
diff --git a/health/storage/impl_common/Android.bp b/health/storage/impl_common/Android.bp
new file mode 100644
index 0000000..e1149c0
--- /dev/null
+++ b/health/storage/impl_common/Android.bp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+// Common implementation between HIDL and AIDL HAL.
+cc_library_static {
+ name: "libhealth_storage_impl_common",
+ vendor: true,
+ srcs: [
+ "impl_common.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libhidlbase",
+ "liblog",
+ "android.hardware.health.storage@1.0",
+ ],
+
+ static_libs: [
+ "libfstab",
+ ],
+
+ export_shared_lib_headers: [
+ "android.hardware.health.storage@1.0",
+ ],
+}
diff --git a/health/storage/impl_common/impl_common.cpp b/health/storage/impl_common/impl_common.cpp
new file mode 100644
index 0000000..6e753d4
--- /dev/null
+++ b/health/storage/impl_common/impl_common.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 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 <health-storage-impl/common.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+
+using ::android::base::ReadFileToString;
+using ::android::base::Timer;
+using ::android::base::Trim;
+using ::android::base::WriteStringToFd;
+using ::android::base::WriteStringToFile;
+using ::android::fs_mgr::Fstab;
+using ::android::fs_mgr::ReadDefaultFstab;
+using ::android::hardware::health::storage::V1_0::Result;
+
+namespace android::hardware::health::storage {
+
+static std::string GetGarbageCollectPath() {
+ Fstab fstab;
+ ReadDefaultFstab(&fstab);
+
+ for (const auto& entry : fstab) {
+ if (!entry.sysfs_path.empty()) {
+ return entry.sysfs_path + "/manual_gc";
+ }
+ }
+
+ return "";
+}
+
+Result GarbageCollect(uint64_t timeout_seconds) {
+ std::string path = GetGarbageCollectPath();
+
+ if (path.empty()) {
+ LOG(WARNING) << "Cannot find Dev GC path";
+ return Result::UNKNOWN_ERROR;
+ }
+
+ Result result = Result::SUCCESS;
+ Timer timer;
+ LOG(INFO) << "Start Dev GC on " << path;
+ while (1) {
+ std::string require;
+ if (!ReadFileToString(path, &require)) {
+ PLOG(WARNING) << "Reading manual_gc failed in " << path;
+ result = Result::IO_ERROR;
+ break;
+ }
+ require = Trim(require);
+ if (require == "" || require == "off" || require == "disabled") {
+ LOG(DEBUG) << "No more to do Dev GC";
+ break;
+ }
+ LOG(DEBUG) << "Trigger Dev GC on " << path;
+ if (!WriteStringToFile("1", path)) {
+ PLOG(WARNING) << "Start Dev GC failed on " << path;
+ result = Result::IO_ERROR;
+ break;
+ }
+ if (timer.duration() >= std::chrono::seconds(timeout_seconds)) {
+ LOG(WARNING) << "Dev GC timeout";
+ // Timeout is not treated as an error. Try next time.
+ break;
+ }
+ sleep(2);
+ }
+ LOG(INFO) << "Stop Dev GC on " << path;
+ if (!WriteStringToFile("0", path)) {
+ PLOG(WARNING) << "Stop Dev GC failed on " << path;
+ result = Result::IO_ERROR;
+ }
+
+ return result;
+}
+
+void DebugDump(int fd) {
+ std::stringstream output;
+
+ std::string path = GetGarbageCollectPath();
+ if (path.empty()) {
+ output << "Cannot find Dev GC path";
+ } else {
+ std::string require;
+
+ if (ReadFileToString(path, &require)) {
+ output << path << ":" << require << std::endl;
+ }
+
+ if (WriteStringToFile("0", path)) {
+ output << "stop success" << std::endl;
+ }
+ }
+
+ if (!WriteStringToFd(output.str(), fd)) {
+ PLOG(WARNING) << "debug: cannot write to fd";
+ }
+
+ fsync(fd);
+}
+
+} // namespace android::hardware::health::storage
diff --git a/health/storage/impl_common/include/health-storage-impl/common.h b/health/storage/impl_common/include/health-storage-impl/common.h
new file mode 100644
index 0000000..c84a6a9
--- /dev/null
+++ b/health/storage/impl_common/include/health-storage-impl/common.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 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/health/storage/1.0/types.h>
+#include <string>
+
+namespace android::hardware::health::storage {
+
+// Run debug on fd
+void DebugDump(int fd);
+
+// Run garbage collection on GetGarbageCollectPath(). Blocks until garbage
+// collect finishes or |timeout_seconds| has reached.
+V1_0::Result GarbageCollect(uint64_t timeout_seconds);
+
+} // namespace android::hardware::health::storage
diff --git a/health/1.0/default/libhealthd/healthd_board_default.cpp b/health/storage/test_common/Android.bp
similarity index 64%
rename from health/1.0/default/libhealthd/healthd_board_default.cpp
rename to health/storage/test_common/Android.bp
index 127f98e..7c6bef4 100644
--- a/health/1.0/default/libhealthd/healthd_board_default.cpp
+++ b/health/storage/test_common/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2021 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,15 +14,7 @@
* limitations under the License.
*/
-#include <healthd/healthd.h>
-
-void healthd_board_init(struct healthd_config*)
-{
- // use defaults
-}
-
-int healthd_board_battery_update(struct android::BatteryProperties*)
-{
- // return 0 to log periodic polled battery status to kernel log
- return 0;
+cc_library_headers {
+ name: "libhealth_storage_test_common_headers",
+ export_include_dirs: ["include"],
}
diff --git a/health/storage/test_common/include/health-storage-test/common.h b/health/storage/test_common/include/health-storage-test/common.h
new file mode 100644
index 0000000..dfda830
--- /dev/null
+++ b/health/storage/test_common/include/health-storage-test/common.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 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 <chrono>
+#include <string>
+
+namespace android::hardware::health::storage::test {
+
+// Dev GC timeout. This is the timeout used by vold.
+const uint64_t kDevGcTimeoutSec = 120;
+const std::chrono::seconds kDevGcTimeout{kDevGcTimeoutSec};
+// Dev GC timeout tolerance. The HAL may not immediately return after the
+// timeout, so include an acceptable tolerance.
+const std::chrono::seconds kDevGcTolerance{3};
+// Time accounted for RPC calls.
+const std::chrono::milliseconds kRpcTime{1000};
+
+template <typename R>
+std::string to_string(std::chrono::duration<R, std::milli> time) {
+ return std::to_string(time.count()) + "ms";
+}
+
+/** An atomic boolean flag that indicates whether a task has finished. */
+class Flag {
+ public:
+ void OnFinish() {
+ std::unique_lock<std::mutex> lock(mutex_);
+ OnFinishLocked(&lock);
+ }
+ template <typename R, typename P>
+ bool Wait(std::chrono::duration<R, P> duration) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ return WaitLocked(&lock, duration);
+ }
+
+ protected:
+ /** Will unlock. */
+ void OnFinishLocked(std::unique_lock<std::mutex>* lock) {
+ finished_ = true;
+ lock->unlock();
+ cv_.notify_all();
+ }
+ template <typename R, typename P>
+ bool WaitLocked(std::unique_lock<std::mutex>* lock, std::chrono::duration<R, P> duration) {
+ cv_.wait_for(*lock, duration, [this] { return finished_; });
+ return finished_;
+ }
+
+ bool finished_{false};
+ std::mutex mutex_;
+ std::condition_variable cv_;
+};
+
+} // namespace android::hardware::health::storage::test
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
new file mode 100644
index 0000000..f2cbe93
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
@@ -0,0 +1,55 @@
+/*
+ * 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_INTERFACES_NEURALNETWORKS_1_0_UTILS_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_BURST_H
+
+#include <nnapi/IBurst.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <optional>
+#include <utility>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+// Class that adapts nn::IPreparedModel to nn::IBurst.
+class Burst final : public nn::IBurst {
+ struct PrivateConstructorTag {};
+
+ public:
+ static nn::GeneralResult<std::shared_ptr<const Burst>> create(
+ nn::SharedPreparedModel preparedModel);
+
+ Burst(PrivateConstructorTag tag, nn::SharedPreparedModel preparedModel);
+
+ OptionalCacheHold cacheMemory(const nn::Memory& memory) const override;
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+ const nn::Request& request, nn::MeasureTiming measure) const override;
+
+ private:
+ const nn::SharedPreparedModel kPreparedModel;
+};
+
+} // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_BURST_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
index db3b2ad..4681b9e 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
@@ -52,6 +52,7 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
+ bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
index 2de1828..8853eea 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
@@ -35,7 +35,8 @@
namespace android::hardware::neuralnetworks::V1_0::utils {
// Class that adapts V1_0::IPreparedModel to nn::IPreparedModel.
-class PreparedModel final : public nn::IPreparedModel {
+class PreparedModel final : public nn::IPreparedModel,
+ public std::enable_shared_from_this<PreparedModel> {
struct PrivateConstructorTag {};
public:
@@ -56,6 +57,8 @@
const nn::OptionalDuration& loopTimeoutDuration,
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+ nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
std::any getUnderlyingResource() const override;
private:
diff --git a/neuralnetworks/1.0/utils/src/Burst.cpp b/neuralnetworks/1.0/utils/src/Burst.cpp
new file mode 100644
index 0000000..384bd9b
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Burst.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 "Burst.h"
+
+#include <android-base/logging.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <optional>
+#include <utility>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+nn::GeneralResult<std::shared_ptr<const Burst>> Burst::create(
+ nn::SharedPreparedModel preparedModel) {
+ if (preparedModel == nullptr) {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "V1_0::utils::Burst::create must have non-null preparedModel";
+ }
+
+ return std::make_shared<const Burst>(PrivateConstructorTag{}, std::move(preparedModel));
+}
+
+Burst::Burst(PrivateConstructorTag /*tag*/, nn::SharedPreparedModel preparedModel)
+ : kPreparedModel(std::move(preparedModel)) {
+ CHECK(kPreparedModel != nullptr);
+}
+
+Burst::OptionalCacheHold Burst::cacheMemory(const nn::Memory& /*memory*/) const {
+ return nullptr;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute(
+ const nn::Request& request, nn::MeasureTiming measure) const {
+ return kPreparedModel->execute(request, measure, {}, {});
+}
+
+} // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Device.cpp b/neuralnetworks/1.0/utils/src/Device.cpp
index 93bd81a..bb31a26 100644
--- a/neuralnetworks/1.0/utils/src/Device.cpp
+++ b/neuralnetworks/1.0/utils/src/Device.cpp
@@ -106,6 +106,10 @@
return nn::DeviceType::OTHER;
}
+bool Device::isUpdatable() const {
+ return false;
+}
+
const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/1.0/utils/src/PreparedModel.cpp b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
index c0c22fb..858571d 100644
--- a/neuralnetworks/1.0/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
@@ -16,6 +16,7 @@
#include "PreparedModel.h"
+#include "Burst.h"
#include "Callbacks.h"
#include "Conversions.h"
#include "Utils.h"
@@ -90,6 +91,10 @@
<< "IPreparedModel::executeFenced is not supported on 1.0 HAL service";
}
+nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
+ return Burst::create(shared_from_this());
+}
+
std::any PreparedModel::getUnderlyingResource() const {
sp<V1_0::IPreparedModel> resource = kPreparedModel;
return resource;
diff --git a/neuralnetworks/1.0/vts/functional/AndroidTest.xml b/neuralnetworks/1.0/vts/functional/AndroidTest.xml
index 13671f9..9dd85ae 100644
--- a/neuralnetworks/1.0/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.0/vts/functional/AndroidTest.xml
@@ -28,5 +28,6 @@
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsHalNeuralnetworksV1_0TargetTest" />
+ <option name="native-test-timeout" value="20m" />
</test>
</configuration>
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
index 5e224b5..3aec8ee 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
@@ -52,6 +52,7 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
+ bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
diff --git a/neuralnetworks/1.1/utils/src/Device.cpp b/neuralnetworks/1.1/utils/src/Device.cpp
index 3197ef4..d2ef57f 100644
--- a/neuralnetworks/1.1/utils/src/Device.cpp
+++ b/neuralnetworks/1.1/utils/src/Device.cpp
@@ -106,6 +106,10 @@
return nn::DeviceType::UNKNOWN;
}
+bool Device::isUpdatable() const {
+ return false;
+}
+
const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/1.1/vts/functional/AndroidTest.xml b/neuralnetworks/1.1/vts/functional/AndroidTest.xml
index cfde60c..74001f9 100644
--- a/neuralnetworks/1.1/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.1/vts/functional/AndroidTest.xml
@@ -28,5 +28,6 @@
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsHalNeuralnetworksV1_1TargetTest" />
+ <option name="native-test-timeout" value="20m" />
</test>
</configuration>
diff --git a/neuralnetworks/1.2/utils/Android.bp b/neuralnetworks/1.2/utils/Android.bp
index 0fec41c..6959056 100644
--- a/neuralnetworks/1.2/utils/Android.bp
+++ b/neuralnetworks/1.2/utils/Android.bp
@@ -18,6 +18,7 @@
name: "neuralnetworks_utils_hal_1_2",
defaults: ["neuralnetworks_utils_defaults"],
srcs: ["src/*"],
+ exclude_srcs: ["src/ExecutionBurst*"],
local_include_dirs: ["include/nnapi/hal/1.2/"],
export_include_dirs: ["include"],
cflags: ["-Wthread-safety"],
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
index b4bef5e..489f857 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
@@ -71,6 +71,7 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
+ bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
new file mode 100644
index 0000000..5356a91
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_CONTROLLER_H
+#define ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_CONTROLLER_H
+
+#include "ExecutionBurstUtils.h"
+
+#include <android-base/macros.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IBurstCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IBurstContext.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include <atomic>
+#include <chrono>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <stack>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::nn {
+
+/**
+ * The ExecutionBurstController class manages both the serialization and
+ * deserialization of data across FMQ, making it appear to the runtime as a
+ * regular synchronous inference. Additionally, this class manages the burst's
+ * memory cache.
+ */
+class ExecutionBurstController {
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExecutionBurstController);
+
+ public:
+ /**
+ * NN runtime burst callback object and memory cache.
+ *
+ * ExecutionBurstCallback associates a hidl_memory object with a slot number
+ * to be passed across FMQ. The ExecutionBurstServer can use this callback
+ * to retrieve this hidl_memory corresponding to the slot via HIDL.
+ *
+ * Whenever a hidl_memory object is copied, it will duplicate the underlying
+ * file descriptor. Because the NN runtime currently copies the hidl_memory
+ * on each execution, it is difficult to associate hidl_memory objects with
+ * previously cached hidl_memory objects. For this reason, callers of this
+ * class must pair each hidl_memory object with an associated key. For
+ * efficiency, if two hidl_memory objects represent the same underlying
+ * buffer, they must use the same key.
+ */
+ class ExecutionBurstCallback : public hardware::neuralnetworks::V1_2::IBurstCallback {
+ DISALLOW_COPY_AND_ASSIGN(ExecutionBurstCallback);
+
+ public:
+ ExecutionBurstCallback() = default;
+
+ hardware::Return<void> getMemories(const hardware::hidl_vec<int32_t>& slots,
+ getMemories_cb cb) override;
+
+ /**
+ * This function performs one of two different actions:
+ * 1) If a key corresponding to a memory resource is unrecognized by the
+ * ExecutionBurstCallback object, the ExecutionBurstCallback object
+ * will allocate a slot, bind the memory to the slot, and return the
+ * slot identifier.
+ * 2) If a key corresponding to a memory resource is recognized by the
+ * ExecutionBurstCallback object, the ExecutionBurstCallback object
+ * will return the existing slot identifier.
+ *
+ * @param memories Memory resources used in an inference.
+ * @param keys Unique identifiers where each element corresponds to a
+ * memory resource element in "memories".
+ * @return Unique slot identifiers where each returned slot element
+ * corresponds to a memory resource element in "memories".
+ */
+ std::vector<int32_t> getSlots(const hardware::hidl_vec<hardware::hidl_memory>& memories,
+ const std::vector<intptr_t>& keys);
+
+ /*
+ * This function performs two different actions:
+ * 1) Removes an entry from the cache (if present), including the local
+ * storage of the hidl_memory object. Note that this call does not
+ * free any corresponding hidl_memory object in ExecutionBurstServer,
+ * which is separately freed via IBurstContext::freeMemory.
+ * 2) Return whether a cache entry was removed and which slot was removed if
+ * found. If the key did not to correspond to any entry in the cache, a
+ * slot number of 0 is returned. The slot number and whether the entry
+ * existed is useful so the same slot can be freed in the
+ * ExecutionBurstServer's cache via IBurstContext::freeMemory.
+ */
+ std::pair<bool, int32_t> freeMemory(intptr_t key);
+
+ private:
+ int32_t getSlotLocked(const hardware::hidl_memory& memory, intptr_t key);
+ int32_t allocateSlotLocked();
+
+ std::mutex mMutex;
+ std::stack<int32_t, std::vector<int32_t>> mFreeSlots;
+ std::map<intptr_t, int32_t> mMemoryIdToSlot;
+ std::vector<hardware::hidl_memory> mMemoryCache;
+ };
+
+ /**
+ * Creates a burst controller on a prepared model.
+ *
+ * Prefer this over ExecutionBurstController's constructor.
+ *
+ * @param preparedModel Model prepared for execution to execute on.
+ * @param pollingTimeWindow How much time (in microseconds) the
+ * ExecutionBurstController is allowed to poll the FMQ before waiting on
+ * the blocking futex. Polling may result in lower latencies at the
+ * potential cost of more power usage.
+ * @return ExecutionBurstController Execution burst controller object.
+ */
+ static std::unique_ptr<ExecutionBurstController> create(
+ const sp<hardware::neuralnetworks::V1_2::IPreparedModel>& preparedModel,
+ std::chrono::microseconds pollingTimeWindow);
+
+ // prefer calling ExecutionBurstController::create
+ ExecutionBurstController(const std::shared_ptr<RequestChannelSender>& requestChannelSender,
+ const std::shared_ptr<ResultChannelReceiver>& resultChannelReceiver,
+ const sp<hardware::neuralnetworks::V1_2::IBurstContext>& burstContext,
+ const sp<ExecutionBurstCallback>& callback,
+ const sp<hardware::hidl_death_recipient>& deathHandler = nullptr);
+
+ // explicit destructor to unregister the death recipient
+ ~ExecutionBurstController();
+
+ /**
+ * Execute a request on a model.
+ *
+ * @param request Arguments to be executed on a model.
+ * @param measure Whether to collect timing measurements, either YES or NO
+ * @param memoryIds Identifiers corresponding to each memory object in the
+ * request's pools.
+ * @return A tuple of:
+ * - result code of the execution
+ * - dynamic output shapes from the execution
+ * - any execution time measurements of the execution
+ * - whether or not a failed burst execution should be re-run using a
+ * different path (e.g., IPreparedModel::executeSynchronously)
+ */
+ std::tuple<int, std::vector<hardware::neuralnetworks::V1_2::OutputShape>,
+ hardware::neuralnetworks::V1_2::Timing, bool>
+ compute(const hardware::neuralnetworks::V1_0::Request& request,
+ hardware::neuralnetworks::V1_2::MeasureTiming measure,
+ const std::vector<intptr_t>& memoryIds);
+
+ /**
+ * Propagate a user's freeing of memory to the service.
+ *
+ * @param key Key corresponding to the memory object.
+ */
+ void freeMemory(intptr_t key);
+
+ private:
+ std::mutex mMutex;
+ const std::shared_ptr<RequestChannelSender> mRequestChannelSender;
+ const std::shared_ptr<ResultChannelReceiver> mResultChannelReceiver;
+ const sp<hardware::neuralnetworks::V1_2::IBurstContext> mBurstContext;
+ const sp<ExecutionBurstCallback> mMemoryCache;
+ const sp<hardware::hidl_death_recipient> mDeathHandler;
+};
+
+} // namespace android::nn
+
+#endif // ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_CONTROLLER_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
new file mode 100644
index 0000000..2e109b2
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_SERVER_H
+#define ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_SERVER_H
+
+#include "ExecutionBurstUtils.h"
+
+#include <android-base/macros.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IBurstCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include <atomic>
+#include <chrono>
+#include <memory>
+#include <optional>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+namespace android::nn {
+
+/**
+ * The ExecutionBurstServer class is responsible for waiting for and
+ * deserializing a request object from a FMQ, performing the inference, and
+ * serializing the result back across another FMQ.
+ */
+class ExecutionBurstServer : public hardware::neuralnetworks::V1_2::IBurstContext {
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExecutionBurstServer);
+
+ public:
+ /**
+ * IBurstExecutorWithCache is a callback object passed to
+ * ExecutionBurstServer's factory function that is used to perform an
+ * execution. Because some memory resources are needed across multiple
+ * executions, this object also contains a local cache that can directly be
+ * used in the execution.
+ *
+ * ExecutionBurstServer will never access its IBurstExecutorWithCache object
+ * with concurrent calls.
+ */
+ class IBurstExecutorWithCache {
+ DISALLOW_COPY_AND_ASSIGN(IBurstExecutorWithCache);
+
+ public:
+ IBurstExecutorWithCache() = default;
+ virtual ~IBurstExecutorWithCache() = default;
+
+ /**
+ * Checks if a cache entry specified by a slot is present in the cache.
+ *
+ * @param slot Identifier of the cache entry.
+ * @return 'true' if the cache entry is present in the cache, 'false'
+ * otherwise.
+ */
+ virtual bool isCacheEntryPresent(int32_t slot) const = 0;
+
+ /**
+ * Adds an entry specified by a slot to the cache.
+ *
+ * The caller of this function must ensure that the cache entry that is
+ * being added is not already present in the cache. This can be checked
+ * via isCacheEntryPresent.
+ *
+ * @param memory Memory resource to be cached.
+ * @param slot Slot identifier corresponding to the memory resource.
+ */
+ virtual void addCacheEntry(const hardware::hidl_memory& memory, int32_t slot) = 0;
+
+ /**
+ * Removes an entry specified by a slot from the cache.
+ *
+ * If the cache entry corresponding to the slot number does not exist,
+ * the call does nothing.
+ *
+ * @param slot Slot identifier corresponding to the memory resource.
+ */
+ virtual void removeCacheEntry(int32_t slot) = 0;
+
+ /**
+ * Perform an execution.
+ *
+ * @param request Request object with inputs and outputs specified.
+ * Request::pools is empty, and DataLocation::poolIndex instead
+ * refers to the 'slots' argument as if it were Request::pools.
+ * @param slots Slots corresponding to the cached memory entries to be
+ * used.
+ * @param measure Whether timing information is requested for the
+ * execution.
+ * @return Result of the execution, including the status of the
+ * execution, dynamic output shapes, and any timing information.
+ */
+ virtual std::tuple<hardware::neuralnetworks::V1_0::ErrorStatus,
+ hardware::hidl_vec<hardware::neuralnetworks::V1_2::OutputShape>,
+ hardware::neuralnetworks::V1_2::Timing>
+ execute(const hardware::neuralnetworks::V1_0::Request& request,
+ const std::vector<int32_t>& slots,
+ hardware::neuralnetworks::V1_2::MeasureTiming measure) = 0;
+ };
+
+ /**
+ * Create automated context to manage FMQ-based executions.
+ *
+ * This function is intended to be used by a service to automatically:
+ * 1) Receive data from a provided FMQ
+ * 2) Execute a model with the given information
+ * 3) Send the result to the created FMQ
+ *
+ * @param callback Callback used to retrieve memories corresponding to
+ * unrecognized slots.
+ * @param requestChannel Input FMQ channel through which the client passes the
+ * request to the service.
+ * @param resultChannel Output FMQ channel from which the client can retrieve
+ * the result of the execution.
+ * @param executorWithCache Object which maintains a local cache of the
+ * memory pools and executes using the cached memory pools.
+ * @param pollingTimeWindow How much time (in microseconds) the
+ * ExecutionBurstServer is allowed to poll the FMQ before waiting on
+ * the blocking futex. Polling may result in lower latencies at the
+ * potential cost of more power usage.
+ * @result IBurstContext Handle to the burst context.
+ */
+ static sp<ExecutionBurstServer> create(
+ const sp<hardware::neuralnetworks::V1_2::IBurstCallback>& callback,
+ const FmqRequestDescriptor& requestChannel, const FmqResultDescriptor& resultChannel,
+ std::shared_ptr<IBurstExecutorWithCache> executorWithCache,
+ std::chrono::microseconds pollingTimeWindow = std::chrono::microseconds{0});
+
+ /**
+ * Create automated context to manage FMQ-based executions.
+ *
+ * This function is intended to be used by a service to automatically:
+ * 1) Receive data from a provided FMQ
+ * 2) Execute a model with the given information
+ * 3) Send the result to the created FMQ
+ *
+ * @param callback Callback used to retrieve memories corresponding to
+ * unrecognized slots.
+ * @param requestChannel Input FMQ channel through which the client passes the
+ * request to the service.
+ * @param resultChannel Output FMQ channel from which the client can retrieve
+ * the result of the execution.
+ * @param preparedModel PreparedModel that the burst object was created from.
+ * IPreparedModel::executeSynchronously will be used to perform the
+ * execution.
+ * @param pollingTimeWindow How much time (in microseconds) the
+ * ExecutionBurstServer is allowed to poll the FMQ before waiting on
+ * the blocking futex. Polling may result in lower latencies at the
+ * potential cost of more power usage.
+ * @result IBurstContext Handle to the burst context.
+ */
+ static sp<ExecutionBurstServer> create(
+ const sp<hardware::neuralnetworks::V1_2::IBurstCallback>& callback,
+ const FmqRequestDescriptor& requestChannel, const FmqResultDescriptor& resultChannel,
+ hardware::neuralnetworks::V1_2::IPreparedModel* preparedModel,
+ std::chrono::microseconds pollingTimeWindow = std::chrono::microseconds{0});
+
+ ExecutionBurstServer(const sp<hardware::neuralnetworks::V1_2::IBurstCallback>& callback,
+ std::unique_ptr<RequestChannelReceiver> requestChannel,
+ std::unique_ptr<ResultChannelSender> resultChannel,
+ std::shared_ptr<IBurstExecutorWithCache> cachedExecutor);
+ ~ExecutionBurstServer();
+
+ // Used by the NN runtime to preemptively remove any stored memory.
+ hardware::Return<void> freeMemory(int32_t slot) override;
+
+ private:
+ // Ensures all cache entries contained in mExecutorWithCache are present in
+ // the cache. If they are not present, they are retrieved (via
+ // IBurstCallback::getMemories) and added to mExecutorWithCache.
+ //
+ // This method is locked via mMutex when it is called.
+ void ensureCacheEntriesArePresentLocked(const std::vector<int32_t>& slots);
+
+ // Work loop that will continue processing execution requests until the
+ // ExecutionBurstServer object is freed.
+ void task();
+
+ std::thread mWorker;
+ std::mutex mMutex;
+ std::atomic<bool> mTeardown{false};
+ const sp<hardware::neuralnetworks::V1_2::IBurstCallback> mCallback;
+ const std::unique_ptr<RequestChannelReceiver> mRequestChannelReceiver;
+ const std::unique_ptr<ResultChannelSender> mResultChannelSender;
+ const std::shared_ptr<IBurstExecutorWithCache> mExecutorWithCache;
+};
+
+} // namespace android::nn
+
+#endif // ANDROID_FRAMEWORKS_ML_NN_COMMON_EXECUTION_BURST_SERVER_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
new file mode 100644
index 0000000..8a41591
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_UTILS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_UTILS_H
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include <atomic>
+#include <chrono>
+#include <memory>
+#include <optional>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+/**
+ * Number of elements in the FMQ.
+ */
+constexpr const size_t kExecutionBurstChannelLength = 1024;
+
+using FmqRequestDescriptor = MQDescriptorSync<FmqRequestDatum>;
+using FmqResultDescriptor = MQDescriptorSync<FmqResultDatum>;
+
+/**
+ * Function to serialize a request.
+ *
+ * Prefer calling RequestChannelSender::send.
+ *
+ * @param request Request object without the pool information.
+ * @param measure Whether to collect timing information for the execution.
+ * @param memoryIds Slot identifiers corresponding to memory resources for the
+ * request.
+ * @return Serialized FMQ request data.
+ */
+std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum> serialize(
+ const hardware::neuralnetworks::V1_0::Request& request,
+ hardware::neuralnetworks::V1_2::MeasureTiming measure, const std::vector<int32_t>& slots);
+
+/**
+ * Deserialize the FMQ request data.
+ *
+ * The three resulting fields are the Request object (where Request::pools is
+ * empty), slot identifiers (which are stand-ins for Request::pools), and
+ * whether timing information must be collected for the run.
+ *
+ * @param data Serialized FMQ request data.
+ * @return Request object if successfully deserialized, std::nullopt otherwise.
+ */
+std::optional<std::tuple<hardware::neuralnetworks::V1_0::Request, std::vector<int32_t>,
+ hardware::neuralnetworks::V1_2::MeasureTiming>>
+deserialize(const std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum>& data);
+
+/**
+ * Function to serialize results.
+ *
+ * Prefer calling ResultChannelSender::send.
+ *
+ * @param errorStatus Status of the execution.
+ * @param outputShapes Dynamic shapes of the output tensors.
+ * @param timing Timing information of the execution.
+ * @return Serialized FMQ result data.
+ */
+std::vector<hardware::neuralnetworks::V1_2::FmqResultDatum> serialize(
+ hardware::neuralnetworks::V1_0::ErrorStatus errorStatus,
+ const std::vector<hardware::neuralnetworks::V1_2::OutputShape>& outputShapes,
+ hardware::neuralnetworks::V1_2::Timing timing);
+
+/**
+ * Deserialize the FMQ result data.
+ *
+ * The three resulting fields are the status of the execution, the dynamic
+ * shapes of the output tensors, and the timing information of the execution.
+ *
+ * @param data Serialized FMQ result data.
+ * @return Result object if successfully deserialized, std::nullopt otherwise.
+ */
+std::optional<std::tuple<hardware::neuralnetworks::V1_0::ErrorStatus,
+ std::vector<hardware::neuralnetworks::V1_2::OutputShape>,
+ hardware::neuralnetworks::V1_2::Timing>>
+deserialize(const std::vector<hardware::neuralnetworks::V1_2::FmqResultDatum>& data);
+
+/**
+ * Convert result code to error status.
+ *
+ * @param resultCode Result code to be converted.
+ * @return ErrorStatus Resultant error status.
+ */
+hardware::neuralnetworks::V1_0::ErrorStatus legacyConvertResultCodeToErrorStatus(int resultCode);
+
+/**
+ * RequestChannelSender is responsible for serializing the result packet of
+ * information, sending it on the result channel, and signaling that the data is
+ * available.
+ */
+class RequestChannelSender {
+ using FmqRequestDescriptor =
+ hardware::MQDescriptorSync<hardware::neuralnetworks::V1_2::FmqRequestDatum>;
+ using FmqRequestChannel =
+ hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqRequestDatum,
+ hardware::kSynchronizedReadWrite>;
+
+ public:
+ /**
+ * Create the sending end of a request channel.
+ *
+ * Prefer this call over the constructor.
+ *
+ * @param channelLength Number of elements in the FMQ.
+ * @return A pair of ResultChannelReceiver and the FMQ descriptor on
+ * successful creation, both nullptr otherwise.
+ */
+ static std::pair<std::unique_ptr<RequestChannelSender>, const FmqRequestDescriptor*> create(
+ size_t channelLength);
+
+ /**
+ * Send the request to the channel.
+ *
+ * @param request Request object without the pool information.
+ * @param measure Whether to collect timing information for the execution.
+ * @param memoryIds Slot identifiers corresponding to memory resources for
+ * the request.
+ * @return 'true' on successful send, 'false' otherwise.
+ */
+ bool send(const hardware::neuralnetworks::V1_0::Request& request,
+ hardware::neuralnetworks::V1_2::MeasureTiming measure,
+ const std::vector<int32_t>& slots);
+
+ /**
+ * Method to mark the channel as invalid, causing all future calls to
+ * RequestChannelSender::send to immediately return false without attempting
+ * to send a message across the FMQ.
+ */
+ void invalidate();
+
+ // prefer calling RequestChannelSender::send
+ bool sendPacket(const std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum>& packet);
+
+ RequestChannelSender(std::unique_ptr<FmqRequestChannel> fmqRequestChannel);
+
+ private:
+ const std::unique_ptr<FmqRequestChannel> mFmqRequestChannel;
+ std::atomic<bool> mValid{true};
+};
+
+/**
+ * RequestChannelReceiver is responsible for waiting on the channel until the
+ * packet is available, extracting the packet from the channel, and
+ * deserializing the packet.
+ *
+ * Because the receiver can wait on a packet that may never come (e.g., because
+ * the sending side of the packet has been closed), this object can be
+ * invalidated, unblocking the receiver.
+ */
+class RequestChannelReceiver {
+ using FmqRequestChannel =
+ hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqRequestDatum,
+ hardware::kSynchronizedReadWrite>;
+
+ public:
+ /**
+ * Create the receiving end of a request channel.
+ *
+ * Prefer this call over the constructor.
+ *
+ * @param requestChannel Descriptor for the request channel.
+ * @param pollingTimeWindow How much time (in microseconds) the
+ * RequestChannelReceiver is allowed to poll the FMQ before waiting on
+ * the blocking futex. Polling may result in lower latencies at the
+ * potential cost of more power usage.
+ * @return RequestChannelReceiver on successful creation, nullptr otherwise.
+ */
+ static std::unique_ptr<RequestChannelReceiver> create(
+ const FmqRequestDescriptor& requestChannel,
+ std::chrono::microseconds pollingTimeWindow);
+
+ /**
+ * Get the request from the channel.
+ *
+ * This method will block until either:
+ * 1) The packet has been retrieved, or
+ * 2) The receiver has been invalidated
+ *
+ * @return Request object if successfully received, std::nullopt if error or
+ * if the receiver object was invalidated.
+ */
+ std::optional<std::tuple<hardware::neuralnetworks::V1_0::Request, std::vector<int32_t>,
+ hardware::neuralnetworks::V1_2::MeasureTiming>>
+ getBlocking();
+
+ /**
+ * Method to mark the channel as invalid, unblocking any current or future
+ * calls to RequestChannelReceiver::getBlocking.
+ */
+ void invalidate();
+
+ RequestChannelReceiver(std::unique_ptr<FmqRequestChannel> fmqRequestChannel,
+ std::chrono::microseconds pollingTimeWindow);
+
+ private:
+ std::optional<std::vector<hardware::neuralnetworks::V1_2::FmqRequestDatum>> getPacketBlocking();
+
+ const std::unique_ptr<FmqRequestChannel> mFmqRequestChannel;
+ std::atomic<bool> mTeardown{false};
+ const std::chrono::microseconds kPollingTimeWindow;
+};
+
+/**
+ * ResultChannelSender is responsible for serializing the result packet of
+ * information, sending it on the result channel, and signaling that the data is
+ * available.
+ */
+class ResultChannelSender {
+ using FmqResultChannel = hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqResultDatum,
+ hardware::kSynchronizedReadWrite>;
+
+ public:
+ /**
+ * Create the sending end of a result channel.
+ *
+ * Prefer this call over the constructor.
+ *
+ * @param resultChannel Descriptor for the result channel.
+ * @return ResultChannelSender on successful creation, nullptr otherwise.
+ */
+ static std::unique_ptr<ResultChannelSender> create(const FmqResultDescriptor& resultChannel);
+
+ /**
+ * Send the result to the channel.
+ *
+ * @param errorStatus Status of the execution.
+ * @param outputShapes Dynamic shapes of the output tensors.
+ * @param timing Timing information of the execution.
+ * @return 'true' on successful send, 'false' otherwise.
+ */
+ bool send(hardware::neuralnetworks::V1_0::ErrorStatus errorStatus,
+ const std::vector<hardware::neuralnetworks::V1_2::OutputShape>& outputShapes,
+ hardware::neuralnetworks::V1_2::Timing timing);
+
+ // prefer calling ResultChannelSender::send
+ bool sendPacket(const std::vector<hardware::neuralnetworks::V1_2::FmqResultDatum>& packet);
+
+ ResultChannelSender(std::unique_ptr<FmqResultChannel> fmqResultChannel);
+
+ private:
+ const std::unique_ptr<FmqResultChannel> mFmqResultChannel;
+};
+
+/**
+ * ResultChannelReceiver is responsible for waiting on the channel until the
+ * packet is available, extracting the packet from the channel, and
+ * deserializing the packet.
+ *
+ * Because the receiver can wait on a packet that may never come (e.g., because
+ * the sending side of the packet has been closed), this object can be
+ * invalidated, unblocking the receiver.
+ */
+class ResultChannelReceiver {
+ using FmqResultDescriptor =
+ hardware::MQDescriptorSync<hardware::neuralnetworks::V1_2::FmqResultDatum>;
+ using FmqResultChannel = hardware::MessageQueue<hardware::neuralnetworks::V1_2::FmqResultDatum,
+ hardware::kSynchronizedReadWrite>;
+
+ public:
+ /**
+ * Create the receiving end of a result channel.
+ *
+ * Prefer this call over the constructor.
+ *
+ * @param channelLength Number of elements in the FMQ.
+ * @param pollingTimeWindow How much time (in microseconds) the
+ * ResultChannelReceiver is allowed to poll the FMQ before waiting on
+ * the blocking futex. Polling may result in lower latencies at the
+ * potential cost of more power usage.
+ * @return A pair of ResultChannelReceiver and the FMQ descriptor on
+ * successful creation, both nullptr otherwise.
+ */
+ static std::pair<std::unique_ptr<ResultChannelReceiver>, const FmqResultDescriptor*> create(
+ size_t channelLength, std::chrono::microseconds pollingTimeWindow);
+
+ /**
+ * Get the result from the channel.
+ *
+ * This method will block until either:
+ * 1) The packet has been retrieved, or
+ * 2) The receiver has been invalidated
+ *
+ * @return Result object if successfully received, std::nullopt if error or
+ * if the receiver object was invalidated.
+ */
+ std::optional<std::tuple<hardware::neuralnetworks::V1_0::ErrorStatus,
+ std::vector<hardware::neuralnetworks::V1_2::OutputShape>,
+ hardware::neuralnetworks::V1_2::Timing>>
+ getBlocking();
+
+ /**
+ * Method to mark the channel as invalid, unblocking any current or future
+ * calls to ResultChannelReceiver::getBlocking.
+ */
+ void invalidate();
+
+ // prefer calling ResultChannelReceiver::getBlocking
+ std::optional<std::vector<hardware::neuralnetworks::V1_2::FmqResultDatum>> getPacketBlocking();
+
+ ResultChannelReceiver(std::unique_ptr<FmqResultChannel> fmqResultChannel,
+ std::chrono::microseconds pollingTimeWindow);
+
+ private:
+ const std::unique_ptr<FmqResultChannel> mFmqResultChannel;
+ std::atomic<bool> mValid{true};
+ const std::chrono::microseconds kPollingTimeWindow;
+};
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_UTILS_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
index 6a56a82..fb11130 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
@@ -36,7 +36,8 @@
namespace android::hardware::neuralnetworks::V1_2::utils {
// Class that adapts V1_2::IPreparedModel to nn::IPreparedModel.
-class PreparedModel final : public nn::IPreparedModel {
+class PreparedModel final : public nn::IPreparedModel,
+ public std::enable_shared_from_this<PreparedModel> {
struct PrivateConstructorTag {};
public:
@@ -57,6 +58,8 @@
const nn::OptionalDuration& loopTimeoutDuration,
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+ nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
std::any getUnderlyingResource() const override;
private:
diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp
index 9fe0de2..1954dfa 100644
--- a/neuralnetworks/1.2/utils/src/Device.cpp
+++ b/neuralnetworks/1.2/utils/src/Device.cpp
@@ -199,6 +199,10 @@
return kDeviceType;
}
+bool Device::isUpdatable() const {
+ return false;
+}
+
const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
new file mode 100644
index 0000000..2265861
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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 "ExecutionBurstController"
+
+#include "ExecutionBurstController.h"
+
+#include <android-base/logging.h>
+
+#include <algorithm>
+#include <cstring>
+#include <limits>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "ExecutionBurstUtils.h"
+#include "HalInterfaces.h"
+#include "Tracing.h"
+#include "Utils.h"
+
+namespace android::nn {
+namespace {
+
+class BurstContextDeathHandler : public hardware::hidl_death_recipient {
+ public:
+ using Callback = std::function<void()>;
+
+ BurstContextDeathHandler(const Callback& onDeathCallback) : mOnDeathCallback(onDeathCallback) {
+ CHECK(onDeathCallback != nullptr);
+ }
+
+ void serviceDied(uint64_t /*cookie*/, const wp<hidl::base::V1_0::IBase>& /*who*/) override {
+ LOG(ERROR) << "BurstContextDeathHandler::serviceDied -- service unexpectedly died!";
+ mOnDeathCallback();
+ }
+
+ private:
+ const Callback mOnDeathCallback;
+};
+
+} // anonymous namespace
+
+hardware::Return<void> ExecutionBurstController::ExecutionBurstCallback::getMemories(
+ const hardware::hidl_vec<int32_t>& slots, getMemories_cb cb) {
+ std::lock_guard<std::mutex> guard(mMutex);
+
+ // get all memories
+ hardware::hidl_vec<hardware::hidl_memory> memories(slots.size());
+ std::transform(slots.begin(), slots.end(), memories.begin(), [this](int32_t slot) {
+ return slot < mMemoryCache.size() ? mMemoryCache[slot] : hardware::hidl_memory{};
+ });
+
+ // ensure all memories are valid
+ if (!std::all_of(memories.begin(), memories.end(),
+ [](const hardware::hidl_memory& memory) { return memory.valid(); })) {
+ cb(V1_0::ErrorStatus::INVALID_ARGUMENT, {});
+ return hardware::Void();
+ }
+
+ // return successful
+ cb(V1_0::ErrorStatus::NONE, std::move(memories));
+ return hardware::Void();
+}
+
+std::vector<int32_t> ExecutionBurstController::ExecutionBurstCallback::getSlots(
+ const hardware::hidl_vec<hardware::hidl_memory>& memories,
+ const std::vector<intptr_t>& keys) {
+ std::lock_guard<std::mutex> guard(mMutex);
+
+ // retrieve (or bind) all slots corresponding to memories
+ std::vector<int32_t> slots;
+ slots.reserve(memories.size());
+ for (size_t i = 0; i < memories.size(); ++i) {
+ slots.push_back(getSlotLocked(memories[i], keys[i]));
+ }
+ return slots;
+}
+
+std::pair<bool, int32_t> ExecutionBurstController::ExecutionBurstCallback::freeMemory(
+ intptr_t key) {
+ std::lock_guard<std::mutex> guard(mMutex);
+
+ auto iter = mMemoryIdToSlot.find(key);
+ if (iter == mMemoryIdToSlot.end()) {
+ return {false, 0};
+ }
+ const int32_t slot = iter->second;
+ mMemoryIdToSlot.erase(key);
+ mMemoryCache[slot] = {};
+ mFreeSlots.push(slot);
+ return {true, slot};
+}
+
+int32_t ExecutionBurstController::ExecutionBurstCallback::getSlotLocked(
+ const hardware::hidl_memory& memory, intptr_t key) {
+ auto iter = mMemoryIdToSlot.find(key);
+ if (iter == mMemoryIdToSlot.end()) {
+ const int32_t slot = allocateSlotLocked();
+ mMemoryIdToSlot[key] = slot;
+ mMemoryCache[slot] = memory;
+ return slot;
+ } else {
+ const int32_t slot = iter->second;
+ return slot;
+ }
+}
+
+int32_t ExecutionBurstController::ExecutionBurstCallback::allocateSlotLocked() {
+ constexpr size_t kMaxNumberOfSlots = std::numeric_limits<int32_t>::max();
+
+ // if there is a free slot, use it
+ if (mFreeSlots.size() > 0) {
+ const int32_t slot = mFreeSlots.top();
+ mFreeSlots.pop();
+ return slot;
+ }
+
+ // otherwise use a slot for the first time
+ CHECK(mMemoryCache.size() < kMaxNumberOfSlots) << "Exceeded maximum number of slots!";
+ const int32_t slot = static_cast<int32_t>(mMemoryCache.size());
+ mMemoryCache.emplace_back();
+
+ return slot;
+}
+
+std::unique_ptr<ExecutionBurstController> ExecutionBurstController::create(
+ const sp<V1_2::IPreparedModel>& preparedModel,
+ std::chrono::microseconds pollingTimeWindow) {
+ // check inputs
+ if (preparedModel == nullptr) {
+ LOG(ERROR) << "ExecutionBurstController::create passed a nullptr";
+ return nullptr;
+ }
+
+ // create callback object
+ sp<ExecutionBurstCallback> callback = new ExecutionBurstCallback();
+
+ // create FMQ objects
+ auto [requestChannelSenderTemp, requestChannelDescriptor] =
+ RequestChannelSender::create(kExecutionBurstChannelLength);
+ auto [resultChannelReceiverTemp, resultChannelDescriptor] =
+ ResultChannelReceiver::create(kExecutionBurstChannelLength, pollingTimeWindow);
+ std::shared_ptr<RequestChannelSender> requestChannelSender =
+ std::move(requestChannelSenderTemp);
+ std::shared_ptr<ResultChannelReceiver> resultChannelReceiver =
+ std::move(resultChannelReceiverTemp);
+
+ // check FMQ objects
+ if (!requestChannelSender || !resultChannelReceiver || !requestChannelDescriptor ||
+ !resultChannelDescriptor) {
+ LOG(ERROR) << "ExecutionBurstController::create failed to create FastMessageQueue";
+ return nullptr;
+ }
+
+ // configure burst
+ V1_0::ErrorStatus errorStatus;
+ sp<IBurstContext> burstContext;
+ const hardware::Return<void> ret = preparedModel->configureExecutionBurst(
+ callback, *requestChannelDescriptor, *resultChannelDescriptor,
+ [&errorStatus, &burstContext](V1_0::ErrorStatus status,
+ const sp<IBurstContext>& context) {
+ errorStatus = status;
+ burstContext = context;
+ });
+
+ // check burst
+ if (!ret.isOk()) {
+ LOG(ERROR) << "IPreparedModel::configureExecutionBurst failed with description "
+ << ret.description();
+ return nullptr;
+ }
+ if (errorStatus != V1_0::ErrorStatus::NONE) {
+ LOG(ERROR) << "IPreparedModel::configureExecutionBurst failed with status "
+ << toString(errorStatus);
+ return nullptr;
+ }
+ if (burstContext == nullptr) {
+ LOG(ERROR) << "IPreparedModel::configureExecutionBurst returned nullptr for burst";
+ return nullptr;
+ }
+
+ // create death handler object
+ BurstContextDeathHandler::Callback onDeathCallback = [requestChannelSender,
+ resultChannelReceiver] {
+ requestChannelSender->invalidate();
+ resultChannelReceiver->invalidate();
+ };
+ const sp<BurstContextDeathHandler> deathHandler = new BurstContextDeathHandler(onDeathCallback);
+
+ // linkToDeath registers a callback that will be invoked on service death to
+ // proactively handle service crashes. If the linkToDeath call fails,
+ // asynchronous calls are susceptible to hangs if the service crashes before
+ // providing the response.
+ const hardware::Return<bool> deathHandlerRet = burstContext->linkToDeath(deathHandler, 0);
+ if (!deathHandlerRet.isOk() || deathHandlerRet != true) {
+ LOG(ERROR) << "ExecutionBurstController::create -- Failed to register a death recipient "
+ "for the IBurstContext object.";
+ return nullptr;
+ }
+
+ // make and return controller
+ return std::make_unique<ExecutionBurstController>(requestChannelSender, resultChannelReceiver,
+ burstContext, callback, deathHandler);
+}
+
+ExecutionBurstController::ExecutionBurstController(
+ const std::shared_ptr<RequestChannelSender>& requestChannelSender,
+ const std::shared_ptr<ResultChannelReceiver>& resultChannelReceiver,
+ const sp<IBurstContext>& burstContext, const sp<ExecutionBurstCallback>& callback,
+ const sp<hardware::hidl_death_recipient>& deathHandler)
+ : mRequestChannelSender(requestChannelSender),
+ mResultChannelReceiver(resultChannelReceiver),
+ mBurstContext(burstContext),
+ mMemoryCache(callback),
+ mDeathHandler(deathHandler) {}
+
+ExecutionBurstController::~ExecutionBurstController() {
+ // It is safe to ignore any errors resulting from this unlinkToDeath call
+ // because the ExecutionBurstController object is already being destroyed
+ // and its underlying IBurstContext object is no longer being used by the NN
+ // runtime.
+ if (mDeathHandler) {
+ mBurstContext->unlinkToDeath(mDeathHandler).isOk();
+ }
+}
+
+static std::tuple<int, std::vector<V1_2::OutputShape>, V1_2::Timing, bool> getExecutionResult(
+ V1_0::ErrorStatus status, std::vector<V1_2::OutputShape> outputShapes, V1_2::Timing timing,
+ bool fallback) {
+ auto [n, checkedOutputShapes, checkedTiming] =
+ getExecutionResult(convertToV1_3(status), std::move(outputShapes), timing);
+ return {n, convertToV1_2(checkedOutputShapes), convertToV1_2(checkedTiming), fallback};
+}
+
+std::tuple<int, std::vector<V1_2::OutputShape>, V1_2::Timing, bool>
+ExecutionBurstController::compute(const V1_0::Request& request, V1_2::MeasureTiming measure,
+ const std::vector<intptr_t>& memoryIds) {
+ // This is the first point when we know an execution is occurring, so begin
+ // to collect systraces. Note that the first point we can begin collecting
+ // systraces in ExecutionBurstServer is when the RequestChannelReceiver
+ // realizes there is data in the FMQ, so ExecutionBurstServer collects
+ // systraces at different points in the code.
+ NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::compute");
+
+ std::lock_guard<std::mutex> guard(mMutex);
+
+ // send request packet
+ const std::vector<int32_t> slots = mMemoryCache->getSlots(request.pools, memoryIds);
+ const bool success = mRequestChannelSender->send(request, measure, slots);
+ if (!success) {
+ LOG(ERROR) << "Error sending FMQ packet";
+ // only use fallback execution path if the packet could not be sent
+ return getExecutionResult(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming12,
+ /*fallback=*/true);
+ }
+
+ // get result packet
+ const auto result = mResultChannelReceiver->getBlocking();
+ if (!result) {
+ LOG(ERROR) << "Error retrieving FMQ packet";
+ // only use fallback execution path if the packet could not be sent
+ return getExecutionResult(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming12,
+ /*fallback=*/false);
+ }
+
+ // unpack results and return (only use fallback execution path if the
+ // packet could not be sent)
+ auto [status, outputShapes, timing] = std::move(*result);
+ return getExecutionResult(status, std::move(outputShapes), timing, /*fallback=*/false);
+}
+
+void ExecutionBurstController::freeMemory(intptr_t key) {
+ std::lock_guard<std::mutex> guard(mMutex);
+
+ bool valid;
+ int32_t slot;
+ std::tie(valid, slot) = mMemoryCache->freeMemory(key);
+ if (valid) {
+ mBurstContext->freeMemory(slot).isOk();
+ }
+}
+
+} // namespace android::nn
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
new file mode 100644
index 0000000..022548d
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
@@ -0,0 +1,260 @@
+/*
+ * 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 "ExecutionBurstServer"
+
+#include "ExecutionBurstServer.h"
+
+#include <android-base/logging.h>
+
+#include <algorithm>
+#include <cstring>
+#include <limits>
+#include <map>
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "ExecutionBurstUtils.h"
+#include "HalInterfaces.h"
+#include "Tracing.h"
+
+namespace android::nn {
+namespace {
+
+// DefaultBurstExecutorWithCache adapts an IPreparedModel so that it can be
+// used as an IBurstExecutorWithCache. Specifically, the cache simply stores the
+// hidl_memory object, and the execution forwards calls to the provided
+// IPreparedModel's "executeSynchronously" method. With this class, hidl_memory
+// must be mapped and unmapped for each execution.
+class DefaultBurstExecutorWithCache : public ExecutionBurstServer::IBurstExecutorWithCache {
+ public:
+ DefaultBurstExecutorWithCache(V1_2::IPreparedModel* preparedModel)
+ : mpPreparedModel(preparedModel) {}
+
+ bool isCacheEntryPresent(int32_t slot) const override {
+ const auto it = mMemoryCache.find(slot);
+ return (it != mMemoryCache.end()) && it->second.valid();
+ }
+
+ void addCacheEntry(const hardware::hidl_memory& memory, int32_t slot) override {
+ mMemoryCache[slot] = memory;
+ }
+
+ void removeCacheEntry(int32_t slot) override { mMemoryCache.erase(slot); }
+
+ std::tuple<V1_0::ErrorStatus, hardware::hidl_vec<V1_2::OutputShape>, V1_2::Timing> execute(
+ const V1_0::Request& request, const std::vector<int32_t>& slots,
+ V1_2::MeasureTiming measure) override {
+ // convert slots to pools
+ hardware::hidl_vec<hardware::hidl_memory> pools(slots.size());
+ std::transform(slots.begin(), slots.end(), pools.begin(),
+ [this](int32_t slot) { return mMemoryCache[slot]; });
+
+ // create full request
+ V1_0::Request fullRequest = request;
+ fullRequest.pools = std::move(pools);
+
+ // setup execution
+ V1_0::ErrorStatus returnedStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
+ hardware::hidl_vec<V1_2::OutputShape> returnedOutputShapes;
+ V1_2::Timing returnedTiming;
+ auto cb = [&returnedStatus, &returnedOutputShapes, &returnedTiming](
+ V1_0::ErrorStatus status,
+ const hardware::hidl_vec<V1_2::OutputShape>& outputShapes,
+ const V1_2::Timing& timing) {
+ returnedStatus = status;
+ returnedOutputShapes = outputShapes;
+ returnedTiming = timing;
+ };
+
+ // execute
+ const hardware::Return<void> ret =
+ mpPreparedModel->executeSynchronously(fullRequest, measure, cb);
+ if (!ret.isOk() || returnedStatus != V1_0::ErrorStatus::NONE) {
+ LOG(ERROR) << "IPreparedModelAdapter::execute -- Error executing";
+ return {returnedStatus, std::move(returnedOutputShapes), kNoTiming};
+ }
+
+ return std::make_tuple(returnedStatus, std::move(returnedOutputShapes), returnedTiming);
+ }
+
+ private:
+ V1_2::IPreparedModel* const mpPreparedModel;
+ std::map<int32_t, hardware::hidl_memory> mMemoryCache;
+};
+
+} // anonymous namespace
+
+// ExecutionBurstServer methods
+
+sp<ExecutionBurstServer> ExecutionBurstServer::create(
+ const sp<IBurstCallback>& callback, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+ const MQDescriptorSync<FmqResultDatum>& resultChannel,
+ std::shared_ptr<IBurstExecutorWithCache> executorWithCache,
+ std::chrono::microseconds pollingTimeWindow) {
+ // check inputs
+ if (callback == nullptr || executorWithCache == nullptr) {
+ LOG(ERROR) << "ExecutionBurstServer::create passed a nullptr";
+ return nullptr;
+ }
+
+ // create FMQ objects
+ std::unique_ptr<RequestChannelReceiver> requestChannelReceiver =
+ RequestChannelReceiver::create(requestChannel, pollingTimeWindow);
+ std::unique_ptr<ResultChannelSender> resultChannelSender =
+ ResultChannelSender::create(resultChannel);
+
+ // check FMQ objects
+ if (!requestChannelReceiver || !resultChannelSender) {
+ LOG(ERROR) << "ExecutionBurstServer::create failed to create FastMessageQueue";
+ return nullptr;
+ }
+
+ // make and return context
+ return new ExecutionBurstServer(callback, std::move(requestChannelReceiver),
+ std::move(resultChannelSender), std::move(executorWithCache));
+}
+
+sp<ExecutionBurstServer> ExecutionBurstServer::create(
+ const sp<IBurstCallback>& callback, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+ const MQDescriptorSync<FmqResultDatum>& resultChannel, V1_2::IPreparedModel* preparedModel,
+ std::chrono::microseconds pollingTimeWindow) {
+ // check relevant input
+ if (preparedModel == nullptr) {
+ LOG(ERROR) << "ExecutionBurstServer::create passed a nullptr";
+ return nullptr;
+ }
+
+ // adapt IPreparedModel to have caching
+ const std::shared_ptr<DefaultBurstExecutorWithCache> preparedModelAdapter =
+ std::make_shared<DefaultBurstExecutorWithCache>(preparedModel);
+
+ // make and return context
+ return ExecutionBurstServer::create(callback, requestChannel, resultChannel,
+ preparedModelAdapter, pollingTimeWindow);
+}
+
+ExecutionBurstServer::ExecutionBurstServer(
+ const sp<IBurstCallback>& callback, std::unique_ptr<RequestChannelReceiver> requestChannel,
+ std::unique_ptr<ResultChannelSender> resultChannel,
+ std::shared_ptr<IBurstExecutorWithCache> executorWithCache)
+ : mCallback(callback),
+ mRequestChannelReceiver(std::move(requestChannel)),
+ mResultChannelSender(std::move(resultChannel)),
+ mExecutorWithCache(std::move(executorWithCache)) {
+ // TODO: highly document the threading behavior of this class
+ mWorker = std::thread([this] { task(); });
+}
+
+ExecutionBurstServer::~ExecutionBurstServer() {
+ // set teardown flag
+ mTeardown = true;
+ mRequestChannelReceiver->invalidate();
+
+ // wait for task thread to end
+ mWorker.join();
+}
+
+hardware::Return<void> ExecutionBurstServer::freeMemory(int32_t slot) {
+ std::lock_guard<std::mutex> hold(mMutex);
+ mExecutorWithCache->removeCacheEntry(slot);
+ return hardware::Void();
+}
+
+void ExecutionBurstServer::ensureCacheEntriesArePresentLocked(const std::vector<int32_t>& slots) {
+ const auto slotIsKnown = [this](int32_t slot) {
+ return mExecutorWithCache->isCacheEntryPresent(slot);
+ };
+
+ // find unique unknown slots
+ std::vector<int32_t> unknownSlots = slots;
+ auto unknownSlotsEnd = unknownSlots.end();
+ std::sort(unknownSlots.begin(), unknownSlotsEnd);
+ unknownSlotsEnd = std::unique(unknownSlots.begin(), unknownSlotsEnd);
+ unknownSlotsEnd = std::remove_if(unknownSlots.begin(), unknownSlotsEnd, slotIsKnown);
+ unknownSlots.erase(unknownSlotsEnd, unknownSlots.end());
+
+ // quick-exit if all slots are known
+ if (unknownSlots.empty()) {
+ return;
+ }
+
+ V1_0::ErrorStatus errorStatus = V1_0::ErrorStatus::GENERAL_FAILURE;
+ std::vector<hardware::hidl_memory> returnedMemories;
+ auto cb = [&errorStatus, &returnedMemories](
+ V1_0::ErrorStatus status,
+ const hardware::hidl_vec<hardware::hidl_memory>& memories) {
+ errorStatus = status;
+ returnedMemories = memories;
+ };
+
+ const hardware::Return<void> ret = mCallback->getMemories(unknownSlots, cb);
+
+ if (!ret.isOk() || errorStatus != V1_0::ErrorStatus::NONE ||
+ returnedMemories.size() != unknownSlots.size()) {
+ LOG(ERROR) << "Error retrieving memories";
+ return;
+ }
+
+ // add memories to unknown slots
+ for (size_t i = 0; i < unknownSlots.size(); ++i) {
+ mExecutorWithCache->addCacheEntry(returnedMemories[i], unknownSlots[i]);
+ }
+}
+
+void ExecutionBurstServer::task() {
+ // loop until the burst object is being destroyed
+ while (!mTeardown) {
+ // receive request
+ auto arguments = mRequestChannelReceiver->getBlocking();
+
+ // if the request packet was not properly received, return a generic
+ // error and skip the execution
+ //
+ // if the burst is being torn down, skip the execution so the "task"
+ // function can end
+ if (!arguments) {
+ if (!mTeardown) {
+ mResultChannelSender->send(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
+ }
+ continue;
+ }
+
+ // otherwise begin tracing execution
+ NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
+ "ExecutionBurstServer getting memory, executing, and returning results");
+
+ // unpack the arguments; types are Request, std::vector<int32_t>, and
+ // MeasureTiming, respectively
+ const auto [requestWithoutPools, slotsOfPools, measure] = std::move(*arguments);
+
+ // ensure executor with cache has required memory
+ std::lock_guard<std::mutex> hold(mMutex);
+ ensureCacheEntriesArePresentLocked(slotsOfPools);
+
+ // perform computation; types are ErrorStatus, hidl_vec<OutputShape>,
+ // and Timing, respectively
+ const auto [errorStatus, outputShapes, returnedTiming] =
+ mExecutorWithCache->execute(requestWithoutPools, slotsOfPools, measure);
+
+ // return result
+ mResultChannelSender->send(errorStatus, outputShapes, returnedTiming);
+ }
+}
+
+} // namespace android::nn
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
new file mode 100644
index 0000000..f0275f9
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
@@ -0,0 +1,749 @@
+/*
+ * 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 "ExecutionBurstUtils"
+
+#include "ExecutionBurstUtils.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include <atomic>
+#include <chrono>
+#include <memory>
+#include <thread>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+constexpr V1_2::Timing kNoTiming = {std::numeric_limits<uint64_t>::max(),
+ std::numeric_limits<uint64_t>::max()};
+
+}
+
+// serialize a request into a packet
+std::vector<FmqRequestDatum> serialize(const V1_0::Request& request, V1_2::MeasureTiming measure,
+ const std::vector<int32_t>& slots) {
+ // count how many elements need to be sent for a request
+ size_t count = 2 + request.inputs.size() + request.outputs.size() + request.pools.size();
+ for (const auto& input : request.inputs) {
+ count += input.dimensions.size();
+ }
+ for (const auto& output : request.outputs) {
+ count += output.dimensions.size();
+ }
+
+ // create buffer to temporarily store elements
+ std::vector<FmqRequestDatum> data;
+ data.reserve(count);
+
+ // package packetInfo
+ {
+ FmqRequestDatum datum;
+ datum.packetInformation(
+ {/*.packetSize=*/static_cast<uint32_t>(count),
+ /*.numberOfInputOperands=*/static_cast<uint32_t>(request.inputs.size()),
+ /*.numberOfOutputOperands=*/static_cast<uint32_t>(request.outputs.size()),
+ /*.numberOfPools=*/static_cast<uint32_t>(request.pools.size())});
+ data.push_back(datum);
+ }
+
+ // package input data
+ for (const auto& input : request.inputs) {
+ // package operand information
+ FmqRequestDatum datum;
+ datum.inputOperandInformation(
+ {/*.hasNoValue=*/input.hasNoValue,
+ /*.location=*/input.location,
+ /*.numberOfDimensions=*/static_cast<uint32_t>(input.dimensions.size())});
+ data.push_back(datum);
+
+ // package operand dimensions
+ for (uint32_t dimension : input.dimensions) {
+ FmqRequestDatum datum;
+ datum.inputOperandDimensionValue(dimension);
+ data.push_back(datum);
+ }
+ }
+
+ // package output data
+ for (const auto& output : request.outputs) {
+ // package operand information
+ FmqRequestDatum datum;
+ datum.outputOperandInformation(
+ {/*.hasNoValue=*/output.hasNoValue,
+ /*.location=*/output.location,
+ /*.numberOfDimensions=*/static_cast<uint32_t>(output.dimensions.size())});
+ data.push_back(datum);
+
+ // package operand dimensions
+ for (uint32_t dimension : output.dimensions) {
+ FmqRequestDatum datum;
+ datum.outputOperandDimensionValue(dimension);
+ data.push_back(datum);
+ }
+ }
+
+ // package pool identifier
+ for (int32_t slot : slots) {
+ FmqRequestDatum datum;
+ datum.poolIdentifier(slot);
+ data.push_back(datum);
+ }
+
+ // package measureTiming
+ {
+ FmqRequestDatum datum;
+ datum.measureTiming(measure);
+ data.push_back(datum);
+ }
+
+ // return packet
+ return data;
+}
+
+// serialize result
+std::vector<FmqResultDatum> serialize(V1_0::ErrorStatus errorStatus,
+ const std::vector<V1_2::OutputShape>& outputShapes,
+ V1_2::Timing timing) {
+ // count how many elements need to be sent for a request
+ size_t count = 2 + outputShapes.size();
+ for (const auto& outputShape : outputShapes) {
+ count += outputShape.dimensions.size();
+ }
+
+ // create buffer to temporarily store elements
+ std::vector<FmqResultDatum> data;
+ data.reserve(count);
+
+ // package packetInfo
+ {
+ FmqResultDatum datum;
+ datum.packetInformation({/*.packetSize=*/static_cast<uint32_t>(count),
+ /*.errorStatus=*/errorStatus,
+ /*.numberOfOperands=*/static_cast<uint32_t>(outputShapes.size())});
+ data.push_back(datum);
+ }
+
+ // package output shape data
+ for (const auto& operand : outputShapes) {
+ // package operand information
+ FmqResultDatum::OperandInformation info{};
+ info.isSufficient = operand.isSufficient;
+ info.numberOfDimensions = static_cast<uint32_t>(operand.dimensions.size());
+
+ FmqResultDatum datum;
+ datum.operandInformation(info);
+ data.push_back(datum);
+
+ // package operand dimensions
+ for (uint32_t dimension : operand.dimensions) {
+ FmqResultDatum datum;
+ datum.operandDimensionValue(dimension);
+ data.push_back(datum);
+ }
+ }
+
+ // package executionTiming
+ {
+ FmqResultDatum datum;
+ datum.executionTiming(timing);
+ data.push_back(datum);
+ }
+
+ // return result
+ return data;
+}
+
+// deserialize request
+std::optional<std::tuple<V1_0::Request, std::vector<int32_t>, V1_2::MeasureTiming>> deserialize(
+ const std::vector<FmqRequestDatum>& data) {
+ using discriminator = FmqRequestDatum::hidl_discriminator;
+
+ size_t index = 0;
+
+ // validate packet information
+ if (data.size() == 0 || data[index].getDiscriminator() != discriminator::packetInformation) {
+ LOG(ERROR) << "FMQ Request packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage packet information
+ const FmqRequestDatum::PacketInformation& packetInfo = data[index].packetInformation();
+ index++;
+ const uint32_t packetSize = packetInfo.packetSize;
+ const uint32_t numberOfInputOperands = packetInfo.numberOfInputOperands;
+ const uint32_t numberOfOutputOperands = packetInfo.numberOfOutputOperands;
+ const uint32_t numberOfPools = packetInfo.numberOfPools;
+
+ // verify packet size
+ if (data.size() != packetSize) {
+ LOG(ERROR) << "FMQ Request packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage input operands
+ std::vector<V1_0::RequestArgument> inputs;
+ inputs.reserve(numberOfInputOperands);
+ for (size_t operand = 0; operand < numberOfInputOperands; ++operand) {
+ // validate input operand information
+ if (data[index].getDiscriminator() != discriminator::inputOperandInformation) {
+ LOG(ERROR) << "FMQ Request packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage operand information
+ const FmqRequestDatum::OperandInformation& operandInfo =
+ data[index].inputOperandInformation();
+ index++;
+ const bool hasNoValue = operandInfo.hasNoValue;
+ const V1_0::DataLocation location = operandInfo.location;
+ const uint32_t numberOfDimensions = operandInfo.numberOfDimensions;
+
+ // unpackage operand dimensions
+ std::vector<uint32_t> dimensions;
+ dimensions.reserve(numberOfDimensions);
+ for (size_t i = 0; i < numberOfDimensions; ++i) {
+ // validate dimension
+ if (data[index].getDiscriminator() != discriminator::inputOperandDimensionValue) {
+ LOG(ERROR) << "FMQ Request packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage dimension
+ const uint32_t dimension = data[index].inputOperandDimensionValue();
+ index++;
+
+ // store result
+ dimensions.push_back(dimension);
+ }
+
+ // store result
+ inputs.push_back(
+ {/*.hasNoValue=*/hasNoValue, /*.location=*/location, /*.dimensions=*/dimensions});
+ }
+
+ // unpackage output operands
+ std::vector<V1_0::RequestArgument> outputs;
+ outputs.reserve(numberOfOutputOperands);
+ for (size_t operand = 0; operand < numberOfOutputOperands; ++operand) {
+ // validate output operand information
+ if (data[index].getDiscriminator() != discriminator::outputOperandInformation) {
+ LOG(ERROR) << "FMQ Request packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage operand information
+ const FmqRequestDatum::OperandInformation& operandInfo =
+ data[index].outputOperandInformation();
+ index++;
+ const bool hasNoValue = operandInfo.hasNoValue;
+ const V1_0::DataLocation location = operandInfo.location;
+ const uint32_t numberOfDimensions = operandInfo.numberOfDimensions;
+
+ // unpackage operand dimensions
+ std::vector<uint32_t> dimensions;
+ dimensions.reserve(numberOfDimensions);
+ for (size_t i = 0; i < numberOfDimensions; ++i) {
+ // validate dimension
+ if (data[index].getDiscriminator() != discriminator::outputOperandDimensionValue) {
+ LOG(ERROR) << "FMQ Request packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage dimension
+ const uint32_t dimension = data[index].outputOperandDimensionValue();
+ index++;
+
+ // store result
+ dimensions.push_back(dimension);
+ }
+
+ // store result
+ outputs.push_back(
+ {/*.hasNoValue=*/hasNoValue, /*.location=*/location, /*.dimensions=*/dimensions});
+ }
+
+ // unpackage pools
+ std::vector<int32_t> slots;
+ slots.reserve(numberOfPools);
+ for (size_t pool = 0; pool < numberOfPools; ++pool) {
+ // validate input operand information
+ if (data[index].getDiscriminator() != discriminator::poolIdentifier) {
+ LOG(ERROR) << "FMQ Request packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage operand information
+ const int32_t poolId = data[index].poolIdentifier();
+ index++;
+
+ // store result
+ slots.push_back(poolId);
+ }
+
+ // validate measureTiming
+ if (data[index].getDiscriminator() != discriminator::measureTiming) {
+ LOG(ERROR) << "FMQ Request packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage measureTiming
+ const V1_2::MeasureTiming measure = data[index].measureTiming();
+ index++;
+
+ // validate packet information
+ if (index != packetSize) {
+ LOG(ERROR) << "FMQ Result packet ill-formed";
+ return std::nullopt;
+ }
+
+ // return request
+ V1_0::Request request = {/*.inputs=*/inputs, /*.outputs=*/outputs, /*.pools=*/{}};
+ return std::make_tuple(std::move(request), std::move(slots), measure);
+}
+
+// deserialize a packet into the result
+std::optional<std::tuple<V1_0::ErrorStatus, std::vector<V1_2::OutputShape>, V1_2::Timing>>
+deserialize(const std::vector<FmqResultDatum>& data) {
+ using discriminator = FmqResultDatum::hidl_discriminator;
+
+ std::vector<V1_2::OutputShape> outputShapes;
+ size_t index = 0;
+
+ // validate packet information
+ if (data.size() == 0 || data[index].getDiscriminator() != discriminator::packetInformation) {
+ LOG(ERROR) << "FMQ Result packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage packet information
+ const FmqResultDatum::PacketInformation& packetInfo = data[index].packetInformation();
+ index++;
+ const uint32_t packetSize = packetInfo.packetSize;
+ const V1_0::ErrorStatus errorStatus = packetInfo.errorStatus;
+ const uint32_t numberOfOperands = packetInfo.numberOfOperands;
+
+ // verify packet size
+ if (data.size() != packetSize) {
+ LOG(ERROR) << "FMQ Result packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage operands
+ for (size_t operand = 0; operand < numberOfOperands; ++operand) {
+ // validate operand information
+ if (data[index].getDiscriminator() != discriminator::operandInformation) {
+ LOG(ERROR) << "FMQ Result packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage operand information
+ const FmqResultDatum::OperandInformation& operandInfo = data[index].operandInformation();
+ index++;
+ const bool isSufficient = operandInfo.isSufficient;
+ const uint32_t numberOfDimensions = operandInfo.numberOfDimensions;
+
+ // unpackage operand dimensions
+ std::vector<uint32_t> dimensions;
+ dimensions.reserve(numberOfDimensions);
+ for (size_t i = 0; i < numberOfDimensions; ++i) {
+ // validate dimension
+ if (data[index].getDiscriminator() != discriminator::operandDimensionValue) {
+ LOG(ERROR) << "FMQ Result packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage dimension
+ const uint32_t dimension = data[index].operandDimensionValue();
+ index++;
+
+ // store result
+ dimensions.push_back(dimension);
+ }
+
+ // store result
+ outputShapes.push_back({/*.dimensions=*/dimensions, /*.isSufficient=*/isSufficient});
+ }
+
+ // validate execution timing
+ if (data[index].getDiscriminator() != discriminator::executionTiming) {
+ LOG(ERROR) << "FMQ Result packet ill-formed";
+ return std::nullopt;
+ }
+
+ // unpackage execution timing
+ const V1_2::Timing timing = data[index].executionTiming();
+ index++;
+
+ // validate packet information
+ if (index != packetSize) {
+ LOG(ERROR) << "FMQ Result packet ill-formed";
+ return std::nullopt;
+ }
+
+ // return result
+ return std::make_tuple(errorStatus, std::move(outputShapes), timing);
+}
+
+V1_0::ErrorStatus legacyConvertResultCodeToErrorStatus(int resultCode) {
+ return convertToV1_0(convertResultCodeToErrorStatus(resultCode));
+}
+
+// RequestChannelSender methods
+
+std::pair<std::unique_ptr<RequestChannelSender>, const FmqRequestDescriptor*>
+RequestChannelSender::create(size_t channelLength) {
+ std::unique_ptr<FmqRequestChannel> fmqRequestChannel =
+ std::make_unique<FmqRequestChannel>(channelLength, /*confEventFlag=*/true);
+ if (!fmqRequestChannel->isValid()) {
+ LOG(ERROR) << "Unable to create RequestChannelSender";
+ return {nullptr, nullptr};
+ }
+
+ const FmqRequestDescriptor* descriptor = fmqRequestChannel->getDesc();
+ return std::make_pair(std::make_unique<RequestChannelSender>(std::move(fmqRequestChannel)),
+ descriptor);
+}
+
+RequestChannelSender::RequestChannelSender(std::unique_ptr<FmqRequestChannel> fmqRequestChannel)
+ : mFmqRequestChannel(std::move(fmqRequestChannel)) {}
+
+bool RequestChannelSender::send(const V1_0::Request& request, V1_2::MeasureTiming measure,
+ const std::vector<int32_t>& slots) {
+ const std::vector<FmqRequestDatum> serialized = serialize(request, measure, slots);
+ return sendPacket(serialized);
+}
+
+bool RequestChannelSender::sendPacket(const std::vector<FmqRequestDatum>& packet) {
+ if (!mValid) {
+ return false;
+ }
+
+ if (packet.size() > mFmqRequestChannel->availableToWrite()) {
+ LOG(ERROR)
+ << "RequestChannelSender::sendPacket -- packet size exceeds size available in FMQ";
+ return false;
+ }
+
+ // Always send the packet with "blocking" because this signals the futex and
+ // unblocks the consumer if it is waiting on the futex.
+ return mFmqRequestChannel->writeBlocking(packet.data(), packet.size());
+}
+
+void RequestChannelSender::invalidate() {
+ mValid = false;
+}
+
+// RequestChannelReceiver methods
+
+std::unique_ptr<RequestChannelReceiver> RequestChannelReceiver::create(
+ const FmqRequestDescriptor& requestChannel, std::chrono::microseconds pollingTimeWindow) {
+ std::unique_ptr<FmqRequestChannel> fmqRequestChannel =
+ std::make_unique<FmqRequestChannel>(requestChannel);
+
+ if (!fmqRequestChannel->isValid()) {
+ LOG(ERROR) << "Unable to create RequestChannelReceiver";
+ return nullptr;
+ }
+ if (fmqRequestChannel->getEventFlagWord() == nullptr) {
+ LOG(ERROR)
+ << "RequestChannelReceiver::create was passed an MQDescriptor without an EventFlag";
+ return nullptr;
+ }
+
+ return std::make_unique<RequestChannelReceiver>(std::move(fmqRequestChannel),
+ pollingTimeWindow);
+}
+
+RequestChannelReceiver::RequestChannelReceiver(std::unique_ptr<FmqRequestChannel> fmqRequestChannel,
+ std::chrono::microseconds pollingTimeWindow)
+ : mFmqRequestChannel(std::move(fmqRequestChannel)), kPollingTimeWindow(pollingTimeWindow) {}
+
+std::optional<std::tuple<V1_0::Request, std::vector<int32_t>, V1_2::MeasureTiming>>
+RequestChannelReceiver::getBlocking() {
+ const auto packet = getPacketBlocking();
+ if (!packet) {
+ return std::nullopt;
+ }
+
+ return deserialize(*packet);
+}
+
+void RequestChannelReceiver::invalidate() {
+ mTeardown = true;
+
+ // force unblock
+ // ExecutionBurstServer is by default waiting on a request packet. If the
+ // client process destroys its burst object, the server may still be waiting
+ // on the futex. This force unblock wakes up any thread waiting on the
+ // futex.
+ // TODO: look for a different/better way to signal/notify the futex to wake
+ // up any thread waiting on it
+ FmqRequestDatum datum;
+ datum.packetInformation({/*.packetSize=*/0, /*.numberOfInputOperands=*/0,
+ /*.numberOfOutputOperands=*/0, /*.numberOfPools=*/0});
+ mFmqRequestChannel->writeBlocking(&datum, 1);
+}
+
+std::optional<std::vector<FmqRequestDatum>> RequestChannelReceiver::getPacketBlocking() {
+ if (mTeardown) {
+ return std::nullopt;
+ }
+
+ // First spend time polling if results are available in FMQ instead of
+ // waiting on the futex. Polling is more responsive (yielding lower
+ // latencies), but can take up more power, so only poll for a limited period
+ // of time.
+
+ auto& getCurrentTime = std::chrono::high_resolution_clock::now;
+ const auto timeToStopPolling = getCurrentTime() + kPollingTimeWindow;
+
+ while (getCurrentTime() < timeToStopPolling) {
+ // if class is being torn down, immediately return
+ if (mTeardown.load(std::memory_order_relaxed)) {
+ return std::nullopt;
+ }
+
+ // Check if data is available. If it is, immediately retrieve it and
+ // return.
+ const size_t available = mFmqRequestChannel->availableToRead();
+ if (available > 0) {
+ // This is the first point when we know an execution is occurring,
+ // so begin to collect systraces. Note that a similar systrace does
+ // not exist at the corresponding point in
+ // ResultChannelReceiver::getPacketBlocking because the execution is
+ // already in flight.
+ NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
+ "ExecutionBurstServer getting packet");
+ std::vector<FmqRequestDatum> packet(available);
+ const bool success = mFmqRequestChannel->read(packet.data(), available);
+ if (!success) {
+ LOG(ERROR) << "Error receiving packet";
+ return std::nullopt;
+ }
+ return std::make_optional(std::move(packet));
+ }
+ }
+
+ // If we get to this point, we either stopped polling because it was taking
+ // too long or polling was not allowed. Instead, perform a blocking call
+ // which uses a futex to save power.
+
+ // wait for request packet and read first element of request packet
+ FmqRequestDatum datum;
+ bool success = mFmqRequestChannel->readBlocking(&datum, 1);
+
+ // This is the first point when we know an execution is occurring, so begin
+ // to collect systraces. Note that a similar systrace does not exist at the
+ // corresponding point in ResultChannelReceiver::getPacketBlocking because
+ // the execution is already in flight.
+ NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION, "ExecutionBurstServer getting packet");
+
+ // retrieve remaining elements
+ // NOTE: all of the data is already available at this point, so there's no
+ // need to do a blocking wait to wait for more data. This is known because
+ // in FMQ, all writes are published (made available) atomically. Currently,
+ // the producer always publishes the entire packet in one function call, so
+ // if the first element of the packet is available, the remaining elements
+ // are also available.
+ const size_t count = mFmqRequestChannel->availableToRead();
+ std::vector<FmqRequestDatum> packet(count + 1);
+ std::memcpy(&packet.front(), &datum, sizeof(datum));
+ success &= mFmqRequestChannel->read(packet.data() + 1, count);
+
+ // terminate loop
+ if (mTeardown) {
+ return std::nullopt;
+ }
+
+ // ensure packet was successfully received
+ if (!success) {
+ LOG(ERROR) << "Error receiving packet";
+ return std::nullopt;
+ }
+
+ return std::make_optional(std::move(packet));
+}
+
+// ResultChannelSender methods
+
+std::unique_ptr<ResultChannelSender> ResultChannelSender::create(
+ const FmqResultDescriptor& resultChannel) {
+ std::unique_ptr<FmqResultChannel> fmqResultChannel =
+ std::make_unique<FmqResultChannel>(resultChannel);
+
+ if (!fmqResultChannel->isValid()) {
+ LOG(ERROR) << "Unable to create RequestChannelSender";
+ return nullptr;
+ }
+ if (fmqResultChannel->getEventFlagWord() == nullptr) {
+ LOG(ERROR) << "ResultChannelSender::create was passed an MQDescriptor without an EventFlag";
+ return nullptr;
+ }
+
+ return std::make_unique<ResultChannelSender>(std::move(fmqResultChannel));
+}
+
+ResultChannelSender::ResultChannelSender(std::unique_ptr<FmqResultChannel> fmqResultChannel)
+ : mFmqResultChannel(std::move(fmqResultChannel)) {}
+
+bool ResultChannelSender::send(V1_0::ErrorStatus errorStatus,
+ const std::vector<V1_2::OutputShape>& outputShapes,
+ V1_2::Timing timing) {
+ const std::vector<FmqResultDatum> serialized = serialize(errorStatus, outputShapes, timing);
+ return sendPacket(serialized);
+}
+
+bool ResultChannelSender::sendPacket(const std::vector<FmqResultDatum>& packet) {
+ if (packet.size() > mFmqResultChannel->availableToWrite()) {
+ LOG(ERROR)
+ << "ResultChannelSender::sendPacket -- packet size exceeds size available in FMQ";
+ const std::vector<FmqResultDatum> errorPacket =
+ serialize(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
+
+ // Always send the packet with "blocking" because this signals the futex
+ // and unblocks the consumer if it is waiting on the futex.
+ return mFmqResultChannel->writeBlocking(errorPacket.data(), errorPacket.size());
+ }
+
+ // Always send the packet with "blocking" because this signals the futex and
+ // unblocks the consumer if it is waiting on the futex.
+ return mFmqResultChannel->writeBlocking(packet.data(), packet.size());
+}
+
+// ResultChannelReceiver methods
+
+std::pair<std::unique_ptr<ResultChannelReceiver>, const FmqResultDescriptor*>
+ResultChannelReceiver::create(size_t channelLength, std::chrono::microseconds pollingTimeWindow) {
+ std::unique_ptr<FmqResultChannel> fmqResultChannel =
+ std::make_unique<FmqResultChannel>(channelLength, /*confEventFlag=*/true);
+ if (!fmqResultChannel->isValid()) {
+ LOG(ERROR) << "Unable to create ResultChannelReceiver";
+ return {nullptr, nullptr};
+ }
+
+ const FmqResultDescriptor* descriptor = fmqResultChannel->getDesc();
+ return std::make_pair(
+ std::make_unique<ResultChannelReceiver>(std::move(fmqResultChannel), pollingTimeWindow),
+ descriptor);
+}
+
+ResultChannelReceiver::ResultChannelReceiver(std::unique_ptr<FmqResultChannel> fmqResultChannel,
+ std::chrono::microseconds pollingTimeWindow)
+ : mFmqResultChannel(std::move(fmqResultChannel)), kPollingTimeWindow(pollingTimeWindow) {}
+
+std::optional<std::tuple<V1_0::ErrorStatus, std::vector<V1_2::OutputShape>, V1_2::Timing>>
+ResultChannelReceiver::getBlocking() {
+ const auto packet = getPacketBlocking();
+ if (!packet) {
+ return std::nullopt;
+ }
+
+ return deserialize(*packet);
+}
+
+void ResultChannelReceiver::invalidate() {
+ mValid = false;
+
+ // force unblock
+ // ExecutionBurstController waits on a result packet after sending a
+ // request. If the driver containing ExecutionBurstServer crashes, the
+ // controller may be waiting on the futex. This force unblock wakes up any
+ // thread waiting on the futex.
+ // TODO: look for a different/better way to signal/notify the futex to
+ // wake up any thread waiting on it
+ FmqResultDatum datum;
+ datum.packetInformation({/*.packetSize=*/0,
+ /*.errorStatus=*/V1_0::ErrorStatus::GENERAL_FAILURE,
+ /*.numberOfOperands=*/0});
+ mFmqResultChannel->writeBlocking(&datum, 1);
+}
+
+std::optional<std::vector<FmqResultDatum>> ResultChannelReceiver::getPacketBlocking() {
+ if (!mValid) {
+ return std::nullopt;
+ }
+
+ // First spend time polling if results are available in FMQ instead of
+ // waiting on the futex. Polling is more responsive (yielding lower
+ // latencies), but can take up more power, so only poll for a limited period
+ // of time.
+
+ auto& getCurrentTime = std::chrono::high_resolution_clock::now;
+ const auto timeToStopPolling = getCurrentTime() + kPollingTimeWindow;
+
+ while (getCurrentTime() < timeToStopPolling) {
+ // if class is being torn down, immediately return
+ if (!mValid.load(std::memory_order_relaxed)) {
+ return std::nullopt;
+ }
+
+ // Check if data is available. If it is, immediately retrieve it and
+ // return.
+ const size_t available = mFmqResultChannel->availableToRead();
+ if (available > 0) {
+ std::vector<FmqResultDatum> packet(available);
+ const bool success = mFmqResultChannel->read(packet.data(), available);
+ if (!success) {
+ LOG(ERROR) << "Error receiving packet";
+ return std::nullopt;
+ }
+ return std::make_optional(std::move(packet));
+ }
+ }
+
+ // If we get to this point, we either stopped polling because it was taking
+ // too long or polling was not allowed. Instead, perform a blocking call
+ // which uses a futex to save power.
+
+ // wait for result packet and read first element of result packet
+ FmqResultDatum datum;
+ bool success = mFmqResultChannel->readBlocking(&datum, 1);
+
+ // retrieve remaining elements
+ // NOTE: all of the data is already available at this point, so there's no
+ // need to do a blocking wait to wait for more data. This is known because
+ // in FMQ, all writes are published (made available) atomically. Currently,
+ // the producer always publishes the entire packet in one function call, so
+ // if the first element of the packet is available, the remaining elements
+ // are also available.
+ const size_t count = mFmqResultChannel->availableToRead();
+ std::vector<FmqResultDatum> packet(count + 1);
+ std::memcpy(&packet.front(), &datum, sizeof(datum));
+ success &= mFmqResultChannel->read(packet.data() + 1, count);
+
+ if (!mValid) {
+ return std::nullopt;
+ }
+
+ // ensure packet was successfully received
+ if (!success) {
+ LOG(ERROR) << "Error receiving packet";
+ return std::nullopt;
+ }
+
+ return std::make_optional(std::move(packet));
+}
+
+} // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
index 6d00082..6841c5e 100644
--- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -27,6 +27,7 @@
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Burst.h>
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/HandleError.h>
@@ -117,6 +118,10 @@
<< "IPreparedModel::executeFenced is not supported on 1.2 HAL service";
}
+nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
+ return V1_0::utils::Burst::create(shared_from_this());
+}
+
std::any PreparedModel::getUnderlyingResource() const {
sp<V1_2::IPreparedModel> resource = kPreparedModel;
return resource;
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
index 84f606a..f36b6c0 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
@@ -54,6 +54,7 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
+ bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
index 664d87a..690fecc 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
@@ -35,7 +35,8 @@
namespace android::hardware::neuralnetworks::V1_3::utils {
// Class that adapts V1_3::IPreparedModel to nn::IPreparedModel.
-class PreparedModel final : public nn::IPreparedModel {
+class PreparedModel final : public nn::IPreparedModel,
+ public std::enable_shared_from_this<PreparedModel> {
struct PrivateConstructorTag {};
public:
@@ -56,6 +57,8 @@
const nn::OptionalDuration& loopTimeoutDuration,
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+ nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
std::any getUnderlyingResource() const override;
private:
diff --git a/neuralnetworks/1.3/utils/src/Device.cpp b/neuralnetworks/1.3/utils/src/Device.cpp
index d710b85..87c9f32 100644
--- a/neuralnetworks/1.3/utils/src/Device.cpp
+++ b/neuralnetworks/1.3/utils/src/Device.cpp
@@ -150,6 +150,10 @@
return kDeviceType;
}
+bool Device::isUpdatable() const {
+ return false;
+}
+
const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
index 7b4b7ba..725e4f5 100644
--- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -29,6 +29,7 @@
#include <nnapi/Result.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Burst.h>
#include <nnapi/hal/1.2/Conversions.h>
#include <nnapi/hal/CommonUtils.h>
#include <nnapi/hal/HandleError.h>
@@ -197,6 +198,10 @@
return std::make_pair(std::move(syncFence), std::move(callback));
}
+nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
+ return V1_0::utils::Burst::create(shared_from_this());
+}
+
std::any PreparedModel::getUnderlyingResource() const {
sp<V1_3::IPreparedModel> resource = kPreparedModel;
return resource;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
new file mode 100644
index 0000000..83e60b6
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BURST_H
+
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <optional>
+#include <utility>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidBurst final : public nn::IBurst {
+ public:
+ OptionalCacheHold cacheMemory(const nn::Memory& memory) const override;
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+ const nn::Request& request, nn::MeasureTiming measure) const override;
+};
+
+} // namespace android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BURST_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
index 5e62b9a..d843526 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
@@ -32,7 +32,7 @@
class InvalidDevice final : public nn::IDevice {
public:
InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
- nn::DeviceType type, std::vector<nn::Extension> extensions,
+ nn::DeviceType type, bool isUpdatable, std::vector<nn::Extension> extensions,
nn::Capabilities capabilities,
std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded);
@@ -40,6 +40,7 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
+ bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
@@ -70,6 +71,7 @@
const std::string kVersionString;
const nn::Version kFeatureLevel;
const nn::DeviceType kType;
+ const bool kIsUpdatable;
const std::vector<nn::Extension> kExtensions;
const nn::Capabilities kCapabilities;
const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
index 985cddb..3e1dca7 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
@@ -40,6 +40,8 @@
const nn::OptionalDuration& loopTimeoutDuration,
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+ nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
std::any getUnderlyingResource() const override;
};
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
new file mode 100644
index 0000000..0df287f
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_BURST_H
+
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <utility>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class ResilientBurst final : public nn::IBurst,
+ public std::enable_shared_from_this<ResilientBurst> {
+ struct PrivateConstructorTag {};
+
+ public:
+ using Factory = std::function<nn::GeneralResult<nn::SharedBurst>()>;
+
+ static nn::GeneralResult<std::shared_ptr<const ResilientBurst>> create(Factory makeBurst);
+
+ ResilientBurst(PrivateConstructorTag tag, Factory makeBurst, nn::SharedBurst burst);
+
+ nn::SharedBurst getBurst() const;
+ nn::GeneralResult<nn::SharedBurst> recover(const nn::IBurst* failingBurst) const;
+
+ OptionalCacheHold cacheMemory(const nn::Memory& memory) const override;
+
+ nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+ const nn::Request& request, nn::MeasureTiming measure) const override;
+
+ private:
+ const Factory kMakeBurst;
+ mutable std::mutex mMutex;
+ mutable nn::SharedBurst mBurst GUARDED_BY(mMutex);
+};
+
+} // namespace android::hardware::neuralnetworks::utils
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_BURST_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
index 84ae799..8199c52 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientDevice.h
@@ -53,6 +53,7 @@
const std::string& getVersionString() const override;
nn::Version getFeatureLevel() const override;
nn::DeviceType getType() const override;
+ bool isUpdatable() const override;
const std::vector<nn::Extension>& getSupportedExtensions() const override;
const nn::Capabilities& getCapabilities() const override;
std::pair<uint32_t, uint32_t> getNumberOfCacheFilesNeeded() const override;
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
index 9b8d924..a6c1b19 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
@@ -30,7 +30,8 @@
namespace android::hardware::neuralnetworks::utils {
-class ResilientPreparedModel final : public nn::IPreparedModel {
+class ResilientPreparedModel final : public nn::IPreparedModel,
+ public std::enable_shared_from_this<ResilientPreparedModel> {
struct PrivateConstructorTag {};
public:
@@ -57,9 +58,14 @@
const nn::OptionalDuration& loopTimeoutDuration,
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+ nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
std::any getUnderlyingResource() const override;
private:
+ bool isValidInternal() const EXCLUDES(mMutex);
+ nn::GeneralResult<nn::SharedBurst> configureExecutionBurstInternal() const;
+
const Factory kMakePreparedModel;
mutable std::mutex mMutex;
mutable nn::SharedPreparedModel mPreparedModel GUARDED_BY(mMutex);
diff --git a/neuralnetworks/utils/common/src/InvalidBurst.cpp b/neuralnetworks/utils/common/src/InvalidBurst.cpp
new file mode 100644
index 0000000..4ca6603
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidBurst.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "InvalidBurst.h"
+
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <optional>
+#include <utility>
+
+namespace android::hardware::neuralnetworks::utils {
+
+InvalidBurst::OptionalCacheHold InvalidBurst::cacheMemory(const nn::Memory& /*memory*/) const {
+ return nullptr;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> InvalidBurst::execute(
+ const nn::Request& /*request*/, nn::MeasureTiming /*measure*/) const {
+ return NN_ERROR() << "InvalidBurst";
+}
+
+} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidDevice.cpp b/neuralnetworks/utils/common/src/InvalidDevice.cpp
index 535ccb4..81bca7f 100644
--- a/neuralnetworks/utils/common/src/InvalidDevice.cpp
+++ b/neuralnetworks/utils/common/src/InvalidDevice.cpp
@@ -32,13 +32,14 @@
namespace android::hardware::neuralnetworks::utils {
InvalidDevice::InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
- nn::DeviceType type, std::vector<nn::Extension> extensions,
- nn::Capabilities capabilities,
+ nn::DeviceType type, bool isUpdatable,
+ std::vector<nn::Extension> extensions, nn::Capabilities capabilities,
std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded)
: kName(std::move(name)),
kVersionString(std::move(versionString)),
kFeatureLevel(featureLevel),
kType(type),
+ kIsUpdatable(isUpdatable),
kExtensions(std::move(extensions)),
kCapabilities(std::move(capabilities)),
kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded) {}
@@ -59,6 +60,10 @@
return kType;
}
+bool InvalidDevice::isUpdatable() const {
+ return kIsUpdatable;
+}
+
const std::vector<nn::Extension>& InvalidDevice::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
index a46f4ac..9081e1f 100644
--- a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
+++ b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
@@ -42,6 +42,10 @@
return NN_ERROR() << "InvalidPreparedModel";
}
+nn::GeneralResult<nn::SharedBurst> InvalidPreparedModel::configureExecutionBurst() const {
+ return NN_ERROR() << "InvalidPreparedModel";
+}
+
std::any InvalidPreparedModel::getUnderlyingResource() const {
return {};
}
diff --git a/neuralnetworks/utils/common/src/ResilientBurst.cpp b/neuralnetworks/utils/common/src/ResilientBurst.cpp
new file mode 100644
index 0000000..0d3cb33
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientBurst.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 "ResilientBurst.h"
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <utility>
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename FnType>
+auto protect(const ResilientBurst& resilientBurst, const FnType& fn)
+ -> decltype(fn(*resilientBurst.getBurst())) {
+ auto burst = resilientBurst.getBurst();
+ auto result = fn(*burst);
+
+ // Immediately return if burst is not dead.
+ if (result.has_value() || result.error().code != nn::ErrorStatus::DEAD_OBJECT) {
+ return result;
+ }
+
+ // Attempt recovery and return if it fails.
+ auto maybeBurst = resilientBurst.recover(burst.get());
+ if (!maybeBurst.has_value()) {
+ auto [resultErrorMessage, resultErrorCode, resultOutputShapes] = std::move(result).error();
+ const auto& [recoveryErrorMessage, recoveryErrorCode] = maybeBurst.error();
+ return nn::error(resultErrorCode, std::move(resultOutputShapes))
+ << resultErrorMessage << ", and failed to recover dead burst object with error "
+ << recoveryErrorCode << ": " << recoveryErrorMessage;
+ }
+ burst = std::move(maybeBurst).value();
+
+ return fn(*burst);
+}
+
+} // namespace
+
+nn::GeneralResult<std::shared_ptr<const ResilientBurst>> ResilientBurst::create(Factory makeBurst) {
+ if (makeBurst == nullptr) {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "utils::ResilientBurst::create must have non-empty makeBurst";
+ }
+ auto burst = NN_TRY(makeBurst());
+ CHECK(burst != nullptr);
+ return std::make_shared<ResilientBurst>(PrivateConstructorTag{}, std::move(makeBurst),
+ std::move(burst));
+}
+
+ResilientBurst::ResilientBurst(PrivateConstructorTag /*tag*/, Factory makeBurst,
+ nn::SharedBurst burst)
+ : kMakeBurst(std::move(makeBurst)), mBurst(std::move(burst)) {
+ CHECK(kMakeBurst != nullptr);
+ CHECK(mBurst != nullptr);
+}
+
+nn::SharedBurst ResilientBurst::getBurst() const {
+ std::lock_guard guard(mMutex);
+ return mBurst;
+}
+
+nn::GeneralResult<nn::SharedBurst> ResilientBurst::recover(const nn::IBurst* failingBurst) const {
+ std::lock_guard guard(mMutex);
+
+ // Another caller updated the failing burst.
+ if (mBurst.get() != failingBurst) {
+ return mBurst;
+ }
+
+ mBurst = NN_TRY(kMakeBurst());
+ return mBurst;
+}
+
+ResilientBurst::OptionalCacheHold ResilientBurst::cacheMemory(const nn::Memory& memory) const {
+ return getBurst()->cacheMemory(memory);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> ResilientBurst::execute(
+ const nn::Request& request, nn::MeasureTiming measure) const {
+ const auto fn = [&request, measure](const nn::IBurst& burst) {
+ return burst.execute(request, measure);
+ };
+ return protect(*this, fn);
+}
+
+} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientDevice.cpp b/neuralnetworks/utils/common/src/ResilientDevice.cpp
index 2023c9a..13965af 100644
--- a/neuralnetworks/utils/common/src/ResilientDevice.cpp
+++ b/neuralnetworks/utils/common/src/ResilientDevice.cpp
@@ -122,12 +122,14 @@
};
if (compare(&IDevice::getName) || compare(&IDevice::getVersionString) ||
compare(&IDevice::getFeatureLevel) || compare(&IDevice::getType) ||
- compare(&IDevice::getSupportedExtensions) || compare(&IDevice::getCapabilities)) {
+ compare(&IDevice::isUpdatable) || compare(&IDevice::getSupportedExtensions) ||
+ compare(&IDevice::getCapabilities)) {
LOG(ERROR) << "Recovered device has different metadata than what is cached. Marking "
"IDevice object as invalid.";
device = std::make_shared<const InvalidDevice>(
- kName, kVersionString, mDevice->getFeatureLevel(), mDevice->getType(), kExtensions,
- kCapabilities, mDevice->getNumberOfCacheFilesNeeded());
+ kName, kVersionString, mDevice->getFeatureLevel(), mDevice->getType(),
+ mDevice->isUpdatable(), kExtensions, kCapabilities,
+ mDevice->getNumberOfCacheFilesNeeded());
mIsValid = false;
}
@@ -151,6 +153,10 @@
return getDevice()->getType();
}
+bool ResilientDevice::isUpdatable() const {
+ return getDevice()->isUpdatable();
+}
+
const std::vector<nn::Extension>& ResilientDevice::getSupportedExtensions() const {
return kExtensions;
}
diff --git a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
index 667df2b..5dd5f99 100644
--- a/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
+++ b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
@@ -16,6 +16,9 @@
#include "ResilientPreparedModel.h"
+#include "InvalidBurst.h"
+#include "ResilientBurst.h"
+
#include <android-base/logging.h>
#include <android-base/thread_annotations.h>
#include <nnapi/IPreparedModel.h>
@@ -124,8 +127,35 @@
return protect(*this, fn);
}
+nn::GeneralResult<nn::SharedBurst> ResilientPreparedModel::configureExecutionBurst() const {
+#if 0
+ auto self = shared_from_this();
+ ResilientBurst::Factory makeBurst =
+ [preparedModel = std::move(self)]() -> nn::GeneralResult<nn::SharedBurst> {
+ return preparedModel->configureExecutionBurst();
+ };
+ return ResilientBurst::create(std::move(makeBurst));
+#else
+ return configureExecutionBurstInternal();
+#endif
+}
+
std::any ResilientPreparedModel::getUnderlyingResource() const {
return getPreparedModel()->getUnderlyingResource();
}
+bool ResilientPreparedModel::isValidInternal() const {
+ return true;
+}
+
+nn::GeneralResult<nn::SharedBurst> ResilientPreparedModel::configureExecutionBurstInternal() const {
+ if (!isValidInternal()) {
+ return std::make_shared<const InvalidBurst>();
+ }
+ const auto fn = [](const nn::IPreparedModel& preparedModel) {
+ return preparedModel.configureExecutionBurst();
+ };
+ return protect(*this, fn);
+}
+
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/test/MockDevice.h b/neuralnetworks/utils/common/test/MockDevice.h
index 08cd5c5..5566968 100644
--- a/neuralnetworks/utils/common/test/MockDevice.h
+++ b/neuralnetworks/utils/common/test/MockDevice.h
@@ -29,6 +29,7 @@
MOCK_METHOD(const std::string&, getVersionString, (), (const, override));
MOCK_METHOD(Version, getFeatureLevel, (), (const, override));
MOCK_METHOD(DeviceType, getType, (), (const, override));
+ MOCK_METHOD(bool, isUpdatable, (), (const, override));
MOCK_METHOD(const std::vector<Extension>&, getSupportedExtensions, (), (const, override));
MOCK_METHOD(const Capabilities&, getCapabilities, (), (const, override));
MOCK_METHOD((std::pair<uint32_t, uint32_t>), getNumberOfCacheFilesNeeded, (),
diff --git a/neuralnetworks/utils/common/test/MockPreparedModel.h b/neuralnetworks/utils/common/test/MockPreparedModel.h
index 928508e..418af61 100644
--- a/neuralnetworks/utils/common/test/MockPreparedModel.h
+++ b/neuralnetworks/utils/common/test/MockPreparedModel.h
@@ -35,6 +35,7 @@
const OptionalDuration& loopTimeoutDuration,
const OptionalDuration& timeoutDurationAfterFence),
(const, override));
+ MOCK_METHOD(GeneralResult<SharedBurst>, configureExecutionBurst, (), (const, override));
MOCK_METHOD(std::any, getUnderlyingResource, (), (const, override));
};
diff --git a/oemlock/aidl/vts/OWNERS b/oemlock/aidl/vts/OWNERS
new file mode 100644
index 0000000..40d95e4
--- /dev/null
+++ b/oemlock/aidl/vts/OWNERS
@@ -0,0 +1,2 @@
+chengyouho@google.com
+frankwoo@google.com
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
index 7166654..0b49b36 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
@@ -236,7 +236,12 @@
ALOGI("setSignalStrengthReportingCriteria_1_5_NGRAN_SSRSRP, rspInfo.error = %s\n",
toString(radioRsp_v1_5->rspInfo.error).c_str());
- ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error, {RadioError::NONE}));
+
+ // Allow REQUEST_NOT_SUPPORTED because some non-5G device may not support NGRAN for
+ // setSignalStrengthReportingCriteria_1_5()
+ ASSERT_TRUE(
+ CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+ {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED}));
}
/*
@@ -261,7 +266,12 @@
ALOGI("setSignalStrengthReportingCriteria_1_5_NGRAN_SSRSRQ, rspInfo.error = %s\n",
toString(radioRsp_v1_5->rspInfo.error).c_str());
- ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error, {RadioError::NONE}));
+
+ // Allow REQUEST_NOT_SUPPORTED because some non-5G device may not support NGRAN for
+ // setSignalStrengthReportingCriteria_1_5()
+ ASSERT_TRUE(
+ CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+ {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED}));
}
/*
@@ -307,7 +317,12 @@
ALOGI("setSignalStrengthReportingCriteria_1_5_NGRAN_SSSINR, rspInfo.error = %s\n",
toString(radioRsp_v1_5->rspInfo.error).c_str());
- ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error, {RadioError::NONE}));
+
+ // Allow REQUEST_NOT_SUPPORTED because some non-5G device may not support NGRAN for
+ // setSignalStrengthReportingCriteria_1_5()
+ ASSERT_TRUE(
+ CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+ {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED}));
}
/*
diff --git a/radio/1.6/IRadioIndication.hal b/radio/1.6/IRadioIndication.hal
index 1b56d40..a53d7c1 100644
--- a/radio/1.6/IRadioIndication.hal
+++ b/radio/1.6/IRadioIndication.hal
@@ -23,6 +23,7 @@
import @1.6::NetworkScanResult;
import @1.6::SignalStrength;
import @1.6::SetupDataCallResult;
+import @1.6::PhysicalChannelConfig;
/**
* Interface declaring unsolicited radio indications.
@@ -101,4 +102,15 @@
* CellInfo.
*/
oneway networkScanResult_1_6(RadioIndicationType type, NetworkScanResult result);
+
+ /**
+ * Indicates physical channel configurations.
+ *
+ * An empty configs list indicates that the radio is in idle mode.
+ *
+ * @param type Type of radio indication
+ * @param configs Vector of PhysicalChannelConfigs
+ */
+ oneway currentPhysicalChannelConfigs_1_6(RadioIndicationType type,
+ vec<PhysicalChannelConfig> configs);
};
diff --git a/radio/1.6/types.hal b/radio/1.6/types.hal
index acd0e08..6dd8315 100644
--- a/radio/1.6/types.hal
+++ b/radio/1.6/types.hal
@@ -23,7 +23,10 @@
import @1.0::RadioError;
import @1.0::RadioResponseType;
import @1.0::RegState;
+import @1.1::EutranBands;
+import @1.1::GeranBands;
import @1.1::ScanStatus;
+import @1.1::UtranBands;
import @1.2::Call;
import @1.2::CellInfoCdma;
import @1.2::CellConnectionStatus;
@@ -41,6 +44,7 @@
import @1.5::CellInfoWcdma;
import @1.5::CellInfoTdscdma;
import @1.5::LinkAddress;
+import @1.5::NgranBands;
import @1.5::RegStateResult.AccessTechnologySpecificInfo.Cdma2000RegistrationInfo;
import @1.5::RegStateResult.AccessTechnologySpecificInfo.EutranRegistrationInfo;
import @1.5::RegistrationFailCause;
@@ -733,3 +737,70 @@
*/
string forwardedNumber;
};
+
+struct PhysicalChannelConfig {
+ /** Connection status for cell. Valid values are PRIMARY_SERVING and SECONDARY_SERVING */
+ CellConnectionStatus status;
+
+ /** The radio technology for this physical channel */
+ RadioTechnology rat;
+
+ /** Downlink Absolute Radio Frequency Channel Number */
+ int32_t downlinkChannelNumber;
+
+ /** Uplink Absolute Radio Frequency Channel Number */
+ int32_t uplinkChannelNumber;
+
+ /** Downlink cell bandwidth, in kHz */
+ int32_t cellBandwidthDownlink;
+
+ /** Uplink cell bandwidth, in kHz */
+ int32_t cellBandwidthUplink;
+
+ /**
+ * A list of data calls mapped to this physical channel. The context id must match the cid of
+ * @1.5::SetupDataCallResult. An empty list means the physical channel has no data call mapped
+ * to it.
+ */
+ vec<int32_t> contextIds;
+
+ /**
+ * The physical cell identifier for this cell.
+ *
+ * In UTRAN, this value is primary scrambling code. The range is [0, 511].
+ * Reference: 3GPP TS 25.213 section 5.2.2.
+ *
+ * In EUTRAN, this value is physical layer cell identity. The range is [0, 503].
+ * Reference: 3GPP TS 36.211 section 6.11.
+ *
+ * In 5G RAN, this value is physical layer cell identity. The range is [0, 1007].
+ * Reference: 3GPP TS 38.211 section 7.4.2.1.
+ */
+ uint32_t physicalCellId;
+
+ /**
+ * The frequency band to scan.
+ */
+ safe_union Band {
+ /** Valid only if radioAccessNetwork = GERAN. */
+ GeranBands geranBand;
+ /** Valid only if radioAccessNetwork = UTRAN. */
+ UtranBands utranBand;
+ /** Valid only if radioAccessNetwork = EUTRAN. */
+ EutranBands eutranBand;
+ /** Valid only if radioAccessNetwork = NGRAN. */
+ NgranBands ngranBand;
+ } band;
+};
+
+/**
+ * Extended from @1.5 NgranBands
+ * IRadio 1.6 supports NGRAN bands up to V16.5.0
+ */
+enum NgranBands : @1.5::NgranBands {
+ /** 3GPP TS 38.101-1, Table 5.2-1: FR1 bands */
+ BAND_26 = 26,
+ BAND_46 = 46,
+ BAND_53 = 53,
+ BAND_96 = 96,
+};
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
index fbcd7a9..5fcfa3b 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
+++ b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
@@ -857,6 +857,11 @@
const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::CellInfo>&
records);
+ Return<void> currentPhysicalChannelConfigs_1_6(
+ RadioIndicationType type,
+ const ::android::hardware::hidl_vec<
+ ::android::hardware::radio::V1_6::PhysicalChannelConfig>& configs);
+
/* 1.5 Api */
Return<void> uiccApplicationsEnablementChanged(RadioIndicationType type, bool enabled);
diff --git a/radio/1.6/vts/functional/radio_indication.cpp b/radio/1.6/vts/functional/radio_indication.cpp
index bfc54c0..e7a9680 100644
--- a/radio/1.6/vts/functional/radio_indication.cpp
+++ b/radio/1.6/vts/functional/radio_indication.cpp
@@ -30,6 +30,13 @@
return Void();
}
+Return<void> RadioIndication_v1_6::currentPhysicalChannelConfigs_1_6(
+ RadioIndicationType /*type*/,
+ const ::android::hardware::hidl_vec<
+ ::android::hardware::radio::V1_6::PhysicalChannelConfig>& /*configs*/) {
+ return Void();
+}
+
/* 1.5 Apis */
Return<void> RadioIndication_v1_6::uiccApplicationsEnablementChanged(RadioIndicationType /*type*/,
bool /*enabled*/) {
diff --git a/radio/config/1.3/Android.bp b/radio/config/1.3/Android.bp
new file mode 100644
index 0000000..ace0de9
--- /dev/null
+++ b/radio/config/1.3/Android.bp
@@ -0,0 +1,21 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.radio.config@1.3",
+ root: "android.hardware",
+ srcs: [
+ "types.hal",
+ "IRadioConfig.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.6",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+ system_ext_specific: true,
+}
diff --git a/radio/config/1.3/IRadioConfig.hal b/radio/config/1.3/IRadioConfig.hal
new file mode 100644
index 0000000..83bcf92
--- /dev/null
+++ b/radio/config/1.3/IRadioConfig.hal
@@ -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.
+ *
+ *
+ * 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.
+ */
+
+package android.hardware.radio.config@1.3;
+
+import @1.1::IRadioConfig;
+import IRadioConfigResponse;
+
+interface IRadioConfig extends @1.1::IRadioConfig {
+ /**
+ * Gets the available Radio Hal capabilities on the current device.
+ *
+ * This is called once per device boot up.
+ *
+ * @param serial Serial number of request
+ *
+ * Response callback is
+ * IRadioConfigResponse.getHalDeviceCapabilitiesResponse()
+ */
+ oneway getHalDeviceCapabilities(int32_t serial);
+};
diff --git a/radio/config/1.3/IRadioConfigResponse.hal b/radio/config/1.3/IRadioConfigResponse.hal
new file mode 100644
index 0000000..863754f
--- /dev/null
+++ b/radio/config/1.3/IRadioConfigResponse.hal
@@ -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.
+ */
+
+package android.hardware.radio.config@1.3;
+
+import android.hardware.radio@1.6::RadioResponseInfo;
+import @1.2::IRadioConfigResponse;
+import HalDeviceCapabilities;
+
+/**
+ * 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 capabilities Capabilities struct containing the capabilities of the
+ * device related to the Radio HAL
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:INTERNAL_ERR
+ */
+ oneway getHalDeviceCapabilitiesResponse(RadioResponseInfo info,
+ HalDeviceCapabilities capabilities);
+};
diff --git a/health/1.0/default/libhealthd/healthd_board_default.cpp b/radio/config/1.3/types.hal
similarity index 63%
copy from health/1.0/default/libhealthd/healthd_board_default.cpp
copy to radio/config/1.3/types.hal
index 127f98e..bedb709 100644
--- a/health/1.0/default/libhealthd/healthd_board_default.cpp
+++ b/radio/config/1.3/types.hal
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 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,15 +14,9 @@
* limitations under the License.
*/
-#include <healthd/healthd.h>
+package android.hardware.radio.config@1.3;
-void healthd_board_init(struct healthd_config*)
-{
- // use defaults
-}
-
-int healthd_board_battery_update(struct android::BatteryProperties*)
-{
- // return 0 to log periodic polled battery status to kernel log
- return 0;
-}
+/**
+ * Contains the device capabilities with respect to the Radio HAL.
+ */
+struct HalDeviceCapabilities {};
diff --git a/radio/config/1.3/vts/functional/Android.bp b/radio/config/1.3/vts/functional/Android.bp
new file mode 100644
index 0000000..abd081f
--- /dev/null
+++ b/radio/config/1.3/vts/functional/Android.bp
@@ -0,0 +1,39 @@
+//
+// 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",
+ "radio_config_indication.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",
+ ],
+}
diff --git a/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp b/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
new file mode 100644
index 0000000..5772d08
--- /dev/null
+++ b/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioConfigHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, RadioConfigHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(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
new file mode 100644
index 0000000..8df02dd
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+
+/*
+ * Test IRadioConfig.getHalDeviceCapabilities()
+ */
+TEST_P(RadioConfigHidlTest, getHalDeviceCapabilities) {
+ const int serial = GetRandomSerialNumber();
+ Return<void> res = radioConfig->getHalDeviceCapabilities(serial);
+ ASSERT_OK(res);
+ ALOGI("getHalDeviceCapabilities, rspInfo.error = %s\n",
+ toString(radioConfigRsp->rspInfo.error).c_str());
+}
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
new file mode 100644
index 0000000..de8365a
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+void RadioConfigHidlTest::SetUp() {
+ radioConfig = IRadioConfig::getService(GetParam());
+ if (radioConfig == NULL) {
+ sleep(60);
+ radioConfig = 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
new file mode 100644
index 0000000..439eb70
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#include <android/hardware/radio/config/1.1/IRadioConfig.h>
+#include <android/hardware/radio/config/1.1/types.h>
+#include <android/hardware/radio/config/1.2/IRadioConfigIndication.h>
+#include <android/hardware/radio/config/1.2/IRadioConfigResponse.h>
+#include <android/hardware/radio/config/1.2/types.h>
+#include <android/hardware/radio/config/1.3/IRadioConfig.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::V1_2;
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::radio::config::V1_1::ModemsConfig;
+using ::android::hardware::radio::config::V1_1::PhoneCapability;
+using ::android::hardware::radio::config::V1_2::SimSlotStatus;
+using ::android::hardware::radio::config::V1_3::HalDeviceCapabilities;
+using ::android::hardware::radio::config::V1_3::IRadioConfig;
+using ::android::hardware::radio::V1_0::RadioResponseInfo;
+
+#define TIMEOUT_PERIOD 75
+#define RADIO_SERVICE_NAME "slot1"
+
+class RadioConfigHidlTest;
+
+/* Callback class for radio config response */
+class RadioConfigResponse : public IRadioConfigResponse {
+ protected:
+ RadioConfigHidlTest& parent;
+
+ public:
+ RadioResponseInfo rspInfo;
+ PhoneCapability phoneCap;
+
+ RadioConfigResponse(RadioConfigHidlTest& parent);
+ virtual ~RadioConfigResponse() = default;
+
+ Return<void> getSimSlotsStatusResponse(
+ const RadioResponseInfo& info,
+ const ::android::hardware::hidl_vec<
+ ::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus);
+
+ Return<void> getSimSlotsStatusResponse_1_2(
+ const RadioResponseInfo& info,
+ const ::android::hardware::hidl_vec<SimSlotStatus>& slotStatus);
+
+ Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info);
+
+ Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
+ const PhoneCapability& phoneCapability);
+
+ Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info);
+
+ Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
+ const ModemsConfig& mConfig);
+
+ Return<void> setModemsConfigResponse(const RadioResponseInfo& info);
+
+ Return<void> getHalDeviceCapabilitiesResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+ const HalDeviceCapabilities& halDeviceCapabilities);
+};
+
+/* Callback class for radio config indication */
+class RadioConfigIndication : public IRadioConfigIndication {
+ protected:
+ RadioConfigHidlTest& parent;
+
+ public:
+ RadioConfigIndication(RadioConfigHidlTest& parent);
+ virtual ~RadioConfigIndication() = default;
+
+ Return<void> simSlotsStatusChanged_1_2(
+ ::android::hardware::radio::V1_0::RadioIndicationType type,
+ const ::android::hardware::hidl_vec<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<IRadioConfig> radioConfig;
+
+ /* radio config response handle */
+ sp<RadioConfigResponse> radioConfigRsp;
+};
diff --git a/radio/config/1.3/vts/functional/radio_config_indication.cpp b/radio/config/1.3/vts/functional/radio_config_indication.cpp
new file mode 100644
index 0000000..6fa443c
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_indication.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <radio_config_hidl_hal_utils.h>
+
+RadioConfigIndication::RadioConfigIndication(RadioConfigHidlTest& parent) : parent(parent) {}
+
+Return<void> RadioConfigIndication::simSlotsStatusChanged_1_2(
+ ::android::hardware::radio::V1_0::RadioIndicationType /*type*/,
+ const ::android::hardware::hidl_vec<SimSlotStatus>& /*slotStatus*/) {
+ return Void();
+}
diff --git a/radio/config/1.3/vts/functional/radio_config_response.cpp b/radio/config/1.3/vts/functional/radio_config_response.cpp
new file mode 100644
index 0000000..2a8b28b
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_response.cpp
@@ -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.
+ */
+
+#include <radio_config_hidl_hal_utils.h>
+
+// SimSlotStatus slotStatus;
+
+RadioConfigResponse::RadioConfigResponse(RadioConfigHidlTest& parent) : parent(parent) {}
+
+Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+ const ::android::hardware::hidl_vec<
+ ::android::hardware::radio::config::V1_0::SimSlotStatus>& /* slotStatus */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+ const ::android::hardware::hidl_vec<SimSlotStatus>& /* slotStatus */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::setSimSlotsMappingResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+ const PhoneCapability& phoneCapability) {
+ rspInfo = info;
+ phoneCap = phoneCapability;
+ parent.notify(info.serial);
+ return Void();
+}
+
+Return<void> RadioConfigResponse::setPreferredDataModemResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::getModemsConfigResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+ const ModemsConfig& /* mConfig */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::setModemsConfigResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+ return Void();
+}
+
+Return<void> RadioConfigResponse::getHalDeviceCapabilitiesResponse(
+ const ::android::hardware::radio::V1_6::RadioResponseInfo& /* info */,
+ const ::android::hardware::radio::config::V1_3::HalDeviceCapabilities& /* capabilities */) {
+ return Void();
+}
\ No newline at end of file
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
index b5adac9..5652827 100644
--- a/security/keymint/aidl/Android.bp
+++ b/security/keymint/aidl/Android.bp
@@ -4,6 +4,9 @@
srcs: [
"android/hardware/security/keymint/*.aidl",
],
+ imports: [
+ "android.hardware.security.secureclock",
+ ],
stability: "vintf",
backend: {
java: {
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
index 0d43d8d..ad5bf39 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
@@ -23,6 +23,6 @@
long userId;
long authenticatorId;
android.hardware.security.keymint.HardwareAuthenticatorType authenticatorType;
- android.hardware.security.keymint.Timestamp timestamp;
+ android.hardware.security.secureclock.Timestamp timestamp;
byte[] mac;
}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
index 07c2844..c95145d 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -20,7 +20,6 @@
@VintfStability
interface IKeyMintDevice {
android.hardware.security.keymint.KeyMintHardwareInfo getHardwareInfo();
- android.hardware.security.keymint.VerificationToken verifyAuthorization(in long challenge, in android.hardware.security.keymint.HardwareAuthToken token);
void addRngEntropy(in byte[] data);
android.hardware.security.keymint.KeyCreationResult generateKey(in android.hardware.security.keymint.KeyParameter[] keyParams);
android.hardware.security.keymint.KeyCreationResult importKey(in android.hardware.security.keymint.KeyParameter[] keyParams, in android.hardware.security.keymint.KeyFormat keyFormat, in byte[] keyData);
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
index 08aa00a..e6ab4c8 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -19,7 +19,7 @@
package android.hardware.security.keymint;
@VintfStability
interface IKeyMintOperation {
- int update(in @nullable android.hardware.security.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken inAuthToken, in @nullable android.hardware.security.keymint.VerificationToken inVerificationToken, out @nullable android.hardware.security.keymint.KeyParameterArray outParams, out @nullable android.hardware.security.keymint.ByteArray output);
- byte[] finish(in @nullable android.hardware.security.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable byte[] inSignature, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.keymint.VerificationToken inVerificationToken, out @nullable android.hardware.security.keymint.KeyParameterArray outParams);
+ int update(in @nullable android.hardware.security.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken inAuthToken, in @nullable android.hardware.security.secureclock.TimeStampToken inTimeStampToken, out @nullable android.hardware.security.keymint.KeyParameterArray outParams, out @nullable android.hardware.security.keymint.ByteArray output);
+ byte[] finish(in @nullable android.hardware.security.keymint.KeyParameterArray inParams, in @nullable byte[] input, in @nullable byte[] inSignature, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.secureclock.TimeStampToken inTimeStampToken, out @nullable android.hardware.security.keymint.KeyParameterArray outParams);
void abort();
}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/VerificationToken.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/VerificationToken.aidl
deleted file mode 100644
index 7dc556c..0000000
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/VerificationToken.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a snapshot of an AIDL file. Do not edit it manually. There are
-// two cases:
-// 1). this is a frozen version file - do not edit this in any case.
-// 2). this is a 'current' file. If you make a backwards compatible change to
-// the interface (from the latest frozen version), the build system will
-// prompt you to update this file with `m <name>-update-api`.
-//
-// You must not make a backward incompatible change to any AIDL file built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.hardware.security.keymint;
-@VintfStability
-parcelable VerificationToken {
- long challenge;
- android.hardware.security.keymint.Timestamp timestamp;
- android.hardware.security.keymint.SecurityLevel securityLevel;
- byte[] mac;
-}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
index 12d615f..1067540 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
@@ -16,7 +16,7 @@
package android.hardware.security.keymint;
-import android.hardware.security.keymint.Timestamp;
+import android.hardware.security.secureclock.Timestamp;
import android.hardware.security.keymint.HardwareAuthenticatorType;
/**
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 820e135..d5f7a1f 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -26,7 +26,7 @@
import android.hardware.security.keymint.KeyMintHardwareInfo;
import android.hardware.security.keymint.KeyPurpose;
import android.hardware.security.keymint.SecurityLevel;
-import android.hardware.security.keymint.VerificationToken;
+import android.hardware.security.secureclock.TimeStampToken;
/**
* KeyMint device definition.
@@ -223,34 +223,6 @@
KeyMintHardwareInfo getHardwareInfo();
/**
- * Verify authorizations for another IKeyMintDevice instance.
- *
- * On systems with both a StrongBox and a TEE IKeyMintDevice instance it is sometimes useful
- * to ask the TEE KeyMintDevice to verify authorizations for a key hosted in StrongBox.
- *
- * For every StrongBox operation, Keystore is required to call this method on the TEE KeyMint,
- * passing in the StrongBox key's hardwareEnforced authorization list and the challenge
- * returned by StrongBox begin(). Keystore must then pass the VerificationToken to the
- * subsequent invocations of StrongBox update() and finish().
- *
- * StrongBox implementations must return ErrorCode::UNIMPLEMENTED.
- *
- * @param the challenge returned by StrongBox's keyMint's begin().
- *
- * @param authToken A HardwareAuthToken if needed to authorize key usage.
- *
- * @return error ErrorCode::OK on success or ErrorCode::UNIMPLEMENTED if the KeyMintDevice is
- * a StrongBox. If the IKeyMintDevice cannot verify one or more elements of
- * parametersToVerify it must not return an error code, but just omit the unverified
- * parameter from the VerificationToken.
- *
- * @return token the verification token. See VerificationToken in VerificationToken.aidl for
- * details.
- */
- VerificationToken verifyAuthorization(in long challenge,
- in HardwareAuthToken token);
-
- /**
* Adds entropy to the RNG used by KeyMint. Entropy added through this method must not be the
* only source of entropy used, and a secure mixing function must be used to mix the entropy
* provided by this method with internally-generated entropy. The mixing function must be
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
index 24960cc..8c49602 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -20,7 +20,7 @@
import android.hardware.security.keymint.HardwareAuthToken;
import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.KeyParameterArray;
-import android.hardware.security.keymint.VerificationToken;
+import android.hardware.security.secureclock.TimeStampToken;
@VintfStability
interface IKeyMintOperation {
@@ -119,10 +119,9 @@
* @param input Data to be processed. Note that update() may or may not consume all of the data
* provided. See return value.
*
- * @param verificationToken Verification token, used to prove that another IKeymasterDevice HAL
- * has verified some parameters, and to deliver the other HAL's current timestamp, if
- * needed. If not provided, all fields must be initialized to zero and vectors must be
- * empty.
+ * @param inTimeStampToken timestamp token, certifies the freshness of an auth token in case
+ * the security domain of this KeyMint instance has a different clock than the
+ * authenticator issuing the auth token.
*
* @return error Returns ErrorCode encountered in keymint as service specific errors. See the
* ErrorCode enum in ErrorCode.aidl.
@@ -141,7 +140,7 @@
int update(in @nullable KeyParameterArray inParams,
in @nullable byte[] input,
in @nullable HardwareAuthToken inAuthToken,
- in @nullable VerificationToken inVerificationToken,
+ in @nullable TimeStampToken inTimeStampToken,
out @nullable KeyParameterArray outParams,
out @nullable ByteArray output);
@@ -241,9 +240,9 @@
*
* @param authToken Authentication token. Can be nullable if not provided.
*
- * @param verificationToken Verification token, used to prove that another IKeyMintDevice HAL
- * has verified some parameters, and to deliver the other HAL's current timestamp, if
- * needed. Can be nullable if not needed.
+ * @param inTimeStampToken timestamp token, certifies the freshness of an auth token in case
+ * the security domain of this KeyMint instance has a different clock than the
+ * authenticator issuing the auth token.
*
* @return outParams Any output parameters generated by finish().
*
@@ -252,7 +251,7 @@
byte[] finish(in @nullable KeyParameterArray inParams, in @nullable byte[] input,
in @nullable byte[] inSignature,
in @nullable HardwareAuthToken authToken,
- in @nullable VerificationToken inVerificationToken,
+ in @nullable TimeStampToken inTimeStampToken,
out @nullable KeyParameterArray outParams);
/**
diff --git a/security/keymint/aidl/android/hardware/security/keymint/VerificationToken.aidl b/security/keymint/aidl/android/hardware/security/keymint/VerificationToken.aidl
deleted file mode 100644
index f76e6a8..0000000
--- a/security/keymint/aidl/android/hardware/security/keymint/VerificationToken.aidl
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.security.keymint;
-
-import android.hardware.security.keymint.SecurityLevel;
-import android.hardware.security.keymint.Timestamp;
-
-/**
- * VerificationToken instances are used for secure environments to authenticate one another.
- *
- * This version of the parcelable currently don't use the parametersVerified field since it's not
- * needed for time-based verification. This can be added in a later version, if needed.
- */
-@VintfStability
-parcelable VerificationToken {
- /**
- * The operation handle, used to ensure freshness.
- */
- long challenge;
-
- /**
- * The current time of the secure environment that generates the VerificationToken. This can be
- * checked against auth tokens generated by the same secure environment, which avoids needing to
- * synchronize clocks.
- */
- Timestamp timestamp;
-
- /**
- * SecurityLevel of the secure environment that generated the token.
- */
- SecurityLevel securityLevel;
-
- /**
- * 32-byte HMAC-SHA256 of the above values, computed as:
- *
- * HMAC(H,
- * "Auth Verification" || challenge || timestamp || securityLevel)
- *
- * where:
- *
- * ``HMAC'' is the shared HMAC key (see computeSharedHmac() in IKeyMint).
- *
- * ``||'' represents concatenation
- *
- * The representation of challenge and timestamp is as 64-bit unsigned integers in big-endian
- * order. securityLevel is represented as a 32-bit unsigned integer in big-endian order.
- */
- byte[] mac;
-}
diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp
index c7cc380..17a4613 100644
--- a/security/keymint/aidl/vts/functional/Android.bp
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -22,7 +22,6 @@
],
srcs: [
"KeyMintTest.cpp",
- "VerificationTokenTest.cpp",
],
shared_libs: [
"libbinder_ndk",
@@ -32,6 +31,7 @@
],
static_libs: [
"android.hardware.security.keymint-unstable-ndk_platform",
+ "android.hardware.security.secureclock-unstable-ndk_platform",
"libcppbor_external",
"libkeymint_vts_test_utils",
],
@@ -61,6 +61,7 @@
],
static_libs: [
"android.hardware.security.keymint-unstable-ndk_platform",
+ "android.hardware.security.secureclock-unstable-ndk_platform",
"libcppbor",
],
}
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 93a216f..766c02d 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -37,7 +37,7 @@
os << "(Empty)" << ::std::endl;
else {
os << "\n";
- for (size_t i = 0; i < set.size(); ++i) os << set[i] << ::std::endl;
+ for (auto& entry : set) os << entry << ::std::endl;
}
return os;
}
@@ -131,6 +131,17 @@
*key_blob = std::move(creationResult.keyBlob);
*key_characteristics = std::move(creationResult.keyCharacteristics);
cert_chain_ = std::move(creationResult.certificateChain);
+
+ auto algorithm = key_desc.GetTagValue(TAG_ALGORITHM);
+ EXPECT_TRUE(algorithm);
+ if (algorithm &&
+ (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC)) {
+ EXPECT_GE(cert_chain_.size(), 1);
+ if (key_desc.Contains(TAG_ATTESTATION_CHALLENGE)) EXPECT_GT(cert_chain_.size(), 1);
+ } else {
+ // For symmetric keys there should be no certificates.
+ EXPECT_EQ(cert_chain_.size(), 0);
+ }
}
return GetReturnErrorCode(result);
@@ -162,6 +173,17 @@
*key_blob = std::move(creationResult.keyBlob);
*key_characteristics = std::move(creationResult.keyCharacteristics);
cert_chain_ = std::move(creationResult.certificateChain);
+
+ auto algorithm = key_desc.GetTagValue(TAG_ALGORITHM);
+ EXPECT_TRUE(algorithm);
+ if (algorithm &&
+ (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC)) {
+ EXPECT_GE(cert_chain_.size(), 1);
+ if (key_desc.Contains(TAG_ATTESTATION_CHALLENGE)) EXPECT_GT(cert_chain_.size(), 1);
+ } else {
+ // For symmetric keys there should be no certificates.
+ EXPECT_EQ(cert_chain_.size(), 0);
+ }
}
return GetReturnErrorCode(result);
@@ -195,6 +217,20 @@
key_blob_ = std::move(creationResult.keyBlob);
key_characteristics_ = std::move(creationResult.keyCharacteristics);
cert_chain_ = std::move(creationResult.certificateChain);
+
+ AuthorizationSet allAuths;
+ for (auto& entry : key_characteristics_) {
+ allAuths.push_back(AuthorizationSet(entry.authorizations));
+ }
+ auto algorithm = allAuths.GetTagValue(TAG_ALGORITHM);
+ EXPECT_TRUE(algorithm);
+ if (algorithm &&
+ (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC)) {
+ EXPECT_GE(cert_chain_.size(), 1);
+ } else {
+ // For symmetric keys there should be no certificates.
+ EXPECT_EQ(cert_chain_.size(), 0);
+ }
}
return GetReturnErrorCode(result);
@@ -788,6 +824,24 @@
return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
}
+const vector<KeyParameter>& KeyMintAidlTestBase::HwEnforcedAuthorizations(
+ const vector<KeyCharacteristics>& key_characteristics) {
+ auto found =
+ std::find_if(key_characteristics.begin(), key_characteristics.end(), [](auto& entry) {
+ return entry.securityLevel == SecurityLevel::STRONGBOX ||
+ entry.securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT;
+ });
+ return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
+}
+
+const vector<KeyParameter>& KeyMintAidlTestBase::SwEnforcedAuthorizations(
+ const vector<KeyCharacteristics>& key_characteristics) {
+ auto found = std::find_if(
+ key_characteristics.begin(), key_characteristics.end(),
+ [](auto& entry) { return entry.securityLevel == SecurityLevel::SOFTWARE; });
+ return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
+}
+
} // namespace test
} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index f36c397..c1a1dd9 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -27,7 +27,11 @@
#include <keymint_support/authorization_set.h>
-namespace aidl::android::hardware::security::keymint::test {
+namespace aidl::android::hardware::security::keymint {
+
+::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set);
+
+namespace test {
using ::android::sp;
using Status = ::ndk::ScopedAStatus;
@@ -37,8 +41,6 @@
constexpr uint64_t kOpHandleSentinel = 0xFFFFFFFFFFFFFFFF;
-::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set);
-
class KeyMintAidlTestBase : public ::testing::TestWithParam<string> {
public:
void SetUp() override;
@@ -173,6 +175,10 @@
inline const vector<KeyParameter>& SecLevelAuthorizations() {
return SecLevelAuthorizations(key_characteristics_);
}
+ const vector<KeyParameter>& HwEnforcedAuthorizations(
+ const vector<KeyCharacteristics>& key_characteristics);
+ const vector<KeyParameter>& SwEnforcedAuthorizations(
+ const vector<KeyCharacteristics>& key_characteristics);
private:
std::shared_ptr<IKeyMintDevice> keymint_;
@@ -190,4 +196,6 @@
testing::ValuesIn(KeyMintAidlTestBase::build_params()), \
::android::PrintInstanceNameToString)
-} // namespace aidl::android::hardware::security::keymint::test
+} // namespace test
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index bd36b8e..e7c94f3 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -180,9 +180,280 @@
void operator()(RSA* p) { RSA_free(p); }
};
-/* TODO(seleneh) add attestation verification codes like verify_chain() and
- * attestation tests after we decided on the keymint 1 attestation changes.
- */
+char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+string bin2hex(const vector<uint8_t>& data) {
+ string retval;
+ retval.reserve(data.size() * 2 + 1);
+ for (uint8_t byte : data) {
+ retval.push_back(nibble2hex[0x0F & (byte >> 4)]);
+ retval.push_back(nibble2hex[0x0F & byte]);
+ }
+ return retval;
+}
+
+X509* parse_cert_blob(const vector<uint8_t>& blob) {
+ const uint8_t* p = blob.data();
+ return d2i_X509(nullptr, &p, blob.size());
+}
+
+bool verify_chain(const vector<Certificate>& chain) {
+ for (size_t i = 0; i < chain.size(); ++i) {
+ X509_Ptr key_cert(parse_cert_blob(chain[i].encodedCertificate));
+ X509_Ptr signing_cert;
+ if (i < chain.size() - 1) {
+ signing_cert.reset(parse_cert_blob(chain[i + 1].encodedCertificate));
+ } else {
+ signing_cert.reset(parse_cert_blob(chain[i].encodedCertificate));
+ }
+ EXPECT_TRUE(!!key_cert.get() && !!signing_cert.get());
+ if (!key_cert.get() || !signing_cert.get()) return false;
+
+ EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
+ EXPECT_TRUE(!!signing_pubkey.get());
+ if (!signing_pubkey.get()) return false;
+
+ EXPECT_EQ(1, X509_verify(key_cert.get(), signing_pubkey.get()))
+ << "Verification of certificate " << i << " failed "
+ << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);
+
+ char* cert_issuer = //
+ X509_NAME_oneline(X509_get_issuer_name(key_cert.get()), nullptr, 0);
+ char* signer_subj =
+ X509_NAME_oneline(X509_get_subject_name(signing_cert.get()), nullptr, 0);
+ EXPECT_STREQ(cert_issuer, signer_subj) << "Cert " << i << " has wrong issuer.";
+ if (i == 0) {
+ char* cert_sub = X509_NAME_oneline(X509_get_subject_name(key_cert.get()), nullptr, 0);
+ EXPECT_STREQ("/CN=Android Keystore Key", cert_sub)
+ << "Cert " << i << " has wrong subject.";
+ OPENSSL_free(cert_sub);
+ }
+
+ OPENSSL_free(cert_issuer);
+ OPENSSL_free(signer_subj);
+
+ if (dump_Attestations) std::cout << bin2hex(chain[i].encodedCertificate) << std::endl;
+ }
+
+ return true;
+}
+
+// Extract attestation record from cert. Returned object is still part of cert; don't free it
+// separately.
+ASN1_OCTET_STRING* get_attestation_record(X509* certificate) {
+ ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */));
+ EXPECT_TRUE(!!oid.get());
+ if (!oid.get()) return nullptr;
+
+ int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */);
+ EXPECT_NE(-1, location) << "Attestation extension not found in certificate";
+ if (location == -1) return nullptr;
+
+ X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
+ EXPECT_TRUE(!!attest_rec_ext)
+ << "Found attestation extension but couldn't retrieve it? Probably a BoringSSL bug.";
+ if (!attest_rec_ext) return nullptr;
+
+ ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
+ EXPECT_TRUE(!!attest_rec) << "Attestation extension contained no data";
+ return attest_rec;
+}
+
+bool tag_in_list(const KeyParameter& entry) {
+ // Attestations don't contain everything in key authorization lists, so we need to filter
+ // the key lists to produce the lists that we expect to match the attestations.
+ auto tag_list = {
+ Tag::BLOB_USAGE_REQUIREMENTS, //
+ Tag::CREATION_DATETIME, //
+ Tag::EC_CURVE,
+ Tag::HARDWARE_TYPE,
+ Tag::INCLUDE_UNIQUE_ID,
+ };
+ return std::find(tag_list.begin(), tag_list.end(), entry.tag) != tag_list.end();
+}
+
+AuthorizationSet filtered_tags(const AuthorizationSet& set) {
+ AuthorizationSet filtered;
+ std::remove_copy_if(set.begin(), set.end(), std::back_inserter(filtered), tag_in_list);
+ return filtered;
+}
+
+bool avb_verification_enabled() {
+ char value[PROPERTY_VALUE_MAX];
+ return property_get("ro.boot.vbmeta.device_state", value, "") != 0;
+}
+
+bool verify_attestation_record(const string& challenge, //
+ const string& app_id, //
+ AuthorizationSet expected_sw_enforced, //
+ AuthorizationSet expected_hw_enforced, //
+ SecurityLevel security_level,
+ const vector<uint8_t>& attestation_cert) {
+ X509_Ptr cert(parse_cert_blob(attestation_cert));
+ EXPECT_TRUE(!!cert.get());
+ if (!cert.get()) return false;
+
+ ASN1_OCTET_STRING* attest_rec = get_attestation_record(cert.get());
+ EXPECT_TRUE(!!attest_rec);
+ if (!attest_rec) return false;
+
+ AuthorizationSet att_sw_enforced;
+ AuthorizationSet att_hw_enforced;
+ uint32_t att_attestation_version;
+ uint32_t att_keymaster_version;
+ SecurityLevel att_attestation_security_level;
+ SecurityLevel att_keymaster_security_level;
+ vector<uint8_t> att_challenge;
+ vector<uint8_t> att_unique_id;
+ vector<uint8_t> att_app_id;
+
+ auto error = parse_attestation_record(attest_rec->data, //
+ attest_rec->length, //
+ &att_attestation_version, //
+ &att_attestation_security_level, //
+ &att_keymaster_version, //
+ &att_keymaster_security_level, //
+ &att_challenge, //
+ &att_sw_enforced, //
+ &att_hw_enforced, //
+ &att_unique_id);
+ EXPECT_EQ(ErrorCode::OK, error);
+ if (error != ErrorCode::OK) return false;
+
+ EXPECT_GE(att_attestation_version, 3U);
+
+ expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID,
+ vector<uint8_t>(app_id.begin(), app_id.end()));
+
+ EXPECT_GE(att_keymaster_version, 4U);
+ EXPECT_EQ(security_level, att_keymaster_security_level);
+ EXPECT_EQ(security_level, att_attestation_security_level);
+
+ EXPECT_EQ(challenge.length(), att_challenge.size());
+ EXPECT_EQ(0, memcmp(challenge.data(), att_challenge.data(), challenge.length()));
+
+ char property_value[PROPERTY_VALUE_MAX] = {};
+ // TODO(b/136282179): When running under VTS-on-GSI the TEE-backed
+ // keymaster implementation will report YYYYMM dates instead of YYYYMMDD
+ // for the BOOT_PATCH_LEVEL.
+ if (avb_verification_enabled()) {
+ for (int i = 0; i < att_hw_enforced.size(); i++) {
+ if (att_hw_enforced[i].tag == TAG_BOOT_PATCHLEVEL ||
+ att_hw_enforced[i].tag == TAG_VENDOR_PATCHLEVEL) {
+ std::string date =
+ std::to_string(att_hw_enforced[i].value.get<KeyParameterValue::dateTime>());
+ // strptime seems to require delimiters, but the tag value will
+ // be YYYYMMDD
+ date.insert(6, "-");
+ date.insert(4, "-");
+ EXPECT_EQ(date.size(), 10);
+ struct tm time;
+ strptime(date.c_str(), "%Y-%m-%d", &time);
+
+ // Day of the month (0-31)
+ EXPECT_GE(time.tm_mday, 0);
+ EXPECT_LT(time.tm_mday, 32);
+ // Months since Jan (0-11)
+ EXPECT_GE(time.tm_mon, 0);
+ EXPECT_LT(time.tm_mon, 12);
+ // Years since 1900
+ EXPECT_GT(time.tm_year, 110);
+ EXPECT_LT(time.tm_year, 200);
+ }
+ }
+ }
+
+ // Check to make sure boolean values are properly encoded. Presence of a boolean tag indicates
+ // true. A provided boolean tag that can be pulled back out of the certificate indicates correct
+ // encoding. No need to check if it's in both lists, since the AuthorizationSet compare below
+ // will handle mismatches of tags.
+ if (security_level == SecurityLevel::SOFTWARE) {
+ EXPECT_TRUE(expected_sw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
+ } else {
+ EXPECT_TRUE(expected_hw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
+ }
+
+ // Alternatively this checks the opposite - a false boolean tag (one that isn't provided in
+ // the authorization list during key generation) isn't being attested to in the certificate.
+ EXPECT_FALSE(expected_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+ EXPECT_FALSE(att_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+ EXPECT_FALSE(expected_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+ EXPECT_FALSE(att_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
+
+ if (att_hw_enforced.Contains(TAG_ALGORITHM, Algorithm::EC)) {
+ // For ECDSA keys, either an EC_CURVE or a KEY_SIZE can be specified, but one must be.
+ EXPECT_TRUE(att_hw_enforced.Contains(TAG_EC_CURVE) ||
+ att_hw_enforced.Contains(TAG_KEY_SIZE));
+ }
+
+ // Test root of trust elements
+ vector<uint8_t> verified_boot_key;
+ VerifiedBoot verified_boot_state;
+ bool device_locked;
+ vector<uint8_t> verified_boot_hash;
+ error = parse_root_of_trust(attest_rec->data, attest_rec->length, &verified_boot_key,
+ &verified_boot_state, &device_locked, &verified_boot_hash);
+ EXPECT_EQ(ErrorCode::OK, error);
+
+ if (avb_verification_enabled()) {
+ EXPECT_NE(property_get("ro.boot.vbmeta.digest", property_value, ""), 0);
+ string prop_string(property_value);
+ EXPECT_EQ(prop_string.size(), 64);
+ EXPECT_EQ(prop_string, bin2hex(verified_boot_hash));
+
+ EXPECT_NE(property_get("ro.boot.vbmeta.device_state", property_value, ""), 0);
+ if (!strcmp(property_value, "unlocked")) {
+ EXPECT_FALSE(device_locked);
+ } else {
+ EXPECT_TRUE(device_locked);
+ }
+
+ // Check that the device is locked if not debuggable, e.g., user build
+ // images in CTS. For VTS, debuggable images are used to allow adb root
+ // and the device is unlocked.
+ if (!property_get_bool("ro.debuggable", false)) {
+ EXPECT_TRUE(device_locked);
+ } else {
+ EXPECT_FALSE(device_locked);
+ }
+ }
+
+ // Verified boot key should be all 0's if the boot state is not verified or self signed
+ std::string empty_boot_key(32, '\0');
+ std::string verified_boot_key_str((const char*)verified_boot_key.data(),
+ verified_boot_key.size());
+ EXPECT_NE(property_get("ro.boot.verifiedbootstate", property_value, ""), 0);
+ if (!strcmp(property_value, "green")) {
+ EXPECT_EQ(verified_boot_state, VerifiedBoot::VERIFIED);
+ EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+ verified_boot_key.size()));
+ } else if (!strcmp(property_value, "yellow")) {
+ EXPECT_EQ(verified_boot_state, VerifiedBoot::SELF_SIGNED);
+ EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+ verified_boot_key.size()));
+ } else if (!strcmp(property_value, "orange")) {
+ EXPECT_EQ(verified_boot_state, VerifiedBoot::UNVERIFIED);
+ EXPECT_EQ(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+ verified_boot_key.size()));
+ } else if (!strcmp(property_value, "red")) {
+ EXPECT_EQ(verified_boot_state, VerifiedBoot::FAILED);
+ } else {
+ EXPECT_EQ(verified_boot_state, VerifiedBoot::UNVERIFIED);
+ EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+ verified_boot_key.size()));
+ }
+
+ att_sw_enforced.Sort();
+ expected_sw_enforced.Sort();
+ EXPECT_EQ(filtered_tags(expected_sw_enforced), filtered_tags(att_sw_enforced));
+
+ att_hw_enforced.Sort();
+ expected_hw_enforced.Sort();
+ EXPECT_EQ(filtered_tags(expected_hw_enforced), filtered_tags(att_hw_enforced));
+
+ return true;
+}
std::string make_string(const uint8_t* data, size_t length) {
return std::string(reinterpret_cast<const char*>(data), length);
@@ -289,6 +560,51 @@
}
/*
+ * NewKeyGenerationTest.Rsa
+ *
+ * Verifies that keymint can generate all required RSA key sizes, and that the resulting keys
+ * have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithAttestation) {
+ for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+ auto challenge = "hello";
+ auto app_id = "foo";
+
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(key_size, 65537)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .AttestationChallenge(challenge)
+ .AttestationApplicationId(app_id)
+ .Authorization(TAG_NO_AUTH_REQUIRED),
+ &key_blob, &key_characteristics));
+
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+ << "Key size " << key_size << "missing";
+ EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+ EXPECT_TRUE(verify_chain(cert_chain_));
+ ASSERT_GT(cert_chain_.size(), 0);
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+ EXPECT_TRUE(verify_attestation_record(challenge, app_id, //
+ sw_enforced, hw_enforced, SecLevel(),
+ cert_chain_[0].encodedCertificate));
+
+ CheckedDeleteKey(&key_blob);
+ }
+}
+
+/*
* NewKeyGenerationTest.NoInvalidRsaSizes
*
* Verifies that keymint cannot generate any RSA key sizes that are designated as invalid.
@@ -3895,16 +4211,6 @@
INSTANTIATE_KEYMINT_AIDL_TEST(AddEntropyTest);
-typedef KeyMintAidlTestBase AttestationTest;
-
-/*
- * AttestationTest.RsaAttestation
- *
- * Verifies that attesting to RSA keys works and generates the expected output.
- */
-// TODO(seleneh) add attestation tests back after decided on the new attestation
-// behavior under generateKey and importKey
-
typedef KeyMintAidlTestBase KeyDeletionTest;
/**
diff --git a/security/keymint/aidl/vts/functional/VerificationTokenTest.cpp b/security/keymint/aidl/vts/functional/VerificationTokenTest.cpp
deleted file mode 100644
index 0b1eccd..0000000
--- a/security/keymint/aidl/vts/functional/VerificationTokenTest.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "KeyMintAidlTestBase.h"
-
-namespace aidl::android::hardware::security::keymint::test {
-
-class VerificationTokenTest : public KeyMintAidlTestBase {
- protected:
- struct VerifyAuthorizationResult {
- ErrorCode error;
- VerificationToken token;
- };
-
- VerifyAuthorizationResult verifyAuthorization(uint64_t operationHandle,
- const HardwareAuthToken& authToken) {
- VerifyAuthorizationResult result;
-
- Status err;
- err = keyMint().verifyAuthorization(operationHandle, //
- authToken, //
- &result.token);
-
- result.error = GetReturnErrorCode(err);
- return result;
- }
-
- uint64_t getTime() {
- struct timespec timespec;
- EXPECT_EQ(0, clock_gettime(CLOCK_BOOTTIME, ×pec));
- return timespec.tv_sec * 1000 + timespec.tv_nsec / 1000000;
- }
-
- int sleep_ms(uint32_t milliseconds) {
- struct timespec sleep_time = {static_cast<time_t>(milliseconds / 1000),
- static_cast<long>(milliseconds % 1000) * 1000000};
- while (sleep_time.tv_sec || sleep_time.tv_nsec) {
- if (nanosleep(&sleep_time /* to wait */,
- &sleep_time /* remaining (on interrruption) */) == 0) {
- sleep_time = {};
- } else {
- if (errno != EINTR) return errno;
- }
- }
- return 0;
- }
-};
-
-/*
- * VerificationTokens exist to facilitate cross-KeyMint verification of requirements. As
- * such, the precise capabilities required will vary depending on the specific vendor
- * implementations. Essentially, VerificationTokens are a "hook" to enable vendor
- * implementations to communicate, so the precise usage is defined by those vendors. The only
- * thing we really can test is that tokens can be created by TEE keyMints, and that the
- * timestamps increase as expected.
- */
-TEST_P(VerificationTokenTest, TestCreation) {
- auto result1 = verifyAuthorization(1 /* operation handle */, HardwareAuthToken());
- auto result1_time = getTime();
-
- if (SecLevel() == SecurityLevel::STRONGBOX) {
- // StrongBox should not implement verifyAuthorization.
- EXPECT_EQ(ErrorCode::UNIMPLEMENTED, result1.error);
- return;
- }
-
- ASSERT_EQ(ErrorCode::OK, result1.error);
- EXPECT_EQ(1U, result1.token.challenge);
- EXPECT_EQ(SecLevel(), result1.token.securityLevel);
- EXPECT_GT(result1.token.timestamp.milliSeconds, 0U);
-
- constexpr uint32_t time_to_sleep = 200;
- sleep_ms(time_to_sleep);
-
- auto result2 = verifyAuthorization(2 /* operation handle */, HardwareAuthToken());
-
- auto result2_time = getTime();
- ASSERT_EQ(ErrorCode::OK, result2.error);
- EXPECT_EQ(2U, result2.token.challenge);
- EXPECT_EQ(SecLevel(), result2.token.securityLevel);
-
- auto host_time_delta = result2_time - result1_time;
-
- EXPECT_GE(host_time_delta, time_to_sleep)
- << "We slept for " << time_to_sleep << " ms, the clock must have advanced by that much";
- EXPECT_LE(host_time_delta, time_to_sleep + 20)
- << "The verifyAuthorization call took " << (host_time_delta - time_to_sleep)
- << " ms? That's awful!";
-
- auto km_time_delta =
- result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds;
-
- // If not too much else is going on on the system, the time delta should be quite close. Allow
- // 2 ms of slop just to avoid test flakiness.
- //
- // TODO(swillden): see if we can output values so they can be gathered across many runs and
- // report if times aren't nearly always <1ms apart.
- EXPECT_LE(host_time_delta, km_time_delta + 2);
- EXPECT_LE(km_time_delta, host_time_delta + 2);
- ASSERT_EQ(result1.token.mac.size(), result2.token.mac.size());
- ASSERT_NE(0,
- memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
-}
-
-/*
- * Test that the mac changes when the time stamp changes. This is does not guarantee that the time
- * stamp is included in the mac but on failure we know that it is not. Other than in the test
- * case above we call verifyAuthorization with the exact same set of parameters.
- */
-TEST_P(VerificationTokenTest, MacChangesOnChangingTimestamp) {
- auto result1 = verifyAuthorization(0 /* operation handle */, HardwareAuthToken());
- auto result1_time = getTime();
-
- if (SecLevel() == SecurityLevel::STRONGBOX) {
- // StrongBox should not implement verifyAuthorization.
- EXPECT_EQ(ErrorCode::UNIMPLEMENTED, result1.error);
- return;
- }
-
- EXPECT_EQ(ErrorCode::OK, result1.error);
- EXPECT_EQ(0U, result1.token.challenge);
- EXPECT_EQ(SecLevel(), result1.token.securityLevel);
- EXPECT_GT(result1.token.timestamp.milliSeconds, 0U);
-
- constexpr uint32_t time_to_sleep = 200;
- sleep_ms(time_to_sleep);
-
- auto result2 = verifyAuthorization(0 /* operation handle */, HardwareAuthToken());
- // ASSERT_TRUE(result2.callSuccessful);
- auto result2_time = getTime();
- EXPECT_EQ(ErrorCode::OK, result2.error);
- EXPECT_EQ(0U, result2.token.challenge);
- EXPECT_EQ(SecLevel(), result2.token.securityLevel);
-
- auto host_time_delta = result2_time - result1_time;
-
- EXPECT_GE(host_time_delta, time_to_sleep)
- << "We slept for " << time_to_sleep << " ms, the clock must have advanced by that much";
- EXPECT_LE(host_time_delta, time_to_sleep + 20)
- << "The verifyAuthorization call took " << (host_time_delta - time_to_sleep)
- << " ms? That's awful!";
-
- auto km_time_delta =
- result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds;
-
- EXPECT_LE(host_time_delta, km_time_delta + 2);
- EXPECT_LE(km_time_delta, host_time_delta + 2);
- ASSERT_EQ(result1.token.mac.size(), result2.token.mac.size());
- ASSERT_NE(0,
- memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
-}
-
-INSTANTIATE_KEYMINT_AIDL_TEST(VerificationTokenTest);
-
-} // namespace aidl::android::hardware::security::keymint::test
diff --git a/security/keymint/support/include/keymint_support/authorization_set.h b/security/keymint/support/include/keymint_support/authorization_set.h
index 596bb89..1407c5f 100644
--- a/security/keymint/support/include/keymint_support/authorization_set.h
+++ b/security/keymint/support/include/keymint_support/authorization_set.h
@@ -259,6 +259,12 @@
size - 1); // drop the terminating '\0'
}
+ template <Tag tag>
+ AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag,
+ const std::string& data) {
+ return Authorization(ttag, reinterpret_cast<const uint8_t*>(data.data()), data.size());
+ }
+
AuthorizationSetBuilder& Authorizations(const AuthorizationSet& set) {
for (const auto& entry : set) {
push_back(entry);
@@ -294,6 +300,20 @@
AuthorizationSetBuilder& Digest(std::vector<Digest> digests);
AuthorizationSetBuilder& Padding(std::initializer_list<PaddingMode> paddings);
+ AuthorizationSetBuilder& AttestationChallenge(const std::string& challenge) {
+ return Authorization(TAG_ATTESTATION_CHALLENGE, challenge);
+ }
+ AuthorizationSetBuilder& AttestationChallenge(std::vector<uint8_t> challenge) {
+ return Authorization(TAG_ATTESTATION_CHALLENGE, challenge);
+ }
+
+ AuthorizationSetBuilder& AttestationApplicationId(const std::string& id) {
+ return Authorization(TAG_ATTESTATION_APPLICATION_ID, id);
+ }
+ AuthorizationSetBuilder& AttestationApplicationId(std::vector<uint8_t> id) {
+ return Authorization(TAG_ATTESTATION_APPLICATION_ID, id);
+ }
+
template <typename... T>
AuthorizationSetBuilder& BlockMode(T&&... a) {
return BlockMode({std::forward<T>(a)...});
diff --git a/security/secureclock/aidl/Android.bp b/security/secureclock/aidl/Android.bp
index 7d26a9b..5a6d7ae 100644
--- a/security/secureclock/aidl/Android.bp
+++ b/security/secureclock/aidl/Android.bp
@@ -5,9 +5,6 @@
"android/hardware/security/secureclock/*.aidl",
],
stability: "vintf",
- imports: [
- "android.hardware.security.keymint",
- ],
backend: {
java: {
sdk_version: "module_current",
diff --git a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl
index c23ddca..51b1824 100644
--- a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl
+++ b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl
@@ -20,7 +20,6 @@
@VintfStability
parcelable TimeStampToken {
long challenge;
- android.hardware.security.keymint.Timestamp timestamp;
- android.hardware.security.keymint.SecurityLevel securityLevel;
+ android.hardware.security.secureclock.Timestamp timestamp;
byte[] mac;
}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl
similarity index 95%
rename from security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
rename to security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl
index 963e66e..50b8b9f 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Timestamp.aidl
+++ b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl
@@ -16,7 +16,7 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.security.keymint;
+package android.hardware.security.secureclock;
@VintfStability
parcelable Timestamp {
long milliSeconds;
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
index 76a2d28..b24d335 100644
--- a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
@@ -16,8 +16,7 @@
package android.hardware.security.secureclock;
-import android.hardware.security.keymint.SecurityLevel;
-import android.hardware.security.keymint.Timestamp;
+import android.hardware.security.secureclock.Timestamp;
/**
* TimeStampToken instances are used for secure environments that requires secure time information.
@@ -36,11 +35,6 @@
Timestamp timestamp;
/**
- * SecurityLevel of the secure environment that generated the token.
- */
- SecurityLevel securityLevel;
-
- /**
* 32-byte HMAC-SHA256 of the above values, computed as:
*
* HMAC(H,
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Timestamp.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl
similarity index 95%
rename from security/keymint/aidl/android/hardware/security/keymint/Timestamp.aidl
rename to security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl
index ebb3684..7bd1f9e 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Timestamp.aidl
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.security.keymint;
+package android.hardware.security.secureclock;
/**
* Time in milliseconds since some arbitrary point in time. Time must be monotonically increasing,
diff --git a/tests/lazy/1.1/ILazy.hal b/tests/lazy/1.1/ILazy.hal
index b0a6a2a..eb48fd3 100644
--- a/tests/lazy/1.1/ILazy.hal
+++ b/tests/lazy/1.1/ILazy.hal
@@ -20,10 +20,10 @@
interface ILazy extends @1.0::ILazy {
/**
- * Ask the process hosting the service to install a callback that notifies
- * it when the number of active (i.e. with clients) services changes.
+ * Ask the process hosting the service to install a callback that notifies if there are
+ * services with clients.
* For testing purposes, this callback exercises the code to unregister/re-register
* the services and eventually shuts down the process.
*/
- setCustomActiveServicesCountCallback();
+ setCustomActiveServicesCallback();
};
diff --git a/tv/input/1.0/default/TvInput.cpp b/tv/input/1.0/default/TvInput.cpp
index 4ea1dec..7583a67 100644
--- a/tv/input/1.0/default/TvInput.cpp
+++ b/tv/input/1.0/default/TvInput.cpp
@@ -142,7 +142,7 @@
// static
void TvInput::notify(struct tv_input_device* __unused, tv_input_event_t* event,
- void* __unused) {
+ void* optionalStatus) {
if (mCallback != nullptr && event != nullptr) {
// Capturing is no longer supported.
if (event->type >= TV_INPUT_EVENT_CAPTURE_SUCCEEDED) {
@@ -154,7 +154,17 @@
tvInputEvent.deviceInfo.type = static_cast<TvInputType>(
event->device_info.type);
tvInputEvent.deviceInfo.portId = event->device_info.hdmi.port_id;
- tvInputEvent.deviceInfo.cableConnectionStatus = CableConnectionStatus::UNKNOWN;
+ CableConnectionStatus connectionStatus = CableConnectionStatus::UNKNOWN;
+ if (optionalStatus != nullptr &&
+ ((event->type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) ||
+ (event->type == TV_INPUT_EVENT_DEVICE_AVAILABLE))) {
+ int newStatus = *reinterpret_cast<int*>(optionalStatus);
+ if (newStatus <= static_cast<int>(CableConnectionStatus::DISCONNECTED) &&
+ newStatus >= static_cast<int>(CableConnectionStatus::UNKNOWN)) {
+ connectionStatus = static_cast<CableConnectionStatus>(newStatus);
+ }
+ }
+ tvInputEvent.deviceInfo.cableConnectionStatus = connectionStatus;
// TODO: Ensure the legacy audio type code is the same once audio HAL default
// implementation is ready.
tvInputEvent.deviceInfo.audioType = static_cast<AudioDevice>(
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.cpp b/tv/tuner/1.0/vts/functional/DvrTests.cpp
index 0dfc032..ba21189 100644
--- a/tv/tuner/1.0/vts/functional/DvrTests.cpp
+++ b/tv/tuner/1.0/vts/functional/DvrTests.cpp
@@ -55,6 +55,7 @@
uint8_t* buffer;
ALOGW("[vts] playback thread loop start %s", mInputDataFile.c_str());
if (fd < 0) {
+ EXPECT_TRUE(fd >= 0) << "Failed to open: " + mInputDataFile;
mPlaybackThreadRunning = false;
ALOGW("[vts] Error %s", strerror(errno));
}
@@ -178,7 +179,7 @@
// Our current implementation filter the data and write it into the filter FMQ
// immediately after the DATA_READY from the VTS/framework
if (!readRecordFMQ()) {
- ALOGD("[vts] record data failed to be filtered. Ending thread");
+ ALOGW("[vts] record data failed to be filtered. Ending thread");
mRecordThreadRunning = false;
break;
}
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.cpp b/tv/tuner/1.0/vts/functional/FilterTests.cpp
index 0ecdf73..a354c78 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.0/vts/functional/FilterTests.cpp
@@ -70,6 +70,10 @@
}
bool FilterCallback::readFilterEventData() {
+ if (mFilterMQ == NULL) {
+ ALOGW("[vts] FMQ is not configured and does not need to be tested.");
+ return true;
+ }
bool result = false;
DemuxFilterEvent filterEvent = mFilterEvent;
ALOGW("[vts] reading from filter FMQ or buffer %d", mFilterId);
@@ -218,7 +222,11 @@
return AssertionResult(status == Result::SUCCESS);
}
-AssertionResult FilterTests::getFilterMQDescriptor(uint32_t filterId) {
+AssertionResult FilterTests::getFilterMQDescriptor(uint32_t filterId, bool getMqDesc) {
+ if (!getMqDesc) {
+ ALOGE("[vts] Filter does not need FMQ.");
+ return success();
+ }
Result status;
EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
@@ -279,16 +287,14 @@
AssertionResult FilterTests::closeFilter(uint32_t filterId) {
EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
Result status = mFilters[filterId]->close();
- if (status == Result::SUCCESS) {
- for (int i = 0; i < mUsedFilterIds.size(); i++) {
- if (mUsedFilterIds[i] == filterId) {
- mUsedFilterIds.erase(mUsedFilterIds.begin() + i);
- break;
- }
+ for (int i = 0; i < mUsedFilterIds.size(); i++) {
+ if (mUsedFilterIds[i] == filterId) {
+ mUsedFilterIds.erase(mUsedFilterIds.begin() + i);
+ break;
}
- mFilterCallbacks.erase(filterId);
- mFilters.erase(filterId);
}
+ mFilterCallbacks.erase(filterId);
+ mFilters.erase(filterId);
return AssertionResult(status == Result::SUCCESS);
}
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.h b/tv/tuner/1.0/vts/functional/FilterTests.h
index a76a6b9..75c59b3 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.h
+++ b/tv/tuner/1.0/vts/functional/FilterTests.h
@@ -157,7 +157,7 @@
AssertionResult getTimeStamp();
AssertionResult getNewlyOpenedFilterId(uint32_t& filterId);
AssertionResult configFilter(DemuxFilterSettings setting, uint32_t filterId);
- AssertionResult getFilterMQDescriptor(uint32_t filterId);
+ AssertionResult getFilterMQDescriptor(uint32_t filterId, bool getMqDesc);
AssertionResult setFilterDataSource(uint32_t sourceFilterId, uint32_t sinkFilterId);
AssertionResult setFilterDataSourceToDemux(uint32_t filterId);
AssertionResult startFilter(uint32_t filterId);
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index 2be68b8..22ba271 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -48,7 +48,7 @@
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
- ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
ASSERT_TRUE(mFilterTests.startFilter(filterId));
ASSERT_TRUE(mFilterTests.stopFilter(filterId));
ASSERT_TRUE(mFilterTests.closeFilter(filterId));
@@ -75,6 +75,9 @@
void TunerBroadcastHidlTest::broadcastSingleFilterTest(FilterConfig filterConf,
FrontendConfig frontendConf) {
+ if (!frontendConf.enable) {
+ return;
+ }
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -99,7 +102,7 @@
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
- ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
ASSERT_TRUE(mFilterTests.startFilter(filterId));
// tune test
ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
@@ -145,7 +148,7 @@
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
- ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
mDvrTests.startPlaybackInputThread(dvrConf.playbackInputFile, dvrConf.settings.playback());
ASSERT_TRUE(mDvrTests.startDvrPlayback());
ASSERT_TRUE(mFilterTests.startFilter(filterId));
@@ -160,6 +163,9 @@
void TunerRecordHidlTest::recordSingleFilterTest(FilterConfig filterConf,
FrontendConfig frontendConf, DvrConfig dvrConf) {
+ if (!frontendConf.enable) {
+ return;
+ }
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -184,7 +190,7 @@
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
- ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
filter = mFilterTests.getFilterById(filterId);
ASSERT_TRUE(filter != nullptr);
mDvrTests.startRecordOutputThread(dvrConf.settings.record());
@@ -247,7 +253,7 @@
ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
- ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+ ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
filter = mFilterTests.getFilterById(filterId);
ASSERT_TRUE(filter != nullptr);
ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
@@ -265,6 +271,9 @@
void TunerDescramblerHidlTest::scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
FrontendConfig frontendConf,
DescramblerConfig descConfig) {
+ if (!frontendConf.enable) {
+ return;
+ }
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
@@ -328,17 +337,17 @@
TEST_P(TunerFrontendHidlTest, TuneFrontend) {
description("Tune one Frontend with specific setting and check Lock event");
- mFrontendTests.tuneTest(frontendArray[DVBT]);
+ mFrontendTests.tuneTest(frontendArray[defaultFrontend]);
}
TEST_P(TunerFrontendHidlTest, AutoScanFrontend) {
description("Run an auto frontend scan with specific setting and check lock scanMessage");
- mFrontendTests.scanTest(frontendScanArray[SCAN_DVBT], FrontendScanType::SCAN_AUTO);
+ mFrontendTests.scanTest(frontendScanArray[defaultScanFrontend], FrontendScanType::SCAN_AUTO);
}
TEST_P(TunerFrontendHidlTest, BlindScanFrontend) {
description("Run an blind frontend scan with specific setting and check lock scanMessage");
- mFrontendTests.scanTest(frontendScanArray[SCAN_DVBT], FrontendScanType::SCAN_BLIND);
+ mFrontendTests.scanTest(frontendScanArray[defaultScanFrontend], FrontendScanType::SCAN_BLIND);
}
TEST_P(TunerLnbHidlTest, OpenLnbByName) {
@@ -374,7 +383,7 @@
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
- mFrontendTests.getFrontendIdByType(frontendArray[DVBT].type, feId);
+ mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].type, feId);
ASSERT_TRUE(feId != INVALID_ID);
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
@@ -394,7 +403,7 @@
uint32_t avSyncHwId;
sp<IFilter> mediaFilter;
- mFrontendTests.getFrontendIdByType(frontendArray[DVBT].type, feId);
+ mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].type, feId);
ASSERT_TRUE(feId != INVALID_ID);
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
@@ -422,7 +431,7 @@
TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
description("Open and start a filter in Demux.");
// TODO use paramterized tests
- configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
+ configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[defaultFrontend]);
}
TEST_P(TunerFilterHidlTest, SetFilterLinkage) {
@@ -463,22 +472,22 @@
TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowVideoFilterTest) {
description("Test Video Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_VIDEO1], frontendArray[DVBT]);
+ broadcastSingleFilterTest(filterArray[TS_VIDEO1], frontendArray[defaultFrontend]);
}
TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowAudioFilterTest) {
description("Test Audio Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_AUDIO0], frontendArray[DVBT]);
+ broadcastSingleFilterTest(filterArray[TS_AUDIO0], frontendArray[defaultFrontend]);
}
TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowSectionFilterTest) {
description("Test Section Filter functionality in Broadcast use case.");
- broadcastSingleFilterTest(filterArray[TS_SECTION0], frontendArray[DVBT]);
+ broadcastSingleFilterTest(filterArray[TS_SECTION0], frontendArray[defaultFrontend]);
}
TEST_P(TunerBroadcastHidlTest, IonBufferTest) {
description("Test the av filter data bufferring.");
- broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
+ broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[defaultFrontend]);
}
TEST_P(TunerBroadcastHidlTest, LnbBroadcastDataFlowVideoFilterTest) {
@@ -494,13 +503,14 @@
TEST_P(TunerRecordHidlTest, AttachFiltersToRecordTest) {
description("Attach a single filter to the record dvr test.");
// TODO use paramterized tests
- attachSingleFilterToRecordDvrTest(filterArray[TS_RECORD0], frontendArray[DVBT],
+ attachSingleFilterToRecordDvrTest(filterArray[TS_RECORD0], frontendArray[defaultFrontend],
dvrArray[DVR_RECORD0]);
}
TEST_P(TunerRecordHidlTest, RecordDataFlowWithTsRecordFilterTest) {
description("Feed ts data from frontend to recording and test with ts record filter");
- recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[DVBT], dvrArray[DVR_RECORD0]);
+ recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[defaultFrontend],
+ dvrArray[DVR_RECORD0]);
}
TEST_P(TunerRecordHidlTest, LnbRecordDataFlowWithTsRecordFilterTest) {
@@ -513,7 +523,7 @@
uint32_t feId;
uint32_t demuxId;
sp<IDemux> demux;
- mFrontendTests.getFrontendIdByType(frontendArray[DVBT].type, feId);
+ mFrontendTests.getFrontendIdByType(frontendArray[defaultFrontend].type, feId);
ASSERT_TRUE(feId != INVALID_ID);
ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
ASSERT_TRUE(mFrontendTests.setFrontendCallback());
@@ -530,7 +540,7 @@
set<FilterConfig> filterConfs;
filterConfs.insert(filterArray[TS_AUDIO0]);
filterConfs.insert(filterArray[TS_VIDEO1]);
- scrambledBroadcastTest(filterConfs, frontendArray[DVBT], descramblerArray[DESC_0]);
+ scrambledBroadcastTest(filterConfs, frontendArray[defaultFrontend], descramblerArray[DESC_0]);
}
INSTANTIATE_TEST_SUITE_P(
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index 6c68e35..92a8130 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -55,6 +55,7 @@
using namespace std;
+const uint32_t FMQ_SIZE_512K = 0x80000;
const uint32_t FMQ_SIZE_1M = 0x100000;
const uint32_t FMQ_SIZE_4M = 0x400000;
const uint32_t FMQ_SIZE_16M = 0x1000000;
@@ -134,6 +135,7 @@
uint32_t bufferSize;
DemuxFilterType type;
DemuxFilterSettings settings;
+ bool getMqDesc;
bool operator<(const FilterConfig& /*c*/) const { return false; }
};
@@ -144,6 +146,7 @@
};
struct FrontendConfig {
+ bool enable;
bool isSoftwareFe;
FrontendType type;
FrontendSettings settings;
@@ -191,6 +194,8 @@
static DvrConfig dvrArray[DVR_MAX];
static DescramblerConfig descramblerArray[DESC_MAX];
static vector<string> goldenOutputFiles;
+static int defaultFrontend = DVBT;
+static int defaultScanFrontend = SCAN_DVBT;
/** Configuration array for the frontend tune test */
inline void initFrontendConfig() {
@@ -216,7 +221,9 @@
frontendArray[DVBT].tuneStatusTypes = types;
frontendArray[DVBT].expectTuneStatuses = statuses;
frontendArray[DVBT].isSoftwareFe = true;
+ frontendArray[DVBS].enable = true;
frontendArray[DVBS].type = FrontendType::DVBS;
+ frontendArray[DVBS].enable = true;
frontendArray[DVBS].isSoftwareFe = true;
};
@@ -283,6 +290,7 @@
.isRaw = false,
.streamId = 0xbd,
});
+ filterArray[TS_PES0].getMqDesc = true;
// TS PCR filter setting
filterArray[TS_PCR0].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_PCR0].type.subType.tsFilterType(DemuxTsFilterType::PCR);
@@ -303,6 +311,7 @@
filterArray[TS_SECTION0].settings.ts().filterSettings.section({
.isRaw = false,
});
+ filterArray[TS_SECTION0].getMqDesc = true;
// TS RECORD filter setting
filterArray[TS_RECORD0].type.mainType = DemuxFilterMainType::TS;
filterArray[TS_RECORD0].type.subType.tsFilterType(DemuxTsFilterType::RECORD);
diff --git a/weaver/aidl/Android.bp b/weaver/aidl/Android.bp
new file mode 100644
index 0000000..5637e0a
--- /dev/null
+++ b/weaver/aidl/Android.bp
@@ -0,0 +1,16 @@
+aidl_interface {
+ name: "android.hardware.weaver",
+ vendor_available: true,
+ srcs: ["android/hardware/weaver/*.aidl"],
+ stability: "vintf",
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/IWeaver.aidl b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/IWeaver.aidl
new file mode 100644
index 0000000..29bd9a9
--- /dev/null
+++ b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/IWeaver.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.weaver;
+@VintfStability
+interface IWeaver {
+ android.hardware.weaver.WeaverConfig getConfig();
+ android.hardware.weaver.WeaverReadResponse read(in int slotId, in byte[] key);
+ void write(in int slotId, in byte[] key, in byte[] value);
+ const int STATUS_FAILED = 1;
+ const int INCORRECT_KEY = 2;
+ const int THROTTLE = 3;
+}
diff --git a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverConfig.aidl b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverConfig.aidl
new file mode 100644
index 0000000..239cdac
--- /dev/null
+++ b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverConfig.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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.weaver;
+@VintfStability
+parcelable WeaverConfig {
+ long slots;
+ long keySize;
+ long valueSize;
+}
diff --git a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl
new file mode 100644
index 0000000..7e5db59
--- /dev/null
+++ b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl
@@ -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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.weaver;
+@VintfStability
+parcelable WeaverReadResponse {
+ long timeout;
+ byte[] value;
+}
diff --git a/weaver/aidl/android/hardware/weaver/IWeaver.aidl b/weaver/aidl/android/hardware/weaver/IWeaver.aidl
new file mode 100644
index 0000000..ebbfabe
--- /dev/null
+++ b/weaver/aidl/android/hardware/weaver/IWeaver.aidl
@@ -0,0 +1,94 @@
+/*
+ * 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.weaver;
+
+import android.hardware.weaver.WeaverConfig;
+import android.hardware.weaver.WeaverReadResponse;
+
+/**
+ * Weaver provides secure storage of secret values that may only be read if the
+ * corresponding key has been presented.
+ *
+ * The storage must be secure as the device's user authentication and encryption
+ * relies on the security of these values. The cardinality of the domains of the
+ * key and value must be suitably large such that they cannot be easily guessed.
+ *
+ * Weaver is structured as an array of slots, each containing a key-value pair.
+ * Slots are uniquely identified by an ID in the range [0, `getConfig().slots`).
+ */
+@VintfStability
+interface IWeaver {
+ /**
+ * Retrieves the config information for this implementation of Weaver.
+ *
+ * The config is static i.e. every invocation returns the same information.
+ *
+ * @return config data for this implementation of Weaver if status is OK,
+ * otherwise undefined.
+ */
+ WeaverConfig getConfig();
+
+ /**
+ * Read binder calls may return a ServiceSpecificException with the following error codes.
+ */
+ const int STATUS_FAILED = 1;
+ const int INCORRECT_KEY = 2;
+ const int THROTTLE = 3;
+
+ /**
+ * Attempts to retrieve the value stored in the identified slot.
+ *
+ * The value is only returned if the provided key matches the key stored in
+ * the slot. The value is never returned if the wrong key is provided.
+ *
+ * Throttling must be used to limit the frequency of failed read attempts.
+ * The value is only returned when throttling is not active, even if the
+ * correct key is provided. If called when throttling is active, the time
+ * until the next attempt can be made is returned.
+ *
+ * Service status return:
+ *
+ * OK if the value was successfully read from slot.
+ * INCORRECT_KEY if the key does not match the key in the slot.
+ * THROTTLE if throttling is active.
+ * STATUS_FAILED if the read was unsuccessful for another reason.
+ *
+ * @param slotId of the slot to read from, this must be positive to be valid.
+ * @param key that is stored in the slot.
+ * @return The WeaverReadResponse for this read request. If the status is OK,
+ * value is set to the value in the slot and timeout is 0. Otherwise, value is
+ * empty and timeout is set accordingly.
+ */
+ WeaverReadResponse read(in int slotId, in byte[] key);
+
+ /**
+ * Overwrites the identified slot with the provided key and value.
+ *
+ * The new values are written regardless of the current state of the slot in
+ * order to remain idempotent.
+ *
+ * Service status return:
+ *
+ * OK if the write was successfully completed.
+ * FAILED if the write was unsuccessful.
+ *
+ * @param slotId of the slot to write to.
+ * @param key to write to the slot.
+ * @param value to write to slot.
+ */
+ void write(in int slotId, in byte[] key, in byte[] value);
+}
diff --git a/health/1.0/default/libhealthd/healthd_board_default.cpp b/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl
similarity index 61%
copy from health/1.0/default/libhealthd/healthd_board_default.cpp
copy to weaver/aidl/android/hardware/weaver/WeaverConfig.aidl
index 127f98e..75d961e 100644
--- a/health/1.0/default/libhealthd/healthd_board_default.cpp
+++ b/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 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,15 +14,21 @@
* limitations under the License.
*/
-#include <healthd/healthd.h>
+package android.hardware.weaver;
-void healthd_board_init(struct healthd_config*)
-{
- // use defaults
+@VintfStability
+parcelable WeaverConfig {
+ /**
+ * The number of slots available.
+ */
+ long slots;
+ /**
+ * The number of bytes used for a key.
+ */
+ long keySize;
+ /**
+ * The number of bytes used for a value.
+ */
+ long valueSize;
}
-int healthd_board_battery_update(struct android::BatteryProperties*)
-{
- // return 0 to log periodic polled battery status to kernel log
- return 0;
-}
diff --git a/health/1.0/default/libhealthd/healthd_board_default.cpp b/weaver/aidl/android/hardware/weaver/WeaverReadResponse.aidl
similarity index 61%
copy from health/1.0/default/libhealthd/healthd_board_default.cpp
copy to weaver/aidl/android/hardware/weaver/WeaverReadResponse.aidl
index 127f98e..ec006e8 100644
--- a/health/1.0/default/libhealthd/healthd_board_default.cpp
+++ b/weaver/aidl/android/hardware/weaver/WeaverReadResponse.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 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,15 +14,17 @@
* limitations under the License.
*/
-#include <healthd/healthd.h>
+package android.hardware.weaver;
-void healthd_board_init(struct healthd_config*)
-{
- // use defaults
+@VintfStability
+parcelable WeaverReadResponse {
+ /**
+ * The time to wait, in milliseconds, before making the next request.
+ */
+ long timeout;
+ /**
+ * The value read from the slot or empty if the value was not read.
+ */
+ byte[] value;
}
-int healthd_board_battery_update(struct android::BatteryProperties*)
-{
- // return 0 to log periodic polled battery status to kernel log
- return 0;
-}
diff --git a/weaver/aidl/default/Android.bp b/weaver/aidl/default/Android.bp
new file mode 100644
index 0000000..d936828
--- /dev/null
+++ b/weaver/aidl/default/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.weaver-service.example",
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.weaver-service.example.rc"],
+ vintf_fragments: ["android.hardware.weaver-service.example.xml"],
+ vendor: true,
+ srcs: [
+ "service.cpp",
+ "Weaver.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.weaver-ndk_platform",
+ "libbase",
+ "libbinder_ndk",
+ ],
+}
diff --git a/weaver/aidl/default/Weaver.cpp b/weaver/aidl/default/Weaver.cpp
new file mode 100644
index 0000000..56d9c4d
--- /dev/null
+++ b/weaver/aidl/default/Weaver.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 "Weaver.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace weaver {
+
+// Methods from ::android::hardware::weaver::IWeaver follow.
+
+::ndk::ScopedAStatus Weaver::getConfig(WeaverConfig* out_config) {
+ (void)out_config;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Weaver::read(int32_t in_slotId, const std::vector<uint8_t>& in_key, WeaverReadResponse* out_response) {
+ (void)in_slotId;
+ (void)in_key;
+ (void)out_response;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Weaver::write(int32_t in_slotId, const std::vector<uint8_t>& in_key, const std::vector<uint8_t>& in_value) {
+ (void)in_slotId;
+ (void)in_key;
+ (void)in_value;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+} //namespace weaver
+} //namespace hardware
+} //namespace android
+} //namespace aidl
diff --git a/weaver/aidl/default/Weaver.h b/weaver/aidl/default/Weaver.h
new file mode 100644
index 0000000..b50018e
--- /dev/null
+++ b/weaver/aidl/default/Weaver.h
@@ -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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/weaver/BnWeaver.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace weaver {
+
+using ::aidl::android::hardware::weaver::WeaverConfig;
+using ::aidl::android::hardware::weaver::WeaverReadResponse;
+
+struct Weaver : public BnWeaver {
+public:
+ Weaver() = default;
+
+ // Methods from ::android::hardware::weaver::IWeaver follow.
+ ::ndk::ScopedAStatus getConfig(WeaverConfig* _aidl_return) override;
+ ::ndk::ScopedAStatus read(int32_t in_slotId, const std::vector<uint8_t>& in_key, WeaverReadResponse* _aidl_return) override;
+ ::ndk::ScopedAStatus write(int32_t in_slotId, const std::vector<uint8_t>& in_key, const std::vector<uint8_t>& in_value) override;
+};
+
+} // namespace weaver
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/weaver/aidl/default/android.hardware.weaver-service.example.rc b/weaver/aidl/default/android.hardware.weaver-service.example.rc
new file mode 100644
index 0000000..ec77774
--- /dev/null
+++ b/weaver/aidl/default/android.hardware.weaver-service.example.rc
@@ -0,0 +1,4 @@
+service vendor.weaver_default /vendor/bin/hw/android.hardware.weaver-service.example
+ class hal
+ user hsm
+ group hsm
diff --git a/weaver/aidl/default/android.hardware.weaver-service.example.xml b/weaver/aidl/default/android.hardware.weaver-service.example.xml
new file mode 100644
index 0000000..ed291cd
--- /dev/null
+++ b/weaver/aidl/default/android.hardware.weaver-service.example.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.weaver</name>
+ <version>1</version>
+ <interface>
+ <name>IWeaver</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/weaver/aidl/default/service.cpp b/weaver/aidl/default/service.cpp
new file mode 100644
index 0000000..1495bc9
--- /dev/null
+++ b/weaver/aidl/default/service.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "Weaver.h"
+
+using ::aidl::android::hardware::weaver::Weaver;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<Weaver> weaver = ndk::SharedRefBase::make<Weaver>();
+
+ const std::string instance = std::string() + Weaver::descriptor + "/default";
+ binder_status_t status = AServiceManager_addService(weaver->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return -1; // Should never be reached
+}
diff --git a/weaver/aidl/vts/Android.bp b/weaver/aidl/vts/Android.bp
new file mode 100644
index 0000000..d7e3ab7
--- /dev/null
+++ b/weaver/aidl/vts/Android.bp
@@ -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.
+//
+
+cc_test {
+ name: "VtsHalWeaverTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["VtsHalWeaverTargetTest.cpp"],
+ shared_libs: [
+ "libbinder_ndk",
+ "libbase",
+ ],
+ static_libs: ["android.hardware.weaver-ndk_platform"],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/weaver/aidl/vts/OWNERS b/weaver/aidl/vts/OWNERS
new file mode 100644
index 0000000..40d95e4
--- /dev/null
+++ b/weaver/aidl/vts/OWNERS
@@ -0,0 +1,2 @@
+chengyouho@google.com
+frankwoo@google.com
diff --git a/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp b/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
new file mode 100644
index 0000000..7d8daa2
--- /dev/null
+++ b/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
@@ -0,0 +1,277 @@
+/*
+ * 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 <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <aidl/android/hardware/weaver/IWeaver.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <limits>
+
+using ::aidl::android::hardware::weaver::IWeaver;
+using ::aidl::android::hardware::weaver::WeaverConfig;
+using ::aidl::android::hardware::weaver::WeaverReadResponse;
+
+using ::ndk::SpAIBinder;
+
+const std::vector<uint8_t> KEY{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+const std::vector<uint8_t> WRONG_KEY{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+const std::vector<uint8_t> VALUE{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+const std::vector<uint8_t> OTHER_VALUE{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 255, 255};
+
+struct WeaverAidlTest : public ::testing::TestWithParam<std::string> {
+ virtual void SetUp() override {
+ weaver = IWeaver::fromBinder(
+ SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+ ASSERT_NE(weaver, nullptr);
+ }
+
+ virtual void TearDown() override {}
+
+ std::shared_ptr<IWeaver> weaver;
+};
+
+/*
+ * Checks config values are suitably large
+ */
+TEST_P(WeaverAidlTest, GetConfig) {
+ WeaverConfig config;
+
+ auto ret = weaver->getConfig(&config);
+
+ ASSERT_TRUE(ret.isOk());
+
+ EXPECT_GE(config.slots, 16u);
+ EXPECT_GE(config.keySize, 16u);
+ EXPECT_GE(config.valueSize, 16u);
+}
+
+/*
+ * Gets the config twice and checks they are the same
+ */
+TEST_P(WeaverAidlTest, GettingConfigMultipleTimesGivesSameResult) {
+ WeaverConfig config1;
+ WeaverConfig config2;
+
+ auto ret = weaver->getConfig(&config1);
+ ASSERT_TRUE(ret.isOk());
+
+ ret = weaver->getConfig(&config2);
+ ASSERT_TRUE(ret.isOk());
+
+ EXPECT_EQ(config1, config2);
+}
+
+/*
+ * Gets the number of slots from the config and writes a key and value to the last one
+ */
+TEST_P(WeaverAidlTest, WriteToLastSlot) {
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig(&config);
+
+ ASSERT_TRUE(configRet.isOk());
+
+ const uint32_t lastSlot = config.slots - 1;
+ const auto writeRet = weaver->write(lastSlot, KEY, VALUE);
+ ASSERT_TRUE(writeRet.isOk());
+}
+
+/*
+ * Writes a key and value to a slot
+ * Reads the slot with the same key and receives the value that was previously written
+ */
+TEST_P(WeaverAidlTest, WriteFollowedByReadGivesTheSameValue) {
+ constexpr uint32_t slotId = 0;
+ const auto ret = weaver->write(slotId, KEY, VALUE);
+ ASSERT_TRUE(ret.isOk());
+
+ WeaverReadResponse response;
+ std::vector<uint8_t> readValue;
+ uint32_t timeout;
+ const auto readRet = weaver->read(slotId, KEY, &response);
+
+ readValue = response.value;
+ timeout = response.timeout;
+
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_EQ(readValue, VALUE);
+ EXPECT_EQ(timeout, 0u);
+}
+
+/*
+ * Writes a key and value to a slot
+ * Overwrites the slot with a new key and value
+ * Reads the slot with the new key and receives the new value
+ */
+TEST_P(WeaverAidlTest, OverwritingSlotUpdatesTheValue) {
+ constexpr uint32_t slotId = 0;
+ const auto initialWriteRet = weaver->write(slotId, WRONG_KEY, VALUE);
+ ASSERT_TRUE(initialWriteRet.isOk());
+
+ const auto overwriteRet = weaver->write(slotId, KEY, OTHER_VALUE);
+ ASSERT_TRUE(overwriteRet.isOk());
+
+ WeaverReadResponse response;
+ std::vector<uint8_t> readValue;
+ uint32_t timeout;
+ const auto readRet = weaver->read(slotId, KEY, &response);
+
+ readValue = response.value;
+ timeout = response.timeout;
+
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_EQ(readValue, OTHER_VALUE);
+ EXPECT_EQ(timeout, 0u);
+}
+
+/*
+ * Writes a key and value to a slot
+ * Reads the slot with a different key so does not receive the value
+ */
+TEST_P(WeaverAidlTest, WriteFollowedByReadWithWrongKeyDoesNotGiveTheValue) {
+ constexpr uint32_t slotId = 0;
+ const auto ret = weaver->write(slotId, KEY, VALUE);
+ ASSERT_TRUE(ret.isOk());
+
+ WeaverReadResponse response;
+ std::vector<uint8_t> readValue;
+ const auto readRet =
+ weaver->read(slotId, WRONG_KEY, &response);
+
+ readValue = response.value;
+
+ ASSERT_FALSE(readRet.isOk());
+ ASSERT_EQ(EX_SERVICE_SPECIFIC, readRet.getExceptionCode());
+ ASSERT_EQ(IWeaver::INCORRECT_KEY, readRet.getServiceSpecificError());
+ EXPECT_TRUE(readValue.empty());
+}
+
+/*
+ * Writing to an invalid slot fails
+ */
+TEST_P(WeaverAidlTest, WritingToInvalidSlotFails) {
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig(&config);
+ ASSERT_TRUE(configRet.isOk());
+
+ if (config.slots == std::numeric_limits<uint32_t>::max()) {
+ // If there are no invalid slots then pass
+ return;
+ }
+
+ const auto writeRet = weaver->write(config.slots, KEY, VALUE);
+ ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Reading from an invalid slot fails rather than incorrect key
+ */
+TEST_P(WeaverAidlTest, ReadingFromInvalidSlotFails) {
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig(&config);
+ ASSERT_TRUE(configRet.isOk());
+
+ if (config.slots == std::numeric_limits<uint32_t>::max()) {
+ // If there are no invalid slots then pass
+ return;
+ }
+
+ WeaverReadResponse response;
+ std::vector<uint8_t> readValue;
+ uint32_t timeout;
+ const auto readRet =
+ weaver->read(config.slots, KEY, &response);
+
+ readValue = response.value;
+ timeout = response.timeout;
+
+ ASSERT_FALSE(readRet.isOk());
+ ASSERT_EQ(EX_SERVICE_SPECIFIC, readRet.getExceptionCode());
+ ASSERT_EQ(IWeaver::STATUS_FAILED, readRet.getServiceSpecificError());
+ EXPECT_TRUE(readValue.empty());
+ EXPECT_EQ(timeout, 0u);
+}
+
+/*
+ * Writing a key that is too large fails
+ */
+TEST_P(WeaverAidlTest, WriteWithTooLargeKeyFails) {
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig(&config);
+ ASSERT_TRUE(configRet.isOk());
+
+ std::vector<uint8_t> bigKey(config.keySize + 1);
+
+ constexpr uint32_t slotId = 0;
+ const auto writeRet = weaver->write(slotId, bigKey, VALUE);
+ ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Writing a value that is too large fails
+ */
+TEST_P(WeaverAidlTest, WriteWithTooLargeValueFails) {
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig(&config);
+ ASSERT_TRUE(configRet.isOk());
+
+ std::vector<uint8_t> bigValue(config.valueSize + 1);
+
+ constexpr uint32_t slotId = 0;
+ const auto writeRet = weaver->write(slotId, KEY, bigValue);
+ ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Reading with a key that is loo large fails
+ */
+TEST_P(WeaverAidlTest, ReadWithTooLargeKeyFails) {
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig(&config);
+ ASSERT_TRUE(configRet.isOk());
+
+ std::vector<uint8_t> bigKey(config.keySize + 1);
+
+ constexpr uint32_t slotId = 0;
+ WeaverReadResponse response;
+ std::vector<uint8_t> readValue;
+ uint32_t timeout;
+ const auto readRet =
+ weaver->read(slotId, bigKey, &response);
+
+ readValue = response.value;
+ timeout = response.timeout;
+
+ ASSERT_FALSE(readRet.isOk());
+ ASSERT_EQ(EX_SERVICE_SPECIFIC, readRet.getExceptionCode());
+ ASSERT_EQ(IWeaver::STATUS_FAILED, readRet.getServiceSpecificError());
+ EXPECT_TRUE(readValue.empty());
+ EXPECT_EQ(timeout, 0u);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WeaverAidlTest);
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, WeaverAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IWeaver::descriptor)),
+ android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/wifi/1.4/default/wifi_legacy_hal.h b/wifi/1.4/default/wifi_legacy_hal.h
index 9964460..1652ae9 100644
--- a/wifi/1.4/default/wifi_legacy_hal.h
+++ b/wifi/1.4/default/wifi_legacy_hal.h
@@ -23,15 +23,9 @@
#include <thread>
#include <vector>
+#include <hardware_legacy/wifi_hal.h>
#include <wifi_system/interface_tool.h>
-// HACK: The include inside the namespace below also transitively includes a
-// bunch of libc headers into the namespace, which leads to functions like
-// socketpair being defined in
-// android::hardware::wifi::V1_1::implementation::legacy_hal. Include this one
-// particular header as a hacky workaround until that's fixed.
-#include <sys/socket.h>
-
namespace android {
namespace hardware {
namespace wifi {
@@ -40,9 +34,273 @@
// This is in a separate namespace to prevent typename conflicts between
// the legacy HAL types and the HIDL interface types.
namespace legacy_hal {
-// Wrap all the types defined inside the legacy HAL header files inside this
+// Import all the types defined inside the legacy HAL header files into this
// namespace.
-#include <hardware_legacy/wifi_hal.h>
+using ::FRAME_TYPE_80211_MGMT;
+using ::FRAME_TYPE_ETHERNET_II;
+using ::FRAME_TYPE_UNKNOWN;
+using ::NAN_CHANNEL_24G_BAND;
+using ::NAN_CHANNEL_5G_BAND_HIGH;
+using ::NAN_CHANNEL_5G_BAND_LOW;
+using ::NAN_DISABLE_RANGE_REPORT;
+using ::NAN_DO_NOT_USE_SRF;
+using ::NAN_DP_CHANNEL_NOT_REQUESTED;
+using ::NAN_DP_CONFIG_NO_SECURITY;
+using ::NAN_DP_CONFIG_SECURITY;
+using ::NAN_DP_END;
+using ::NAN_DP_FORCE_CHANNEL_SETUP;
+using ::NAN_DP_INITIATOR_RESPONSE;
+using ::NAN_DP_INTERFACE_CREATE;
+using ::NAN_DP_INTERFACE_DELETE;
+using ::NAN_DP_REQUEST_ACCEPT;
+using ::NAN_DP_REQUEST_CHANNEL_SETUP;
+using ::NAN_DP_REQUEST_REJECT;
+using ::NAN_DP_RESPONDER_RESPONSE;
+using ::NAN_GET_CAPABILITIES;
+using ::NAN_MATCH_ALG_MATCH_CONTINUOUS;
+using ::NAN_MATCH_ALG_MATCH_NEVER;
+using ::NAN_MATCH_ALG_MATCH_ONCE;
+using ::NAN_PUBLISH_TYPE_SOLICITED;
+using ::NAN_PUBLISH_TYPE_UNSOLICITED;
+using ::NAN_PUBLISH_TYPE_UNSOLICITED_SOLICITED;
+using ::NAN_RANGING_AUTO_RESPONSE_DISABLE;
+using ::NAN_RANGING_AUTO_RESPONSE_ENABLE;
+using ::NAN_RANGING_DISABLE;
+using ::NAN_RANGING_ENABLE;
+using ::NAN_RESPONSE_BEACON_SDF_PAYLOAD;
+using ::NAN_RESPONSE_CONFIG;
+using ::NAN_RESPONSE_DISABLED;
+using ::NAN_RESPONSE_ENABLED;
+using ::NAN_RESPONSE_ERROR;
+using ::NAN_RESPONSE_PUBLISH;
+using ::NAN_RESPONSE_PUBLISH_CANCEL;
+using ::NAN_RESPONSE_STATS;
+using ::NAN_RESPONSE_SUBSCRIBE;
+using ::NAN_RESPONSE_SUBSCRIBE_CANCEL;
+using ::NAN_RESPONSE_TCA;
+using ::NAN_RESPONSE_TRANSMIT_FOLLOWUP;
+using ::NAN_SECURITY_KEY_INPUT_PASSPHRASE;
+using ::NAN_SECURITY_KEY_INPUT_PASSPHRASE;
+using ::NAN_SECURITY_KEY_INPUT_PMK;
+using ::NAN_SECURITY_KEY_INPUT_PMK;
+using ::NAN_SERVICE_ACCEPT_POLICY_ALL;
+using ::NAN_SERVICE_ACCEPT_POLICY_NONE;
+using ::NAN_SRF_ATTR_BLOOM_FILTER;
+using ::NAN_SRF_ATTR_PARTIAL_MAC_ADDR;
+using ::NAN_SRF_INCLUDE_DO_NOT_RESPOND;
+using ::NAN_SRF_INCLUDE_RESPOND;
+using ::NAN_SSI_NOT_REQUIRED_IN_MATCH_IND;
+using ::NAN_SSI_REQUIRED_IN_MATCH_IND;
+using ::NAN_STATUS_ALREADY_ENABLED;
+using ::NAN_STATUS_FOLLOWUP_QUEUE_FULL;
+using ::NAN_STATUS_INTERNAL_FAILURE;
+using ::NAN_STATUS_INVALID_NDP_ID;
+using ::NAN_STATUS_INVALID_PARAM;
+using ::NAN_STATUS_INVALID_PUBLISH_SUBSCRIBE_ID;
+using ::NAN_STATUS_INVALID_REQUESTOR_INSTANCE_ID;
+using ::NAN_STATUS_NAN_NOT_ALLOWED;
+using ::NAN_STATUS_NO_OTA_ACK;
+using ::NAN_STATUS_NO_RESOURCE_AVAILABLE;
+using ::NAN_STATUS_PROTOCOL_FAILURE;
+using ::NAN_STATUS_SUCCESS;
+using ::NAN_STATUS_UNSUPPORTED_CONCURRENCY_NAN_DISABLED;
+using ::NAN_SUBSCRIBE_TYPE_ACTIVE;
+using ::NAN_SUBSCRIBE_TYPE_PASSIVE;
+using ::NAN_TRANSMIT_IN_DW;
+using ::NAN_TRANSMIT_IN_FAW;
+using ::NAN_TX_PRIORITY_HIGH;
+using ::NAN_TX_PRIORITY_NORMAL;
+using ::NAN_TX_TYPE_BROADCAST;
+using ::NAN_TX_TYPE_UNICAST;
+using ::NAN_USE_SRF;
+using ::NanBeaconSdfPayloadInd;
+using ::NanCapabilities;
+using ::NanChannelInfo;
+using ::NanConfigRequest;
+using ::NanDataPathChannelCfg;
+using ::NanDataPathConfirmInd;
+using ::NanDataPathEndInd;
+using ::NanDataPathIndicationResponse;
+using ::NanDataPathInitiatorRequest;
+using ::NanDataPathRequestInd;
+using ::NanDataPathScheduleUpdateInd;
+using ::NanDisabledInd;
+using ::NanDiscEngEventInd;
+using ::NanEnableRequest;
+using ::NanFollowupInd;
+using ::NanMatchAlg;
+using ::NanMatchExpiredInd;
+using ::NanMatchInd;
+using ::NanPublishCancelRequest;
+using ::NanPublishRequest;
+using ::NanPublishTerminatedInd;
+using ::NanPublishType;
+using ::NanRangeReportInd;
+using ::NanRangeRequestInd;
+using ::NanResponseMsg;
+using ::NanSRFType;
+using ::NanStatusType;
+using ::NanSubscribeCancelRequest;
+using ::NanSubscribeRequest;
+using ::NanSubscribeTerminatedInd;
+using ::NanSubscribeType;
+using ::NanTransmitFollowupInd;
+using ::NanTransmitFollowupRequest;
+using ::NanTxType;
+using ::ROAMING_DISABLE;
+using ::ROAMING_ENABLE;
+using ::RTT_PEER_AP;
+using ::RTT_PEER_NAN;
+using ::RTT_PEER_P2P_CLIENT;
+using ::RTT_PEER_P2P_GO;
+using ::RTT_PEER_STA;
+using ::RTT_STATUS_ABORTED;
+using ::RTT_STATUS_FAILURE;
+using ::RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL;
+using ::RTT_STATUS_FAIL_BUSY_TRY_LATER;
+using ::RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE;
+using ::RTT_STATUS_FAIL_INVALID_TS;
+using ::RTT_STATUS_FAIL_NOT_SCHEDULED_YET;
+using ::RTT_STATUS_FAIL_NO_CAPABILITY;
+using ::RTT_STATUS_FAIL_NO_RSP;
+using ::RTT_STATUS_FAIL_PROTOCOL;
+using ::RTT_STATUS_FAIL_REJECTED;
+using ::RTT_STATUS_FAIL_SCHEDULE;
+using ::RTT_STATUS_FAIL_TM_TIMEOUT;
+using ::RTT_STATUS_INVALID_REQ;
+using ::RTT_STATUS_NAN_RANGING_CONCURRENCY_NOT_SUPPORTED;
+using ::RTT_STATUS_NAN_RANGING_PROTOCOL_FAILURE;
+using ::RTT_STATUS_NO_WIFI;
+using ::RTT_STATUS_SUCCESS;
+using ::RTT_TYPE_1_SIDED;
+using ::RTT_TYPE_2_SIDED;
+using ::RX_PKT_FATE_DRV_DROP_FILTER;
+using ::RX_PKT_FATE_DRV_DROP_INVALID;
+using ::RX_PKT_FATE_DRV_DROP_NOBUFS;
+using ::RX_PKT_FATE_DRV_DROP_OTHER;
+using ::RX_PKT_FATE_DRV_QUEUED;
+using ::RX_PKT_FATE_FW_DROP_FILTER;
+using ::RX_PKT_FATE_FW_DROP_INVALID;
+using ::RX_PKT_FATE_FW_DROP_NOBUFS;
+using ::RX_PKT_FATE_FW_DROP_OTHER;
+using ::RX_PKT_FATE_FW_QUEUED;
+using ::RX_PKT_FATE_SUCCESS;
+using ::TX_PKT_FATE_ACKED;
+using ::TX_PKT_FATE_DRV_DROP_INVALID;
+using ::TX_PKT_FATE_DRV_DROP_NOBUFS;
+using ::TX_PKT_FATE_DRV_DROP_OTHER;
+using ::TX_PKT_FATE_DRV_QUEUED;
+using ::TX_PKT_FATE_FW_DROP_INVALID;
+using ::TX_PKT_FATE_FW_DROP_NOBUFS;
+using ::TX_PKT_FATE_FW_DROP_OTHER;
+using ::TX_PKT_FATE_FW_QUEUED;
+using ::TX_PKT_FATE_SENT;
+using ::WIFI_AC_BE;
+using ::WIFI_AC_BK;
+using ::WIFI_AC_VI;
+using ::WIFI_AC_VO;
+using ::WIFI_BAND_A;
+using ::WIFI_BAND_ABG;
+using ::WIFI_BAND_ABG_WITH_DFS;
+using ::WIFI_BAND_A_DFS;
+using ::WIFI_BAND_A_WITH_DFS;
+using ::WIFI_BAND_BG;
+using ::WIFI_BAND_UNSPECIFIED;
+using ::WIFI_CHAN_WIDTH_10;
+using ::WIFI_CHAN_WIDTH_160;
+using ::WIFI_CHAN_WIDTH_20;
+using ::WIFI_CHAN_WIDTH_40;
+using ::WIFI_CHAN_WIDTH_5;
+using ::WIFI_CHAN_WIDTH_5;
+using ::WIFI_CHAN_WIDTH_80;
+using ::WIFI_CHAN_WIDTH_80P80;
+using ::WIFI_CHAN_WIDTH_INVALID;
+using ::WIFI_ERROR_BUSY;
+using ::WIFI_ERROR_INVALID_ARGS;
+using ::WIFI_ERROR_INVALID_REQUEST_ID;
+using ::WIFI_ERROR_NONE;
+using ::WIFI_ERROR_NOT_AVAILABLE;
+using ::WIFI_ERROR_NOT_SUPPORTED;
+using ::WIFI_ERROR_OUT_OF_MEMORY;
+using ::WIFI_ERROR_TIMED_OUT;
+using ::WIFI_ERROR_TOO_MANY_REQUESTS;
+using ::WIFI_ERROR_UNINITIALIZED;
+using ::WIFI_ERROR_UNKNOWN;
+using ::WIFI_INTERFACE_TYPE_AP;
+using ::WIFI_INTERFACE_TYPE_NAN;
+using ::WIFI_INTERFACE_TYPE_P2P;
+using ::WIFI_INTERFACE_TYPE_STA;
+using ::WIFI_LATENCY_MODE_LOW;
+using ::WIFI_LATENCY_MODE_NORMAL;
+using ::WIFI_LOGGER_CONNECT_EVENT_SUPPORTED;
+using ::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED;
+using ::WIFI_LOGGER_MEMORY_DUMP_SUPPORTED;
+using ::WIFI_LOGGER_PACKET_FATE_SUPPORTED;
+using ::WIFI_LOGGER_POWER_EVENT_SUPPORTED;
+using ::WIFI_LOGGER_WAKE_LOCK_SUPPORTED;
+using ::WIFI_MOTION_EXPECTED;
+using ::WIFI_MOTION_NOT_EXPECTED;
+using ::WIFI_MOTION_UNKNOWN;
+using ::WIFI_POWER_SCENARIO_ON_BODY_CELL_OFF;
+using ::WIFI_POWER_SCENARIO_ON_BODY_CELL_ON;
+using ::WIFI_POWER_SCENARIO_ON_HEAD_CELL_OFF;
+using ::WIFI_POWER_SCENARIO_ON_HEAD_CELL_ON;
+using ::WIFI_POWER_SCENARIO_VOICE_CALL;
+using ::WIFI_RTT_BW_10;
+using ::WIFI_RTT_BW_160;
+using ::WIFI_RTT_BW_20;
+using ::WIFI_RTT_BW_40;
+using ::WIFI_RTT_BW_5;
+using ::WIFI_RTT_BW_80;
+using ::WIFI_RTT_PREAMBLE_HE;
+using ::WIFI_RTT_PREAMBLE_HT;
+using ::WIFI_RTT_PREAMBLE_LEGACY;
+using ::WIFI_RTT_PREAMBLE_VHT;
+using ::WIFI_SCAN_FLAG_INTERRUPTED;
+using ::WIFI_SUCCESS;
+using ::WLAN_MAC_2_4_BAND;
+using ::WLAN_MAC_5_0_BAND;
+using ::WLAN_MAC_6_0_BAND;
+using ::frame_info;
+using ::frame_type;
+using ::fw_roaming_state_t;
+using ::mac_addr;
+using ::rtt_peer_type;
+using ::ssid_t;
+using ::transaction_id;
+using ::wifi_band;
+using ::wifi_cached_scan_results;
+using ::wifi_channel_info;
+using ::wifi_channel_width;
+using ::wifi_error;
+using ::wifi_gscan_capabilities;
+using ::wifi_information_element;
+using ::wifi_interface_type;
+using ::wifi_latency_mode;
+using ::wifi_lci_information;
+using ::wifi_lcr_information;
+using ::wifi_motion_pattern;
+using ::wifi_power_scenario;
+using ::wifi_rate;
+using ::wifi_request_id;
+using ::wifi_ring_buffer_status;
+using ::wifi_roaming_capabilities;
+using ::wifi_roaming_config;
+using ::wifi_rtt_bw;
+using ::wifi_rtt_capabilities;
+using ::wifi_rtt_config;
+using ::wifi_rtt_preamble;
+using ::wifi_rtt_responder;
+using ::wifi_rtt_result;
+using ::wifi_rtt_status;
+using ::wifi_rtt_type;
+using ::wifi_rx_packet_fate;
+using ::wifi_rx_report;
+using ::wifi_scan_bucket_spec;
+using ::wifi_scan_cmd_params;
+using ::wifi_scan_result;
+using ::wifi_tx_packet_fate;
+using ::wifi_tx_report;
// APF capabilities supported by the iface.
struct PacketFilterCapabilities {
diff --git a/wifi/1.4/default/wifi_legacy_hal_stubs.h b/wifi/1.4/default/wifi_legacy_hal_stubs.h
index 577a545..c709680 100644
--- a/wifi/1.4/default/wifi_legacy_hal_stubs.h
+++ b/wifi/1.4/default/wifi_legacy_hal_stubs.h
@@ -17,13 +17,14 @@
#ifndef WIFI_LEGACY_HAL_STUBS_H_
#define WIFI_LEGACY_HAL_STUBS_H_
+#include <hardware_legacy/wifi_hal.h>
+
namespace android {
namespace hardware {
namespace wifi {
namespace V1_4 {
namespace implementation {
namespace legacy_hal {
-#include <hardware_legacy/wifi_hal.h>
bool initHalFuncTableWithStubs(wifi_hal_fn* hal_fn);
} // namespace legacy_hal